From 723b2a8327171d8c1a93577f90d0b96d5bbbaffa Mon Sep 17 00:00:00 2001 From: Timur Osmanov <54434686+TOsmanov@users.noreply.github.com> Date: Mon, 22 Nov 2021 09:53:16 +0300 Subject: [PATCH 1/6] update docs rotated bbox (#3924) --- .../annotation-with-rectangle-by-4-points.md | 18 ---------- .../advanced/annotation-with-rectangles.md | 34 ++++++++++++++++++ .../en/docs/manual/basics/controls-sidebar.md | 2 +- .../docs/manual/basics/shape-mode-basics.md | 2 +- site/content/en/images/image230.jpg | Bin 0 -> 25538 bytes 5 files changed, 36 insertions(+), 20 deletions(-) delete mode 100644 site/content/en/docs/manual/advanced/annotation-with-rectangle-by-4-points.md create mode 100644 site/content/en/docs/manual/advanced/annotation-with-rectangles.md create mode 100644 site/content/en/images/image230.jpg diff --git a/site/content/en/docs/manual/advanced/annotation-with-rectangle-by-4-points.md b/site/content/en/docs/manual/advanced/annotation-with-rectangle-by-4-points.md deleted file mode 100644 index 5be367ede991..000000000000 --- a/site/content/en/docs/manual/advanced/annotation-with-rectangle-by-4-points.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: 'Annotation with rectangle by 4 points' -linkTitle: 'Annotation with rectangle by 4 points' -weight: 7 ---- - -It is an efficient method of bounding box annotation, proposed -[here](https://arxiv.org/pdf/1708.02750.pdf). -Before starting, you need to make sure that the drawing method by 4 points is selected. - -![](/images/image134.jpg) - -Press `Shape` or `Track` for entering drawing mode. Click on four extreme points: -the top, bottom, left- and right-most physical points on the object. -Drawing will be automatically completed right after clicking the fourth point. -Press `Esc` to cancel editing. - -![](/images/gif016_mapillary_vistas.gif) diff --git a/site/content/en/docs/manual/advanced/annotation-with-rectangles.md b/site/content/en/docs/manual/advanced/annotation-with-rectangles.md new file mode 100644 index 000000000000..01fa88c7b1c3 --- /dev/null +++ b/site/content/en/docs/manual/advanced/annotation-with-rectangles.md @@ -0,0 +1,34 @@ +--- +title: 'Annotation with rectangles' +linkTitle: 'Annotation with rectangles' +weight: 7 +--- + +To learn more about annotation using a rectangle, see the sections: +- [Shape mode (basics)](/docs/manual/basics/shape-mode-basics/) +- [Track mode (basics)](/docs/manual/basics/track-mode-basics/) +- [Shape mode (advanced)](/docs/manual/advanced/shape-mode-advanced/) +- [Track mode (advanced)](/docs/manual/advanced/track-mode-advanced/) + +## Rotation rectangle + +To rotate the rectangle, pull on the `rotation point`. Rotation is done around the center of the rectangle. +To rotate at a fixed angle (multiple of 15 degrees), +hold `shift`. In the process of rotation, you can see the angle of rotation. + +![](/images/image230.jpg) + +## Annotation with rectangle by 4 points + +It is an efficient method of bounding box annotation, proposed +[here](https://arxiv.org/pdf/1708.02750.pdf). +Before starting, you need to make sure that the drawing method by 4 points is selected. + +![](/images/image134.jpg) + +Press `Shape` or `Track` for entering drawing mode. Click on four extreme points: +the top, bottom, left- and right-most physical points on the object. +Drawing will be automatically completed right after clicking the fourth point. +Press `Esc` to cancel editing. + +![](/images/gif016_mapillary_vistas.gif) diff --git a/site/content/en/docs/manual/basics/controls-sidebar.md b/site/content/en/docs/manual/basics/controls-sidebar.md index dc696e4e19b6..1a25dcf58851 100644 --- a/site/content/en/docs/manual/basics/controls-sidebar.md +++ b/site/content/en/docs/manual/basics/controls-sidebar.md @@ -33,7 +33,7 @@ description: 'Overview of available functions on the controls sidebar of the ann |-- |-- |-- | |![](/images/image189.jpg)|`AI Tools`|[AI Tools](/docs/manual/advanced/ai-tools/)| |![](/images/image201.jpg)|`OpenCV`|[OpenCV](/docs/manual/advanced/opencv-tools/)| -|![](/images/image167.jpg)|`Rectangle`|[Shape mode](/docs/manual/basics/shape-mode-basics/); [Track mode](/docs/manual/basics/track-mode-basics/);
[Drawing by 4 points](/docs/manual/advanced/annotation-with-rectangle-by-4-points/)| +|![](/images/image167.jpg)|`Rectangle`|[Shape mode](/docs/manual/basics/shape-mode-basics/); [Track mode](/docs/manual/basics/track-mode-basics/);
[Drawing by 4 points](/docs/manual/advanced/annotation-with-rectangles/)| |![](/images/image168.jpg)|`Polygon`|[Annotation with polygons](/docs/manual/advanced/annotation-with-polygons/); [Track mode with polygons](/docs/manual/advanced/annotation-with-polygons/track-mode-with-polygons/)| |![](/images/image169.jpg)|`Polyline`|[Annotation with polylines](/docs/manual/advanced/annotation-with-polylines/)| |![](/images/image170.jpg)|`Points`|[Annotation with points](/docs/manual/advanced/annotation-with-points/)| diff --git a/site/content/en/docs/manual/basics/shape-mode-basics.md b/site/content/en/docs/manual/basics/shape-mode-basics.md index 8001a3844835..4450bf89717d 100644 --- a/site/content/en/docs/manual/basics/shape-mode-basics.md +++ b/site/content/en/docs/manual/basics/shape-mode-basics.md @@ -28,7 +28,7 @@ Usage examples: ![](/images/image011_detrac.jpg) - - To learn about creating a rectangle using the by 4 point drawing method, ([read here](/docs/manual/advanced/annotation-with-rectangle-by-4-points/)). + - To learn more about creating a rectangle [read here](/docs/manual/advanced/annotation-with-rectangles/). - It is possible to adjust boundaries and location of the rectangle using a mouse. Rectangle's size is shown in the top right corner , you can check it by clicking on any point of the shape. diff --git a/site/content/en/images/image230.jpg b/site/content/en/images/image230.jpg new file mode 100644 index 0000000000000000000000000000000000000000..af3c529773940e190fc9255a8f77db695045a3e2 GIT binary patch literal 25538 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 cgNY?!9k&OOBk(oqT+3+N%hJ73$`n`s* Date: Tue, 23 Nov 2021 10:34:03 +0300 Subject: [PATCH 2/6] Export rotation field when backup a task (#3932) --- cvat/apps/engine/backup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cvat/apps/engine/backup.py b/cvat/apps/engine/backup.py index da42cab6b305..3d340b815a5b 100644 --- a/cvat/apps/engine/backup.py +++ b/cvat/apps/engine/backup.py @@ -106,6 +106,7 @@ def _prepare_annotations(self, annotations, label_mapping): 'outside', 'z_order', 'points', + 'rotation', 'frame', 'group', 'source', From 1f33f505efebf42d91fbfa8b8443fa37f648b07a Mon Sep 17 00:00:00 2001 From: Eric Hofesmann Date: Tue, 23 Nov 2021 15:15:14 -0500 Subject: [PATCH 3/6] Add FiftyOne to partners list (#3943) * Add FiftyOne to partners list * remove screenshot * Fix linter issues Co-authored-by: Nikita Manovich --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 2b430cab55e6..29d59df4fb4d 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,10 @@ connection with your use of FFmpeg. - [Human Protocol](https://hmt.ai) uses CVAT as a way of adding annotation service to the human protocol. - [Cogito Tech LLC](https://bit.ly/3klT0h6), a Human-in-the-Loop Workforce Solutions Provider, used CVAT in annotation of about 5,000 images for a brand operating in the fashion segment. +- [FiftyOne](https://fiftyone.ai) is an open-source dataset curation and model analysis +tool for visualizing, exploring, and improving computer vision datasets and models that is +[tightly integrated](https://voxel51.com/docs/fiftyone/integrations/cvat.html) with CVAT +for annotation and label refinement. ## Questions From f36735c71c6729ca13c591aae1f0a055c980c3b7 Mon Sep 17 00:00:00 2001 From: Michael Lynch Date: Tue, 30 Nov 2021 13:52:59 +1100 Subject: [PATCH 4/6] Initial commit of Weed AI custom annotation workspace --- cvat-ui/package-lock.json | 16 +- .../annotation-page/annotation-page.tsx | 6 + .../weed-ai-annotation-workspace/styles.scss | 297 ++++++++++++++++++ .../weed-ai-annotation-workspace.tsx | 30 ++ cvat-ui/src/reducers/interfaces.ts | 1 + package-lock.json | 6 +- 6 files changed, 344 insertions(+), 12 deletions(-) create mode 100644 cvat-ui/src/components/annotation-page/weed-ai-annotation-workspace/styles.scss create mode 100644 cvat-ui/src/components/annotation-page/weed-ai-annotation-workspace/weed-ai-annotation-workspace.tsx diff --git a/cvat-ui/package-lock.json b/cvat-ui/package-lock.json index f90e12606495..07187455c154 100644 --- a/cvat-ui/package-lock.json +++ b/cvat-ui/package-lock.json @@ -35,7 +35,7 @@ "platform": "^1.3.6", "prop-types": "^15.7.2", "react": "^16.14.0", - "react-awesome-query-builder": "^4.4.2", + "react-awesome-query-builder": "^4.5.1", "react-color": "^2.19.3", "react-cookie": "^4.0.3", "react-dom": "^16.14.0", @@ -53,7 +53,7 @@ "devDependencies": {} }, "../cvat-canvas": { - "version": "2.8.0", + "version": "2.9.0", "license": "MIT", "dependencies": { "svg.draggable.js": "2.2.2", @@ -75,13 +75,13 @@ "devDependencies": {} }, "../cvat-core": { - "version": "3.16.1", + "version": "3.19.0", "license": "MIT", "dependencies": { "axios": "^0.21.4", "browser-or-node": "^1.2.1", "cvat-data": "../cvat-data", - "detect-browser": "^5.2.0", + "detect-browser": "^5.2.1", "error-stack-parser": "^2.0.2", "form-data": "^2.5.0", "jest-config": "^26.6.3", @@ -89,8 +89,7 @@ "json-logic-js": "^2.0.1", "platform": "^1.3.5", "quickhull": "^1.0.3", - "store": "^2.0.12", - "worker-loader": "^2.0.0" + "store": "^2.0.12" }, "devDependencies": { "coveralls": "^3.0.5", @@ -7931,7 +7930,7 @@ "browser-or-node": "^1.2.1", "coveralls": "^3.0.5", "cvat-data": "../cvat-data", - "detect-browser": "^5.2.0", + "detect-browser": "^5.2.1", "error-stack-parser": "^2.0.2", "form-data": "^2.5.0", "jest": "^26.6.3", @@ -7942,8 +7941,7 @@ "json-logic-js": "^2.0.1", "platform": "^1.3.5", "quickhull": "^1.0.3", - "store": "^2.0.12", - "worker-loader": "^2.0.0" + "store": "^2.0.12" } }, "cyclist": { diff --git a/cvat-ui/src/components/annotation-page/annotation-page.tsx b/cvat-ui/src/components/annotation-page/annotation-page.tsx index 973e8cebe8cd..66c7c4615bee 100644 --- a/cvat-ui/src/components/annotation-page/annotation-page.tsx +++ b/cvat-ui/src/components/annotation-page/annotation-page.tsx @@ -14,6 +14,7 @@ import SubmitAnnotationsModal from 'components/annotation-page/request-review-mo import ReviewAnnotationsWorkspace from 'components/annotation-page/review-workspace/review-workspace'; import SubmitReviewModal from 'components/annotation-page/review/submit-review-modal'; import StandardWorkspaceComponent from 'components/annotation-page/standard-workspace/standard-workspace'; +import WeedAIAnnotationWorkspaceComponent from 'components/annotation-page/weed-ai-annotation-workspace/weed-ai-annotation-workspace'; import StandardWorkspace3DComponent from 'components/annotation-page/standard3D-workspace/standard3D-workspace'; import TagAnnotationWorkspace from 'components/annotation-page/tag-annotation-workspace/tag-annotation-workspace'; import FiltersModalComponent from 'components/annotation-page/top-bar/filters-modal'; @@ -121,6 +122,11 @@ export default function AnnotationPageComponent(props: Props): JSX.Element { )} + {workspace === Workspace.WEED_AI_ANNOTATION && ( + + + + )} {workspace === Workspace.TAG_ANNOTATION && ( diff --git a/cvat-ui/src/components/annotation-page/weed-ai-annotation-workspace/styles.scss b/cvat-ui/src/components/annotation-page/weed-ai-annotation-workspace/styles.scss new file mode 100644 index 000000000000..ac11a6b5bfcd --- /dev/null +++ b/cvat-ui/src/components/annotation-page/weed-ai-annotation-workspace/styles.scss @@ -0,0 +1,297 @@ +// Copyright (C) 2020-2021 Intel Corporation +// +// SPDX-License-Identifier: MIT + +@import 'base.scss'; + +.cvat-weed-ai-annotation-workspace.ant-layout { + height: 100%; +} + +.cvat-context-image-wrapper { + height: auto; + width: $grid-unit-size * 32; + position: absolute; + top: $grid-unit-size; + right: $grid-unit-size; + z-index: 100; + background: black; + display: flex; + flex-direction: column; + justify-content: space-between; + user-select: none; + + > .cvat-context-image-wrapper-header { + height: $grid-unit-size * 4; + width: 100%; + z-index: 101; + background: rgba(0, 0, 0, 0.2); + position: absolute; + top: 0; + left: 0; + } + + > .ant-image { + margin: $grid-unit-size * 0.5; + } + + > span { + position: absolute; + font-size: 18px; + top: 7px; + right: 7px; + z-index: 102; + color: white; + + &:hover { + > svg { + transform: scale(1.2); + } + } + } +} + +.cvat-context-image { + width: 100%; + height: auto; + display: block; +} + +.cvat-objects-sidebar-sider { + top: 0; + right: 0; + left: auto; + background-color: $background-color-2; + border-left: 1px solid $border-color-1; + border-bottom: 1px solid $border-color-1; + border-radius: 4px 0 0 4px; + z-index: 2; +} + +.cvat-objects-sidebar { + height: 100%; + overflow-y: auto; + overflow-x: hidden; + + > .ant-layout-sider-children { + display: flex; + flex-direction: column; + + > .cvat-objects-sidebar-tabs { + flex-grow: 10; + + > div { + display: flex; + + div[role='tabpanel'] { + height: 100%; + } + } + } + } + + &.ant-layout-sider-collapsed { + overflow: initial; + } +} + +.cvat-rotate-canvas-controls-right > svg { + transform: scaleX(-1); +} + +.cvat-canvas-controls-sidebar { + background-color: $background-color-2; + border-right: 1px solid $border-color-1; + overflow: hidden; +} + +.cvat-cursor-control, +.cvat-move-control, +.cvat-rotate-canvas-control, +.cvat-fit-control, +.cvat-resize-control, +.cvat-draw-rectangle-control, +.cvat-draw-polygon-control, +.cvat-draw-polyline-control, +.cvat-draw-points-control, +.cvat-draw-cuboid-control, +.cvat-setup-tag-control, +.cvat-merge-control, +.cvat-group-control, +.cvat-split-track-control, +.cvat-issue-control, +.cvat-tools-control, +.cvat-extra-controls-control, +.cvat-opencv-control { + border-radius: 3.3px; + transform: scale(0.65); + padding: 2px; + + &:hover:not(.cvat-disabled-canvas-control) { + background: $header-color; + transform: scale(0.75); + } + + &:active:not(.cvat-disabled-canvas-control) { + transform: scale(0.65); + } + + > svg { + transform: scale(0.8); + } +} + +.cvat-antd-icon-control { + > svg { + width: 40px; + height: 40px; + } +} + +.cvat-active-canvas-control { + background: $header-color; + transform: scale(0.75); +} + +.cvat-disabled-canvas-control > svg { + filter: opacity(0.45); +} + +.cvat-rotate-canvas-controls-left, +.cvat-rotate-canvas-controls-right { + transform: scale(0.65); + border-radius: 5px; + + &:hover { + transform: scale(0.75); + } + + &:active { + transform: scale(0.65); + } +} + +.cvat-rotate-canvas-popover { + .ant-popover-inner-content { + padding: 0; + } +} + +.cvat-draw-shape-popover, +.cvat-opencv-control-popover, +.cvat-setup-tag-popover, +.cvat-tools-control-popover { + .ant-popover-inner-content { + padding: 0; + } +} + +.cvat-tools-track-button, +.cvat-tools-interact-button { + width: 100%; + margin-top: 10px; +} + +.cvat-interactors-tips-icon-container { + text-align: center; + font-size: 20px; +} + +.cvat-interactor-tip-container { + background: $background-color-2; + padding: $grid-unit-size; + box-shadow: $box-shadow-base; + width: $grid-unit-size * 40; + text-align: center; + border-radius: 4px; +} + +.cvat-interactor-tip-image { + width: $grid-unit-size * 37; +} + +.cvat-draw-shape-popover-points-selector { + width: 100%; +} + +.cvat-tools-control-popover-content, +.cvat-opencv-control-popover-content { + width: fit-content; + padding: $grid-unit-size; + border-radius: $grid-unit-size; + background: $background-color-2; + + .ant-tabs-tab { + width: $grid-unit-size * 14; + justify-content: center; + } +} + +.cvat-opencv-initialization-button { + width: 100%; + margin: $grid-unit-size 0; +} + +.cvat-opencv-drawing-tools { + margin-top: $grid-unit-size; +} + +.cvat-opencv-drawing-tool { + padding: $grid-unit-size; + width: $grid-unit-size * 5; + height: $grid-unit-size * 5; + + > i { + font-size: 16px; + } +} + +.cvat-opencv-image-tool { + @extend .cvat-opencv-drawing-tool; +} + +.cvat-opencv-image-tool-active { + color: #40a9ff; + border-color: #40a9ff; +} + +.cvat-setup-tag-popover-content, +.cvat-draw-shape-popover-content { + padding: $grid-unit-size; + border-radius: $grid-unit-size; + background: $background-color-2; + width: 34 * $grid-unit-size; + + > div { + margin-top: $grid-unit-size; + } + + > div:nth-child(3) > div > div { + width: 100%; + } + + > div:last-child { + span { + width: 100%; + } + + button { + width: 100%; + + &:nth-child(1) { + border-radius: 3px 0 0 3px; + } + + &:nth-child(2) { + border-radius: 0 3px 3px 0; + } + } + } +} + +.cvat-propagate-confirm { + > .ant-input-number { + width: 70px; + margin: 0 5px; + } +} diff --git a/cvat-ui/src/components/annotation-page/weed-ai-annotation-workspace/weed-ai-annotation-workspace.tsx b/cvat-ui/src/components/annotation-page/weed-ai-annotation-workspace/weed-ai-annotation-workspace.tsx new file mode 100644 index 000000000000..d356aaa5a24a --- /dev/null +++ b/cvat-ui/src/components/annotation-page/weed-ai-annotation-workspace/weed-ai-annotation-workspace.tsx @@ -0,0 +1,30 @@ +// Copyright (C) 2021 Intel Corporation +// +// SPDX-License-Identifier: MIT + +import './styles.scss'; +import React from 'react'; +import Layout from 'antd/lib/layout'; + +import CanvasWrapperContainer from 'containers/annotation-page/canvas/canvas-wrapper'; +import ControlsSideBarContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/controls-side-bar'; +import PropagateConfirmContainer from 'containers/annotation-page/standard-workspace/propagate-confirm'; +import CanvasContextMenuContainer from 'containers/annotation-page/canvas/canvas-context-menu'; +import ObjectsListContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/objects-list'; +import ObjectSideBarComponent from 'components/annotation-page/standard-workspace/objects-side-bar/objects-side-bar'; +import CanvasPointContextMenuComponent from 'components/annotation-page/canvas/canvas-point-context-menu'; +import IssueAggregatorComponent from 'components/annotation-page/review/issues-aggregator'; + +export default function WeedAIAnnotationWorkspaceComponent(): JSX.Element { + return ( + + + + } /> + + + + + + ); +} diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index 00b1d3bc4674..cc1e21e145a6 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -584,6 +584,7 @@ export enum Workspace { STANDARD3D = 'Standard 3D', STANDARD = 'Standard', ATTRIBUTE_ANNOTATION = 'Attribute annotation', + WEED_AI_ANNOTATION = 'Weed AI annotation', TAG_ANNOTATION = 'Tag annotation', REVIEW_WORKSPACE = 'Review', } diff --git a/package-lock.json b/package-lock.json index 9ce7c5a9c0ad..24a371f0d250 100644 --- a/package-lock.json +++ b/package-lock.json @@ -91,7 +91,7 @@ } }, "cvat-canvas": { - "version": "2.8.0", + "version": "2.9.0", "license": "MIT", "dependencies": { "svg.draggable.js": "2.2.2", @@ -113,7 +113,7 @@ "devDependencies": {} }, "cvat-core": { - "version": "3.17.0", + "version": "3.19.0", "license": "MIT", "dependencies": { "axios": "^0.21.4", @@ -251,7 +251,7 @@ "devDependencies": {} }, "cvat-ui": { - "version": "1.25.0", + "version": "1.26.0", "license": "MIT", "dependencies": { "@ant-design/icons": "^4.6.3", From e87df649b050c1cbadd092b8844e6a9d3b2b9017 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Wed, 1 Dec 2021 14:31:47 +1100 Subject: [PATCH 5/6] Made a new workspace, weed-ai-workspace, as a proof-of-concept for a workspace with a reduced set of annotation tools. Weed-ai-workspace doesn't have the cuboid. --- .../annotation-page/annotation-page.tsx | 6 +- .../controls-side-bar/controls-side-bar.tsx | 301 ++++++++++++++++++ .../styles.scss | 2 +- .../weed-ai-workspace.tsx} | 6 +- .../controls-side-bar/controls-side-bar.tsx | 96 ++++++ cvat-ui/src/reducers/interfaces.ts | 2 +- 6 files changed, 405 insertions(+), 8 deletions(-) create mode 100644 cvat-ui/src/components/annotation-page/weed-ai-workspace/controls-side-bar/controls-side-bar.tsx rename cvat-ui/src/components/annotation-page/{weed-ai-annotation-workspace => weed-ai-workspace}/styles.scss (99%) rename cvat-ui/src/components/annotation-page/{weed-ai-annotation-workspace/weed-ai-annotation-workspace.tsx => weed-ai-workspace/weed-ai-workspace.tsx} (86%) create mode 100644 cvat-ui/src/containers/annotation-page/weed-ai-workspace/controls-side-bar/controls-side-bar.tsx diff --git a/cvat-ui/src/components/annotation-page/annotation-page.tsx b/cvat-ui/src/components/annotation-page/annotation-page.tsx index 66c7c4615bee..4fc40481d07b 100644 --- a/cvat-ui/src/components/annotation-page/annotation-page.tsx +++ b/cvat-ui/src/components/annotation-page/annotation-page.tsx @@ -14,7 +14,7 @@ import SubmitAnnotationsModal from 'components/annotation-page/request-review-mo import ReviewAnnotationsWorkspace from 'components/annotation-page/review-workspace/review-workspace'; import SubmitReviewModal from 'components/annotation-page/review/submit-review-modal'; import StandardWorkspaceComponent from 'components/annotation-page/standard-workspace/standard-workspace'; -import WeedAIAnnotationWorkspaceComponent from 'components/annotation-page/weed-ai-annotation-workspace/weed-ai-annotation-workspace'; +import WeedAIWorkspaceComponent from 'components/annotation-page/weed-ai-workspace/weed-ai-workspace'; import StandardWorkspace3DComponent from 'components/annotation-page/standard3D-workspace/standard3D-workspace'; import TagAnnotationWorkspace from 'components/annotation-page/tag-annotation-workspace/tag-annotation-workspace'; import FiltersModalComponent from 'components/annotation-page/top-bar/filters-modal'; @@ -122,9 +122,9 @@ export default function AnnotationPageComponent(props: Props): JSX.Element { )} - {workspace === Workspace.WEED_AI_ANNOTATION && ( + {workspace === Workspace.WEED_AI_WORKSPACE && ( - + )} {workspace === Workspace.TAG_ANNOTATION && ( diff --git a/cvat-ui/src/components/annotation-page/weed-ai-workspace/controls-side-bar/controls-side-bar.tsx b/cvat-ui/src/components/annotation-page/weed-ai-workspace/controls-side-bar/controls-side-bar.tsx new file mode 100644 index 000000000000..5997fea08639 --- /dev/null +++ b/cvat-ui/src/components/annotation-page/weed-ai-workspace/controls-side-bar/controls-side-bar.tsx @@ -0,0 +1,301 @@ +// Copyright (C) 2020-2021 Intel Corporation +// +// SPDX-License-Identifier: MIT + +import React from 'react'; +import Layout from 'antd/lib/layout'; + +import { ActiveControl, Rotation } from 'reducers/interfaces'; +import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react'; +import { Canvas } from 'cvat-canvas-wrapper'; + +import ControlVisibilityObserver, { + ExtraControlsControl, +} from '../../standard-workspace/controls-side-bar/control-visibility-observer'; +import RotateControl, { + Props as RotateControlProps, +} from '../../standard-workspace/controls-side-bar/rotate-control'; +import CursorControl, { + Props as CursorControlProps, +} from '../../standard-workspace/controls-side-bar/cursor-control'; +import MoveControl, { + Props as MoveControlProps, +} from '../../standard-workspace/controls-side-bar/move-control'; +import FitControl, { + Props as FitControlProps, +} from '../../standard-workspace/controls-side-bar/fit-control'; +import ResizeControl, { + Props as ResizeControlProps, +} from '../../standard-workspace/controls-side-bar/resize-control'; +import ToolsControl from '../../standard-workspace/controls-side-bar/tools-control'; +import OpenCVControl from '../../standard-workspace/controls-side-bar/opencv-control'; +import DrawRectangleControl, { + Props as DrawRectangleControlProps, +} from '../../standard-workspace/controls-side-bar/draw-rectangle-control'; +import DrawPolygonControl, { + Props as DrawPolygonControlProps, +} from '../../standard-workspace/controls-side-bar/draw-polygon-control'; +import DrawPolylineControl, { + Props as DrawPolylineControlProps, +} from '../../standard-workspace/controls-side-bar/draw-polyline-control'; +import DrawPointsControl, { + Props as DrawPointsControlProps, +} from '../../standard-workspace/controls-side-bar/draw-points-control'; +import SetupTagControl, { + Props as SetupTagControlProps, +} from '../../standard-workspace/controls-side-bar/setup-tag-control'; +import MergeControl, { + Props as MergeControlProps, +} from '../../standard-workspace/controls-side-bar/merge-control'; +import GroupControl, { + Props as GroupControlProps, +} from '../../standard-workspace/controls-side-bar/group-control'; +import SplitControl, { + Props as SplitControlProps, +} from '../../standard-workspace/controls-side-bar/split-control'; + +interface Props { + canvasInstance: Canvas; + activeControl: ActiveControl; + keyMap: KeyMap; + normalizedKeyMap: Record; + labels: any[]; + + mergeObjects(enabled: boolean): void; + groupObjects(enabled: boolean): void; + splitTrack(enabled: boolean): void; + rotateFrame(rotation: Rotation): void; + repeatDrawShape(): void; + pasteShape(): void; + resetGroup(): void; + redrawShape(): void; +} + +// We use the observer to see if these controls are in the viewport +// They automatically put to extra if not +const ObservedCursorControl = ControlVisibilityObserver(CursorControl); +const ObservedMoveControl = ControlVisibilityObserver(MoveControl); +const ObservedRotateControl = ControlVisibilityObserver(RotateControl); +const ObservedFitControl = ControlVisibilityObserver(FitControl); +const ObservedResizeControl = ControlVisibilityObserver(ResizeControl); +const ObservedToolsControl = ControlVisibilityObserver(ToolsControl); +const ObservedOpenCVControl = ControlVisibilityObserver(OpenCVControl); +const ObservedDrawRectangleControl = ControlVisibilityObserver(DrawRectangleControl); +const ObservedDrawPolygonControl = ControlVisibilityObserver(DrawPolygonControl); +const ObservedDrawPolylineControl = ControlVisibilityObserver(DrawPolylineControl); +const ObservedDrawPointsControl = ControlVisibilityObserver(DrawPointsControl); +// const ObservedDrawCuboidControl = ControlVisibilityObserver(DrawCuboidControl); +const ObservedSetupTagControl = ControlVisibilityObserver(SetupTagControl); +const ObservedMergeControl = ControlVisibilityObserver(MergeControl); +const ObservedGroupControl = ControlVisibilityObserver(GroupControl); +const ObservedSplitControl = ControlVisibilityObserver(SplitControl); + +export default function ControlsSideBarComponent(props: Props): JSX.Element { + const { + activeControl, + canvasInstance, + normalizedKeyMap, + keyMap, + labels, + mergeObjects, + groupObjects, + splitTrack, + rotateFrame, + repeatDrawShape, + pasteShape, + resetGroup, + redrawShape, + } = props; + + const preventDefault = (event: KeyboardEvent | undefined): void => { + if (event) { + event.preventDefault(); + } + }; + + let subKeyMap: any = { + CANCEL: keyMap.CANCEL, + CLOCKWISE_ROTATION: keyMap.CLOCKWISE_ROTATION, + ANTICLOCKWISE_ROTATION: keyMap.ANTICLOCKWISE_ROTATION, + }; + + let handlers: any = { + CANCEL: (event: KeyboardEvent | undefined) => { + preventDefault(event); + if (activeControl !== ActiveControl.CURSOR) { + canvasInstance.cancel(); + } + }, + CLOCKWISE_ROTATION: (event: KeyboardEvent | undefined) => { + preventDefault(event); + rotateFrame(Rotation.CLOCKWISE90); + }, + ANTICLOCKWISE_ROTATION: (event: KeyboardEvent | undefined) => { + preventDefault(event); + rotateFrame(Rotation.ANTICLOCKWISE90); + }, + }; + + if (labels.length) { + handlers = { + ...handlers, + PASTE_SHAPE: (event: KeyboardEvent | undefined) => { + preventDefault(event); + canvasInstance.cancel(); + pasteShape(); + }, + SWITCH_DRAW_MODE: (event: KeyboardEvent | undefined) => { + preventDefault(event); + const drawing = [ + ActiveControl.DRAW_POINTS, + ActiveControl.DRAW_POLYGON, + ActiveControl.DRAW_POLYLINE, + ActiveControl.DRAW_RECTANGLE, + ActiveControl.DRAW_CUBOID, + ActiveControl.AI_TOOLS, + ActiveControl.OPENCV_TOOLS, + ].includes(activeControl); + + if (!drawing) { + canvasInstance.cancel(); + // repeateDrawShapes gets all the latest parameters + // and calls canvasInstance.draw() with them + + if (event && event.shiftKey) { + redrawShape(); + } else { + repeatDrawShape(); + } + } else { + if ([ActiveControl.AI_TOOLS, ActiveControl.OPENCV_TOOLS].includes(activeControl)) { + // separated API method + canvasInstance.interact({ enabled: false }); + return; + } + + canvasInstance.draw({ enabled: false }); + } + }, + SWITCH_MERGE_MODE: (event: KeyboardEvent | undefined) => { + preventDefault(event); + const merging = activeControl === ActiveControl.MERGE; + if (!merging) { + canvasInstance.cancel(); + } + canvasInstance.merge({ enabled: !merging }); + mergeObjects(!merging); + }, + SWITCH_SPLIT_MODE: (event: KeyboardEvent | undefined) => { + preventDefault(event); + const splitting = activeControl === ActiveControl.SPLIT; + if (!splitting) { + canvasInstance.cancel(); + } + canvasInstance.split({ enabled: !splitting }); + splitTrack(!splitting); + }, + SWITCH_GROUP_MODE: (event: KeyboardEvent | undefined) => { + preventDefault(event); + const grouping = activeControl === ActiveControl.GROUP; + if (!grouping) { + canvasInstance.cancel(); + } + canvasInstance.group({ enabled: !grouping }); + groupObjects(!grouping); + }, + RESET_GROUP: (event: KeyboardEvent | undefined) => { + preventDefault(event); + const grouping = activeControl === ActiveControl.GROUP; + if (!grouping) { + return; + } + resetGroup(); + canvasInstance.group({ enabled: false }); + groupObjects(false); + }, + }; + subKeyMap = { + ...subKeyMap, + PASTE_SHAPE: keyMap.PASTE_SHAPE, + SWITCH_DRAW_MODE: keyMap.SWITCH_DRAW_MODE, + SWITCH_MERGE_MODE: keyMap.SWITCH_MERGE_MODE, + SWITCH_SPLIT_MODE: keyMap.SWITCH_SPLIT_MODE, + SWITCH_GROUP_MODE: keyMap.SWITCH_GROUP_MODE, + RESET_GROUP: keyMap.RESET_GROUP, + }; + } + + return ( + + + + + + +
+ + + + +
+ + + + + + + + +
+ + + + + + +
+ ); +} diff --git a/cvat-ui/src/components/annotation-page/weed-ai-annotation-workspace/styles.scss b/cvat-ui/src/components/annotation-page/weed-ai-workspace/styles.scss similarity index 99% rename from cvat-ui/src/components/annotation-page/weed-ai-annotation-workspace/styles.scss rename to cvat-ui/src/components/annotation-page/weed-ai-workspace/styles.scss index ac11a6b5bfcd..5c4eb383e7ed 100644 --- a/cvat-ui/src/components/annotation-page/weed-ai-annotation-workspace/styles.scss +++ b/cvat-ui/src/components/annotation-page/weed-ai-workspace/styles.scss @@ -4,7 +4,7 @@ @import 'base.scss'; -.cvat-weed-ai-annotation-workspace.ant-layout { +.cvat-weed-ai-workspace.ant-layout { height: 100%; } diff --git a/cvat-ui/src/components/annotation-page/weed-ai-annotation-workspace/weed-ai-annotation-workspace.tsx b/cvat-ui/src/components/annotation-page/weed-ai-workspace/weed-ai-workspace.tsx similarity index 86% rename from cvat-ui/src/components/annotation-page/weed-ai-annotation-workspace/weed-ai-annotation-workspace.tsx rename to cvat-ui/src/components/annotation-page/weed-ai-workspace/weed-ai-workspace.tsx index d356aaa5a24a..21551890fc61 100644 --- a/cvat-ui/src/components/annotation-page/weed-ai-annotation-workspace/weed-ai-annotation-workspace.tsx +++ b/cvat-ui/src/components/annotation-page/weed-ai-workspace/weed-ai-workspace.tsx @@ -7,7 +7,7 @@ import React from 'react'; import Layout from 'antd/lib/layout'; import CanvasWrapperContainer from 'containers/annotation-page/canvas/canvas-wrapper'; -import ControlsSideBarContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/controls-side-bar'; +import ControlsSideBarContainer from 'containers/annotation-page/weed-ai-workspace/controls-side-bar/controls-side-bar'; import PropagateConfirmContainer from 'containers/annotation-page/standard-workspace/propagate-confirm'; import CanvasContextMenuContainer from 'containers/annotation-page/canvas/canvas-context-menu'; import ObjectsListContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/objects-list'; @@ -15,9 +15,9 @@ import ObjectSideBarComponent from 'components/annotation-page/standard-workspac import CanvasPointContextMenuComponent from 'components/annotation-page/canvas/canvas-point-context-menu'; import IssueAggregatorComponent from 'components/annotation-page/review/issues-aggregator'; -export default function WeedAIAnnotationWorkspaceComponent(): JSX.Element { +export default function WeedAIWorkspaceComponent(): JSX.Element { return ( - + } /> diff --git a/cvat-ui/src/containers/annotation-page/weed-ai-workspace/controls-side-bar/controls-side-bar.tsx b/cvat-ui/src/containers/annotation-page/weed-ai-workspace/controls-side-bar/controls-side-bar.tsx new file mode 100644 index 000000000000..fa670b5c6230 --- /dev/null +++ b/cvat-ui/src/containers/annotation-page/weed-ai-workspace/controls-side-bar/controls-side-bar.tsx @@ -0,0 +1,96 @@ +// Copyright (C) 2020-2021 Intel Corporation +// +// SPDX-License-Identifier: MIT +// +// This is only here so that line 20 can call the weed-ai-workspace version of the controls-side-bar +// component. + +import { connect } from 'react-redux'; + +import { Canvas } from 'cvat-canvas-wrapper'; +import { + mergeObjects, + groupObjects, + splitTrack, + redrawShapeAsync, + rotateCurrentFrame, + repeatDrawShapeAsync, + pasteShapeAsync, + resetAnnotationsGroup, +} from 'actions/annotation-actions'; +import ControlsSideBarComponent from 'components/annotation-page/weed-ai-workspace/controls-side-bar/controls-side-bar'; +import { ActiveControl, CombinedState, Rotation } from 'reducers/interfaces'; +import { KeyMap } from 'utils/mousetrap-react'; + +interface StateToProps { + canvasInstance: Canvas; + rotateAll: boolean; + activeControl: ActiveControl; + keyMap: KeyMap; + normalizedKeyMap: Record; + labels: any[]; +} + +interface DispatchToProps { + mergeObjects(enabled: boolean): void; + groupObjects(enabled: boolean): void; + splitTrack(enabled: boolean): void; + rotateFrame(angle: Rotation): void; + resetGroup(): void; + repeatDrawShape(): void; + pasteShape(): void; + redrawShape(): void; +} + +function mapStateToProps(state: CombinedState): StateToProps { + const { + annotation: { + canvas: { instance: canvasInstance, activeControl }, + job: { labels }, + }, + settings: { + player: { rotateAll }, + }, + shortcuts: { keyMap, normalizedKeyMap }, + } = state; + + return { + rotateAll, + canvasInstance, + activeControl, + labels, + normalizedKeyMap, + keyMap, + }; +} + +function dispatchToProps(dispatch: any): DispatchToProps { + return { + mergeObjects(enabled: boolean): void { + dispatch(mergeObjects(enabled)); + }, + groupObjects(enabled: boolean): void { + dispatch(groupObjects(enabled)); + }, + splitTrack(enabled: boolean): void { + dispatch(splitTrack(enabled)); + }, + rotateFrame(rotation: Rotation): void { + dispatch(rotateCurrentFrame(rotation)); + }, + repeatDrawShape(): void { + dispatch(repeatDrawShapeAsync()); + }, + pasteShape(): void { + dispatch(pasteShapeAsync()); + }, + resetGroup(): void { + dispatch(resetAnnotationsGroup()); + }, + redrawShape(): void { + dispatch(redrawShapeAsync()); + }, + }; +} + +export default connect(mapStateToProps, dispatchToProps)(ControlsSideBarComponent); diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index cc1e21e145a6..9916da7c164b 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -584,7 +584,7 @@ export enum Workspace { STANDARD3D = 'Standard 3D', STANDARD = 'Standard', ATTRIBUTE_ANNOTATION = 'Attribute annotation', - WEED_AI_ANNOTATION = 'Weed AI annotation', + WEED_AI_WORKSPACE = 'Weed AI', TAG_ANNOTATION = 'Tag annotation', REVIEW_WORKSPACE = 'Review', } From 65b2fb0b3205f2253b8c0fd12cef0f9fee4f21d2 Mon Sep 17 00:00:00 2001 From: Mike Lynch Date: Tue, 7 Dec 2021 09:50:32 +1100 Subject: [PATCH 6/6] Proof-of-concept adding a user help panel to the weeds-ai workspace --- cvat-ui/src/actions/annotation-actions.ts | 8 ++ .../objects-side-bar/objects-side-bar.tsx | 132 ++++++++++++++++++ .../weed-ai-workspace/user-help-block.tsx | 76 ++++++++++ .../weed-ai-workspace/weed-ai-workspace.tsx | 2 +- cvat-ui/src/reducers/annotation-reducer.ts | 7 + 5 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 cvat-ui/src/components/annotation-page/weed-ai-workspace/objects-side-bar/objects-side-bar.tsx create mode 100644 cvat-ui/src/components/annotation-page/weed-ai-workspace/user-help-block.tsx diff --git a/cvat-ui/src/actions/annotation-actions.ts b/cvat-ui/src/actions/annotation-actions.ts index 99bfb84ca927..c82facc6a91b 100644 --- a/cvat-ui/src/actions/annotation-actions.ts +++ b/cvat-ui/src/actions/annotation-actions.ts @@ -147,6 +147,7 @@ export enum AnnotationActionTypes { SPLIT_ANNOTATIONS_FAILED = 'SPLIT_ANNOTATIONS_FAILED', COLLAPSE_SIDEBAR = 'COLLAPSE_SIDEBAR', COLLAPSE_APPEARANCE = 'COLLAPSE_APPEARANCE', + COLLAPSE_USER_HELP = 'COLLAPSE_USER_HELP', COLLAPSE_OBJECT_ITEMS = 'COLLAPSE_OBJECT_ITEMS', ACTIVATE_OBJECT = 'ACTIVATE_OBJECT', REMOVE_OBJECT_SUCCESS = 'REMOVE_OBJECT_SUCCESS', @@ -587,6 +588,13 @@ export function collapseAppearance(): AnyAction { }; } +export function collapseUserHelp(): AnyAction { + return { + type: AnnotationActionTypes.COLLAPSE_USER_HELP, + payload: {}, + }; +} + export function collapseObjectItems(states: any[], collapsed: boolean): AnyAction { return { type: AnnotationActionTypes.COLLAPSE_OBJECT_ITEMS, diff --git a/cvat-ui/src/components/annotation-page/weed-ai-workspace/objects-side-bar/objects-side-bar.tsx b/cvat-ui/src/components/annotation-page/weed-ai-workspace/objects-side-bar/objects-side-bar.tsx new file mode 100644 index 000000000000..84812da56d3a --- /dev/null +++ b/cvat-ui/src/components/annotation-page/weed-ai-workspace/objects-side-bar/objects-side-bar.tsx @@ -0,0 +1,132 @@ +// Copyright (C) 2020-2021 Intel Corporation +// +// SPDX-License-Identifier: MIT + +import './styles.scss'; +import React, { Dispatch, TransitionEvent } from 'react'; +import { AnyAction } from 'redux'; +import { connect } from 'react-redux'; +import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons'; +import Text from 'antd/lib/typography/Text'; +import Tabs from 'antd/lib/tabs'; +import Layout from 'antd/lib/layout'; + +import { Canvas } from 'cvat-canvas-wrapper'; +import { Canvas3d } from 'cvat-canvas3d-wrapper'; +import { CombinedState, DimensionType } from 'reducers/interfaces'; +import LabelsList from 'components/annotation-page/standard-workspace/objects-side-bar/labels-list'; +import { adjustContextImagePosition } from 'components/annotation-page/standard-workspace/context-image/context-image'; +import { collapseSidebar as collapseSidebarAction } from 'actions/annotation-actions'; +import UserHelpBlock from 'components/annotation-page/weed-ai-workspace/user-help-block'; +import AppearanceBlock from 'components/annotation-page/appearance-block'; +import IssuesListComponent from 'components/annotation-page/standard-workspace/objects-side-bar/issues-list'; + +interface OwnProps { + objectsList: JSX.Element; +} + +interface StateToProps { + sidebarCollapsed: boolean; + canvasInstance: Canvas | Canvas3d; + jobInstance: any; +} + +interface DispatchToProps { + collapseSidebar(): void; +} + +function mapStateToProps(state: CombinedState): StateToProps { + const { + annotation: { + sidebarCollapsed, + canvas: { instance: canvasInstance }, + job: { instance: jobInstance }, + }, + } = state; + + return { + sidebarCollapsed, + canvasInstance, + jobInstance, + }; +} + +function mapDispatchToProps(dispatch: Dispatch): DispatchToProps { + return { + collapseSidebar(): void { + dispatch(collapseSidebarAction()); + }, + }; +} + +function ObjectsSideBar(props: StateToProps & DispatchToProps & OwnProps): JSX.Element { + const { + sidebarCollapsed, canvasInstance, collapseSidebar, objectsList, jobInstance, + } = props; + + 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); + } + }; + + if (collapser) { + (collapser as HTMLElement).addEventListener('transitionend', listener as any); + } + + adjustContextImagePosition(!sidebarCollapsed); + collapseSidebar(); + }; + + let is2D = true; + if (jobInstance) { + is2D = jobInstance.task.dimension === DimensionType.DIM_2D; + } + + return ( + + {/* eslint-disable-next-line */} + + {sidebarCollapsed ? : } + + + + Objects} key='objects'> + {objectsList} + + Labels} key='labels'> + + + + {is2D ? ( + Issues} key='issues'> + + + ) : null} + + + {!sidebarCollapsed && } + {!sidebarCollapsed && } + + ); +} + +export default connect(mapStateToProps, mapDispatchToProps)(React.memo(ObjectsSideBar)); diff --git a/cvat-ui/src/components/annotation-page/weed-ai-workspace/user-help-block.tsx b/cvat-ui/src/components/annotation-page/weed-ai-workspace/user-help-block.tsx new file mode 100644 index 000000000000..5d295e49b119 --- /dev/null +++ b/cvat-ui/src/components/annotation-page/weed-ai-workspace/user-help-block.tsx @@ -0,0 +1,76 @@ +// Copyright (C) 2020-2021 Intel Corporation +// +// SPDX-License-Identifier: MIT + +import React, { Dispatch } from 'react'; +import { AnyAction } from 'redux'; +import { connect } from 'react-redux'; +import Text from 'antd/lib/typography/Text'; +import Collapse from 'antd/lib/collapse'; + +import { CombinedState } from 'reducers/interfaces'; +import { collapseUserHelp as collapseUserHelpAction } from 'actions/annotation-actions'; + +interface StateToProps { + userHelpCollapsed: boolean; +} + +interface DispatchToProps { + collapseUserHelp(): void; +} + +function mapStateToProps(state: CombinedState): StateToProps { + const { + annotation: { + userHelpCollapsed, + }, + } = state; + + return { + userHelpCollapsed, + }; +} + +function mapDispatchToProps(dispatch: Dispatch): DispatchToProps { + return { + collapseUserHelp(): void { + dispatch(collapseUserHelpAction()); + }, + }; +} + +type Props = StateToProps & DispatchToProps; + +function UserHelpBlock(props: Props): JSX.Element { + const { + userHelpCollapsed, + collapseUserHelp, + } = props; + + return ( + + + Annotation guide + + )} + key='userhelp' + > +
+ + + ); +} + +export default connect(mapStateToProps, mapDispatchToProps)(React.memo(UserHelpBlock)); diff --git a/cvat-ui/src/components/annotation-page/weed-ai-workspace/weed-ai-workspace.tsx b/cvat-ui/src/components/annotation-page/weed-ai-workspace/weed-ai-workspace.tsx index 21551890fc61..de407112b4df 100644 --- a/cvat-ui/src/components/annotation-page/weed-ai-workspace/weed-ai-workspace.tsx +++ b/cvat-ui/src/components/annotation-page/weed-ai-workspace/weed-ai-workspace.tsx @@ -11,7 +11,7 @@ import ControlsSideBarContainer from 'containers/annotation-page/weed-ai-workspa import PropagateConfirmContainer from 'containers/annotation-page/standard-workspace/propagate-confirm'; import CanvasContextMenuContainer from 'containers/annotation-page/canvas/canvas-context-menu'; import ObjectsListContainer from 'containers/annotation-page/standard-workspace/objects-side-bar/objects-list'; -import ObjectSideBarComponent from 'components/annotation-page/standard-workspace/objects-side-bar/objects-side-bar'; +import ObjectSideBarComponent from 'components/annotation-page/weed-ai-workspace/objects-side-bar/objects-side-bar'; import CanvasPointContextMenuComponent from 'components/annotation-page/canvas/canvas-point-context-menu'; import IssueAggregatorComponent from 'components/annotation-page/review/issues-aggregator'; diff --git a/cvat-ui/src/reducers/annotation-reducer.ts b/cvat-ui/src/reducers/annotation-reducer.ts index 028db9268195..affa58a5d763 100644 --- a/cvat-ui/src/reducers/annotation-reducer.ts +++ b/cvat-ui/src/reducers/annotation-reducer.ts @@ -110,6 +110,7 @@ const defaultState: AnnotationState = { colors: [], sidebarCollapsed: false, appearanceCollapsed: false, + userHelpCollapsed: false, filtersPanelVisible: false, requestReviewDialogVisible: false, submitReviewDialogVisible: false, @@ -410,6 +411,12 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { appearanceCollapsed: !state.appearanceCollapsed, }; } + case AnnotationActionTypes.COLLAPSE_USER_HELP: { + return { + ...state, + userHelpCollapsed: !state.userHelpCollapsed, + }; + } case AnnotationActionTypes.COLLAPSE_OBJECT_ITEMS: { const { states, collapsed } = action.payload;