From efd43637a41b7a7d1a494aae11e98a57964a541d Mon Sep 17 00:00:00 2001 From: Boris Sekachev Date: Tue, 6 Sep 2022 14:51:39 +0300 Subject: [PATCH] Fixed redrawing a skeleton (Shift+N) (#4906) * Fixed redrawing a skeleton (Shift+N) * Updated version * Added cypress test * Enabled disabled tests * Updated test IDs --- cvat-canvas/package.json | 2 +- cvat-canvas/src/typescript/canvasView.ts | 3 +- cvat-ui/package.json | 2 +- cvat-ui/src/actions/annotation-actions.ts | 64 ++++++------------- .../cypress/integration/skeletons_pipeline.js | 63 ++++++++++++------ 5 files changed, 67 insertions(+), 67 deletions(-) diff --git a/cvat-canvas/package.json b/cvat-canvas/package.json index bd3706b4..bd865fb1 100644 --- a/cvat-canvas/package.json +++ b/cvat-canvas/package.json @@ -1,6 +1,6 @@ { "name": "cvat-canvas", - "version": "2.15.1", + "version": "2.15.2", "description": "Part of Computer Vision Annotation Tool which presents its canvas library", "main": "src/canvas.ts", "scripts": { diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index bf25a369..c120cd73 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -263,7 +263,8 @@ export class CanvasViewImpl implements CanvasView, Listener { } if (data) { - const { clientID, points } = data as any; + const { clientID, elements } = data as any; + const points = data.points || elements.map((el: any) => el.points).flat(); if (typeof clientID === 'number') { const event: CustomEvent = new CustomEvent('canvas.canceled', { bubbles: false, diff --git a/cvat-ui/package.json b/cvat-ui/package.json index 1dfdc1af..b95a900d 100644 --- a/cvat-ui/package.json +++ b/cvat-ui/package.json @@ -1,6 +1,6 @@ { "name": "cvat-ui", - "version": "1.41.3", + "version": "1.41.4", "description": "CVAT single-page application", "main": "src/index.tsx", "scripts": { diff --git a/cvat-ui/src/actions/annotation-actions.ts b/cvat-ui/src/actions/annotation-actions.ts index 90bcc1cf..b404333f 100644 --- a/cvat-ui/src/actions/annotation-actions.ts +++ b/cvat-ui/src/actions/annotation-actions.ts @@ -1,4 +1,5 @@ // Copyright (C) 2020-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -1516,6 +1517,16 @@ export function searchEmptyFrameAsync(sessionInstance: any, frameFrom: number, f }; } +const ShapeTypeToControl: Record = { + [ShapeType.RECTANGLE]: ActiveControl.DRAW_RECTANGLE, + [ShapeType.POLYLINE]: ActiveControl.DRAW_POLYLINE, + [ShapeType.POLYGON]: ActiveControl.DRAW_POLYGON, + [ShapeType.POINTS]: ActiveControl.DRAW_POINTS, + [ShapeType.CUBOID]: ActiveControl.DRAW_CUBOID, + [ShapeType.ELLIPSE]: ActiveControl.DRAW_ELLIPSE, + [ShapeType.SKELETON]: ActiveControl.DRAW_SKELETON, +}; + export function pasteShapeAsync(): ThunkAction { return async (dispatch: ActionCreator): Promise => { const { @@ -1528,22 +1539,7 @@ export function pasteShapeAsync(): ThunkAction { } = getStore().getState().annotation; if (initialState && canvasInstance) { - let activeControl = ActiveControl.CURSOR; - if (initialState.shapeType === ShapeType.RECTANGLE) { - activeControl = ActiveControl.DRAW_RECTANGLE; - } else if (initialState.shapeType === ShapeType.POINTS) { - activeControl = ActiveControl.DRAW_POINTS; - } else if (initialState.shapeType === ShapeType.POLYGON) { - activeControl = ActiveControl.DRAW_POLYGON; - } else if (initialState.shapeType === ShapeType.POLYLINE) { - activeControl = ActiveControl.DRAW_POLYLINE; - } else if (initialState.shapeType === ShapeType.CUBOID) { - activeControl = ActiveControl.DRAW_CUBOID; - } else if (initialState.shapeType === ShapeType.ELLIPSE) { - activeControl = ActiveControl.DRAW_ELLIPSE; - } else if (initialState.shapeType === ShapeType.SKELETON) { - activeControl = ActiveControl.DRAW_SKELETON; - } + const activeControl = ShapeTypeToControl[initialState.shapeType as ShapeType] || ActiveControl.CURSOR; dispatch({ type: AnnotationActionTypes.PASTE_SHAPE, @@ -1623,21 +1619,8 @@ export function repeatDrawShapeAsync(): ThunkAction { return; } - if (activeShapeType === ShapeType.RECTANGLE) { - activeControl = ActiveControl.DRAW_RECTANGLE; - } else if (activeShapeType === ShapeType.POINTS) { - activeControl = ActiveControl.DRAW_POINTS; - } else if (activeShapeType === ShapeType.POLYGON) { - activeControl = ActiveControl.DRAW_POLYGON; - } else if (activeShapeType === ShapeType.POLYLINE) { - activeControl = ActiveControl.DRAW_POLYLINE; - } else if (activeShapeType === ShapeType.CUBOID) { - activeControl = ActiveControl.DRAW_CUBOID; - } else if (activeShapeType === ShapeType.ELLIPSE) { - activeControl = ActiveControl.DRAW_ELLIPSE; - } else if (activeShapeType === ShapeType.SKELETON) { - activeControl = ActiveControl.DRAW_SKELETON; - } + + activeControl = ShapeTypeToControl[activeShapeType]; dispatch({ type: AnnotationActionTypes.REPEAT_DRAW_SHAPE, @@ -1689,31 +1672,20 @@ export function redrawShapeAsync(): ThunkAction { if (activatedStateID !== null) { const [state] = states.filter((_state: any): boolean => _state.clientID === activatedStateID); if (state && state.objectType !== ObjectType.TAG) { - let activeControl = ActiveControl.CURSOR; - if (state.shapeType === ShapeType.RECTANGLE) { - activeControl = ActiveControl.DRAW_RECTANGLE; - } else if (state.shapeType === ShapeType.POINTS) { - activeControl = ActiveControl.DRAW_POINTS; - } else if (state.shapeType === ShapeType.POLYGON) { - activeControl = ActiveControl.DRAW_POLYGON; - } else if (state.shapeType === ShapeType.POLYLINE) { - activeControl = ActiveControl.DRAW_POLYLINE; - } else if (state.shapeType === ShapeType.CUBOID) { - activeControl = ActiveControl.DRAW_CUBOID; - } else if (state.shapeType === ShapeType.SKELETON) { - activeControl = ActiveControl.DRAW_SKELETON; - } - + const activeControl = ShapeTypeToControl[state.shapeType as ShapeType] || ActiveControl.CURSOR; dispatch({ type: AnnotationActionTypes.REPEAT_DRAW_SHAPE, payload: { activeControl, }, }); + if (canvasInstance instanceof Canvas) { canvasInstance.cancel(); } + canvasInstance.draw({ + skeletonSVG: state.shapeType === ShapeType.SKELETON ? state.label.structure.svg : undefined, enabled: true, redraw: activatedStateID, shapeType: state.shapeType, diff --git a/tests/cypress/integration/skeletons_pipeline.js b/tests/cypress/integration/skeletons_pipeline.js index 8fff6b38..21be2f0e 100644 --- a/tests/cypress/integration/skeletons_pipeline.js +++ b/tests/cypress/integration/skeletons_pipeline.js @@ -19,6 +19,12 @@ context('Manipulations with skeletons', () => { text: 'skeletons pipeline', count: 5, }; + const skeletonPosition = { + xtl: 100, + ytl: 100, + xbr: 300, + ybr: 300, + }; let taskID = null; before(() => { @@ -135,11 +141,8 @@ context('Manipulations with skeletons', () => { describe('Working with objects', () => { function createSkeletonObject(shapeType) { cy.createSkeleton({ + ...skeletonPosition, labelName, - xtl: 100, - ytl: 100, - xbr: 300, - ybr: 300, type: `${shapeType[0].toUpperCase()}${shapeType.slice(1).toLowerCase()}`, }); cy.get('#cvat_canvas_shape_1').should('exist').and('be.visible'); @@ -172,19 +175,43 @@ context('Manipulations with skeletons', () => { cy.removeAnnotations(); }); - it('Creating and removing a skeleton track', () => { + it('Creating, re-drawing, and removing a skeleton track', () => { createSkeletonObject('track'); + + // redraw a tracked shape on the latest frame + const REDRAW_MARGIN = 400; + let prevX = Number.MAX_SAFE_INTEGER; + let prevY = Number.MAX_SAFE_INTEGER; + cy.goCheckFrameNumber(imageParams.count - 1); + cy.get('#cvat_canvas_shape_1').within(() => { + cy.get('rect').then(($rect) => { + prevX = +$rect[0].getAttribute('x'); + prevY = +$rect[0].getAttribute('y'); + }); + }); + cy.get('#cvat_canvas_shape_1').trigger('mousemove').should('have.class', 'cvat_canvas_shape_activated'); + cy.get('body').trigger('keydown', { keyCode: 78, code: 'KeyN', shiftKey: true }); + cy.get('.cvat-canvas-container') + .click(skeletonPosition.xtl + REDRAW_MARGIN, skeletonPosition.ytl + REDRAW_MARGIN) + .click(skeletonPosition.xbr + REDRAW_MARGIN, skeletonPosition.ybr + REDRAW_MARGIN); + cy.get('.cvat-cursor-control').should('have.class', 'cvat-active-canvas-control'); + cy.get('#cvat_canvas_shape_1').within(() => { + cy.get('rect').then(($rect) => { + expect(+$rect[0].getAttribute('x')).to.be.gt(prevX); + expect(+$rect[0].getAttribute('y')).to.be.gt(prevY); + }); + }); + // and, finally delete the skeleton deleteSkeleton('#cvat_canvas_shape_1', 'track', false); cy.removeAnnotations(); - + cy.goCheckFrameNumber(0); createSkeletonObject('track'); deleteSkeleton('#cvat_canvas_shape_1', 'track', true); - - cy.removeAnnotations(); }); it('Splitting two skeletons and merge them back', () => { + cy.removeAnnotations(); createSkeletonObject('track'); const splittingFrame = Math.trunc(imageParams.count / 2); @@ -195,32 +222,32 @@ context('Manipulations with skeletons', () => { // check objects after splitting cy.get('#cvat_canvas_shape_1').should('not.exist'); - cy.get('#cvat_canvas_shape_18').should('exist').and('not.be.visible'); - cy.get('#cvat_canvas_shape_24').should('exist').and('be.visible'); + cy.get('#cvat_canvas_shape_12').should('exist').and('not.be.visible'); + cy.get('#cvat_canvas_shape_18').should('exist').and('be.visible'); cy.goToNextFrame(splittingFrame + 1); - cy.get('#cvat_canvas_shape_18').should('not.exist'); - cy.get('#cvat_canvas_shape_24').should('exist').and('be.visible'); + cy.get('#cvat_canvas_shape_12').should('not.exist'); + cy.get('#cvat_canvas_shape_18').should('exist').and('be.visible'); // now merge them back cy.get('.cvat-merge-control').click(); - cy.get('#cvat_canvas_shape_24').click(); + cy.get('#cvat_canvas_shape_18').click(); cy.goCheckFrameNumber(0); - cy.get('#cvat_canvas_shape_18').click(); + cy.get('#cvat_canvas_shape_12').click(); cy.get('body').type('m'); // and check objects after merge + cy.get('#cvat_canvas_shape_12').should('not.exist'); cy.get('#cvat_canvas_shape_18').should('not.exist'); - cy.get('#cvat_canvas_shape_24').should('not.exist'); - cy.get('#cvat_canvas_shape_30').should('exist').and('be.visible'); + cy.get('#cvat_canvas_shape_24').should('exist').and('be.visible'); cy.goCheckFrameNumber(splittingFrame + 1); - cy.get('#cvat_canvas_shape_30').should('exist').and('be.visible'); + cy.get('#cvat_canvas_shape_24').should('exist').and('be.visible'); cy.goCheckFrameNumber(imageParams.count - 1); - cy.get('#cvat_canvas_shape_30').should('exist').and('be.visible'); + cy.get('#cvat_canvas_shape_24').should('exist').and('be.visible'); cy.removeAnnotations(); });