Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Semi-automatic tools enhancements (Stage 0) #3417

Merged
merged 17 commits into from
Jul 20, 2021
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support of cloud storage without copying data into CVAT: server part (<https://github.com/openvinotoolkit/cvat/pull/2620>)
- Filter `is_active` for user list (<https://github.com/openvinotoolkit/cvat/pull/3235>)
- Ability to export/import tasks (<https://github.com/openvinotoolkit/cvat/pull/3056>)
- Explicit "Done" button when drawing any polyshapes (<https://github.com/openvinotoolkit/cvat/pull/3417>)

### Changed

- Updated manifest format, added meta with related images (<https://github.com/openvinotoolkit/cvat/pull/3122>)
- Update of COCO format documentation (<https://github.com/openvinotoolkit/cvat/pull/3197>)
- Updated Webpack Dev Server config to add proxxy (<https://github.com/openvinotoolkit/cvat/pull/3368>)
- Update to Django 3.1.12 (<https://github.com/openvinotoolkit/cvat/pull/3378>)
- Updated visibility for removable points in AI tools (<https://github.com/openvinotoolkit/cvat/pull/3417>)
- Updated UI handling for IOG serverless function (<https://github.com/openvinotoolkit/cvat/pull/3417>)

### Deprecated

Expand Down
2 changes: 1 addition & 1 deletion cvat-canvas/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cvat-canvas/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-canvas",
"version": "2.4.5",
"version": "2.5.0",
"description": "Part of Computer Vision Annotation Tool which presents its canvas library",
"main": "src/canvas.ts",
"scripts": {
Expand Down
11 changes: 11 additions & 0 deletions cvat-canvas/src/scss/canvas.scss
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,17 @@ polyline.cvat_canvas_shape_splitting {
fill: blueviolet;
}

.cvat_canvas_interact_intermediate_shape {
@extend .cvat_canvas_shape;
}

.cvat_canvas_removable_interaction_point {
cursor:
url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAxMCAxMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEgMUw5IDlNMSA5TDkgMSIgc3Ryb2tlPSJibGFjayIvPgo8L3N2Zz4K')
10 10,
auto;
}

.svg_select_boundingRect {
opacity: 0;
pointer-events: none;
Expand Down
7 changes: 6 additions & 1 deletion cvat-canvas/src/typescript/canvasModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,14 @@ export interface InteractionData {
minPosVertices?: number;
minNegVertices?: number;
enableNegVertices?: boolean;
startWithBox?: boolean;
enableThreshold?: boolean;
enableSliding?: boolean;
allowRemoveOnlyLast?: boolean;
intermediateShape?: {
shapeType: string;
points: number[];
};
}

export interface InteractionResult {
Expand Down Expand Up @@ -551,7 +556,7 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
throw Error(`Canvas is busy. Action: ${this.data.mode}`);
}

if (interactionData.enabled) {
if (interactionData.enabled && !interactionData.intermediateShape) {
if (this.data.interactionData.enabled) {
throw new Error('Interaction has been already started');
} else if (!interactionData.shapeType) {
Expand Down
11 changes: 7 additions & 4 deletions cvat-canvas/src/typescript/canvasView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import consts from './consts';
import {
translateToSVG,
translateFromSVG,
translateToCanvas,
pointsToNumberArray,
parsePoints,
displayShapeSize,
Expand Down Expand Up @@ -103,7 +104,7 @@ export class CanvasViewImpl implements CanvasView, Listener {

private translateToCanvas(points: number[]): number[] {
const { offset } = this.controller.geometry;
return points.map((coord: number): number => coord + offset);
return translateToCanvas(offset, points);
}

private translateFromCanvas(points: number[]): number[] {
Expand Down Expand Up @@ -1267,9 +1268,11 @@ export class CanvasViewImpl implements CanvasView, Listener {
}
} else if (reason === UpdateReasons.INTERACT) {
const data: InteractionData = this.controller.interactionData;
if (data.enabled && this.mode === Mode.IDLE) {
this.canvas.style.cursor = 'crosshair';
this.mode = Mode.INTERACT;
if (data.enabled && (this.mode === Mode.IDLE || data.intermediateShape)) {
if (!data.intermediateShape) {
this.canvas.style.cursor = 'crosshair';
this.mode = Mode.INTERACT;
}
this.interactionHandler.interact(data);
} else {
this.canvas.style.cursor = '';
Expand Down
74 changes: 65 additions & 9 deletions cvat-canvas/src/typescript/interactionHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import * as SVG from 'svg.js';
import consts from './consts';
import Crosshair from './crosshair';
import { translateToSVG } from './shared';
import {
translateToSVG, PropType, stringifyPoints, translateToCanvas,
} from './shared';
import { InteractionData, InteractionResult, Geometry } from './canvasModel';

export interface InteractionHandler {
Expand All @@ -26,6 +28,8 @@ export class InteractionHandlerImpl implements InteractionHandler {
private crosshair: Crosshair;
private threshold: SVG.Rect | null;
private thresholdRectSize: number;
private intermediateShape: PropType<InteractionData, 'intermediateShape'>;
private drawnIntermediateShape: SVG.Shape;

private prepareResult(): InteractionResult[] {
return this.interactionShapes.map(
Expand Down Expand Up @@ -121,8 +125,10 @@ export class InteractionHandlerImpl implements InteractionHandler {
}
}

self.addClass('cvat_canvas_removable_interaction_point');
self.attr({
'stroke-width': consts.POINTS_SELECTED_STROKE_WIDTH / this.geometry.scale,
r: (consts.BASE_POINT_SIZE * 1.5) / this.geometry.scale,
});

self.on('mousedown', (_e: MouseEvent): void => {
Expand All @@ -140,8 +146,10 @@ export class InteractionHandlerImpl implements InteractionHandler {
});

self.on('mouseleave', (): void => {
self.removeClass('cvat_canvas_removable_interaction_point');
self.attr({
'stroke-width': consts.POINTS_STROKE_WIDTH / this.geometry.scale,
r: consts.BASE_POINT_SIZE / this.geometry.scale,
});

self.off('mousedown');
Expand All @@ -153,7 +161,7 @@ export class InteractionHandlerImpl implements InteractionHandler {
this.canvas.on('mousedown.interaction', eventListener);
}

private interactRectangle(): void {
private interactRectangle(shouldFinish: boolean, onContinue?: () => void): void {
let initialized = false;
const eventListener = (e: MouseEvent): void => {
if (e.button === 0 && !e.altKey) {
Expand All @@ -170,11 +178,15 @@ export class InteractionHandlerImpl implements InteractionHandler {
this.canvas.on('mousedown.interaction', eventListener);
this.currentInteractionShape
.on('drawstop', (): void => {
this.canvas.off('mousedown.interaction', eventListener);
this.interactionShapes.push(this.currentInteractionShape);
this.shapesWereUpdated = true;

this.canvas.off('mousedown.interaction', eventListener);
this.interact({ enabled: false });
if (shouldFinish) {
this.interact({ enabled: false });
} else if (onContinue) {
onContinue();
}
})
.addClass('cvat_canvas_shape_drawing')
.attr({
Expand All @@ -194,15 +206,24 @@ export class InteractionHandlerImpl implements InteractionHandler {

private startInteraction(): void {
if (this.interactionData.shapeType === 'rectangle') {
this.interactRectangle();
this.interactRectangle(true);
} else if (this.interactionData.shapeType === 'points') {
this.interactPoints();
if (this.interactionData.startWithBox) {
this.interactRectangle(false, (): void => this.interactPoints());
} else {
this.interactPoints();
}
} else {
throw new Error('Interactor implementation supports only rectangle and points');
}
}

private release(): void {
if (this.drawnIntermediateShape) {
this.drawnIntermediateShape.remove();
this.drawnIntermediateShape = null;
}

if (this.crosshair) {
this.removeCrosshair();
}
Expand Down Expand Up @@ -241,6 +262,31 @@ export class InteractionHandlerImpl implements InteractionHandler {
return imageX >= 0 && imageX < width && imageY >= 0 && imageY < height;
}

private updateIntermediateShape(): void {
const { intermediateShape, geometry } = this;
if (this.drawnIntermediateShape) {
this.drawnIntermediateShape.remove();
}

if (!intermediateShape) return;
const { shapeType, points } = intermediateShape;
if (shapeType === 'polygon') {
this.drawnIntermediateShape = this.canvas
.polygon(stringifyPoints(translateToCanvas(geometry.offset, points)))
.attr({
'color-rendering': 'optimizeQuality',
'shape-rendering': 'geometricprecision',
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
fill: 'none',
})
.addClass('cvat_canvas_interact_intermediate_shape');
} else {
throw new Error(
`Shape type "${shapeType}" was not implemented at interactionHandler::updateIntermediateShape`,
);
}
}

public constructor(
onInteraction: (
shapes: InteractionResult[] | null,
Expand All @@ -264,6 +310,8 @@ export class InteractionHandlerImpl implements InteractionHandler {
this.crosshair = new Crosshair();
this.threshold = null;
this.thresholdRectSize = 300;
this.intermediateShape = null;
this.drawnIntermediateShape = null;
this.cursorPosition = {
x: 0,
y: 0,
Expand Down Expand Up @@ -334,16 +382,24 @@ export class InteractionHandlerImpl implements InteractionHandler {
: [...this.interactionShapes];
for (const shape of shapesToBeScaled) {
if (shape.type === 'circle') {
(shape as SVG.Circle).radius(consts.BASE_POINT_SIZE / this.geometry.scale);
shape.attr('stroke-width', consts.POINTS_STROKE_WIDTH / this.geometry.scale);
if (shape.hasClass('cvat_canvas_removable_interaction_point')) {
(shape as SVG.Circle).radius((consts.BASE_POINT_SIZE * 1.5) / this.geometry.scale);
shape.attr('stroke-width', consts.POINTS_SELECTED_STROKE_WIDTH / this.geometry.scale);
} else {
(shape as SVG.Circle).radius(consts.BASE_POINT_SIZE / this.geometry.scale);
shape.attr('stroke-width', consts.POINTS_STROKE_WIDTH / this.geometry.scale);
}
} else {
shape.attr('stroke-width', consts.BASE_STROKE_WIDTH / this.geometry.scale);
}
}
}

public interact(interactionData: InteractionData): void {
if (interactionData.enabled) {
if (interactionData.intermediateShape) {
this.intermediateShape = interactionData.intermediateShape;
this.updateIntermediateShape();
} else if (interactionData.enabled) {
this.interactionData = interactionData;
this.initInteraction();
this.startInteraction();
Expand Down
6 changes: 6 additions & 0 deletions cvat-canvas/src/typescript/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,9 @@ export function vectorLength(vector: Vector2D): number {
const sqrJ = vector.j ** 2;
return Math.sqrt(sqrI + sqrJ);
}

export function translateToCanvas(offset: number, points: number[]): number[] {
return points.map((coord: number): number => coord + offset);
}

export type PropType<T, Prop extends keyof T> = T[Prop];
3 changes: 2 additions & 1 deletion cvat-core/src/ml-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class MLModel {
this._params = {
canvas: {
minPosVertices: data.min_pos_points,
enableNegVertices: true,
enableNegVertices: !['DEXTR'].includes(data.name),
bsekachev marked this conversation as resolved.
Show resolved Hide resolved
startWithBox: data.startswith_box,
},
};
}
Expand Down
2 changes: 1 addition & 1 deletion cvat-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cvat-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.20.7",
"version": "1.21.0",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion cvat-ui/src/assets/fullscreen-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion cvat-ui/src/assets/info-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion cvat-ui/src/assets/main-menu-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion cvat-ui/src/assets/object-filter-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion cvat-ui/src/assets/redo-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion cvat-ui/src/assets/save-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion cvat-ui/src/assets/undo-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading