Merge branch 'release-1.4.0'

main
Nikita Manovich 5 years ago
commit 315c9cc8d2

@ -0,0 +1,50 @@
name: Cache
on:
push:
branches:
- 'develop'
jobs:
Caching_CVAT:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
path: /tmp/cvat_cache_server
key: ${{ runner.os }}-build-server-${{ github.sha }}
restore-keys: |
${{ runner.os }}-build-server-
- uses: actions/cache@v2
with:
path: /tmp/cvat_cache_ui
key: ${{ runner.os }}-build-ui-${{ github.sha }}
restore-keys: |
${{ runner.os }}-build-ui-
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1.1.2
- name: Caching CVAT server
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
cache-from: type=local,src=/tmp/cvat_cache_server
cache-to: type=local,dest=/tmp/cvat_cache_server-new
- name: Caching CVAT UI
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile.ui
cache-from: type=local,src=/tmp/cvat_cache_ui
cache-to: type=local,dest=/tmp/cvat_cache_ui-new
- name: Moving cache
run: |
rm -rf /tmp/cvat_cache_server
mv /tmp/cvat_cache_server-new /tmp/cvat_cache_server
rm -rf /tmp/cvat_cache_ui
mv /tmp/cvat_cache_ui-new /tmp/cvat_cache_ui

@ -0,0 +1,17 @@
name: Cancelling Duplicates
on:
workflow_run:
workflows: ['CI']
types: ['requested']
jobs:
cancel-duplicate-workflow-runs:
name: "Cancel duplicate workflow runs"
runs-on: ubuntu-latest
steps:
- uses: potiuk/cancel-workflow-runs@master
name: "Cancel duplicate workflow runs"
with:
cancelMode: duplicates
token: ${{ secrets.GITHUB_TOKEN }}
sourceRunId: ${{ github.event.workflow_run.id }}

@ -0,0 +1,49 @@
name: Linter
on: pull_request
jobs:
HadoLint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run checks
env:
HADOLINT: "${{ github.workspace }}/hadolint"
HADOLINT_VER: "2.1.0"
VERIFICATION_LEVEL: "error"
run: |
URL="https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files"
PR_FILES=$(curl -s -X GET -G $URL | jq -r '.[] | select(.status != "removed") | .filename')
for file in $PR_FILES; do
if [[ ${file} =~ 'Dockerfile' ]]; then
changed_dockerfiles+=" ${file}"
fi
done
if [[ ! -z ${changed_dockerfiles} ]]; then
curl -sL -o ${HADOLINT} "https://github.com/hadolint/hadolint/releases/download/v${HADOLINT_VER}/hadolint-Linux-x86_64" && chmod 700 ${HADOLINT}
echo "HadoLint version: "`${HADOLINT} --version`
echo "The files will be checked: "`echo ${changed_dockerfiles}`
mkdir -p hadolint_report
${HADOLINT} --no-fail --format json ${changed_dockerfiles} > ./hadolint_report/hadolint_report.json
get_verification_level=`cat ./hadolint_report/hadolint_report.json | jq -r '.[] | .level'`
for line in ${get_verification_level}; do
if [[ ${line} =~ ${VERIFICATION_LEVEL} ]]; then
pip install json2html
python ./tests/json_to_html.py ./hadolint_report/hadolint_report.json
exit 1
else
exit 0
fi
done
else
echo "No files with the \"Dockerfile*\" name found"
fi
- name: Upload artifacts
if: failure()
uses: actions/upload-artifact@v2
with:
name: hadolint_report
path: hadolint_report

@ -8,50 +8,238 @@ on:
branches:
- '*'
jobs:
build:
runs-on: ubuntu-latest
steps:
Unit_testing:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- name: Getting SHA from the default branch
id: get-sha
run: |
URL_get_default_branch="https://api.github.com/repos/${{ github.repository }}"
DEFAULT_BRANCH=$(curl -s -X GET -G ${URL_get_default_branch} | jq -r '.default_branch')
URL_get_sha_default_branch="https://api.github.com/repos/${{ github.repository }}/git/ref/heads/${DEFAULT_BRANCH}"
SHA=$(curl -s -X GET -G ${URL_get_sha_default_branch} | jq .object.sha | tr -d '"')
echo ::set-output name=default_branch::${DEFAULT_BRANCH}
echo ::set-output name=sha::${SHA}
- name: Waiting a cache creation in the default branch
run: |
URL_runs="https://api.github.com/repos/${{ github.repository }}/actions/workflows/cache.yml/runs"
SLEEP=45
NUMBER_ATTEMPTS=10
while [[ ${NUMBER_ATTEMPTS} -gt 0 ]]; do
RUN_status=$(curl -s -X GET -G ${URL_runs} | jq -r '.workflow_runs[]? | select((.head_sha == "${{ steps.get-sha.outputs.sha }}") and (.event == "push") and (.name == "Cache") and (.head_branch == "${{ steps.get-sha.outputs.default_branch }}")) | .status')
if [[ ${RUN_status} == "completed" ]]; then
echo "The cache creation on the '${{ steps.get-sha.outputs.default_branch }}' branch has finished. Status: ${RUN_status}"
break
else
echo "The creation of the cache is not yet complete."
echo "There are still attempts to check the cache: ${NUMBER_ATTEMPTS}"
echo "Status of caching in the '${{ steps.get-sha.outputs.default_branch }}' branch: ${RUN_status}"
echo "sleep ${SLEEP}"
sleep ${SLEEP}
((NUMBER_ATTEMPTS--))
fi
done
if [[ ${NUMBER_ATTEMPTS} -eq 0 ]]; then
echo "Number of attempts expired!"
echo "Probably the creation of the cache is not yet complete. Will continue working without the cache."
fi
- name: Getting CVAT server cache from the default branch
uses: actions/cache@v2
with:
node-version: 12
- name: Build CVAT
path: /tmp/cvat_cache_server
key: ${{ runner.os }}-build-server-${{ steps.get-sha.outputs.sha }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1.1.2
- name: Building CVAT server image
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
cache-from: type=local,src=/tmp/cvat_cache_server
tags: openvino/cvat_server:latest
load: true
- name: Runing unit tests
env:
HOST_COVERAGE_DATA_DIR: ${{ github.workspace }}
CONTAINER_COVERAGE_DATA_DIR: '/coverage_data'
DJANGO_SU_NAME: 'admin'
DJANGO_SU_EMAIL: 'admin@localhost.company'
DJANGO_SU_PASSWORD: '12qwaszx'
CONTAINER_COVERAGE_DATA_DIR: "/coverage_data"
run: |
docker-compose -f docker-compose.yml -f docker-compose.dev.yml -f docker-compose.ci.yml build
docker-compose -f docker-compose.yml -f docker-compose.dev.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'coverage run -a manage.py test cvat/apps utils/cli && mv .coverage ${CONTAINER_COVERAGE_DATA_DIR}'
docker-compose -f docker-compose.yml -f docker-compose.dev.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'cd cvat-data && npm ci && cd ../cvat-core && npm ci && npm run test && mv ./reports/coverage/lcov.info ${CONTAINER_COVERAGE_DATA_DIR} && chmod a+rwx ${CONTAINER_COVERAGE_DATA_DIR}/lcov.info'
docker-compose up -d
docker exec -i cvat /bin/bash -c "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('${DJANGO_SU_NAME}', '${DJANGO_SU_EMAIL}', '${DJANGO_SU_PASSWORD}')\" | python3 ~/manage.py shell"
- name: Code instrumentation
- name: Uploading code coverage results as an artifact
if: github.ref == 'refs/heads/develop'
uses: actions/upload-artifact@v2
with:
name: coverage_results
path: |
${{ github.workspace }}/.coverage
${{ github.workspace }}/lcov.info
E2E_testing:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
specs: ['actions_tasks', 'actions_tasks2', 'actions_tasks3', 'actions_objects', 'actions_objects2', 'actions_users', 'actions_projects', 'canvas3d_functionality', 'issues_prs', 'issues_prs2']
steps:
- uses: actions/checkout@v2
- name: Getting SHA from the default branch
id: get-sha
run: |
URL_get_default_branch="https://api.github.com/repos/${{ github.repository }}"
DEFAULT_BRANCH=$(curl -s -X GET -G ${URL_get_default_branch} | jq -r '.default_branch')
URL_get_sha_default_branch="https://api.github.com/repos/${{ github.repository }}/git/ref/heads/${DEFAULT_BRANCH}"
SHA=$(curl -s -X GET -G ${URL_get_sha_default_branch} | jq .object.sha | tr -d '"')
echo ::set-output name=default_branch::${DEFAULT_BRANCH}
echo ::set-output name=sha::${SHA}
- name: Waiting a cache creation in the default branch
run: |
URL_runs="https://api.github.com/repos/${{ github.repository }}/actions/workflows/cache.yml/runs"
SLEEP=45
NUMBER_ATTEMPTS=10
while [[ ${NUMBER_ATTEMPTS} -gt 0 ]]; do
RUN_status=$(curl -s -X GET -G ${URL_runs} | jq -r '.workflow_runs[]? | select((.head_sha == "${{ steps.get-sha.outputs.sha }}") and (.event == "push") and (.name == "Cache") and (.head_branch == "${{ steps.get-sha.outputs.default_branch }}")) | .status')
if [[ ${RUN_status} == "completed" ]]; then
echo "The cache creation on the '${{ steps.get-sha.outputs.default_branch }}' branch has finished. Status: ${RUN_status}"
break
else
echo "The creation of the cache is not yet complete."
echo "There are still attempts to check the cache: ${NUMBER_ATTEMPTS}"
echo "Status of caching in the '${{ steps.get-sha.outputs.default_branch }}' branch: ${RUN_status}"
echo "sleep ${SLEEP}"
sleep ${SLEEP}
((NUMBER_ATTEMPTS--))
fi
done
if [[ ${NUMBER_ATTEMPTS} -eq 0 ]]; then
echo "Number of attempts expired!"
echo "Probably the creation of the cache is not yet complete. Will continue working without the cache."
fi
- name: Getting CVAT server cache from the default branch
uses: actions/cache@v2
with:
path: /tmp/cvat_cache_server
key: ${{ runner.os }}-build-server-${{ steps.get-sha.outputs.sha }}
- name: Getting cache CVAT UI from the default branch
uses: actions/cache@v2
with:
path: /tmp/cvat_cache_ui
key: ${{ runner.os }}-build-ui-${{ steps.get-sha.outputs.sha }}
- uses: actions/setup-node@v2
with:
node-version: 12
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1.1.2
- name: Building CVAT server image
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
cache-from: type=local,src=/tmp/cvat_cache_server
tags: openvino/cvat_server:latest
load: true
- name: Building CVAT UI image
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile.ui
cache-from: type=local,src=/tmp/cvat_cache_ui
tags: openvino/cvat_ui:latest
load: true
- name: Instrumentation of the code then rebuilding the CVAT UI
if: github.ref == 'refs/heads/develop'
run: |
npm ci
npm run coverage
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
- name: End-to-end testing
docker-compose -f docker-compose.yml -f docker-compose.dev.yml build cvat_ui
- name: Running e2e tests
env:
DJANGO_SU_NAME: 'admin'
DJANGO_SU_EMAIL: 'admin@localhost.company'
DJANGO_SU_PASSWORD: '12qwaszx'
API_ABOUT_PAGE: "localhost:8080/api/v1/server/about"
run: |
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d
/bin/bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' ${API_ABOUT_PAGE})" != "401" ]]; do sleep 5; done'
docker exec -i cvat /bin/bash -c "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('${DJANGO_SU_NAME}', '${DJANGO_SU_EMAIL}', '${DJANGO_SU_PASSWORD}')\" | python3 ~/manage.py shell" docker exec -i cvat /bin/bash -c "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('${DJANGO_SU_NAME}', '${DJANGO_SU_EMAIL}', '${DJANGO_SU_PASSWORD}')\" | python3 ~/manage.py shell"
cd ./tests
npm ci
npx cypress run --headless --browser chrome
if [[ ${{ github.ref }} == 'refs/heads/develop' ]]; then
npx cypress run --headless --browser chrome --spec 'cypress/integration/${{ matrix.specs }}/**/*.js'
mv ./.nyc_output/out.json ./.nyc_output/out_${{ matrix.specs }}.json
else
npx cypress run --headless --browser chrome --env coverage=false --spec 'cypress/integration/${{ matrix.specs }}/**/*.js'
fi
- name: Creating a log file from "cvat" container logs
if: failure()
run: |
docker logs cvat > ${{ github.workspace }}/tests/cvat_${{ matrix.specs }}.log
- name: Uploading cypress screenshots as an artifact
if: failure()
uses: actions/upload-artifact@v2
with:
name: cypress_screenshots
name: cypress_screenshots_${{ matrix.specs }}
path: ${{ github.workspace }}/tests/cypress/screenshots
- name: Collect coverage data
- name: Uploading "cvat" container logs as an artifact
if: failure()
uses: actions/upload-artifact@v2
with:
name: cvat_container_logs
path: ${{ github.workspace }}/tests/cvat_${{ matrix.specs }}.log
- name: Uploading code coverage results as an artifact
if: github.ref == 'refs/heads/develop'
uses: actions/upload-artifact@v2
with:
name: coverage_results
path: ${{ github.workspace }}/tests/.nyc_output
Coveralls:
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
needs: [Unit_testing, E2E_testing]
steps:
- uses: actions/checkout@v2
- name: Geting SHA from the default branch
id: get-sha
run: |
URL_get_default_branch="https://api.github.com/repos/${{ github.repository }}"
DEFAULT_BRANCH=$(curl -s -X GET -G ${URL_get_default_branch} | jq -r '.default_branch')
URL_get_sha_default_branch="https://api.github.com/repos/${{ github.repository }}/git/ref/heads/${DEFAULT_BRANCH}"
SHA=$(curl -s -X GET -G ${URL_get_sha_default_branch} | jq .object.sha | tr -d '"')
echo ::set-output name=sha::${SHA}
- name: Getting CVAT server cache from the default branch
uses: actions/cache@v2
with:
path: /tmp/cvat_cache_server
key: ${{ runner.os }}-build-server-${{ steps.get-sha.outputs.sha }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1.1.2
- name: Building CVAT server image
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
cache-from: type=local,src=/tmp/cvat_cache_server
tags: openvino/cvat_server:latest
load: true
- name: Downloading coverage results
uses: actions/download-artifact@v2
with:
name: coverage_results
- name: Combining coverage results
run: |
mkdir -p ./nyc_output_tmp
mv ./out_*.json ./nyc_output_tmp
mkdir -p ./.nyc_output
npm ci
npx nyc merge ./nyc_output_tmp ./.nyc_output/out.json
- name: Sending results to Coveralls
env:
HOST_COVERAGE_DATA_DIR: ${{ github.workspace }}
CONTAINER_COVERAGE_DATA_DIR: "/coverage_data"
COVERALLS_SERVICE_NAME: github
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mv ./tests/.nyc_output ./
npx nyc report --reporter=text-lcov >> ${HOST_COVERAGE_DATA_DIR}/lcov.info
docker-compose -f docker-compose.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'cd ${CONTAINER_COVERAGE_DATA_DIR} && coveralls-lcov -v -n lcov.info > ${CONTAINER_COVERAGE_DATA_DIR}/coverage.json'
docker-compose -f docker-compose.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'ln -s ${CONTAINER_COVERAGE_DATA_DIR}/.git . && ln -s ${CONTAINER_COVERAGE_DATA_DIR}/.coverage . && ln -s ${CONTAINER_COVERAGE_DATA_DIR}/coverage.json . && coveralls --merge=coverage.json'
npx nyc report --reporter=text-lcov >> ${HOST_COVERAGE_DATA_DIR}/lcov.info
docker-compose -f docker-compose.yml -f docker-compose.dev.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'cd ${CONTAINER_COVERAGE_DATA_DIR} && coveralls-lcov -v -n lcov.info > ${CONTAINER_COVERAGE_DATA_DIR}/coverage.json'
docker-compose -f docker-compose.yml -f docker-compose.dev.yml -f docker-compose.ci.yml run cvat_ci /bin/bash -c 'ln -s ${CONTAINER_COVERAGE_DATA_DIR}/.git . && ln -s ${CONTAINER_COVERAGE_DATA_DIR}/.coverage . && ln -s ${CONTAINER_COVERAGE_DATA_DIR}/coverage.json . && coveralls --merge=coverage.json'

@ -0,0 +1,46 @@
name: Linter
on: pull_request
jobs:
Remark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 12
- name: Run checks
run: |
URL="https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files"
PR_FILES=$(curl -s -X GET -G $URL | jq -r '.[] | select(.status != "removed") | .filename')
for files in $PR_FILES; do
extension="${files##*.}"
if [[ $extension == 'md' ]]; then
changed_files_remark+=" ${files}"
fi
done
if [[ ! -z ${changed_files_remark} ]]; then
npm ci
npm install remark-cli@9.0.0 vfile-reporter-json@2.0.2
mkdir -p remark_report
echo "Remark version: "`npx remark --version`
echo "The files will be checked: "`echo ${changed_files_remark}`
npx remark --quiet --report json --no-stdout ${changed_files_remark} 2> ./remark_report/remark_report.json
get_report=`cat ./remark_report/remark_report.json | jq -r '.[] | select(.messages | length > 0)'`
if [[ ! -z ${get_report} ]]; then
pip install json2html
python ./tests/json_to_html.py ./remark_report/remark_report.json
exit 1
fi
else
echo "No files with the \"md\" extension found"
fi
- name: Upload artifacts
if: failure()
uses: actions/upload-artifact@v2
with:
name: remark_report
path: remark_report

@ -0,0 +1,42 @@
name: Linter
on: pull_request
jobs:
StyleLint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 12
- name: Run checks
run: |
URL="https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files"
PR_FILES=$(curl -s -X GET -G $URL | jq -r '.[] | select(.status != "removed") | .filename')
for files in $PR_FILES; do
extension="${files##*.}"
if [[ $extension == 'css' || $extension == 'scss' ]]; then
changed_files_stylelint+=" ${files}"
fi
done
if [[ ! -z ${changed_files_stylelint} ]]; then
npm ci
mkdir -p stylelint_report
echo "StyleLint version: "`npx stylelint --version`
echo "The files will be checked: "`echo ${changed_files_stylelint}`
npx stylelint --formatter json --output-file ./stylelint_report/stylelint_report.json ${changed_files_stylelint} || exit_code=`echo $?` || true
pip install json2html
python ./tests/json_to_html.py ./stylelint_report/stylelint_report.json
exit ${exit_code}
else
echo "No files with the \"css|scss\" extension found"
fi
- name: Upload artifacts
if: failure()
uses: actions/upload-artifact@v2
with:
name: stylelint_report
path: stylelint_report

7
.gitignore vendored

@ -34,3 +34,10 @@ yarn-error.log*
#Ignore Cypress tests temp files
/tests/cypress/fixtures
/tests/cypress/screenshots
.idea/
#Ignore helm-related files
/helm-chart/Chart.lock
/helm-chart/values.*.yaml
/helm-chart/*.values.yaml
/helm-chart/charts/*

@ -4,7 +4,7 @@ exports.plugins = [
'remark-preset-lint-recommended',
'remark-preset-lint-consistent',
['remark-lint-list-item-indent', 'space'],
['remark-lint-no-dead-urls', { skipOffline: true }],
['remark-lint-no-dead-urls', false], // Does not work because of github protection system
['remark-lint-maximum-line-length', 120],
['remark-lint-maximum-heading-length', 120],
['remark-lint-strong-marker', '*'],

@ -1,5 +1,5 @@
{
"python.pythonPath": ".env/bin/python",
"eslint.enable": true,
"eslint.probe": [
"javascript",
"typescript",

@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.4.0] - 2021-05-18
### Added
- Documentation on mask annotation (<https://github.com/openvinotoolkit/cvat/pull/3044>)
- Hotkeys to switch a label of existing object or to change default label (for objects created with N) (<https://github.com/openvinotoolkit/cvat/pull/3070>)
- A script to convert some kinds of DICOM files to regular images (<https://github.com/openvinotoolkit/cvat/pull/3095>)
- Helm chart prototype (<https://github.com/openvinotoolkit/cvat/pull/3102>)
### Changed
- Place of migration logger initialization (<https://github.com/openvinotoolkit/cvat/pull/3170>)
### Removed
- Kubernetes templates from (<https://github.com/openvinotoolkit/cvat/pull/1962>) due to helm charts (<https://github.com/openvinotoolkit/cvat/pull/3171>)
### Fixed
- Export of instance masks with holes (<https://github.com/openvinotoolkit/cvat/pull/3044>)
- Changing a label on canvas does not work when 'Show object details' enabled (<https://github.com/openvinotoolkit/cvat/pull/3084>)
- Make sure frame unzip web worker correctly terminates after unzipping all images in a requested chunk (<https://github.com/openvinotoolkit/cvat/pull/3096>)
- Reset password link was unavailable before login (<https://github.com/openvinotoolkit/cvat/pull/3140>)
- Manifest: migration (<https://github.com/openvinotoolkit/cvat/pull/3146>)
- Fixed cropping polygon in some corner cases (<https://github.com/openvinotoolkit/cvat/pull/3184>)
## [1.3.0] - 3/31/2021
### Added
@ -235,7 +261,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Smaller object details (<https://github.com/opencv/cvat/pull/1877>)
- `COCO` format does not convert bboxes to polygons on export (<https://github.com/opencv/cvat/pull/1953>)
- It is impossible to submit a DL model in OpenVINO format using UI. Now you can deploy new models on the server using serverless functions (<https://github.com/opencv/cvat/pull/1767>)
- It is impossible to submit a DL model in OpenVINO format using UI.
Now you can deploy new models on the server using serverless functions
(<https://github.com/opencv/cvat/pull/1767>)
- Files and folders under share path are now alphabetically sorted
### Removed
@ -280,7 +308,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added canvas background color selector (<https://github.com/opencv/cvat/pull/1705>)
- SCSS files linting with Stylelint tool (<https://github.com/opencv/cvat/pull/1766>)
- Supported import and export or single boxes in MOT format (https://github.com/opencv/cvat/pull/1764)
- [Datumaro] Added `stats` command, which shows some dataset statistics like image mean and std (https://github.com/opencv/cvat/pull/1734)
- [Datumaro] Added `stats` command, which shows some dataset statistics
like image mean and std (https://github.com/opencv/cvat/pull/1734)
- Add option to upload annotations upon task creation on CLI
- Polygon and polylines interpolation (<https://github.com/opencv/cvat/pull/1571>)
- Ability to redraw shape from scratch (Shift + N) for an activated shape (<https://github.com/opencv/cvat/pull/1571>)
@ -297,7 +326,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Removed information about e-mail from the basic user information (<https://github.com/opencv/cvat/pull/1627>)
- Update https install manual. Makes it easier and more robust. Includes automatic renewing of lets encrypt certificates.
- Update https install manual. Makes it easier and more robust.
Includes automatic renewing of lets encrypt certificates.
- Settings page move to the modal. (<https://github.com/opencv/cvat/pull/1705>)
- Implemented import and export of annotations with relative image paths (<https://github.com/opencv/cvat/pull/1463>)
- Using only single click to start editing or remove a point (<https://github.com/opencv/cvat/pull/1571>)
@ -346,7 +376,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Downloaded file name in annotations export became more informative (<https://github.com/opencv/cvat/pull/1352>)
- Added auto trimming for trailing whitespaces style enforcement (<https://github.com/opencv/cvat/pull/1352>)
- REST API: updated `GET /task/<id>/annotations`: parameters are `format`, `filename` (now optional), `action` (optional) (<https://github.com/opencv/cvat/pull/1352>)
- REST API: updated `GET /task/<id>/annotations`: parameters are `format`, `filename`
(now optional), `action` (optional) (<https://github.com/opencv/cvat/pull/1352>)
- REST API: removed `dataset/formats`, changed format of `annotation/formats` (<https://github.com/opencv/cvat/pull/1352>)
- Exported annotations are stored for N hours instead of indefinitely (<https://github.com/opencv/cvat/pull/1352>)
- Formats: CVAT format now accepts ZIP and XML (<https://github.com/opencv/cvat/pull/1352>)
@ -366,7 +397,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Categories for empty projects with no sources are taken from own dataset (<https://github.com/opencv/cvat/pull/1352>)
- Added directory removal on error during `extract` command (<https://github.com/opencv/cvat/pull/1352>)
- Added debug error message on incorrect XPath (<https://github.com/opencv/cvat/pull/1352>)
- Exporting frame stepped task (<https://github.com/opencv/cvat/issues/1294, https://github.com/opencv/cvat/issues/1334>)
- Exporting frame stepped task
(<https://github.com/opencv/cvat/issues/1294, https://github.com/opencv/cvat/issues/1334>)
- Fixed broken command line interface for `cvat` export format in Datumaro (<https://github.com/opencv/cvat/issues/1494>)
- Updated Rest API document, Swagger document serving instruction issue (<https://github.com/opencv/cvat/issues/1495>)
- Fixed cuboid occluded view (<https://github.com/opencv/cvat/pull/1500>)
@ -402,12 +434,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Removed
- Annotation convertation utils, currently supported natively via Datumaro framework (https://github.com/opencv/cvat/pull/1477)
- Annotation convertation utils, currently supported natively via Datumaro framework
(https://github.com/opencv/cvat/pull/1477)
### Fixed
- Auto annotation, TF annotation and Auto segmentation apps (https://github.com/opencv/cvat/pull/1409)
- Import works with truncated images now: "OSError:broken data stream" on corrupt images (https://github.com/opencv/cvat/pull/1430)
- Import works with truncated images now: "OSError:broken data stream" on corrupt images
(https://github.com/opencv/cvat/pull/1430)
- Hide functionality (H) doesn't work (<https://github.com/opencv/cvat/pull/1445>)
- The highlighted attribute doesn't correspond to the chosen attribute in AAM (<https://github.com/opencv/cvat/pull/1445>)
- Inconvinient image shaking while drawing a polygon (hold Alt key during drawing/editing/grouping to drag an image) (<https://github.com/opencv/cvat/pull/1445>)
@ -432,7 +466,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Option to display shape text always
- Dedicated message with clarifications when share is unmounted (https://github.com/opencv/cvat/pull/1373)
- Ability to create one tracked point (https://github.com/opencv/cvat/pull/1383)
- Ability to draw/edit polygons and polylines with automatic bordering feature (https://github.com/opencv/cvat/pull/1394)
- Ability to draw/edit polygons and polylines with automatic bordering feature
(https://github.com/opencv/cvat/pull/1394)
- Tutorial: instructions for CVAT over HTTPS
- Deep extreme cut (semi-automatic segmentation) to the new UI (https://github.com/opencv/cvat/pull/1398)
@ -456,13 +491,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Removed objects are visible for search
- Add missed task_id and job_id fields into exception logs for the new UI (https://github.com/opencv/cvat/pull/1372)
- UI fails when annotations saving occurs during drag/resize/edit (https://github.com/opencv/cvat/pull/1383)
- Multiple savings when hold Ctrl+S (a lot of the same copies of events were sent with the same working time) (https://github.com/opencv/cvat/pull/1383)
- Multiple savings when hold Ctrl+S (a lot of the same copies of events were sent with the same working time)
(https://github.com/opencv/cvat/pull/1383)
- UI doesn't have any reaction when git repos synchronization failed (https://github.com/opencv/cvat/pull/1383)
- Bug when annotations cannot be saved after (delete - save - undo - save) (https://github.com/opencv/cvat/pull/1383)
- VOC format exports Upper case labels correctly in lower case (https://github.com/opencv/cvat/pull/1379)
- Fixed polygon exporting bug in COCO dataset (https://github.com/opencv/cvat/issues/1387)
- Task creation from remote files (https://github.com/opencv/cvat/pull/1392)
- Job cannot be opened in some cases when the previous job was failed during opening (https://github.com/opencv/cvat/issues/1403)
- Job cannot be opened in some cases when the previous job was failed during opening
(https://github.com/opencv/cvat/issues/1403)
- Deactivated shape is still highlighted on the canvas (https://github.com/opencv/cvat/issues/1403)
- AttributeError: 'tuple' object has no attribute 'read' in ReID algorithm (https://github.com/opencv/cvat/issues/1403)
- Wrong semi-automatic segmentation near edges of an image (https://github.com/opencv/cvat/issues/1403)
@ -514,7 +551,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Yolo Auto Annotation Script
- Auto segmentation using Mask_RCNN component (Keras+Tensorflow Mask R-CNN Segmentation)
- REST API to export an annotation task (images + annotations)
- [Datumaro](https://github.com/opencv/cvat/tree/develop/datumaro) - a framework to build, analyze, debug and visualize datasets
[Datumaro](https://github.com/opencv/cvat/tree/develop/datumaro) -
a framework to build, analyze, debug and visualize datasets
- Text Detection Auto Annotation Script in OpenVINO format for version 4
- Added in OpenVINO Semantic Segmentation for roads
- Ability to visualize labels when using Auto Annotation runner
@ -636,11 +674,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Deprecated
- "Flip images" flag in the create task dialog will be removed. Rotation functionality in client part have been added instead.
### Removed
-
- "Flip images" flag in the create task dialog will be removed.
Rotation functionality in client part have been added instead.
### Fixed
@ -664,7 +699,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Silk profiler to improve development process
- Admin panel can be used to edit labels and attributes for annotation tasks
- Analytics component to manage a data annotation team, monitor exceptions, collect client and server logs
- Changeable job and task statuses (annotation, validation, completed). A job status can be changed manually, a task status is computed automatically based on job statuses (#153)
- Changeable job and task statuses (annotation, validation, completed).
A job status can be changed manually, a task status is computed automatically based on job statuses (#153)
- Backlink to a task from its job annotation view (#156)
- Buttons lock/hide for labels. They work for all objects with the same label on a current frame (#116)
@ -677,7 +713,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Per task/job permissions to create/access/change/delete tasks and annotations
- Documentation was improved
- Timeout for creating tasks was increased (from 1h to 4h) (#136)
- Drawing has become more convenience. Now it is possible to draw outside an image. Shapes will be automatically truncated after drawing process (#202)
- Drawing has become more convenience. Now it is possible to draw outside an image.
Shapes will be automatically truncated after drawing process (#202)
### Fixed

@ -2,9 +2,9 @@ FROM ubuntu:20.04 as build-image
ARG http_proxy
ARG https_proxy
ARG no_proxy
ARG no_proxy="nuclio,${no_proxy}"
ARG socks_proxy
ARG DJANGO_CONFIGURATION
ARG DJANGO_CONFIGURATION="production"
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends install -yq \
@ -53,9 +53,9 @@ FROM ubuntu:20.04
ARG http_proxy
ARG https_proxy
ARG no_proxy
ARG no_proxy="nuclio,${no_proxy}"
ARG socks_proxy
ARG TZ
ARG TZ="Etc/UTC"
ENV TERM=xterm \
http_proxy=${http_proxy} \
@ -66,8 +66,8 @@ ENV TERM=xterm \
LC_ALL='C.UTF-8' \
TZ=${TZ}
ARG USER
ARG DJANGO_CONFIGURATION
ARG USER="django"
ARG DJANGO_CONFIGURATION="production"
ENV DJANGO_CONFIGURATION=${DJANGO_CONFIGURATION}
# Install necessary apt packages

@ -1,10 +1,11 @@
# Computer Vision Annotation Tool (CVAT)
[![CI](https://github.com/openvinotoolkit/cvat/workflows/CI/badge.svg?branch=develop)](https://github.com/openvinotoolkit/cvat/actions)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/b9899c72f2764df0b5d26390cb872e21)](https://app.codacy.com/gh/openvinotoolkit/cvat?utm_source=github.com&utm_medium=referral&utm_content=openvinotoolkit/cvat&utm_campaign=Badge_Grade_Dashboard)
[![Gitter chat](https://badges.gitter.im/opencv-cvat/gitter.png)](https://gitter.im/opencv-cvat)
[![Coverage Status](https://coveralls.io/repos/github/openvinotoolkit/cvat/badge.svg?branch=develop)](https://coveralls.io/github/openvinotoolkit/cvat?branch=develop)
[![DOI](https://zenodo.org/badge/139156354.svg)](https://zenodo.org/badge/latestdoi/139156354)
[![CI][ci-img]][ci-url]
[![Gitter chat][gitter-img]][gitter-url]
[![Coverage Status][coverage-img]][coverage-url]
[![server pulls][docker-server-pulls-img]][docker-server-image-url]
[![ui pulls][docker-ui-pulls-img]][docker-ui-image-url]
[![DOI][doi-img]][doi-url]
CVAT is free, online, interactive video and image annotation
tool for computer vision. It is being used by our team to
@ -57,7 +58,7 @@ For more information about supported formats look at the
| Segmentation masks from [PASCAL VOC](http://host.robots.ox.ac.uk/pascal/VOC/) | X | X |
| [YOLO](https://pjreddie.com/darknet/yolo/) | X | X |
| [MS COCO Object Detection](http://cocodataset.org/#format-data) | X | X |
| [TFrecord](https://www.tensorflow.org/tutorials/load_data/tf_records) | X | X |
| [TFrecord](https://www.tensorflow.org/tutorials/load_data/tfrecord) | X | X |
| [MOT](https://motchallenge.net/) | X | X |
| [LabelMe 3.0](http://labelme.csail.mit.edu/Release3.0) | X | X |
| [ImageNet](http://www.image-net.org) | X | X |
@ -159,6 +160,24 @@ Other ways to ask questions and get our support:
## Projects using CVAT
- [Onepanel](https://github.com/onepanelio/core) - Onepanel is an open source
- [Onepanel](https://github.com/onepanelio/core) is an open source
vision AI platform that fully integrates CVAT with scalable data processing
and parallelized training pipelines.
- [DataIsKey](https://dataiskey.eu/annotation-tool/) uses CVAT as their prime data labeling tool
to offer annotation services for projects of any size.
<!-- prettier-ignore-start -->
<!-- Badges -->
[docker-server-pulls-img]: https://img.shields.io/docker/pulls/openvino/cvat_server.svg?style=flat-square&label=server%20pulls
[docker-server-image-url]: https://hub.docker.com/r/openvino/cvat_server
[docker-ui-pulls-img]: https://img.shields.io/docker/pulls/openvino/cvat_ui.svg?style=flat-square&label=UI%20pulls
[docker-ui-image-url]: https://hub.docker.com/r/openvino/cvat_ui
[ci-img]: https://github.com/openvinotoolkit/cvat/workflows/CI/badge.svg?branch=develop
[ci-url]: https://github.com/openvinotoolkit/cvat/actions
[gitter-img]: https://badges.gitter.im/opencv-cvat/gitter.png
[gitter-url]: https://gitter.im/opencv-cvat
[coverage-img]: https://coveralls.io/repos/github/openvinotoolkit/cvat/badge.svg?branch=develop
[coverage-url]: https://coveralls.io/github/openvinotoolkit/cvat?branch=develop
[doi-img]: https://zenodo.org/badge/139156354.svg
[doi-url]: https://zenodo.org/badge/latestdoi/139156354

@ -1,6 +1,6 @@
{
"name": "cvat-canvas",
"version": "2.4.1",
"version": "2.4.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -1235,6 +1235,12 @@
"integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==",
"dev": true
},
"@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@ -1670,13 +1676,113 @@
"dev": true
},
"array-includes": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
"integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=",
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz",
"integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==",
"dev": true,
"requires": {
"define-properties": "^1.1.2",
"es-abstract": "^1.7.0"
"call-bind": "^1.0.2",
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.2",
"get-intrinsic": "^1.1.1",
"is-string": "^1.0.5"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz",
"integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"get-intrinsic": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.2",
"is-callable": "^1.2.3",
"is-negative-zero": "^2.0.1",
"is-regex": "^1.1.2",
"is-string": "^1.0.5",
"object-inspect": "^1.9.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.2",
"string.prototype.trimend": "^1.0.4",
"string.prototype.trimstart": "^1.0.4",
"unbox-primitive": "^1.0.0"
}
},
"es-to-primitive": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
"integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
"dev": true,
"requires": {
"is-callable": "^1.1.4",
"is-date-object": "^1.0.1",
"is-symbol": "^1.0.2"
}
},
"has-symbols": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
"dev": true
},
"is-callable": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
"integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==",
"dev": true
},
"is-regex": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz",
"integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"has-symbols": "^1.0.1"
}
},
"object-inspect": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.2.tgz",
"integrity": "sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA==",
"dev": true
},
"object.assign": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
"integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
"dev": true,
"requires": {
"call-bind": "^1.0.0",
"define-properties": "^1.1.3",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
}
},
"string.prototype.trimend": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
"integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
}
},
"string.prototype.trimstart": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
"integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
}
}
}
},
"array-union": {
@ -1700,6 +1806,114 @@
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true
},
"array.prototype.flat": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz",
"integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==",
"dev": true,
"requires": {
"call-bind": "^1.0.0",
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz",
"integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"get-intrinsic": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.2",
"is-callable": "^1.2.3",
"is-negative-zero": "^2.0.1",
"is-regex": "^1.1.2",
"is-string": "^1.0.5",
"object-inspect": "^1.9.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.2",
"string.prototype.trimend": "^1.0.4",
"string.prototype.trimstart": "^1.0.4",
"unbox-primitive": "^1.0.0"
}
},
"es-to-primitive": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
"integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
"dev": true,
"requires": {
"is-callable": "^1.1.4",
"is-date-object": "^1.0.1",
"is-symbol": "^1.0.2"
}
},
"has-symbols": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
"dev": true
},
"is-callable": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
"integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==",
"dev": true
},
"is-regex": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz",
"integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"has-symbols": "^1.0.1"
}
},
"object-inspect": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.2.tgz",
"integrity": "sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA==",
"dev": true
},
"object.assign": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
"integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
"dev": true,
"requires": {
"call-bind": "^1.0.0",
"define-properties": "^1.1.3",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
}
},
"string.prototype.trimend": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
"integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
}
},
"string.prototype.trimstart": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
"integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
}
}
}
},
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
@ -2230,6 +2444,16 @@
}
}
},
"call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"dev": true,
"requires": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
}
},
"caller-callsite": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
@ -3542,22 +3766,34 @@
"dev": true
},
"eslint-import-resolver-node": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz",
"integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==",
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz",
"integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==",
"dev": true,
"requires": {
"debug": "^2.6.9",
"resolve": "^1.5.0"
"resolve": "^1.13.1"
},
"dependencies": {
"resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
"dev": true,
"requires": {
"is-core-module": "^2.2.0",
"path-parse": "^1.0.6"
}
}
}
},
"eslint-module-utils": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz",
"integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==",
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz",
"integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==",
"dev": true,
"requires": {
"debug": "^2.6.8",
"debug": "^2.6.9",
"pkg-dir": "^2.0.0"
},
"dependencies": {
@ -3616,22 +3852,24 @@
}
},
"eslint-plugin-import": {
"version": "2.18.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz",
"integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==",
"version": "2.22.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz",
"integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==",
"dev": true,
"requires": {
"array-includes": "^3.0.3",
"array-includes": "^3.1.1",
"array.prototype.flat": "^1.2.3",
"contains-path": "^0.1.0",
"debug": "^2.6.9",
"doctrine": "1.5.0",
"eslint-import-resolver-node": "^0.3.2",
"eslint-module-utils": "^2.4.0",
"eslint-import-resolver-node": "^0.3.4",
"eslint-module-utils": "^2.6.0",
"has": "^1.0.3",
"minimatch": "^3.0.4",
"object.values": "^1.1.0",
"object.values": "^1.1.1",
"read-pkg-up": "^2.0.0",
"resolve": "^1.11.0"
"resolve": "^1.17.0",
"tsconfig-paths": "^3.9.0"
},
"dependencies": {
"doctrine": {
@ -3643,6 +3881,16 @@
"esutils": "^2.0.2",
"isarray": "^1.0.0"
}
},
"resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
"dev": true,
"requires": {
"is-core-module": "^2.2.0",
"path-parse": "^1.0.6"
}
}
}
},
@ -4780,6 +5028,25 @@
"globule": "^1.0.0"
}
},
"get-intrinsic": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
"dev": true,
"requires": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1"
},
"dependencies": {
"has-symbols": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
"dev": true
}
}
},
"get-stdin": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-0.1.0.tgz",
@ -5018,6 +5285,12 @@
"ansi-regex": "^2.0.0"
}
},
"has-bigints": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
"integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==",
"dev": true
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@ -5084,13 +5357,10 @@
}
},
"hosted-git-info": {
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.2.tgz",
"integrity": "sha512-CyjlXII6LMsPMyUzxpTt8fzh5QwzGqPmQXgY/Jyf4Zfp27t/FvfhwoE/8laaMUcMy816CkWF20I7NeQhwwY88w==",
"dev": true,
"requires": {
"lru-cache": "^5.1.1"
}
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
"dev": true
},
"hpack.js": {
"version": "2.1.6",
@ -5433,6 +5703,12 @@
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
"dev": true
},
"is-bigint": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz",
"integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==",
"dev": true
},
"is-binary-path": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
@ -5442,6 +5718,15 @@
"binary-extensions": "^1.0.0"
}
},
"is-boolean-object": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz",
"integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==",
"dev": true,
"requires": {
"call-bind": "^1.0.0"
}
},
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
@ -5463,6 +5748,15 @@
"ci-info": "^2.0.0"
}
},
"is-core-module": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.3.0.tgz",
"integrity": "sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==",
"dev": true,
"requires": {
"has": "^1.0.3"
}
},
"is-data-descriptor": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
@ -5565,6 +5859,12 @@
}
}
},
"is-negative-zero": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz",
"integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==",
"dev": true
},
"is-npm": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz",
@ -5591,6 +5891,12 @@
}
}
},
"is-number-object": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz",
"integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==",
"dev": true
},
"is-obj": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
@ -5651,6 +5957,12 @@
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
"dev": true
},
"is-string": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
"integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
"dev": true
},
"is-symbol": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
@ -5942,9 +6254,9 @@
}
},
"lodash": {
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"lodash._reinterpolate": {
@ -6009,15 +6321,6 @@
"integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
"dev": true
},
"lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"requires": {
"yallist": "^3.0.2"
}
},
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
@ -6811,15 +7114,112 @@
}
},
"object.values": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
"integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz",
"integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3",
"es-abstract": "^1.12.0",
"function-bind": "^1.1.1",
"es-abstract": "^1.18.0-next.2",
"has": "^1.0.3"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz",
"integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"get-intrinsic": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.2",
"is-callable": "^1.2.3",
"is-negative-zero": "^2.0.1",
"is-regex": "^1.1.2",
"is-string": "^1.0.5",
"object-inspect": "^1.9.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.2",
"string.prototype.trimend": "^1.0.4",
"string.prototype.trimstart": "^1.0.4",
"unbox-primitive": "^1.0.0"
}
},
"es-to-primitive": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
"integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
"dev": true,
"requires": {
"is-callable": "^1.1.4",
"is-date-object": "^1.0.1",
"is-symbol": "^1.0.2"
}
},
"has-symbols": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
"dev": true
},
"is-callable": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz",
"integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==",
"dev": true
},
"is-regex": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz",
"integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"has-symbols": "^1.0.1"
}
},
"object-inspect": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.2.tgz",
"integrity": "sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA==",
"dev": true
},
"object.assign": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
"integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
"dev": true,
"requires": {
"call-bind": "^1.0.0",
"define-properties": "^1.1.3",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
}
},
"string.prototype.trimend": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
"integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
}
},
"string.prototype.trimstart": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
"integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
}
}
}
},
"obuf": {
@ -9582,6 +9982,18 @@
"glob": "^7.1.2"
}
},
"tsconfig-paths": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
"integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==",
"dev": true,
"requires": {
"@types/json5": "^0.0.29",
"json5": "^1.0.1",
"minimist": "^1.2.0",
"strip-bom": "^3.0.0"
}
},
"tslib": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
@ -9652,6 +10064,26 @@
"integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==",
"dev": true
},
"unbox-primitive": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
"integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1",
"has-bigints": "^1.0.1",
"has-symbols": "^1.0.2",
"which-boxed-primitive": "^1.0.2"
},
"dependencies": {
"has-symbols": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
"dev": true
}
}
},
"undefsafe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz",
@ -9874,9 +10306,9 @@
}
},
"url-parse": {
"version": "1.4.7",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz",
"integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
"dev": true,
"requires": {
"querystringify": "^2.1.1",
@ -10248,9 +10680,9 @@
}
},
"yargs-parser": {
"version": "13.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
"integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
"version": "13.1.2",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
@ -10416,6 +10848,36 @@
"isexe": "^2.0.0"
}
},
"which-boxed-primitive": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
"integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
"dev": true,
"requires": {
"is-bigint": "^1.0.1",
"is-boolean-object": "^1.1.0",
"is-number-object": "^1.0.4",
"is-string": "^1.0.5",
"is-symbol": "^1.0.3"
},
"dependencies": {
"has-symbols": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
"integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
"dev": true
},
"is-symbol": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
"integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
"dev": true,
"requires": {
"has-symbols": "^1.0.1"
}
}
}
},
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
@ -10534,12 +10996,6 @@
"integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
"dev": true
},
"yallist": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
"dev": true
},
"yargs": {
"version": "13.3.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",

@ -1,6 +1,6 @@
{
"name": "cvat-canvas",
"version": "2.4.1",
"version": "2.4.3",
"description": "Part of Computer Vision Annotation Tool which presents its canvas library",
"main": "src/canvas.ts",
"scripts": {
@ -32,7 +32,7 @@
"eslint": "^6.1.0",
"eslint-config-airbnb-typescript": "^4.0.1",
"eslint-config-typescript-recommended": "^1.4.17",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-import": "^2.22.1",
"node-sass": "^4.14.1",
"nodemon": "^2.0.7",
"postcss-loader": "^3.0.0",

@ -1,4 +1,4 @@
// Copyright (C) 2019-2020 Intel Corporation
// Copyright (C) 2019-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -1175,7 +1175,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
}
} else if (reason === UpdateReasons.IMAGE_MOVED) {
this.moveCanvas();
} else if ([UpdateReasons.OBJECTS_UPDATED].includes(reason)) {
} else if (reason === UpdateReasons.OBJECTS_UPDATED) {
if (this.mode === Mode.GROUP) {
this.groupHandler.resetSelectedObjects();
}
@ -1443,6 +1443,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
clientID: state.clientID,
outside: state.outside,
occluded: state.occluded,
source: state.source,
hidden: state.hidden,
lock: state.lock,
shapeType: state.shapeType,
@ -1534,13 +1535,22 @@ export class CanvasViewImpl implements CanvasView, Listener {
}
}
for (const attrID of Object.keys(state.attributes)) {
if (state.attributes[attrID] !== drawnState.attributes[+attrID]) {
if (text) {
const [span] = (text.node.querySelectorAll(`[attrID="${attrID}"]`) as any) as SVGTSpanElement[];
if (span && span.textContent) {
const prefix = span.textContent.split(':').slice(0, -1).join(':');
span.textContent = `${prefix}: ${state.attributes[attrID]}`;
if (drawnState.label.id !== state.label.id) {
// need to remove created text and create it again
if (text) {
text.remove();
this.svgTexts[state.clientID] = this.addText(state);
}
} else {
// check if there are updates in attributes
for (const attrID of Object.keys(state.attributes)) {
if (state.attributes[attrID] !== drawnState.attributes[+attrID]) {
if (text) {
const [span] = text.node.querySelectorAll<SVGTSpanElement>(`[attrID="${attrID}"]`);
if (span && span.textContent) {
const prefix = span.textContent.split(':').slice(0, -1).join(':');
span.textContent = `${prefix}: ${state.attributes[attrID]}`;
}
}
}
}

@ -32,6 +32,11 @@ export interface DrawHandler {
cancel(): void;
}
interface FinalCoordinates {
points: number[];
box: Box;
}
export class DrawHandlerImpl implements DrawHandler {
// callback is used to notify about creating new shape
private onDrawDone: (data: object | null, duration?: number, continueDraw?: boolean) => void;
@ -73,19 +78,14 @@ export class DrawHandlerImpl implements DrawHandler {
return [xtl, ytl, xbr, ybr];
}
private getFinalPolyshapeCoordinates(
targetPoints: number[],
): {
points: number[];
box: Box;
} {
private getFinalPolyshapeCoordinates(targetPoints: number[]): FinalCoordinates {
const { offset } = this.geometry;
let points = targetPoints.map((coord: number): number => coord - offset);
const box = {
xtl: Number.MAX_SAFE_INTEGER,
ytl: Number.MAX_SAFE_INTEGER,
xbr: Number.MAX_SAFE_INTEGER,
ybr: Number.MAX_SAFE_INTEGER,
xbr: Number.MIN_SAFE_INTEGER,
ybr: Number.MIN_SAFE_INTEGER,
};
const frameWidth = this.geometry.image.width;
@ -96,9 +96,9 @@ export class DrawHandlerImpl implements DrawHandler {
Vertical,
}
const isBetween = (x1: number, x2: number, c: number): boolean => (
c >= Math.min(x1, x2) && c <= Math.max(x1, x2)
);
function isBetween(x1: number, x2: number, c: number): boolean {
return c >= Math.min(x1, x2) && c <= Math.max(x1, x2);
}
const isInsideFrame = (p: Point, direction: Direction): boolean => {
if (direction === Direction.Horizontal) {
@ -121,22 +121,35 @@ export class DrawHandlerImpl implements DrawHandler {
const findIntersectionsWithFrameBorders = (p1: Point, p2: Point, direction: Direction): number[] => {
const resultPoints = [];
const leftLine = [
{ x: 0, y: 0 },
{ x: 0, y: frameHeight },
];
const topLine = [
{ x: frameWidth, y: 0 },
{ x: 0, y: 0 },
];
const rightLine = [
{ x: frameWidth, y: frameHeight },
{ x: frameWidth, y: 0 },
];
const bottomLine = [
{ x: 0, y: frameHeight },
{ x: frameWidth, y: frameHeight },
];
if (direction === Direction.Horizontal) {
resultPoints.push(...findInersection(p1, p2, { x: 0, y: 0 }, { x: 0, y: frameHeight }));
resultPoints.push(
...findInersection(p1, p2, { x: frameWidth, y: frameHeight }, { x: frameWidth, y: 0 }),
);
resultPoints.push(...findInersection(p1, p2, leftLine[0], leftLine[1]));
resultPoints.push(...findInersection(p1, p2, rightLine[0], rightLine[1]));
} else {
resultPoints.push(
...findInersection(p1, p2, { x: 0, y: frameHeight }, { x: frameWidth, y: frameHeight }),
);
resultPoints.push(...findInersection(p1, p2, { x: frameWidth, y: 0 }, { x: 0, y: 0 }));
resultPoints.push(...findInersection(p1, p2, bottomLine[0], bottomLine[1]));
resultPoints.push(...findInersection(p1, p2, topLine[0], topLine[1]));
}
if (resultPoints.length === 4) {
if (
Math.sign(resultPoints[0] - resultPoints[2]) !== Math.sign(p1.x - p2.x)
&& Math.sign(resultPoints[1] - resultPoints[3]) !== Math.sign(p1.y - p2.y)
(p1.x === p2.x || Math.sign(resultPoints[0] - resultPoints[2]) !== Math.sign(p1.x - p2.x))
&& (p1.y === p2.y || Math.sign(resultPoints[1] - resultPoints[3]) !== Math.sign(p1.y - p2.y))
) {
[resultPoints[0], resultPoints[2]] = [resultPoints[2], resultPoints[0]];
[resultPoints[1], resultPoints[3]] = [resultPoints[3], resultPoints[1]];
@ -145,24 +158,23 @@ export class DrawHandlerImpl implements DrawHandler {
return resultPoints;
};
const crop = (polygonPoints: number[], direction: Direction): number[] => {
const crop = (shapePoints: number[], direction: Direction): number[] => {
const resultPoints = [];
for (let i = 0; i < polygonPoints.length - 1; i += 2) {
const curPoint = { x: polygonPoints[i], y: polygonPoints[i + 1] };
const isPolyline = this.drawData.shapeType === 'polyline';
const isPolygon = this.drawData.shapeType === 'polygon';
for (let i = 0; i < shapePoints.length - 1; i += 2) {
const curPoint = { x: shapePoints[i], y: shapePoints[i + 1] };
if (isInsideFrame(curPoint, direction)) {
resultPoints.push(polygonPoints[i], polygonPoints[i + 1]);
resultPoints.push(shapePoints[i], shapePoints[i + 1]);
}
const isLastPoint = i === polygonPoints.length - 2;
if (
isLastPoint
&& (this.drawData.shapeType === 'polyline'
|| (this.drawData.shapeType === 'polygon' && polygonPoints.length === 4))
) {
const isLastPoint = i === shapePoints.length - 2;
if (isLastPoint && (isPolyline || (isPolygon && shapePoints.length === 4))) {
break;
}
const nextPoint = isLastPoint
? { x: polygonPoints[0], y: polygonPoints[1] }
: { x: polygonPoints[i + 2], y: polygonPoints[i + 3] };
? { x: shapePoints[0], y: shapePoints[1] }
: { x: shapePoints[i + 2], y: shapePoints[i + 3] };
const intersectionPoints = findIntersectionsWithFrameBorders(curPoint, nextPoint, direction);
if (intersectionPoints.length !== 0) {
resultPoints.push(...intersectionPoints);
@ -187,20 +199,15 @@ export class DrawHandlerImpl implements DrawHandler {
};
}
private getFinalCuboidCoordinates(
targetPoints: number[],
): {
points: number[];
box: Box;
} {
private getFinalCuboidCoordinates(targetPoints: number[]): FinalCoordinates {
const { offset } = this.geometry;
let points = targetPoints;
const box = {
xtl: 0,
ytl: 0,
xbr: Number.MAX_SAFE_INTEGER,
ybr: Number.MAX_SAFE_INTEGER,
xtl: Number.MAX_SAFE_INTEGER,
ytl: Number.MAX_SAFE_INTEGER,
xbr: Number.MIN_SAFE_INTEGER,
ybr: Number.MIN_SAFE_INTEGER,
};
const frameWidth = this.geometry.image.width;
@ -238,27 +245,34 @@ export class DrawHandlerImpl implements DrawHandler {
if (cuboidOffsets.length === points.length / 2) {
cuboidOffsets.forEach((offsetCoords: number[]): void => {
if (Math.sqrt((offsetCoords[0] ** 2) + (offsetCoords[1] ** 2)) < minCuboidOffset.d) {
minCuboidOffset.d = Math.sqrt((offsetCoords[0] ** 2) + (offsetCoords[1] ** 2));
const dx = offsetCoords[0] ** 2;
const dy = offsetCoords[1] ** 2;
if (Math.sqrt(dx + dy) < minCuboidOffset.d) {
minCuboidOffset.d = Math.sqrt(dx + dy);
[minCuboidOffset.dx, minCuboidOffset.dy] = offsetCoords;
}
});
points = points.map((coord: number, i: number): number => {
const finalCoord = coord + (i % 2 === 0 ? minCuboidOffset.dx : minCuboidOffset.dy);
if (i % 2 === 0) {
box.xtl = Math.max(box.xtl, finalCoord);
box.xbr = Math.min(box.xbr, finalCoord);
} else {
box.ytl = Math.max(box.ytl, finalCoord);
box.ybr = Math.min(box.ybr, finalCoord);
if (i % 2) {
return coord + minCuboidOffset.dy;
}
return finalCoord;
return coord + minCuboidOffset.dx;
});
}
points.forEach((coord: number, i: number): number => {
if (i % 2 === 0) {
box.xtl = Math.min(box.xtl, coord);
box.xbr = Math.max(box.xbr, coord);
} else {
box.ytl = Math.min(box.ytl, coord);
box.ybr = Math.max(box.ybr, coord);
}
return coord;
});
return {
points: points.map((coord: number): number => coord - offset),
box,
@ -449,8 +463,9 @@ export class DrawHandlerImpl implements DrawHandler {
} else {
this.drawInstance.draw('update', e);
const deltaTreshold = 15;
const delta = Math.sqrt(((e.clientX - lastDrawnPoint.x) ** 2)
+ ((e.clientY - lastDrawnPoint.y) ** 2));
const dx = (e.clientX - lastDrawnPoint.x) ** 2;
const dy = (e.clientY - lastDrawnPoint.y) ** 2;
const delta = Math.sqrt(dx + dy);
if (delta > deltaTreshold) {
this.drawInstance.draw('point', e);
}
@ -482,36 +497,15 @@ export class DrawHandlerImpl implements DrawHandler {
&& (box.xbr - box.xtl) * (box.ybr - box.ytl) >= consts.AREA_THRESHOLD
&& points.length >= 3 * 2
) {
this.onDrawDone(
{
clientID,
shapeType,
points,
},
Date.now() - this.startTimestamp,
);
this.onDrawDone({ clientID, shapeType, points }, Date.now() - this.startTimestamp);
} else if (
shapeType === 'polyline'
&& (box.xbr - box.xtl >= consts.SIZE_THRESHOLD || box.ybr - box.ytl >= consts.SIZE_THRESHOLD)
&& points.length >= 2 * 2
) {
this.onDrawDone(
{
clientID,
shapeType,
points,
},
Date.now() - this.startTimestamp,
);
this.onDrawDone({ clientID, shapeType, points }, Date.now() - this.startTimestamp);
} else if (shapeType === 'points' && (e.target as any).getAttribute('points') !== '0,0') {
this.onDrawDone(
{
clientID,
shapeType,
points,
},
Date.now() - this.startTimestamp,
);
this.onDrawDone({ clientID, shapeType, points }, Date.now() - this.startTimestamp);
// TODO: think about correct constraign for cuboids
} else if (shapeType === 'cuboid' && points.length === 4 * 2) {
this.onDrawDone(

@ -41,6 +41,7 @@ export interface DrawnState {
occluded?: boolean;
hidden?: boolean;
lock: boolean;
source: 'AUTO' | 'MANUAL';
shapeType: string;
points?: number[];
attributes: Record<number, string>;
@ -176,5 +177,7 @@ export function scalarProduct(a: Vector2D, b: Vector2D): number {
}
export function vectorLength(vector: Vector2D): number {
return Math.sqrt((vector.i ** 2) + (vector.j ** 2));
const sqrI = vector.i ** 2;
const sqrJ = vector.j ** 2;
return Math.sqrt(sqrI + sqrJ);
}

@ -3,7 +3,7 @@
"baseUrl": ".",
"emitDeclarationOnly": true,
"module": "es6",
"target": "es6",
"target": "es2016",
"noImplicitAny": true,
"preserveConstEnums": true,
"declaration": true,

@ -5,21 +5,28 @@
"requires": true,
"dependencies": {
"@babel/cli": {
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.5.5.tgz",
"integrity": "sha512-UHI+7pHv/tk9g6WXQKYz+kmXTI77YtuY3vqC59KIqcoWEjsJJSG6rAxKaLsgj3LDyadsPrCB929gVOKM6Hui0w==",
"version": "7.13.16",
"resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.13.16.tgz",
"integrity": "sha512-cL9tllhqvsQ6r1+d9Invf7nNXg/3BlfL1vvvL/AdH9fZ2l5j0CeBcoq6UjsqHpvyN1v5nXSZgqJZoGeK+ZOAbw==",
"dev": true,
"requires": {
"chokidar": "^2.0.4",
"commander": "^2.8.1",
"@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents",
"chokidar": "^3.4.0",
"commander": "^4.0.1",
"convert-source-map": "^1.1.0",
"fs-readdir-recursive": "^1.1.0",
"glob": "^7.0.0",
"lodash": "^4.17.13",
"mkdirp": "^0.5.1",
"output-file-sync": "^2.0.0",
"make-dir": "^2.1.0",
"slash": "^2.0.0",
"source-map": "^0.5.0"
},
"dependencies": {
"commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
"dev": true
}
}
},
"@babel/code-frame": {
@ -69,6 +76,14 @@
"dev": true,
"requires": {
"minimist": "^1.2.0"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
}
}
},
"ms": {
@ -1138,6 +1153,26 @@
"integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==",
"dev": true
},
"@nicolo-ribaudo/chokidar-2": {
"version": "2.1.8-no-fsevents",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz",
"integrity": "sha512-+nb9vWloHNNMFHjGofEam3wopE3m1yuambrrd/fnPc+lFOMB9ROTqQlche9ByFWNkdNqfSgR/kkQtQ8DzEWt2w==",
"dev": true,
"optional": true,
"requires": {
"anymatch": "^2.0.0",
"async-each": "^1.0.1",
"braces": "^2.3.2",
"glob-parent": "^3.1.0",
"inherits": "^2.0.3",
"is-binary-path": "^1.0.0",
"is-glob": "^4.0.0",
"normalize-path": "^3.0.0",
"path-is-absolute": "^1.0.0",
"readdirp": "^2.2.1",
"upath": "^1.1.1"
}
},
"@types/detect-indent": {
"version": "0.1.30",
"resolved": "https://registry.npmjs.org/@types/detect-indent/-/detect-indent-0.1.30.tgz",
@ -2383,24 +2418,114 @@
"dev": true
},
"chokidar": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz",
"integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==",
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
"integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==",
"dev": true,
"optional": true,
"requires": {
"anymatch": "^2.0.0",
"async-each": "^1.0.1",
"braces": "^2.3.2",
"fsevents": "^1.2.7",
"glob-parent": "^3.1.0",
"inherits": "^2.0.3",
"is-binary-path": "^1.0.0",
"is-glob": "^4.0.0",
"normalize-path": "^3.0.0",
"path-is-absolute": "^1.0.0",
"readdirp": "^2.2.1",
"upath": "^1.1.1"
"anymatch": "~3.1.1",
"braces": "~3.0.2",
"fsevents": "~2.3.1",
"glob-parent": "~5.1.0",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.5.0"
},
"dependencies": {
"anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"dev": true,
"optional": true,
"requires": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
}
},
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true,
"optional": true
},
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"optional": true,
"requires": {
"fill-range": "^7.0.1"
}
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"optional": true,
"requires": {
"to-regex-range": "^5.0.1"
}
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"optional": true
},
"glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"optional": true,
"requires": {
"is-glob": "^4.0.1"
}
},
"is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"optional": true,
"requires": {
"binary-extensions": "^2.0.0"
}
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"optional": true
},
"readdirp": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
"integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
"dev": true,
"optional": true,
"requires": {
"picomatch": "^2.2.1"
}
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"optional": true,
"requires": {
"is-number": "^7.0.0"
}
}
}
},
"chownr": {
@ -3410,24 +3535,24 @@
"dev": true
},
"elliptic": {
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
"integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
"dev": true,
"requires": {
"bn.js": "^4.4.0",
"brorand": "^1.0.1",
"bn.js": "^4.11.9",
"brorand": "^1.1.0",
"hash.js": "^1.0.0",
"hmac-drbg": "^1.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"minimalistic-crypto-utils": "^1.0.0"
"hmac-drbg": "^1.0.1",
"inherits": "^2.0.4",
"minimalistic-assert": "^1.0.1",
"minimalistic-crypto-utils": "^1.0.1"
},
"dependencies": {
"bn.js": {
"version": "4.11.9",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
"integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
"dev": true
}
}
@ -5868,12 +5993,6 @@
"path-is-inside": "^1.0.2"
}
},
"is-plain-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
"integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
"dev": true
},
"is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@ -6077,9 +6196,9 @@
"dev": true
},
"kind-of": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
"dev": true
},
"latest-version": {
@ -6158,9 +6277,9 @@
}
},
"lodash": {
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"lodash._reinterpolate": {
@ -6511,9 +6630,9 @@
}
},
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mississippi": {
@ -6556,20 +6675,12 @@
}
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "0.0.8"
},
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
}
"minimist": "^1.2.5"
}
},
"move-concurrently": {
@ -7264,17 +7375,6 @@
"os-tmpdir": "^1.0.0"
}
},
"output-file-sync": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-2.0.1.tgz",
"integrity": "sha512-mDho4qm7WgIXIGf4eYU1RHN2UU5tPfVYVSRwDJw0uTmj35DQUt/eNp19N7v6T3SrR0ESTEf2up2CGO73qI35zQ==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.11",
"is-plain-obj": "^1.1.0",
"mkdirp": "^0.5.1"
}
},
"p-defer": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
@ -9441,9 +9541,9 @@
}
},
"ssri": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
"integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
"integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
"dev": true,
"requires": {
"figgy-pudding": "^3.5.1"
@ -10337,9 +10437,9 @@
}
},
"url-parse": {
"version": "1.4.7",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz",
"integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
"dev": true,
"requires": {
"querystringify": "^2.1.1",
@ -10770,9 +10870,9 @@
}
},
"yargs-parser": {
"version": "13.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
"integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
"version": "13.1.2",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
@ -10782,9 +10882,9 @@
}
},
"webpack-dev-middleware": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz",
"integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==",
"version": "3.7.3",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz",
"integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==",
"dev": true,
"requires": {
"memory-fs": "^0.4.1",
@ -10795,9 +10895,9 @@
},
"dependencies": {
"mime": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
"integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==",
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz",
"integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==",
"dev": true
}
}
@ -11025,9 +11125,9 @@
"dev": true
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"dev": true
},
"yallist": {

@ -10,7 +10,7 @@
"author": "Intel",
"license": "MIT",
"devDependencies": {
"@babel/cli": "^7.5.5",
"@babel/cli": "^7.13.16",
"@babel/core": "^7.5.5",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-proposal-optional-chaining": "^7.11.0",

@ -1,6 +1,6 @@
{
"name": "cvat-core",
"version": "3.12.0",
"version": "3.12.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -5051,7 +5051,7 @@
"cvat-data": {
"version": "file:../cvat-data",
"requires": {
"async-mutex": "^0.3.0",
"async-mutex": "^0.3.1",
"jszip": "3.6.0"
},
"dependencies": {
@ -6164,17 +6164,17 @@
"integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ=="
},
"async-mutex": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.2.6.tgz",
"integrity": "sha512-Hs4R+4SPgamu6rSGW8C7cV9gaWUKEHykfzCCvIRuaVv636Ju10ZdeUbvb4TBEW0INuq2DHZqXbK4Nd3yG4RaRw==",
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.1.tgz",
"integrity": "sha512-vRfQwcqBnJTLzVQo72Sf7KIUbcSUP5hNchx6udI1U6LuPQpfePgdjJzlCe76yFZ8pxlLjn9lwcl/Ya0TSOv0Tw==",
"requires": {
"tslib": "^2.0.0"
"tslib": "^2.1.0"
},
"dependencies": {
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
}
}
},
@ -9018,9 +9018,9 @@
"integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk="
},
"jszip": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz",
"integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==",
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.6.0.tgz",
"integrity": "sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ==",
"requires": {
"lie": "~3.3.0",
"pako": "~1.0.2",
@ -11986,24 +11986,24 @@
"dev": true
},
"elliptic": {
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
"integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
"dev": true,
"requires": {
"bn.js": "^4.4.0",
"brorand": "^1.0.1",
"bn.js": "^4.11.9",
"brorand": "^1.1.0",
"hash.js": "^1.0.0",
"hmac-drbg": "^1.0.0",
"inherits": "^2.0.1",
"minimalistic-assert": "^1.0.0",
"minimalistic-crypto-utils": "^1.0.0"
"hmac-drbg": "^1.0.1",
"inherits": "^2.0.4",
"minimalistic-assert": "^1.0.1",
"minimalistic-crypto-utils": "^1.0.1"
},
"dependencies": {
"bn.js": {
"version": "4.11.9",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
"integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
"dev": true
}
}
@ -18247,9 +18247,9 @@
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
},
"jsdoc": {
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.5.tgz",
"integrity": "sha512-SbY+i9ONuxSK35cgVHaI8O9senTE4CDYAmGSDJ5l3+sfe62Ff4gy96osy6OW84t4K4A8iGnMrlRrsSItSNp3RQ==",
"version": "3.6.6",
"resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.6.tgz",
"integrity": "sha512-znR99e1BHeyEkSvgDDpX0sTiTu+8aQyDl9DawrkOGZTTW8hv0deIFXx87114zJ7gRaDZKVQD/4tr1ifmJp9xhQ==",
"dev": true,
"requires": {
"@babel/parser": "^7.9.4",
@ -18279,12 +18279,6 @@
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true
},
"underscore": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz",
"integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==",
"dev": true
}
}
},
@ -18344,9 +18338,9 @@
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
},
"json-logic-js": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/json-logic-js/-/json-logic-js-2.0.0.tgz",
"integrity": "sha512-cQBDOXgFtFladCg99wnQ7YfN+nv1+Sznj4K6bp3CTgDJNJKgEXJE2VCXzVBjEU2e1UagDHSek52IQk5Ha38n7Q=="
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/json-logic-js/-/json-logic-js-2.0.1.tgz",
"integrity": "sha512-J3hhqM4IY66sL8qyzU7cwLmTAt3kA6ZsYxyuZBEwhcc+OYPTmAHc64fBTXHT6K5RwFeUqJUX1tfO7wpKsUx+9A=="
},
"json-parse-better-errors": {
"version": "1.0.2",
@ -18520,9 +18514,9 @@
}
},
"lodash": {
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash.sortby": {
"version": "4.7.0",
@ -20379,9 +20373,9 @@
}
},
"ssri": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
"integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
"integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
"dev": true,
"requires": {
"figgy-pudding": "^3.5.1"
@ -20409,14 +20403,6 @@
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz",
"integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA=="
},
"static-eval": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz",
"integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==",
"requires": {
"escodegen": "^1.8.1"
}
},
"static-extend": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
@ -20948,9 +20934,10 @@
"dev": true
},
"underscore": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz",
"integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk="
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz",
"integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==",
"dev": true
},
"unicode-canonical-property-names-ecmascript": {
"version": "1.0.4",
@ -21582,9 +21569,9 @@
"dev": true
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
},
"yallist": {
"version": "3.1.1",

@ -1,6 +1,6 @@
{
"name": "cvat-core",
"version": "3.12.0",
"version": "3.12.1",
"description": "Part of Computer Vision Tool which presents an interface for client-side integration",
"main": "babel.config.js",
"scripts": {
@ -22,13 +22,13 @@
"eslint": "6.1.0",
"eslint-config-airbnb-base": "14.0.0",
"eslint-plugin-import": "2.18.2",
"eslint-plugin-jest": "^24.1.0",
"eslint-plugin-no-unsafe-innerhtml": "^1.0.16",
"eslint-plugin-no-unsanitized": "^3.0.2",
"eslint-plugin-security": "^1.4.0",
"eslint-plugin-jest": "^24.1.0",
"jest": "^26.6.3",
"jest-junit": "^6.4.0",
"jsdoc": "^3.6.4",
"jsdoc": "^3.6.6",
"webpack": "^4.31.0",
"webpack-cli": "^3.3.2"
},
@ -40,7 +40,7 @@
"error-stack-parser": "^2.0.2",
"form-data": "^2.5.0",
"jest-config": "^26.6.3",
"json-logic-js": "^2.0.0",
"json-logic-js": "^2.0.1",
"js-cookie": "^2.2.0",
"platform": "^1.3.5",
"quickhull": "^1.0.3",

@ -573,7 +573,7 @@ function build() {
* @param {module:API.cvat.classes.Task} task task to be annotated
* @param {module:API.cvat.classes.MLModel} model model used to get annotation
* @param {object} [args] extra arguments
* @returns {string} requestID
* @returns {object[]} annotations
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}

@ -286,7 +286,7 @@
if (nextChunkNumber * chunkSize < this.stopFrame) {
provider.setReadyToLoading(nextChunkNumber);
const nextStart = nextChunkNumber * chunkSize;
const nextStop = (nextChunkNumber + 1) * chunkSize - 1;
const nextStop = Math.min(this.stopFrame, (nextChunkNumber + 1) * chunkSize - 1);
if (!provider.isChunkCached(nextStart, nextStop)) {
if (!frameDataCache[this.tid].activeChunkRequest) {
frameDataCache[this.tid].activeChunkRequest = {

@ -33,6 +33,7 @@
created_date: undefined,
updated_date: undefined,
task_subsets: undefined,
training_project: undefined,
};
for (const property in data) {
@ -64,6 +65,9 @@
}
data.task_subsets = Array.from(subsetsSet);
}
if (typeof initialData.training_project === 'object') {
data.training_project = { ...initialData.training_project };
}
Object.defineProperties(
this,
@ -94,6 +98,7 @@
data.name = value;
},
},
/**
* @name status
* @type {module:API.cvat.enums.TaskStatus}
@ -217,6 +222,31 @@
subsets: {
get: () => [...data.task_subsets],
},
/**
* Training project associated with this annotation project
* This is a simple object which contains
* keys like host, username, password, enabled, project_class
* @name trainingProject
* @type {object}
* @memberof module:API.cvat.classes.Project
* @readonly
* @instance
*/
trainingProject: {
get: () => {
if (typeof data.training_project === 'object') {
return { ...data.training_project };
}
return data.training_project;
},
set: (updatedProject) => {
if (typeof training === 'object') {
data.training_project = { ...updatedProject };
} else {
data.training_project = updatedProject;
}
},
},
_internalData: {
get: () => data,
},
@ -261,7 +291,9 @@
};
Project.prototype.save.implementation = async function () {
const trainingProjectCopy = this.trainingProject;
if (typeof this.id !== 'undefined') {
// project has been already created, need to update some data
const projectData = {
name: this.name,
assignee_id: this.assignee ? this.assignee.id : null,
@ -269,10 +301,15 @@
labels: [...this._internalData.labels.map((el) => el.toJSON())],
};
if (trainingProjectCopy) {
projectData.training_project = trainingProjectCopy;
}
await serverProxy.projects.save(this.id, projectData);
return this;
}
// initial creating
const projectSpec = {
name: this.name,
labels: [...this.labels.map((el) => el.toJSON())],
@ -282,6 +319,10 @@
projectSpec.bug_tracker = this.bugTracker;
}
if (trainingProjectCopy) {
projectSpec.training_project = trainingProjectCopy;
}
const project = await serverProxy.projects.create(projectSpec);
return new Project(project);
};

@ -9,6 +9,31 @@
const config = require('./config');
const DownloadWorker = require('./download.worker');
function waitFor(frequencyHz, predicate) {
return new Promise((resolve, reject) => {
if (typeof predicate !== 'function') {
reject(new Error(`Predicate must be a function, got ${typeof predicate}`));
}
const internalWait = () => {
let result = false;
try {
result = predicate();
} catch (error) {
reject(error);
}
if (result) {
resolve();
} else {
setTimeout(internalWait, 1000 / frequencyHz);
}
};
setTimeout(internalWait);
});
}
function generateError(errorData) {
if (errorData.response) {
const message = `${errorData.message}. ${JSON.stringify(errorData.response.data) || ''}.`;
@ -993,6 +1018,96 @@
}
}
function predictorStatus(projectId) {
const { backendAPI } = config;
return new Promise((resolve, reject) => {
async function request() {
try {
const response = await Axios.get(`${backendAPI}/predict/status?project=${projectId}`);
return response.data;
} catch (errorData) {
throw generateError(errorData);
}
}
const timeoutCallback = async () => {
let data = null;
try {
data = await request();
if (data.status === 'queued') {
setTimeout(timeoutCallback, 1000);
} else if (data.status === 'done') {
resolve(data);
} else {
throw new Error(`Unknown status was received "${data.status}"`);
}
} catch (error) {
reject(error);
}
};
setTimeout(timeoutCallback);
});
}
function predictAnnotations(taskId, frame) {
return new Promise((resolve, reject) => {
const { backendAPI } = config;
async function request() {
try {
const response = await Axios.get(
`${backendAPI}/predict/frame?task=${taskId}&frame=${frame}`,
);
return response.data;
} catch (errorData) {
throw generateError(errorData);
}
}
const timeoutCallback = async () => {
let data = null;
try {
data = await request();
if (data.status === 'queued') {
setTimeout(timeoutCallback, 1000);
} else if (data.status === 'done') {
predictAnnotations.latestRequest.fetching = false;
resolve(data.annotation);
} else {
throw new Error(`Unknown status was received "${data.status}"`);
}
} catch (error) {
predictAnnotations.latestRequest.fetching = false;
reject(error);
}
};
const closureId = Date.now();
predictAnnotations.latestRequest.id = closureId;
const predicate = () => !predictAnnotations.latestRequest.fetching || predictAnnotations.latestRequest.id !== closureId;
if (predictAnnotations.latestRequest.fetching) {
waitFor(5, predicate).then(() => {
if (predictAnnotations.latestRequest.id !== closureId) {
resolve(null);
} else {
predictAnnotations.latestRequest.fetching = true;
setTimeout(timeoutCallback);
}
});
} else {
predictAnnotations.latestRequest.fetching = true;
setTimeout(timeoutCallback);
}
});
}
predictAnnotations.latestRequest = {
fetching: false,
id: null,
};
async function installedApps() {
const { backendAPI } = config;
try {
@ -1123,6 +1238,14 @@
}),
writable: false,
},
predictor: {
value: Object.freeze({
status: predictorStatus,
predict: predictAnnotations,
}),
writable: false,
},
}),
);
}

@ -10,7 +10,7 @@
const {
getFrame, getRanges, getPreview, clear: clearFrames, getContextImage,
} = require('./frames');
const { ArgumentError } = require('./exceptions');
const { ArgumentError, DataError } = require('./exceptions');
const { TaskStatus } = require('./enums');
const { Label } = require('./labels');
const User = require('./user');
@ -258,6 +258,19 @@
},
writable: true,
}),
predictor: Object.freeze({
value: {
async status() {
const result = await PluginRegistry.apiWrapper.call(this, prototype.predictor.status);
return result;
},
async predict(frame) {
const result = await PluginRegistry.apiWrapper.call(this, prototype.predictor.predict, frame);
return result;
},
},
writable: true,
}),
});
}
@ -665,6 +678,40 @@
* @instance
* @async
*/
/**
* @typedef {Object} PredictorStatus
* @property {string} message - message for a user to be displayed somewhere
* @property {number} projectScore - model accuracy
* @global
*/
/**
* Namespace is used for an interaction with events
* @namespace predictor
* @memberof Session
*/
/**
* Subscribe to updates of a ML model binded to the project
* @method status
* @memberof Session.predictor
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ServerError}
* @returns {PredictorStatus}
* @instance
* @async
*/
/**
* Get predictions from a ML model binded to the project
* @method predict
* @memberof Session.predictor
* @param {number} frame - number of frame to inference
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
* @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.DataError}
* @returns {object[] | null} annotations
* @instance
* @async
*/
}
}
@ -865,6 +912,11 @@
this.logger = {
log: Object.getPrototypeOf(this).logger.log.bind(this),
};
this.predictor = {
status: Object.getPrototypeOf(this).predictor.status.bind(this),
predict: Object.getPrototypeOf(this).predictor.predict.bind(this),
};
}
/**
@ -1554,6 +1606,11 @@
this.logger = {
log: Object.getPrototypeOf(this).logger.log.bind(this),
};
this.predictor = {
status: Object.getPrototypeOf(this).predictor.status.bind(this),
predict: Object.getPrototypeOf(this).predictor.predict.bind(this),
};
}
/**
@ -1741,6 +1798,11 @@
return rangesData;
};
Job.prototype.frames.preview.implementation = async function () {
const frameData = await getPreview(this.task.id);
return frameData;
};
// TODO: Check filter for annotations
Job.prototype.annotations.get.implementation = async function (frame, allTracks, filters) {
if (!Array.isArray(filters)) {
@ -1897,6 +1959,16 @@
return result;
};
Job.prototype.predictor.status.implementation = async function () {
const result = await this.task.predictor.status();
return result;
};
Job.prototype.predictor.predict.implementation = async function (frame) {
const result = await this.task.predictor.predict(frame);
return result;
};
Task.prototype.close.implementation = function closeTask() {
clearFrames(this.id);
for (const job of this.jobs) {
@ -2028,11 +2100,6 @@
return result;
};
Job.prototype.frames.preview.implementation = async function () {
const frameData = await getPreview(this.task.id);
return frameData;
};
Task.prototype.frames.ranges.implementation = async function () {
const rangesData = await getRanges(this.id);
return rangesData;
@ -2199,6 +2266,39 @@
return result;
};
Task.prototype.predictor.status.implementation = async function () {
if (!Number.isInteger(this.projectId)) {
throw new DataError('The task must belong to a project to use the feature');
}
const result = await serverProxy.predictor.status(this.projectId);
return {
message: result.message,
progress: result.progress,
projectScore: result.score,
timeRemaining: result.time_remaining,
mediaAmount: result.media_amount,
annotationAmount: result.annotation_amount,
};
};
Task.prototype.predictor.predict.implementation = async function (frame) {
if (!Number.isInteger(frame) || frame < 0) {
throw new ArgumentError(`Frame must be a positive integer. Got: "${frame}"`);
}
if (frame >= this.size) {
throw new ArgumentError(`The frame with number ${frame} is out of the task`);
}
if (!Number.isInteger(this.projectId)) {
throw new DataError('The task must belong to a project to use the feature');
}
const result = await serverProxy.predictor.predict(this.id, frame);
return result;
};
Job.prototype.frames.contextImage.implementation = async function (taskId, frameId) {
const result = await getContextImage(taskId, frameId);
return result;

@ -5,21 +5,137 @@
"requires": true,
"dependencies": {
"@babel/cli": {
"version": "7.6.4",
"resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.6.4.tgz",
"integrity": "sha512-tqrDyvPryBM6xjIyKKUwr3s8CzmmYidwgdswd7Uc/Cv0ogZcuS1TYQTLx/eWKP3UbJ6JxZAiYlBZabXm/rtRsQ==",
"version": "7.13.16",
"resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.13.16.tgz",
"integrity": "sha512-cL9tllhqvsQ6r1+d9Invf7nNXg/3BlfL1vvvL/AdH9fZ2l5j0CeBcoq6UjsqHpvyN1v5nXSZgqJZoGeK+ZOAbw==",
"dev": true,
"requires": {
"chokidar": "^2.1.8",
"commander": "^2.8.1",
"@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents",
"chokidar": "^3.4.0",
"commander": "^4.0.1",
"convert-source-map": "^1.1.0",
"fs-readdir-recursive": "^1.1.0",
"glob": "^7.0.0",
"lodash": "^4.17.13",
"mkdirp": "^0.5.1",
"output-file-sync": "^2.0.0",
"make-dir": "^2.1.0",
"slash": "^2.0.0",
"source-map": "^0.5.0"
},
"dependencies": {
"anymatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
"dev": true,
"optional": true,
"requires": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
}
},
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
"dev": true,
"optional": true
},
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"optional": true,
"requires": {
"fill-range": "^7.0.1"
}
},
"chokidar": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
"integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==",
"dev": true,
"optional": true,
"requires": {
"anymatch": "~3.1.1",
"braces": "~3.0.2",
"fsevents": "~2.3.1",
"glob-parent": "~5.1.0",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.5.0"
}
},
"commander": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
"dev": true
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"optional": true,
"requires": {
"to-regex-range": "^5.0.1"
}
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"optional": true
},
"glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"optional": true,
"requires": {
"is-glob": "^4.0.1"
}
},
"is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"optional": true,
"requires": {
"binary-extensions": "^2.0.0"
}
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"optional": true
},
"readdirp": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
"integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
"dev": true,
"optional": true,
"requires": {
"picomatch": "^2.2.1"
}
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"optional": true,
"requires": {
"is-number": "^7.0.0"
}
}
}
},
"@babel/code-frame": {
@ -831,6 +947,26 @@
"to-fast-properties": "^2.0.0"
}
},
"@nicolo-ribaudo/chokidar-2": {
"version": "2.1.8-no-fsevents",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz",
"integrity": "sha512-+nb9vWloHNNMFHjGofEam3wopE3m1yuambrrd/fnPc+lFOMB9ROTqQlche9ByFWNkdNqfSgR/kkQtQ8DzEWt2w==",
"dev": true,
"optional": true,
"requires": {
"anymatch": "^2.0.0",
"async-each": "^1.0.1",
"braces": "^2.3.2",
"glob-parent": "^3.1.0",
"inherits": "^2.0.3",
"is-binary-path": "^1.0.0",
"is-glob": "^4.0.0",
"normalize-path": "^3.0.0",
"path-is-absolute": "^1.0.0",
"readdirp": "^2.2.1",
"upath": "^1.1.1"
}
},
"@nodelib/fs.scandir": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
@ -2843,9 +2979,9 @@
},
"dependencies": {
"acorn": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
"integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==",
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
"dev": true
}
}
@ -3296,28 +3432,28 @@
"dependencies": {
"abbrev": {
"version": "1.1.1",
"resolved": false,
"resolved": "",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true,
"optional": true
},
"ansi-regex": {
"version": "2.1.1",
"resolved": false,
"resolved": "",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
"resolved": false,
"resolved": "",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
"dev": true,
"optional": true
},
"are-we-there-yet": {
"version": "1.1.5",
"resolved": false,
"resolved": "",
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
"dev": true,
"optional": true,
@ -3328,14 +3464,14 @@
},
"balanced-match": {
"version": "1.0.0",
"resolved": false,
"resolved": "",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": false,
"resolved": "",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"optional": true,
@ -3346,42 +3482,42 @@
},
"chownr": {
"version": "1.1.1",
"resolved": false,
"resolved": "",
"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
"dev": true,
"optional": true
},
"code-point-at": {
"version": "1.1.0",
"resolved": false,
"resolved": "",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"resolved": false,
"resolved": "",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"resolved": false,
"resolved": "",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
"resolved": false,
"resolved": "",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true,
"optional": true
},
"debug": {
"version": "4.1.1",
"resolved": false,
"resolved": "",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"optional": true,
@ -3391,28 +3527,28 @@
},
"deep-extend": {
"version": "0.6.0",
"resolved": false,
"resolved": "",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"dev": true,
"optional": true
},
"delegates": {
"version": "1.0.0",
"resolved": false,
"resolved": "",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
"dev": true,
"optional": true
},
"detect-libc": {
"version": "1.0.3",
"resolved": false,
"resolved": "",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"dev": true,
"optional": true
},
"fs-minipass": {
"version": "1.2.5",
"resolved": false,
"resolved": "",
"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
"dev": true,
"optional": true,
@ -3422,14 +3558,14 @@
},
"fs.realpath": {
"version": "1.0.0",
"resolved": false,
"resolved": "",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true,
"optional": true
},
"gauge": {
"version": "2.7.4",
"resolved": false,
"resolved": "",
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"dev": true,
"optional": true,
@ -3446,7 +3582,7 @@
},
"glob": {
"version": "7.1.3",
"resolved": false,
"resolved": "",
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
"dev": true,
"optional": true,
@ -3461,14 +3597,14 @@
},
"has-unicode": {
"version": "2.0.1",
"resolved": false,
"resolved": "",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
"dev": true,
"optional": true
},
"iconv-lite": {
"version": "0.4.24",
"resolved": false,
"resolved": "",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"optional": true,
@ -3478,7 +3614,7 @@
},
"ignore-walk": {
"version": "3.0.1",
"resolved": false,
"resolved": "",
"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
"dev": true,
"optional": true,
@ -3488,7 +3624,7 @@
},
"inflight": {
"version": "1.0.6",
"resolved": false,
"resolved": "",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"dev": true,
"optional": true,
@ -3499,21 +3635,21 @@
},
"inherits": {
"version": "2.0.3",
"resolved": false,
"resolved": "",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
"resolved": false,
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
"dev": true,
"optional": true
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": false,
"resolved": "",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"dev": true,
"optional": true,
@ -3523,14 +3659,14 @@
},
"isarray": {
"version": "1.0.0",
"resolved": false,
"resolved": "",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true,
"optional": true
},
"minimatch": {
"version": "3.0.4",
"resolved": false,
"resolved": "",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"optional": true,
@ -3538,16 +3674,9 @@
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"resolved": false,
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true,
"optional": true
},
"minipass": {
"version": "2.3.5",
"resolved": false,
"resolved": "",
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
"dev": true,
"optional": true,
@ -3558,7 +3687,7 @@
},
"minizlib": {
"version": "1.2.1",
"resolved": false,
"resolved": "",
"integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
"dev": true,
"optional": true,
@ -3567,25 +3696,25 @@
}
},
"mkdirp": {
"version": "0.5.1",
"resolved": false,
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
"minimist": "^1.2.5"
}
},
"ms": {
"version": "2.1.1",
"resolved": false,
"resolved": "",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true,
"optional": true
},
"needle": {
"version": "2.3.0",
"resolved": false,
"resolved": "",
"integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==",
"dev": true,
"optional": true,
@ -3597,7 +3726,7 @@
},
"node-pre-gyp": {
"version": "0.12.0",
"resolved": false,
"resolved": "",
"integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==",
"dev": true,
"optional": true,
@ -3616,7 +3745,7 @@
},
"nopt": {
"version": "4.0.1",
"resolved": false,
"resolved": "",
"integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
"dev": true,
"optional": true,
@ -3627,14 +3756,14 @@
},
"npm-bundled": {
"version": "1.0.6",
"resolved": false,
"resolved": "",
"integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==",
"dev": true,
"optional": true
},
"npm-packlist": {
"version": "1.4.1",
"resolved": false,
"resolved": "",
"integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==",
"dev": true,
"optional": true,
@ -3645,7 +3774,7 @@
},
"npmlog": {
"version": "4.1.2",
"resolved": false,
"resolved": "",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"dev": true,
"optional": true,
@ -3658,21 +3787,21 @@
},
"number-is-nan": {
"version": "1.0.1",
"resolved": false,
"resolved": "",
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
"resolved": false,
"resolved": "",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true,
"optional": true
},
"once": {
"version": "1.4.0",
"resolved": false,
"resolved": "",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"optional": true,
@ -3682,21 +3811,21 @@
},
"os-homedir": {
"version": "1.0.2",
"resolved": false,
"resolved": "",
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"dev": true,
"optional": true
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": false,
"resolved": "",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true,
"optional": true
},
"osenv": {
"version": "0.1.5",
"resolved": false,
"resolved": "",
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"dev": true,
"optional": true,
@ -3707,21 +3836,21 @@
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": false,
"resolved": "",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true,
"optional": true
},
"process-nextick-args": {
"version": "2.0.0",
"resolved": false,
"resolved": "",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
"dev": true,
"optional": true
},
"rc": {
"version": "1.2.8",
"resolved": false,
"resolved": "",
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
"dev": true,
"optional": true,
@ -3733,9 +3862,9 @@
},
"dependencies": {
"minimist": {
"version": "1.2.0",
"resolved": false,
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true,
"optional": true
}
@ -3743,7 +3872,7 @@
},
"readable-stream": {
"version": "2.3.6",
"resolved": false,
"resolved": "",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"optional": true,
@ -3759,7 +3888,7 @@
},
"rimraf": {
"version": "2.6.3",
"resolved": false,
"resolved": "",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"dev": true,
"optional": true,
@ -3769,49 +3898,49 @@
},
"safe-buffer": {
"version": "5.1.2",
"resolved": false,
"resolved": "",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
"resolved": false,
"resolved": "",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true,
"optional": true
},
"sax": {
"version": "1.2.4",
"resolved": false,
"resolved": "",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"dev": true,
"optional": true
},
"semver": {
"version": "5.7.0",
"resolved": false,
"resolved": "",
"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
"dev": true,
"optional": true
},
"set-blocking": {
"version": "2.0.0",
"resolved": false,
"resolved": "",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"dev": true,
"optional": true
},
"signal-exit": {
"version": "3.0.2",
"resolved": false,
"resolved": "",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true,
"optional": true
},
"string-width": {
"version": "1.0.2",
"resolved": false,
"resolved": "",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"dev": true,
"optional": true,
@ -3823,7 +3952,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": false,
"resolved": "",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"optional": true,
@ -3833,7 +3962,7 @@
},
"strip-ansi": {
"version": "3.0.1",
"resolved": false,
"resolved": "",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"optional": true,
@ -3843,14 +3972,14 @@
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": false,
"resolved": "",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true,
"optional": true
},
"tar": {
"version": "4.4.8",
"resolved": false,
"resolved": "",
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
"dev": true,
"optional": true,
@ -3866,14 +3995,14 @@
},
"util-deprecate": {
"version": "1.0.2",
"resolved": false,
"resolved": "",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true,
"optional": true
},
"wide-align": {
"version": "1.1.3",
"resolved": false,
"resolved": "",
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
"dev": true,
"optional": true,
@ -3883,14 +4012,14 @@
},
"wrappy": {
"version": "1.0.2",
"resolved": false,
"resolved": "",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true,
"optional": true
},
"yallist": {
"version": "3.0.3",
"resolved": false,
"resolved": "",
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
"dev": true,
"optional": true
@ -4496,12 +4625,6 @@
"path-is-inside": "^1.0.1"
}
},
"is-plain-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
"integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
"dev": true
},
"is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
@ -4689,9 +4812,9 @@
},
"dependencies": {
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
}
}
@ -4828,9 +4951,9 @@
}
},
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
}
}
@ -4854,9 +4977,9 @@
}
},
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"loose-envify": {
@ -5357,17 +5480,6 @@
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true
},
"output-file-sync": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-2.0.1.tgz",
"integrity": "sha512-mDho4qm7WgIXIGf4eYU1RHN2UU5tPfVYVSRwDJw0uTmj35DQUt/eNp19N7v6T3SrR0ESTEf2up2CGO73qI35zQ==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.11",
"is-plain-obj": "^1.1.0",
"mkdirp": "^0.5.1"
}
},
"p-defer": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
@ -5623,9 +5735,9 @@
},
"dependencies": {
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"strip-json-comments": {
@ -7229,12 +7341,6 @@
"integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==",
"dev": true
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
},
"yargs": {
"version": "13.2.4",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz",
@ -7403,6 +7509,12 @@
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dev": true
},
"y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"dev": true
},
"yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",

@ -4,7 +4,7 @@
"description": "",
"main": "src/js/cvat-data.js",
"devDependencies": {
"@babel/cli": "^7.4.4",
"@babel/cli": "^7.13.16",
"@babel/core": "^7.4.4",
"@babel/preset-env": "^7.4.4",
"babel-loader": "^8.0.6",

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.18.1",
"version": "1.19.1",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
@ -48,14 +48,15 @@
"worker-loader": "^2.0.0"
},
"dependencies": {
"@ant-design/icons": "^4.5.0",
"@ant-design/icons": "^4.6.2",
"@types/lodash": "^4.14.168",
"@types/platform": "^1.3.3",
"@types/react": "^16.14.5",
"@types/react-color": "^3.0.4",
"@types/react-dom": "^16.9.11",
"@types/react-dom": "^16.9.12",
"@types/react-redux": "^7.1.16",
"@types/react-router": "^5.1.12",
"@types/react-resizable": "^1.7.2",
"@types/react-router": "^5.1.13",
"@types/react-router-dom": "^5.1.7",
"@types/react-share": "^3.0.3",
"@types/redux-logger": "^3.0.8",
@ -77,13 +78,13 @@
"react-color": "^2.19.3",
"react-cookie": "^4.0.3",
"react-dom": "^16.14.0",
"react-redux": "^7.2.2",
"react-moment": "^1.1.1",
"react-redux": "^7.2.4",
"react-resizable": "^1.11.1",
"@types/react-resizable": "^1.7.2",
"react-router": "^5.1.0",
"react-router-dom": "^5.1.0",
"react-share": "^3.0.1",
"redux": "^4.0.5",
"redux": "^4.1.0",
"redux-devtools-extension": "^2.13.9",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0"

@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -190,6 +190,10 @@ export enum AnnotationActionTypes {
SWITCH_REQUEST_REVIEW_DIALOG = 'SWITCH_REQUEST_REVIEW_DIALOG',
SWITCH_SUBMIT_REVIEW_DIALOG = 'SWITCH_SUBMIT_REVIEW_DIALOG',
SET_FORCE_EXIT_ANNOTATION_PAGE_FLAG = 'SET_FORCE_EXIT_ANNOTATION_PAGE_FLAG',
UPDATE_PREDICTOR_STATE = 'UPDATE_PREDICTOR_STATE',
GET_PREDICTIONS = 'GET_PREDICTIONS',
GET_PREDICTIONS_FAILED = 'GET_PREDICTIONS_FAILED',
GET_PREDICTIONS_SUCCESS = 'GET_PREDICTIONS_SUCCESS',
HIDE_SHOW_CONTEXT_IMAGE = 'HIDE_SHOW_CONTEXT_IMAGE',
GET_CONTEXT_IMAGE = 'GET_CONTEXT_IMAGE',
}
@ -612,6 +616,87 @@ export function switchPlay(playing: boolean): AnyAction {
};
}
export function getPredictionsAsync(): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
const {
annotations: {
states: currentStates,
zLayer: { cur: curZOrder },
},
predictor: { enabled, annotatedFrames },
} = getStore().getState().annotation;
const {
filters, frame, showAllInterpolationTracks, jobInstance: job,
} = receiveAnnotationsParameters();
if (!enabled || currentStates.length || annotatedFrames.includes(frame)) return;
dispatch({
type: AnnotationActionTypes.GET_PREDICTIONS,
payload: {},
});
let annotations = [];
try {
annotations = await job.predictor.predict(frame);
// current frame could be changed during a request above, need to fetch it from store again
const { number: currentFrame } = getStore().getState().annotation.player.frame;
if (frame !== currentFrame || annotations === null) {
// another request has already been sent or user went to another frame
// we do not need dispatch predictions success action
return;
}
annotations = annotations.map(
(data: any): any =>
new cvat.classes.ObjectState({
shapeType: data.type,
label: job.task.labels.filter((label: any): boolean => label.id === data.label)[0],
points: data.points,
objectType: ObjectType.SHAPE,
frame,
occluded: false,
source: 'auto',
attributes: {},
zOrder: curZOrder,
}),
);
dispatch({
type: AnnotationActionTypes.GET_PREDICTIONS_SUCCESS,
payload: { frame },
});
} catch (error) {
dispatch({
type: AnnotationActionTypes.GET_PREDICTIONS_FAILED,
payload: {
error,
},
});
}
try {
await job.annotations.put(annotations);
const states = await job.annotations.get(frame, showAllInterpolationTracks, filters);
const history = await job.actions.get();
dispatch({
type: AnnotationActionTypes.CREATE_ANNOTATIONS_SUCCESS,
payload: {
states,
history,
},
});
} catch (error) {
dispatch({
type: AnnotationActionTypes.CREATE_ANNOTATIONS_FAILED,
payload: {
error,
},
});
}
};
}
export function changeFrameAsync(toFrame: number, fillBuffer?: boolean, frameStep?: number): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
const state: CombinedState = getStore().getState();
@ -689,6 +774,7 @@ export function changeFrameAsync(toFrame: number, fillBuffer?: boolean, frameSte
delay,
},
});
dispatch(getPredictionsAsync());
} catch (error) {
if (error !== 'not needed') {
dispatch({
@ -934,9 +1020,11 @@ export function getJobAsync(tid: number, jid: number, initialFrame: number, init
loadJobEvent.close(await jobInfoGenerator(job));
const openTime = Date.now();
dispatch({
type: AnnotationActionTypes.GET_JOB_SUCCESS,
payload: {
openTime,
job,
issues,
reviews,
@ -950,10 +1038,41 @@ export function getJobAsync(tid: number, jid: number, initialFrame: number, init
maxZ,
},
});
if (job.task.dimension === DimensionType.DIM_3D) {
const workspace = Workspace.STANDARD3D;
dispatch(changeWorkspace(workspace));
}
const updatePredictorStatus = async (): Promise<void> => {
// get current job
const currentState: CombinedState = getStore().getState();
const { openTime: currentOpenTime, instance: currentJob } = currentState.annotation.job;
if (currentJob === null || currentJob.id !== job.id || currentOpenTime !== openTime) {
// the job was closed, changed or reopened
return;
}
try {
const status = await job.predictor.status();
dispatch({
type: AnnotationActionTypes.UPDATE_PREDICTOR_STATE,
payload: status,
});
setTimeout(updatePredictorStatus, 60 * 1000);
} catch (error) {
dispatch({
type: AnnotationActionTypes.UPDATE_PREDICTOR_STATE,
payload: { error },
});
setTimeout(updatePredictorStatus, 20 * 1000);
}
};
if (state.plugins.list.PREDICT && job.task.projectId !== null) {
updatePredictorStatus();
}
dispatch(changeFrameAsync(frameNumber, false));
} catch (error) {
dispatch({
@ -1014,36 +1133,16 @@ export function saveAnnotationsAsync(sessionInstance: any, afterSave?: () => voi
}
// used to reproduce the latest drawing (in case of tags just creating) by using N
export function rememberObject(
objectType: ObjectType,
labelID: number,
shapeType?: ShapeType,
points?: number,
rectDrawingMethod?: RectDrawingMethod,
): AnyAction {
let activeControl = ActiveControl.CURSOR;
if (shapeType === ShapeType.RECTANGLE) {
activeControl = ActiveControl.DRAW_RECTANGLE;
} else if (shapeType === ShapeType.POLYGON) {
activeControl = ActiveControl.DRAW_POLYGON;
} else if (shapeType === ShapeType.POLYLINE) {
activeControl = ActiveControl.DRAW_POLYLINE;
} else if (shapeType === ShapeType.POINTS) {
activeControl = ActiveControl.DRAW_POINTS;
} else if (shapeType === ShapeType.CUBOID) {
activeControl = ActiveControl.DRAW_CUBOID;
}
export function rememberObject(createParams: {
activeObjectType?: ObjectType;
activeLabelID?: number;
activeShapeType?: ShapeType;
activeNumOfPoints?: number;
activeRectDrawingMethod?: RectDrawingMethod;
}): AnyAction {
return {
type: AnnotationActionTypes.REMEMBER_CREATED_OBJECT,
payload: {
shapeType,
labelID,
objectType,
points,
activeControl,
rectDrawingMethod,
},
payload: createParams,
};
}
@ -1516,6 +1615,14 @@ export function setForceExitAnnotationFlag(forceExit: boolean): AnyAction {
};
}
export function switchPredictor(predictorEnabled: boolean): AnyAction {
return {
type: AnnotationActionTypes.UPDATE_PREDICTOR_STATE,
payload: {
enabled: predictorEnabled,
},
};
}
export function hideShowContextImage(hidden: boolean): AnyAction {
return {
type: AnnotationActionTypes.HIDE_SHOW_CONTEXT_IMAGE,

@ -1,8 +1,10 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
import { ActionUnion, createAction, ThunkAction, ThunkDispatch } from 'utils/redux';
import {
ActionUnion, createAction, ThunkAction, ThunkDispatch,
} from 'utils/redux';
import getCore from 'cvat-core-wrapper';
import { LogType } from 'cvat-logger';
import { computeZRange } from './annotation-actions';

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- The icon received from: https://www.svgrepo.com/svg/25187/brain -->
<!-- License: CC0 Creative Commons License -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 463 463" width="40px" height="40px" style="enable-background:new 0 0 463 463;" xml:space="preserve">
<g>
<path d="M151.245,222.446C148.054,237.039,135.036,248,119.5,248c-4.142,0-7.5,3.357-7.5,7.5s3.358,7.5,7.5,7.5
c23.774,0,43.522-17.557,46.966-40.386c14.556-1.574,27.993-8.06,38.395-18.677c2.899-2.959,2.85-7.708-0.109-10.606
c-2.958-2.897-7.707-2.851-10.606,0.108C184.947,202.829,172.643,208,159.5,208c-26.743,0-48.5-21.757-48.5-48.5
c0-4.143-3.358-7.5-7.5-7.5s-7.5,3.357-7.5,7.5C96,191.715,120.119,218.384,151.245,222.446z"/>
<path d="M183,287.5c0-4.143-3.358-7.5-7.5-7.5c-35.014,0-63.5,28.486-63.5,63.5c0,0.362,0.013,0.725,0.019,1.088
C109.23,344.212,106.39,344,103.5,344c-4.142,0-7.5,3.357-7.5,7.5s3.358,7.5,7.5,7.5c26.743,0,48.5,21.757,48.5,48.5
c0,4.143,3.358,7.5,7.5,7.5s7.5-3.357,7.5-7.5c0-26.611-16.462-49.437-39.731-58.867c-0.178-1.699-0.269-3.418-0.269-5.133
c0-26.743,21.757-48.5,48.5-48.5C179.642,295,183,291.643,183,287.5z"/>
<path d="M439,223.5c0-17.075-6.82-33.256-18.875-45.156c1.909-6.108,2.875-12.426,2.875-18.844
c0-30.874-22.152-56.659-51.394-62.329C373.841,91.6,375,85.628,375,79.5c0-19.557-11.883-36.387-28.806-43.661
C317.999,13.383,287.162,0,263.5,0c-13.153,0-24.817,6.468-32,16.384C224.317,6.468,212.653,0,199.5,0
c-23.662,0-54.499,13.383-82.694,35.839C99.883,43.113,88,59.943,88,79.5c0,6.128,1.159,12.1,3.394,17.671
C62.152,102.841,40,128.626,40,159.5c0,6.418,0.965,12.735,2.875,18.844C30.82,190.244,24,206.425,24,223.5
c0,13.348,4.149,25.741,11.213,35.975C27.872,270.087,24,282.466,24,295.5c0,23.088,12.587,44.242,32.516,55.396
C56.173,353.748,56,356.626,56,359.5c0,31.144,20.315,58.679,49.79,68.063C118.611,449.505,141.965,463,167.5,463
c27.995,0,52.269-16.181,64-39.674c11.731,23.493,36.005,39.674,64,39.674c25.535,0,48.889-13.495,61.71-35.437
c29.475-9.385,49.79-36.92,49.79-68.063c0-2.874-0.173-5.752-0.516-8.604C426.413,339.742,439,318.588,439,295.5
c0-13.034-3.872-25.413-11.213-36.025C434.851,249.241,439,236.848,439,223.5z M167.5,448c-21.029,0-40.191-11.594-50.009-30.256
c-0.973-1.849-2.671-3.208-4.688-3.751C88.19,407.369,71,384.961,71,359.5c0-3.81,0.384-7.626,1.141-11.344
c0.702-3.447-1.087-6.92-4.302-8.35C50.32,332.018,39,314.626,39,295.5c0-8.699,2.256-17.014,6.561-24.379
C56.757,280.992,71.436,287,87.5,287c4.142,0,7.5-3.357,7.5-7.5s-3.358-7.5-7.5-7.5C60.757,272,39,250.243,39,223.5
c0-14.396,6.352-27.964,17.428-37.221c2.5-2.09,3.365-5.555,2.14-8.574C56.2,171.869,55,165.744,55,159.5
c0-26.743,21.757-48.5,48.5-48.5s48.5,21.757,48.5,48.5c0,4.143,3.358,7.5,7.5,7.5s7.5-3.357,7.5-7.5
c0-33.642-26.302-61.243-59.421-63.355C104.577,91.127,103,85.421,103,79.5c0-13.369,8.116-24.875,19.678-29.859
c0.447-0.133,0.885-0.307,1.308-0.527C127.568,47.752,131.447,47,135.5,47c12.557,0,23.767,7.021,29.256,18.325
c1.81,3.727,6.298,5.281,10.023,3.47c3.726-1.809,5.28-6.296,3.47-10.022c-6.266-12.903-18.125-22.177-31.782-25.462
C165.609,21.631,184.454,15,199.5,15c13.509,0,24.5,10.99,24.5,24.5v97.051c-6.739-5.346-15.25-8.551-24.5-8.551
c-4.142,0-7.5,3.357-7.5,7.5s3.358,7.5,7.5,7.5c13.509,0,24.5,10.99,24.5,24.5v180.279c-9.325-12.031-22.471-21.111-37.935-25.266
c-3.999-1.071-8.114,1.297-9.189,5.297c-1.075,4.001,1.297,8.115,5.297,9.189C206.8,343.616,224,366.027,224,391.5
C224,422.654,198.654,448,167.5,448z M395.161,339.807c-3.215,1.43-5.004,4.902-4.302,8.35c0.757,3.718,1.141,7.534,1.141,11.344
c0,25.461-17.19,47.869-41.803,54.493c-2.017,0.543-3.716,1.902-4.688,3.751C335.691,436.406,316.529,448,295.5,448
c-31.154,0-56.5-25.346-56.5-56.5c0-2.109-0.098-4.2-0.281-6.271c0.178-0.641,0.281-1.314,0.281-2.012V135.5
c0-13.51,10.991-24.5,24.5-24.5c4.142,0,7.5-3.357,7.5-7.5s-3.358-7.5-7.5-7.5c-9.25,0-17.761,3.205-24.5,8.551V39.5
c0-13.51,10.991-24.5,24.5-24.5c15.046,0,33.891,6.631,53.033,18.311c-13.657,3.284-25.516,12.559-31.782,25.462
c-1.81,3.727-0.256,8.214,3.47,10.022c3.726,1.81,8.213,0.257,10.023-3.47C303.733,54.021,314.943,47,327.5,47
c4.053,0,7.933,0.752,11.514,2.114c0.422,0.22,0.86,0.393,1.305,0.526C351.883,54.624,360,66.13,360,79.5
c0,5.921-1.577,11.627-4.579,16.645C322.302,98.257,296,125.858,296,159.5c0,4.143,3.358,7.5,7.5,7.5s7.5-3.357,7.5-7.5
c0-26.743,21.757-48.5,48.5-48.5s48.5,21.757,48.5,48.5c0,6.244-1.2,12.369-3.567,18.205c-1.225,3.02-0.36,6.484,2.14,8.574
C417.648,195.536,424,209.104,424,223.5c0,26.743-21.757,48.5-48.5,48.5c-4.142,0-7.5,3.357-7.5,7.5s3.358,7.5,7.5,7.5
c16.064,0,30.743-6.008,41.939-15.879c4.306,7.365,6.561,15.68,6.561,24.379C424,314.626,412.68,332.018,395.161,339.807z"/>
<path d="M359.5,240c-15.536,0-28.554-10.961-31.745-25.554C358.881,210.384,383,183.715,383,151.5c0-4.143-3.358-7.5-7.5-7.5
s-7.5,3.357-7.5,7.5c0,26.743-21.757,48.5-48.5,48.5c-13.143,0-25.447-5.171-34.646-14.561c-2.898-2.958-7.647-3.007-10.606-0.108
s-3.008,7.647-0.109,10.606c10.402,10.617,23.839,17.103,38.395,18.677C315.978,237.443,335.726,255,359.5,255
c4.142,0,7.5-3.357,7.5-7.5S363.642,240,359.5,240z"/>
<path d="M335.5,328c-2.89,0-5.73,0.212-8.519,0.588c0.006-0.363,0.019-0.726,0.019-1.088c0-35.014-28.486-63.5-63.5-63.5
c-4.142,0-7.5,3.357-7.5,7.5s3.358,7.5,7.5,7.5c26.743,0,48.5,21.757,48.5,48.5c0,1.714-0.091,3.434-0.269,5.133
C288.462,342.063,272,364.889,272,391.5c0,4.143,3.358,7.5,7.5,7.5s7.5-3.357,7.5-7.5c0-26.743,21.757-48.5,48.5-48.5
c4.142,0,7.5-3.357,7.5-7.5S339.642,328,335.5,328z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

@ -16,7 +16,7 @@ import SubmitReviewModal from 'components/annotation-page/review/submit-review-m
import StandardWorkspaceComponent from 'components/annotation-page/standard-workspace/standard-workspace';
import StandardWorkspace3DComponent from 'components/annotation-page/standard3D-workspace/standard3D-workspace';
import TagAnnotationWorkspace from 'components/annotation-page/tag-annotation-workspace/tag-annotation-workspace';
import FiltersModalContainer from 'containers/annotation-page/top-bar/filters-modal';
import FiltersModalComponent from 'components/annotation-page/top-bar/filters-modal';
import StatisticsModalContainer from 'containers/annotation-page/top-bar/statistics-modal';
import AnnotationTopBarContainer from 'containers/annotation-page/top-bar/top-bar';
import { Workspace } from 'reducers/interfaces';
@ -131,7 +131,7 @@ export default function AnnotationPageComponent(props: Props): JSX.Element {
<ReviewAnnotationsWorkspace />
</Layout.Content>
)}
<FiltersModalContainer visible={false} />
<FiltersModalComponent />
<StatisticsModalContainer />
<SubmitAnnotationsModal />
<SubmitReviewModal />

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -10,6 +10,7 @@ import Radio, { RadioChangeEvent } from 'antd/lib/radio';
import Slider from 'antd/lib/slider';
import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
import Collapse from 'antd/lib/collapse';
import Button from 'antd/lib/button';
import ColorPicker from 'components/annotation-page/standard-workspace/objects-side-bar/color-picker';
import { ColorizeIcon } from 'icons';
@ -26,7 +27,6 @@ import {
changeShowBitmap as changeShowBitmapAction,
changeShowProjections as changeShowProjectionsAction,
} from 'actions/settings-actions';
import Button from 'antd/lib/button';
interface StateToProps {
appearanceCollapsed: boolean;
@ -152,7 +152,14 @@ function AppearanceBlock(props: Props): JSX.Element {
activeKey={appearanceCollapsed ? [] : ['appearance']}
className='cvat-objects-appearance-collapse'
>
<Collapse.Panel header={<Text strong className='cvat-objects-appearance-collapse-header'>Appearance</Text>} key='appearance'>
<Collapse.Panel
header={(
<Text strong className='cvat-objects-appearance-collapse-header'>
Appearance
</Text>
)}
key='appearance'
>
<div className='cvat-objects-appearance-content'>
<Text type='secondary'>Color by</Text>
<Radio.Group

@ -3,13 +3,14 @@
// SPDX-License-Identifier: MIT
import React from 'react';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import Text from 'antd/lib/typography/Text';
import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox';
import Select, { SelectValue } from 'antd/lib/select';
import Radio, { RadioChangeEvent } from 'antd/lib/radio';
import Input from 'antd/lib/input';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import consts from 'consts';
interface InputElementParameters {

@ -3,9 +3,9 @@
// SPDX-License-Identifier: MIT
import React from 'react';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import Layout from 'antd/lib/layout';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import { ActiveControl, Rotation } from 'reducers/interfaces';
import { Canvas } from 'cvat-canvas-wrapper';

@ -4,9 +4,9 @@
import React from 'react';
import Layout from 'antd/lib/layout';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import { ActiveControl, Rotation } from 'reducers/interfaces';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import { Canvas } from 'cvat-canvas-wrapper';
import ControlVisibilityObserver, { ExtraControlsControl } from './control-visibility-observer';

@ -4,7 +4,6 @@
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { CombinedState } from 'reducers/interfaces';
import {
LeftOutlined, RightOutlined, EyeInvisibleFilled, EyeOutlined,
} from '@ant-design/icons';
@ -14,6 +13,7 @@ import { Row, Col } from 'antd/lib/grid';
import { changeFrameAsync } from 'actions/annotation-actions';
import { reviewActions } from 'actions/review-actions';
import CVATTooltip from 'components/common/cvat-tooltip';
import { CombinedState } from 'reducers/interfaces';
export default function LabelsListComponent(): JSX.Element {
const dispatch = useDispatch();

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -10,22 +10,30 @@ import {
LockFilled, UnlockOutlined, EyeInvisibleFilled, EyeOutlined,
} from '@ant-design/icons';
import CVATTooltip from 'components/common/cvat-tooltip';
import LabelKeySelectorPopover from './label-key-selector-popover';
interface Props {
labelName: string;
labelColor: string;
labelID: number;
visible: boolean;
statesHidden: boolean;
statesLocked: boolean;
keyToLabelMapping: Record<string, number>;
hideStates(): void;
showStates(): void;
lockStates(): void;
unlockStates(): void;
updateLabelShortcutKey(updatedKey: string, labelID: number): void;
}
function LabelItemComponent(props: Props): JSX.Element {
const {
labelName,
labelColor,
labelID,
keyToLabelMapping,
visible,
statesHidden,
statesLocked,
@ -33,8 +41,14 @@ function LabelItemComponent(props: Props): JSX.Element {
showStates,
lockStates,
unlockStates,
updateLabelShortcutKey,
} = props;
// create reversed mapping just to receive key easily
const labelToKeyMapping: Record<string, string> = Object.fromEntries(
Object.entries(keyToLabelMapping).map(([key, _labelID]) => [_labelID, key]),
);
const labelShortcutKey = labelToKeyMapping[labelID] || '?';
const classes = {
lock: {
enabled: { className: 'cvat-label-item-button-lock cvat-label-item-button-lock-enabled' },
@ -48,22 +62,37 @@ function LabelItemComponent(props: Props): JSX.Element {
return (
<Row
align='middle'
align='stretch'
justify='space-around'
className='cvat-objects-sidebar-label-item'
style={{ display: visible ? 'flex' : 'none' }}
className={[
'cvat-objects-sidebar-label-item',
visible ? '' : 'cvat-objects-sidebar-label-item-disabled',
].join(' ')}
>
<Col span={4}>
<Button style={{ background: labelColor }} className='cvat-label-item-color-button'>
<Col span={2}>
<div style={{ background: labelColor }} className='cvat-label-item-color'>
{' '}
</Button>
</div>
</Col>
<Col span={14}>
<Text strong className='cvat-text'>
{labelName}
</Text>
<Col span={12}>
<CVATTooltip title={labelName}>
<Text strong className='cvat-text'>
{labelName}
</Text>
</CVATTooltip>
</Col>
<Col span={3}>
<LabelKeySelectorPopover
keyToLabelMapping={keyToLabelMapping}
labelID={labelID}
updateLabelShortcutKey={updateLabelShortcutKey}
>
<Button className='cvat-label-item-setup-shortcut-button' size='small' ghost type='dashed'>
{labelShortcutKey}
</Button>
</LabelKeySelectorPopover>
</Col>
<Col span={2} offset={1}>
{statesLocked ? (
<LockFilled {...classes.lock.enabled} onClick={unlockStates} />
) : (

@ -0,0 +1,85 @@
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
import React from 'react';
import { useSelector } from 'react-redux';
import Popover from 'antd/lib/popover';
import Button from 'antd/lib/button';
import { Row, Col } from 'antd/lib/grid';
import Text from 'antd/lib/typography/Text';
import { CombinedState } from 'reducers/interfaces';
import CVATTooltip from 'components/common/cvat-tooltip';
interface LabelKeySelectorPopoverProps {
updateLabelShortcutKey(updatedKey: string, labelID: number): void;
keyToLabelMapping: Record<string, number>;
labelID: number;
children: JSX.Element;
}
interface LabelKeySelectorPopoverContentProps {
updateLabelShortcutKey(updatedKey: string, labelID: number): void;
labelID: number;
keyToLabelMapping: Record<string, number>;
}
function PopoverContent(props: LabelKeySelectorPopoverContentProps): JSX.Element {
const { keyToLabelMapping, labelID, updateLabelShortcutKey } = props;
const labels = useSelector((state: CombinedState) => state.annotation.job.labels);
return (
<div className='cvat-label-item-setup-shortcut-popover'>
{[['1', '2', '3'], ['4', '5', '6'], ['7', '8', '9'], ['0']].map((arr, i_) => (
<Row justify='space-around' gutter={[16, 16]} key={i_}>
{arr.map((i) => {
const previousLabelID = keyToLabelMapping[i];
const labelName = Number.isInteger(previousLabelID) ?
labels.filter((label: any): boolean => label.id === previousLabelID)[0]?.name ||
'undefined' :
'None';
return (
<Col key={i} span={8}>
<CVATTooltip title={labelName}>
<Button onClick={() => updateLabelShortcutKey(i, labelID)}>
<Text>{`${i}:`}</Text>
<Text type='secondary'>{labelName}</Text>
</Button>
</CVATTooltip>
</Col>
);
})}
</Row>
))}
</div>
);
}
const MemoizedContent = React.memo(PopoverContent);
function LabelKeySelectorPopover(props: LabelKeySelectorPopoverProps): JSX.Element {
const {
children, labelID, updateLabelShortcutKey, keyToLabelMapping,
} = props;
return (
<Popover
destroyTooltipOnHide={{ keepParent: false }}
trigger='click'
content={(
<MemoizedContent
keyToLabelMapping={keyToLabelMapping}
labelID={labelID}
updateLabelShortcutKey={updateLabelShortcutKey}
/>
)}
placement='left'
>
{children}
</Popover>
);
}
export default React.memo(LabelKeySelectorPopover);

@ -1,26 +1,106 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
import React from 'react';
import React, { useCallback, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import message from 'antd/lib/message';
import { CombinedState } from 'reducers/interfaces';
import { rememberObject, updateAnnotationsAsync } from 'actions/annotation-actions';
import LabelItemContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/label-item';
import GlobalHotKeys from 'utils/mousetrap-react';
interface Props {
labelIDs: number[];
listHeight: number;
}
function LabelsListComponent(): JSX.Element {
const dispatch = useDispatch();
const labels = useSelector((state: CombinedState) => state.annotation.job.labels);
const listHeight = useSelector((state: CombinedState) => state.annotation.tabContentHeight);
const activatedStateID = useSelector((state: CombinedState) => state.annotation.annotations.activatedStateID);
const states = useSelector((state: CombinedState) => state.annotation.annotations.states);
const keyMap = useSelector((state: CombinedState) => state.shortcuts.keyMap);
const labelIDs = labels.map((label: any): number => label.id);
const [keyToLabelMapping, setKeyToLabelMapping] = useState<Record<string, number>>(
Object.fromEntries(labelIDs.slice(0, 10).map((labelID: number, idx: number) => [(idx + 1) % 10, labelID])),
);
const updateLabelShortcutKey = useCallback(
(key: string, labelID: number) => {
// unassign any keys assigned to the current labels
const keyToLabelMappingCopy = { ...keyToLabelMapping };
for (const shortKey of Object.keys(keyToLabelMappingCopy)) {
if (keyToLabelMappingCopy[shortKey] === labelID) {
delete keyToLabelMappingCopy[shortKey];
}
}
if (key === '—') {
setKeyToLabelMapping(keyToLabelMappingCopy);
return;
}
export default function LabelsListComponent(props: Props): JSX.Element {
const { listHeight, labelIDs } = props;
// check if this key is assigned to another label
if (key in keyToLabelMappingCopy) {
// try to find a new key for the other label
for (let i = 0; i < 10; i++) {
const adjustedI = (i + 1) % 10;
if (!(adjustedI in keyToLabelMappingCopy)) {
keyToLabelMappingCopy[adjustedI] = keyToLabelMappingCopy[key];
break;
}
}
// delete assigning to the other label
delete keyToLabelMappingCopy[key];
}
// assigning to the current label
keyToLabelMappingCopy[key] = labelID;
setKeyToLabelMapping(keyToLabelMappingCopy);
},
[keyToLabelMapping],
);
const subKeyMap = {
SWITCH_LABEL: keyMap.SWITCH_LABEL,
};
const handlers = {
SWITCH_LABEL: (event: KeyboardEvent | undefined, shortcut: string) => {
if (event) event.preventDefault();
const labelID = keyToLabelMapping[shortcut.split('+')[1].trim()];
const label = labels.filter((_label: any) => _label.id === labelID)[0];
if (Number.isInteger(labelID) && label) {
if (Number.isInteger(activatedStateID)) {
const activatedState = states.filter((state: any) => state.clientID === activatedStateID)[0];
if (activatedState) {
activatedState.label = label;
dispatch(updateAnnotationsAsync([activatedState]));
}
} else {
dispatch(rememberObject({ activeLabelID: labelID }));
message.destroy();
message.success(`Default label was changed to "${label.name}"`);
}
}
},
};
return (
<div style={{ height: listHeight }} className='cvat-objects-sidebar-labels-list'>
<GlobalHotKeys keyMap={subKeyMap} handlers={handlers} />
{labelIDs.map(
(labelID: number): JSX.Element => (
<LabelItemContainer key={labelID} labelID={labelID} />
<LabelItemContainer
key={labelID}
labelID={labelID}
keyToLabelMapping={keyToLabelMapping}
updateLabelShortcutKey={updateLabelShortcutKey}
/>
),
)}
</div>
);
}
export default React.memo(LabelsListComponent);

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -13,7 +13,7 @@ import Layout from 'antd/lib/layout';
import { Canvas } from 'cvat-canvas-wrapper';
import { CombinedState } from 'reducers/interfaces';
import LabelsListContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/labels-list';
import LabelsList from 'components/annotation-page/standard-workspace/objects-side-bar/labels-list';
import {
collapseSidebar as collapseSidebarAction,
updateTabContentHeight as updateTabContentHeightAction,
@ -123,8 +123,8 @@ function ObjectsSideBar(props: StateToProps & DispatchToProps & OwnProps): JSX.E
<Tabs.TabPane tab={<Text strong>Objects</Text>} key='objects'>
{objectsList}
</Tabs.TabPane>
<Tabs.TabPane tab={<Text strong>Labels</Text>} key='labels'>
<LabelsListContainer />
<Tabs.TabPane forceRender tab={<Text strong>Labels</Text>} key='labels'>
<LabelsList />
</Tabs.TabPane>
<Tabs.TabPane tab={<Text strong>Issues</Text>} key='issues'>
<IssuesListComponent />

@ -167,7 +167,8 @@
padding: 3px 1px 1px 3px;
}
.cvat-objects-sidebar-state-item-color {
.cvat-objects-sidebar-state-item-color,
.cvat-objects-sidebar-label-item-color {
border: 1px solid $object-item-border-color;
width: 7px;
opacity: 1;
@ -288,12 +289,16 @@
.cvat-objects-sidebar-label-active-item {
background: $active-label-background-color;
border-top: 2px solid $object-item-border-color;
border-right: 2px solid $object-item-border-color;
border-bottom: 2px solid $object-item-border-color;
padding: 3px 1px 1px 3px;
}
.cvat-objects-sidebar-label-item {
height: 2.5em;
border-bottom: 1px solid $border-color-1;
padding: 5px;
padding: 5px 3px 3px 3px;
span {
@extend .cvat-object-sidebar-icon;
@ -311,10 +316,39 @@
}
}
.cvat-label-item-color-button {
width: 30px;
height: 20px;
border-radius: 5px;
.cvat-label-item-color {
background: rgb(25, 184, 14);
height: 80%;
width: 90%;
border-radius: $grid-unit-size / 2;
}
.cvat-label-item-setup-shortcut-button {
border-color: $objects-bar-icons-color;
}
.cvat-label-item-setup-shortcut-popover {
margin-top: -$grid-unit-size;
margin-bottom: -$grid-unit-size;
> div {
padding-top: $grid-unit-size;
padding-bottom: $grid-unit-size;
> div {
display: flex;
justify-content: center;
> button {
width: $grid-unit-size * 15;
overflow-x: hidden;
span:first-child {
margin-right: $grid-unit-size;
}
}
}
}
}
.cvat-objects-appearance-content {
@ -361,3 +395,7 @@
margin-right: $grid-unit-size;
}
}
.cvat-objects-sidebar-label-item-disabled {
opacity: 0.5;
}

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -56,7 +56,8 @@
.cvat-issue-control,
.cvat-tools-control,
.cvat-extra-controls-control,
.cvat-opencv-control {
.cvat-opencv-control,
.cvat-context-image-control {
border-radius: 3.3px;
transform: scale(0.65);
padding: 2px;

@ -22,7 +22,7 @@ function PhotoContextControl(props: Props): JSX.Element {
return (
<CVATTooltip title='Photo context show/hide' placement='right'>
<CameraIcon
className={`cvat-move-control
className={`cvat-context-image-control
cvat-control-side-bar-icon-size ${
activeControl === ActiveControl.PHOTO_CONTEXT ? 'cvat-active-canvas-control' : ''
}`}

@ -81,6 +81,52 @@
}
}
button.cvat-predictor-button {
&.cvat-predictor-inprogress {
> span {
> svg {
fill: $inprogress-progress-color;
}
}
}
&.cvat-predictor-fetching {
> span {
> svg {
animation-duration: 500ms;
animation-name: predictorBlinking;
animation-iteration-count: infinite;
@keyframes predictorBlinking {
0% {
fill: $inprogress-progress-color;
}
50% {
fill: $completed-progress-color;
}
100% {
fill: $inprogress-progress-color;
}
}
}
}
}
&.cvat-predictor-disabled {
opacity: 0.5;
&:active {
pointer-events: none;
}
> span[role='img'] {
transform: scale(0.8) !important;
}
}
}
.cvat-annotation-disabled-header-button {
@extend .cvat-annotation-header-button;

@ -4,12 +4,12 @@
import React, { useState, useEffect } from 'react';
import { useSelector } from 'react-redux';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import { Row, Col } from 'antd/lib/grid';
import Text from 'antd/lib/typography/Text';
import Select from 'antd/lib/select';
import { CombinedState } from 'reducers/interfaces';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import { shift } from 'utils/math';
interface ShortcutLabelMap {

@ -20,11 +20,11 @@ import {
changeFrameAsync,
rememberObject,
} from 'actions/annotation-actions';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import { Canvas } from 'cvat-canvas-wrapper';
import { CombinedState, ObjectType } from 'reducers/interfaces';
import LabelSelector from 'components/label-selector/label-selector';
import getCore from 'cvat-core-wrapper';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import ShortcutsSelect from './shortcuts-select';
const cvat = getCore();
@ -82,7 +82,7 @@ function mapDispatchToProps(dispatch: ThunkDispatch<CombinedState, {}, Action>):
dispatch(removeObjectAsync(jobInstance, objectState, true));
},
onRememberObject(labelID: number): void {
dispatch(rememberObject(ObjectType.TAG, labelID));
dispatch(rememberObject({ activeObjectType: ObjectType.TAG, activeLabelID: labelID }));
},
};
}

@ -23,19 +23,15 @@ const { FieldDropdown } = AntdWidgets;
const FILTERS_HISTORY = 'annotationFiltersHistory';
interface Props {
visible: boolean;
}
interface StoredFilter {
id: string;
logic: JsonLogicTree;
}
export default function FiltersModalComponent(props: Props): JSX.Element {
const { visible } = props;
const { labels } = useSelector((state: CombinedState) => state.annotation.job);
const { filters: activeFilters } = useSelector((state: CombinedState) => state.annotation.annotations);
function FiltersModalComponent(): JSX.Element {
const labels = useSelector((state: CombinedState) => state.annotation.job.labels);
const activeFilters = useSelector((state: CombinedState) => state.annotation.annotations.filters);
const visible = useSelector((state: CombinedState) => state.annotation.filtersPanelVisible);
const getConvertedInputType = (inputType: string): string => {
switch (inputType) {
@ -234,18 +230,23 @@ export default function FiltersModalComponent(props: Props): JSX.Element {
const menu = (
<Menu>
{filters
.filter((filter: StoredFilter) => {
const tree = QbUtils.loadFromJsonLogic(filter.logic, config);
return !!QbUtils.queryString(tree, config);
})
.map((filter: StoredFilter) => {
// if a logic received from local storage does not correspond to current config
// which depends on label specification
// (it can be when history from another task with another specification or when label was removed)
// loadFromJsonLogic() prints a warning to console
// the are not ways to configure this behaviour
const tree = QbUtils.loadFromJsonLogic(filter.logic, config);
return (
<Menu.Item key={filter.id} onClick={() => setState({ tree, config })}>
{QbUtils.queryString(tree, config)}
</Menu.Item>
);
})}
const queryString = QbUtils.queryString(tree, config);
return { tree, queryString, filter };
})
.filter(({ queryString }) => !!queryString)
.map(({ filter, tree, queryString }) => (
<Menu.Item key={filter.id} onClick={() => setState({ tree, config })}>
{queryString}
</Menu.Item>
))}
</Menu>
);
@ -286,3 +287,5 @@ export default function FiltersModalComponent(props: Props): JSX.Element {
</Modal>
);
}
export default React.memo(FiltersModalComponent);

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -11,7 +11,9 @@ import Timeline from 'antd/lib/timeline';
import Dropdown from 'antd/lib/dropdown';
import AnnotationMenuContainer from 'containers/annotation-page/top-bar/annotation-menu';
import { MainMenuIcon, SaveIcon, UndoIcon, RedoIcon } from 'icons';
import {
MainMenuIcon, SaveIcon, UndoIcon, RedoIcon,
} from 'icons';
interface Props {
saving: boolean;

@ -7,28 +7,141 @@ import { Col } from 'antd/lib/grid';
import Icon from '@ant-design/icons';
import Select from 'antd/lib/select';
import Button from 'antd/lib/button';
import Text from 'antd/lib/typography/Text';
import Tooltip from 'antd/lib/tooltip';
import Moment from 'react-moment';
import moment from 'moment';
import { useSelector } from 'react-redux';
import { FilterIcon, FullscreenIcon, InfoIcon } from 'icons';
import { CombinedState, DimensionType, Workspace } from 'reducers/interfaces';
import {
FilterIcon, FullscreenIcon, InfoIcon, BrainIcon,
} from 'icons';
import {
CombinedState, DimensionType, Workspace, PredictorState,
} from 'reducers/interfaces';
interface Props {
workspace: Workspace;
predictor: PredictorState;
isTrainingActive: boolean;
showStatistics(): void;
switchPredictor(predictorEnabled: boolean): void;
showFilters(): void;
changeWorkspace(workspace: Workspace): void;
jobInstance: any;
}
function RightGroup(props: Props): JSX.Element {
const {
showFilters, showStatistics, changeWorkspace, workspace, jobInstance,
showStatistics,
changeWorkspace,
switchPredictor,
workspace,
predictor,
jobInstance,
isTrainingActive,
showFilters,
} = props;
const annotationAmount = predictor.annotationAmount || 0;
const mediaAmount = predictor.mediaAmount || 0;
const formattedScore = `${(predictor.projectScore * 100).toFixed(0)}%`;
const predictorTooltip = (
<div className='cvat-predictor-tooltip'>
<span>Adaptive auto annotation is</span>
{predictor.enabled ? (
<Text type='success' strong>
{' active'}
</Text>
) : (
<Text type='warning' strong>
{' inactive'}
</Text>
)}
<br />
<span>
Annotations amount:
{annotationAmount}
</span>
<br />
<span>
Media amount:
{mediaAmount}
</span>
<br />
{annotationAmount > 0 ? (
<span>
Model mAP is
{' '}
{formattedScore}
<br />
</span>
) : null}
{predictor.error ? (
<Text type='danger'>
{predictor.error.toString()}
<br />
</Text>
) : null}
{predictor.message ? (
<span>
Status:
{' '}
{predictor.message}
<br />
</span>
) : null}
{predictor.timeRemaining > 0 ? (
<span>
Time Remaining:
{' '}
<Moment date={moment().add(-predictor.timeRemaining, 's')} format='hh:mm:ss' trim durationFromNow />
<br />
</span>
) : null}
{predictor.progress > 0 ? (
<span>
Progress:
{predictor.progress.toFixed(1)}
{' '}
%
</span>
) : null}
</div>
);
let predictorClassName = 'cvat-annotation-header-button cvat-predictor-button';
if (!!predictor.error || !predictor.projectScore) {
predictorClassName += ' cvat-predictor-disabled';
} else if (predictor.enabled) {
if (predictor.fetching) {
predictorClassName += ' cvat-predictor-fetching';
}
predictorClassName += ' cvat-predictor-inprogress';
}
const filters = useSelector((state: CombinedState) => state.annotation.annotations.filters);
return (
<Col className='cvat-annotation-header-right-group'>
{isTrainingActive && (
<Button
type='link'
className={predictorClassName}
onClick={() => {
switchPredictor(!predictor.enabled);
}}
>
<Tooltip title={predictorTooltip}>
<Icon component={BrainIcon} />
</Tooltip>
{annotationAmount ? `mAP ${formattedScore}` : 'not trained'}
</Button>
)}
<Button
type='link'
className='cvat-annotation-header-button'

@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -6,7 +6,7 @@ import React from 'react';
import Input from 'antd/lib/input';
import { Col, Row } from 'antd/lib/grid';
import { Workspace } from 'reducers/interfaces';
import { PredictorState, Workspace } from 'reducers/interfaces';
import LeftGroup from './left-group';
import PlayerButtons from './player-buttons';
import PlayerNavigation from './player-navigation';
@ -35,7 +35,10 @@ interface Props {
prevButtonType: string;
nextButtonType: string;
focusFrameInputShortcut: string;
predictor: PredictorState;
isTrainingActive: boolean;
changeWorkspace(workspace: Workspace): void;
switchPredictor(predictorEnabled: boolean): void;
showStatistics(): void;
showFilters(): void;
onSwitchPlay(): void;
@ -80,8 +83,10 @@ export default function AnnotationTopBarComponent(props: Props): JSX.Element {
backwardShortcut,
prevButtonType,
nextButtonType,
predictor,
focusFrameInputShortcut,
showStatistics,
switchPredictor,
showFilters,
changeWorkspace,
onSwitchPlay,
@ -100,6 +105,7 @@ export default function AnnotationTopBarComponent(props: Props): JSX.Element {
onUndoClick,
onRedoClick,
jobInstance,
isTrainingActive,
} = props;
return (
@ -151,10 +157,13 @@ export default function AnnotationTopBarComponent(props: Props): JSX.Element {
</Row>
</Col>
<RightGroup
jobInstance={jobInstance}
predictor={predictor}
workspace={workspace}
switchPredictor={switchPredictor}
jobInstance={jobInstance}
changeWorkspace={changeWorkspace}
showStatistics={showStatistics}
isTrainingActive={isTrainingActive}
showFilters={showFilters}
/>
</Row>

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -48,7 +48,8 @@ function ChangePasswordFormComponent({ fetching, onSubmit }: Props): JSX.Element
{
required: true,
message: 'Please input new password!',
}, validatePassword,
},
validatePassword,
]}
>
<Input.Password
@ -66,7 +67,8 @@ function ChangePasswordFormComponent({ fetching, onSubmit }: Props): JSX.Element
{
required: true,
message: 'Please confirm your new password!',
}, validateConfirmation('newPassword1'),
},
validateConfirmation('newPassword1'),
]}
>
<Input.Password

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -43,7 +43,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
}
function ChangePasswordComponent(props: ChangePasswordPageComponentProps): JSX.Element {
const { fetching, onChangePassword, visible, onClose } = props;
const {
fetching, onChangePassword, visible, onClose,
} = props;
return (
<Modal

@ -1,12 +1,13 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
import React, {
useState, useRef, useEffect, RefObject,
RefObject, useContext, useEffect, useRef, useState,
} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory } from 'react-router';
import { Switch, Select } from 'antd';
import { Col, Row } from 'antd/lib/grid';
import Text from 'antd/lib/typography/Text';
import Form, { FormInstance } from 'antd/lib/form';
@ -18,6 +19,9 @@ import patterns from 'utils/validation-patterns';
import { CombinedState } from 'reducers/interfaces';
import LabelsEditor from 'components/labels-editor/labels-editor';
import { createProjectAsync } from 'actions/projects-actions';
import CreateProjectContext from './create-project.context';
const { Option } = Select;
function NameConfigurationForm({ formRef }: { formRef: RefObject<FormInstance> }): JSX.Element {
return (
@ -39,6 +43,59 @@ function NameConfigurationForm({ formRef }: { formRef: RefObject<FormInstance> }
);
}
function AdaptiveAutoAnnotationForm({ formRef }: { formRef: RefObject<FormInstance> }): JSX.Element {
const { projectClass, trainingEnabled } = useContext(CreateProjectContext);
const projectClassesForTraining = ['OD'];
return (
<Form layout='vertical' ref={formRef}>
<Form.Item name='project_class' hasFeedback label='Class'>
<Select value={projectClass.value} onChange={(v) => projectClass.set(v)}>
<Option value=''>--Not Selected--</Option>
<Option value='OD'>Detection</Option>
</Select>
</Form.Item>
<Form.Item name='enabled' label='Adaptive auto annotation' initialValue={false}>
<Switch
disabled={!projectClassesForTraining.includes(projectClass.value)}
checked={trainingEnabled.value}
onClick={() => trainingEnabled.set(!trainingEnabled.value)}
/>
</Form.Item>
<Form.Item
name='host'
label='Host'
rules={[
{
validator: (_, value, callback): void => {
if (value && !patterns.validateURL.pattern.test(value)) {
callback('Training server host must be url.');
} else {
callback();
}
},
},
]}
>
<Input placeholder='https://example.host' disabled={!trainingEnabled.value} />
</Form.Item>
<Row gutter={16}>
<Col span={12}>
<Form.Item name='username' label='Username'>
<Input placeholder='UserName' disabled={!trainingEnabled.value} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item name='password' label='Password'>
<Input.Password placeholder='Pa$$w0rd' disabled={!trainingEnabled.value} />
</Form.Item>
</Col>
</Row>
</Form>
);
}
function AdvanvedConfigurationForm({ formRef }: { formRef: RefObject<FormInstance> }): JSX.Element {
return (
<Form layout='vertical' ref={formRef}>
@ -69,12 +126,15 @@ export default function CreateProjectContent(): JSX.Element {
const [projectLabels, setProjectLabels] = useState<any[]>([]);
const shouldShowNotification = useRef(false);
const nameFormRef = useRef<FormInstance>(null);
const adaptiveAutoAnnotationFormRef = useRef<FormInstance>(null);
const advancedFormRef = useRef<FormInstance>(null);
const dispatch = useDispatch();
const history = useHistory();
const newProjectId = useSelector((state: CombinedState) => state.projects.activities.creates.id);
const { isTrainingActive } = useContext(CreateProjectContext);
useEffect(() => {
if (Number.isInteger(newProjectId) && shouldShowNotification.current) {
const btn = <Button onClick={() => history.push(`/projects/${newProjectId}`)}>Open project</Button>;
@ -94,17 +154,19 @@ export default function CreateProjectContent(): JSX.Element {
}, [newProjectId]);
const onSumbit = async (): Promise<void> => {
interface Project {
[key: string]: any;
}
const projectData: Project = {};
let projectData: Record<string, any> = {};
if (nameFormRef.current && advancedFormRef.current) {
const basicValues = await nameFormRef.current.validateFields();
const advancedValues = await advancedFormRef.current.validateFields();
projectData.name = basicValues.name;
for (const [field, value] of Object.entries(advancedValues)) {
projectData[field] = value;
const adaptiveAutoAnnotationValues = await adaptiveAutoAnnotationFormRef.current?.validateFields();
projectData = {
...projectData,
...advancedValues,
name: basicValues.name,
};
if (adaptiveAutoAnnotationValues) {
projectData.training_project = { ...adaptiveAutoAnnotationValues };
}
}
@ -120,6 +182,11 @@ export default function CreateProjectContent(): JSX.Element {
<Col span={24}>
<NameConfigurationForm formRef={nameFormRef} />
</Col>
{isTrainingActive.value && (
<Col span={24}>
<AdaptiveAutoAnnotationForm formRef={adaptiveAutoAnnotationFormRef} />
</Col>
)}
<Col span={24}>
<Text className='cvat-text-color'>Labels:</Text>
<LabelsEditor

@ -1,21 +1,56 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
import './styles.scss';
import React from 'react';
import React, { useState } from 'react';
import { Row, Col } from 'antd/lib/grid';
import Text from 'antd/lib/typography/Text';
import { connect } from 'react-redux';
import CreateProjectContent from './create-project-content';
import { CombinedState } from '../../reducers/interfaces';
import CreateProjectContext, { ICreateProjectContext } from './create-project.context';
export default function CreateProjectPageComponent(): JSX.Element {
function CreateProjectPageComponent(props: StateToProps): JSX.Element {
const { isTrainingActive } = props;
const [projectClass, setProjectClass] = useState('');
const [trainingEnabled, setTrainingEnabled] = useState(false);
const [isTrainingActiveState] = useState(isTrainingActive);
const defaultContext: ICreateProjectContext = {
projectClass: {
value: projectClass,
set: setProjectClass,
},
trainingEnabled: {
value: trainingEnabled,
set: setTrainingEnabled,
},
isTrainingActive: {
value: isTrainingActiveState,
},
};
return (
<Row justify='center' align='top' className='cvat-create-task-form-wrapper'>
<Col md={20} lg={16} xl={14} xxl={9}>
<Text className='cvat-title'>Create a new project</Text>
<CreateProjectContent />
</Col>
</Row>
<CreateProjectContext.Provider value={defaultContext}>
<Row justify='center' align='top' className='cvat-create-task-form-wrapper'>
<Col md={20} lg={16} xl={14} xxl={9}>
<Text className='cvat-title'>Create a new project</Text>
<CreateProjectContent />
</Col>
</Row>
</CreateProjectContext.Provider>
);
}
interface StateToProps {
isTrainingActive: boolean;
}
function mapStateToProps(state: CombinedState): StateToProps {
return {
isTrainingActive: state.plugins.list.PREDICT,
};
}
export default connect(mapStateToProps)(CreateProjectPageComponent);

@ -0,0 +1,31 @@
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
import { createContext, Dispatch, SetStateAction } from 'react';
export interface IState<T> {
value: T;
set?: Dispatch<SetStateAction<T>>;
}
export function getDefaultState<T>(v: T): IState<T> {
return {
value: v,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
set: (value: SetStateAction<T>): void => {},
};
}
export interface ICreateProjectContext {
projectClass: IState<string>;
trainingEnabled: IState<boolean>;
isTrainingActive: IState<boolean>;
}
export const defaultState: ICreateProjectContext = {
projectClass: getDefaultState<string>(''),
trainingEnabled: getDefaultState<boolean>(false),
isTrainingActive: getDefaultState<boolean>(false),
};
export default createContext<ICreateProjectContext>(defaultState);

@ -52,6 +52,7 @@ export default function CreateTaskPage(props: Props): JSX.Element {
Modal.error({
width: 800,
title: 'Could not clone the repository',
className: 'cvat-create-task-clone-repository-fail',
content: (
<>
<Paragraph>

@ -2,13 +2,17 @@
//
// SPDX-License-Identifier: MIT
import 'antd/dist/antd.css';
import React from 'react';
import { Redirect, Route, Switch } from 'react-router';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { Col, Row } from 'antd/lib/grid';
import Layout from 'antd/lib/layout';
import Modal from 'antd/lib/modal';
import notification from 'antd/lib/notification';
import Spin from 'antd/lib/spin';
import Text from 'antd/lib/typography/Text';
import 'antd/dist/antd.css';
import GlobalErrorBoundary from 'components/global-error-boundary/global-error-boundary';
import Header from 'components/header/header';
import ResetPasswordPageConfirmComponent from 'components/reset-password-confirm-page/reset-password-confirm-page';
@ -26,10 +30,7 @@ import AnnotationPageContainer from 'containers/annotation-page/annotation-page'
import LoginPageContainer from 'containers/login-page/login-page';
import RegisterPageContainer from 'containers/register-page/register-page';
import getCore from 'cvat-core-wrapper';
import React from 'react';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import { Redirect, Route, Switch } from 'react-router';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { NotificationsState } from 'reducers/interfaces';
import { customWaViewHit } from 'utils/enviroment';
import showPlatformNotification, { platformInfo, stopNotifications } from 'utils/platform-checker';
@ -128,14 +129,14 @@ class CVATApplication extends React.PureComponent<CVATAppProps & RouteComponentP
return;
}
if (user == null || !user.isVerified) {
return;
}
if (!authActionsInitialized && !authActionsFetching) {
loadAuthActions();
}
if (user == null || !user.isVerified) {
return;
}
if (!formatsInitialized && !formatsFetching) {
loadFormats();
}

@ -39,6 +39,7 @@ const SettingsModal = (props: SettingsModalProps): JSX.Element => {
localStorage.setItem('clientSettings', JSON.stringify(settingsForSaving));
notification.success({
message: 'Settings was successfully saved',
className: 'cvat-notification-notice-save-settings-success',
});
};
@ -62,6 +63,7 @@ const SettingsModal = (props: SettingsModalProps): JSX.Element => {
} catch {
notification.error({
message: 'Failed to load settings from local storage',
className: 'cvat-notification-notice-load-settings-fail',
});
}
}, []);

@ -4,8 +4,9 @@
import React, { useCallback, useState } from 'react';
import ReactDOM from 'react-dom';
import GlobalHotKeys from 'utils/mousetrap-react';
import { useSelector } from 'react-redux';
import GlobalHotKeys from 'utils/mousetrap-react';
import { CombinedState } from 'reducers/interfaces';
import './styles.scss';

@ -2,12 +2,12 @@
//
// SPDX-License-Identifier: MIT
import { shortcutsActions } from 'actions/shortcuts-actions';
import Modal from 'antd/lib/modal';
import Table from 'antd/lib/table';
import React from 'react';
import { getApplicationKeyMap } from 'utils/mousetrap-react';
import { connect } from 'react-redux';
import { getApplicationKeyMap } from 'utils/mousetrap-react';
import { shortcutsActions } from 'actions/shortcuts-actions';
import { CombinedState } from 'reducers/interfaces';
interface StateToProps {
@ -93,8 +93,9 @@ function ShorcutsDialog(props: StateToProps & DispatchToProps): JSX.Element | nu
onOk={switchShortcutsDialog}
cancelButtonProps={{ style: { display: 'none' } }}
zIndex={1001} /* default antd is 1000 */
className='cvat-shortcuts-modal-window'
>
<Table dataSource={dataSource} columns={columns} size='small' />
<Table dataSource={dataSource} columns={columns} size='small' className='cvat-shortcuts-modal-window-table' />
</Modal>
);
}

@ -166,7 +166,7 @@ function JobListComponent(props: Props & RouteComponentProps): JSX.Element {
title: 'Frames',
dataIndex: 'frames',
key: 'frames',
className: 'cvat-text-color',
className: 'cvat-text-color cvat-job-item-frames',
},
{
title: 'Status',
@ -217,6 +217,7 @@ function JobListComponent(props: Props & RouteComponentProps): JSX.Element {
title: 'Assignee',
dataIndex: 'assignee',
key: 'assignee',
className: 'cvat-job-item-assignee',
render: (jobInstance: any): JSX.Element => (
<UserSelector
className='cvat-job-assignee-selector'
@ -237,6 +238,7 @@ function JobListComponent(props: Props & RouteComponentProps): JSX.Element {
title: 'Reviewer',
dataIndex: 'reviewer',
key: 'reviewer',
className: 'cvat-job-item-reviewer',
render: (jobInstance: any): JSX.Element => (
<UserSelector
className='cvat-job-reviewer-selector'

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

@ -3,8 +3,8 @@
// SPDX-License-Identifier: MIT
import { connect } from 'react-redux';
import { KeyMap } from 'utils/mousetrap-react';
import CanvasWrapperComponent from 'components/annotation-page/canvas/canvas-wrapper';
import {
confirmCanvasReady,

@ -2,7 +2,6 @@
//
// SPDX-License-Identifier: MIT
import { KeyMap } from 'utils/mousetrap-react';
import { connect } from 'react-redux';
import { Canvas } from 'cvat-canvas-wrapper';
@ -19,6 +18,7 @@ import {
} from 'actions/annotation-actions';
import ControlsSideBarComponent from 'components/annotation-page/review-workspace/controls-side-bar/controls-side-bar';
import { ActiveControl, CombinedState, Rotation } from 'reducers/interfaces';
import { KeyMap } from 'utils/mousetrap-react';
interface StateToProps {
canvasInstance: Canvas;

@ -41,7 +41,15 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
points?: number,
rectDrawingMethod?: RectDrawingMethod,
): void {
dispatch(rememberObject(objectType, labelID, shapeType, points, rectDrawingMethod));
dispatch(
rememberObject({
activeObjectType: objectType,
activeShapeType: shapeType,
activeLabelID: labelID,
activeNumOfPoints: points,
activeRectDrawingMethod: rectDrawingMethod,
}),
);
},
};
}

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -32,7 +32,7 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
dispatch(createAnnotationsAsync(sessionInstance, frame, states));
},
onRememberObject(labelID: number): void {
dispatch(rememberObject(ObjectType.TAG, labelID));
dispatch(rememberObject({ activeObjectType: ObjectType.TAG, activeLabelID: labelID }));
},
};
}

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -12,6 +12,8 @@ import { CombinedState, ObjectType } from 'reducers/interfaces';
interface OwnProps {
labelID: number;
keyToLabelMapping: Record<string, number>;
updateLabelShortcutKey(updatedKey: string, labelID: number): void;
}
interface StateToProps {
@ -20,7 +22,7 @@ interface StateToProps {
labelColor: string;
objectStates: any[];
jobInstance: any;
frameNumber: any;
frameNumber: number;
}
interface DispatchToProps {
@ -127,35 +129,38 @@ class LabelItemContainer extends React.PureComponent<Props, State> {
private switchHidden(value: boolean): void {
const { updateAnnotations } = this.props;
const { ownObjectStates } = this.state;
for (const state of ownObjectStates) {
state.hidden = value;
}
updateAnnotations(ownObjectStates);
if (ownObjectStates.length) {
// false alarm
// eslint-disable-next-line
updateAnnotations(ownObjectStates.map((state: any) => ((state.hidden = value), state)));
}
}
private switchLock(value: boolean): void {
const { updateAnnotations } = this.props;
const { ownObjectStates } = this.state;
for (const state of ownObjectStates) {
state.lock = value;
}
updateAnnotations(ownObjectStates);
if (ownObjectStates.length) {
// false alarm
// eslint-disable-next-line
updateAnnotations(ownObjectStates.map((state: any) => ((state.lock = value), state)));
}
}
public render(): JSX.Element {
const {
labelName, labelColor, keyToLabelMapping, labelID, updateLabelShortcutKey,
} = this.props;
const { visible, statesHidden, statesLocked } = this.state;
const { labelName, labelColor } = this.props;
return (
<LabelItemComponent
labelName={labelName}
labelColor={labelColor}
labelID={labelID}
keyToLabelMapping={keyToLabelMapping}
visible={visible}
statesHidden={statesHidden}
statesLocked={statesLocked}
@ -163,6 +168,7 @@ class LabelItemContainer extends React.PureComponent<Props, State> {
showStates={this.showStates}
lockStates={this.lockStates}
unlockStates={this.unlockStates}
updateLabelShortcutKey={updateLabelShortcutKey}
/>
);
}

@ -1,29 +0,0 @@
// Copyright (C) 2020 Intel Corporation
//
// SPDX-License-Identifier: MIT
import { connect } from 'react-redux';
import LabelsListComponent from 'components/annotation-page/standard-workspace/objects-side-bar/labels-list';
import { CombinedState } from 'reducers/interfaces';
interface StateToProps {
labelIDs: number[];
listHeight: number;
}
function mapStateToProps(state: CombinedState): StateToProps {
const {
annotation: {
job: { labels },
tabContentHeight: listHeight,
},
} = state;
return {
labelIDs: labels.map((label: any): number => label.id),
listHeight,
};
}
export default connect(mapStateToProps)(LabelsListComponent);

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -68,7 +68,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
type Props = StateToProps & DispatchToProps;
class PropagateConfirmContainer extends React.PureComponent<Props> {
private propagateObject = (): void => {
const { propagateObject, objectState, propagateFrames, frameNumber, stopFrame, jobInstance } = this.props;
const {
propagateObject, objectState, propagateFrames, frameNumber, stopFrame, jobInstance,
} = this.props;
const propagateUpToFrame = Math.min(frameNumber + propagateFrames, stopFrame);
propagateObject(jobInstance, objectState, frameNumber + 1, propagateUpToFrame);
@ -87,7 +89,9 @@ class PropagateConfirmContainer extends React.PureComponent<Props> {
};
public render(): JSX.Element {
const { frameNumber, stopFrame, propagateFrames, cancel, objectState } = this.props;
const {
frameNumber, stopFrame, propagateFrames, cancel, objectState,
} = this.props;
const propagateUpToFrame = Math.min(frameNumber + propagateFrames, stopFrame);

@ -1,26 +0,0 @@
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
import React from 'react';
import { connect } from 'react-redux';
import { CombinedState } from 'reducers/interfaces';
import FiltersModalComponent from 'components/annotation-page/top-bar/filters-modal';
interface StateToProps {
visible: boolean;
}
const mapStateToProps = (state: CombinedState): StateToProps => {
const {
annotation: { filtersPanelVisible: visible },
} = state;
return { visible };
};
function FiltersModalContainer(props: StateToProps): JSX.Element {
const { visible } = props;
return <FiltersModalComponent visible={visible} />;
}
export default connect(mapStateToProps, null)(FiltersModalContainer);

@ -18,6 +18,8 @@ import {
searchAnnotationsAsync,
searchEmptyFrameAsync,
setForceExitAnnotationFlag as setForceExitAnnotationFlagAction,
switchPredictor as switchPredictorAction,
getPredictionsAsync,
showFilters as showFiltersAction,
showStatistics as showStatisticsAction,
switchPlay,
@ -25,7 +27,9 @@ import {
} from 'actions/annotation-actions';
import AnnotationTopBarComponent from 'components/annotation-page/top-bar/top-bar';
import { Canvas } from 'cvat-canvas-wrapper';
import { CombinedState, FrameSpeed, Workspace } from 'reducers/interfaces';
import {
CombinedState, FrameSpeed, Workspace, PredictorState,
} from 'reducers/interfaces';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
interface StateToProps {
@ -48,6 +52,8 @@ interface StateToProps {
normalizedKeyMap: Record<string, string>;
canvasInstance: Canvas;
forceExit: boolean;
predictor: PredictorState;
isTrainingActive: boolean;
}
interface DispatchToProps {
@ -62,6 +68,7 @@ interface DispatchToProps {
searchEmptyFrame(sessionInstance: any, frameFrom: number, frameTo: number): void;
setForceExitAnnotationFlag(forceExit: boolean): void;
changeWorkspace(workspace: Workspace): void;
switchPredictor(predictorEnabled: boolean): void;
}
function mapStateToProps(state: CombinedState): StateToProps {
@ -78,12 +85,14 @@ function mapStateToProps(state: CombinedState): StateToProps {
job: { instance: jobInstance },
canvas: { ready: canvasIsReady, instance: canvasInstance },
workspace,
predictor,
},
settings: {
player: { frameSpeed, frameStep },
workspace: { autoSave, autoSaveInterval },
},
shortcuts: { keyMap, normalizedKeyMap },
plugins: { list },
} = state;
return {
@ -106,6 +115,8 @@ function mapStateToProps(state: CombinedState): StateToProps {
normalizedKeyMap,
canvasInstance,
forceExit,
predictor,
isTrainingActive: list.PREDICT,
};
}
@ -146,6 +157,12 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
setForceExitAnnotationFlag(forceExit: boolean): void {
dispatch(setForceExitAnnotationFlagAction(forceExit));
},
switchPredictor(predictorEnabled: boolean): void {
dispatch(switchPredictorAction(predictorEnabled));
if (predictorEnabled) {
dispatch(getPredictionsAsync());
}
},
};
}
@ -497,11 +514,14 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
redoAction,
workspace,
canvasIsReady,
searchAnnotations,
changeWorkspace,
keyMap,
normalizedKeyMap,
canvasInstance,
predictor,
searchAnnotations,
changeWorkspace,
switchPredictor,
isTrainingActive,
} = this.props;
const preventDefault = (event: KeyboardEvent | undefined): void => {
@ -611,6 +631,8 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
onInputChange={this.onChangePlayerInputValue}
onURLIconClick={this.onURLIconClick}
changeWorkspace={changeWorkspace}
switchPredictor={switchPredictor}
predictor={predictor}
workspace={workspace}
playing={playing}
saving={saving}
@ -636,6 +658,7 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
onUndoClick={this.undo}
onRedoClick={this.redo}
jobInstance={jobInstance}
isTrainingActive={isTrainingActive}
/>
</>
);

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -69,7 +69,9 @@ export class FileManagerContainer extends React.PureComponent<Props> {
}
public render(): JSX.Element {
const { treeData, getTreeData, withRemote, onChangeActiveKey } = this.props;
const {
treeData, getTreeData, withRemote, onChangeActiveKey,
} = this.props;
return (
<FileManagerComponent

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -16,7 +16,9 @@ interface StateToProps {
function mapStateToProps(state: CombinedState): StateToProps {
const { models } = state;
const { interactors, detectors, trackers, reid } = models;
const {
interactors, detectors, trackers, reid,
} = models;
return {
interactors,

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -31,9 +31,9 @@ function mapStateToProps(state: CombinedState): StateToProps {
gettingQuery: tasks.gettingQuery,
numberOfTasks: state.tasks.count,
numberOfVisibleTasks: state.tasks.current.length,
numberOfHiddenTasks: tasks.hideEmpty
? tasks.current.filter((task: Task): boolean => !task.instance.jobs.length).length
: 0,
numberOfHiddenTasks: tasks.hideEmpty ?
tasks.current.filter((task: Task): boolean => !task.instance.jobs.length).length :
0,
};
}

@ -47,6 +47,7 @@ import SVGCubeIcon from './assets/cube-icon.svg';
import SVGResetPerspectiveIcon from './assets/reset-perspective.svg';
import SVGColorizeIcon from './assets/colorize-icon.svg';
import SVGAITools from './assets/ai-tools-icon.svg';
import SVGBrain from './assets/brain.svg';
import SVGOpenCV from './assets/opencv.svg';
import SVGFilterIcon from './assets/object-filter-icon.svg';
@ -93,5 +94,6 @@ export const CubeIcon = React.memo((): JSX.Element => <SVGCubeIcon />);
export const ResetPerspectiveIcon = React.memo((): JSX.Element => <SVGResetPerspectiveIcon />);
export const AIToolsIcon = React.memo((): JSX.Element => <SVGAITools />);
export const ColorizeIcon = React.memo((): JSX.Element => <SVGColorizeIcon />);
export const BrainIcon = React.memo((): JSX.Element => <SVGBrain />);
export const OpenCVIcon = React.memo((): JSX.Element => <SVGOpenCV />);
export const FilterIcon = React.memo((): JSX.Element => <SVGFilterIcon />);

@ -2,6 +2,11 @@
//
// SPDX-License-Identifier: MIT
import React from 'react';
import ReactDOM from 'react-dom';
import { connect, Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import { getAboutAsync } from 'actions/about-actions';
import { authorizedAsync, loadAuthActionsAsync } from 'actions/auth-actions';
import { getFormatsAsync } from 'actions/formats-actions';
@ -14,11 +19,7 @@ import CVATApplication from 'components/cvat-app';
import LayoutGrid from 'components/layout-grid/layout-grid';
import logger, { LogType } from 'cvat-logger';
import createCVATStore, { getCVATStore } from 'cvat-store';
import React from 'react';
import ReactDOM from 'react-dom';
import { KeyMap } from 'utils/mousetrap-react';
import { connect, Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import createRootReducer from 'reducers/root-reducer';
import { resetErrors, resetMessages } from './actions/notification-actions';
import { CombinedState, NotificationsState } from './reducers/interfaces';

@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -38,6 +38,7 @@ const defaultState: AnnotationState = {
activeControl: ActiveControl.CURSOR,
},
job: {
openTime: null,
labels: [],
requestedId: null,
instance: null,
@ -108,6 +109,18 @@ const defaultState: AnnotationState = {
requestReviewDialogVisible: false,
submitReviewDialogVisible: false,
tabContentHeight: 0,
predictor: {
enabled: false,
error: null,
message: '',
projectScore: 0,
fetching: false,
annotatedFrames: [],
timeRemaining: 0,
progress: 0,
annotationAmount: 0,
mediaAmount: 0,
},
workspace: Workspace.STANDARD,
};
@ -129,6 +142,7 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
const {
job,
states,
openTime,
frameNumber: number,
frameFilename: filename,
colors,
@ -148,6 +162,7 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
...state,
job: {
...state.job,
openTime,
fetching: false,
instance: job,
labels: job.task.labels,
@ -445,9 +460,22 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
};
}
case AnnotationActionTypes.REMEMBER_CREATED_OBJECT: {
const {
shapeType, labelID, objectType, points, activeControl, rectDrawingMethod,
} = action.payload;
const { payload } = action;
let { activeControl } = state.canvas;
if (payload.activeShapeType === ShapeType.RECTANGLE) {
activeControl = ActiveControl.DRAW_RECTANGLE;
} else if (payload.activeShapeType === ShapeType.POLYGON) {
activeControl = ActiveControl.DRAW_POLYGON;
} else if (payload.activeShapeType === ShapeType.POLYLINE) {
activeControl = ActiveControl.DRAW_POLYLINE;
} else if (payload.activeShapeType === ShapeType.POINTS) {
activeControl = ActiveControl.DRAW_POINTS;
} else if (payload.activeShapeType === ShapeType.CUBOID) {
activeControl = ActiveControl.DRAW_CUBOID;
} else if (payload.activeObjectType === ObjectType.TAG) {
activeControl = ActiveControl.CURSOR;
}
return {
...state,
@ -460,12 +488,9 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
activeControl,
},
drawing: {
...state.drawing,
...payload,
activeInteractor: undefined,
activeLabelID: labelID,
activeNumOfPoints: points,
activeObjectType: objectType,
activeShapeType: shapeType,
activeRectDrawingMethod: rectDrawingMethod,
},
};
}
@ -1093,6 +1118,47 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
workspace,
};
}
case AnnotationActionTypes.UPDATE_PREDICTOR_STATE: {
const { payload } = action;
return {
...state,
predictor: {
...state.predictor,
...payload,
},
};
}
case AnnotationActionTypes.GET_PREDICTIONS: {
return {
...state,
predictor: {
...state.predictor,
fetching: true,
},
};
}
case AnnotationActionTypes.GET_PREDICTIONS_SUCCESS: {
const { frame } = action.payload;
const annotatedFrames = [...state.predictor.annotatedFrames, frame];
return {
...state,
predictor: {
...state.predictor,
fetching: false,
annotatedFrames,
},
};
}
case AnnotationActionTypes.GET_PREDICTIONS_FAILED: {
return {
...state,
predictor: {
...state.predictor,
fetching: false,
},
};
}
case AnnotationActionTypes.RESET_CANVAS: {
return {
...state,

@ -111,6 +111,7 @@ export enum SupportedPlugins {
GIT_INTEGRATION = 'GIT_INTEGRATION',
ANALYTICS = 'ANALYTICS',
MODELS = 'MODELS',
PREDICT = 'PREDICT',
}
export type PluginsList = {
@ -301,6 +302,9 @@ export interface NotificationsState {
commentingIssue: null | ErrorState;
submittingReview: null | ErrorState;
};
predictor: {
prediction: null | ErrorState;
};
};
messages: {
tasks: {
@ -367,6 +371,19 @@ export enum Rotation {
CLOCKWISE90,
}
export interface PredictorState {
timeRemaining: number;
progress: number;
projectScore: number;
message: string;
error: Error | null;
enabled: boolean;
fetching: boolean;
annotationAmount: number;
mediaAmount: number;
annotatedFrames: number[];
}
export interface AnnotationState {
activities: {
loads: {
@ -388,6 +405,7 @@ export interface AnnotationState {
activeControl: ActiveControl;
};
job: {
openTime: null | number;
labels: any[];
requestedId: number | null;
instance: any | null | undefined;
@ -462,6 +480,7 @@ export interface AnnotationState {
appearanceCollapsed: boolean;
tabContentHeight: number;
workspace: Workspace;
predictor: PredictorState;
aiToolsRef: MutableRefObject<any>;
}

@ -102,6 +102,9 @@ const defaultState: NotificationsState = {
resolvingIssue: null,
submittingReview: null,
},
predictor: {
prediction: null,
},
},
messages: {
tasks: {
@ -843,6 +846,7 @@ export default function (state = defaultState, action: AnyAction): Notifications
'Could not upload annotations for the ' +
`<a href="/tasks/${taskID}/jobs/${jobID}" target="_blank">job ${taskID}</a>`,
reason: error.toString(),
className: 'cvat-notification-notice-upload-annotations-fail',
},
},
},
@ -1104,6 +1108,21 @@ export default function (state = defaultState, action: AnyAction): Notifications
},
};
}
case AnnotationActionTypes.GET_PREDICTIONS_FAILED: {
return {
...state,
errors: {
...state.errors,
predictor: {
...state.errors.predictor,
prediction: {
message: 'Could not fetch prediction data',
reason: action.payload.error,
},
},
},
};
}
case BoundariesActionTypes.RESET_AFTER_ERROR:
case AuthActionTypes.LOGOUT_SUCCESS: {
return { ...defaultState };

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2021 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -13,6 +13,7 @@ const defaultState: PluginsState = {
GIT_INTEGRATION: false,
ANALYTICS: false,
MODELS: false,
PREDICT: false,
},
};

@ -287,10 +287,16 @@ const defaultKeyMap = ({
},
TOGGLE_LAYOUT_GRID: {
name: 'Toggle layout grid',
description: 'Is used in development',
description: 'The grid is used to UI development',
sequences: ['ctrl+alt+enter'],
action: 'keydown',
},
SWITCH_LABEL: {
name: 'Switch label',
description: 'Changes a label for an activated object or for the next drawn object if no objects are activated',
sequences: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'].map((val: string): string => `ctrl+${val}`),
action: 'keydown',
},
} as any) as KeyMap;
const defaultState: ShortcutsState = {

@ -17,7 +17,7 @@ export interface KeyMap {
}
export interface Handlers {
[index: string]: (event: KeyboardEvent) => void;
[index: string]: (event: KeyboardEvent, shortcut: string) => void;
}
interface Props {

@ -4,6 +4,6 @@
from cvat.utils.version import get_version
VERSION = (1, 3, 0, 'final', 0)
VERSION = (1, 4, 0, 'final', 0)
__version__ = get_version(VERSION)

@ -754,7 +754,7 @@ class TrackManager(ObjectManager):
break
if not prev_shape["outside"]:
shape = copy(prev_shape)
shape = deepcopy(prev_shape)
shape["frame"] = end_frame
shapes.extend(interpolate(prev_shape, shape))

@ -435,8 +435,9 @@ class TaskData:
def _get_filename(path):
return osp.splitext(path)[0]
def match_frame(self, path, root_hint=None):
path = self._get_filename(path)
def match_frame(self, path, root_hint=None, path_has_ext=True):
if path_has_ext:
path = self._get_filename(path)
match = self._frame_mapping.get(path)
if not match and root_hint and not path.startswith(root_hint):
path = osp.join(root_hint, path)
@ -611,7 +612,7 @@ def match_dm_item(item, task_data, root_hint=None):
if frame_number is None and item.has_image:
frame_number = task_data.match_frame(item.id + item.image.ext, root_hint)
if frame_number is None:
frame_number = task_data.match_frame(item.id, root_hint)
frame_number = task_data.match_frame(item.id, root_hint, path_has_ext=False)
if frame_number is None:
frame_number = cast(item.attributes.get('frame', item.id), int)
if frame_number is None and is_video:

@ -11,6 +11,7 @@
- [How to add a format](#how-to-add)
- [Format descriptions](#formats)
- [CVAT](#cvat)
- [Datumaro](#datumaro)
- [LabelMe](#labelme)
- [MOT](#mot)
- [MOTS](#mots)
@ -175,7 +176,7 @@ features, so it can be used to make data backups.
- [Format specification](/cvat/apps/documentation/xml_format.md)
#### CVAT for images dumper
#### CVAT for images export
Downloaded file: a ZIP file of the following structure:
@ -189,7 +190,7 @@ taskname.zip/
- tracks are split by frames
#### CVAT for videos dumper
#### CVAT for videos export
Downloaded file: a ZIP file of the following structure:
@ -207,6 +208,17 @@ taskname.zip/
Uploaded file: an XML file or a ZIP file of the structures above
### Datumaro format <a id="datumaro" />
[Datumaro](https://github.com/openvinotoolkit/datumaro/) is a tool, which can
help with complex dataset and annotation transformations, format conversions,
dataset statistics, merging, custom formats etc. It is used as a provider
of dataset support in CVAT, so basically, everything possible in CVAT
is possible in Datumaro too, but Datumaro can offer dataset operations.
- supported annotations: any 2D shapes, labels
- supported attributes: any
### [Pascal VOC](http://host.robots.ox.ac.uk/pascal/VOC/)<a id="voc" />
- [Format specification](http://host.robots.ox.ac.uk/pascal/VOC/voc2012/devkit_doc.pdf)
@ -219,9 +231,10 @@ Uploaded file: an XML file or a ZIP file of the structures above
- supported attributes:
- `occluded`
- `occluded` (both UI option and a separate attribute)
- `truncated` and `difficult` (should be defined for labels as `checkbox` -es)
- action attributes (import only, should be defined as `checkbox` -es)
- arbitrary attributes (in the `attributes` secion of XML files)
#### Pascal VOC export
@ -229,7 +242,7 @@ Downloaded file: a zip archive of the following structure:
```bash
taskname.zip/
├── JpegImages/
├── JPEGImages/
│   ├── <image_name1>.jpg
│   ├── <image_name2>.jpg
│   └── <image_nameN>.jpg
@ -499,17 +512,47 @@ zip images.zip -j -@ < train.txt
- [Format specification](http://cocodataset.org/#format-data)
#### COCO dumper description
#### COCO export
Downloaded file: single unpacked `json`.
Downloaded file: a zip archive with following structure:
```bash
archive.zip/
├── images/
│ ├── <image_name1.ext>
│ ├── <image_name2.ext>
│ └── ...
└── annotations/
   └── instances_default.json
```
- supported annotations: Polygons, Rectangles
- supported attributes:
- `is_crowd` (checkbox or integer with values 0 and 1) -
specifies that the instance (an object group) should have an
RLE-encoded mask in the `segmentation` field. All the grouped shapes
are merged into a single mask, the largest one defines all
the object properties
- `score` (number) - the annotation `score` field
- arbitrary attributes - will be stored in the `attributes` annotation section
*Note*: there is also a [support for COCO keypoints over Datumaro](https://github.com/openvinotoolkit/cvat/issues/2910#issuecomment-726077582)
#### COCO loader description
1. Install [Datumaro](https://github.com/openvinotoolkit/datumaro)
`pip install datumaro`
1. Export the task in the `Datumaro` format, unzip
1. Export the Datumaro project in `coco` / `coco_person_keypoints` formats
`datum export -f coco -p path/to/project [-- --save-images]`
Uploaded file: single unpacked `*.json` .
This way, one can export CVAT points as single keypoints or
keypoint lists (without the `visibility` COCO flag).
- supported annotations: Polygons, Rectangles (if `segmentation` field is empty)
#### COCO import
Uploaded file: a single unpacked `*.json` or a zip archive with the structure above (without images).
- supported annotations: Polygons, Rectangles (if the `segmentation` field is empty)
#### How to create a task from MS COCO dataset
@ -559,25 +602,48 @@ image_feature_description = {
}
```
#### TFRecord dumper description
#### TFRecord export
Downloaded file: a zip archive with following structure:
```bash
taskname.zip/
├── task2.tfrecord
├── default.tfrecord
└── label_map.pbtxt
# label_map.pbtxt
item {
id: 1
name: 'label_0'
}
item {
id: 2
name: 'label_1'
}
...
```
- supported annotations: Rectangles
- supported annotations: Rectangles, Polygons (as masks, manually over [Datumaro](https://github.com/openvinotoolkit/datumaro/blob/develop/docs/user_manual.md))
#### TFRecord loader description
How to export masks:
1. Export annotations in `Datumaro` format
1. Apply `polygons_to_masks` and `boxes_to_masks` transforms
```bash
datum transform -t polygons_to_masks -p path/to/proj -o ptm
datum transform -t boxes_to_masks -p ptm -o btm
```
1. Export in the `TF Detection API` format
```bash
datum export -f tf_detection_api -p btm [-- --save-images]
```
#### TFRecord import
Uploaded file: a zip archive of following structure:
```bash
taskname.zip/
└── task2.tfrecord
└── <any name>.tfrecord
```
- supported annotations: Rectangles
@ -706,7 +772,7 @@ python create_pascal_tf_record.py --data_dir <path to VOCdevkit> --set train --y
### [MOT sequence](https://arxiv.org/pdf/1906.04567.pdf)<a id="mot" />
#### MOT Dumper
#### MOT export
Downloaded file: a zip archive of the following structure:
@ -735,7 +801,7 @@ person
- supported annotations: Rectangle shapes and tracks
- supported attributes: `visibility` (number), `ignored` (checkbox)
#### MOT Loader
#### MOT import
Uploaded file: a zip archive of the structure above or:
@ -749,7 +815,7 @@ taskname.zip/
### [MOTS PNG](https://www.vision.rwth-aachen.de/page/mots)<a id="mots" />
#### MOTS PNG Dumper
#### MOTS PNG export
Downloaded file: a zip archive of the following structure:
@ -773,7 +839,7 @@ person
- supported annotations: Rectangle and Polygon tracks
#### MOTS PNG Loader
#### MOTS PNG import
Uploaded file: a zip archive of the structure above
@ -781,7 +847,7 @@ Uploaded file: a zip archive of the structure above
### [LabelMe](http://labelme.csail.mit.edu/Release3.0)<a id="labelme" />
#### LabelMe Dumper
#### LabelMe export
Downloaded file: a zip archive of the following structure:
@ -793,7 +859,7 @@ taskname.zip/
- supported annotations: Rectangles, Polygons (with attributes)
#### LabelMe Loader
#### LabelMe import
Uploaded file: a zip archive of the following structure:
@ -811,7 +877,7 @@ taskname.zip/
### [ImageNet](http://www.image-net.org)<a id="imagenet" />
#### ImageNet Dumper
#### ImageNet export
Downloaded file: a zip archive of the following structure:
@ -835,7 +901,7 @@ taskname.zip/
- supported annotations: Labels
#### ImageNet Loader
#### ImageNet import
Uploaded file: a zip archive of the structure above
@ -843,7 +909,7 @@ Uploaded file: a zip archive of the structure above
### [CamVid](http://mi.eng.cam.ac.uk/research/projects/VideoRec/CamVid/)<a id="camvid" />
#### CamVid Dumper
#### CamVid export
Downloaded file: a zip archive of the following structure:
@ -873,7 +939,7 @@ has own color which corresponds to a label.
- supported annotations: Rectangles, Polygons
#### CamVid Loader
#### CamVid import
Uploaded file: a zip archive of the structure above
@ -881,7 +947,7 @@ Uploaded file: a zip archive of the structure above
### [WIDER Face](http://shuoyang1213.me/WIDERFACE/)<a id="widerface" />
#### WIDER Face Dumper
#### WIDER Face export
Downloaded file: a zip archive of the following structure:
@ -899,19 +965,21 @@ taskname.zip/
```
- supported annotations: Rectangles (with attributes), Labels
- supported attributes: `blur`, `expression`, `illumination`,
`occluded` (both the annotation property & an attribute), `pose`, `invalid`
- supported attributes:
- `blur`, `expression`, `illumination`, `pose`, `invalid`
- `occluded` (both the annotation property & an attribute)
#### WIDER Face Loader
#### WIDER Face import
Uploaded file: a zip archive of the structure above
- supported annotations: Rectangles (with attributes), Labels
- supported attributes: `blur`, `expression`, `illumination`, `occluded`, `pose`, `invalid`
- supported attributes:
- `blur`, `expression`, `illumination`, `occluded`, `pose`, `invalid`
### [VGGFace2](https://github.com/ox-vgg/vgg_face2)<a id="vggface2" />
#### VGGFace2 Dumper
#### VGGFace2 export
Downloaded file: a zip archive of the following structure:
@ -934,7 +1002,7 @@ label1 <class1>
- supported annotations: Rectangles, Points (landmarks - groups of 5 points)
#### VGGFace2 Loader
#### VGGFace2 import
Uploaded file: a zip archive of the structure above
@ -942,7 +1010,7 @@ Uploaded file: a zip archive of the structure above
### [Market-1501](https://www.aitribune.com/dataset/2018051063)<a id="market1501" />
#### Market-1501 Dumper
#### Market-1501 export
Downloaded file: a zip archive of the following structure:
@ -970,7 +1038,7 @@ s1 - sequence
- supported annotations: Label `market-1501` with atrributes (`query`, `person_id`, `camera_id`)
#### Market-1501 Loader
#### Market-1501 import
Uploaded file: a zip archive of the structure above
@ -978,7 +1046,7 @@ Uploaded file: a zip archive of the structure above
### [ICDAR13/15](https://rrc.cvc.uab.es/?ch=2)<a id="icdar" />
#### ICDAR13/15 Dumper
#### ICDAR13/15 export
Downloaded file: a zip archive of the following structure:
@ -1027,7 +1095,7 @@ taskname.zip/
- supported annotations: Rectangles and Polygons with label `icdar`
and attributes `index`, `text`, `color`, `center`
#### ICDAR13/15 Loader
#### ICDAR13/15 import
Uploaded file: a zip archive of the structure above

@ -19,7 +19,8 @@ def _export(dst_file, task_data, save_images=False):
dataset = Dataset.from_extractors(CvatTaskDataExtractor(
task_data, include_images=save_images), env=dm_env)
with TemporaryDirectory() as temp_dir:
dataset.export(temp_dir, 'coco_instances', save_images=save_images)
dataset.export(temp_dir, 'coco_instances', save_images=save_images,
merge_images=True)
make_zip_archive(temp_dir, dst_file)

@ -441,8 +441,9 @@ def load(file_object, annotations):
elif el.tag == 'image':
image_is_opened = True
frame_id = annotations.abs_frame_id(match_dm_item(
DatasetItem(id=el.attrib['name'],
attributes={'frame': el.attrib['id']}
DatasetItem(id=osp.splitext(el.attrib['name'])[0],
attributes={'frame': el.attrib['id']},
image=el.attrib['name']
),
task_data=annotations
))

@ -496,6 +496,7 @@ class TaskExportTest(_DbTestBase):
self.assertTrue(frame.frame in range(6, 10))
self.assertEqual(i + 1, 4)
class FrameMatchingTest(_DbTestBase):
def _generate_task_images(self, paths): # pylint: disable=no-self-use
f = BytesIO()
@ -586,3 +587,327 @@ class FrameMatchingTest(_DbTestBase):
root = find_dataset_root(dataset, task_data)
self.assertEqual(expected, root)
class TaskAnnotationsImportTest(_DbTestBase):
def _generate_custom_annotations(self, annotations, task):
self._put_api_v1_task_id_annotations(task["id"], annotations)
return annotations
def _generate_task_images(self, count, name="image"):
images = {
"client_files[%d]" % i: generate_image_file("image_%d.jpg" % i)
for i in range(count)
}
images["image_quality"] = 75
return images
def _generate_task(self, images, annotation_format, **overrides):
labels = []
if annotation_format in ["ICDAR Recognition 1.0",
"ICDAR Localization 1.0"]:
labels = [{
"name": "icdar",
"attributes": [{
"name": "text",
"mutable": False,
"input_type": "text",
"values": ["word1", "word2"]
}]
}]
elif annotation_format == "ICDAR Segmentation 1.0":
labels = [{
"name": "icdar",
"attributes": [
{
"name": "text",
"mutable": False,
"input_type": "text",
"values": ["word_1", "word_2", "word_3"]
},
{
"name": "index",
"mutable": False,
"input_type": "number",
"values": ["0", "1", "2"]
},
{
"name": "color",
"mutable": False,
"input_type": "text",
"values": ["100 110 240", "10 15 20", "120 128 64"]
},
{
"name": "center",
"mutable": False,
"input_type": "text",
"values": ["1 2", "2 4", "10 45"]
},
]
}]
elif annotation_format == "Market-1501 1.0":
labels = [{
"name": "market-1501",
"attributes": [
{
"name": "query",
"mutable": False,
"input_type": "select",
"values": ["True", "False"]
},
{
"name": "camera_id",
"mutable": False,
"input_type": "number",
"values": ["0", "1", "2", "3"]
},
{
"name": "person_id",
"mutable": False,
"input_type": "number",
"values": ["1", "2", "3"]
},
]
}]
else:
labels = [
{
"name": "car",
"attributes": [
{
"name": "model",
"mutable": False,
"input_type": "select",
"default_value": "mazda",
"values": ["bmw", "mazda", "renault"]
},
{
"name": "parked",
"mutable": True,
"input_type": "checkbox",
"default_value": False
}
]
},
{"name": "person"}
]
task = {
"name": "my task #1",
"overlap": 0,
"segment_size": 100,
"labels": labels
}
task.update(overrides)
return self._create_task(task, images)
def _generate_annotations(self, task, annotation_format):
shapes = []
tracks = []
tags = []
if annotation_format in ["ICDAR Recognition 1.0",
"ICDAR Localization 1.0"]:
shapes = [{
"frame": 0,
"label_id": task["labels"][0]["id"],
"group": 0,
"source": "manual",
"attributes": [
{
"spec_id": task["labels"][0]["attributes"][0]["id"],
"value": task["labels"][0]["attributes"][0]["values"][0]
},
],
"points": [1.0, 2.1, 10.6, 53.22],
"type": "rectangle",
"occluded": False,
}]
elif annotation_format == "Market-1501 1.0":
tags = [{
"frame": 1,
"label_id": task["labels"][0]["id"],
"group": 0,
"source": "manual",
"attributes": [
{
"spec_id": task["labels"][0]["attributes"][0]["id"],
"value": task["labels"][0]["attributes"][0]["values"][1]
},
{
"spec_id": task["labels"][0]["attributes"][1]["id"],
"value": task["labels"][0]["attributes"][1]["values"][2]
},
{
"spec_id": task["labels"][0]["attributes"][2]["id"],
"value": task["labels"][0]["attributes"][2]["values"][0]
}
],
}]
elif annotation_format == "ICDAR Segmentation 1.0":
shapes = [{
"frame": 0,
"label_id": task["labels"][0]["id"],
"group": 0,
"source": "manual",
"attributes": [
{
"spec_id": task["labels"][0]["attributes"][0]["id"],
"value": task["labels"][0]["attributes"][0]["values"][0]
},
{
"spec_id": task["labels"][0]["attributes"][1]["id"],
"value": task["labels"][0]["attributes"][1]["values"][0]
},
{
"spec_id": task["labels"][0]["attributes"][2]["id"],
"value": task["labels"][0]["attributes"][2]["values"][1]
},
{
"spec_id": task["labels"][0]["attributes"][3]["id"],
"value": task["labels"][0]["attributes"][3]["values"][2]
}
],
"points": [1.0, 2.1, 10.6, 53.22],
"type": "rectangle",
"occluded": False,
}]
elif annotation_format == "VGGFace2 1.0":
shapes = [{
"frame": 1,
"label_id": task["labels"][1]["id"],
"group": None,
"source": "manual",
"attributes": [],
"points": [2.0, 2.1, 40, 50.7],
"type": "rectangle",
"occluded": False
}]
else:
rectangle_shape_wo_attrs = {
"frame": 1,
"label_id": task["labels"][1]["id"],
"group": 0,
"source": "manual",
"attributes": [],
"points": [2.0, 2.1, 40, 50.7],
"type": "rectangle",
"occluded": False,
}
rectangle_shape_with_attrs = {
"frame": 0,
"label_id": task["labels"][0]["id"],
"group": 0,
"source": "manual",
"attributes": [
{
"spec_id": task["labels"][0]["attributes"][0]["id"],
"value": task["labels"][0]["attributes"][0]["values"][0]
},
{
"spec_id": task["labels"][0]["attributes"][1]["id"],
"value": task["labels"][0]["attributes"][1]["default_value"]
}
],
"points": [1.0, 2.1, 10.6, 53.22],
"type": "rectangle",
"occluded": False,
}
track_wo_attrs = {
"frame": 0,
"label_id": task["labels"][1]["id"],
"group": 0,
"source": "manual",
"attributes": [],
"shapes": [
{
"frame": 0,
"attributes": [],
"points": [1.0, 2.1, 10.6, 53.22, 100, 300.222],
"type": "polygon",
"occluded": False,
"outside": False
}
]
}
tag_wo_attrs = {
"frame": 0,
"label_id": task["labels"][0]["id"],
"group": None,
"attributes": []
}
tag_with_attrs = {
"frame": 1,
"label_id": task["labels"][0]["id"],
"group": 3,
"source": "manual",
"attributes": [
{
"spec_id": task["labels"][0]["attributes"][0]["id"],
"value": task["labels"][0]["attributes"][0]["values"][1]
},
{
"spec_id": task["labels"][0]["attributes"][1]["id"],
"value": task["labels"][0]["attributes"][1]["default_value"]
}
],
}
if annotation_format == "VGGFace2 1.0":
shapes = rectangle_shape_wo_attrs
elif annotation_format == "CVAT 1.1":
shapes = [rectangle_shape_wo_attrs,
rectangle_shape_with_attrs]
tags = [tag_with_attrs, tag_wo_attrs]
elif annotation_format == "MOTS PNG 1.0":
tracks = [track_wo_attrs]
else:
shapes = [rectangle_shape_wo_attrs,
rectangle_shape_with_attrs]
tags = tag_wo_attrs
tracks = track_wo_attrs
annotations = {
"version": 0,
"tags": tags,
"shapes": shapes,
"tracks": tracks
}
return self._generate_custom_annotations(annotations, task)
def _test_can_import_annotations(self, task, import_format):
with tempfile.TemporaryDirectory() as temp_dir:
file_path = osp.join(temp_dir, import_format)
export_format = import_format
if import_format == "CVAT 1.1":
export_format = "CVAT for images 1.1"
dm.task.export_task(task["id"], file_path, export_format)
expected_ann = TaskAnnotation(task["id"])
expected_ann.init_from_db()
dm.task.import_task_annotations(task["id"],
file_path, import_format)
actual_ann = TaskAnnotation(task["id"])
actual_ann.init_from_db()
self.assertEqual(len(expected_ann.data), len(actual_ann.data))
def test_can_import_annotations_for_image_with_dots_in_filename(self):
for f in dm.views.get_import_formats():
format_name = f.DISPLAY_NAME
images = self._generate_task_images(3, "img0.0.0")
task = self._generate_task(images, format_name)
self._generate_annotations(task, format_name)
with self.subTest(format=format_name):
if not f.ENABLED:
self.skipTest("Format is disabled")
self._test_can_import_annotations(task, format_name)

@ -10,6 +10,7 @@
- [Advanced settings](#advanced-settings)
- [Share path](#share-path)
- [Email verification](#email-verification)
- [Deploy CVAT on the Scaleway public cloud](#deploy-cvat-on-the-scaleway-public-cloud)
- [Deploy secure CVAT instance with HTTPS](#deploy-secure-cvat-instance-with-https)
- [Prerequisites](#prerequisites)
- [Roadmap](#roadmap)
@ -374,6 +375,10 @@ This depends on the email server you are using and is not covered in this tutori
[Django SMTP backend configuration](https://docs.djangoproject.com/en/3.1/topics/email/#django.core.mail.backends.smtp.EmailBackend)
for details.
### Deploy CVAT on the Scaleway public cloud
Please follow [this tutorial](https://blog.scaleway.com/smart-data-annotation-for-your-computer-vision-projects-cvat-on-scaleway/) to install and set up remote access to CVAT on a Scaleway cloud instance with data in a mounted object storage bucket.
### Deploy secure CVAT instance with HTTPS
Certificates (issued by let's encrypt) to cloud instance.

@ -60,10 +60,10 @@
```bash
nuctl deploy --project-name cvat \
--path `pwd`/tensorflow/matterport/mask_rcnn/nuclio \
--path serverless/tensorflow/matterport/mask_rcnn/nuclio \
--platform local --base-image tensorflow/tensorflow:1.15.5-gpu-py3 \
--desc "GPU based implementation of Mask RCNN on Python 3, Keras, and TensorFlow." \
--image cvat/tf.matterport.mask_rcnn_gpu
--image cvat/tf.matterport.mask_rcnn_gpu \
--triggers '{"myHttpTrigger": {"maxWorkers": 1}}' \
--resource-limit nvidia.com/gpu=1
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save