From b1ac4b13aef2e8d41747429bb7b4ff36f5c99791 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Fri, 2 Oct 2020 14:38:01 +0300 Subject: [PATCH 01/25] Some UI implementations --- cvat-ui/src/assets/opencv.svg | 10 + .../controls-side-bar/controls-side-bar.tsx | 2 + .../controls-side-bar/opencv-control.tsx | 214 ++++++++++++++++++ .../standard-workspace/styles.scss | 25 +- cvat-ui/src/icons.tsx | 4 + cvat-ui/src/reducers/interfaces.ts | 1 + 6 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 cvat-ui/src/assets/opencv.svg create mode 100644 cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx diff --git a/cvat-ui/src/assets/opencv.svg b/cvat-ui/src/assets/opencv.svg new file mode 100644 index 000000000000..7608729daf81 --- /dev/null +++ b/cvat-ui/src/assets/opencv.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx index ef4b457e11d5..cce0f05a8ade 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx @@ -15,6 +15,7 @@ import MoveControl from './move-control'; import FitControl from './fit-control'; import ResizeControl from './resize-control'; import ToolsControl from './tools-control'; +import OpenCVControl from './opencv-control'; import DrawRectangleControl from './draw-rectangle-control'; import DrawPolygonControl from './draw-polygon-control'; import DrawPolylineControl from './draw-polyline-control'; @@ -186,6 +187,7 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element {
+ { + public constructor(props: Props) { + super(props); + const { labels } = props; + this.state = { + libraryInitialized: false, + initializationError: false, + initializationProgress: -1, + activeLabelID: labels[0].id, + }; + } + + private renderDrawingContent(): JSX.Element { + const { activeLabelID } = this.state; + const { labels } = this.props; + + return ( + <> + + + + + + + + + + + + + + ); + } + + + private renderContent(): JSX.Element { + const { libraryInitialized, initializationProgress, initializationError } = this.state; + + return ( +
+ + + OpenCV.js + + + { libraryInitialized ? ( + + + { this.renderDrawingContent() } + + + + + + ) : ( + <> + + + + + + { initializationProgress >= 0 && ( + + + + + + )} + + )} +
+ ); + } + + public render(): JSX.Element { + const { isActivated, canvasInstance } = this.props; + const dynamcPopoverPros = isActivated ? { + overlayStyle: { + display: 'none', + }, + } : {}; + + const dynamicIconProps = isActivated ? { + className: 'cvat-active-canvas-control cvat-opencv-control', + onClick: (): void => { + canvasInstance.interact({ enabled: false }); + }, + } : { + className: 'cvat-tools-control', + }; + + return ( + + + + ); + } +} + +export default connect( + mapStateToProps, +)(OpenCVControlComponent); diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss b/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss index 9dcfdd2b28e2..59e21deeb41f 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss +++ b/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss @@ -84,7 +84,8 @@ } .cvat-draw-shape-popover, -.cvat-tools-control-popover { +.cvat-tools-control-popover, +.cvat-opencv-control-popover { > .ant-popover-content > .ant-popover-inner > div > .ant-popover-inner-content { @@ -102,13 +103,33 @@ width: 100%; } -.cvat-tools-control-popover-content { +.cvat-tools-control-popover-content, +.cvat-opencv-control-popover-content { width: fit-content; padding: 10px; border-radius: 5px; background: $background-color-2; } +.cvat-opencv-initialization-button { + width: 120px; + margin: 16px 8px 16px 8px; +} + +.cvat-opencv-drawing-tools { + margin-top: 8px; +} + +.cvat-opencv-drawing-tool { + padding: 4px; + width: 40px; + height: 40px; + + > i { + font-size: 16px; + } +} + .cvat-draw-shape-popover-content { padding: 10px; border-radius: 5px; diff --git a/cvat-ui/src/icons.tsx b/cvat-ui/src/icons.tsx index 9a071b1c1d7f..05dd362213f9 100644 --- a/cvat-ui/src/icons.tsx +++ b/cvat-ui/src/icons.tsx @@ -47,6 +47,7 @@ import SVGCubeIcon from './assets/cube-icon.svg'; import SVGResetPerspectiveIcon from './assets/reset-perspective.svg'; import SVGColorizeIcon from './assets/colorize-icon.svg'; import SVGAITools from './assets/ai-tools-icon.svg'; +import SVGOpenCV from './assets/opencv.svg'; export const CVATLogo = React.memo( @@ -178,3 +179,6 @@ export const AIToolsIcon = React.memo( export const ColorizeIcon = React.memo( (): JSX.Element => , ); +export const OpenCVIcon = React.memo( + (): JSX.Element => , +); diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index d0565886a4b0..6ae23c810dc5 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -285,6 +285,7 @@ export enum ActiveControl { SPLIT = 'split', EDIT = 'edit', AI_TOOLS = 'ai_tools', + OPENCV_TOOLS = 'opencv_tools', } export enum ShapeType { From b553502def469e18bbfb646afbd763fa60945137 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Fri, 2 Oct 2020 15:02:07 +0300 Subject: [PATCH 02/25] Added opencv wrapper --- cvat-ui/src/utils/opencv-wrapper.ts | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 cvat-ui/src/utils/opencv-wrapper.ts diff --git a/cvat-ui/src/utils/opencv-wrapper.ts b/cvat-ui/src/utils/opencv-wrapper.ts new file mode 100644 index 000000000000..5676a03f57c9 --- /dev/null +++ b/cvat-ui/src/utils/opencv-wrapper.ts @@ -0,0 +1,39 @@ +// Copyright (C) 2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +/* eslint-disable */ + + +type ProgressCallback = (percent: number) => void; +type ScissorsState = { }; +type ScissorsResult = { + points: number[]; + state: ScissorsState | null; +}; + + +class OpenCVWrapper { + public constructor() { + // todo + } + + public async initialize(onProgress: ProgressCallback): Promise { + // todo + } + + public initialized(): boolean { + // todo + return false; + } + + public intelligentScissors(points: number[], image: ImageData, state: ScissorsState | null = null): ScissorsResult { + // todo + return { + points: [], + state: null, + }; + } +} + +export default new OpenCVWrapper(); From a058c0273586f0998c50a1c213cd140c706e2a31 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Fri, 2 Oct 2020 15:13:15 +0300 Subject: [PATCH 03/25] Updated Opencv wrapper --- cvat-ui/src/utils/opencv-wrapper.ts | 31 +++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/cvat-ui/src/utils/opencv-wrapper.ts b/cvat-ui/src/utils/opencv-wrapper.ts index 5676a03f57c9..760f45f6c20e 100644 --- a/cvat-ui/src/utils/opencv-wrapper.ts +++ b/cvat-ui/src/utils/opencv-wrapper.ts @@ -4,21 +4,22 @@ /* eslint-disable */ - -type ProgressCallback = (percent: number) => void; type ScissorsState = { }; type ScissorsResult = { points: number[]; state: ScissorsState | null; }; - +interface Scissors { + run(points: number[], image: ImageData, state: ScissorsState | null): Promise; + params: object; +} class OpenCVWrapper { public constructor() { // todo } - public async initialize(onProgress: ProgressCallback): Promise { + public async initialize(onProgress: (percent: number) => void): Promise { // todo } @@ -27,13 +28,27 @@ class OpenCVWrapper { return false; } - public intelligentScissors(points: number[], image: ImageData, state: ScissorsState | null = null): ScissorsResult { - // todo + public intelligentScissors(): Scissors { return { - points: [], - state: null, + run: this.runIntelligentScissors, + params: { + shapeType: 'polygon', + minPosVertices: 1, + }, }; } + + private runIntelligentScissors = async ( + points: number[], image: ImageData, state: ScissorsState | null = null, + ): Promise => { + // todo + return new Promise((resolve, reject) => { + resolve({ + points: [], + state: null, + }); + }); + } } export default new OpenCVWrapper(); From 28101b444c6a2f5842e110b9690d264e26cc88ab Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Fri, 2 Oct 2020 15:23:55 +0300 Subject: [PATCH 04/25] Moved initialization stub --- .../controls-side-bar/opencv-control.tsx | 16 +++++----------- cvat-ui/src/utils/opencv-wrapper.ts | 14 +++++++++++++- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx index 6a3ebc497454..51792af22ded 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx @@ -18,6 +18,7 @@ import notification from 'antd/lib/notification'; import { OpenCVIcon } from 'icons'; import { Canvas } from 'cvat-canvas-wrapper'; import { CombinedState, ActiveControl } from 'reducers/interfaces'; +import OpenCVWrapper from 'utils/opencv-wrapper'; interface Props { labels: any[]; @@ -121,7 +122,7 @@ class OpenCVControlComponent extends React.PureComponent { { this.renderDrawingContent() } - + @@ -134,18 +135,11 @@ class OpenCVControlComponent extends React.PureComponent { className='cvat-opencv-initialization-button' onClick={async () => { try { - // temporary stub - let progress = 0; this.setState({ initializationProgress: 0 }); - setInterval(() => { - progress += 10; + await OpenCVWrapper.initialize((progress: number) => { this.setState({ initializationProgress: progress }); - if (progress >= 100) { - setTimeout(() => { - this.setState({ libraryInitialized: true }); - }, 500); - } - }, 500); + }); + this.setState({ libraryInitialized: true }); } catch (error) { notification.error({ description: error.toString(), diff --git a/cvat-ui/src/utils/opencv-wrapper.ts b/cvat-ui/src/utils/opencv-wrapper.ts index 760f45f6c20e..e00113345ea8 100644 --- a/cvat-ui/src/utils/opencv-wrapper.ts +++ b/cvat-ui/src/utils/opencv-wrapper.ts @@ -20,7 +20,19 @@ class OpenCVWrapper { } public async initialize(onProgress: (percent: number) => void): Promise { - // todo + // temporary stub + return new Promise((resolve, reject) => { + let progress = 0; + const interval = setInterval(() => { + progress += 10; + onProgress(progress); + if (progress >= 100) { + reject(new Error('An error message')); + clearInterval(interval); + resolve(); + } + }, 500); + }) } public initialized(): boolean { From 5c46aaadb5b24b11649a191809d47847f434474e Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Fri, 2 Oct 2020 17:37:40 +0300 Subject: [PATCH 05/25] Added threshold --- .../standard-workspace/controls-side-bar/opencv-control.tsx | 2 +- cvat-ui/src/utils/opencv-wrapper.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx index 51792af22ded..27aeff574e3e 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx @@ -152,7 +152,7 @@ class OpenCVControlComponent extends React.PureComponent { } }} > - Initialize + Load OpenCV diff --git a/cvat-ui/src/utils/opencv-wrapper.ts b/cvat-ui/src/utils/opencv-wrapper.ts index e00113345ea8..3621eb1acaab 100644 --- a/cvat-ui/src/utils/opencv-wrapper.ts +++ b/cvat-ui/src/utils/opencv-wrapper.ts @@ -10,7 +10,7 @@ type ScissorsResult = { state: ScissorsState | null; }; interface Scissors { - run(points: number[], image: ImageData, state: ScissorsState | null): Promise; + run(points: number[], image: ImageData, threshold: number, state: ScissorsState | null): Promise; params: object; } @@ -51,7 +51,7 @@ class OpenCVWrapper { } private runIntelligentScissors = async ( - points: number[], image: ImageData, state: ScissorsState | null = null, + points: number[], image: ImageData, threshold: number, state: ScissorsState | null = null, ): Promise => { // todo return new Promise((resolve, reject) => { From 45c7f96c265671105447bdce84a715c040afacf3 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 7 Oct 2020 15:56:16 +0300 Subject: [PATCH 06/25] Setup interaction with canvas --- cvat-ui/src/actions/annotation-actions.ts | 7 +- .../controls-side-bar/opencv-control.tsx | 354 ++++++++++++++++-- .../controls-side-bar/tools-control.tsx | 60 +-- .../standard-workspace/styles.scss | 29 +- cvat-ui/src/cvat-canvas-wrapper.ts | 16 + cvat-ui/src/reducers/annotation-reducer.ts | 8 +- cvat-ui/src/reducers/interfaces.ts | 5 +- cvat-ui/src/utils/opencv-wrapper.ts | 32 +- 8 files changed, 402 insertions(+), 109 deletions(-) diff --git a/cvat-ui/src/actions/annotation-actions.ts b/cvat-ui/src/actions/annotation-actions.ts index 1e667ee1d5da..9e2a49eba1bb 100644 --- a/cvat-ui/src/actions/annotation-actions.ts +++ b/cvat-ui/src/actions/annotation-actions.ts @@ -21,6 +21,7 @@ import { ContextMenuType, Workspace, Model, + OpenCVTool, } from 'reducers/interfaces'; import getCore from 'cvat-core-wrapper'; @@ -1413,7 +1414,10 @@ export function pasteShapeAsync(): ThunkAction { }; } -export function interactWithCanvas(activeInteractor: Model, activeLabelID: number): AnyAction { +export function interactWithCanvas( + activeInteractor: Model | OpenCVTool, + activeLabelID: number, +): AnyAction { return { type: AnnotationActionTypes.INTERACT_WITH_CANVAS, payload: { @@ -1432,7 +1436,6 @@ export function setAIToolsRef(ref: MutableRefObject): AnyAction { }; } - export function repeatDrawShapeAsync(): ThunkAction { return async (dispatch: ActionCreator): Promise => { const { diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx index 27aeff574e3e..0fb57992767a 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx @@ -13,17 +13,41 @@ import Text from 'antd/lib/typography/Text'; import Tabs from 'antd/lib/tabs'; import Button from 'antd/lib/button'; import Progress from 'antd/lib/progress'; +import Modal from 'antd/lib/modal'; import notification from 'antd/lib/notification'; import { OpenCVIcon } from 'icons'; -import { Canvas } from 'cvat-canvas-wrapper'; -import { CombinedState, ActiveControl } from 'reducers/interfaces'; -import OpenCVWrapper from 'utils/opencv-wrapper'; +import { Canvas, convertShapesForInteractor } from 'cvat-canvas-wrapper'; +import getCore from 'cvat-core-wrapper'; +import openCVWrapper, { Scissors, ScissorsState } from 'utils/opencv-wrapper'; +import { + CombinedState, + ActiveControl, + OpenCVTool, + ObjectType, +} from 'reducers/interfaces'; +import { + interactWithCanvas, + fetchAnnotationsAsync, + updateAnnotationsAsync, + createAnnotationsAsync, +} from 'actions/annotation-actions'; interface Props { labels: any[]; canvasInstance: Canvas; + jobInstance: any; isActivated: boolean; + states: any[]; + frame: number; + curZOrder: number; +} + +interface DispatchToProps { + onInteractionStart(activeInteractor: OpenCVTool, activeLabelID: number): void; + updateAnnotations(statesToUpdate: any[]): void; + createAnnotations(sessionInstance: any, frame: number, statesToCreate: any[]): void; + fetchAnnotations(): void; } interface State { @@ -31,35 +55,268 @@ interface State { initializationError: boolean; initializationProgress: number; activeLabelID: number; + processing: boolean; } +const core = getCore(); + function mapStateToProps(state: CombinedState): Props { - const { annotation } = state; - const { job, canvas } = annotation; - const { activeControl } = canvas; + const { + annotation: { + annotations: { + states, + zLayer: { + cur: curZOrder, + }, + }, + job: { + instance: jobInstance, + labels, + }, + canvas: { + activeControl, + instance: canvasInstance, + }, + player: { + frame: { + number: frame, + }, + }, + }, + } = state; return { - labels: job.labels, - canvasInstance: canvas.instance, isActivated: activeControl === ActiveControl.OPENCV_TOOLS, + canvasInstance, + jobInstance, + curZOrder, + labels, + states, + frame, }; } -class OpenCVControlComponent extends React.PureComponent { - public constructor(props: Props) { +const mapDispatchToProps = { + onInteractionStart: interactWithCanvas, + updateAnnotations: updateAnnotationsAsync, + fetchAnnotations: fetchAnnotationsAsync, + createAnnotations: createAnnotationsAsync, +}; + + +class OpenCVControlComponent extends React.PureComponent { + private activeTool: Scissors | null; + private toolState: ScissorsState | null; + private interactiveStateID: number | null; + private interactionIsDone: boolean; + private interactionIsAborted: boolean; + + public constructor(props: Props & DispatchToProps) { super(props); const { labels } = props; + + this.activeTool = null; + this.toolState = null; + this.interactiveStateID = null; + this.interactionIsDone = false; + this.interactionIsAborted = false; + this.state = { - libraryInitialized: false, + libraryInitialized: true, initializationError: false, initializationProgress: -1, activeLabelID: labels[0].id, + processing: false, }; } + public componentDidMount(): void { + const { canvasInstance } = this.props; + canvasInstance.html().addEventListener('canvas.interacted', this.interactionListener); + canvasInstance.html().addEventListener('canvas.canceled', this.cancelListener); + } + + public componentDidUpdate(prevProps: Props): void { + const { isActivated } = this.props; + if (!prevProps.isActivated && isActivated) { + // reset flags when before using a tool + this.interactiveStateID = null; + this.interactionIsDone = false; + this.interactionIsAborted = false; + } + } + + public componentWillUnmount(): void { + const { canvasInstance } = this.props; + canvasInstance.html().removeEventListener('canvas.interacted', this.interactionListener); + canvasInstance.html().removeEventListener('canvas.canceled', this.cancelListener); + } + + private getInteractiveState(): any | null { + const { states } = this.props; + return states + .filter((_state: any): boolean => ( + _state.clientID === this.interactiveStateID + ))[0] || null; + } + + private cancelListener = async (): Promise => { + const { processing } = this.state; + const { + fetchAnnotations, + isActivated, + jobInstance, + frame, + } = this.props; + + if (isActivated) { + if (processing && !this.interactionIsDone) { + // user pressed ESC + this.setState({ processing: false }); + this.interactionIsAborted = true; + } + + if (this.interactiveStateID !== null) { + const state = this.getInteractiveState(); + this.interactiveStateID = null; + await state.delete(frame); + fetchAnnotations(); + } + + await jobInstance.actions.freeze(false); + } + }; + + private interactionListener = async (e: Event): Promise => { + const { + fetchAnnotations, + updateAnnotations, + isActivated, + jobInstance, + frame, + labels, + curZOrder, + } = this.props; + const { activeLabelID, processing } = this.state; + if (!isActivated || !this.activeTool) { + return; + } + + this.interactionIsDone = (e as CustomEvent).detail.isDone; + if (processing) { + return; + } + + try { + let points: number[] = []; + if ((e as CustomEvent).detail.shapesUpdated) { + this.setState({ processing: true }); + try { + // Getting image data + const canvas: HTMLCanvasElement | undefined = window.document + .getElementById('cvat_canvas_background') as HTMLCanvasElement | undefined; + if (!canvas) { + throw new Error('Element #cvat_canvas_background was not found'); + } + + const { width, height } = canvas; + const context = canvas.getContext('2d'); + if (!context) { + throw new Error('Canvas context is empty'); + } + + const imageData = context.getImageData(0, 0, width, height); + + // Handling via OpenCV.js + const result = await this.activeTool.run( + convertShapesForInteractor((e as CustomEvent).detail.shapes).flat(), + imageData, + 500, + this.toolState, + ); + this.toolState = result.state; + points = result.points; + + // Increasing number of points artificially + let minNumberOfPoints = 1; + if (this.activeTool.params.shape.shapeType === 'polyline') { + minNumberOfPoints = 2; + } else if (this.activeTool.params.shape.shapeType === 'polygon') { + minNumberOfPoints = 3; + } + while (points.length < minNumberOfPoints * 2) { + points.push(...points.slice(points.length - 2)); + } + + if (this.interactionIsAborted) { + // user has cancelled interaction (for example pressed ESC) + // while opencv was processing the request + return; + } + } finally { + this.setState({ processing: false }); + } + } + + if (this.interactiveStateID === null) { + if (!this.interactionIsDone) { + await jobInstance.actions.freeze(true); + } + + const object = new core.classes.ObjectState({ + ...this.activeTool.params.shape, + frame, + objectType: ObjectType.SHAPE, + label: labels + .filter((label: any) => label.id === activeLabelID)[0], + points, + occluded: false, + zOrder: curZOrder, + }); + // need a clientID of a created object to interact with it further + // so, we do not use createAnnotationAction + const [clientID] = await jobInstance.annotations.put([object]); + this.interactiveStateID = clientID; + + // update annotations on a canvas + fetchAnnotations(); + return; + } + + const state = this.getInteractiveState(); + if ((e as CustomEvent).detail.isDone) { + const finalObject = new core.classes.ObjectState({ + frame: state.frame, + objectType: state.objectType, + label: state.label, + shapeType: state.shapeType, + points: points || state.points, + occluded: state.occluded, + zOrder: state.zOrder, + }); + this.interactiveStateID = null; + await state.delete(frame); + await jobInstance.actions.freeze(false); + await jobInstance.annotations.put([finalObject]); + fetchAnnotations(); + } else { + state.points = points; + updateAnnotations([state]); + fetchAnnotations(); + } + } catch (error) { + notification.error({ + description: error.toString(), + message: 'Processing error occured', + }); + } finally { + this.setState({ processing: false }); + } + }; + private renderDrawingContent(): JSX.Element { const { activeLabelID } = this.state; - const { labels } = this.props; + const { labels, canvasInstance, onInteractionStart } = this.props; return ( <> @@ -96,7 +353,18 @@ class OpenCVControlComponent extends React.PureComponent { - @@ -106,7 +374,6 @@ class OpenCVControlComponent extends React.PureComponent { ); } - private renderContent(): JSX.Element { const { libraryInitialized, initializationProgress, initializationError } = this.state; @@ -119,24 +386,27 @@ class OpenCVControlComponent extends React.PureComponent { { libraryInitialized ? ( - + { this.renderDrawingContent() } - + ) : ( <> - - + + = 0 ? 17 : 24}> - - { initializationProgress >= 0 && ( - - + { initializationProgress >= 0 && ( + - - )} + )} + )} @@ -175,6 +444,7 @@ class OpenCVControlComponent extends React.PureComponent { public render(): JSX.Element { const { isActivated, canvasInstance } = this.props; + const { processing } = this.state; const dynamcPopoverPros = isActivated ? { overlayStyle: { display: 'none', @@ -191,18 +461,32 @@ class OpenCVControlComponent extends React.PureComponent { }; return ( - - - + <> + + OpenCV handles your request. Please wait.. + + + + + + ); } } export default connect( mapStateToProps, + mapDispatchToProps, )(OpenCVControlComponent); diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/tools-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/tools-control.tsx index 89d89912df27..60e8f27e9496 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/tools-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/tools-control.tsx @@ -16,7 +16,7 @@ import notification from 'antd/lib/notification'; import Progress from 'antd/lib/progress'; import { AIToolsIcon } from 'icons'; -import { Canvas } from 'cvat-canvas-wrapper'; +import { Canvas, convertShapesForInteractor } from 'cvat-canvas-wrapper'; import range from 'utils/range'; import getCore from 'cvat-core-wrapper'; import { @@ -32,7 +32,6 @@ import { updateAnnotationsAsync, createAnnotationsAsync, } from 'actions/annotation-actions'; -import { InteractionResult } from 'cvat-canvas/src/typescript/canvas'; import DetectorRunner from 'components/model-runner-modal/detector-runner'; import InputNumber from 'antd/lib/input-number'; @@ -91,22 +90,6 @@ const mapDispatchToProps = { createAnnotations: createAnnotationsAsync, }; -function convertShapesForInteractor(shapes: InteractionResult[]): number[][] { - const reducer = (acc: number[][], _: number, index: number, array: number[]): number[][] => { - if (!(index % 2)) { // 0, 2, 4 - acc.push([ - array[index], - array[index + 1], - ]); - } - return acc; - }; - - return shapes.filter((shape: InteractionResult): boolean => shape.shapeType === 'points' && shape.button === 0) - .map((shape: InteractionResult): number[] => shape.points) - .flat().reduce(reducer, []); -} - type Props = StateToProps & DispatchToProps; interface State { activeInteractor: Model | null; @@ -220,11 +203,11 @@ export class ToolsControlComponent extends React.PureComponent { } = this.props; const { activeInteractor, interactiveStateID, fetching } = this.state; - try { - if (!isActivated) { - throw Error('Canvas raises event "canvas.interacted" when interaction with it is off'); - } + if (!isActivated) { + return; + } + try { if (fetching) { this.interactionIsDone = (e as CustomEvent).detail.isDone; return; @@ -330,6 +313,11 @@ export class ToolsControlComponent extends React.PureComponent { curZOrder, fetchAnnotations, } = this.props; + + if (!isActivated) { + return; + } + const { activeLabelID } = this.state; const [label] = jobInstance.task.labels.filter( (_label: any): boolean => _label.id === activeLabelID, @@ -341,10 +329,6 @@ export class ToolsControlComponent extends React.PureComponent { this.interactionIsDone = true; try { - if (!isActivated) { - throw Error('Canvas raises event "canvas.interacted" when interaction with it is off'); - } - const { points } = (e as CustomEvent).detail.shapes[0]; const state = new core.classes.ObjectState({ shapeType: ShapeType.RECTANGLE, @@ -566,9 +550,7 @@ export class ToolsControlComponent extends React.PureComponent { max={jobInstance.stopFrame - frame} onChange={(value: number | undefined): void => { if (typeof (value) !== 'undefined') { - this.setState({ - trackingFrames: value, - }); + this.setState({ trackingFrames: value }); } }} /> @@ -582,9 +564,7 @@ export class ToolsControlComponent extends React.PureComponent { className='cvat-tools-track-button' disabled={!activeTracker || fetching || frame === jobInstance.stopFrame} onClick={() => { - this.setState({ - mode: 'tracking', - }); + this.setState({ mode: 'tracking' }); if (activeTracker) { canvasInstance.cancel(); @@ -649,9 +629,7 @@ export class ToolsControlComponent extends React.PureComponent { className='cvat-tools-interact-button' disabled={!activeInteractor || fetching} onClick={() => { - this.setState({ - mode: 'interaction', - }); + this.setState({ mode: 'interaction' }); if (activeInteractor) { canvasInstance.cancel(); @@ -699,16 +677,8 @@ export class ToolsControlComponent extends React.PureComponent { task={jobInstance.task} runInference={async (task: any, model: Model, body: object) => { try { - this.setState({ - mode: 'detection', - }); - - this.setState({ fetching: true }); - const result = await core.lambda.call(task, model, { - ...body, - frame, - }); - + this.setState({ mode: 'detection', fetching: true }); + const result = await core.lambda.call(task, model, { ...body, frame }); const states = result .map((data: any): any => ( new core.classes.ObjectState({ diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss b/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss index 59e21deeb41f..79e9806d2e82 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss +++ b/cvat-ui/src/components/annotation-page/standard-workspace/styles.scss @@ -106,24 +106,29 @@ .cvat-tools-control-popover-content, .cvat-opencv-control-popover-content { width: fit-content; - padding: 10px; - border-radius: 5px; + padding: $grid-unit-size; + border-radius: $grid-unit-size; background: $background-color-2; + + .ant-tabs-tab { + width: $grid-unit-size * 14; + text-align: center; + } } .cvat-opencv-initialization-button { - width: 120px; - margin: 16px 8px 16px 8px; + width: 100%; + margin: $grid-unit-size 0; } .cvat-opencv-drawing-tools { - margin-top: 8px; + margin-top: $grid-unit-size; } .cvat-opencv-drawing-tool { - padding: 4px; - width: 40px; - height: 40px; + padding: $grid-unit-size; + width: $grid-unit-size * 5; + height: $grid-unit-size * 5; > i { font-size: 16px; @@ -131,13 +136,13 @@ } .cvat-draw-shape-popover-content { - padding: 10px; - border-radius: 5px; + padding: $grid-unit-size; + border-radius: $grid-unit-size; background: $background-color-2; - width: 270px; + width: 34 * $grid-unit-size; > div { - margin-top: 5px; + margin-top: $grid-unit-size; } > div:nth-child(3) > div > div { diff --git a/cvat-ui/src/cvat-canvas-wrapper.ts b/cvat-ui/src/cvat-canvas-wrapper.ts index 2dc70b8db859..8f897b49dbff 100644 --- a/cvat-ui/src/cvat-canvas-wrapper.ts +++ b/cvat-ui/src/cvat-canvas-wrapper.ts @@ -12,6 +12,22 @@ import { InteractionResult as InteractionResultType, } from 'cvat-canvas/src/typescript/canvas'; +export function convertShapesForInteractor(shapes: InteractionResult[]): number[][] { + const reducer = (acc: number[][], _: number, index: number, array: number[]): number[][] => { + if (!(index % 2)) { // 0, 2, 4 + acc.push([ + array[index], + array[index + 1], + ]); + } + return acc; + }; + + return shapes.filter((shape: InteractionResult): boolean => shape.shapeType === 'points' && shape.button === 0) + .map((shape: InteractionResult): number[] => shape.points) + .flat().reduce(reducer, []); +} + export type InteractionData = InteractionDataType; export type InteractionResult = InteractionResultType; diff --git a/cvat-ui/src/reducers/annotation-reducer.ts b/cvat-ui/src/reducers/annotation-reducer.ts index b284b377788f..02ae8cd27d6e 100644 --- a/cvat-ui/src/reducers/annotation-reducer.ts +++ b/cvat-ui/src/reducers/annotation-reducer.ts @@ -1047,6 +1047,7 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { }; } case AnnotationActionTypes.INTERACT_WITH_CANVAS: { + const { activeInteractor, activeLabelID } = action.payload; return { ...state, annotations: { @@ -1055,12 +1056,13 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { }, drawing: { ...state.drawing, - activeInteractor: action.payload.activeInteractor, - activeLabelID: action.payload.activeLabelID, + activeInteractor, + activeLabelID, }, canvas: { ...state.canvas, - activeControl: ActiveControl.AI_TOOLS, + activeControl: activeInteractor + .type.startsWith('opencv') ? ActiveControl.OPENCV_TOOLS : ActiveControl.AI_TOOLS, }, }; } diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index 6ae23c810dc5..03d41eb3fc02 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -4,6 +4,7 @@ import { ExtendedKeyMapOptions } from 'react-hotkeys'; import { Canvas, RectDrawingMethod } from 'cvat-canvas-wrapper'; +import { Scissors } from 'utils/opencv-wrapper'; import { MutableRefObject } from 'react'; export type StringObject = { @@ -150,6 +151,8 @@ export interface Model { }; } +export type OpenCVTool = Scissors; + export enum RQStatus { unknown = 'unknown', queued = 'queued', @@ -358,7 +361,7 @@ export interface AnnotationState { frameAngles: number[]; }; drawing: { - activeInteractor?: Model; + activeInteractor?: Model | OpenCVTool; activeShapeType: ShapeType; activeRectDrawingMethod?: RectDrawingMethod; activeNumOfPoints?: number; diff --git a/cvat-ui/src/utils/opencv-wrapper.ts b/cvat-ui/src/utils/opencv-wrapper.ts index 3621eb1acaab..a7f7465a567d 100644 --- a/cvat-ui/src/utils/opencv-wrapper.ts +++ b/cvat-ui/src/utils/opencv-wrapper.ts @@ -4,17 +4,21 @@ /* eslint-disable */ -type ScissorsState = { }; -type ScissorsResult = { +export type ScissorsState = { }; +export type ScissorsResult = { points: number[]; state: ScissorsState | null; }; -interface Scissors { +export interface Scissors { run(points: number[], image: ImageData, threshold: number, state: ScissorsState | null): Promise; - params: object; + type: 'opencv_scissors'; + params: { + shape: object; + canvas: object; + }; } -class OpenCVWrapper { +export class OpenCVWrapper { public constructor() { // todo } @@ -40,23 +44,29 @@ class OpenCVWrapper { return false; } - public intelligentScissors(): Scissors { + public scissors(): Scissors { return { - run: this.runIntelligentScissors, + run: this.scissorsRunner, params: { - shapeType: 'polygon', - minPosVertices: 1, + shape: { + shapeType: 'polygon', + }, + canvas: { + shapeType: 'points', + minPosVertices: 1, + }, }, + type: 'opencv_scissors', }; } - private runIntelligentScissors = async ( + private scissorsRunner = async ( points: number[], image: ImageData, threshold: number, state: ScissorsState | null = null, ): Promise => { // todo return new Promise((resolve, reject) => { resolve({ - points: [], + points, state: null, }); }); From 516d666f68c1587443dc922a6c68e0f2ecf5419a Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 14 Oct 2020 13:57:10 +0300 Subject: [PATCH 07/25] Fixed couple of issues --- .../controls-side-bar/controls-side-bar.tsx | 4 ++-- .../controls-side-bar/opencv-control.tsx | 7 +++---- cvat-ui/src/utils/opencv-wrapper.ts | 4 +++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx index cce0f05a8ade..5a14ae89d32a 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar.tsx @@ -86,7 +86,7 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element { preventDefault(event); const drawing = [ActiveControl.DRAW_POINTS, ActiveControl.DRAW_POLYGON, ActiveControl.DRAW_POLYLINE, ActiveControl.DRAW_RECTANGLE, - ActiveControl.DRAW_CUBOID, ActiveControl.AI_TOOLS].includes(activeControl); + ActiveControl.DRAW_CUBOID, ActiveControl.AI_TOOLS, ActiveControl.OPENCV_TOOLS].includes(activeControl); if (!drawing) { canvasInstance.cancel(); @@ -99,7 +99,7 @@ export default function ControlsSideBarComponent(props: Props): JSX.Element { repeatDrawShape(); } } else { - if (activeControl === ActiveControl.AI_TOOLS) { + if ([ActiveControl.AI_TOOLS, ActiveControl.OPENCV_TOOLS].includes(activeControl)) { // separated API method canvasInstance.interact({ enabled: false }); return; diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx index 0fb57992767a..acd400af229a 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx @@ -203,9 +203,7 @@ class OpenCVControlComponent extends React.PureComponent; type: 'opencv_scissors'; params: { - shape: object; + shape: { + shapeType?: string; + }; canvas: object; }; } From 9c83aed3d31c0650a1b1afb6adfea3b1db69291b Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 14 Oct 2020 14:36:03 +0300 Subject: [PATCH 08/25] Added threshold, changing size via ctrl --- cvat-canvas/src/scss/canvas.scss | 4 ++ cvat-canvas/src/typescript/canvasModel.ts | 1 + cvat-canvas/src/typescript/canvasView.ts | 3 ++ .../src/typescript/interactionHandler.ts | 47 ++++++++++++++++++- .../controls-side-bar/opencv-control.tsx | 14 ++++-- cvat-ui/src/utils/opencv-wrapper.ts | 7 ++- 6 files changed, 70 insertions(+), 6 deletions(-) diff --git a/cvat-canvas/src/scss/canvas.scss b/cvat-canvas/src/scss/canvas.scss index 090ec163f8d3..169663c9b6cd 100644 --- a/cvat-canvas/src/scss/canvas.scss +++ b/cvat-canvas/src/scss/canvas.scss @@ -51,6 +51,10 @@ polyline.cvat_shape_drawing_opacity { stroke: red; } +.cvat_canvas_threshold { + stroke: red; +} + .cvat_canvas_shape_grouping { @extend .cvat_shape_action_dasharray; @extend .cvat_shape_action_opacity; diff --git a/cvat-canvas/src/typescript/canvasModel.ts b/cvat-canvas/src/typescript/canvasModel.ts index 74d9f49cea8f..0a72d62a155a 100644 --- a/cvat-canvas/src/typescript/canvasModel.ts +++ b/cvat-canvas/src/typescript/canvasModel.ts @@ -75,6 +75,7 @@ export interface InteractionData { crosshair?: boolean; minPosVertices?: number; minNegVertices?: number; + withThreshold?: boolean; } export interface InteractionResult { diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 5c1660f2b5d8..9b6b234c2411 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -135,6 +135,7 @@ export class CanvasViewImpl implements CanvasView, Listener { shapes: InteractionResult[] | null, shapesUpdated: boolean = true, isDone: boolean = false, + threshold: number | null = null, ): void { const { zLayer } = this.controller; if (Array.isArray(shapes)) { @@ -146,6 +147,7 @@ export class CanvasViewImpl implements CanvasView, Listener { isDone, shapes, zOrder: zLayer || 0, + threshold, }, }); @@ -918,6 +920,7 @@ export class CanvasViewImpl implements CanvasView, Listener { }); this.content.addEventListener('wheel', (event): void => { + if (event.ctrlKey) return; const { offset } = this.controller.geometry; const point = translateToSVG(this.content, [event.clientX, event.clientY]); self.controller.zoom(point[0] - offset, point[1] - offset, event.deltaY > 0 ? -1 : 1); diff --git a/cvat-canvas/src/typescript/interactionHandler.ts b/cvat-canvas/src/typescript/interactionHandler.ts index 7ede21e3265b..06737dcb0979 100644 --- a/cvat-canvas/src/typescript/interactionHandler.ts +++ b/cvat-canvas/src/typescript/interactionHandler.ts @@ -28,6 +28,8 @@ export class InteractionHandlerImpl implements InteractionHandler { private interactionShapes: SVG.Shape[]; private currentInteractionShape: SVG.Shape | null; private crosshair: Crosshair; + private threshold: SVG.Rect | null; + private thresholdValue: number; private prepareResult(): InteractionResult[] { return this.interactionShapes.map((shape: SVG.Shape): InteractionResult => { @@ -69,6 +71,13 @@ export class InteractionHandlerImpl implements InteractionHandler { return enabled && !ctrlKey && minimumVerticesAchieved && shapesWereUpdated; } + private addThreshold(): void { + const { x, y } = this.cursorPosition; + this.threshold = this.canvas.rect(this.thresholdValue, this.thresholdValue) + .fill('none').addClass('cvat_canvas_threshold'); + this.threshold.center(x, y); + } + private addCrosshair(): void { const { x, y } = this.cursorPosition; this.crosshair.show(this.canvas, x, y, this.geometry.scale); @@ -165,6 +174,10 @@ export class InteractionHandlerImpl implements InteractionHandler { if (this.interactionData.crosshair) { this.addCrosshair(); } + + if (this.interactionData.withThreshold) { + this.addThreshold(); + } } private startInteraction(): void { @@ -182,6 +195,11 @@ export class InteractionHandlerImpl implements InteractionHandler { this.removeCrosshair(); } + if (this.threshold) { + this.threshold.remove(); + this.threshold = null; + } + this.canvas.off('mousedown.interaction'); this.interactionShapes.forEach((shape: SVG.Shape): SVG.Shape => shape.remove()); this.interactionShapes = []; @@ -196,6 +214,7 @@ export class InteractionHandlerImpl implements InteractionHandler { shapes: InteractionResult[] | null, shapesUpdated?: boolean, isDone?: boolean, + threshold?: number, ) => void, canvas: SVG.Container, geometry: Geometry, @@ -206,7 +225,12 @@ export class InteractionHandlerImpl implements InteractionHandler { isDone?: boolean, ): void => { this.shapesWereUpdated = false; - onInteraction(shapes, shapesUpdated, isDone); + onInteraction( + shapes, + shapesUpdated, + isDone, + this.threshold ? this.thresholdValue : null, + ); }; this.canvas = canvas; this.geometry = geometry; @@ -215,6 +239,8 @@ export class InteractionHandlerImpl implements InteractionHandler { this.interactionData = { enabled: false }; this.currentInteractionShape = null; this.crosshair = new Crosshair(); + this.threshold = null; + this.thresholdValue = 100; this.cursorPosition = { x: 0, y: 0, @@ -229,6 +255,25 @@ export class InteractionHandlerImpl implements InteractionHandler { if (this.crosshair) { this.crosshair.move(x, y); } + if (this.threshold) { + this.threshold.center(x, y); + } + }); + + this.canvas.on('wheel.interaction', (e: WheelEvent): void => { + if (e.ctrlKey) { + if (this.threshold) { + const { x, y } = this.cursorPosition; + e.preventDefault(); + if (e.deltaY > 0) { + this.thresholdValue *= 6 / 5; + } else { + this.thresholdValue *= 5 / 6; + } + this.threshold.size(this.thresholdValue, this.thresholdValue); + this.threshold.center(x, y); + } + } }); document.body.addEventListener('keyup', (e: KeyboardEvent): void => { diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx index acd400af229a..451fe3811570 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx @@ -202,12 +202,18 @@ class OpenCVControlComponent extends React.PureComponent Date: Mon, 19 Oct 2020 15:42:05 +0300 Subject: [PATCH 09/25] tmp --- .vscode/launch.json | 2 +- cvat-ui/package.json | 2 +- cvat-ui/webpack.config.js | 1 + cvat/settings/base.py | 4 ++-- cvat/settings/development.py | 2 +- docker-compose.yml | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 48bc45835ce0..c764cd2d75b8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -33,7 +33,7 @@ "runserver", "--noreload", "--insecure", - "127.0.0.1:7000" + "192.168.0.22:7000" ], "django": true, "cwd": "${workspaceFolder}", diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 282289b80812..2515f4e28d4a 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -5,7 +5,7 @@ "main": "src/index.tsx", "scripts": { "build": "webpack --config ./webpack.config.js", - "start": "REACT_APP_API_URL=http://localhost:7000 webpack-dev-server --config ./webpack.config.js --mode=development", + "start": "REACT_APP_API_URL=http://192.168.0.22:7000 webpack-dev-server --config ./webpack.config.js --mode=development", "type-check": "tsc --noEmit", "type-check:watch": "npm run type-check -- --watch", "lint": "eslint './src/**/*.{ts,tsx}'", diff --git a/cvat-ui/webpack.config.js b/cvat-ui/webpack.config.js index 89caff6ed26a..992f36b053e0 100644 --- a/cvat-ui/webpack.config.js +++ b/cvat-ui/webpack.config.js @@ -26,6 +26,7 @@ module.exports = { contentBase: path.join(__dirname, 'dist'), compress: false, inline: true, + host: '192.168.0.22', port: 3000, historyApiFallback: true, }, diff --git a/cvat/settings/base.py b/cvat/settings/base.py index 307570440945..e76e54e98db6 100644 --- a/cvat/settings/base.py +++ b/cvat/settings/base.py @@ -27,8 +27,8 @@ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = str(Path(__file__).parents[2]) -ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', 'localhost,127.0.0.1').split(',') -INTERNAL_IPS = ['127.0.0.1'] +ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', 'localhost,127.0.0.1,192.168.0.22').split(',') +INTERNAL_IPS = ['127.0.0.1', '192.168.0.22'] try: sys.path.append(BASE_DIR) diff --git a/cvat/settings/development.py b/cvat/settings/development.py index 0382d41e3dd6..d8519ef45906 100644 --- a/cvat/settings/development.py +++ b/cvat/settings/development.py @@ -29,7 +29,7 @@ # Cross-Origin Resource Sharing settings for CVAT UI UI_SCHEME = os.environ.get('UI_SCHEME', 'http') -UI_HOST = os.environ.get('UI_HOST', 'localhost') +UI_HOST = os.environ.get('UI_HOST', '192.168.0.22') UI_PORT = os.environ.get('UI_PORT', 3000) CORS_ALLOW_CREDENTIALS = True CSRF_TRUSTED_ORIGINS = [UI_HOST] diff --git a/docker-compose.yml b/docker-compose.yml index 4838cf786706..bf00899852b0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -87,7 +87,7 @@ services: - cvat - cvat_ui environment: - CVAT_HOST: localhost + CVAT_HOST: 192.168.0.22 ports: - "8080:80" volumes: From 6605cb2662d191568380d72db6e8119a28db76e4 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Fri, 23 Oct 2020 20:10:51 +0300 Subject: [PATCH 10/25] Aborted host change --- cvat-ui/webpack.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/cvat-ui/webpack.config.js b/cvat-ui/webpack.config.js index 3aae6d8c9a89..50c13cfee39f 100644 --- a/cvat-ui/webpack.config.js +++ b/cvat-ui/webpack.config.js @@ -28,7 +28,6 @@ module.exports = { contentBase: path.join(__dirname, 'dist'), compress: false, inline: true, - host: '192.168.0.22', port: 3000, historyApiFallback: true, }, From fd7a7673c09b3aaf19d84741b2fce29b38bb312a Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Fri, 23 Oct 2020 20:13:12 +0300 Subject: [PATCH 11/25] Fixed threshold --- cvat-canvas/src/typescript/interactionHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvat-canvas/src/typescript/interactionHandler.ts b/cvat-canvas/src/typescript/interactionHandler.ts index 0fcc585dd155..cb43a8de78c3 100644 --- a/cvat-canvas/src/typescript/interactionHandler.ts +++ b/cvat-canvas/src/typescript/interactionHandler.ts @@ -226,7 +226,7 @@ export class InteractionHandlerImpl implements InteractionHandler { shapes, shapesUpdated, isDone, - this.threshold ? this.thresholdValue : null, + this.threshold ? this.thresholdValue / 2 : null, ); }; this.canvas = canvas; From 29e61214ac8c66e15269a1f2c8849709d6a4b7be Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Sat, 24 Oct 2020 19:42:51 +0300 Subject: [PATCH 12/25] Aborted host --- .vscode/launch.json | 2 +- cvat/settings/base.py | 4 ++-- cvat/settings/development.py | 2 +- docker-compose.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index c764cd2d75b8..48bc45835ce0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -33,7 +33,7 @@ "runserver", "--noreload", "--insecure", - "192.168.0.22:7000" + "127.0.0.1:7000" ], "django": true, "cwd": "${workspaceFolder}", diff --git a/cvat/settings/base.py b/cvat/settings/base.py index e76e54e98db6..307570440945 100644 --- a/cvat/settings/base.py +++ b/cvat/settings/base.py @@ -27,8 +27,8 @@ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = str(Path(__file__).parents[2]) -ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', 'localhost,127.0.0.1,192.168.0.22').split(',') -INTERNAL_IPS = ['127.0.0.1', '192.168.0.22'] +ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', 'localhost,127.0.0.1').split(',') +INTERNAL_IPS = ['127.0.0.1'] try: sys.path.append(BASE_DIR) diff --git a/cvat/settings/development.py b/cvat/settings/development.py index d8519ef45906..0382d41e3dd6 100644 --- a/cvat/settings/development.py +++ b/cvat/settings/development.py @@ -29,7 +29,7 @@ # Cross-Origin Resource Sharing settings for CVAT UI UI_SCHEME = os.environ.get('UI_SCHEME', 'http') -UI_HOST = os.environ.get('UI_HOST', '192.168.0.22') +UI_HOST = os.environ.get('UI_HOST', 'localhost') UI_PORT = os.environ.get('UI_PORT', 3000) CORS_ALLOW_CREDENTIALS = True CSRF_TRUSTED_ORIGINS = [UI_HOST] diff --git a/docker-compose.yml b/docker-compose.yml index 5b7adffa3a80..677177c39325 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -87,7 +87,7 @@ services: - cvat - cvat_ui environment: - CVAT_HOST: 192.168.0.22 + CVAT_HOST: localhost ports: - '8080:80' volumes: From 9c4f80bdcaa7026f18cd3e146bcc20e75dc92d33 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 29 Dec 2020 11:01:33 +0300 Subject: [PATCH 13/25] Some fixes --- .../controls-side-bar/opencv-control.tsx | 145 +++++++----------- 1 file changed, 57 insertions(+), 88 deletions(-) diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx index 451fe3811570..5f0bad65d3ff 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx @@ -8,7 +8,7 @@ import { Row, Col } from 'antd/lib/grid'; import Select, { OptionProps } from 'antd/lib/select'; import Tooltip from 'antd/lib/tooltip'; import Popover from 'antd/lib/popover'; -import Icon from 'antd/lib/icon'; +import Icon, { ScissorOutlined, LoadingOutlined } from '@ant-design/icons'; import Text from 'antd/lib/typography/Text'; import Tabs from 'antd/lib/tabs'; import Button from 'antd/lib/button'; @@ -21,10 +21,7 @@ import { Canvas, convertShapesForInteractor } from 'cvat-canvas-wrapper'; import getCore from 'cvat-core-wrapper'; import openCVWrapper, { Scissors, ScissorsState } from 'utils/opencv-wrapper'; import { - CombinedState, - ActiveControl, - OpenCVTool, - ObjectType, + CombinedState, ActiveControl, OpenCVTool, ObjectType, } from 'reducers/interfaces'; import { interactWithCanvas, @@ -65,22 +62,12 @@ function mapStateToProps(state: CombinedState): Props { annotation: { annotations: { states, - zLayer: { - cur: curZOrder, - }, - }, - job: { - instance: jobInstance, - labels, - }, - canvas: { - activeControl, - instance: canvasInstance, + zLayer: { cur: curZOrder }, }, + job: { instance: jobInstance, labels }, + canvas: { activeControl, instance: canvasInstance }, player: { - frame: { - number: frame, - }, + frame: { number: frame }, }, }, } = state; @@ -103,7 +90,6 @@ const mapDispatchToProps = { createAnnotations: createAnnotationsAsync, }; - class OpenCVControlComponent extends React.PureComponent { private activeTool: Scissors | null; private toolState: ScissorsState | null; @@ -154,19 +140,13 @@ class OpenCVControlComponent extends React.PureComponent ( - _state.clientID === this.interactiveStateID - ))[0] || null; + return states.filter((_state: any): boolean => _state.clientID === this.interactiveStateID)[0] || null; } private cancelListener = async (): Promise => { const { processing } = this.state; const { - fetchAnnotations, - isActivated, - jobInstance, - frame, + fetchAnnotations, isActivated, jobInstance, frame, } = this.props; if (isActivated) { @@ -189,13 +169,7 @@ class OpenCVControlComponent extends React.PureComponent => { const { - fetchAnnotations, - updateAnnotations, - isActivated, - jobInstance, - frame, - labels, - curZOrder, + fetchAnnotations, updateAnnotations, isActivated, jobInstance, frame, labels, curZOrder, } = this.props; const { activeLabelID, processing } = this.state; if (!isActivated || !this.activeTool) { @@ -203,10 +177,7 @@ class OpenCVControlComponent extends React.PureComponent label.id === activeLabelID)[0], + label: labels.filter((label: any) => label.id === activeLabelID)[0], points, occluded: false, zOrder: curZOrder, @@ -325,37 +296,35 @@ class OpenCVControlComponent extends React.PureComponent - + - + @@ -384,23 +353,23 @@ class OpenCVControlComponent extends React.PureComponent - + - OpenCV.js + + OpenCV.js + - { libraryInitialized ? ( + {libraryInitialized ? ( - { this.renderDrawingContent() } - - - + {this.renderDrawingContent()} + ) : ( <> - + = 0 ? 17 : 24}>