diff --git a/CHANGELOG.md b/CHANGELOG.md index 33bc23a96849..a6ff367abafc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Increase preview size of a task till 256, 256 on the server +- Minor style updates ### Deprecated - @@ -23,9 +24,15 @@ 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 -- Fixed exception logs in the new UI (missed fields: task_id and job_id) +- Dextr segmentation doesn't work. +- `FileNotFoundError` during dump after moving format files +- CVAT doesn't append outside shapes when merge polyshapes in old UI +- Layout sometimes shows double scroll bars on create task, dashboard and settings pages +- UI fails after trying to change frame during resizing, dragging, editing +- Hidden points (or outsided) are visible after changing a frame +- Merge is allowed for points, but clicks on points conflict with frame dragging logic +- Removed objects are visible for search +- Exception logs in the new UI (missed fields: task_id and job_id) ### Security - diff --git a/cvat-canvas/README.md b/cvat-canvas/README.md index 59806746eb59..d9cabfa94a8c 100644 --- a/cvat-canvas/README.md +++ b/cvat-canvas/README.md @@ -179,23 +179,23 @@ Standard JS events are used. ## API Reaction -| | IDLE | GROUPING | SPLITTING | DRAWING | MERGING | EDITING | DRAG | ZOOM | -|--------------|------|----------|-----------|---------|---------|---------|------|------| -| html() | + | + | + | + | + | + | + | + | -| setup() | + | + | + | + | + | - | + | + | -| activate() | + | - | - | - | - | - | - | - | -| rotate() | + | + | + | + | + | + | + | + | -| focus() | + | + | + | + | + | + | + | + | -| fit() | + | + | + | + | + | + | + | + | -| grid() | + | + | + | + | + | + | + | + | -| draw() | + | - | - | - | - | - | - | - | -| split() | + | - | + | - | - | - | - | - | -| group() | + | + | - | - | - | - | - | - | -| merge() | + | - | - | - | + | - | - | - | -| fitCanvas() | + | + | + | + | + | + | + | + | -| dragCanvas() | + | - | - | - | - | - | + | - | -| zoomCanvas() | + | - | - | - | - | - | - | + | -| cancel() | - | + | + | + | + | + | + | + | -| configure() | + | - | - | - | - | - | - | - | -| bitmap() | + | + | + | + | + | + | + | + | -| setZLayer() | + | + | + | + | + | + | + | + | +| | IDLE | GROUP | SPLIT | DRAW | MERGE | EDIT | DRAG | RESIZE | ZOOM_CANVAS | DRAG_CANVAS | +|--------------|------|-------|-------|------|-------|------|------|--------|-------------|-------------| +| html() | + | + | + | + | + | + | + | + | + | + | +| setup() | + | + | + | + | + | - | - | - | + | + | +| activate() | + | - | - | - | - | - | - | - | - | - | +| rotate() | + | + | + | + | + | + | + | + | + | + | +| focus() | + | + | + | + | + | + | + | + | + | + | +| fit() | + | + | + | + | + | + | + | + | + | + | +| grid() | + | + | + | + | + | + | + | + | + | + | +| draw() | + | - | - | - | - | - | - | - | - | - | +| split() | + | - | + | - | - | - | - | - | - | - | +| group() | + | + | - | - | - | - | - | - | - | - | +| merge() | + | - | - | - | + | - | - | - | - | - | +| fitCanvas() | + | + | + | + | + | + | + | + | + | + | +| dragCanvas() | + | - | - | - | - | - | + | - | - | + | +| zoomCanvas() | + | - | - | - | - | - | - | + | + | - | +| cancel() | - | + | + | + | + | + | + | + | + | + | +| configure() | + | - | - | - | - | - | - | - | - | - | +| bitmap() | + | + | + | + | + | + | + | + | + | + | +| setZLayer() | + | + | + | + | + | + | + | + | + | + | diff --git a/cvat-canvas/src/typescript/canvasModel.ts b/cvat-canvas/src/typescript/canvasModel.ts index 13f99be1b08a..da7d112ad061 100644 --- a/cvat-canvas/src/typescript/canvasModel.ts +++ b/cvat-canvas/src/typescript/canvasModel.ts @@ -327,6 +327,10 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel { } public setup(frameData: any, objectStates: any[]): void { + if ([Mode.EDIT, Mode.DRAG, Mode.RESIZE].includes(this.data.mode)) { + throw Error(`Canvas is busy. Action: ${this.data.mode}`); + } + if (frameData.number === this.data.imageID) { this.data.objects = objectStates; this.notify(UpdateReasons.OBJECTS_UPDATED); diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 27160436bf5d..1de8fa524a37 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -1041,7 +1041,7 @@ export class CanvasViewImpl implements CanvasView, Listener { (shape as any).clear(); shape.attr('points', stringified); - if (state.shapeType === 'points') { + if (state.shapeType === 'points' && !state.hidden) { this.selectize(false, shape); this.setupPoints(shape as SVG.PolyLine, state); } @@ -1187,7 +1187,7 @@ export class CanvasViewImpl implements CanvasView, Listener { (shape as any).off('resizestart'); (shape as any).off('resizing'); (shape as any).off('resizedone'); - (shape as any).resize(false); + (shape as any).resize('stop'); // TODO: Hide text only if it is hidden by settings const text = this.svgTexts[clientID]; @@ -1543,6 +1543,8 @@ export class CanvasViewImpl implements CanvasView, Listener { group.on('click.canvas', (event: MouseEvent): void => { // Need to redispatch the event on another element basicPolyline.fire(new MouseEvent('click', event)); + // redispatch event to canvas to be able merge points clicking them + this.content.dispatchEvent(new MouseEvent('click', event)); }); group.bbox = basicPolyline.bbox.bind(basicPolyline); diff --git a/cvat-core/src/annotations-collection.js b/cvat-core/src/annotations-collection.js index e3ba2735bc96..e06e60f49bad 100644 --- a/cvat-core/src/annotations-collection.js +++ b/cvat-core/src/annotations-collection.js @@ -871,8 +871,10 @@ // In particular consider first and last frame as keyframes for all frames const statesData = [].concat( (frame in this.shapes ? this.shapes[frame] : []) + .filter((shape) => !shape.removed) .map((shape) => shape.get(frame)), (frame in this.tags ? this.tags[frame] : []) + .filter((tag) => !tag.removed) .map((tag) => tag.get(frame)), ); const tracks = Object.values(this.tracks) @@ -880,7 +882,7 @@ frame in track.shapes || frame === frameFrom || frame === frameTo - )); + )).filter((track) => !track.removed); statesData.push( ...tracks.map((track) => track.get(frame)) .filter((state) => !state.outside), diff --git a/cvat-ui/src/components/annotation-page/annotation-page.tsx b/cvat-ui/src/components/annotation-page/annotation-page.tsx index db5b6500e9b6..529ea77e218a 100644 --- a/cvat-ui/src/components/annotation-page/annotation-page.tsx +++ b/cvat-ui/src/components/annotation-page/annotation-page.tsx @@ -36,7 +36,17 @@ export default function AnnotationPageComponent(props: Props): JSX.Element { useEffect(() => { saveLogs(); - return saveLogs; + const root = window.document.getElementById('root'); + if (root) { + root.style.minHeight = '768px'; + } + + return () => { + saveLogs(); + if (root) { + root.style.minHeight = ''; + } + }; }, []); if (job === null) { diff --git a/cvat-ui/src/components/create-task-page/styles.scss b/cvat-ui/src/components/create-task-page/styles.scss index 3419d8608342..df2bcc459cca 100644 --- a/cvat-ui/src/components/create-task-page/styles.scss +++ b/cvat-ui/src/components/create-task-page/styles.scss @@ -6,7 +6,7 @@ .cvat-create-task-form-wrapper { text-align: center; - margin-top: 40px; + padding-top: 40px; overflow-y: auto; height: 90%; diff --git a/cvat-ui/src/components/tasks-page/styles.scss b/cvat-ui/src/components/tasks-page/styles.scss index b2fbc9be79c2..66cd8d4c3349 100644 --- a/cvat-ui/src/components/tasks-page/styles.scss +++ b/cvat-ui/src/components/tasks-page/styles.scss @@ -9,7 +9,7 @@ height: 100%; > div:nth-child(1) { - margin-bottom: 10px; + padding-bottom: 10px; div > { span { @@ -36,11 +36,11 @@ > div:nth-child(3) { height: 83%; - margin-top: 10px; + padding-top: 10px; } > div:nth-child(4) { - margin-top: 10px; + padding-top: 10px; } } diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx index b80fcbbca61c..d7855e5d0446 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx @@ -7,6 +7,7 @@ import copy from 'copy-to-clipboard'; import { connect } from 'react-redux'; import { LogType } from 'cvat-logger'; +import { Canvas, isAbleToChangeFrame } from 'cvat-canvas'; import { ActiveControl, CombinedState, ColorBy } from 'reducers/interfaces'; import { collapseObjectItems, @@ -24,7 +25,6 @@ import { import ObjectStateItemComponent from 'components/annotation-page/standard-workspace/objects-side-bar/object-item'; - interface OwnProps { clientID: number; } @@ -44,6 +44,7 @@ interface StateToProps { minZLayer: number; maxZLayer: number; normalizedKeyMap: Record; + canvasInstance: Canvas; } interface DispatchToProps { @@ -84,6 +85,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps { canvas: { ready, activeControl, + instance: canvasInstance, }, colors, }, @@ -119,6 +121,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps { minZLayer, maxZLayer, normalizedKeyMap, + canvasInstance, }; } @@ -166,72 +169,44 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { type Props = StateToProps & DispatchToProps; class ObjectItemContainer extends React.PureComponent { private navigateFirstKeyframe = (): void => { - const { - objectState, - changeFrame, - frameNumber, - } = this.props; - + const { objectState, frameNumber } = this.props; const { first } = objectState.keyframes; if (first !== frameNumber) { - changeFrame(first); + this.changeFrame(first); } }; private navigatePrevKeyframe = (): void => { - const { - objectState, - changeFrame, - frameNumber, - } = this.props; - + const { objectState, frameNumber } = this.props; const { prev } = objectState.keyframes; if (prev !== null && prev !== frameNumber) { - changeFrame(prev); + this.changeFrame(prev); } }; private navigateNextKeyframe = (): void => { - const { - objectState, - changeFrame, - frameNumber, - } = this.props; - + const { objectState, frameNumber } = this.props; const { next } = objectState.keyframes; if (next !== null && next !== frameNumber) { - changeFrame(next); + this.changeFrame(next); } }; private navigateLastKeyframe = (): void => { - const { - objectState, - changeFrame, - frameNumber, - } = this.props; - + const { objectState, frameNumber } = this.props; const { last } = objectState.keyframes; if (last !== frameNumber) { - changeFrame(last); + this.changeFrame(last); } }; private copy = (): void => { - const { - objectState, - copyShape, - } = this.props; - + const { objectState, copyShape } = this.props; copyShape(objectState); }; private propagate = (): void => { - const { - objectState, - propagateObject, - } = this.props; - + const { objectState, propagateObject } = this.props; propagateObject(objectState); }; @@ -422,6 +397,13 @@ class ObjectItemContainer extends React.PureComponent { this.commit(); }; + private changeFrame(frame: number): void { + const { changeFrame, canvasInstance } = this.props; + if (isAbleToChangeFrame(canvasInstance)) { + changeFrame(frame); + } + } + private commit(): void { const { objectState, diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/objects-list.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/objects-list.tsx index 49e61a27041e..df533b87e7e3 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/objects-list.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/objects-list.tsx @@ -15,6 +15,7 @@ import { copyShape as copyShapeAction, propagateObject as propagateObjectAction, } from 'actions/annotation-actions'; +import { Canvas, isAbleToChangeFrame } from 'cvat-canvas'; import { CombinedState, StatesOrdering, ObjectType } from 'reducers/interfaces'; interface StateToProps { @@ -32,6 +33,7 @@ interface StateToProps { annotationsFiltersHistory: string[]; keyMap: Record; normalizedKeyMap: Record; + canvasInstance: Canvas; } interface DispatchToProps { @@ -65,6 +67,9 @@ function mapStateToProps(state: CombinedState): StateToProps { number: frameNumber, }, }, + canvas: { + instance: canvasInstance, + }, tabContentHeight: listHeight, }, shortcuts: { @@ -104,6 +109,7 @@ function mapStateToProps(state: CombinedState): StateToProps { annotationsFiltersHistory, keyMap, normalizedKeyMap, + canvasInstance, }; } @@ -254,6 +260,7 @@ class ObjectsListContainer extends React.PureComponent { minZLayer, keyMap, normalizedKeyMap, + canvasInstance, } = this.props; const { sortedStatesID, @@ -388,7 +395,7 @@ class ObjectsListContainer extends React.PureComponent { if (state && state.objectType === ObjectType.TRACK) { const frame = typeof (state.keyframes.next) === 'number' ? state.keyframes.next : null; - if (frame !== null) { + if (frame !== null && isAbleToChangeFrame(canvasInstance)) { changeFrame(frame); } } @@ -399,7 +406,7 @@ class ObjectsListContainer extends React.PureComponent { if (state && state.objectType === ObjectType.TRACK) { const frame = typeof (state.keyframes.prev) === 'number' ? state.keyframes.prev : null; - if (frame !== null) { + if (frame !== null && isAbleToChangeFrame(canvasInstance)) { changeFrame(frame); } } diff --git a/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx b/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx index 21226ce97089..fcfe943b9a8a 100644 --- a/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx +++ b/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx @@ -23,6 +23,7 @@ import { changeWorkspace as changeWorkspaceAction, activateObject, } from 'actions/annotation-actions'; +import { Canvas, isAbleToChangeFrame } from 'cvat-canvas'; import AnnotationTopBarComponent from 'components/annotation-page/top-bar/top-bar'; import { CombinedState, FrameSpeed, Workspace } from 'reducers/interfaces'; @@ -45,6 +46,7 @@ interface StateToProps { workspace: Workspace; keyMap: Record; normalizedKeyMap: Record; + canvasInstance: Canvas; } interface DispatchToProps { @@ -81,6 +83,7 @@ function mapStateToProps(state: CombinedState): StateToProps { }, canvas: { ready: canvasIsReady, + instance: canvasInstance, }, workspace, }, @@ -118,6 +121,7 @@ function mapStateToProps(state: CombinedState): StateToProps { workspace, keyMap, normalizedKeyMap, + canvasInstance, }; } @@ -197,6 +201,7 @@ class AnnotationTopBarContainer extends React.PureComponent { frameDelay, playing, canvasIsReady, + canvasInstance, onSwitchPlay, onChangeFrame, } = this.props; @@ -217,10 +222,14 @@ class AnnotationTopBarContainer extends React.PureComponent { setTimeout(() => { const { playing: stillPlaying } = this.props; if (stillPlaying) { - onChangeFrame( - frameNumber + 1 + framesSkiped, - stillPlaying, framesSkiped + 1, - ); + if (isAbleToChangeFrame(canvasInstance)) { + onChangeFrame( + frameNumber + 1 + framesSkiped, + stillPlaying, framesSkiped + 1, + ); + } else { + onSwitchPlay(false); + } } }, frameDelay); } else { @@ -240,9 +249,12 @@ class AnnotationTopBarContainer extends React.PureComponent { undo, jobInstance, frameNumber, + canvasInstance, } = this.props; - undo(jobInstance, frameNumber); + if (isAbleToChangeFrame(canvasInstance)) { + undo(jobInstance, frameNumber); + } }; private redo = (): void => { @@ -250,9 +262,12 @@ class AnnotationTopBarContainer extends React.PureComponent { redo, jobInstance, frameNumber, + canvasInstance, } = this.props; - redo(jobInstance, frameNumber); + if (isAbleToChangeFrame(canvasInstance)) { + redo(jobInstance, frameNumber); + } }; private showStatistics = (): void => { @@ -285,7 +300,6 @@ class AnnotationTopBarContainer extends React.PureComponent { jobInstance, playing, onSwitchPlay, - onChangeFrame, } = this.props; const newFrame = jobInstance.startFrame; @@ -293,7 +307,7 @@ class AnnotationTopBarContainer extends React.PureComponent { if (playing) { onSwitchPlay(false); } - onChangeFrame(newFrame); + this.changeFrame(newFrame); } }; @@ -304,7 +318,6 @@ class AnnotationTopBarContainer extends React.PureComponent { jobInstance, playing, onSwitchPlay, - onChangeFrame, } = this.props; const newFrame = Math @@ -313,7 +326,7 @@ class AnnotationTopBarContainer extends React.PureComponent { if (playing) { onSwitchPlay(false); } - onChangeFrame(newFrame); + this.changeFrame(newFrame); } }; @@ -323,7 +336,6 @@ class AnnotationTopBarContainer extends React.PureComponent { jobInstance, playing, onSwitchPlay, - onChangeFrame, } = this.props; const newFrame = Math @@ -332,7 +344,7 @@ class AnnotationTopBarContainer extends React.PureComponent { if (playing) { onSwitchPlay(false); } - onChangeFrame(newFrame); + this.changeFrame(newFrame); } }; @@ -342,7 +354,6 @@ class AnnotationTopBarContainer extends React.PureComponent { jobInstance, playing, onSwitchPlay, - onChangeFrame, } = this.props; const newFrame = Math @@ -351,7 +362,7 @@ class AnnotationTopBarContainer extends React.PureComponent { if (playing) { onSwitchPlay(false); } - onChangeFrame(newFrame); + this.changeFrame(newFrame); } }; @@ -362,7 +373,6 @@ class AnnotationTopBarContainer extends React.PureComponent { jobInstance, playing, onSwitchPlay, - onChangeFrame, } = this.props; const newFrame = Math @@ -371,7 +381,7 @@ class AnnotationTopBarContainer extends React.PureComponent { if (playing) { onSwitchPlay(false); } - onChangeFrame(newFrame); + this.changeFrame(newFrame); } }; @@ -381,7 +391,6 @@ class AnnotationTopBarContainer extends React.PureComponent { jobInstance, playing, onSwitchPlay, - onChangeFrame, } = this.props; const newFrame = jobInstance.stopFrame; @@ -389,7 +398,7 @@ class AnnotationTopBarContainer extends React.PureComponent { if (playing) { onSwitchPlay(false); } - onChangeFrame(newFrame); + this.changeFrame(newFrame); } }; @@ -403,22 +412,16 @@ class AnnotationTopBarContainer extends React.PureComponent { }; private onChangePlayerSliderValue = (value: SliderValue): void => { - const { - playing, - onSwitchPlay, - onChangeFrame, - } = this.props; - + const { playing, onSwitchPlay } = this.props; if (playing) { onSwitchPlay(false); } - onChangeFrame(value as number); + this.changeFrame(value as number); }; private onChangePlayerInputValue = (value: number): void => { const { onSwitchPlay, - onChangeFrame, playing, frameNumber, } = this.props; @@ -427,7 +430,7 @@ class AnnotationTopBarContainer extends React.PureComponent { if (playing) { onSwitchPlay(false); } - onChangeFrame(value); + this.changeFrame(value); } }; @@ -441,6 +444,13 @@ class AnnotationTopBarContainer extends React.PureComponent { copy(url); }; + private changeFrame(frame: number): void { + const { onChangeFrame, canvasInstance } = this.props; + if (isAbleToChangeFrame(canvasInstance)) { + onChangeFrame(frame); + } + } + private beforeUnloadCallback(event: BeforeUnloadEvent): any { const { jobInstance } = this.props; if (jobInstance.annotations.hasUnsavedChanges()) { @@ -472,6 +482,7 @@ class AnnotationTopBarContainer extends React.PureComponent { changeWorkspace, keyMap, normalizedKeyMap, + canvasInstance, } = this.props; const preventDefault = (event: KeyboardEvent | undefined): void => { @@ -537,13 +548,17 @@ class AnnotationTopBarContainer extends React.PureComponent { }, SEARCH_FORWARD: (event: KeyboardEvent | undefined) => { preventDefault(event); - if (frameNumber + 1 <= stopFrame && canvasIsReady) { + if (frameNumber + 1 <= stopFrame && canvasIsReady + && isAbleToChangeFrame(canvasInstance) + ) { searchAnnotations(jobInstance, frameNumber + 1, stopFrame); } }, SEARCH_BACKWARD: (event: KeyboardEvent | undefined) => { preventDefault(event); - if (frameNumber - 1 >= startFrame && canvasIsReady) { + if (frameNumber - 1 >= startFrame && canvasIsReady + && isAbleToChangeFrame(canvasInstance) + ) { searchAnnotations(jobInstance, frameNumber - 1, startFrame); } }, diff --git a/cvat-ui/src/cvat-canvas.ts b/cvat-ui/src/cvat-canvas.ts index 6317c43567e7..1a21be5a3718 100644 --- a/cvat-ui/src/cvat-canvas.ts +++ b/cvat-ui/src/cvat-canvas.ts @@ -9,9 +9,15 @@ import { RectDrawingMethod, } from '../../cvat-canvas/src/typescript/canvas'; +function isAbleToChangeFrame(canvas: Canvas): boolean { + return ![CanvasMode.DRAG, CanvasMode.EDIT, CanvasMode.RESIZE] + .includes(canvas.mode()); +} + export { Canvas, CanvasMode, CanvasVersion, RectDrawingMethod, + isAbleToChangeFrame, }; diff --git a/cvat-ui/src/styles.scss b/cvat-ui/src/styles.scss index 965ea7567361..cbd6a4c2e763 100644 --- a/cvat-ui/src/styles.scss +++ b/cvat-ui/src/styles.scss @@ -48,5 +48,4 @@ hr { height: 100%; display: grid; min-width: 1280px; - min-height: 768px; } diff --git a/cvat/apps/engine/static/engine/js/shapeMerger.js b/cvat/apps/engine/static/engine/js/shapeMerger.js index c7fc478c540b..f04853296bc0 100644 --- a/cvat/apps/engine/static/engine/js/shapeMerger.js +++ b/cvat/apps/engine/static/engine/js/shapeMerger.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018-2020 Intel Corporation * * SPDX-License-Identifier: MIT */ @@ -145,7 +145,7 @@ class ShapeMergerModel extends Listener { let nextFrame = frame + 1; let stopFrame = window.cvat.player.frames.stop; let type = shapeDict[frame].shape.type; - if (type === 'annotation_box' && !(nextFrame in shapeDict) && nextFrame <= stopFrame) { + if (type.startsWith('annotation_') && !(nextFrame in shapeDict) && nextFrame <= stopFrame) { let copy = Object.assign({}, object.shapes[object.shapes.length - 1]); copy.outside = true; copy.frame += 1; diff --git a/cvat/apps/engine/templates/engine/annotation.html b/cvat/apps/engine/templates/engine/annotation.html index 66cf69fad65a..d78b4ecf93e6 100644 --- a/cvat/apps/engine/templates/engine/annotation.html +++ b/cvat/apps/engine/templates/engine/annotation.html @@ -1,5 +1,5 @@ @@ -451,7 +451,7 @@