diff --git a/.travis.yml b/.travis.yml index 9450e7ea7f19..207d155ee69c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,5 @@ -language: python - -python: - - '3.5' +language: generic +dist: focal cache: npm: true diff --git a/CHANGELOG.md b/CHANGELOG.md index afd20a1e658f..174d47b881d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,21 +5,15 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [1.2.0-beta] - Unreleased +## [1.2.0] - Unreleased ### Added -- Manual review pipeline: issues/comments/workspace () -- Added basic projects implementation () -- Added documentation on how to mount cloud starage(AWS S3 bucket, Azure container, Google Drive) as FUSE () -- Added ability to work with share files without copying inside () -- Tooltips in label selectors () -- Page redirect after login using `next` query parameter () +- ### Changed -- PATCH requests from cvat-core submit only changed fields () -- Migrated to Antd 4.9 () +- ### Deprecated @@ -31,6 +25,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- + +### Security + +- + +## [1.2.0-beta] - 2020-12-15 + +### Added + +- Manual review pipeline: issues/comments/workspace () +- Basic projects implementation () +- Documentation on how to mount cloud starage(AWS S3 bucket, Azure container, Google Drive) as FUSE () +- Ability to work with share files without copying inside () +- Tooltips in label selectors () +- Page redirect after login using `next` query parameter () +- [ImageNet](http://www.image-net.org) format support () +- [CamVid](http://mi.eng.cam.ac.uk/research/projects/VideoRec/CamVid/) format support () + +### Changed + +- PATCH requests from cvat-core submit only changed fields () +- Migrated to Antd 4.9 () + +### Fixed + - Django templates for email and user guide () - Saving relative paths in dummy chunks instead of absolute () - Objects with a specific label cannot be displayed if at least one tag with the label exist () @@ -47,10 +67,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Reset state (reviews, issues) after logout or changing a job () - TypeError: Cannot read property 'id' of undefined when updating a task () -### Security - -- - ## [1.2.0-alpha] - 2020-11-09 ### Added diff --git a/Dockerfile b/Dockerfile index 844a18c01196..fe79a5eb844b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,52 @@ +FROM ubuntu:20.04 as build-image + +ARG http_proxy +ARG https_proxy +ARG no_proxy +ARG socks_proxy +ARG DJANGO_CONFIGURATION + +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends install -yq \ + apache2-dev \ + build-essential \ + curl \ + libldap2-dev \ + libsasl2-dev \ + nasm \ + git \ + pkg-config \ + python3-dev \ + python3-pip \ + python3-venv && \ + rm -rf /var/lib/apt/lists/* + +# Compile Openh264 and FFmpeg +ARG PREFIX=/opt/ffmpeg +ARG PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig + +ENV FFMPEG_VERSION=4.3.1 \ + OPENH264_VERSION=2.1.1 + +WORKDIR /tmp/openh264 +RUN curl -sL https://github.com/cisco/openh264/archive/v${OPENH264_VERSION}.tar.gz --output openh264-${OPENH264_VERSION}.tar.gz && \ + tar -zx --strip-components=1 -f openh264-${OPENH264_VERSION}.tar.gz && \ + make -j5 && make install PREFIX=${PREFIX} && make clean + +WORKDIR /tmp/ffmpeg +RUN curl -sLO https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2 && \ + tar -jx --strip-components=1 -f ffmpeg-${FFMPEG_VERSION}.tar.bz2 && \ + ./configure --disable-nonfree --disable-gpl --enable-libopenh264 --enable-shared --disable-static --prefix="${PREFIX}" && \ + make -j5 && make install && make distclean + +# Install requirements +RUN python3 -m venv /opt/venv +ENV PATH="/opt/venv/bin:${PATH}" +RUN python3 -m pip install --no-cache-dir -U pip==20.0.1 setuptools==49.6.0 wheel==0.35.1 +COPY cvat/requirements/ /tmp/requirements/ +RUN DATUMARO_HEADLESS=1 python3 -m pip install --no-cache-dir -r /tmp/requirements/${DJANGO_CONFIGURATION}.txt + + FROM ubuntu:20.04 ARG http_proxy @@ -21,59 +70,26 @@ ENV DJANGO_CONFIGURATION=${DJANGO_CONFIGURATION} # Install necessary apt packages RUN apt-get update && \ - apt-get --no-install-recommends install -yq \ - software-properties-common && \ - apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends install -yq \ apache2 \ - apache2-dev \ - apt-utils \ - build-essential \ libapache2-mod-xsendfile \ supervisor \ - libavcodec-dev=7:4.2.4-1ubuntu0.1 \ - libavdevice-dev=7:4.2.4-1ubuntu0.1 \ - libavfilter-dev=7:4.2.4-1ubuntu0.1 \ - libavformat-dev=7:4.2.4-1ubuntu0.1 \ - libavutil-dev=7:4.2.4-1ubuntu0.1 \ - libswresample-dev=7:4.2.4-1ubuntu0.1 \ - libswscale-dev=7:4.2.4-1ubuntu0.1 \ - libldap2-dev \ - libsasl2-dev \ - pkg-config \ - python3-dev \ - python3-pip \ + libldap-2.4-2 \ + libsasl2-2 \ + libpython3-dev \ tzdata \ + python3-distutils \ p7zip-full \ git \ git-lfs \ - ssh \ poppler-utils \ + ssh \ curl && \ - python3 -m pip install --no-cache-dir -U pip==20.0.1 setuptools==49.6.0 wheel==0.35.1 && \ ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime && \ dpkg-reconfigure -f noninteractive tzdata && \ rm -rf /var/lib/apt/lists/* && \ echo 'application/wasm wasm' >> /etc/mime.types -# Add a non-root user -ENV USER=${USER} -ENV HOME /home/${USER} -WORKDIR ${HOME} -RUN adduser --shell /bin/bash --disabled-password --gecos "" ${USER} && \ - if [ -z ${socks_proxy} ]; then \ - echo export "GIT_SSH_COMMAND=\"ssh -o StrictHostKeyChecking=no -o ConnectTimeout=30\"" >> ${HOME}/.bashrc; \ - else \ - echo export "GIT_SSH_COMMAND=\"ssh -o StrictHostKeyChecking=no -o ConnectTimeout=30 -o ProxyCommand='nc -X 5 -x ${socks_proxy} %h %p'\"" >> ${HOME}/.bashrc; \ - fi - -COPY components /tmp/components - -# Install and initialize CVAT, copy all necessary files -COPY cvat/requirements/ /tmp/requirements/ -COPY supervisord.conf mod_wsgi.conf wait-for-it.sh manage.py ${HOME}/ -RUN python3 -m pip install --no-cache-dir -r /tmp/requirements/${DJANGO_CONFIGURATION}.txt - ARG CLAM_AV ENV CLAM_AV=${CLAM_AV} RUN if [ "$CLAM_AV" = "yes" ]; then \ @@ -87,20 +103,51 @@ RUN if [ "$CLAM_AV" = "yes" ]; then \ rm -rf /var/lib/apt/lists/*; \ fi -COPY ssh ${HOME}/.ssh -COPY utils ${HOME}/utils -COPY cvat/ ${HOME}/cvat -COPY cvat-core/ ${HOME}/cvat-core -COPY cvat-data/ ${HOME}/cvat-data -COPY tests ${HOME}/tests +# Add a non-root user +ENV USER=${USER} +ENV HOME /home/${USER} +RUN adduser --shell /bin/bash --disabled-password --gecos "" ${USER} && \ + if [ -z ${socks_proxy} ]; then \ + echo export "GIT_SSH_COMMAND=\"ssh -o StrictHostKeyChecking=no -o ConnectTimeout=30\"" >> ${HOME}/.bashrc; \ + else \ + echo export "GIT_SSH_COMMAND=\"ssh -o StrictHostKeyChecking=no -o ConnectTimeout=30 -o ProxyCommand='nc -X 5 -x ${socks_proxy} %h %p'\"" >> ${HOME}/.bashrc; \ + fi + +ARG INSTALL_SOURCES='no' +WORKDIR ${HOME}/sources +RUN if [ "$INSTALL_SOURCES" = "yes" ]; then \ + sed -Ei 's/^# deb-src /deb-src /' /etc/apt/sources.list && \ + apt-get update && \ + dpkg --get-selections | while read -r line; do \ + package=$(echo "$line" | awk '{print $1}'); \ + mkdir "$package"; \ + ( \ + cd "$package"; \ + apt-get -q --download-only source "$package"; \ + ) \ + done && \ + rm -rf /var/lib/apt/lists/*; \ + fi +COPY --from=build-image /tmp/openh264/openh264*.tar.gz /tmp/ffmpeg/ffmpeg*.tar.bz2 ${HOME}/sources/ + +# Copy python virtual enviroment and FFmpeg binaries from build-image +COPY --from=build-image /opt/venv /opt/venv +ENV PATH="/opt/venv/bin:${PATH}" +COPY --from=build-image /opt/ffmpeg /usr -RUN chown -R ${USER}:${USER} . +# Install and initialize CVAT, copy all necessary files +COPY --chown=${USER} components /tmp/components +COPY --chown=${USER} ssh ${HOME}/.ssh +COPY --chown=${USER} supervisord.conf mod_wsgi.conf wait-for-it.sh manage.py ${HOME}/ +COPY --chown=${USER} cvat/ ${HOME}/cvat +COPY --chown=${USER} utils/ ${HOME}/utils # RUN all commands below as 'django' user USER ${USER} +WORKDIR ${HOME} RUN mkdir data share media keys logs /tmp/supervisord RUN python3 manage.py collectstatic -EXPOSE 8080 8443 +EXPOSE 8080 ENTRYPOINT ["/usr/bin/supervisord"] diff --git a/Dockerfile.ci b/Dockerfile.ci index b51268dd1957..bc642137e17b 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -6,6 +6,7 @@ USER root RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends install -yq \ gpg-agent \ + gnupg2 \ apt-utils \ build-essential \ python3-dev \ @@ -20,10 +21,17 @@ RUN apt-get update && \ && \ rm -rf /var/lib/apt/lists/*; -RUN python3 -m pip install --no-cache-dir -r /tmp/requirements/${DJANGO_CONFIGURATION}.txt && \ +COPY cvat/requirements/ /tmp/requirements/ + +RUN DATUMARO_HEADLESS=1 python3 -m pip install --no-cache-dir -r /tmp/requirements/${DJANGO_CONFIGURATION}.txt && \ python3 -m pip install --no-cache-dir coveralls RUN gem install coveralls-lcov +COPY utils ${HOME}/utils +COPY cvat-core ${HOME}/cvat-core +COPY cvat-data ${HOME}/cvat-data +COPY tests ${HOME}/tests + COPY .coveragerc . ENTRYPOINT [] diff --git a/README.md b/README.md index de2baca2c9ea..e0921ba525c0 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,13 @@ annotation team. Try it online [cvat.org](https://cvat.org). ## Supported annotation formats -Format selection is possible after clicking on the Upload annotation -and Dump annotation buttons. -[Datumaro](https://github.com/openvinotoolkit/datumaro) dataset -framework allows additional dataset transformations -via its command line tool and Python library. +Format selection is possible after clicking on the Upload annotation and Dump +annotation buttons. [Datumaro](https://github.com/openvinotoolkit/datumaro) +dataset framework allows additional dataset transformations via its command +line tool and Python library. + +For more information about supported formats look at the +[documentation](cvat/apps/dataset_manager/formats/README.md#formats). | Annotation format | Import | Export | | ----------------------------------------------------------------------------- | ------ | ------ | @@ -58,19 +60,21 @@ via its command line tool and Python library. | [TFrecord](https://www.tensorflow.org/tutorials/load_data/tf_records) | X | X | | [MOT](https://motchallenge.net/) | X | X | | [LabelMe 3.0](http://labelme.csail.mit.edu/Release3.0) | X | X | +| [ImageNet](http://www.image-net.org) | X | X | +| [CamVid](http://mi.eng.cam.ac.uk/research/projects/VideoRec/CamVid/) | X | X | ## Deep learning models for automatic labeling -| Name | Type | Framework | -| ------------------------------------------------------------------------------------------------------- | ---------- | ---------- | -| [Deep Extreme Cut](/serverless/openvino/dextr/nuclio) | interactor | OpenVINO | -| [Faster RCNN](/serverless/tensorflow/faster_rcnn_inception_v2_coco/nuclio) | detector | TensorFlow | -| [Mask RCNN](/serverless/openvino/omz/public/mask_rcnn_inception_resnet_v2_atrous_coco/nuclio) | detector | OpenVINO | -| [YOLO v3](/serverless/openvino/omz/public/yolo-v3-tf/nuclio) | detector | OpenVINO | -| [Text detection v4](/serverless/openvino/omz/intel/text-detection-0004/nuclio) | detector | OpenVINO | -| [Semantic segmentation for ADAS](/serverless/openvino/omz/intel/semantic-segmentation-adas-0001/nuclio) | detector | OpenVINO | -| [Mask RCNN](/serverless/tensorflow/matterport/mask_rcnn/nuclio) | detector | TensorFlow | -| [Object reidentification](/serverless/openvino/omz/intel/person-reidentification-retail-300/nuclio) | reid | OpenVINO | +| Name | Type | Framework | CPU | GPU | +| ------------------------------------------------------------------------------------------------------- | ---------- | ---------- | --- | --- | +| [Deep Extreme Cut](/serverless/openvino/dextr/nuclio) | interactor | OpenVINO | X | +| [Faster RCNN](/serverless/tensorflow/faster_rcnn_inception_v2_coco/nuclio) | detector | TensorFlow | X | X | +| [Mask RCNN](/serverless/openvino/omz/public/mask_rcnn_inception_resnet_v2_atrous_coco/nuclio) | detector | OpenVINO | X | +| [YOLO v3](/serverless/openvino/omz/public/yolo-v3-tf/nuclio) | detector | OpenVINO | X | +| [Text detection v4](/serverless/openvino/omz/intel/text-detection-0004/nuclio) | detector | OpenVINO | X | +| [Semantic segmentation for ADAS](/serverless/openvino/omz/intel/semantic-segmentation-adas-0001/nuclio) | detector | OpenVINO | X | +| [Mask RCNN](/serverless/tensorflow/matterport/mask_rcnn/nuclio) | detector | TensorFlow | X | +| [Object reidentification](/serverless/openvino/omz/intel/person-reidentification-retail-300/nuclio) | reid | OpenVINO | X | ## Online demo: [cvat.org](https://cvat.org) diff --git a/components/serverless/docker-compose.serverless.yml b/components/serverless/docker-compose.serverless.yml index 3309ab495138..de94f6166b83 100644 --- a/components/serverless/docker-compose.serverless.yml +++ b/components/serverless/docker-compose.serverless.yml @@ -2,7 +2,7 @@ version: '3.3' services: serverless: container_name: nuclio - image: quay.io/nuclio/dashboard:1.4.8-amd64 + image: quay.io/nuclio/dashboard:1.5.8-amd64 restart: always networks: default: diff --git a/cvat-canvas/package-lock.json b/cvat-canvas/package-lock.json index e13c0da193ed..ff9e2c0b4308 100644 --- a/cvat-canvas/package-lock.json +++ b/cvat-canvas/package-lock.json @@ -4528,12 +4528,6 @@ "dev": true, "optional": true }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, @@ -5538,9 +5532,9 @@ "dev": true }, "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", + "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", "dev": true }, "inquirer": { diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json index df634c485c4c..3f5f2a7e72db 100644 --- a/cvat-ui/package-lock.json +++ b/cvat-ui/package-lock.json @@ -1255,12 +1255,11 @@ "dev": true }, "@types/react": { - "version": "16.9.56", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.56.tgz", - "integrity": "sha512-gIkl4J44G/qxbuC6r2Xh+D3CGZpJ+NdWTItAPmZbR5mUS+JQ8Zvzpl0ea5qT/ZT3ZNTUcDKUVqV3xBE8wv/DyQ==", + "version": "16.14.1", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.1.tgz", + "integrity": "sha512-32mxrbX62m5b+lMTSzucFKNIr8Eq4T6T3rDVxYzKqyRwyfnPcwZppWW0YXUlPNPUE+r6phBtHXYRgr8ad/Zl9A==", "requires": { - "@types/prop-types": "*", - "csstype": "^3.0.2" + "@types/prop-types": "*" } }, "@types/react-color": { @@ -1273,11 +1272,11 @@ } }, "@types/react-dom": { - "version": "16.9.9", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.9.tgz", - "integrity": "sha512-jE16FNWO3Logq/Lf+yvEAjKzhpST/Eac8EMd1i4dgZdMczfgqC8EjpxwNgEe3SExHYLliabXDh9DEhhqnlXJhg==", + "version": "16.9.10", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.10.tgz", + "integrity": "sha512-ItatOrnXDMAYpv6G8UCk2VhbYVTjZT9aorLtA/OzDN9XJ2GKcfam68jutoAcILdRjsRUO8qb7AmyObF77Q8QFw==", "requires": { - "@types/react": "*" + "@types/react": "^16" } }, "@types/react-redux": { @@ -3954,11 +3953,6 @@ "css-tree": "1.0.0-alpha.37" } }, - "csstype": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", - "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==" - }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -7799,10 +7793,7 @@ "global-dirs": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", - "requires": { - "ini": "^1.3.4" - } + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=" }, "global-modules": { "version": "2.0.0", @@ -7817,7 +7808,6 @@ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", "requires": { - "ini": "^1.3.5", "kind-of": "^6.0.2", "which": "^1.3.1" } @@ -7831,7 +7821,6 @@ "requires": { "expand-tilde": "^2.0.2", "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", "is-windows": "^1.0.1", "which": "^1.2.14" } @@ -8215,11 +8204,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, "inquirer": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.0.tgz", @@ -10514,7 +10498,6 @@ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "requires": { "deep-extend": "^0.6.0", - "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" } @@ -17151,7 +17134,6 @@ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", "requires": { - "ini": "^1.3.5", "kind-of": "^6.0.2", "which": "^1.3.1" } @@ -17165,7 +17147,6 @@ "requires": { "expand-tilde": "^2.0.2", "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", "is-windows": "^1.0.1", "which": "^1.2.14" } @@ -17421,11 +17402,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" - }, "inquirer": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", @@ -23728,12 +23704,6 @@ "dev": true, "optional": true }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, @@ -24815,9 +24785,9 @@ "dev": true }, "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, "insert-css": { @@ -28698,9 +28668,9 @@ } }, "rc-virtual-list": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.2.2.tgz", - "integrity": "sha512-OepvZDQGUbQQBFk5m2Ds32rfO/tSj9gZkLbzwaIw/hwGgvatDmW+j97YQvFkUQp/XDgdSGcfFfj/6XTKpz0J4g==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.2.3.tgz", + "integrity": "sha512-uEeYDQWwQhxR97SekPeGRbzPtHSbSpw/mYb6QpZZ9bA43kf7s1socV3fD3ySYhQVzo0I+/IUD9jFGit6FbM0WA==", "requires": { "classnames": "^2.2.6", "rc-resize-observer": "^0.2.3", diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 3582a2b334ce..6e1b20723bca 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -50,9 +50,9 @@ "@ant-design/icons": "^4.3.0", "@types/lodash": "^4.14.165", "@types/platform": "^1.3.3", - "@types/react": "^16.9.56", + "@types/react": "^16.14.1", "@types/react-color": "^3.0.4", - "@types/react-dom": "^16.9.9", + "@types/react-dom": "^16.9.10", "@types/react-redux": "^7.1.11", "@types/react-router": "^5.0.5", "@types/react-router-dom": "^5.1.6", @@ -68,6 +68,7 @@ "moment": "^2.29.1", "platform": "^1.3.6", "prop-types": "^15.7.2", + "rc-virtual-list": "^3.2.3", "react": "^16.14.0", "react-color": "^2.19.3", "react-cookie": "^4.0.3", diff --git a/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-annotation-sidebar.tsx b/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-annotation-sidebar.tsx index 8782eb18376c..10f0b5961c48 100644 --- a/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-annotation-sidebar.tsx +++ b/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-annotation-sidebar.tsx @@ -125,14 +125,16 @@ function AttributeAnnotationSidebar(props: StateToProps & DispatchToProps): JSX. const collapse = (): void => { const [collapser] = window.document.getElementsByClassName('attribute-annotation-sidebar'); + const listener = (event: TransitionEvent): void => { + if (event.target && event.propertyName === 'width' && event.target === collapser) { + canvasInstance.fitCanvas(); + canvasInstance.fit(); + (collapser as HTMLElement).removeEventListener('transitionend', listener as any); + } + }; + if (collapser) { - collapser.addEventListener( - 'transitionend', - () => { - canvasInstance.fitCanvas(); - }, - { once: true }, - ); + (collapser as HTMLElement).addEventListener('transitionend', listener as any); } setSidebarCollapsed(!sidebarCollapsed); diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx index feaef8c523f3..c5288a9b7132 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MIT import './styles.scss'; -import React, { Dispatch, useEffect } from 'react'; +import React, { Dispatch, useEffect, TransitionEvent } from 'react'; import { AnyAction } from 'redux'; import { connect } from 'react-redux'; import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons'; @@ -81,24 +81,22 @@ function ObjectsSideBar(props: StateToProps & DispatchToProps & OwnProps): JSX.E }; }, []); - useEffect(() => { - const listener = (event: Event): void => { - if ( - (event as TransitionEvent).propertyName === 'width' && - ((event.target as any).classList as DOMTokenList).contains('ant-tabs-tab-prev') - ) { + const collapse = (): void => { + const [collapser] = window.document.getElementsByClassName('cvat-objects-sidebar'); + const listener = (event: TransitionEvent): void => { + if (event.target && event.propertyName === 'width' && event.target === collapser) { + canvasInstance.fitCanvas(); canvasInstance.fit(); + (collapser as HTMLElement).removeEventListener('transitionend', listener as any); } }; - const [sidebar] = window.document.getElementsByClassName('cvat-objects-sidebar'); - - sidebar.addEventListener('transitionstart', listener); + if (collapser) { + (collapser as HTMLElement).addEventListener('transitionend', listener as any); + } - return () => { - sidebar.removeEventListener('transitionstart', listener); - }; - }, []); + collapseSidebar(); + }; return ( {sidebarCollapsed ? : } diff --git a/cvat-ui/src/components/projects-page/project-list.tsx b/cvat-ui/src/components/projects-page/project-list.tsx index 5ff8b2ff19c0..1d7138902dac 100644 --- a/cvat-ui/src/components/projects-page/project-list.tsx +++ b/cvat-ui/src/components/projects-page/project-list.tsx @@ -58,6 +58,7 @@ export default function ProjectListComponent(): JSX.Element { - + @@ -835,3 +836,41 @@ taskname.zip/ Uploaded file: a zip archive of the structure above - supported annotations: Labels + +### [CamVid](http://mi.eng.cam.ac.uk/research/projects/VideoRec/CamVid/) + +#### CamVid Dumper + +Downloaded file: a zip archive of the following structure: + +```bash +taskname.zip/ +├── labelmap.txt # optional, required for non-CamVid labels +└── / + ├── image1.png + └── image2.png +└── annot/ + ├── image1.png + └── image2.png +└── .txt + +# labelmap.txt +# color (RGB) label +0 0 0 Void +64 128 64 Animal +192 0 128 Archway +0 128 192 Bicyclist +0 128 64 Bridge +``` + +Mask is a `png` image with 1 or 3 channels where each pixel +has own color which corresponds to a label. +`(0, 0, 0)` is used for background by default. + +- supported annotations: Rectangles, Polygons + +#### CamVid Loader + +Uploaded file: a zip archive of the structure above + +- supported annotations: Polygons diff --git a/cvat/apps/dataset_manager/formats/camvid.py b/cvat/apps/dataset_manager/formats/camvid.py new file mode 100644 index 000000000000..bcd00b7a7bf1 --- /dev/null +++ b/cvat/apps/dataset_manager/formats/camvid.py @@ -0,0 +1,42 @@ +# Copyright (C) 2020 Intel Corporation +# +# SPDX-License-Identifier: MIT + +from tempfile import TemporaryDirectory + +from datumaro.components.project import Dataset +from pyunpack import Archive + +from cvat.apps.dataset_manager.bindings import (CvatTaskDataExtractor, + import_dm_annotations) +from cvat.apps.dataset_manager.util import make_zip_archive + +from .registry import dm_env, exporter, importer +from .utils import make_colormap + + +@exporter(name='CamVid', ext='ZIP', version='1.0') +def _export(dst_file, task_data, save_images=False): + extractor = CvatTaskDataExtractor(task_data, include_images=save_images) + envt = dm_env.transforms + extractor = extractor.transform(envt.get('polygons_to_masks')) + extractor = extractor.transform(envt.get('boxes_to_masks')) + extractor = extractor.transform(envt.get('merge_instance_segments')) + extractor = Dataset.from_extractors(extractor) # apply lazy transforms + label_map = make_colormap(task_data) + with TemporaryDirectory() as temp_dir: + dm_env.converters.get('camvid').convert(extractor, + save_dir=temp_dir, save_images=save_images, apply_colormap=True, + label_map={label: label_map[label][0] for label in label_map}) + + make_zip_archive(temp_dir, dst_file) + +@importer(name='CamVid', ext='ZIP', version='1.0') +def _import(src_file, task_data): + with TemporaryDirectory() as tmp_dir: + Archive(src_file.name).extractall(tmp_dir) + + dataset = dm_env.make_importer('camvid')(tmp_dir).make_dataset() + masks_to_polygons = dm_env.transforms.get('masks_to_polygons') + dataset = dataset.transform(masks_to_polygons) + import_dm_annotations(dataset, task_data) diff --git a/cvat/apps/dataset_manager/formats/registry.py b/cvat/apps/dataset_manager/formats/registry.py index c175a42b7728..507b497992b8 100644 --- a/cvat/apps/dataset_manager/formats/registry.py +++ b/cvat/apps/dataset_manager/formats/registry.py @@ -91,4 +91,5 @@ def make_exporter(name): import cvat.apps.dataset_manager.formats.pascal_voc import cvat.apps.dataset_manager.formats.tfrecord import cvat.apps.dataset_manager.formats.yolo -import cvat.apps.dataset_manager.formats.imagenet \ No newline at end of file +import cvat.apps.dataset_manager.formats.imagenet +import cvat.apps.dataset_manager.formats.camvid \ No newline at end of file diff --git a/cvat/apps/dataset_manager/tests/test_formats.py b/cvat/apps/dataset_manager/tests/test_formats.py index d41e253c5821..a01e60b78ae7 100644 --- a/cvat/apps/dataset_manager/tests/test_formats.py +++ b/cvat/apps/dataset_manager/tests/test_formats.py @@ -270,6 +270,7 @@ def test_export_formats_query(self): 'TFRecord 1.0', 'YOLO 1.1', 'ImageNet 1.0', + 'CamVid 1.0', }) def test_import_formats_query(self): @@ -287,6 +288,7 @@ def test_import_formats_query(self): 'TFRecord 1.0', 'YOLO 1.1', 'ImageNet 1.0', + 'CamVid 1.0', }) def test_exports(self): @@ -323,6 +325,7 @@ def test_empty_images_are_exported(self): ('TFRecord 1.0', 'tf_detection_api'), ('YOLO 1.1', 'yolo'), ('ImageNet 1.0', 'imagenet_txt'), + ('CamVid 1.0', 'camvid'), ]: with self.subTest(format=format_name): if not dm.formats.registry.EXPORT_FORMATS[format_name].ENABLED: diff --git a/cvat/apps/documentation/installation.md b/cvat/apps/documentation/installation.md index 8abd316daa1e..9b23485cd392 100644 --- a/cvat/apps/documentation/installation.md +++ b/cvat/apps/documentation/installation.md @@ -290,32 +290,7 @@ docker-compose -f docker-compose.yml -f components/analytics/docker-compose.anal ### Semi-automatic and automatic annotation -- You have to install `nuctl` command line tool to build and deploy serverless - functions. Download [the latest release](https://github.com/nuclio/nuclio/releases). -- Create `cvat` project inside nuclio dashboard where you will deploy new - serverless functions and deploy a couple of DL models. Commands below should - be run only after CVAT has been installed using docker-compose because it - runs nuclio dashboard which manages all serverless functions. - -```bash -nuctl create project cvat -``` - -```bash -nuctl deploy --project-name cvat \ - --path serverless/openvino/dextr/nuclio \ - --volume `pwd`/serverless/openvino/common:/opt/nuclio/common \ - --platform local -``` - -```bash -nuctl deploy --project-name cvat \ - --path serverless/openvino/omz/public/yolo-v3-tf/nuclio \ - --volume `pwd`/serverless/openvino/common:/opt/nuclio/common \ - --platform local -``` - -Note: see [deploy.sh](/serverless/deploy.sh) script for more examples. +Please follow [instructions](/cvat/apps/documentation/installation_automatic_annotation.md) ### Stop all containers diff --git a/cvat/apps/documentation/installation_automatic_annotation.md b/cvat/apps/documentation/installation_automatic_annotation.md new file mode 100644 index 000000000000..e3343211ffd5 --- /dev/null +++ b/cvat/apps/documentation/installation_automatic_annotation.md @@ -0,0 +1,91 @@ + +### Semi-automatic and Automatic Annotation + + +> **⚠ WARNING: Do not use `docker-compose up`** +> If you did, make sure all containers are stopped by `docker-compose down`. +- To bring up cvat with auto annotation tool, from cvat root directory, you need to run: + ```bash + docker-compose -f docker-compose.yml -f components/serverless/docker-compose.serverless.yml up -d + ``` + If you did any changes to the docker-compose files, make sure to add `--build` at the end. + + To stop the containers, simply run: + + ```bash + docker-compose -f docker-compose.yml -f components/serverless/docker-compose.serverless.yml down + ``` + +- You have to install `nuctl` command line tool to build and deploy serverless + functions. Download [version 1.5.8](https://github.com/nuclio/nuclio/releases). + It is important that the version you download matches the version in + [docker-compose.serverless.yml](/components/serverless/docker-compose.serverless.yml) + After downloading the nuclio, give it a proper permission and do a softlink + ``` + sudo chmod +x nuctl--linux-amd64 + sudo ln -sf $(pwd)/nuctl--linux-amd64 /usr/local/bin/nuctl + ``` + +- Create `cvat` project inside nuclio dashboard where you will deploy new serverless functions and deploy a couple of DL models. Commands below should be run only after CVAT has been installed using `docker-compose` because it runs nuclio dashboard which manages all serverless functions. + + ```bash + nuctl create project cvat + ``` + + ```bash + nuctl deploy --project-name cvat \ + --path serverless/openvino/dextr/nuclio \ + --volume `pwd`/serverless/openvino/common:/opt/nuclio/common \ + --platform local + ``` + + ```bash + nuctl deploy --project-name cvat \ + --path serverless/openvino/omz/public/yolo-v3-tf/nuclio \ + --volume `pwd`/serverless/openvino/common:/opt/nuclio/common \ + --platform local + ``` + **Note:** + - See [deploy_cpu.sh](/serverless/deploy_cpu.sh) for more examples. + + #### GPU Support + You will need to install Nvidia Container Toolkit and make sure your docker supports GPU. Follow [Nvidia docker instructions](https://www.tensorflow.org/install/docker#gpu_support). + Also you will need to add `--resource-limit nvidia.com/gpu=1` to the nuclio deployment command. + As an example, below will run on the GPU: + + ```bash + nuctl deploy tf-faster-rcnn-inception-v2-coco-gpu \ + --project-name cvat --path "serverless/tensorflow/faster_rcnn_inception_v2_coco/nuclio" --platform local \ + --base-image tensorflow/tensorflow:2.1.1-gpu \ + --desc "Faster RCNN from Tensorflow Object Detection GPU API" \ + --image cvat/tf.faster_rcnn_inception_v2_coco_gpu \ + --resource-limit nvidia.com/gpu=1 + ``` + + **Note:** + - Since the model is loaded during deployment, the number of GPU functions you can deploy will be limited to your GPU memory. + + - See [deploy_gpu.sh](/serverless/deploy_gpu.sh) script for more examples. + +####Debugging Nuclio Functions: + +- You can open nuclio dashboard at [localhost:8070](http://localhost:8070). Make sure status of your functions are up and running without any error. + +- To check for internal server errors, run `docker ps -a` to see the list of containers. Find the container that you are interested, e.g. `nuclio-nuclio-tf-faster-rcnn-inception-v2-coco-gpu`. Then check its logs by + + ```bash + docker logs + ``` + e.g., + + ```bash + docker logs nuclio-nuclio-tf-faster-rcnn-inception-v2-coco-gpu + ``` + +- If you would like to debug a code inside a container, you can use vscode to directly attach to a container [instructions](https://code.visualstudio.com/docs/remote/attach-container). To apply your changes, make sure to restart the container. + ```bash + docker restart + ``` + + > **⚠ WARNING:** + > Do not use nuclio dashboard to stop the container because with any modifications, it rebuilds the container and you will lose your changes. \ No newline at end of file diff --git a/cvat/apps/engine/media_extractors.py b/cvat/apps/engine/media_extractors.py index a83fb6bb5399..7ee7725eafa2 100644 --- a/cvat/apps/engine/media_extractors.py +++ b/cvat/apps/engine/media_extractors.py @@ -348,7 +348,7 @@ def _create_av_container(path, w, h, rate, options, f='mp4'): w += 1 container = av.open(path, 'w',format=f) - video_stream = container.add_stream('libx264', rate=rate) + video_stream = container.add_stream('libopenh264', rate=rate) video_stream.pix_fmt = "yuv420p" video_stream.width = w video_stream.height = h @@ -369,8 +369,10 @@ def save_as_chunk(self, images, chunk_path): h=input_h, rate=self._output_fps, options={ - "crf": str(self._image_quality), - "preset": "ultrafast", + 'profile': 'constrained_baseline', + 'qmin': str(self._image_quality), + 'qmax': str(self._image_quality), + 'rc_mode': 'buffer', }, ) @@ -419,11 +421,10 @@ def save_as_chunk(self, images, chunk_path): h=output_h, rate=self._output_fps, options={ - 'profile': 'baseline', - 'coder': '0', - 'crf': str(self._image_quality), - 'wpredp': '0', - 'flags': '-loop' + 'profile': 'constrained_baseline', + 'qmin': str(self._image_quality), + 'qmax': str(self._image_quality), + 'rc_mode': 'buffer', }, ) diff --git a/cvat/apps/engine/tests/test_rest_api.py b/cvat/apps/engine/tests/test_rest_api.py index ff938adfc25f..32b1ddbdace0 100644 --- a/cvat/apps/engine/tests/test_rest_api.py +++ b/cvat/apps/engine/tests/test_rest_api.py @@ -3751,6 +3751,10 @@ def _get_initial_annotation(annotation_format): elif annotation_format == "ImageNet 1.0": annotations["tags"] = tags_wo_attrs + elif annotation_format == "CamVid 1.0": + annotations["shapes"] = rectangle_shapes_wo_attrs \ + + polygon_shapes_wo_attrs + else: raise Exception("Unknown format {}".format(annotation_format)) @@ -3847,7 +3851,7 @@ def _get_initial_annotation(annotation_format): self.assertEqual(response.status_code, HTTP_201_CREATED) # 7. check annotation - if import_format in {"Segmentation mask 1.1", "MOTS PNG 1.0"}: + if import_format in {"Segmentation mask 1.1", "MOTS PNG 1.0", "CamVid 1.0"}: continue # can't really predict the result to check response = self._get_api_v1_tasks_id_annotations(task["id"], annotator) self.assertEqual(response.status_code, HTTP_200_OK) diff --git a/cvat/requirements/base.txt b/cvat/requirements/base.txt index 4d9dabf1e336..79329bbb729a 100644 --- a/cvat/requirements/base.txt +++ b/cvat/requirements/base.txt @@ -34,7 +34,7 @@ Shapely==1.7.1 pdf2image==1.14.0 django-rest-auth[with_social]==0.9.5 cython==0.29.21 -opencv-python==4.4.0.42 +opencv-python-headless==4.4.0.42 h5py==2.10.0 django-cors-headers==3.5.0 furl==2.1.0 @@ -44,4 +44,4 @@ tensorflow==2.2.1 # Optional requirement of Datumaro # archives. Don't use as a python module because it has GPL license. patool==1.12 diskcache==5.0.2 -git+https://github.com/openvinotoolkit/datumaro@v0.1.3 \ No newline at end of file +git+https://github.com/openvinotoolkit/datumaro@v0.1.4 diff --git a/serverless/deploy.sh b/serverless/deploy_cpu.sh similarity index 97% rename from serverless/deploy.sh rename to serverless/deploy_cpu.sh index 20face66bf55..a86149fef7ab 100755 --- a/serverless/deploy.sh +++ b/serverless/deploy_cpu.sh @@ -1,4 +1,5 @@ #!/bin/bash +# Sample commands to deploy nuclio functions on CPU SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" diff --git a/serverless/deploy_gpu.sh b/serverless/deploy_gpu.sh new file mode 100755 index 000000000000..f0b89649fc88 --- /dev/null +++ b/serverless/deploy_gpu.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Sample commands to deploy nuclio functions on GPU + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +nuctl create project cvat + +nuctl deploy --project-name cvat \ + --path "$SCRIPT_DIR/tensorflow/faster_rcnn_inception_v2_coco/nuclio" \ + --platform local --base-image tensorflow/tensorflow:2.1.1-gpu \ + --desc "Faster RCNN from Tensorflow Object Detection GPU API" \ + --image cvat/tf.faster_rcnn_inception_v2_coco_gpu \ + --resource-limit nvidia.com/gpu=1 + +nuctl get function diff --git a/serverless/tensorflow/faster_rcnn_inception_v2_coco/nuclio/model_loader.py b/serverless/tensorflow/faster_rcnn_inception_v2_coco/nuclio/model_loader.py index 8158eee31288..74aa85bcac1b 100644 --- a/serverless/tensorflow/faster_rcnn_inception_v2_coco/nuclio/model_loader.py +++ b/serverless/tensorflow/faster_rcnn_inception_v2_coco/nuclio/model_loader.py @@ -35,7 +35,8 @@ def infer(self, image): width, height = image.size if width > 1920 or height > 1080: image = image.resize((width // 2, height // 2), Image.ANTIALIAS) - image_np = np.array(image.getdata()).reshape((image.height, image.width, 3)).astype(np.uint8) + image_np = np.array(image.getdata())[:, :3].reshape( + (image.height, image.width, -1)).astype(np.uint8) image_np = np.expand_dims(image_np, axis=0) return self.session.run( diff --git a/supervisord.conf b/supervisord.conf index a991b2b9ddba..6d9b8ee2f42f 100644 --- a/supervisord.conf +++ b/supervisord.conf @@ -24,26 +24,26 @@ autorestart=true [program:rqworker_default] command=%(ENV_HOME)s/wait-for-it.sh %(ENV_CVAT_REDIS_HOST)s:6379 -t 0 -- bash -ic \ - "exec /usr/bin/python3 %(ENV_HOME)s/manage.py rqworker -v 3 default" + "exec python3 %(ENV_HOME)s/manage.py rqworker -v 3 default" environment=SSH_AUTH_SOCK="/tmp/ssh-agent.sock" numprocs=2 process_name=rqworker_default_%(process_num)s [program:rqworker_low] command=%(ENV_HOME)s/wait-for-it.sh %(ENV_CVAT_REDIS_HOST)s:6379 -t 0 -- bash -ic \ - "exec /usr/bin/python3 %(ENV_HOME)s/manage.py rqworker -v 3 low" + "exec python3 %(ENV_HOME)s/manage.py rqworker -v 3 low" environment=SSH_AUTH_SOCK="/tmp/ssh-agent.sock" numprocs=1 [program:git_status_updater] command=%(ENV_HOME)s/wait-for-it.sh %(ENV_CVAT_REDIS_HOST)s:6379 -t 0 -- bash -ic \ - "/usr/bin/python3 ~/manage.py update_git_states" + "python3 ~/manage.py update_git_states" environment=SSH_AUTH_SOCK="/tmp/ssh-agent.sock" numprocs=1 [program:rqscheduler] command=%(ENV_HOME)s/wait-for-it.sh %(ENV_CVAT_REDIS_HOST)s:6379 -t 0 -- bash -ic \ - "/usr/bin/python3 /usr/local/bin/rqscheduler --host redis -i 30" + "python3 /opt/venv/bin/rqscheduler --host %(ENV_CVAT_REDIS_HOST)s -i 30" environment=SSH_AUTH_SOCK="/tmp/ssh-agent.sock" numprocs=1 @@ -59,9 +59,9 @@ numprocs=1 ; with docker cache. Thus it is necessary to run collectstatic here for such ; apps. command=%(ENV_HOME)s/wait-for-it.sh %(ENV_CVAT_POSTGRES_HOST)s:5432 -t 0 -- bash -ic \ - "rm -f /tmp/cvat-server/httpd.pid && /usr/bin/python3 ~/manage.py migrate && \ - /usr/bin/python3 ~/manage.py collectstatic --no-input && \ - exec /usr/bin/python3 $HOME/manage.py runmodwsgi --log-to-terminal --port 8080 \ + "rm -f /tmp/cvat-server/httpd.pid && python3 ~/manage.py migrate && \ + python3 ~/manage.py collectstatic --no-input && \ + exec python3 $HOME/manage.py runmodwsgi --log-to-terminal --port 8080 \ --limit-request-body 1073741824 --log-level INFO --include-file ~/mod_wsgi.conf \ %(ENV_DJANGO_MODWSGI_EXTRA_ARGS)s --locale %(ENV_LC_ALL)s \ --server-root /tmp/cvat-server" diff --git a/tests/cypress/integration/actions_tasks_objects/case_26_canvas_brightness_contrast_saturation_feature.js b/tests/cypress/integration/actions_tasks_objects/case_26_canvas_brightness_contrast_saturation_feature.js new file mode 100644 index 000000000000..c5f641567b06 --- /dev/null +++ b/tests/cypress/integration/actions_tasks_objects/case_26_canvas_brightness_contrast_saturation_feature.js @@ -0,0 +1,62 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +import { taskName } from '../../support/const'; + +context('Canvas brightness/contrast/saturation feature', () => { + const caseId = '26'; + const countActionMoveSlider = 10; + const defaultValueInSidebar = 100; + const expectedResultInSetting = defaultValueInSidebar + countActionMoveSlider; + const classNameSliders = [ + '.cvat-player-settings-brightness', + '.cvat-player-settings-contrast', + '.cvat-player-settings-saturation', + ]; + + function generateStringCountAction(countAction) { + let stringAction = ''; + for (let i = 0; i < countAction; i++) { + stringAction += '{rightarrow}'; + }; + return stringAction; + }; + + function checkStateValuesInBackground(expectedValue) { + cy.get('#cvat_canvas_background') + .should('have.attr', 'style') + .and('contain', `filter: brightness(${expectedValue}) contrast(${expectedValue}) saturate(${expectedValue})`); + }; + + before(() => { + cy.openTaskJob(taskName); + }); + + describe(`Testing case "${caseId}"`, () => { + it('Check apply of settings', () => { + let stringAction = generateStringCountAction(countActionMoveSlider); + cy.openSettings(); + cy.get('.cvat-settings-modal').within(() => { + cy.contains('Player').click(); + cy.wrap(classNameSliders).each(($el) => { + cy.wrap($el).get($el).within(() => { + cy.get('[role=slider]') + .type(stringAction) + .should('have.attr', 'aria-valuenow', expectedResultInSetting); + }); + }); + }); + const expectedResultInBackground = (defaultValueInSidebar + countActionMoveSlider) / 100; + checkStateValuesInBackground(expectedResultInBackground); + }); + + it('Check reset of settings', () => { + cy.get('.cvat-player-reset-color-settings').click(); + const expectedResultInBackground = defaultValueInSidebar / 100; + checkStateValuesInBackground(expectedResultInBackground); + }); + }); +}); diff --git a/tests/cypress/integration/actions_tasks_objects/case_27_undo_redo_feature.js b/tests/cypress/integration/actions_tasks_objects/case_27_undo_redo_feature.js new file mode 100644 index 000000000000..704fc66c2ab9 --- /dev/null +++ b/tests/cypress/integration/actions_tasks_objects/case_27_undo_redo_feature.js @@ -0,0 +1,82 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +import { taskName, labelName } from '../../support/const'; + +context('Undo/redo feature', () => { + const caseId = '27'; + const firstRectangleShape2Points = { + points: 'By 2 Points', + type: 'Shape', + labelName: labelName, + firstX: 100, + firstY: 100, + secondX: 150, + secondY: 150, + }; + const secondRectangleShape2Points = { + points: 'By 2 Points', + type: 'Shape', + labelName: labelName, + firstX: 150, + firstY: 150, + secondX: 200, + secondY: 200, + }; + const thirdRectangleShape2Points = { + points: 'By 2 Points', + type: 'Shape', + labelName: labelName, + firstX: 200, + firstY: 200, + secondX: 250, + secondY: 250, + }; + + function checkExistObject(stateFirstObject, stateSecondObject, stateThirdObject) { + // check objects on background + cy.get('#cvat_canvas_shape_1').should(stateFirstObject); + cy.get('#cvat_canvas_shape_2').should(stateSecondObject); + cy.get('#cvat_canvas_shape_3').should(stateThirdObject); + // check objects on sidebar + cy.get('#cvat-objects-sidebar-state-item-1').should(stateFirstObject); + cy.get('#cvat-objects-sidebar-state-item-2').should(stateSecondObject); + cy.get('#cvat-objects-sidebar-state-item-3').should(stateThirdObject); + }; + + before(() => { + cy.openTaskJob(taskName); + + // create objects + cy.createRectangle(firstRectangleShape2Points); + cy.createRectangle(secondRectangleShape2Points); + cy.createRectangle(thirdRectangleShape2Points); + }); + + describe(`Testing case "${caseId}"`, () => { + it('Undo objects', () => { + cy.contains('.cvat-annotation-header-button', 'Undo').click(); + checkExistObject('exist', 'exist', 'not.exist'); + + cy.contains('.cvat-annotation-header-button', 'Undo').click(); + checkExistObject('exist', 'not.exist', 'not.exist'); + + cy.get('body').type('{ctrl}{z}'); + checkExistObject('not.exist', 'not.exist', 'not.exist'); + }); + + it('Redo objects', () => { + cy.contains('.cvat-annotation-header-button', 'Redo').click(); + checkExistObject('exist', 'not.exist', 'not.exist'); + + cy.get('body').type('{ctrl}{shift}{z}'); + checkExistObject('exist', 'exist', 'not.exist'); + + cy.get('body').type('{ctrl}{y}'); + checkExistObject('exist', 'exist', 'exist'); + }); + }); +}); diff --git a/tests/cypress/integration/actions_tasks_objects/case_29_settings_player_step.js b/tests/cypress/integration/actions_tasks_objects/case_29_settings_player_step.js new file mode 100644 index 000000000000..d8b3a10b0ae3 --- /dev/null +++ b/tests/cypress/integration/actions_tasks_objects/case_29_settings_player_step.js @@ -0,0 +1,59 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/// + +import { taskName } from '../../support/const'; + +context('Settings "Player step"', () => { + const caseId = '29'; + const countJumpStep = 3; + let startFrame; + + before(() => { + cy.openTaskJob(taskName); + }); + + describe(`Testing case "${caseId}"`, () => { + it('Change player step ', () => { + cy.openSettings(); + cy.get('.cvat-settings-modal').within(() => { + cy.contains('Player').click(); + cy.get('.cvat-player-settings-step').within(() => { + cy.get('[role="spinbutton"]').clear().type(countJumpStep); + }); + }); + cy.closeSettings(); + + // get and save start frame + cy.get('.cvat-player-frame-selector').within(() => { + cy.get('[role="spinbutton"]') + .should('have.attr', 'aria-valuenow') + .then((valueFrameNow) => { + startFrame = Number(valueFrameNow); + }); + }); + }); + + it('Jump to forward frame via GUI', () => { + cy.get('.cvat-player-forward-button').click(); + cy.checkFrameNum(startFrame + countJumpStep); + }); + + it('Jump to backward frame via GUI', () => { + cy.get('.cvat-player-backward-button').click(); + cy.checkFrameNum(startFrame); + }); + + it('Jump to forward frame via shortcuts', () => { + cy.get('body').type('{v}'); + cy.checkFrameNum(startFrame + countJumpStep); + }); + + it('Jump to backward frame via shortcuts', () => { + cy.get('body').type('{c}'); + cy.checkFrameNum(startFrame); + }); + }); +}); diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index ce086227a30f..c019f19571c6 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -255,14 +255,15 @@ Cypress.Commands.add('createPolygon', (createPolygonParams) => { Cypress.Commands.add('openSettings', () => { cy.get('.cvat-right-header').find('.cvat-header-menu-dropdown').trigger('mouseover', { which: 1 }); cy.get('.anticon-setting').click(); + cy.get('.cvat-settings-modal').should('be.visible'); }); Cypress.Commands.add('closeSettings', () => { - cy.get('.ant-modal-content') - .should('contain', 'Settings') + cy.get('.cvat-settings-modal') .within(() => { cy.contains('button', 'Close').click(); }); + cy.get('.cvat-settings-modal').should('not.be.visible'); }); Cypress.Commands.add('changeWorkspace', (mode, labelName) => {