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: branches:
- '*' - '*'
jobs: jobs:
build: Unit_testing:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - 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: with:
node-version: 12 path: /tmp/cvat_cache_server
- name: Build CVAT 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: env:
HOST_COVERAGE_DATA_DIR: ${{ github.workspace }} HOST_COVERAGE_DATA_DIR: ${{ github.workspace }}
CONTAINER_COVERAGE_DATA_DIR: '/coverage_data' CONTAINER_COVERAGE_DATA_DIR: "/coverage_data"
DJANGO_SU_NAME: 'admin'
DJANGO_SU_EMAIL: 'admin@localhost.company'
DJANGO_SU_PASSWORD: '12qwaszx'
run: | 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 '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 -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 - name: Uploading code coverage results as an artifact
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" if: github.ref == 'refs/heads/develop'
- name: Code instrumentation 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: | run: |
npm ci npm ci
npm run coverage npm run coverage
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build docker-compose -f docker-compose.yml -f docker-compose.dev.yml build cvat_ui
- name: End-to-end testing - 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: | 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 cd ./tests
npm ci 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 - name: Uploading cypress screenshots as an artifact
if: failure() if: failure()
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: cypress_screenshots name: cypress_screenshots_${{ matrix.specs }}
path: ${{ github.workspace }}/tests/cypress/screenshots 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: env:
HOST_COVERAGE_DATA_DIR: ${{ github.workspace }} HOST_COVERAGE_DATA_DIR: ${{ github.workspace }}
CONTAINER_COVERAGE_DATA_DIR: "/coverage_data" CONTAINER_COVERAGE_DATA_DIR: "/coverage_data"
COVERALLS_SERVICE_NAME: github COVERALLS_SERVICE_NAME: github
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: | run: |
mv ./tests/.nyc_output ./ npx nyc report --reporter=text-lcov >> ${HOST_COVERAGE_DATA_DIR}/lcov.info
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.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'
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'

@ -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 #Ignore Cypress tests temp files
/tests/cypress/fixtures /tests/cypress/fixtures
/tests/cypress/screenshots /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-recommended',
'remark-preset-lint-consistent', 'remark-preset-lint-consistent',
['remark-lint-list-item-indent', 'space'], ['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-line-length', 120],
['remark-lint-maximum-heading-length', 120], ['remark-lint-maximum-heading-length', 120],
['remark-lint-strong-marker', '*'], ['remark-lint-strong-marker', '*'],

@ -1,5 +1,5 @@
{ {
"python.pythonPath": ".env/bin/python", "eslint.enable": true,
"eslint.probe": [ "eslint.probe": [
"javascript", "javascript",
"typescript", "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/), 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). 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 ## [1.3.0] - 3/31/2021
### Added ### 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>) - 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>) - `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 - Files and folders under share path are now alphabetically sorted
### Removed ### 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>) - 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>) - 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) - 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 - Add option to upload annotations upon task creation on CLI
- Polygon and polylines interpolation (<https://github.com/opencv/cvat/pull/1571>) - 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>) - 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 ### Changed
- Removed information about e-mail from the basic user information (<https://github.com/opencv/cvat/pull/1627>) - 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>) - 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>) - 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>) - 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>) - 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>) - 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>) - 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>) - 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>) - 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>) - 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 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>) - 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>) - 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>) - 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>) - 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 ### 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 ### Fixed
- Auto annotation, TF annotation and Auto segmentation apps (https://github.com/opencv/cvat/pull/1409) - 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>) - 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>) - 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>) - 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 - Option to display shape text always
- Dedicated message with clarifications when share is unmounted (https://github.com/opencv/cvat/pull/1373) - 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 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 - Tutorial: instructions for CVAT over HTTPS
- Deep extreme cut (semi-automatic segmentation) to the new UI (https://github.com/opencv/cvat/pull/1398) - 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 - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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 - Yolo Auto Annotation Script
- Auto segmentation using Mask_RCNN component (Keras+Tensorflow Mask R-CNN Segmentation) - Auto segmentation using Mask_RCNN component (Keras+Tensorflow Mask R-CNN Segmentation)
- REST API to export an annotation task (images + annotations) - 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 - Text Detection Auto Annotation Script in OpenVINO format for version 4
- Added in OpenVINO Semantic Segmentation for roads - Added in OpenVINO Semantic Segmentation for roads
- Ability to visualize labels when using Auto Annotation runner - 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 ### Deprecated
- "Flip images" flag in the create task dialog will be removed. Rotation functionality in client part have been added instead. - "Flip images" flag in the create task dialog will be removed.
Rotation functionality in client part have been added instead.
### Removed
-
### Fixed ### 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 - Silk profiler to improve development process
- Admin panel can be used to edit labels and attributes for annotation tasks - 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 - 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) - 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) - 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 - Per task/job permissions to create/access/change/delete tasks and annotations
- Documentation was improved - Documentation was improved
- Timeout for creating tasks was increased (from 1h to 4h) (#136) - 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 ### Fixed

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

@ -1,10 +1,11 @@
# Computer Vision Annotation Tool (CVAT) # Computer Vision Annotation Tool (CVAT)
[![CI](https://github.com/openvinotoolkit/cvat/workflows/CI/badge.svg?branch=develop)](https://github.com/openvinotoolkit/cvat/actions) [![CI][ci-img]][ci-url]
[![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][gitter-img]][gitter-url]
[![Gitter chat](https://badges.gitter.im/opencv-cvat/gitter.png)](https://gitter.im/opencv-cvat) [![Coverage Status][coverage-img]][coverage-url]
[![Coverage Status](https://coveralls.io/repos/github/openvinotoolkit/cvat/badge.svg?branch=develop)](https://coveralls.io/github/openvinotoolkit/cvat?branch=develop) [![server pulls][docker-server-pulls-img]][docker-server-image-url]
[![DOI](https://zenodo.org/badge/139156354.svg)](https://zenodo.org/badge/latestdoi/139156354) [![ui pulls][docker-ui-pulls-img]][docker-ui-image-url]
[![DOI][doi-img]][doi-url]
CVAT is free, online, interactive video and image annotation CVAT is free, online, interactive video and image annotation
tool for computer vision. It is being used by our team to 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 | | Segmentation masks from [PASCAL VOC](http://host.robots.ox.ac.uk/pascal/VOC/) | X | X |
| [YOLO](https://pjreddie.com/darknet/yolo/) | X | X | | [YOLO](https://pjreddie.com/darknet/yolo/) | X | X |
| [MS COCO Object Detection](http://cocodataset.org/#format-data) | 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 | | [MOT](https://motchallenge.net/) | X | X |
| [LabelMe 3.0](http://labelme.csail.mit.edu/Release3.0) | X | X | | [LabelMe 3.0](http://labelme.csail.mit.edu/Release3.0) | X | X |
| [ImageNet](http://www.image-net.org) | 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 ## 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 vision AI platform that fully integrates CVAT with scalable data processing
and parallelized training pipelines. 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", "name": "cvat-canvas",
"version": "2.4.1", "version": "2.4.3",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -1235,6 +1235,12 @@
"integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==", "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==",
"dev": true "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": { "@types/minimatch": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@ -1670,13 +1676,113 @@
"dev": true "dev": true
}, },
"array-includes": { "array-includes": {
"version": "3.0.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz",
"integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==",
"dev": true, "dev": true,
"requires": { "requires": {
"define-properties": "^1.1.2", "call-bind": "^1.0.2",
"es-abstract": "^1.7.0" "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": { "array-union": {
@ -1700,6 +1806,114 @@
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true "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": { "asn1": {
"version": "0.2.4", "version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "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": { "caller-callsite": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
@ -3542,22 +3766,34 @@
"dev": true "dev": true
}, },
"eslint-import-resolver-node": { "eslint-import-resolver-node": {
"version": "0.3.2", "version": "0.3.4",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz",
"integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==",
"dev": true, "dev": true,
"requires": { "requires": {
"debug": "^2.6.9", "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": { "eslint-module-utils": {
"version": "2.4.1", "version": "2.6.0",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz", "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz",
"integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==", "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==",
"dev": true, "dev": true,
"requires": { "requires": {
"debug": "^2.6.8", "debug": "^2.6.9",
"pkg-dir": "^2.0.0" "pkg-dir": "^2.0.0"
}, },
"dependencies": { "dependencies": {
@ -3616,22 +3852,24 @@
} }
}, },
"eslint-plugin-import": { "eslint-plugin-import": {
"version": "2.18.2", "version": "2.22.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz",
"integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==", "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==",
"dev": true, "dev": true,
"requires": { "requires": {
"array-includes": "^3.0.3", "array-includes": "^3.1.1",
"array.prototype.flat": "^1.2.3",
"contains-path": "^0.1.0", "contains-path": "^0.1.0",
"debug": "^2.6.9", "debug": "^2.6.9",
"doctrine": "1.5.0", "doctrine": "1.5.0",
"eslint-import-resolver-node": "^0.3.2", "eslint-import-resolver-node": "^0.3.4",
"eslint-module-utils": "^2.4.0", "eslint-module-utils": "^2.6.0",
"has": "^1.0.3", "has": "^1.0.3",
"minimatch": "^3.0.4", "minimatch": "^3.0.4",
"object.values": "^1.1.0", "object.values": "^1.1.1",
"read-pkg-up": "^2.0.0", "read-pkg-up": "^2.0.0",
"resolve": "^1.11.0" "resolve": "^1.17.0",
"tsconfig-paths": "^3.9.0"
}, },
"dependencies": { "dependencies": {
"doctrine": { "doctrine": {
@ -3643,6 +3881,16 @@
"esutils": "^2.0.2", "esutils": "^2.0.2",
"isarray": "^1.0.0" "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" "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": { "get-stdin": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-0.1.0.tgz", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-0.1.0.tgz",
@ -5018,6 +5285,12 @@
"ansi-regex": "^2.0.0" "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": { "has-flag": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@ -5084,13 +5357,10 @@
} }
}, },
"hosted-git-info": { "hosted-git-info": {
"version": "2.8.2", "version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.2.tgz", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
"integrity": "sha512-CyjlXII6LMsPMyUzxpTt8fzh5QwzGqPmQXgY/Jyf4Zfp27t/FvfhwoE/8laaMUcMy816CkWF20I7NeQhwwY88w==", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
"dev": true, "dev": true
"requires": {
"lru-cache": "^5.1.1"
}
}, },
"hpack.js": { "hpack.js": {
"version": "2.1.6", "version": "2.1.6",
@ -5433,6 +5703,12 @@
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
"dev": true "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": { "is-binary-path": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
@ -5442,6 +5718,15 @@
"binary-extensions": "^1.0.0" "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": { "is-buffer": {
"version": "1.1.6", "version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
@ -5463,6 +5748,15 @@
"ci-info": "^2.0.0" "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": { "is-data-descriptor": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "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": { "is-npm": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", "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": { "is-obj": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
@ -5651,6 +5957,12 @@
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
"dev": true "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": { "is-symbol": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
@ -5942,9 +6254,9 @@
} }
}, },
"lodash": { "lodash": {
"version": "4.17.19", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true "dev": true
}, },
"lodash._reinterpolate": { "lodash._reinterpolate": {
@ -6009,15 +6321,6 @@
"integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
"dev": true "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": { "make-dir": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
@ -6811,15 +7114,112 @@
} }
}, },
"object.values": { "object.values": {
"version": "1.1.0", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz",
"integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==",
"dev": true, "dev": true,
"requires": { "requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3", "define-properties": "^1.1.3",
"es-abstract": "^1.12.0", "es-abstract": "^1.18.0-next.2",
"function-bind": "^1.1.1",
"has": "^1.0.3" "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": { "obuf": {
@ -9582,6 +9982,18 @@
"glob": "^7.1.2" "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": { "tslib": {
"version": "1.10.0", "version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
@ -9652,6 +10064,26 @@
"integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==",
"dev": true "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": { "undefsafe": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz",
@ -9874,9 +10306,9 @@
} }
}, },
"url-parse": { "url-parse": {
"version": "1.4.7", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
"integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", "integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"querystringify": "^2.1.1", "querystringify": "^2.1.1",
@ -10248,9 +10680,9 @@
} }
}, },
"yargs-parser": { "yargs-parser": {
"version": "13.1.1", "version": "13.1.2",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
"integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
"dev": true, "dev": true,
"requires": { "requires": {
"camelcase": "^5.0.0", "camelcase": "^5.0.0",
@ -10416,6 +10848,36 @@
"isexe": "^2.0.0" "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": { "which-module": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
@ -10534,12 +10996,6 @@
"integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
"dev": true "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": { "yargs": {
"version": "13.3.2", "version": "13.3.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",

@ -1,6 +1,6 @@
{ {
"name": "cvat-canvas", "name": "cvat-canvas",
"version": "2.4.1", "version": "2.4.3",
"description": "Part of Computer Vision Annotation Tool which presents its canvas library", "description": "Part of Computer Vision Annotation Tool which presents its canvas library",
"main": "src/canvas.ts", "main": "src/canvas.ts",
"scripts": { "scripts": {
@ -32,7 +32,7 @@
"eslint": "^6.1.0", "eslint": "^6.1.0",
"eslint-config-airbnb-typescript": "^4.0.1", "eslint-config-airbnb-typescript": "^4.0.1",
"eslint-config-typescript-recommended": "^1.4.17", "eslint-config-typescript-recommended": "^1.4.17",
"eslint-plugin-import": "^2.18.2", "eslint-plugin-import": "^2.22.1",
"node-sass": "^4.14.1", "node-sass": "^4.14.1",
"nodemon": "^2.0.7", "nodemon": "^2.0.7",
"postcss-loader": "^3.0.0", "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 // SPDX-License-Identifier: MIT
@ -1175,7 +1175,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
} }
} else if (reason === UpdateReasons.IMAGE_MOVED) { } else if (reason === UpdateReasons.IMAGE_MOVED) {
this.moveCanvas(); this.moveCanvas();
} else if ([UpdateReasons.OBJECTS_UPDATED].includes(reason)) { } else if (reason === UpdateReasons.OBJECTS_UPDATED) {
if (this.mode === Mode.GROUP) { if (this.mode === Mode.GROUP) {
this.groupHandler.resetSelectedObjects(); this.groupHandler.resetSelectedObjects();
} }
@ -1443,6 +1443,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
clientID: state.clientID, clientID: state.clientID,
outside: state.outside, outside: state.outside,
occluded: state.occluded, occluded: state.occluded,
source: state.source,
hidden: state.hidden, hidden: state.hidden,
lock: state.lock, lock: state.lock,
shapeType: state.shapeType, shapeType: state.shapeType,
@ -1534,13 +1535,22 @@ export class CanvasViewImpl implements CanvasView, Listener {
} }
} }
for (const attrID of Object.keys(state.attributes)) { if (drawnState.label.id !== state.label.id) {
if (state.attributes[attrID] !== drawnState.attributes[+attrID]) { // need to remove created text and create it again
if (text) { if (text) {
const [span] = (text.node.querySelectorAll(`[attrID="${attrID}"]`) as any) as SVGTSpanElement[]; text.remove();
if (span && span.textContent) { this.svgTexts[state.clientID] = this.addText(state);
const prefix = span.textContent.split(':').slice(0, -1).join(':'); }
span.textContent = `${prefix}: ${state.attributes[attrID]}`; } 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; cancel(): void;
} }
interface FinalCoordinates {
points: number[];
box: Box;
}
export class DrawHandlerImpl implements DrawHandler { export class DrawHandlerImpl implements DrawHandler {
// callback is used to notify about creating new shape // callback is used to notify about creating new shape
private onDrawDone: (data: object | null, duration?: number, continueDraw?: boolean) => void; private onDrawDone: (data: object | null, duration?: number, continueDraw?: boolean) => void;
@ -73,19 +78,14 @@ export class DrawHandlerImpl implements DrawHandler {
return [xtl, ytl, xbr, ybr]; return [xtl, ytl, xbr, ybr];
} }
private getFinalPolyshapeCoordinates( private getFinalPolyshapeCoordinates(targetPoints: number[]): FinalCoordinates {
targetPoints: number[],
): {
points: number[];
box: Box;
} {
const { offset } = this.geometry; const { offset } = this.geometry;
let points = targetPoints.map((coord: number): number => coord - offset); let points = targetPoints.map((coord: number): number => coord - offset);
const box = { const box = {
xtl: Number.MAX_SAFE_INTEGER, xtl: Number.MAX_SAFE_INTEGER,
ytl: Number.MAX_SAFE_INTEGER, ytl: Number.MAX_SAFE_INTEGER,
xbr: Number.MAX_SAFE_INTEGER, xbr: Number.MIN_SAFE_INTEGER,
ybr: Number.MAX_SAFE_INTEGER, ybr: Number.MIN_SAFE_INTEGER,
}; };
const frameWidth = this.geometry.image.width; const frameWidth = this.geometry.image.width;
@ -96,9 +96,9 @@ export class DrawHandlerImpl implements DrawHandler {
Vertical, Vertical,
} }
const isBetween = (x1: number, x2: number, c: number): boolean => ( function isBetween(x1: number, x2: number, c: number): boolean {
c >= Math.min(x1, x2) && c <= Math.max(x1, x2) return c >= Math.min(x1, x2) && c <= Math.max(x1, x2);
); }
const isInsideFrame = (p: Point, direction: Direction): boolean => { const isInsideFrame = (p: Point, direction: Direction): boolean => {
if (direction === Direction.Horizontal) { if (direction === Direction.Horizontal) {
@ -121,22 +121,35 @@ export class DrawHandlerImpl implements DrawHandler {
const findIntersectionsWithFrameBorders = (p1: Point, p2: Point, direction: Direction): number[] => { const findIntersectionsWithFrameBorders = (p1: Point, p2: Point, direction: Direction): number[] => {
const resultPoints = []; 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) { if (direction === Direction.Horizontal) {
resultPoints.push(...findInersection(p1, p2, { x: 0, y: 0 }, { x: 0, y: frameHeight })); resultPoints.push(...findInersection(p1, p2, leftLine[0], leftLine[1]));
resultPoints.push( resultPoints.push(...findInersection(p1, p2, rightLine[0], rightLine[1]));
...findInersection(p1, p2, { x: frameWidth, y: frameHeight }, { x: frameWidth, y: 0 }),
);
} else { } else {
resultPoints.push( resultPoints.push(...findInersection(p1, p2, bottomLine[0], bottomLine[1]));
...findInersection(p1, p2, { x: 0, y: frameHeight }, { x: frameWidth, y: frameHeight }), resultPoints.push(...findInersection(p1, p2, topLine[0], topLine[1]));
);
resultPoints.push(...findInersection(p1, p2, { x: frameWidth, y: 0 }, { x: 0, y: 0 }));
} }
if (resultPoints.length === 4) { if (resultPoints.length === 4) {
if ( if (
Math.sign(resultPoints[0] - resultPoints[2]) !== Math.sign(p1.x - p2.x) (p1.x === p2.x || 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.y === p2.y || Math.sign(resultPoints[1] - resultPoints[3]) !== Math.sign(p1.y - p2.y))
) { ) {
[resultPoints[0], resultPoints[2]] = [resultPoints[2], resultPoints[0]]; [resultPoints[0], resultPoints[2]] = [resultPoints[2], resultPoints[0]];
[resultPoints[1], resultPoints[3]] = [resultPoints[3], resultPoints[1]]; [resultPoints[1], resultPoints[3]] = [resultPoints[3], resultPoints[1]];
@ -145,24 +158,23 @@ export class DrawHandlerImpl implements DrawHandler {
return resultPoints; return resultPoints;
}; };
const crop = (polygonPoints: number[], direction: Direction): number[] => { const crop = (shapePoints: number[], direction: Direction): number[] => {
const resultPoints = []; const resultPoints = [];
for (let i = 0; i < polygonPoints.length - 1; i += 2) { const isPolyline = this.drawData.shapeType === 'polyline';
const curPoint = { x: polygonPoints[i], y: polygonPoints[i + 1] }; 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)) { if (isInsideFrame(curPoint, direction)) {
resultPoints.push(polygonPoints[i], polygonPoints[i + 1]); resultPoints.push(shapePoints[i], shapePoints[i + 1]);
} }
const isLastPoint = i === polygonPoints.length - 2; const isLastPoint = i === shapePoints.length - 2;
if ( if (isLastPoint && (isPolyline || (isPolygon && shapePoints.length === 4))) {
isLastPoint
&& (this.drawData.shapeType === 'polyline'
|| (this.drawData.shapeType === 'polygon' && polygonPoints.length === 4))
) {
break; break;
} }
const nextPoint = isLastPoint const nextPoint = isLastPoint
? { x: polygonPoints[0], y: polygonPoints[1] } ? { x: shapePoints[0], y: shapePoints[1] }
: { x: polygonPoints[i + 2], y: polygonPoints[i + 3] }; : { x: shapePoints[i + 2], y: shapePoints[i + 3] };
const intersectionPoints = findIntersectionsWithFrameBorders(curPoint, nextPoint, direction); const intersectionPoints = findIntersectionsWithFrameBorders(curPoint, nextPoint, direction);
if (intersectionPoints.length !== 0) { if (intersectionPoints.length !== 0) {
resultPoints.push(...intersectionPoints); resultPoints.push(...intersectionPoints);
@ -187,20 +199,15 @@ export class DrawHandlerImpl implements DrawHandler {
}; };
} }
private getFinalCuboidCoordinates( private getFinalCuboidCoordinates(targetPoints: number[]): FinalCoordinates {
targetPoints: number[],
): {
points: number[];
box: Box;
} {
const { offset } = this.geometry; const { offset } = this.geometry;
let points = targetPoints; let points = targetPoints;
const box = { const box = {
xtl: 0, xtl: Number.MAX_SAFE_INTEGER,
ytl: 0, ytl: Number.MAX_SAFE_INTEGER,
xbr: Number.MAX_SAFE_INTEGER, xbr: Number.MIN_SAFE_INTEGER,
ybr: Number.MAX_SAFE_INTEGER, ybr: Number.MIN_SAFE_INTEGER,
}; };
const frameWidth = this.geometry.image.width; const frameWidth = this.geometry.image.width;
@ -238,27 +245,34 @@ export class DrawHandlerImpl implements DrawHandler {
if (cuboidOffsets.length === points.length / 2) { if (cuboidOffsets.length === points.length / 2) {
cuboidOffsets.forEach((offsetCoords: number[]): void => { cuboidOffsets.forEach((offsetCoords: number[]): void => {
if (Math.sqrt((offsetCoords[0] ** 2) + (offsetCoords[1] ** 2)) < minCuboidOffset.d) { const dx = offsetCoords[0] ** 2;
minCuboidOffset.d = Math.sqrt((offsetCoords[0] ** 2) + (offsetCoords[1] ** 2)); const dy = offsetCoords[1] ** 2;
if (Math.sqrt(dx + dy) < minCuboidOffset.d) {
minCuboidOffset.d = Math.sqrt(dx + dy);
[minCuboidOffset.dx, minCuboidOffset.dy] = offsetCoords; [minCuboidOffset.dx, minCuboidOffset.dy] = offsetCoords;
} }
}); });
points = points.map((coord: number, i: number): number => { points = points.map((coord: number, i: number): number => {
const finalCoord = coord + (i % 2 === 0 ? minCuboidOffset.dx : minCuboidOffset.dy); if (i % 2) {
return coord + 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);
} }
return coord + minCuboidOffset.dx;
return finalCoord;
}); });
} }
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 { return {
points: points.map((coord: number): number => coord - offset), points: points.map((coord: number): number => coord - offset),
box, box,
@ -449,8 +463,9 @@ export class DrawHandlerImpl implements DrawHandler {
} else { } else {
this.drawInstance.draw('update', e); this.drawInstance.draw('update', e);
const deltaTreshold = 15; const deltaTreshold = 15;
const delta = Math.sqrt(((e.clientX - lastDrawnPoint.x) ** 2) const dx = (e.clientX - lastDrawnPoint.x) ** 2;
+ ((e.clientY - lastDrawnPoint.y) ** 2)); const dy = (e.clientY - lastDrawnPoint.y) ** 2;
const delta = Math.sqrt(dx + dy);
if (delta > deltaTreshold) { if (delta > deltaTreshold) {
this.drawInstance.draw('point', e); 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 && (box.xbr - box.xtl) * (box.ybr - box.ytl) >= consts.AREA_THRESHOLD
&& points.length >= 3 * 2 && points.length >= 3 * 2
) { ) {
this.onDrawDone( this.onDrawDone({ clientID, shapeType, points }, Date.now() - this.startTimestamp);
{
clientID,
shapeType,
points,
},
Date.now() - this.startTimestamp,
);
} else if ( } else if (
shapeType === 'polyline' shapeType === 'polyline'
&& (box.xbr - box.xtl >= consts.SIZE_THRESHOLD || box.ybr - box.ytl >= consts.SIZE_THRESHOLD) && (box.xbr - box.xtl >= consts.SIZE_THRESHOLD || box.ybr - box.ytl >= consts.SIZE_THRESHOLD)
&& points.length >= 2 * 2 && points.length >= 2 * 2
) { ) {
this.onDrawDone( this.onDrawDone({ clientID, shapeType, points }, Date.now() - this.startTimestamp);
{
clientID,
shapeType,
points,
},
Date.now() - this.startTimestamp,
);
} else if (shapeType === 'points' && (e.target as any).getAttribute('points') !== '0,0') { } else if (shapeType === 'points' && (e.target as any).getAttribute('points') !== '0,0') {
this.onDrawDone( this.onDrawDone({ clientID, shapeType, points }, Date.now() - this.startTimestamp);
{
clientID,
shapeType,
points,
},
Date.now() - this.startTimestamp,
);
// TODO: think about correct constraign for cuboids // TODO: think about correct constraign for cuboids
} else if (shapeType === 'cuboid' && points.length === 4 * 2) { } else if (shapeType === 'cuboid' && points.length === 4 * 2) {
this.onDrawDone( this.onDrawDone(

@ -41,6 +41,7 @@ export interface DrawnState {
occluded?: boolean; occluded?: boolean;
hidden?: boolean; hidden?: boolean;
lock: boolean; lock: boolean;
source: 'AUTO' | 'MANUAL';
shapeType: string; shapeType: string;
points?: number[]; points?: number[];
attributes: Record<number, string>; attributes: Record<number, string>;
@ -176,5 +177,7 @@ export function scalarProduct(a: Vector2D, b: Vector2D): number {
} }
export function vectorLength(vector: 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": ".", "baseUrl": ".",
"emitDeclarationOnly": true, "emitDeclarationOnly": true,
"module": "es6", "module": "es6",
"target": "es6", "target": "es2016",
"noImplicitAny": true, "noImplicitAny": true,
"preserveConstEnums": true, "preserveConstEnums": true,
"declaration": true, "declaration": true,

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

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

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

@ -1,6 +1,6 @@
{ {
"name": "cvat-core", "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", "description": "Part of Computer Vision Tool which presents an interface for client-side integration",
"main": "babel.config.js", "main": "babel.config.js",
"scripts": { "scripts": {
@ -22,13 +22,13 @@
"eslint": "6.1.0", "eslint": "6.1.0",
"eslint-config-airbnb-base": "14.0.0", "eslint-config-airbnb-base": "14.0.0",
"eslint-plugin-import": "2.18.2", "eslint-plugin-import": "2.18.2",
"eslint-plugin-jest": "^24.1.0",
"eslint-plugin-no-unsafe-innerhtml": "^1.0.16", "eslint-plugin-no-unsafe-innerhtml": "^1.0.16",
"eslint-plugin-no-unsanitized": "^3.0.2", "eslint-plugin-no-unsanitized": "^3.0.2",
"eslint-plugin-security": "^1.4.0", "eslint-plugin-security": "^1.4.0",
"eslint-plugin-jest": "^24.1.0",
"jest": "^26.6.3", "jest": "^26.6.3",
"jest-junit": "^6.4.0", "jest-junit": "^6.4.0",
"jsdoc": "^3.6.4", "jsdoc": "^3.6.6",
"webpack": "^4.31.0", "webpack": "^4.31.0",
"webpack-cli": "^3.3.2" "webpack-cli": "^3.3.2"
}, },
@ -40,7 +40,7 @@
"error-stack-parser": "^2.0.2", "error-stack-parser": "^2.0.2",
"form-data": "^2.5.0", "form-data": "^2.5.0",
"jest-config": "^26.6.3", "jest-config": "^26.6.3",
"json-logic-js": "^2.0.0", "json-logic-js": "^2.0.1",
"js-cookie": "^2.2.0", "js-cookie": "^2.2.0",
"platform": "^1.3.5", "platform": "^1.3.5",
"quickhull": "^1.0.3", "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.Task} task task to be annotated
* @param {module:API.cvat.classes.MLModel} model model used to get annotation * @param {module:API.cvat.classes.MLModel} model model used to get annotation
* @param {object} [args] extra arguments * @param {object} [args] extra arguments
* @returns {string} requestID * @returns {object[]} annotations
* @throws {module:API.cvat.exceptions.ServerError} * @throws {module:API.cvat.exceptions.ServerError}
* @throws {module:API.cvat.exceptions.PluginError} * @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError} * @throws {module:API.cvat.exceptions.ArgumentError}

@ -286,7 +286,7 @@
if (nextChunkNumber * chunkSize < this.stopFrame) { if (nextChunkNumber * chunkSize < this.stopFrame) {
provider.setReadyToLoading(nextChunkNumber); provider.setReadyToLoading(nextChunkNumber);
const nextStart = nextChunkNumber * chunkSize; 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 (!provider.isChunkCached(nextStart, nextStop)) {
if (!frameDataCache[this.tid].activeChunkRequest) { if (!frameDataCache[this.tid].activeChunkRequest) {
frameDataCache[this.tid].activeChunkRequest = { frameDataCache[this.tid].activeChunkRequest = {

@ -33,6 +33,7 @@
created_date: undefined, created_date: undefined,
updated_date: undefined, updated_date: undefined,
task_subsets: undefined, task_subsets: undefined,
training_project: undefined,
}; };
for (const property in data) { for (const property in data) {
@ -64,6 +65,9 @@
} }
data.task_subsets = Array.from(subsetsSet); data.task_subsets = Array.from(subsetsSet);
} }
if (typeof initialData.training_project === 'object') {
data.training_project = { ...initialData.training_project };
}
Object.defineProperties( Object.defineProperties(
this, this,
@ -94,6 +98,7 @@
data.name = value; data.name = value;
}, },
}, },
/** /**
* @name status * @name status
* @type {module:API.cvat.enums.TaskStatus} * @type {module:API.cvat.enums.TaskStatus}
@ -217,6 +222,31 @@
subsets: { subsets: {
get: () => [...data.task_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: { _internalData: {
get: () => data, get: () => data,
}, },
@ -261,7 +291,9 @@
}; };
Project.prototype.save.implementation = async function () { Project.prototype.save.implementation = async function () {
const trainingProjectCopy = this.trainingProject;
if (typeof this.id !== 'undefined') { if (typeof this.id !== 'undefined') {
// project has been already created, need to update some data
const projectData = { const projectData = {
name: this.name, name: this.name,
assignee_id: this.assignee ? this.assignee.id : null, assignee_id: this.assignee ? this.assignee.id : null,
@ -269,10 +301,15 @@
labels: [...this._internalData.labels.map((el) => el.toJSON())], labels: [...this._internalData.labels.map((el) => el.toJSON())],
}; };
if (trainingProjectCopy) {
projectData.training_project = trainingProjectCopy;
}
await serverProxy.projects.save(this.id, projectData); await serverProxy.projects.save(this.id, projectData);
return this; return this;
} }
// initial creating
const projectSpec = { const projectSpec = {
name: this.name, name: this.name,
labels: [...this.labels.map((el) => el.toJSON())], labels: [...this.labels.map((el) => el.toJSON())],
@ -282,6 +319,10 @@
projectSpec.bug_tracker = this.bugTracker; projectSpec.bug_tracker = this.bugTracker;
} }
if (trainingProjectCopy) {
projectSpec.training_project = trainingProjectCopy;
}
const project = await serverProxy.projects.create(projectSpec); const project = await serverProxy.projects.create(projectSpec);
return new Project(project); return new Project(project);
}; };

@ -9,6 +9,31 @@
const config = require('./config'); const config = require('./config');
const DownloadWorker = require('./download.worker'); 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) { function generateError(errorData) {
if (errorData.response) { if (errorData.response) {
const message = `${errorData.message}. ${JSON.stringify(errorData.response.data) || ''}.`; 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() { async function installedApps() {
const { backendAPI } = config; const { backendAPI } = config;
try { try {
@ -1123,6 +1238,14 @@
}), }),
writable: false, writable: false,
}, },
predictor: {
value: Object.freeze({
status: predictorStatus,
predict: predictAnnotations,
}),
writable: false,
},
}), }),
); );
} }

@ -10,7 +10,7 @@
const { const {
getFrame, getRanges, getPreview, clear: clearFrames, getContextImage, getFrame, getRanges, getPreview, clear: clearFrames, getContextImage,
} = require('./frames'); } = require('./frames');
const { ArgumentError } = require('./exceptions'); const { ArgumentError, DataError } = require('./exceptions');
const { TaskStatus } = require('./enums'); const { TaskStatus } = require('./enums');
const { Label } = require('./labels'); const { Label } = require('./labels');
const User = require('./user'); const User = require('./user');
@ -258,6 +258,19 @@
}, },
writable: true, 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 * @instance
* @async * @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 = { this.logger = {
log: Object.getPrototypeOf(this).logger.log.bind(this), 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 = { this.logger = {
log: Object.getPrototypeOf(this).logger.log.bind(this), 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; return rangesData;
}; };
Job.prototype.frames.preview.implementation = async function () {
const frameData = await getPreview(this.task.id);
return frameData;
};
// TODO: Check filter for annotations // TODO: Check filter for annotations
Job.prototype.annotations.get.implementation = async function (frame, allTracks, filters) { Job.prototype.annotations.get.implementation = async function (frame, allTracks, filters) {
if (!Array.isArray(filters)) { if (!Array.isArray(filters)) {
@ -1897,6 +1959,16 @@
return result; 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() { Task.prototype.close.implementation = function closeTask() {
clearFrames(this.id); clearFrames(this.id);
for (const job of this.jobs) { for (const job of this.jobs) {
@ -2028,11 +2100,6 @@
return result; 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 () { Task.prototype.frames.ranges.implementation = async function () {
const rangesData = await getRanges(this.id); const rangesData = await getRanges(this.id);
return rangesData; return rangesData;
@ -2199,6 +2266,39 @@
return result; 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) { Job.prototype.frames.contextImage.implementation = async function (taskId, frameId) {
const result = await getContextImage(taskId, frameId); const result = await getContextImage(taskId, frameId);
return result; return result;

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

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

File diff suppressed because it is too large Load Diff

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

@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation // Copyright (C) 2020-2021 Intel Corporation
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@ -190,6 +190,10 @@ export enum AnnotationActionTypes {
SWITCH_REQUEST_REVIEW_DIALOG = 'SWITCH_REQUEST_REVIEW_DIALOG', SWITCH_REQUEST_REVIEW_DIALOG = 'SWITCH_REQUEST_REVIEW_DIALOG',
SWITCH_SUBMIT_REVIEW_DIALOG = 'SWITCH_SUBMIT_REVIEW_DIALOG', SWITCH_SUBMIT_REVIEW_DIALOG = 'SWITCH_SUBMIT_REVIEW_DIALOG',
SET_FORCE_EXIT_ANNOTATION_PAGE_FLAG = 'SET_FORCE_EXIT_ANNOTATION_PAGE_FLAG', 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', HIDE_SHOW_CONTEXT_IMAGE = 'HIDE_SHOW_CONTEXT_IMAGE',
GET_CONTEXT_IMAGE = 'GET_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 { export function changeFrameAsync(toFrame: number, fillBuffer?: boolean, frameStep?: number): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => { return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
const state: CombinedState = getStore().getState(); const state: CombinedState = getStore().getState();
@ -689,6 +774,7 @@ export function changeFrameAsync(toFrame: number, fillBuffer?: boolean, frameSte
delay, delay,
}, },
}); });
dispatch(getPredictionsAsync());
} catch (error) { } catch (error) {
if (error !== 'not needed') { if (error !== 'not needed') {
dispatch({ dispatch({
@ -934,9 +1020,11 @@ export function getJobAsync(tid: number, jid: number, initialFrame: number, init
loadJobEvent.close(await jobInfoGenerator(job)); loadJobEvent.close(await jobInfoGenerator(job));
const openTime = Date.now();
dispatch({ dispatch({
type: AnnotationActionTypes.GET_JOB_SUCCESS, type: AnnotationActionTypes.GET_JOB_SUCCESS,
payload: { payload: {
openTime,
job, job,
issues, issues,
reviews, reviews,
@ -950,10 +1038,41 @@ export function getJobAsync(tid: number, jid: number, initialFrame: number, init
maxZ, maxZ,
}, },
}); });
if (job.task.dimension === DimensionType.DIM_3D) { if (job.task.dimension === DimensionType.DIM_3D) {
const workspace = Workspace.STANDARD3D; const workspace = Workspace.STANDARD3D;
dispatch(changeWorkspace(workspace)); 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)); dispatch(changeFrameAsync(frameNumber, false));
} catch (error) { } catch (error) {
dispatch({ 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 // used to reproduce the latest drawing (in case of tags just creating) by using N
export function rememberObject( export function rememberObject(createParams: {
objectType: ObjectType, activeObjectType?: ObjectType;
labelID: number, activeLabelID?: number;
shapeType?: ShapeType, activeShapeType?: ShapeType;
points?: number, activeNumOfPoints?: number;
rectDrawingMethod?: RectDrawingMethod, activeRectDrawingMethod?: RectDrawingMethod;
): AnyAction { }): 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;
}
return { return {
type: AnnotationActionTypes.REMEMBER_CREATED_OBJECT, type: AnnotationActionTypes.REMEMBER_CREATED_OBJECT,
payload: { payload: createParams,
shapeType,
labelID,
objectType,
points,
activeControl,
rectDrawingMethod,
},
}; };
} }
@ -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 { export function hideShowContextImage(hidden: boolean): AnyAction {
return { return {
type: AnnotationActionTypes.HIDE_SHOW_CONTEXT_IMAGE, 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 // 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 getCore from 'cvat-core-wrapper';
import { LogType } from 'cvat-logger'; import { LogType } from 'cvat-logger';
import { computeZRange } from './annotation-actions'; 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 StandardWorkspaceComponent from 'components/annotation-page/standard-workspace/standard-workspace';
import StandardWorkspace3DComponent from 'components/annotation-page/standard3D-workspace/standard3D-workspace'; import StandardWorkspace3DComponent from 'components/annotation-page/standard3D-workspace/standard3D-workspace';
import TagAnnotationWorkspace from 'components/annotation-page/tag-annotation-workspace/tag-annotation-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 StatisticsModalContainer from 'containers/annotation-page/top-bar/statistics-modal';
import AnnotationTopBarContainer from 'containers/annotation-page/top-bar/top-bar'; import AnnotationTopBarContainer from 'containers/annotation-page/top-bar/top-bar';
import { Workspace } from 'reducers/interfaces'; import { Workspace } from 'reducers/interfaces';
@ -131,7 +131,7 @@ export default function AnnotationPageComponent(props: Props): JSX.Element {
<ReviewAnnotationsWorkspace /> <ReviewAnnotationsWorkspace />
</Layout.Content> </Layout.Content>
)} )}
<FiltersModalContainer visible={false} /> <FiltersModalComponent />
<StatisticsModalContainer /> <StatisticsModalContainer />
<SubmitAnnotationsModal /> <SubmitAnnotationsModal />
<SubmitReviewModal /> <SubmitReviewModal />

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

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

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

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

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

@ -1,4 +1,4 @@
// Copyright (C) 2020 Intel Corporation // Copyright (C) 2020-2021 Intel Corporation
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
@ -10,22 +10,30 @@ import {
LockFilled, UnlockOutlined, EyeInvisibleFilled, EyeOutlined, LockFilled, UnlockOutlined, EyeInvisibleFilled, EyeOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import CVATTooltip from 'components/common/cvat-tooltip';
import LabelKeySelectorPopover from './label-key-selector-popover';
interface Props { interface Props {
labelName: string; labelName: string;
labelColor: string; labelColor: string;
labelID: number;
visible: boolean; visible: boolean;
statesHidden: boolean; statesHidden: boolean;
statesLocked: boolean; statesLocked: boolean;
keyToLabelMapping: Record<string, number>;
hideStates(): void; hideStates(): void;
showStates(): void; showStates(): void;
lockStates(): void; lockStates(): void;
unlockStates(): void; unlockStates(): void;
updateLabelShortcutKey(updatedKey: string, labelID: number): void;
} }
function LabelItemComponent(props: Props): JSX.Element { function LabelItemComponent(props: Props): JSX.Element {
const { const {
labelName, labelName,
labelColor, labelColor,
labelID,
keyToLabelMapping,
visible, visible,
statesHidden, statesHidden,
statesLocked, statesLocked,
@ -33,8 +41,14 @@ function LabelItemComponent(props: Props): JSX.Element {
showStates, showStates,
lockStates, lockStates,
unlockStates, unlockStates,
updateLabelShortcutKey,
} = props; } = 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 = { const classes = {
lock: { lock: {
enabled: { className: 'cvat-label-item-button-lock cvat-label-item-button-lock-enabled' }, enabled: { className: 'cvat-label-item-button-lock cvat-label-item-button-lock-enabled' },
@ -48,22 +62,37 @@ function LabelItemComponent(props: Props): JSX.Element {
return ( return (
<Row <Row
align='middle' align='stretch'
justify='space-around' justify='space-around'
className='cvat-objects-sidebar-label-item' className={[
style={{ display: visible ? 'flex' : 'none' }} 'cvat-objects-sidebar-label-item',
visible ? '' : 'cvat-objects-sidebar-label-item-disabled',
].join(' ')}
> >
<Col span={4}> <Col span={2}>
<Button style={{ background: labelColor }} className='cvat-label-item-color-button'> <div style={{ background: labelColor }} className='cvat-label-item-color'>
{' '} {' '}
</Button> </div>
</Col> </Col>
<Col span={14}> <Col span={12}>
<Text strong className='cvat-text'> <CVATTooltip title={labelName}>
{labelName} <Text strong className='cvat-text'>
</Text> {labelName}
</Text>
</CVATTooltip>
</Col> </Col>
<Col span={3}> <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 ? ( {statesLocked ? (
<LockFilled {...classes.lock.enabled} onClick={unlockStates} /> <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 // 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 LabelItemContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/label-item';
import GlobalHotKeys from 'utils/mousetrap-react';
interface Props { function LabelsListComponent(): JSX.Element {
labelIDs: number[]; const dispatch = useDispatch();
listHeight: number; 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 { // check if this key is assigned to another label
const { listHeight, labelIDs } = props; 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 ( return (
<div style={{ height: listHeight }} className='cvat-objects-sidebar-labels-list'> <div style={{ height: listHeight }} className='cvat-objects-sidebar-labels-list'>
<GlobalHotKeys keyMap={subKeyMap} handlers={handlers} />
{labelIDs.map( {labelIDs.map(
(labelID: number): JSX.Element => ( (labelID: number): JSX.Element => (
<LabelItemContainer key={labelID} labelID={labelID} /> <LabelItemContainer
key={labelID}
labelID={labelID}
keyToLabelMapping={keyToLabelMapping}
updateLabelShortcutKey={updateLabelShortcutKey}
/>
), ),
)} )}
</div> </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 // SPDX-License-Identifier: MIT
@ -13,7 +13,7 @@ import Layout from 'antd/lib/layout';
import { Canvas } from 'cvat-canvas-wrapper'; import { Canvas } from 'cvat-canvas-wrapper';
import { CombinedState } from 'reducers/interfaces'; 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 { import {
collapseSidebar as collapseSidebarAction, collapseSidebar as collapseSidebarAction,
updateTabContentHeight as updateTabContentHeightAction, updateTabContentHeight as updateTabContentHeightAction,
@ -123,8 +123,8 @@ function ObjectsSideBar(props: StateToProps & DispatchToProps & OwnProps): JSX.E
<Tabs.TabPane tab={<Text strong>Objects</Text>} key='objects'> <Tabs.TabPane tab={<Text strong>Objects</Text>} key='objects'>
{objectsList} {objectsList}
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane tab={<Text strong>Labels</Text>} key='labels'> <Tabs.TabPane forceRender tab={<Text strong>Labels</Text>} key='labels'>
<LabelsListContainer /> <LabelsList />
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane tab={<Text strong>Issues</Text>} key='issues'> <Tabs.TabPane tab={<Text strong>Issues</Text>} key='issues'>
<IssuesListComponent /> <IssuesListComponent />

@ -167,7 +167,8 @@
padding: 3px 1px 1px 3px; 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; border: 1px solid $object-item-border-color;
width: 7px; width: 7px;
opacity: 1; opacity: 1;
@ -288,12 +289,16 @@
.cvat-objects-sidebar-label-active-item { .cvat-objects-sidebar-label-active-item {
background: $active-label-background-color; 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 { .cvat-objects-sidebar-label-item {
height: 2.5em; height: 2.5em;
border-bottom: 1px solid $border-color-1; border-bottom: 1px solid $border-color-1;
padding: 5px; padding: 5px 3px 3px 3px;
span { span {
@extend .cvat-object-sidebar-icon; @extend .cvat-object-sidebar-icon;
@ -311,10 +316,39 @@
} }
} }
.cvat-label-item-color-button { .cvat-label-item-color {
width: 30px; background: rgb(25, 184, 14);
height: 20px; height: 80%;
border-radius: 5px; 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 { .cvat-objects-appearance-content {
@ -361,3 +395,7 @@
margin-right: $grid-unit-size; 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 // SPDX-License-Identifier: MIT
@ -56,7 +56,8 @@
.cvat-issue-control, .cvat-issue-control,
.cvat-tools-control, .cvat-tools-control,
.cvat-extra-controls-control, .cvat-extra-controls-control,
.cvat-opencv-control { .cvat-opencv-control,
.cvat-context-image-control {
border-radius: 3.3px; border-radius: 3.3px;
transform: scale(0.65); transform: scale(0.65);
padding: 2px; padding: 2px;

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

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

@ -20,11 +20,11 @@ import {
changeFrameAsync, changeFrameAsync,
rememberObject, rememberObject,
} from 'actions/annotation-actions'; } from 'actions/annotation-actions';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import { Canvas } from 'cvat-canvas-wrapper'; import { Canvas } from 'cvat-canvas-wrapper';
import { CombinedState, ObjectType } from 'reducers/interfaces'; import { CombinedState, ObjectType } from 'reducers/interfaces';
import LabelSelector from 'components/label-selector/label-selector'; import LabelSelector from 'components/label-selector/label-selector';
import getCore from 'cvat-core-wrapper'; import getCore from 'cvat-core-wrapper';
import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
import ShortcutsSelect from './shortcuts-select'; import ShortcutsSelect from './shortcuts-select';
const cvat = getCore(); const cvat = getCore();
@ -82,7 +82,7 @@ function mapDispatchToProps(dispatch: ThunkDispatch<CombinedState, {}, Action>):
dispatch(removeObjectAsync(jobInstance, objectState, true)); dispatch(removeObjectAsync(jobInstance, objectState, true));
}, },
onRememberObject(labelID: number): void { 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'; const FILTERS_HISTORY = 'annotationFiltersHistory';
interface Props {
visible: boolean;
}
interface StoredFilter { interface StoredFilter {
id: string; id: string;
logic: JsonLogicTree; logic: JsonLogicTree;
} }
export default function FiltersModalComponent(props: Props): JSX.Element { function FiltersModalComponent(): JSX.Element {
const { visible } = props; const labels = useSelector((state: CombinedState) => state.annotation.job.labels);
const { labels } = useSelector((state: CombinedState) => state.annotation.job); const activeFilters = useSelector((state: CombinedState) => state.annotation.annotations.filters);
const { filters: activeFilters } = useSelector((state: CombinedState) => state.annotation.annotations); const visible = useSelector((state: CombinedState) => state.annotation.filtersPanelVisible);
const getConvertedInputType = (inputType: string): string => { const getConvertedInputType = (inputType: string): string => {
switch (inputType) { switch (inputType) {
@ -234,18 +230,23 @@ export default function FiltersModalComponent(props: Props): JSX.Element {
const menu = ( const menu = (
<Menu> <Menu>
{filters {filters
.filter((filter: StoredFilter) => {
const tree = QbUtils.loadFromJsonLogic(filter.logic, config);
return !!QbUtils.queryString(tree, config);
})
.map((filter: StoredFilter) => { .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); const tree = QbUtils.loadFromJsonLogic(filter.logic, config);
return ( const queryString = QbUtils.queryString(tree, config);
<Menu.Item key={filter.id} onClick={() => setState({ tree, config })}> return { tree, queryString, filter };
{QbUtils.queryString(tree, config)} })
</Menu.Item> .filter(({ queryString }) => !!queryString)
); .map(({ filter, tree, queryString }) => (
})} <Menu.Item key={filter.id} onClick={() => setState({ tree, config })}>
{queryString}
</Menu.Item>
))}
</Menu> </Menu>
); );
@ -286,3 +287,5 @@ export default function FiltersModalComponent(props: Props): JSX.Element {
</Modal> </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 // SPDX-License-Identifier: MIT
@ -11,7 +11,9 @@ import Timeline from 'antd/lib/timeline';
import Dropdown from 'antd/lib/dropdown'; import Dropdown from 'antd/lib/dropdown';
import AnnotationMenuContainer from 'containers/annotation-page/top-bar/annotation-menu'; 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 { interface Props {
saving: boolean; saving: boolean;

@ -7,28 +7,141 @@ import { Col } from 'antd/lib/grid';
import Icon from '@ant-design/icons'; import Icon from '@ant-design/icons';
import Select from 'antd/lib/select'; import Select from 'antd/lib/select';
import Button from 'antd/lib/button'; 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 { useSelector } from 'react-redux';
import { FilterIcon, FullscreenIcon, InfoIcon } from 'icons'; import {
import { CombinedState, DimensionType, Workspace } from 'reducers/interfaces'; FilterIcon, FullscreenIcon, InfoIcon, BrainIcon,
} from 'icons';
import {
CombinedState, DimensionType, Workspace, PredictorState,
} from 'reducers/interfaces';
interface Props { interface Props {
workspace: Workspace; workspace: Workspace;
predictor: PredictorState;
isTrainingActive: boolean;
showStatistics(): void; showStatistics(): void;
switchPredictor(predictorEnabled: boolean): void;
showFilters(): void; showFilters(): void;
changeWorkspace(workspace: Workspace): void; changeWorkspace(workspace: Workspace): void;
jobInstance: any; jobInstance: any;
} }
function RightGroup(props: Props): JSX.Element { function RightGroup(props: Props): JSX.Element {
const { const {
showFilters, showStatistics, changeWorkspace, workspace, jobInstance, showStatistics,
changeWorkspace,
switchPredictor,
workspace,
predictor,
jobInstance,
isTrainingActive,
showFilters,
} = props; } = 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); const filters = useSelector((state: CombinedState) => state.annotation.annotations.filters);
return ( return (
<Col className='cvat-annotation-header-right-group'> <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 <Button
type='link' type='link'
className='cvat-annotation-header-button' className='cvat-annotation-header-button'

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

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

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

@ -1,12 +1,13 @@
// Copyright (C) 2020 Intel Corporation // Copyright (C) 2020-2021 Intel Corporation
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React, { import React, {
useState, useRef, useEffect, RefObject, RefObject, useContext, useEffect, useRef, useState,
} from 'react'; } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { useHistory } from 'react-router'; import { useHistory } from 'react-router';
import { Switch, Select } from 'antd';
import { Col, Row } from 'antd/lib/grid'; import { Col, Row } from 'antd/lib/grid';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import Form, { FormInstance } from 'antd/lib/form'; import Form, { FormInstance } from 'antd/lib/form';
@ -18,6 +19,9 @@ import patterns from 'utils/validation-patterns';
import { CombinedState } from 'reducers/interfaces'; import { CombinedState } from 'reducers/interfaces';
import LabelsEditor from 'components/labels-editor/labels-editor'; import LabelsEditor from 'components/labels-editor/labels-editor';
import { createProjectAsync } from 'actions/projects-actions'; import { createProjectAsync } from 'actions/projects-actions';
import CreateProjectContext from './create-project.context';
const { Option } = Select;
function NameConfigurationForm({ formRef }: { formRef: RefObject<FormInstance> }): JSX.Element { function NameConfigurationForm({ formRef }: { formRef: RefObject<FormInstance> }): JSX.Element {
return ( 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 { function AdvanvedConfigurationForm({ formRef }: { formRef: RefObject<FormInstance> }): JSX.Element {
return ( return (
<Form layout='vertical' ref={formRef}> <Form layout='vertical' ref={formRef}>
@ -69,12 +126,15 @@ export default function CreateProjectContent(): JSX.Element {
const [projectLabels, setProjectLabels] = useState<any[]>([]); const [projectLabels, setProjectLabels] = useState<any[]>([]);
const shouldShowNotification = useRef(false); const shouldShowNotification = useRef(false);
const nameFormRef = useRef<FormInstance>(null); const nameFormRef = useRef<FormInstance>(null);
const adaptiveAutoAnnotationFormRef = useRef<FormInstance>(null);
const advancedFormRef = useRef<FormInstance>(null); const advancedFormRef = useRef<FormInstance>(null);
const dispatch = useDispatch(); const dispatch = useDispatch();
const history = useHistory(); const history = useHistory();
const newProjectId = useSelector((state: CombinedState) => state.projects.activities.creates.id); const newProjectId = useSelector((state: CombinedState) => state.projects.activities.creates.id);
const { isTrainingActive } = useContext(CreateProjectContext);
useEffect(() => { useEffect(() => {
if (Number.isInteger(newProjectId) && shouldShowNotification.current) { if (Number.isInteger(newProjectId) && shouldShowNotification.current) {
const btn = <Button onClick={() => history.push(`/projects/${newProjectId}`)}>Open project</Button>; const btn = <Button onClick={() => history.push(`/projects/${newProjectId}`)}>Open project</Button>;
@ -94,17 +154,19 @@ export default function CreateProjectContent(): JSX.Element {
}, [newProjectId]); }, [newProjectId]);
const onSumbit = async (): Promise<void> => { const onSumbit = async (): Promise<void> => {
interface Project { let projectData: Record<string, any> = {};
[key: string]: any;
}
const projectData: Project = {};
if (nameFormRef.current && advancedFormRef.current) { if (nameFormRef.current && advancedFormRef.current) {
const basicValues = await nameFormRef.current.validateFields(); const basicValues = await nameFormRef.current.validateFields();
const advancedValues = await advancedFormRef.current.validateFields(); const advancedValues = await advancedFormRef.current.validateFields();
projectData.name = basicValues.name; const adaptiveAutoAnnotationValues = await adaptiveAutoAnnotationFormRef.current?.validateFields();
for (const [field, value] of Object.entries(advancedValues)) { projectData = {
projectData[field] = value; ...projectData,
...advancedValues,
name: basicValues.name,
};
if (adaptiveAutoAnnotationValues) {
projectData.training_project = { ...adaptiveAutoAnnotationValues };
} }
} }
@ -120,6 +182,11 @@ export default function CreateProjectContent(): JSX.Element {
<Col span={24}> <Col span={24}>
<NameConfigurationForm formRef={nameFormRef} /> <NameConfigurationForm formRef={nameFormRef} />
</Col> </Col>
{isTrainingActive.value && (
<Col span={24}>
<AdaptiveAutoAnnotationForm formRef={adaptiveAutoAnnotationFormRef} />
</Col>
)}
<Col span={24}> <Col span={24}>
<Text className='cvat-text-color'>Labels:</Text> <Text className='cvat-text-color'>Labels:</Text>
<LabelsEditor <LabelsEditor

@ -1,21 +1,56 @@
// Copyright (C) 2020 Intel Corporation // Copyright (C) 2020-2021 Intel Corporation
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import './styles.scss'; import './styles.scss';
import React from 'react'; import React, { useState } from 'react';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import { connect } from 'react-redux';
import CreateProjectContent from './create-project-content'; 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 ( return (
<Row justify='center' align='top' className='cvat-create-task-form-wrapper'> <CreateProjectContext.Provider value={defaultContext}>
<Col md={20} lg={16} xl={14} xxl={9}> <Row justify='center' align='top' className='cvat-create-task-form-wrapper'>
<Text className='cvat-title'>Create a new project</Text> <Col md={20} lg={16} xl={14} xxl={9}>
<CreateProjectContent /> <Text className='cvat-title'>Create a new project</Text>
</Col> <CreateProjectContent />
</Row> </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({ Modal.error({
width: 800, width: 800,
title: 'Could not clone the repository', title: 'Could not clone the repository',
className: 'cvat-create-task-clone-repository-fail',
content: ( content: (
<> <>
<Paragraph> <Paragraph>

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

@ -39,6 +39,7 @@ const SettingsModal = (props: SettingsModalProps): JSX.Element => {
localStorage.setItem('clientSettings', JSON.stringify(settingsForSaving)); localStorage.setItem('clientSettings', JSON.stringify(settingsForSaving));
notification.success({ notification.success({
message: 'Settings was successfully saved', message: 'Settings was successfully saved',
className: 'cvat-notification-notice-save-settings-success',
}); });
}; };
@ -62,6 +63,7 @@ const SettingsModal = (props: SettingsModalProps): JSX.Element => {
} catch { } catch {
notification.error({ notification.error({
message: 'Failed to load settings from local storage', 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 React, { useCallback, useState } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import GlobalHotKeys from 'utils/mousetrap-react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import GlobalHotKeys from 'utils/mousetrap-react';
import { CombinedState } from 'reducers/interfaces'; import { CombinedState } from 'reducers/interfaces';
import './styles.scss'; import './styles.scss';

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

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

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

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

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

@ -41,7 +41,15 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
points?: number, points?: number,
rectDrawingMethod?: RectDrawingMethod, rectDrawingMethod?: RectDrawingMethod,
): void { ): 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 // SPDX-License-Identifier: MIT
@ -32,7 +32,7 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
dispatch(createAnnotationsAsync(sessionInstance, frame, states)); dispatch(createAnnotationsAsync(sessionInstance, frame, states));
}, },
onRememberObject(labelID: number): void { 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 // SPDX-License-Identifier: MIT
@ -12,6 +12,8 @@ import { CombinedState, ObjectType } from 'reducers/interfaces';
interface OwnProps { interface OwnProps {
labelID: number; labelID: number;
keyToLabelMapping: Record<string, number>;
updateLabelShortcutKey(updatedKey: string, labelID: number): void;
} }
interface StateToProps { interface StateToProps {
@ -20,7 +22,7 @@ interface StateToProps {
labelColor: string; labelColor: string;
objectStates: any[]; objectStates: any[];
jobInstance: any; jobInstance: any;
frameNumber: any; frameNumber: number;
} }
interface DispatchToProps { interface DispatchToProps {
@ -127,35 +129,38 @@ class LabelItemContainer extends React.PureComponent<Props, State> {
private switchHidden(value: boolean): void { private switchHidden(value: boolean): void {
const { updateAnnotations } = this.props; const { updateAnnotations } = this.props;
const { ownObjectStates } = this.state; 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 { private switchLock(value: boolean): void {
const { updateAnnotations } = this.props; const { updateAnnotations } = this.props;
const { ownObjectStates } = this.state; 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 { public render(): JSX.Element {
const {
labelName, labelColor, keyToLabelMapping, labelID, updateLabelShortcutKey,
} = this.props;
const { visible, statesHidden, statesLocked } = this.state; const { visible, statesHidden, statesLocked } = this.state;
const { labelName, labelColor } = this.props;
return ( return (
<LabelItemComponent <LabelItemComponent
labelName={labelName} labelName={labelName}
labelColor={labelColor} labelColor={labelColor}
labelID={labelID}
keyToLabelMapping={keyToLabelMapping}
visible={visible} visible={visible}
statesHidden={statesHidden} statesHidden={statesHidden}
statesLocked={statesLocked} statesLocked={statesLocked}
@ -163,6 +168,7 @@ class LabelItemContainer extends React.PureComponent<Props, State> {
showStates={this.showStates} showStates={this.showStates}
lockStates={this.lockStates} lockStates={this.lockStates}
unlockStates={this.unlockStates} 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 // SPDX-License-Identifier: MIT
@ -68,7 +68,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
type Props = StateToProps & DispatchToProps; type Props = StateToProps & DispatchToProps;
class PropagateConfirmContainer extends React.PureComponent<Props> { class PropagateConfirmContainer extends React.PureComponent<Props> {
private propagateObject = (): void => { 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); const propagateUpToFrame = Math.min(frameNumber + propagateFrames, stopFrame);
propagateObject(jobInstance, objectState, frameNumber + 1, propagateUpToFrame); propagateObject(jobInstance, objectState, frameNumber + 1, propagateUpToFrame);
@ -87,7 +89,9 @@ class PropagateConfirmContainer extends React.PureComponent<Props> {
}; };
public render(): JSX.Element { 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); 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, searchAnnotationsAsync,
searchEmptyFrameAsync, searchEmptyFrameAsync,
setForceExitAnnotationFlag as setForceExitAnnotationFlagAction, setForceExitAnnotationFlag as setForceExitAnnotationFlagAction,
switchPredictor as switchPredictorAction,
getPredictionsAsync,
showFilters as showFiltersAction, showFilters as showFiltersAction,
showStatistics as showStatisticsAction, showStatistics as showStatisticsAction,
switchPlay, switchPlay,
@ -25,7 +27,9 @@ import {
} from 'actions/annotation-actions'; } from 'actions/annotation-actions';
import AnnotationTopBarComponent from 'components/annotation-page/top-bar/top-bar'; import AnnotationTopBarComponent from 'components/annotation-page/top-bar/top-bar';
import { Canvas } from 'cvat-canvas-wrapper'; 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'; import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react';
interface StateToProps { interface StateToProps {
@ -48,6 +52,8 @@ interface StateToProps {
normalizedKeyMap: Record<string, string>; normalizedKeyMap: Record<string, string>;
canvasInstance: Canvas; canvasInstance: Canvas;
forceExit: boolean; forceExit: boolean;
predictor: PredictorState;
isTrainingActive: boolean;
} }
interface DispatchToProps { interface DispatchToProps {
@ -62,6 +68,7 @@ interface DispatchToProps {
searchEmptyFrame(sessionInstance: any, frameFrom: number, frameTo: number): void; searchEmptyFrame(sessionInstance: any, frameFrom: number, frameTo: number): void;
setForceExitAnnotationFlag(forceExit: boolean): void; setForceExitAnnotationFlag(forceExit: boolean): void;
changeWorkspace(workspace: Workspace): void; changeWorkspace(workspace: Workspace): void;
switchPredictor(predictorEnabled: boolean): void;
} }
function mapStateToProps(state: CombinedState): StateToProps { function mapStateToProps(state: CombinedState): StateToProps {
@ -78,12 +85,14 @@ function mapStateToProps(state: CombinedState): StateToProps {
job: { instance: jobInstance }, job: { instance: jobInstance },
canvas: { ready: canvasIsReady, instance: canvasInstance }, canvas: { ready: canvasIsReady, instance: canvasInstance },
workspace, workspace,
predictor,
}, },
settings: { settings: {
player: { frameSpeed, frameStep }, player: { frameSpeed, frameStep },
workspace: { autoSave, autoSaveInterval }, workspace: { autoSave, autoSaveInterval },
}, },
shortcuts: { keyMap, normalizedKeyMap }, shortcuts: { keyMap, normalizedKeyMap },
plugins: { list },
} = state; } = state;
return { return {
@ -106,6 +115,8 @@ function mapStateToProps(state: CombinedState): StateToProps {
normalizedKeyMap, normalizedKeyMap,
canvasInstance, canvasInstance,
forceExit, forceExit,
predictor,
isTrainingActive: list.PREDICT,
}; };
} }
@ -146,6 +157,12 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
setForceExitAnnotationFlag(forceExit: boolean): void { setForceExitAnnotationFlag(forceExit: boolean): void {
dispatch(setForceExitAnnotationFlagAction(forceExit)); 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, redoAction,
workspace, workspace,
canvasIsReady, canvasIsReady,
searchAnnotations,
changeWorkspace,
keyMap, keyMap,
normalizedKeyMap, normalizedKeyMap,
canvasInstance, canvasInstance,
predictor,
searchAnnotations,
changeWorkspace,
switchPredictor,
isTrainingActive,
} = this.props; } = this.props;
const preventDefault = (event: KeyboardEvent | undefined): void => { const preventDefault = (event: KeyboardEvent | undefined): void => {
@ -611,6 +631,8 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
onInputChange={this.onChangePlayerInputValue} onInputChange={this.onChangePlayerInputValue}
onURLIconClick={this.onURLIconClick} onURLIconClick={this.onURLIconClick}
changeWorkspace={changeWorkspace} changeWorkspace={changeWorkspace}
switchPredictor={switchPredictor}
predictor={predictor}
workspace={workspace} workspace={workspace}
playing={playing} playing={playing}
saving={saving} saving={saving}
@ -636,6 +658,7 @@ class AnnotationTopBarContainer extends React.PureComponent<Props, State> {
onUndoClick={this.undo} onUndoClick={this.undo}
onRedoClick={this.redo} onRedoClick={this.redo}
jobInstance={jobInstance} jobInstance={jobInstance}
isTrainingActive={isTrainingActive}
/> />
</> </>
); );

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

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

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

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

@ -2,6 +2,11 @@
// //
// SPDX-License-Identifier: MIT // 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 { getAboutAsync } from 'actions/about-actions';
import { authorizedAsync, loadAuthActionsAsync } from 'actions/auth-actions'; import { authorizedAsync, loadAuthActionsAsync } from 'actions/auth-actions';
import { getFormatsAsync } from 'actions/formats-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 LayoutGrid from 'components/layout-grid/layout-grid';
import logger, { LogType } from 'cvat-logger'; import logger, { LogType } from 'cvat-logger';
import createCVATStore, { getCVATStore } from 'cvat-store'; import createCVATStore, { getCVATStore } from 'cvat-store';
import React from 'react';
import ReactDOM from 'react-dom';
import { KeyMap } from 'utils/mousetrap-react'; 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 createRootReducer from 'reducers/root-reducer';
import { resetErrors, resetMessages } from './actions/notification-actions'; import { resetErrors, resetMessages } from './actions/notification-actions';
import { CombinedState, NotificationsState } from './reducers/interfaces'; 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 // SPDX-License-Identifier: MIT
@ -38,6 +38,7 @@ const defaultState: AnnotationState = {
activeControl: ActiveControl.CURSOR, activeControl: ActiveControl.CURSOR,
}, },
job: { job: {
openTime: null,
labels: [], labels: [],
requestedId: null, requestedId: null,
instance: null, instance: null,
@ -108,6 +109,18 @@ const defaultState: AnnotationState = {
requestReviewDialogVisible: false, requestReviewDialogVisible: false,
submitReviewDialogVisible: false, submitReviewDialogVisible: false,
tabContentHeight: 0, tabContentHeight: 0,
predictor: {
enabled: false,
error: null,
message: '',
projectScore: 0,
fetching: false,
annotatedFrames: [],
timeRemaining: 0,
progress: 0,
annotationAmount: 0,
mediaAmount: 0,
},
workspace: Workspace.STANDARD, workspace: Workspace.STANDARD,
}; };
@ -129,6 +142,7 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
const { const {
job, job,
states, states,
openTime,
frameNumber: number, frameNumber: number,
frameFilename: filename, frameFilename: filename,
colors, colors,
@ -148,6 +162,7 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
...state, ...state,
job: { job: {
...state.job, ...state.job,
openTime,
fetching: false, fetching: false,
instance: job, instance: job,
labels: job.task.labels, labels: job.task.labels,
@ -445,9 +460,22 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
}; };
} }
case AnnotationActionTypes.REMEMBER_CREATED_OBJECT: { case AnnotationActionTypes.REMEMBER_CREATED_OBJECT: {
const { const { payload } = action;
shapeType, labelID, objectType, points, activeControl, rectDrawingMethod,
} = action.payload; 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 { return {
...state, ...state,
@ -460,12 +488,9 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
activeControl, activeControl,
}, },
drawing: { drawing: {
...state.drawing,
...payload,
activeInteractor: undefined, activeInteractor: undefined,
activeLabelID: labelID,
activeNumOfPoints: points,
activeObjectType: objectType,
activeShapeType: shapeType,
activeRectDrawingMethod: rectDrawingMethod,
}, },
}; };
} }
@ -1093,6 +1118,47 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
workspace, 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: { case AnnotationActionTypes.RESET_CANVAS: {
return { return {
...state, ...state,

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

@ -102,6 +102,9 @@ const defaultState: NotificationsState = {
resolvingIssue: null, resolvingIssue: null,
submittingReview: null, submittingReview: null,
}, },
predictor: {
prediction: null,
},
}, },
messages: { messages: {
tasks: { tasks: {
@ -843,6 +846,7 @@ export default function (state = defaultState, action: AnyAction): Notifications
'Could not upload annotations for the ' + 'Could not upload annotations for the ' +
`<a href="/tasks/${taskID}/jobs/${jobID}" target="_blank">job ${taskID}</a>`, `<a href="/tasks/${taskID}/jobs/${jobID}" target="_blank">job ${taskID}</a>`,
reason: error.toString(), 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 BoundariesActionTypes.RESET_AFTER_ERROR:
case AuthActionTypes.LOGOUT_SUCCESS: { case AuthActionTypes.LOGOUT_SUCCESS: {
return { ...defaultState }; return { ...defaultState };

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

@ -287,10 +287,16 @@ const defaultKeyMap = ({
}, },
TOGGLE_LAYOUT_GRID: { TOGGLE_LAYOUT_GRID: {
name: 'Toggle layout grid', name: 'Toggle layout grid',
description: 'Is used in development', description: 'The grid is used to UI development',
sequences: ['ctrl+alt+enter'], sequences: ['ctrl+alt+enter'],
action: 'keydown', 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; } as any) as KeyMap;
const defaultState: ShortcutsState = { const defaultState: ShortcutsState = {

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

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

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

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

@ -11,6 +11,7 @@
- [How to add a format](#how-to-add) - [How to add a format](#how-to-add)
- [Format descriptions](#formats) - [Format descriptions](#formats)
- [CVAT](#cvat) - [CVAT](#cvat)
- [Datumaro](#datumaro)
- [LabelMe](#labelme) - [LabelMe](#labelme)
- [MOT](#mot) - [MOT](#mot)
- [MOTS](#mots) - [MOTS](#mots)
@ -175,7 +176,7 @@ features, so it can be used to make data backups.
- [Format specification](/cvat/apps/documentation/xml_format.md) - [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: Downloaded file: a ZIP file of the following structure:
@ -189,7 +190,7 @@ taskname.zip/
- tracks are split by frames - tracks are split by frames
#### CVAT for videos dumper #### CVAT for videos export
Downloaded file: a ZIP file of the following structure: 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 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" /> ### [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) - [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: - supported attributes:
- `occluded` - `occluded` (both UI option and a separate attribute)
- `truncated` and `difficult` (should be defined for labels as `checkbox` -es) - `truncated` and `difficult` (should be defined for labels as `checkbox` -es)
- action attributes (import only, should be defined 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 #### Pascal VOC export
@ -229,7 +242,7 @@ Downloaded file: a zip archive of the following structure:
```bash ```bash
taskname.zip/ taskname.zip/
├── JpegImages/ ├── JPEGImages/
│   ├── <image_name1>.jpg │   ├── <image_name1>.jpg
│   ├── <image_name2>.jpg │   ├── <image_name2>.jpg
│   └── <image_nameN>.jpg │   └── <image_nameN>.jpg
@ -499,17 +512,47 @@ zip images.zip -j -@ < train.txt
- [Format specification](http://cocodataset.org/#format-data) - [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 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 #### 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: Downloaded file: a zip archive with following structure:
```bash ```bash
taskname.zip/ taskname.zip/
├── task2.tfrecord ├── default.tfrecord
└── label_map.pbtxt └── 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: Uploaded file: a zip archive of following structure:
```bash ```bash
taskname.zip/ taskname.zip/
└── task2.tfrecord └── <any name>.tfrecord
``` ```
- supported annotations: Rectangles - 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 sequence](https://arxiv.org/pdf/1906.04567.pdf)<a id="mot" />
#### MOT Dumper #### MOT export
Downloaded file: a zip archive of the following structure: Downloaded file: a zip archive of the following structure:
@ -735,7 +801,7 @@ person
- supported annotations: Rectangle shapes and tracks - supported annotations: Rectangle shapes and tracks
- supported attributes: `visibility` (number), `ignored` (checkbox) - supported attributes: `visibility` (number), `ignored` (checkbox)
#### MOT Loader #### MOT import
Uploaded file: a zip archive of the structure above or: 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](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: Downloaded file: a zip archive of the following structure:
@ -773,7 +839,7 @@ person
- supported annotations: Rectangle and Polygon tracks - supported annotations: Rectangle and Polygon tracks
#### MOTS PNG Loader #### MOTS PNG import
Uploaded file: a zip archive of the structure above 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](http://labelme.csail.mit.edu/Release3.0)<a id="labelme" />
#### LabelMe Dumper #### LabelMe export
Downloaded file: a zip archive of the following structure: Downloaded file: a zip archive of the following structure:
@ -793,7 +859,7 @@ taskname.zip/
- supported annotations: Rectangles, Polygons (with attributes) - supported annotations: Rectangles, Polygons (with attributes)
#### LabelMe Loader #### LabelMe import
Uploaded file: a zip archive of the following structure: 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](http://www.image-net.org)<a id="imagenet" />
#### ImageNet Dumper #### ImageNet export
Downloaded file: a zip archive of the following structure: Downloaded file: a zip archive of the following structure:
@ -835,7 +901,7 @@ taskname.zip/
- supported annotations: Labels - supported annotations: Labels
#### ImageNet Loader #### ImageNet import
Uploaded file: a zip archive of the structure above 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](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: 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 - supported annotations: Rectangles, Polygons
#### CamVid Loader #### CamVid import
Uploaded file: a zip archive of the structure above 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](http://shuoyang1213.me/WIDERFACE/)<a id="widerface" />
#### WIDER Face Dumper #### WIDER Face export
Downloaded file: a zip archive of the following structure: Downloaded file: a zip archive of the following structure:
@ -899,19 +965,21 @@ taskname.zip/
``` ```
- supported annotations: Rectangles (with attributes), Labels - supported annotations: Rectangles (with attributes), Labels
- supported attributes: `blur`, `expression`, `illumination`, - supported attributes:
`occluded` (both the annotation property & an attribute), `pose`, `invalid` - `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 Uploaded file: a zip archive of the structure above
- supported annotations: Rectangles (with attributes), Labels - 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](https://github.com/ox-vgg/vgg_face2)<a id="vggface2" />
#### VGGFace2 Dumper #### VGGFace2 export
Downloaded file: a zip archive of the following structure: Downloaded file: a zip archive of the following structure:
@ -934,7 +1002,7 @@ label1 <class1>
- supported annotations: Rectangles, Points (landmarks - groups of 5 points) - supported annotations: Rectangles, Points (landmarks - groups of 5 points)
#### VGGFace2 Loader #### VGGFace2 import
Uploaded file: a zip archive of the structure above 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](https://www.aitribune.com/dataset/2018051063)<a id="market1501" />
#### Market-1501 Dumper #### Market-1501 export
Downloaded file: a zip archive of the following structure: 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`) - 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 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](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: Downloaded file: a zip archive of the following structure:
@ -1027,7 +1095,7 @@ taskname.zip/
- supported annotations: Rectangles and Polygons with label `icdar` - supported annotations: Rectangles and Polygons with label `icdar`
and attributes `index`, `text`, `color`, `center` and attributes `index`, `text`, `color`, `center`
#### ICDAR13/15 Loader #### ICDAR13/15 import
Uploaded file: a zip archive of the structure above 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( dataset = Dataset.from_extractors(CvatTaskDataExtractor(
task_data, include_images=save_images), env=dm_env) task_data, include_images=save_images), env=dm_env)
with TemporaryDirectory() as temp_dir: 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) make_zip_archive(temp_dir, dst_file)

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

@ -496,6 +496,7 @@ class TaskExportTest(_DbTestBase):
self.assertTrue(frame.frame in range(6, 10)) self.assertTrue(frame.frame in range(6, 10))
self.assertEqual(i + 1, 4) self.assertEqual(i + 1, 4)
class FrameMatchingTest(_DbTestBase): class FrameMatchingTest(_DbTestBase):
def _generate_task_images(self, paths): # pylint: disable=no-self-use def _generate_task_images(self, paths): # pylint: disable=no-self-use
f = BytesIO() f = BytesIO()
@ -586,3 +587,327 @@ class FrameMatchingTest(_DbTestBase):
root = find_dataset_root(dataset, task_data) root = find_dataset_root(dataset, task_data)
self.assertEqual(expected, root) 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) - [Advanced settings](#advanced-settings)
- [Share path](#share-path) - [Share path](#share-path)
- [Email verification](#email-verification) - [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) - [Deploy secure CVAT instance with HTTPS](#deploy-secure-cvat-instance-with-https)
- [Prerequisites](#prerequisites) - [Prerequisites](#prerequisites)
- [Roadmap](#roadmap) - [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) [Django SMTP backend configuration](https://docs.djangoproject.com/en/3.1/topics/email/#django.core.mail.backends.smtp.EmailBackend)
for details. 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 ### Deploy secure CVAT instance with HTTPS
Certificates (issued by let's encrypt) to cloud instance. Certificates (issued by let's encrypt) to cloud instance.

@ -60,10 +60,10 @@
```bash ```bash
nuctl deploy --project-name cvat \ 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 \ --platform local --base-image tensorflow/tensorflow:1.15.5-gpu-py3 \
--desc "GPU based implementation of Mask RCNN on Python 3, Keras, and TensorFlow." \ --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}}' \ --triggers '{"myHttpTrigger": {"maxWorkers": 1}}' \
--resource-limit nvidia.com/gpu=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