From 0cf3978ead40b97799f7cb637aac0e6daee27c56 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 31 Mar 2020 14:04:18 +0300 Subject: [PATCH 01/30] Fixed escape in draw --- cvat-canvas/src/typescript/drawHandler.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cvat-canvas/src/typescript/drawHandler.ts b/cvat-canvas/src/typescript/drawHandler.ts index 4afe75ec..f6c1d947 100644 --- a/cvat-canvas/src/typescript/drawHandler.ts +++ b/cvat-canvas/src/typescript/drawHandler.ts @@ -50,6 +50,7 @@ export class DrawHandlerImpl implements DrawHandler { // so, methods like draw() just undefined for SVG.Shape, but nevertheless they exist private drawInstance: any; private initialized: boolean; + private canceled: boolean; private pointsGroup: SVG.G | null; private shapeSizeElement: ShapeSizeElement; @@ -149,6 +150,7 @@ export class DrawHandlerImpl implements DrawHandler { // Clear drawing this.drawInstance.draw('stop'); } + this.drawInstance.off(); this.drawInstance.remove(); this.drawInstance = null; @@ -161,6 +163,7 @@ export class DrawHandlerImpl implements DrawHandler { if (this.crosshair) { this.removeCrosshair(); } + this.onDrawDone(null); } private initDrawing(): void { @@ -175,8 +178,9 @@ export class DrawHandlerImpl implements DrawHandler { const bbox = (e.target as SVGRectElement).getBBox(); const [xtl, ytl, xbr, ybr] = this.getFinalRectCoordinates(bbox); const { shapeType } = this.drawData; - this.cancel(); + this.release(); + if (this.canceled) return; if ((xbr - xtl) * (ybr - ytl) >= consts.AREA_THRESHOLD) { this.onDrawDone({ shapeType, @@ -290,11 +294,11 @@ export class DrawHandlerImpl implements DrawHandler { this.drawInstance.on('drawdone', (e: CustomEvent): void => { const targetPoints = pointsToArray((e.target as SVGElement).getAttribute('points')); - const { points, box } = this.getFinalPolyshapeCoordinates(targetPoints); const { shapeType } = this.drawData; - this.cancel(); + this.release(); + if (this.canceled) return; if (shapeType === 'polygon' && ((box.xbr - box.xtl) * (box.ybr - box.ytl) >= consts.AREA_THRESHOLD) && points.length >= 3 * 2) { @@ -598,6 +602,7 @@ export class DrawHandlerImpl implements DrawHandler { this.canvas = canvas; this.text = text; this.initialized = false; + this.canceled = false; this.drawData = null; this.geometry = null; this.crosshair = null; @@ -671,17 +676,18 @@ export class DrawHandlerImpl implements DrawHandler { this.geometry = geometry; if (drawData.enabled) { + this.canceled = false; this.drawData = drawData; this.initDrawing(); this.startDraw(); } else { - this.cancel(); + this.release(); this.drawData = drawData; } } public cancel(): void { + this.canceled = true; this.release(); - this.onDrawDone(null); } } From 2e6a92afa4fd52c531678bcb2f4cb9f40d1c244f Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 31 Mar 2020 14:09:47 +0300 Subject: [PATCH 02/30] Insert multiple shapes --- cvat-canvas/src/typescript/drawHandler.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cvat-canvas/src/typescript/drawHandler.ts b/cvat-canvas/src/typescript/drawHandler.ts index f6c1d947..a2c401c5 100644 --- a/cvat-canvas/src/typescript/drawHandler.ts +++ b/cvat-canvas/src/typescript/drawHandler.ts @@ -163,7 +163,10 @@ export class DrawHandlerImpl implements DrawHandler { if (this.crosshair) { this.removeCrosshair(); } - this.onDrawDone(null); + + if (!this.drawData.initialState) { + this.onDrawDone(null); + } } private initDrawing(): void { From a7f02df7d07842f1cd4f00fa41ac5daa0437f28c Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 31 Mar 2020 16:11:35 +0300 Subject: [PATCH 03/30] Added dialog window with some help info about filters --- cvat-core/src/session.js | 5 +- .../annotations-filters-input.tsx | 110 ++++++++++++++++-- 2 files changed, 106 insertions(+), 9 deletions(-) diff --git a/cvat-core/src/session.js b/cvat-core/src/session.js index 987b3fae..548dd191 100644 --- a/cvat-core/src/session.js +++ b/cvat-core/src/session.js @@ -441,7 +441,7 @@ * Returns the ranges of cached frames * @method ranges * @memberof Session.frames - * @returns {Array{string}} + * @returns {Array.} * @instance * @async */ @@ -520,7 +520,8 @@ * @returns {HistoryActions} * @throws {module:API.cvat.exceptions.PluginError} * @throws {module:API.cvat.exceptions.ArgumentError} - * @returns {[string, number][]} array of pairs [action name, frame number] + * @returns {Array.>} + * array of pairs [action name, frame number] * @instance * @async */ diff --git a/cvat-ui/src/components/annotation-page/annotations-filters-input.tsx b/cvat-ui/src/components/annotation-page/annotations-filters-input.tsx index 39ff37b6..b6e98198 100644 --- a/cvat-ui/src/components/annotation-page/annotations-filters-input.tsx +++ b/cvat-ui/src/components/annotation-page/annotations-filters-input.tsx @@ -2,9 +2,14 @@ // // SPDX-License-Identifier: MIT -import React from 'react'; +import React, { useState } from 'react'; import { connect } from 'react-redux'; import Select, { SelectValue, LabeledValue } from 'antd/lib/select'; +import Title from 'antd/lib/typography/Title'; +import Text from 'antd/lib/typography/Text'; +import Paragraph from 'antd/lib/typography/Paragraph'; +import Tooltip from 'antd/lib/tooltip'; +import Modal from 'antd/lib/modal'; import Icon from 'antd/lib/icon'; import { @@ -16,6 +21,8 @@ import { CombinedState } from 'reducers/interfaces'; interface StateToProps { annotationsFilters: string[]; annotationsFiltersHistory: string[]; + searchForwardShortcut: string; + searchBackwardShortcut: string; } interface DispatchToProps { @@ -30,11 +37,16 @@ function mapStateToProps(state: CombinedState): StateToProps { filtersHistory: annotationsFiltersHistory, }, }, + shortcuts: { + normalizedKeyMap, + }, } = state; return { annotationsFilters, annotationsFiltersHistory, + searchForwardShortcut: normalizedKeyMap.SEARCH_FORWARD, + searchBackwardShortcut: normalizedKeyMap.SEARCH_BACKWARD, }; } @@ -56,13 +68,74 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { }; } +function filtersHelpModalContent( + searchForwardShortcut: string, + searchBackwardShortcut: string, +): JSX.Element { + return ( + <> + + General + + + You can use filters to display only subset of objects on a frame + or to search objects that satisfy the filters using hotkeys + + {` ${searchForwardShortcut} `} + + and + + {` ${searchBackwardShortcut} `} + + + + Supported properties: + width, height, label, serverID, clientID, type, shape, occluded +
+ Supported operators: + ==, !=, >, >=, <, <=, ~=, (), & and | +
+ + If you have double quotes in your query string, + please escape them using back slash: \" (see the latest example) + +
+ All properties and values are case-sensitive. + CVAT uses json queries to perform search. +
+ + Examples +
    +
  • label=="car" | label==["road sign"]
  • +
  • width >= height
  • +
  • attr["Attribute 1"] == attr["Attribute 2"]
  • +
  • clientID == 50
  • +
  • + (label=="car" & attr["parked"]==true) + | (label=="pedestrian" & width > 150) +
  • +
  • + (( label==["car \"mazda\""]) + & (attr["sunglasses"]==true + | (width > 150 | height > 150 & (clientID == serverID))))) +
  • +
+
+ + ); +} + function AnnotationsFiltersInput(props: StateToProps & DispatchToProps): JSX.Element { const { annotationsFilters, annotationsFiltersHistory, + searchForwardShortcut, + searchBackwardShortcut, changeAnnotationsFilters, } = props; + const [underCursor, setUnderCursor] = useState(false); + return ( @@ -71,7 +75,9 @@ function renderInputElement(parameters: InputElementParameters): JSX.Element { )} > {values.map((value: string): JSX.Element => ( - {value} + + {value === consts.UNDEFINED_ATTRIBUTE_VALUE ? '\u00a0' : value} + ))} @@ -193,7 +199,9 @@ function renderList(parameters: ListParameters): JSX.Element | null { [key: string]: (keyEvent?: KeyboardEvent) => void; } = {}; - values.slice(0, 10).forEach((value: string, index: number): void => { + const filteredValues = values + .filter((value: string): boolean => value !== consts.UNDEFINED_ATTRIBUTE_VALUE); + filteredValues.slice(0, 10).forEach((value: string, index: number): void => { const key = `SET_${index}_VALUE`; keyMap[key] = { name: `Set value "${value}"`, @@ -214,7 +222,7 @@ function renderList(parameters: ListParameters): JSX.Element | null { return (
- {values.map((value: string, index: number): JSX.Element => ( + {filteredValues.map((value: string, index: number): JSX.Element => (
{`${index}:`} {` ${value}`} diff --git a/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/styles.scss b/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/styles.scss index 1f8de841..231cc13b 100644 --- a/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/styles.scss +++ b/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/styles.scss @@ -48,9 +48,7 @@ margin: 10px 0px 10px 10px; } - .attribute-annotation-sidebar-attr-elem-wrapper { - display: inline-block; width: 60%; } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx index fcf0fe38..efd81d67 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx @@ -20,7 +20,7 @@ import Text from 'antd/lib/typography/Text'; import Tooltip from 'antd/lib/tooltip'; import ColorChanger from 'components/annotation-page/standard-workspace/objects-side-bar/color-changer'; - +import consts from 'consts'; import { ObjectOutsideIcon, FirstIcon, @@ -30,10 +30,10 @@ import { BackgroundIcon, ForegroundIcon, } from 'icons'; - import { ObjectType, ShapeType } from 'reducers/interfaces'; import { clamp } from 'utils/math'; + function ItemMenu( serverID: number | undefined, locked: boolean, @@ -508,7 +508,9 @@ function ItemAttributeComponent(props: ItemAttributeComponentProps): JSX.Element }} > { attrValues.map((value: string): JSX.Element => ( - {value} + + {value === consts.UNDEFINED_ATTRIBUTE_VALUE ? '\u00a0' : value} + )) } @@ -534,7 +536,9 @@ function ItemAttributeComponent(props: ItemAttributeComponentProps): JSX.Element className='cvat-object-item-select-attribute' > { attrValues.map((value: string): JSX.Element => ( - {value} + + {value === consts.UNDEFINED_ATTRIBUTE_VALUE ? '\u00a0' : value} + )) } diff --git a/cvat-ui/src/consts.ts b/cvat-ui/src/consts.ts new file mode 100644 index 00000000..666dc98d --- /dev/null +++ b/cvat-ui/src/consts.ts @@ -0,0 +1,9 @@ +// Copyright (C) 2019-2020 Intel Corporation +// +// SPDX-License-Identifier: MIT + +const UNDEFINED_ATTRIBUTE_VALUE = '__undefined__'; + +export default { + UNDEFINED_ATTRIBUTE_VALUE, +}; diff --git a/cvat-ui/src/utils/enviroment.ts b/cvat-ui/src/utils/enviroment.ts index e1f01755..137e9dd1 100644 --- a/cvat-ui/src/utils/enviroment.ts +++ b/cvat-ui/src/utils/enviroment.ts @@ -2,6 +2,6 @@ // // SPDX-License-Identifier: MIT -export default function isDev() { +export default function isDev(): boolean { return process.env.NODE_ENV === 'development'; } From 5b8ee72d4f32b318c540b6a5d7483d9af5089d31 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 1 Apr 2020 00:27:55 +0300 Subject: [PATCH 09/30] Fixed license year --- cvat-canvas/src/typescript/consts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvat-canvas/src/typescript/consts.ts b/cvat-canvas/src/typescript/consts.ts index 23a36f11..84b89940 100644 --- a/cvat-canvas/src/typescript/consts.ts +++ b/cvat-canvas/src/typescript/consts.ts @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2020 Intel Corporation +// Copyright (C) 2020 Intel Corporation // // SPDX-License-Identifier: MIT From 10c300605f6ef8fc3c786a4bb4bd5423aebbc479 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 1 Apr 2020 00:30:29 +0300 Subject: [PATCH 10/30] No break space const --- .../attribute-annotation-sidebar/attribute-editor.tsx | 6 ++++-- .../standard-workspace/objects-side-bar/object-item.tsx | 6 ++++-- cvat-ui/src/consts.ts | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-editor.tsx b/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-editor.tsx index 50cec85a..6917e42f 100644 --- a/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-editor.tsx +++ b/cvat-ui/src/components/annotation-page/attribute-annotation-workspace/attribute-annotation-sidebar/attribute-editor.tsx @@ -56,7 +56,8 @@ function renderInputElement(parameters: InputElementParameters): JSX.Element { > {values.map((value: string): JSX.Element => ( - {value === consts.UNDEFINED_ATTRIBUTE_VALUE ? '\u00a0' : value} + {value === consts.UNDEFINED_ATTRIBUTE_VALUE + ? consts.NO_BREAK_SPACE : value} ))} @@ -76,7 +77,8 @@ function renderInputElement(parameters: InputElementParameters): JSX.Element { > {values.map((value: string): JSX.Element => ( - {value === consts.UNDEFINED_ATTRIBUTE_VALUE ? '\u00a0' : value} + {value === consts.UNDEFINED_ATTRIBUTE_VALUE + ? consts.NO_BREAK_SPACE : value} ))} diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx index efd81d67..2a43e042 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx @@ -509,7 +509,8 @@ function ItemAttributeComponent(props: ItemAttributeComponentProps): JSX.Element > { attrValues.map((value: string): JSX.Element => ( - {value === consts.UNDEFINED_ATTRIBUTE_VALUE ? '\u00a0' : value} + {value === consts.UNDEFINED_ATTRIBUTE_VALUE + ? consts.NO_BREAK_SPACE : value} )) } @@ -537,7 +538,8 @@ function ItemAttributeComponent(props: ItemAttributeComponentProps): JSX.Element > { attrValues.map((value: string): JSX.Element => ( - {value === consts.UNDEFINED_ATTRIBUTE_VALUE ? '\u00a0' : value} + {value === consts.UNDEFINED_ATTRIBUTE_VALUE + ? consts.NO_BREAK_SPACE : value} )) } diff --git a/cvat-ui/src/consts.ts b/cvat-ui/src/consts.ts index 666dc98d..aceacc0f 100644 --- a/cvat-ui/src/consts.ts +++ b/cvat-ui/src/consts.ts @@ -3,7 +3,9 @@ // SPDX-License-Identifier: MIT const UNDEFINED_ATTRIBUTE_VALUE = '__undefined__'; +const NO_BREAK_SPACE = '\u00a0'; export default { UNDEFINED_ATTRIBUTE_VALUE, + NO_BREAK_SPACE, }; From c91fa277ce2c33e6cb3b59241c1e65e5902aa299 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 1 Apr 2020 00:34:59 +0300 Subject: [PATCH 11/30] Updated changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f15c960..726e5775 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.0.0-alpha] - Unreleased ### Added -- +- Special behaviour for attribute value ``__undefined__`` (invisibility, no shortcuts to be set in AAM) ### Changed - From 5e092cd932be32bf49ad408e65dc8f8f0f02f85f Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Wed, 1 Apr 2020 00:36:21 +0300 Subject: [PATCH 12/30] Fixed year in license headers --- cvat-canvas/src/typescript/consts.ts | 2 +- cvat-ui/src/consts.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cvat-canvas/src/typescript/consts.ts b/cvat-canvas/src/typescript/consts.ts index 84b89940..23a36f11 100644 --- a/cvat-canvas/src/typescript/consts.ts +++ b/cvat-canvas/src/typescript/consts.ts @@ -1,4 +1,4 @@ -// Copyright (C) 2020 Intel Corporation +// Copyright (C) 2019-2020 Intel Corporation // // SPDX-License-Identifier: MIT diff --git a/cvat-ui/src/consts.ts b/cvat-ui/src/consts.ts index aceacc0f..b5942c32 100644 --- a/cvat-ui/src/consts.ts +++ b/cvat-ui/src/consts.ts @@ -1,4 +1,4 @@ -// Copyright (C) 2019-2020 Intel Corporation +// Copyright (C) 2020 Intel Corporation // // SPDX-License-Identifier: MIT From c842c25e78a55ddf75d13054e63627899514b05b Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 2 Apr 2020 13:32:03 +0300 Subject: [PATCH 13/30] Implementation of bitmap in client --- cvat-canvas/README.md | 2 + cvat-canvas/src/scss/canvas.scss | 10 +++ cvat-canvas/src/typescript/canvas.ts | 5 ++ cvat-canvas/src/typescript/canvasModel.ts | 16 ++++- cvat-canvas/src/typescript/canvasView.ts | 65 +++++++++++++++++-- cvat-ui/src/actions/settings-actions.ts | 10 +++ .../standard-workspace/canvas-wrapper.tsx | 8 +++ .../objects-side-bar/appearance-block.tsx | 10 +++ .../objects-side-bar/objects-side-bar.tsx | 6 ++ .../objects-side-bar/styles.scss | 9 +++ .../standard-workspace/canvas-wrapper.tsx | 3 + .../objects-side-bar/objects-side-bar.tsx | 16 +++++ cvat-ui/src/reducers/interfaces.ts | 1 + cvat-ui/src/reducers/settings-reducer.ts | 10 +++ 14 files changed, 166 insertions(+), 5 deletions(-) diff --git a/cvat-canvas/README.md b/cvat-canvas/README.md index 7fe60491..dbf85b8c 100644 --- a/cvat-canvas/README.md +++ b/cvat-canvas/README.md @@ -100,6 +100,7 @@ Canvas itself handles: select(objectState: any): void; fitCanvas(): void; + bitmap(enabled: boolean): void; dragCanvas(enable: boolean): void; zoomCanvas(enable: boolean): void; @@ -189,4 +190,5 @@ Standard JS events are used. | dragCanvas() | + | - | - | - | - | - | + | - | | zoomCanvas() | + | - | - | - | - | - | - | + | | cancel() | - | + | + | + | + | + | + | + | +| bitmap() | + | + | + | + | + | + | + | + | | setZLayer() | + | + | + | + | + | + | + | + | diff --git a/cvat-canvas/src/scss/canvas.scss b/cvat-canvas/src/scss/canvas.scss index d46edb0e..fab01d0b 100644 --- a/cvat-canvas/src/scss/canvas.scss +++ b/cvat-canvas/src/scss/canvas.scss @@ -152,6 +152,16 @@ polyline.cvat_canvas_shape_splitting { box-shadow: 2px 2px 5px 0px rgba(0,0,0,0.75); } +#cvat_canvas_bitmap { + pointer-events: none; + position: absolute; + z-index: 4; + background: black; + width: 100%; + height: 100%; + box-shadow: 2px 2px 5px 0px rgba(0,0,0,0.75); +} + #cvat_canvas_grid { position: absolute; z-index: 2; diff --git a/cvat-canvas/src/typescript/canvas.ts b/cvat-canvas/src/typescript/canvas.ts index eef1de40..bb169e38 100644 --- a/cvat-canvas/src/typescript/canvas.ts +++ b/cvat-canvas/src/typescript/canvas.ts @@ -49,6 +49,7 @@ interface Canvas { select(objectState: any): void; fitCanvas(): void; + bitmap(enable: boolean): void; dragCanvas(enable: boolean): void; zoomCanvas(enable: boolean): void; @@ -86,6 +87,10 @@ class CanvasImpl implements Canvas { ); } + public bitmap(enable: boolean): void { + this.model.bitmap(enable); + } + public dragCanvas(enable: boolean): void { this.model.dragCanvas(enable); } diff --git a/cvat-canvas/src/typescript/canvasModel.ts b/cvat-canvas/src/typescript/canvasModel.ts index 66a2717b..87e56092 100644 --- a/cvat-canvas/src/typescript/canvasModel.ts +++ b/cvat-canvas/src/typescript/canvasModel.ts @@ -98,8 +98,9 @@ export enum UpdateReasons { GROUP = 'group', SELECT = 'select', CANCEL = 'cancel', + BITMAP = 'bitmap', DRAG_CANVAS = 'drag_canvas', - ZOOM_CANVAS = 'ZOOM_CANVAS', + ZOOM_CANVAS = 'zoom_canvas', } export enum Mode { @@ -116,6 +117,7 @@ export enum Mode { } export interface CanvasModel { + readonly imageBitmap: boolean; readonly image: Image | null; readonly objects: any[]; readonly zLayer: number | null; @@ -148,6 +150,7 @@ export interface CanvasModel { select(objectState: any): void; fitCanvas(width: number, height: number): void; + bitmap(enabled: boolean): void; dragCanvas(enable: boolean): void; zoomCanvas(enable: boolean): void; @@ -159,6 +162,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { activeElement: ActiveElement; angle: number; canvasSize: Size; + imageBitmap: boolean; image: Image | null; imageID: number | null; imageOffset: number; @@ -191,6 +195,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { height: 0, width: 0, }, + imageBitmap: false, image: null, imageID: null, imageOffset: 0, @@ -277,6 +282,11 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { this.notify(UpdateReasons.OBJECTS_UPDATED); } + public bitmap(enabled: boolean): void { + this.data.imageBitmap = enabled; + this.notify(UpdateReasons.BITMAP); + } + public dragCanvas(enable: boolean): void { if (enable && this.data.mode !== Mode.IDLE) { throw Error(`Canvas is busy. Action: ${this.data.mode}`); @@ -522,6 +532,10 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { return this.data.zLayer; } + public get imageBitmap(): boolean { + return this.data.imageBitmap; + } + public get image(): Image | null { return this.data.image; } diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 5d614948..487136a7 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -47,6 +47,7 @@ export class CanvasViewImpl implements CanvasView, Listener { private text: SVGSVGElement; private adoptedText: SVG.Container; private background: HTMLCanvasElement; + private bitmap: HTMLCanvasElement; private grid: SVGSVGElement; private content: SVGSVGElement; private adoptedContent: SVG.Container; @@ -285,7 +286,7 @@ export class CanvasViewImpl implements CanvasView, Listener { } private moveCanvas(): void { - for (const obj of [this.background, this.grid]) { + for (const obj of [this.background, this.grid, this.bitmap]) { obj.style.top = `${this.geometry.top}px`; obj.style.left = `${this.geometry.left}px`; } @@ -303,7 +304,7 @@ export class CanvasViewImpl implements CanvasView, Listener { private transformCanvas(): void { // Transform canvas - for (const obj of [this.background, this.grid, this.content]) { + for (const obj of [this.background, this.grid, this.content, this.bitmap]) { obj.style.transform = `scale(${this.geometry.scale}) rotate(${this.geometry.angle}deg)`; } @@ -358,7 +359,7 @@ export class CanvasViewImpl implements CanvasView, Listener { } private resizeCanvas(): void { - for (const obj of [this.background, this.grid]) { + for (const obj of [this.background, this.grid, this.bitmap]) { obj.style.width = `${this.geometry.image.width}px`; obj.style.height = `${this.geometry.image.height}px`; } @@ -546,6 +547,7 @@ export class CanvasViewImpl implements CanvasView, Listener { this.text = window.document.createElementNS('http://www.w3.org/2000/svg', 'svg'); this.adoptedText = (SVG.adopt((this.text as any as HTMLElement)) as SVG.Container); this.background = window.document.createElement('canvas'); + this.bitmap = window.document.createElement('canvas'); // window.document.createElementNS('http://www.w3.org/2000/svg', 'svg'); this.grid = window.document.createElementNS('http://www.w3.org/2000/svg', 'svg'); @@ -590,6 +592,8 @@ export class CanvasViewImpl implements CanvasView, Listener { this.text.setAttribute('id', 'cvat_canvas_text_content'); this.background.setAttribute('id', 'cvat_canvas_background'); this.content.setAttribute('id', 'cvat_canvas_content'); + this.bitmap.setAttribute('id', 'cvat_canvas_bitmap'); + this.bitmap.style.display = 'none'; // Setup wrappers this.canvas.setAttribute('id', 'cvat_canvas_wrapper'); @@ -605,6 +609,7 @@ export class CanvasViewImpl implements CanvasView, Listener { this.canvas.appendChild(this.loadingAnimation); this.canvas.appendChild(this.text); this.canvas.appendChild(this.background); + this.canvas.appendChild(this.bitmap); this.canvas.appendChild(this.grid); this.canvas.appendChild(this.content); @@ -702,7 +707,16 @@ export class CanvasViewImpl implements CanvasView, Listener { public notify(model: CanvasModel & Master, reason: UpdateReasons): void { this.geometry = this.controller.geometry; - if (reason === UpdateReasons.IMAGE_CHANGED) { + + if (reason === UpdateReasons.BITMAP) { + const { imageBitmap } = model; + if (imageBitmap) { + this.bitmap.style.display = ''; + this.redrawBitmap(); + } else { + this.bitmap.style.display = 'none'; + } + } else if (reason === UpdateReasons.IMAGE_CHANGED) { const { image } = model; if (!image) { this.loadingAnimation.classList.remove('cvat_canvas_hidden'); @@ -875,12 +889,55 @@ export class CanvasViewImpl implements CanvasView, Listener { this.mode = Mode.IDLE; this.canvas.style.cursor = ''; } + + if (model.imageBitmap + && [UpdateReasons.IMAGE_CHANGED, UpdateReasons.OBJECTS_UPDATED].includes(reason) + ) { + this.redrawBitmap(); + } } public html(): HTMLDivElement { return this.canvas; } + private redrawBitmap(): void { + const width = +this.background.style.width.slice(0, -2); + const height = +this.background.style.height.slice(0, -2); + this.bitmap.setAttribute('width', `${width}px`); + this.bitmap.setAttribute('height', `${height}px`); + const states = this.controller.objects; + + const ctx = this.bitmap.getContext('2d'); + if (ctx) { + ctx.fillStyle = 'black'; + ctx.fillRect(0, 0, width, height); + for (const state of states) { + ctx.fillStyle = 'white'; + if (['rectangle', 'polygon'].includes(state.shapeType)) { + const points = state.shapeType === 'rectangle' ? [ + state.points[0], // xtl + state.points[1], // ytl + state.points[2], // xbr + state.points[1], // ytl + state.points[2], // xbr + state.points[3], // ybr + state.points[0], // xtl + state.points[3], // ybr + ] : state.points; + ctx.beginPath(); + ctx.moveTo(points[0], points[1]); + for (let i = 0; i < points.length; i += 2) { + ctx.lineTo(points[i], points[i + 1]); + } + ctx.closePath(); + } + + ctx.fill(); + } + } + } + private saveState(state: any): void { this.drawnStates[state.clientID] = { clientID: state.clientID, diff --git a/cvat-ui/src/actions/settings-actions.ts b/cvat-ui/src/actions/settings-actions.ts index c14c2db2..3cb18f56 100644 --- a/cvat-ui/src/actions/settings-actions.ts +++ b/cvat-ui/src/actions/settings-actions.ts @@ -18,6 +18,7 @@ export enum SettingsActionTypes { CHANGE_SELECTED_SHAPES_OPACITY = 'CHANGE_SELECTED_SHAPES_OPACITY', CHANGE_SHAPES_COLOR_BY = 'CHANGE_SHAPES_COLOR_BY', CHANGE_SHAPES_BLACK_BORDERS = 'CHANGE_SHAPES_BLACK_BORDERS', + CHANGE_SHOW_UNLABELED_REGIONS = 'CHANGE_SHOW_UNLABELED_REGIONS', CHANGE_FRAME_STEP = 'CHANGE_FRAME_STEP', CHANGE_FRAME_SPEED = 'CHANGE_FRAME_SPEED', SWITCH_RESET_ZOOM = 'SWITCH_RESET_ZOOM', @@ -66,6 +67,15 @@ export function changeShapesBlackBorders(blackBorders: boolean): AnyAction { }; } +export function changeShowBitmap(showBitmap: boolean): AnyAction { + return { + type: SettingsActionTypes.CHANGE_SHOW_UNLABELED_REGIONS, + payload: { + showBitmap, + }, + }; +} + export function switchRotateAll(rotateAll: boolean): AnyAction { return { type: SettingsActionTypes.SWITCH_ROTATE_ALL, diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx index 038157e2..dbd24fee 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx @@ -42,6 +42,7 @@ interface Props { colorBy: ColorBy; selectedOpacity: number; blackBorders: boolean; + showBitmap: boolean; grid: boolean; gridSize: number; gridColor: GridColor; @@ -112,6 +113,7 @@ export default class CanvasWrapperComponent extends React.PureComponent { colorBy, selectedOpacity, blackBorders, + showBitmap, frameData, frameAngle, annotations, @@ -198,6 +200,10 @@ export default class CanvasWrapperComponent extends React.PureComponent { this.updateShapesView(); } + if (prevProps.showBitmap !== showBitmap) { + canvasInstance.bitmap(showBitmap); + } + if (prevProps.frameAngle !== frameAngle) { canvasInstance.rotate(frameAngle); } @@ -557,6 +563,7 @@ export default class CanvasWrapperComponent extends React.PureComponent { for (const state of annotations) { let shapeColor = ''; + if (colorBy === ColorBy.INSTANCE) { shapeColor = state.color; } else if (colorBy === ColorBy.GROUP) { @@ -572,6 +579,7 @@ export default class CanvasWrapperComponent extends React.PureComponent { if (handler && handler.nested) { handler.nested.fill({ color: shapeColor }); } + (shapeView as any).instance.fill({ color: shapeColor, opacity: opacity / 100 }); (shapeView as any).instance.stroke({ color: blackBorders ? 'black' : shapeColor }); } diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/appearance-block.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/appearance-block.tsx index 56c5b5ac..d3ed0f03 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/appearance-block.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/appearance-block.tsx @@ -24,12 +24,14 @@ interface Props { opacity: number; selectedOpacity: number; blackBorders: boolean; + showBitmap: boolean; collapseAppearance(): void; changeShapesColorBy(event: RadioChangeEvent): void; changeShapesOpacity(event: SliderValue): void; changeSelectedShapesOpacity(event: SliderValue): void; changeShapesBlackBorders(event: CheckboxChangeEvent): void; + changeShowBitmap(event: CheckboxChangeEvent): void; } function AppearanceBlock(props: Props): JSX.Element { @@ -39,11 +41,13 @@ function AppearanceBlock(props: Props): JSX.Element { opacity, selectedOpacity, blackBorders, + showBitmap, collapseAppearance, changeShapesColorBy, changeShapesOpacity, changeSelectedShapesOpacity, changeShapesBlackBorders, + changeShowBitmap, } = props; return ( @@ -85,6 +89,12 @@ function AppearanceBlock(props: Props): JSX.Element { > Black borders + + Show bitmap +
diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx index 1a8cd5bd..b173d870 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx @@ -29,6 +29,7 @@ interface Props { opacity: number; selectedOpacity: number; blackBorders: boolean; + showBitmap: boolean; collapseSidebar(): void; collapseAppearance(): void; @@ -37,6 +38,7 @@ interface Props { changeShapesOpacity(event: SliderValue): void; changeSelectedShapesOpacity(event: SliderValue): void; changeShapesBlackBorders(event: CheckboxChangeEvent): void; + changeShowBitmap(event: CheckboxChangeEvent): void; } function ObjectsSideBar(props: Props): JSX.Element { @@ -47,12 +49,14 @@ function ObjectsSideBar(props: Props): JSX.Element { opacity, selectedOpacity, blackBorders, + showBitmap, collapseSidebar, collapseAppearance, changeShapesColorBy, changeShapesOpacity, changeSelectedShapesOpacity, changeShapesBlackBorders, + changeShowBitmap, } = props; const appearanceProps = { @@ -62,11 +66,13 @@ function ObjectsSideBar(props: Props): JSX.Element { opacity, selectedOpacity, blackBorders, + showBitmap, changeShapesColorBy, changeShapesOpacity, changeSelectedShapesOpacity, changeShapesBlackBorders, + changeShowBitmap, }; return ( diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss index 07f3c0f6..51ca31fd 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/styles.scss @@ -23,6 +23,11 @@ background: $background-color-2; border-bottom: none; height: 230px; + + > .ant-collapse-content-box { + padding: 10px; + } + } } } @@ -254,6 +259,10 @@ width: 33%; } } + + .ant-checkbox-wrapper { + margin-left: 0px; + } } .cvat-object-item-menu { diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx index e5b74d9b..4d68d9c4 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx @@ -62,6 +62,7 @@ interface StateToProps { colorBy: ColorBy; selectedOpacity: number; blackBorders: boolean; + showBitmap: boolean; grid: boolean; gridSize: number; gridColor: GridColor; @@ -169,6 +170,7 @@ function mapStateToProps(state: CombinedState): StateToProps { colorBy, selectedOpacity, blackBorders, + showBitmap, }, }, shortcuts: { @@ -192,6 +194,7 @@ function mapStateToProps(state: CombinedState): StateToProps { colorBy, selectedOpacity, blackBorders, + showBitmap, grid, gridSize, gridColor, diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx index bb6162e9..309700cc 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/objects-side-bar.tsx @@ -27,6 +27,7 @@ import { changeShapesOpacity as changeShapesOpacityAction, changeSelectedShapesOpacity as changeSelectedShapesOpacityAction, changeShapesBlackBorders as changeShapesBlackBordersAction, + changeShowBitmap as changeShowUnlabeledRegionsAction, } from 'actions/settings-actions'; @@ -37,6 +38,7 @@ interface StateToProps { opacity: number; selectedOpacity: number; blackBorders: boolean; + showBitmap: boolean; } interface DispatchToProps { @@ -47,6 +49,7 @@ interface DispatchToProps { changeShapesOpacity(shapesOpacity: number): void; changeSelectedShapesOpacity(selectedShapesOpacity: number): void; changeShapesBlackBorders(blackBorders: boolean): void; + changeShowBitmap(showBitmap: boolean): void; } function mapStateToProps(state: CombinedState): StateToProps { @@ -61,6 +64,7 @@ function mapStateToProps(state: CombinedState): StateToProps { opacity, selectedOpacity, blackBorders, + showBitmap, }, }, } = state; @@ -72,6 +76,7 @@ function mapStateToProps(state: CombinedState): StateToProps { opacity, selectedOpacity, blackBorders, + showBitmap, }; } @@ -132,6 +137,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { changeShapesBlackBorders(blackBorders: boolean): void { dispatch(changeShapesBlackBordersAction(blackBorders)); }, + changeShowBitmap(showBitmap: boolean) { + dispatch(changeShowUnlabeledRegionsAction(showBitmap)); + }, }; } @@ -177,6 +185,11 @@ class ObjectsSideBarContainer extends React.PureComponent { changeShapesBlackBorders(event.target.checked); }; + private changeShowBitmap = (event: CheckboxChangeEvent): void => { + const { changeShowBitmap } = this.props; + changeShowBitmap(event.target.checked); + }; + public render(): JSX.Element { const { sidebarCollapsed, @@ -185,6 +198,7 @@ class ObjectsSideBarContainer extends React.PureComponent { opacity, selectedOpacity, blackBorders, + showBitmap, collapseSidebar, collapseAppearance, } = this.props; @@ -197,12 +211,14 @@ class ObjectsSideBarContainer extends React.PureComponent { opacity={opacity} selectedOpacity={selectedOpacity} blackBorders={blackBorders} + showBitmap={showBitmap} collapseSidebar={collapseSidebar} collapseAppearance={collapseAppearance} changeShapesColorBy={this.changeShapesColorBy} changeShapesOpacity={this.changeShapesOpacity} changeSelectedShapesOpacity={this.changeSelectedShapesOpacity} changeShapesBlackBorders={this.changeShapesBlackBorders} + changeShowBitmap={this.changeShowBitmap} /> ); } diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index c99be219..5df7c6db 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -434,6 +434,7 @@ export interface ShapesSettingsState { opacity: number; selectedOpacity: number; blackBorders: boolean; + showBitmap: boolean; } export interface SettingsState { diff --git a/cvat-ui/src/reducers/settings-reducer.ts b/cvat-ui/src/reducers/settings-reducer.ts index 0a53aca4..1f1491f4 100644 --- a/cvat-ui/src/reducers/settings-reducer.ts +++ b/cvat-ui/src/reducers/settings-reducer.ts @@ -22,6 +22,7 @@ const defaultState: SettingsState = { opacity: 3, selectedOpacity: 30, blackBorders: false, + showBitmap: false, }, workspace: { autoSave: false, @@ -127,6 +128,15 @@ export default (state = defaultState, action: AnyAction): SettingsState => { }, }; } + case SettingsActionTypes.CHANGE_SHOW_UNLABELED_REGIONS: { + return { + ...state, + shapes: { + ...state.shapes, + showBitmap: action.payload.showBitmap, + }, + }; + } case SettingsActionTypes.CHANGE_FRAME_STEP: { return { ...state, From ec38159e15f755a9d7b3c53611ef6a35372ad2df Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 2 Apr 2020 13:33:21 +0300 Subject: [PATCH 14/30] Updated changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f15c960..f6e20684 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.0.0-alpha] - Unreleased ### Added -- +- Ability to display a bitmap in the new UI ### Changed - From 7303ae209483ec73c631e613f969aea46677e2fc Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 2 Apr 2020 13:34:57 +0300 Subject: [PATCH 15/30] Z-layer support --- cvat-canvas/src/typescript/canvasView.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 487136a7..280aac83 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -891,7 +891,10 @@ export class CanvasViewImpl implements CanvasView, Listener { } if (model.imageBitmap - && [UpdateReasons.IMAGE_CHANGED, UpdateReasons.OBJECTS_UPDATED].includes(reason) + && [UpdateReasons.IMAGE_CHANGED, + UpdateReasons.OBJECTS_UPDATED, + UpdateReasons.SET_Z_LAYER, + ].includes(reason) ) { this.redrawBitmap(); } From 4da951a812be829f7e14fdc60107a14f4dd41698 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 2 Apr 2020 13:42:22 +0300 Subject: [PATCH 16/30] Fixed settings after reopen a job --- cvat-ui/src/reducers/settings-reducer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cvat-ui/src/reducers/settings-reducer.ts b/cvat-ui/src/reducers/settings-reducer.ts index 1f1491f4..1b38bb89 100644 --- a/cvat-ui/src/reducers/settings-reducer.ts +++ b/cvat-ui/src/reducers/settings-reducer.ts @@ -227,18 +227,18 @@ export default (state = defaultState, action: AnyAction): SettingsState => { }, }; } + case BoundariesActionTypes.RESET_AFTER_ERROR: case AnnotationActionTypes.GET_JOB_SUCCESS: { const { job } = action.payload; return { - ...state, + ...defaultState, player: { - ...state.player, + ...defaultState.player, resetZoom: job && job.task.mode === 'annotation', }, }; } - case BoundariesActionTypes.RESET_AFTER_ERROR: case AuthActionTypes.LOGOUT_SUCCESS: { return { ...defaultState }; } From 80a07e593e10dd91087df02e6aceb39adf2f6208 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 2 Apr 2020 13:45:03 +0300 Subject: [PATCH 17/30] Do not show invisible objects on bitmap --- cvat-canvas/src/typescript/canvasView.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 280aac83..57370e50 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -916,6 +916,7 @@ export class CanvasViewImpl implements CanvasView, Listener { ctx.fillStyle = 'black'; ctx.fillRect(0, 0, width, height); for (const state of states) { + if (state.hidden || state.outside) continue; ctx.fillStyle = 'white'; if (['rectangle', 'polygon'].includes(state.shapeType)) { const points = state.shapeType === 'rectangle' ? [ From 66c6e7e9195507627e697eaa86ef5020784cc754 Mon Sep 17 00:00:00 2001 From: zhiltsov-max Date: Thu, 2 Apr 2020 14:08:22 +0300 Subject: [PATCH 18/30] Fix point interpolation (#1344) * Extend formats tests with different track types * Add unordered list comparison * Skip empty list comparison * fix * fix * Reproduce problem * Fix point interpolation for single point * undo rest api refactor --- cvat/apps/engine/data_manager.py | 5 ++- cvat/apps/engine/tests/test_data_manager.py | 39 +++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 cvat/apps/engine/tests/test_data_manager.py diff --git a/cvat/apps/engine/data_manager.py b/cvat/apps/engine/data_manager.py index d7adc8b8..b39c6783 100644 --- a/cvat/apps/engine/data_manager.py +++ b/cvat/apps/engine/data_manager.py @@ -293,7 +293,10 @@ class TrackManager(ObjectManager): @staticmethod def normalize_shape(shape): - points = np.asarray(shape["points"]).reshape(-1, 2) + points = list(shape["points"]) + if len(points) == 2: + points.extend(points) # duplicate points for single point case + points = np.asarray(points).reshape(-1, 2) broken_line = geometry.LineString(points) points = [] for off in range(0, 100, 1): diff --git a/cvat/apps/engine/tests/test_data_manager.py b/cvat/apps/engine/tests/test_data_manager.py new file mode 100644 index 00000000..968b5752 --- /dev/null +++ b/cvat/apps/engine/tests/test_data_manager.py @@ -0,0 +1,39 @@ +# Copyright (C) 2020 Intel Corporation +# +# SPDX-License-Identifier: MIT + +from cvat.apps.engine.data_manager import TrackManager + +from unittest import TestCase + + +class TrackManagerTest(TestCase): + def test_single_point_interpolation(self): + track = { + "frame": 0, + "label_id": 0, + "group": None, + "attributes": [], + "shapes": [ + { + "frame": 0, + "points": [1.0, 2.0], + "type": "points", + "occluded": False, + "outside": False, + "attributes": [] + }, + { + "frame": 2, + "attributes": [], + "points": [3.0, 4.0, 5.0, 6.0], + "type": "points", + "occluded": False, + "outside": True + }, + ] + } + + interpolated = TrackManager.get_interpolated_shapes(track, 0, 2) + + self.assertEqual(len(interpolated), 3) \ No newline at end of file From 3d44c0616f276243dae056ff19bd2c37821b2fe4 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 2 Apr 2020 15:36:07 +0300 Subject: [PATCH 19/30] Added button to reset color settings --- .../components/settings-page/player-settings.tsx | 15 +++++++++++++++ cvat-ui/src/components/settings-page/styles.scss | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/cvat-ui/src/components/settings-page/player-settings.tsx b/cvat-ui/src/components/settings-page/player-settings.tsx index 779a03b0..6b834122 100644 --- a/cvat-ui/src/components/settings-page/player-settings.tsx +++ b/cvat-ui/src/components/settings-page/player-settings.tsx @@ -6,6 +6,7 @@ import React from 'react'; import { Row, Col } from 'antd/lib/grid'; import Checkbox, { CheckboxChangeEvent } from 'antd/lib/checkbox'; +import Button from 'antd/lib/button'; import Slider from 'antd/lib/slider'; import Select from 'antd/lib/select'; import InputNumber from 'antd/lib/input-number'; @@ -16,6 +17,7 @@ import { clamp } from 'utils/math'; import { BackJumpIcon, ForwardJumpIcon } from 'icons'; import { FrameSpeed, GridColor } from 'reducers/interfaces'; + interface Props { frameStep: number; frameSpeed: FrameSpeed; @@ -263,6 +265,19 @@ export default function PlayerSettingsComponent(props: Props): JSX.Element { /> + + + + +
); } diff --git a/cvat-ui/src/components/settings-page/styles.scss b/cvat-ui/src/components/settings-page/styles.scss index ef4b005a..926d6029 100644 --- a/cvat-ui/src/components/settings-page/styles.scss +++ b/cvat-ui/src/components/settings-page/styles.scss @@ -73,12 +73,19 @@ width: 90px; } +.cvat-player-reset-color-settings, .cvat-player-settings-brightness, .cvat-player-settings-contrast, .cvat-player-settings-saturation { width: 40%; } +.cvat-player-reset-color-settings { + > .ant-col { + text-align: center; + } +} + .cvat-settings-page-back-button { width: 100px; margin-top: 15px; From 02d6a0c0fba0c754431c691d78076f78ca98097a Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 2 Apr 2020 15:38:29 +0300 Subject: [PATCH 20/30] Updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index af02b0be..09c965d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Special behaviour for attribute value ``__undefined__`` (invisibility, no shortcuts to be set in AAM) - Dialog window with some helpful information about using filters +- Button to reset colors settings (brightness, saturation, contrast) in the new UI ### Changed - From 7d986d599eb3e85fd06e55717678b4f19ae22df5 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 2 Apr 2020 22:14:27 +0300 Subject: [PATCH 21/30] Added option to display shape text always --- cvat-canvas/README.md | 9 ++++- cvat-canvas/src/typescript/canvas.ts | 7 ++++ .../src/typescript/canvasController.ts | 1 - cvat-canvas/src/typescript/canvasModel.ts | 33 +++++++++++++++++++ cvat-canvas/src/typescript/canvasView.ts | 25 ++++++++++++-- cvat-ui/src/actions/settings-actions.ts | 10 ++++++ .../standard-workspace/canvas-wrapper.tsx | 16 +++++++++ .../src/components/settings-page/styles.scss | 3 ++ .../settings-page/workspace-settings.tsx | 20 +++++++++++ .../standard-workspace/canvas-wrapper.tsx | 3 ++ .../settings-page/workspace-settings.tsx | 12 +++++-- cvat-ui/src/reducers/interfaces.ts | 1 + cvat-ui/src/reducers/settings-reducer.ts | 10 ++++++ 13 files changed, 142 insertions(+), 8 deletions(-) diff --git a/cvat-canvas/README.md b/cvat-canvas/README.md index 7fe60491..ea6bfa0e 100644 --- a/cvat-canvas/README.md +++ b/cvat-canvas/README.md @@ -50,6 +50,11 @@ Canvas itself handles: ZOOM_CANVAS = 'zoom_canvas', } + interface Configuration { + displayAllText?: boolean; + undefinedAttrValue?: string; + } + interface DrawData { enabled: boolean; shapeType?: string; @@ -83,7 +88,6 @@ Canvas itself handles: } interface Canvas { - mode(): Mode; html(): HTMLDivElement; setZLayer(zLayer: number | null): void; setup(frameData: any, objectStates: any[]): void; @@ -103,7 +107,9 @@ Canvas itself handles: dragCanvas(enable: boolean): void; zoomCanvas(enable: boolean): void; + mode(): void; cancel(): void; + configure(configuration: Configuration): void; } ``` @@ -189,4 +195,5 @@ Standard JS events are used. | dragCanvas() | + | - | - | - | - | - | + | - | | zoomCanvas() | + | - | - | - | - | - | - | + | | cancel() | - | + | + | + | + | + | + | + | +| configure() | + | - | - | - | - | - | - | - | | setZLayer() | + | + | + | + | + | + | + | + | diff --git a/cvat-canvas/src/typescript/canvas.ts b/cvat-canvas/src/typescript/canvas.ts index eef1de40..c601034f 100644 --- a/cvat-canvas/src/typescript/canvas.ts +++ b/cvat-canvas/src/typescript/canvas.ts @@ -11,6 +11,7 @@ import { CanvasModel, CanvasModelImpl, RectDrawingMethod, + Configuration, } from './canvasModel'; import { @@ -54,6 +55,7 @@ interface Canvas { mode(): void; cancel(): void; + configure(configuration: Configuration): void; } class CanvasImpl implements Canvas { @@ -141,11 +143,16 @@ class CanvasImpl implements Canvas { public cancel(): void { this.model.cancel(); } + + public configure(configuration: Configuration): void { + this.model.configure(configuration); + } } export { CanvasImpl as Canvas, CanvasVersion, + Configuration, RectDrawingMethod, Mode as CanvasMode, }; diff --git a/cvat-canvas/src/typescript/canvasController.ts b/cvat-canvas/src/typescript/canvasController.ts index 7fc555c6..179f9b32 100644 --- a/cvat-canvas/src/typescript/canvasController.ts +++ b/cvat-canvas/src/typescript/canvasController.ts @@ -36,7 +36,6 @@ export interface CanvasController { enableDrag(x: number, y: number): void; drag(x: number, y: number): void; disableDrag(): void; - fit(): void; } diff --git a/cvat-canvas/src/typescript/canvasModel.ts b/cvat-canvas/src/typescript/canvasModel.ts index 66a2717b..888b5499 100644 --- a/cvat-canvas/src/typescript/canvasModel.ts +++ b/cvat-canvas/src/typescript/canvasModel.ts @@ -46,6 +46,11 @@ export enum RectDrawingMethod { EXTREME_POINTS = 'By 4 points' } +export interface Configuration { + displayAllText?: boolean; + undefinedAttrValue?: string; +} + export interface DrawData { enabled: boolean; shapeType?: string; @@ -100,6 +105,7 @@ export enum UpdateReasons { CANCEL = 'cancel', DRAG_CANVAS = 'drag_canvas', ZOOM_CANVAS = 'ZOOM_CANVAS', + CONFIG_UPDATED = 'config_updated', } export enum Mode { @@ -126,6 +132,7 @@ export interface CanvasModel { readonly mergeData: MergeData; readonly splitData: SplitData; readonly groupData: GroupData; + readonly configuration: Configuration; readonly selected: any; geometry: Geometry; mode: Mode; @@ -151,6 +158,7 @@ export interface CanvasModel { dragCanvas(enable: boolean): void; zoomCanvas(enable: boolean): void; + configure(configuration: Configuration): void; cancel(): void; } @@ -159,6 +167,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { activeElement: ActiveElement; angle: number; canvasSize: Size; + configuration: Configuration; image: Image | null; imageID: number | null; imageOffset: number; @@ -191,6 +200,10 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { height: 0, width: 0, }, + configuration: { + displayAllText: false, + undefinedAttrValue: '', + }, image: null, imageID: null, imageOffset: 0, @@ -485,10 +498,30 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { this.data.selected = null; } + public configure(configuration: Configuration): void { + if (this.data.mode !== Mode.IDLE) { + throw Error(`Canvas is busy. Action: ${this.data.mode}`); + } + + if (typeof (configuration.displayAllText) !== 'undefined') { + this.data.configuration.displayAllText = configuration.displayAllText; + } + + if (typeof (configuration.undefinedAttrValue) !== 'undefined') { + this.data.configuration.undefinedAttrValue = configuration.undefinedAttrValue; + } + + this.notify(UpdateReasons.CONFIG_UPDATED); + } + public cancel(): void { this.notify(UpdateReasons.CANCEL); } + public get configuration(): Configuration { + return { ...this.data.configuration }; + } + public get geometry(): Geometry { return { angle: this.data.angle, diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 75963144..bd460be6 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -36,6 +36,7 @@ import { GroupData, Mode, Size, + Configuration, } from './canvasModel'; export interface CanvasView { @@ -65,6 +66,7 @@ export class CanvasViewImpl implements CanvasView, Listener { private groupHandler: GroupHandler; private zoomHandler: ZoomHandler; private activeElement: ActiveElement; + private configuration: Configuration; private set mode(value: Mode) { this.controller.mode = value; @@ -538,6 +540,7 @@ export class CanvasViewImpl implements CanvasView, Listener { clientID: null, attributeID: null, }; + this.configuration = model.configuration; this.mode = Mode.IDLE; // Create HTML elements @@ -702,7 +705,11 @@ export class CanvasViewImpl implements CanvasView, Listener { public notify(model: CanvasModel & Master, reason: UpdateReasons): void { this.geometry = this.controller.geometry; - if (reason === UpdateReasons.IMAGE_CHANGED) { + if (reason === UpdateReasons.CONFIG_UPDATED) { + this.configuration = model.configuration; + this.setupObjects([]); + this.setupObjects(model.objects); + } else if (reason === UpdateReasons.IMAGE_CHANGED) { const { image } = model; if (!image) { this.loadingAnimation.classList.remove('cvat_canvas_hidden'); @@ -987,6 +994,8 @@ export class CanvasViewImpl implements CanvasView, Listener { } private addObjects(states: any[], translate: (points: number[]) => number[]): void { + const { displayAllText } = this.configuration; + for (const state of states) { if (state.objectType === 'tag') { this.addTag(state); @@ -1030,6 +1039,14 @@ export class CanvasViewImpl implements CanvasView, Listener { }, })); }); + + if (displayAllText) { + this.svgTexts[state.clientID] = this.addText(state); + this.updateTextPosition( + this.svgTexts[state.clientID], + this.svgShapes[state.clientID], + ); + } } this.saveState(state); @@ -1078,6 +1095,7 @@ export class CanvasViewImpl implements CanvasView, Listener { private deactivateShape(): void { if (this.activeElement.clientID !== null) { + const { displayAllText } = this.configuration; const { clientID } = this.activeElement; const drawnState = this.drawnStates[clientID]; const shape = this.svgShapes[clientID]; @@ -1101,7 +1119,7 @@ export class CanvasViewImpl implements CanvasView, Listener { // TODO: Hide text only if it is hidden by settings const text = this.svgTexts[clientID]; - if (text) { + if (text && !displayAllText) { text.remove(); delete this.svgTexts[clientID]; } @@ -1347,6 +1365,7 @@ export class CanvasViewImpl implements CanvasView, Listener { } private addText(state: any): SVG.Text { + const { undefinedAttrValue } = this.configuration; const { label, clientID, attributes } = state; const attrNames = label.attributes.reduce((acc: any, val: any): void => { acc[val.id] = val.name; @@ -1356,7 +1375,7 @@ export class CanvasViewImpl implements CanvasView, Listener { return this.adoptedText.text((block): void => { block.tspan(`${label.name} ${clientID}`).style('text-transform', 'uppercase'); for (const attrID of Object.keys(attributes)) { - const value = attributes[attrID] === consts.UNDEFINED_ATTRIBUTE_VALUE + const value = attributes[attrID] === undefinedAttrValue ? '' : attributes[attrID]; block.tspan(`${attrNames[attrID]}: ${value}`).attr({ attrID, diff --git a/cvat-ui/src/actions/settings-actions.ts b/cvat-ui/src/actions/settings-actions.ts index c14c2db2..03b56cc2 100644 --- a/cvat-ui/src/actions/settings-actions.ts +++ b/cvat-ui/src/actions/settings-actions.ts @@ -28,6 +28,7 @@ export enum SettingsActionTypes { CHANGE_AUTO_SAVE_INTERVAL = 'CHANGE_AUTO_SAVE_INTERVAL', CHANGE_AAM_ZOOM_MARGIN = 'CHANGE_AAM_ZOOM_MARGIN', SWITCH_SHOWNIG_INTERPOLATED_TRACKS = 'SWITCH_SHOWNIG_INTERPOLATED_TRACKS', + SWITCH_SHOWING_OBJECTS_TEXT_ALWAYS = 'SWITCH_SHOWING_OBJECTS_TEXT_ALWAYS', } export function changeShapesOpacity(opacity: number): AnyAction { @@ -200,3 +201,12 @@ export function switchShowingInterpolatedTracks(showAllInterpolationTracks: bool }, }; } + +export function switchShowingObjectsTextAlways(showObjectsTextAlways: boolean): AnyAction { + return { + type: SettingsActionTypes.SWITCH_SHOWING_OBJECTS_TEXT_ALWAYS, + payload: { + showObjectsTextAlways, + }, + }; +} diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx index 038157e2..4f38f3db 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/canvas-wrapper.tsx @@ -21,6 +21,7 @@ import { import { LogType } from 'cvat-logger'; import { Canvas } from 'cvat-canvas'; import getCore from 'cvat-core'; +import consts from 'consts'; const cvat = getCore(); @@ -58,6 +59,7 @@ interface Props { contextVisible: boolean; contextType: ContextMenuType; aamZoomMargin: number; + showObjectsTextAlways: boolean; workspace: Workspace; keyMap: Record; onSetupCanvas: () => void; @@ -91,6 +93,7 @@ interface Props { export default class CanvasWrapperComponent extends React.PureComponent { public componentDidMount(): void { const { + showObjectsTextAlways, canvasInstance, curZLayer, } = this.props; @@ -101,7 +104,12 @@ export default class CanvasWrapperComponent extends React.PureComponent { .getElementsByClassName('cvat-canvas-container'); wrapper.appendChild(canvasInstance.html()); + canvasInstance.configure({ + undefinedAttrValue: consts.UNDEFINED_ATTRIBUTE_VALUE, + displayAllText: showObjectsTextAlways, + }); canvasInstance.setZLayer(curZLayer); + this.initialSetup(); this.updateCanvas(); } @@ -128,8 +136,16 @@ export default class CanvasWrapperComponent extends React.PureComponent { saturationLevel, workspace, frameFetching, + showObjectsTextAlways, } = this.props; + if (prevProps.showObjectsTextAlways !== showObjectsTextAlways) { + canvasInstance.configure({ + undefinedAttrValue: consts.UNDEFINED_ATTRIBUTE_VALUE, + displayAllText: showObjectsTextAlways, + }); + } + if (prevProps.sidebarCollapsed !== sidebarCollapsed) { const [sidebar] = window.document.getElementsByClassName('cvat-objects-sidebar'); if (sidebar) { diff --git a/cvat-ui/src/components/settings-page/styles.scss b/cvat-ui/src/components/settings-page/styles.scss index ef4b005a..d54c7206 100644 --- a/cvat-ui/src/components/settings-page/styles.scss +++ b/cvat-ui/src/components/settings-page/styles.scss @@ -20,6 +20,8 @@ .cvat-player-settings-grid, .cvat-workspace-settings-auto-save, +.cvat-workspace-settings-show-text-always, +.cvat-workspace-settings-show-text-always-checkbox, .cvat-workspace-settings-show-interpolated-checkbox { margin-bottom: 10px; } @@ -31,6 +33,7 @@ .cvat-player-settings-speed, .cvat-player-settings-reset-zoom, .cvat-player-settings-rotate-all, +.cvat-workspace-settings-show-text-always, .cvat-workspace-settings-show-interpolated, .cvat-workspace-settings-aam-zoom-margin, .cvat-workspace-settings-auto-save-interval { diff --git a/cvat-ui/src/components/settings-page/workspace-settings.tsx b/cvat-ui/src/components/settings-page/workspace-settings.tsx index 52ba4a50..0ee068ae 100644 --- a/cvat-ui/src/components/settings-page/workspace-settings.tsx +++ b/cvat-ui/src/components/settings-page/workspace-settings.tsx @@ -16,10 +16,12 @@ interface Props { autoSaveInterval: number; aamZoomMargin: number; showAllInterpolationTracks: boolean; + showObjectsTextAlways: boolean; onSwitchAutoSave(enabled: boolean): void; onChangeAutoSaveInterval(interval: number): void; onChangeAAMZoomMargin(margin: number): void; onSwitchShowingInterpolatedTracks(enabled: boolean): void; + onSwitchShowingObjectsTextAlways(enabled: boolean): void; } export default function WorkspaceSettingsComponent(props: Props): JSX.Element { @@ -28,10 +30,12 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element { autoSaveInterval, aamZoomMargin, showAllInterpolationTracks, + showObjectsTextAlways, onSwitchAutoSave, onChangeAutoSaveInterval, onChangeAAMZoomMargin, onSwitchShowingInterpolatedTracks, + onSwitchShowingObjectsTextAlways, } = props; const minAutoSaveInterval = 5; @@ -93,6 +97,22 @@ export default function WorkspaceSettingsComponent(props: Props): JSX.Element { Show hidden interpolated objects in the side panel + + + { + onSwitchShowingObjectsTextAlways(event.target.checked); + }} + > + Always show object details + + + + Show text for an object on the canvas not only when the object is activated + + Attribute annotation mode (AAM) zoom margin diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx index e5b74d9b..fc4d62e5 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/canvas-wrapper.tsx @@ -73,6 +73,7 @@ interface StateToProps { saturationLevel: number; resetZoom: boolean; aamZoomMargin: number; + showObjectsTextAlways: boolean; workspace: Workspace; minZLayer: number; maxZLayer: number; @@ -163,6 +164,7 @@ function mapStateToProps(state: CombinedState): StateToProps { }, workspace: { aamZoomMargin, + showObjectsTextAlways, }, shapes: { opacity, @@ -203,6 +205,7 @@ function mapStateToProps(state: CombinedState): StateToProps { saturationLevel, resetZoom, aamZoomMargin, + showObjectsTextAlways, curZLayer, minZLayer, maxZLayer, diff --git a/cvat-ui/src/containers/settings-page/workspace-settings.tsx b/cvat-ui/src/containers/settings-page/workspace-settings.tsx index 4ab527b3..db744564 100644 --- a/cvat-ui/src/containers/settings-page/workspace-settings.tsx +++ b/cvat-ui/src/containers/settings-page/workspace-settings.tsx @@ -10,11 +10,10 @@ import { changeAutoSaveInterval, changeAAMZoomMargin, switchShowingInterpolatedTracks, + switchShowingObjectsTextAlways, } from 'actions/settings-actions'; -import { - CombinedState, -} from 'reducers/interfaces'; +import { CombinedState } from 'reducers/interfaces'; import WorkspaceSettingsComponent from 'components/settings-page/workspace-settings'; @@ -23,6 +22,7 @@ interface StateToProps { autoSaveInterval: number; aamZoomMargin: number; showAllInterpolationTracks: boolean; + showObjectsTextAlways: boolean; } interface DispatchToProps { @@ -30,6 +30,7 @@ interface DispatchToProps { onChangeAutoSaveInterval(interval: number): void; onChangeAAMZoomMargin(margin: number): void; onSwitchShowingInterpolatedTracks(enabled: boolean): void; + onSwitchShowingObjectsTextAlways(enabled: boolean): void; } function mapStateToProps(state: CombinedState): StateToProps { @@ -39,6 +40,7 @@ function mapStateToProps(state: CombinedState): StateToProps { autoSaveInterval, aamZoomMargin, showAllInterpolationTracks, + showObjectsTextAlways, } = workspace; return { @@ -46,6 +48,7 @@ function mapStateToProps(state: CombinedState): StateToProps { autoSaveInterval, aamZoomMargin, showAllInterpolationTracks, + showObjectsTextAlways, }; } @@ -63,6 +66,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { onSwitchShowingInterpolatedTracks(enabled: boolean): void { dispatch(switchShowingInterpolatedTracks(enabled)); }, + onSwitchShowingObjectsTextAlways(enabled: boolean): void { + dispatch(switchShowingObjectsTextAlways(enabled)); + }, }; } diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index c99be219..2ab26f7c 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -426,6 +426,7 @@ export interface WorkspaceSettingsState { autoSave: boolean; autoSaveInterval: number; // in ms aamZoomMargin: number; + showObjectsTextAlways: boolean; showAllInterpolationTracks: boolean; } diff --git a/cvat-ui/src/reducers/settings-reducer.ts b/cvat-ui/src/reducers/settings-reducer.ts index 0a53aca4..632ed5b9 100644 --- a/cvat-ui/src/reducers/settings-reducer.ts +++ b/cvat-ui/src/reducers/settings-reducer.ts @@ -27,6 +27,7 @@ const defaultState: SettingsState = { autoSave: false, autoSaveInterval: 15 * 60 * 1000, aamZoomMargin: 100, + showObjectsTextAlways: false, showAllInterpolationTracks: false, }, player: { @@ -217,6 +218,15 @@ export default (state = defaultState, action: AnyAction): SettingsState => { }, }; } + case SettingsActionTypes.SWITCH_SHOWING_OBJECTS_TEXT_ALWAYS: { + return { + ...state, + workspace: { + ...state.workspace, + showObjectsTextAlways: action.payload.showObjectsTextAlways, + }, + }; + } case AnnotationActionTypes.GET_JOB_SUCCESS: { const { job } = action.payload; From 5f53302512bc01cd78adf42d6f8feb67ce05ae9d Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Thu, 2 Apr 2020 22:20:33 +0300 Subject: [PATCH 22/30] Updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index af02b0be..80ff9f40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Special behaviour for attribute value ``__undefined__`` (invisibility, no shortcuts to be set in AAM) - Dialog window with some helpful information about using filters +- Added option to display shape text always ### Changed - From 96c9427bed1456bea8d9d42a22ba4437da139b31 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Fri, 3 Apr 2020 10:25:04 +0300 Subject: [PATCH 23/30] Hidden/outside fix --- cvat-canvas/src/typescript/canvasView.ts | 45 +++++++++++++++--------- cvat-ui/src/reducers/settings-reducer.ts | 2 +- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index da226cf4..27160436 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -967,31 +967,44 @@ export class CanvasViewImpl implements CanvasView, Listener { for (const state of states) { const { clientID } = state; const drawnState = this.drawnStates[clientID]; + const shape = this.svgShapes[state.clientID]; + const text = this.svgTexts[state.clientID]; if (drawnState.hidden !== state.hidden || drawnState.outside !== state.outside) { - const none = state.hidden || state.outside; - if (state.shapeType === 'points') { - this.svgShapes[clientID].remember('_selectHandler').nested - .style('display', none ? 'none' : ''); + const isInvisible = state.hidden || state.outside; + if (isInvisible) { + (state.shapeType === 'points' ? shape.remember('_selectHandler').nested : shape) + .style('display', 'none'); + if (text) { + text.addClass('cvat_canvas_hidden'); + } } else { - this.svgShapes[clientID].style('display', none ? 'none' : ''); + (state.shapeType === 'points' ? shape.remember('_selectHandler').nested : shape) + .style('display', ''); + if (text) { + text.removeClass('cvat_canvas_hidden'); + this.updateTextPosition( + text, + shape, + ); + } } } if (drawnState.zOrder !== state.zOrder) { if (state.shapeType === 'points') { - this.svgShapes[clientID].remember('_selectHandler').nested + shape.remember('_selectHandler').nested .attr('data-z-order', state.zOrder); } else { - this.svgShapes[clientID].attr('data-z-order', state.zOrder); + shape.attr('data-z-order', state.zOrder); } } if (drawnState.occluded !== state.occluded) { if (state.occluded) { - this.svgShapes[clientID].addClass('cvat_canvas_shape_occluded'); + shape.addClass('cvat_canvas_shape_occluded'); } else { - this.svgShapes[clientID].removeClass('cvat_canvas_shape_occluded'); + shape.removeClass('cvat_canvas_shape_occluded'); } } @@ -1009,7 +1022,7 @@ export class CanvasViewImpl implements CanvasView, Listener { if (state.shapeType === 'rectangle') { const [xtl, ytl, xbr, ybr] = translatedPoints; - this.svgShapes[clientID].attr({ + shape.attr({ x: xtl, y: ytl, width: xbr - xtl, @@ -1025,21 +1038,20 @@ export class CanvasViewImpl implements CanvasView, Listener { return `${acc}${val},`; }, '', ); - (this.svgShapes[clientID] as any).clear(); - this.svgShapes[clientID].attr('points', stringified); + (shape as any).clear(); + shape.attr('points', stringified); if (state.shapeType === 'points') { - this.selectize(false, this.svgShapes[clientID]); - this.setupPoints(this.svgShapes[clientID] as SVG.PolyLine, state); + this.selectize(false, shape); + this.setupPoints(shape as SVG.PolyLine, state); } } } for (const attrID of Object.keys(state.attributes)) { if (state.attributes[attrID] !== drawnState.attributes[attrID]) { - const text = this.svgTexts[state.clientID]; if (text) { - const [span] = this.svgTexts[state.clientID].node + const [span] = text.node .querySelectorAll(`[attrID="${attrID}"]`) as any as SVGTSpanElement[]; if (span && span.textContent) { const prefix = span.textContent.split(':').slice(0, -1).join(':'); @@ -1387,6 +1399,7 @@ export class CanvasViewImpl implements CanvasView, Listener { // Update text position after corresponding box has been moved, resized, etc. private updateTextPosition(text: SVG.Text, shape: SVG.Shape): void { + if (text.node.style.display === 'none') return; // wrong transformation matrix let box = (shape.node as any).getBBox(); // Translate the whole box to the client coordinate system diff --git a/cvat-ui/src/reducers/settings-reducer.ts b/cvat-ui/src/reducers/settings-reducer.ts index 00cc5527..87396429 100644 --- a/cvat-ui/src/reducers/settings-reducer.ts +++ b/cvat-ui/src/reducers/settings-reducer.ts @@ -28,7 +28,7 @@ const defaultState: SettingsState = { autoSave: false, autoSaveInterval: 15 * 60 * 1000, aamZoomMargin: 100, - showObjectsTextAlways: false, + showObjectsTextAlways: true, showAllInterpolationTracks: false, }, player: { From abbc635101f790e1c2a18021bc9d866ca9ca1286 Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Fri, 3 Apr 2020 10:51:49 +0300 Subject: [PATCH 24/30] Fixed screen scaling --- cvat-ui/src/components/settings-page/styles.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cvat-ui/src/components/settings-page/styles.scss b/cvat-ui/src/components/settings-page/styles.scss index 926d6029..82046ae1 100644 --- a/cvat-ui/src/components/settings-page/styles.scss +++ b/cvat-ui/src/components/settings-page/styles.scss @@ -5,6 +5,9 @@ @import '../../base.scss'; .cvat-settings-page { + height: 90%; + overflow-y: auto; + > div:nth-child(1) { margin-top: 30px; margin-bottom: 10px; From be2ec3ad7276774f34f50dee9716f41a025a4c44 Mon Sep 17 00:00:00 2001 From: Andrey Zhavoronkov <41117609+azhavoro@users.noreply.github.com> Date: Fri, 3 Apr 2020 12:32:20 +0300 Subject: [PATCH 25/30] fixed dump error after moving format files (#1342) * fixed dump error after moving format files * updated changelog --- CHANGELOG.md | 1 + cvat/apps/annotation/serializers.py | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c862c85..a331eaf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - New shape is added when press ``esc`` when drawing instead of cancellation +- Fixed `FileNotFoundError` during dump after moving format files ### Security - diff --git a/cvat/apps/annotation/serializers.py b/cvat/apps/annotation/serializers.py index 8fa8b345..7284c041 100644 --- a/cvat/apps/annotation/serializers.py +++ b/cvat/apps/annotation/serializers.py @@ -1,8 +1,10 @@ -# Copyright (C) 2018 Intel Corporation +# Copyright (C) 2018-2020 Intel Corporation # # SPDX-License-Identifier: MIT +from django.utils import timezone from rest_framework import serializers + from cvat.apps.annotation import models class AnnotationDumperSerializer(serializers.ModelSerializer): @@ -57,6 +59,9 @@ class AnnotationFormatSerializer(serializers.ModelSerializer): def update(self, instance, validated_data): dumper_names = [handler["display_name"] for handler in validated_data["annotationdumper_set"]] loader_names = [handler["display_name"] for handler in validated_data["annotationloader_set"]] + instance.handler_file = validated_data.get('handler_file', instance.handler_file) + instance.owner = validated_data.get('owner', instance.owner) + instance.updated_date = timezone.localtime(timezone.now()) handlers_to_delete = [d for d in instance.annotationdumper_set.all() if d.display_name not in dumper_names] + \ [l for l in instance.annotationloader_set.all() if l.display_name not in loader_names] From 7bdf9bb481d3ce7efe91407b1662a23866d39815 Mon Sep 17 00:00:00 2001 From: Andrey Zhavoronkov <41117609+azhavoro@users.noreply.github.com> Date: Fri, 3 Apr 2020 18:01:13 +0300 Subject: [PATCH 26/30] Az/fix dextr (#1348) * Fixed dextr_segmentation app * updated changelog Co-authored-by: Nikita Manovich <40690625+nmanovic@users.noreply.github.com> --- CHANGELOG.md | 1 + cvat/apps/dextr_segmentation/dextr.py | 7 +++++-- cvat/apps/dextr_segmentation/views.py | 10 ++++------ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a331eaf9..3f04b1d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - New shape is added when press ``esc`` when drawing instead of cancellation +- Fixed dextr segmentation. - Fixed `FileNotFoundError` during dump after moving format files ### Security diff --git a/cvat/apps/dextr_segmentation/dextr.py b/cvat/apps/dextr_segmentation/dextr.py index 628961ff..db8c71e7 100644 --- a/cvat/apps/dextr_segmentation/dextr.py +++ b/cvat/apps/dextr_segmentation/dextr.py @@ -4,6 +4,7 @@ # SPDX-License-Identifier: MIT from cvat.apps.auto_annotation.inference_engine import make_plugin_or_core, make_network +from cvat.apps.engine.frame_provider import FrameProvider import os import cv2 @@ -29,7 +30,7 @@ class DEXTR_HANDLER: raise Exception("DEXTR_MODEL_DIR is not defined") - def handle(self, im_path, points): + def handle(self, db_data, frame, points): # Lazy initialization if not self._plugin: self._plugin = make_plugin_or_core() @@ -42,7 +43,9 @@ class DEXTR_HANDLER: else: self._exec_network = self._plugin.load(network=self._network) - image = PIL.Image.open(im_path) + frame_provider = FrameProvider(db_data) + image = frame_provider.get_frame(frame, frame_provider.Quality.ORIGINAL) + image = PIL.Image.open(image[0]) numpy_image = np.array(image) points = np.asarray([[int(p["x"]), int(p["y"])] for p in points], dtype=int) bounding_box = ( diff --git a/cvat/apps/dextr_segmentation/views.py b/cvat/apps/dextr_segmentation/views.py index 0a837b3a..a4827e99 100644 --- a/cvat/apps/dextr_segmentation/views.py +++ b/cvat/apps/dextr_segmentation/views.py @@ -12,15 +12,14 @@ from cvat.apps.dextr_segmentation.dextr import DEXTR_HANDLER import django_rq import json -import os import rq __RQ_QUEUE_NAME = "default" __DEXTR_HANDLER = DEXTR_HANDLER() -def _dextr_thread(im_path, points): +def _dextr_thread(db_data, frame, points): job = rq.get_current_job() - job.meta["result"] = __DEXTR_HANDLER.handle(im_path, points) + job.meta["result"] = __DEXTR_HANDLER.handle(db_data, frame, points) job.save_meta() @@ -38,8 +37,7 @@ def create(request, jid): slogger.job[jid].info("create dextr request for the JOB: {} ".format(jid) + "by the USER: {} on the FRAME: {}".format(username, frame)) - db_task = Job.objects.select_related("segment__task").get(id=jid).segment.task - im_path = os.path.realpath(db_task.get_frame_path(frame)) + db_data = Job.objects.select_related("segment__task__data").get(id=jid).segment.task.data queue = django_rq.get_queue(__RQ_QUEUE_NAME) rq_id = "dextr.create/{}/{}".format(jid, username) @@ -53,7 +51,7 @@ def create(request, jid): job.delete() queue.enqueue_call(func=_dextr_thread, - args=(im_path, points), + args=(db_data, frame, points), job_id=rq_id, timeout=15, ttl=30) From e0db8f43210c54e30146df3207d9d43e2434a77e Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Fri, 3 Apr 2020 19:07:17 +0300 Subject: [PATCH 27/30] Disabled option by default --- cvat-ui/src/reducers/settings-reducer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvat-ui/src/reducers/settings-reducer.ts b/cvat-ui/src/reducers/settings-reducer.ts index 87396429..00cc5527 100644 --- a/cvat-ui/src/reducers/settings-reducer.ts +++ b/cvat-ui/src/reducers/settings-reducer.ts @@ -28,7 +28,7 @@ const defaultState: SettingsState = { autoSave: false, autoSaveInterval: 15 * 60 * 1000, aamZoomMargin: 100, - showObjectsTextAlways: true, + showObjectsTextAlways: false, showAllInterpolationTracks: false, }, player: { From 364db2cb168ae4d9cef08dc61c18d4b1f9722aac Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Fri, 3 Apr 2020 19:39:40 +0300 Subject: [PATCH 28/30] Fixed typos in interface --- cvat-canvas/README.md | 2 +- cvat-canvas/src/typescript/canvas.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cvat-canvas/README.md b/cvat-canvas/README.md index c3e7b640..59806746 100644 --- a/cvat-canvas/README.md +++ b/cvat-canvas/README.md @@ -108,7 +108,7 @@ Canvas itself handles: dragCanvas(enable: boolean): void; zoomCanvas(enable: boolean): void; - mode(): void; + mode(): Mode; cancel(): void; configure(configuration: Configuration): void; } diff --git a/cvat-canvas/src/typescript/canvas.ts b/cvat-canvas/src/typescript/canvas.ts index 1df3efe2..04a99d5b 100644 --- a/cvat-canvas/src/typescript/canvas.ts +++ b/cvat-canvas/src/typescript/canvas.ts @@ -54,7 +54,7 @@ interface Canvas { dragCanvas(enable: boolean): void; zoomCanvas(enable: boolean): void; - mode(): void; + mode(): Mode; cancel(): void; configure(configuration: Configuration): void; } From 87784fa97ed104f9d77f95e4f0668bf0c8890d64 Mon Sep 17 00:00:00 2001 From: Nikita Manovich Date: Sat, 4 Apr 2020 06:25:00 +0300 Subject: [PATCH 29/30] Increase preview size till 256, 256. Previous preview size was not optimal and led to a blurred image due to too small size. --- cvat/apps/engine/media_extractors.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cvat/apps/engine/media_extractors.py b/cvat/apps/engine/media_extractors.py index 778aec9a..c32d7abd 100644 --- a/cvat/apps/engine/media_extractors.py +++ b/cvat/apps/engine/media_extractors.py @@ -52,11 +52,12 @@ class IMediaReader(ABC): @staticmethod def _get_preview(obj): + PREVIEW_SIZE = (256, 256) if isinstance(obj, io.IOBase): preview = Image.open(obj) else: preview = obj - preview.thumbnail((128, 128)) + preview.thumbnail(PREVIEW_SIZE) return preview.convert('RGB') From de52a734905ff941041a2c1c71c29162239a4009 Mon Sep 17 00:00:00 2001 From: Nikita Manovich Date: Sat, 4 Apr 2020 06:31:19 +0300 Subject: [PATCH 30/30] Add a line into CHANGELOG.md about the change. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e32ad1bb..d8a3778c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added option to display shape text always ### Changed -- +- Increase preview size of a task till 256, 256 on the server ### Deprecated -