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

Single object annotation mode #7486

Merged
merged 45 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
33aa969
open mode by query parameters
bsekachev Feb 12, 2024
6492e1b
Draft solution
bsekachev Feb 12, 2024
dcd20ba
Updated implementation
bsekachev Feb 13, 2024
f70e1fd
Navigate only empty
bsekachev Feb 13, 2024
2f13539
Optionally hidden message
bsekachev Feb 13, 2024
7c97529
Merge branch 'develop' into bs/single_object_mode
bsekachev Feb 15, 2024
e5ef56c
Refactored existing components
bsekachev Feb 15, 2024
df2ffca
Navigation added
bsekachev Feb 15, 2024
5bdbb8c
Minor refactoring
bsekachev Feb 15, 2024
76fe914
Some code improvements
bsekachev Feb 15, 2024
b7af793
Simplified core api to search empty frames
bsekachev Feb 18, 2024
a05ec82
Many refactoring
bsekachev Feb 18, 2024
fd7b09d
Improvements and bugfixes
bsekachev Feb 19, 2024
253a8f8
Minor fixes
bsekachev Feb 19, 2024
5a9dc61
Updated changelog
bsekachev Feb 19, 2024
9c81aaf
Updated versiob
bsekachev Feb 19, 2024
34938b0
Merge branch 'develop' into bs/single_object_mode
bsekachev Feb 19, 2024
c27baf9
Minor fix
bsekachev Feb 19, 2024
6420185
Fixed test
bsekachev Feb 19, 2024
a82cb62
Additional query parameters
bsekachev Feb 19, 2024
db76f1c
Minor improvements
bsekachev Feb 19, 2024
2546f19
Refactoring
bsekachev Feb 19, 2024
c80bd99
Canvas refactoring
bsekachev Feb 19, 2024
0ee714b
Removed skeletons from mode
bsekachev Feb 19, 2024
f935458
Fixed one more issue from develop
bsekachev Feb 19, 2024
059fec1
Updated license headers
bsekachev Feb 19, 2024
632cda1
Updated cscc
bsekachev Feb 19, 2024
4e5b361
Fixed test
bsekachev Feb 20, 2024
b29f747
Fixed corner case
bsekachev Feb 20, 2024
3c313dd
Fixed merge
bsekachev Feb 20, 2024
0c70b8f
Fixed several issues reported by Maxim
bsekachev Feb 20, 2024
ce8b76e
Fixed conditional rendering
bsekachev Feb 20, 2024
59f475a
Added progress bar
bsekachev Feb 20, 2024
ea68340
Improved progress
bsekachev Feb 20, 2024
1d879c4
Removed extra code
bsekachev Feb 20, 2024
ed74948
Adjusted save button
bsekachev Feb 20, 2024
9cca324
Applied changes from the latest meeting
bsekachev Feb 21, 2024
6ea623c
Applied comments
bsekachev Feb 22, 2024
0e9219c
Updated tests
bsekachev Feb 22, 2024
be5d672
Skip button
bsekachev Feb 22, 2024
c1bc407
Fixed empty labels
bsekachev Feb 22, 2024
2e5f40c
Added return to initial navitation type
bsekachev Feb 22, 2024
aae100f
Correct unmounting
bsekachev Feb 22, 2024
68fe484
Fixed tests
bsekachev Feb 22, 2024
bf1dcdf
Renamed checkbox
bsekachev Feb 23, 2024
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
4 changes: 4 additions & 0 deletions changelog.d/20240219_101835_boris_single_object_mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### Added

- Single shape annotation mode allowing to easily annotate scenarious where a user
only needs to draw one object on one image (<https://github.com/opencv/cvat/pull/7486>)
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.19.1",
"version": "2.20.0",
"type": "module",
"description": "Part of Computer Vision Annotation Tool which presents its canvas library",
"main": "src/canvas.ts",
Expand Down
37 changes: 1 addition & 36 deletions cvat-canvas/src/typescript/canvasController.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (C) 2019-2022 Intel Corporation
// Copyright (C) 2022-2023 CVAT.ai Corporation
// Copyright (C) 2022-2024 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -45,13 +45,6 @@ export interface CanvasController {
zoom(x: number, y: number, direction: number): void;
draw(drawData: DrawData): void;
edit(editData: MasksEditData): void;
interact(interactionData: InteractionData): void;
merge(mergeData: MergeData): void;
split(splitData: SplitData): void;
group(groupData: GroupData): void;
join(joinData: JoinData): void;
slice(sliceData: SliceData): void;
selectRegion(enabled: boolean): void;
enableDrag(x: number, y: number): void;
drag(x: number, y: number): void;
disableDrag(): void;
Expand Down Expand Up @@ -107,34 +100,6 @@ export class CanvasControllerImpl implements CanvasController {
this.model.edit(editData);
}

public interact(interactionData: InteractionData): void {
this.model.interact(interactionData);
}

public merge(mergeData: MergeData): void {
this.model.merge(mergeData);
}

public split(splitData: SplitData): void {
this.model.split(splitData);
}

public group(groupData: GroupData): void {
this.model.group(groupData);
}

public join(joinData: JoinData): void {
this.model.join(joinData);
}

public slice(sliceData: SliceData): void {
this.model.slice(sliceData);
}

public selectRegion(enable: boolean): void {
this.model.selectRegion(enable);
}

public get geometry(): Geometry {
return this.model.geometry;
}
Expand Down
138 changes: 40 additions & 98 deletions cvat-canvas/src/typescript/canvasView.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright (C) 2019-2022 Intel Corporation
// Copyright (C) 2022-2023 CVAT.ai Corporation
// Copyright (C) 2022-2024 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

Expand Down Expand Up @@ -97,7 +97,7 @@
return this.controller.mode;
}

private onMessage = (messages: CanvasHint[] | null, topic: string) => {

Check warning on line 100 in cvat-canvas/src/typescript/canvasView.ts

View workflow job for this annotation

GitHub Actions / Linter

Missing return type on function
this.canvas.dispatchEvent(
new CustomEvent('canvas.message', {
bubbles: false,
Expand Down Expand Up @@ -232,6 +232,16 @@
}
}

private dispatchCanceledEvent(): void {
this.mode = Mode.IDLE;
const event: CustomEvent = new CustomEvent('canvas.canceled', {
bubbles: false,
cancelable: true,
});

this.canvas.dispatchEvent(event);
}

private onInteraction = (
shapes: InteractionResult[] | null,
shapesUpdated = true,
Expand All @@ -256,16 +266,7 @@
}

if (shapes === null || isDone) {
const event: CustomEvent = new CustomEvent('canvas.canceled', {
bubbles: false,
cancelable: true,
});

this.canvas.dispatchEvent(event);
this.mode = Mode.IDLE;
this.controller.interact({
enabled: false,
});
this.dispatchCanceledEvent();
}
};

Expand All @@ -290,13 +291,7 @@
const [state] = this.controller.objects
.filter((_state: any): boolean => _state.clientID === clientID);
this.onEditDone(state, points);

const event: CustomEvent = new CustomEvent('canvas.canceled', {
bubbles: false,
cancelable: true,
});

this.canvas.dispatchEvent(event);
this.dispatchCanceledEvent();
return;
}

Expand All @@ -317,10 +312,7 @@

this.canvas.dispatchEvent(event);
} else if (!continueDraw) {
this.canvas.dispatchEvent(new CustomEvent('canvas.canceled', {
bubbles: false,
cancelable: true,
}));
this.dispatchCanceledEvent();
}

if (continueDraw) {
Expand All @@ -335,7 +327,8 @@
);
} else {
// when draw stops from inside canvas (for example if use predefined number of points)
this.controller.draw({ enabled: false });
this.mode = Mode.IDLE;
this.canvas.style.cursor = '';
}
};

Expand All @@ -361,6 +354,7 @@

private onEditDone = (state: any, points: number[], rotation?: number): void => {
this.canvas.style.cursor = '';
this.mode = Mode.IDLE;
if (state && points) {
const event: CustomEvent = new CustomEvent('canvas.edited', {
bubbles: false,
Expand All @@ -374,18 +368,12 @@

this.canvas.dispatchEvent(event);
} else {
const event: CustomEvent = new CustomEvent('canvas.canceled', {
bubbles: false,
cancelable: true,
});

this.canvas.dispatchEvent(event);
this.dispatchCanceledEvent();
}

for (const clientID of Object.keys(this.innerObjectsFlags.editHidden)) {
this.setupInnerFlags(+clientID, 'editHidden', false);
}
this.mode = Mode.IDLE;
};

private onMergeDone = (objects: any[] | null, duration?: number): void => {
Expand All @@ -399,18 +387,11 @@
},
});

this.mode = Mode.IDLE;
this.canvas.dispatchEvent(event);
} else {
const event: CustomEvent = new CustomEvent('canvas.canceled', {
bubbles: false,
cancelable: true,
});

this.canvas.dispatchEvent(event);
this.dispatchCanceledEvent();
}

this.controller.merge({ enabled: false });
this.mode = Mode.IDLE;
};

private onSplitDone = (object?: any, duration?: number): void => {
Expand All @@ -425,23 +406,23 @@
},
});

this.canvas.style.cursor = '';
this.mode = Mode.IDLE;
this.splitHandler.split({ enabled: false });
this.canvas.dispatchEvent(event);
} else {
const event: CustomEvent = new CustomEvent('canvas.canceled', {
bubbles: false,
cancelable: true,
});

this.canvas.dispatchEvent(event);
this.dispatchCanceledEvent();
}

this.controller.split({ enabled: false });
this.mode = Mode.IDLE;
};

private onSelectDone = (objects?: any[], duration?: number): void => {
if (this.mode === Mode.JOIN) {
this.onMessage(null, 'join');
}

if (objects && typeof duration !== 'undefined') {
if (this.mode === Mode.GROUP && objects.length > 1) {
this.mode = Mode.IDLE;
this.canvas.dispatchEvent(new CustomEvent('canvas.groupped', {
bubbles: false,
cancelable: true,
Expand All @@ -451,6 +432,7 @@
},
}));
} else if (this.mode === Mode.JOIN && objects.length > 1) {
this.mode = Mode.IDLE;
let [left, top, right, bottom] = objects[0].points.slice(-4);
objects.forEach((state) => {
const [curLeft, curTop, curRight, curBottom] = state.points.slice(-4);
Expand Down Expand Up @@ -487,26 +469,14 @@
}).catch(this.onError);
}
} else {
const event: CustomEvent = new CustomEvent('canvas.canceled', {
bubbles: false,
cancelable: true,
});

this.canvas.dispatchEvent(event);
this.dispatchCanceledEvent();
}

if (this.mode === Mode.GROUP) {
this.controller.group({ enabled: false });
} else if (this.mode === Mode.JOIN) {
this.controller.join({ enabled: false });
this.onMessage(null, 'join');
}

this.mode = Mode.IDLE;
};

private onSliceDone = (state?: any, results?: number[][], duration?: number): void => {
if (state && results && typeof duration !== 'undefined') {
this.mode = Mode.IDLE;
this.sliceHandler.slice({ enabled: false });
this.canvas.dispatchEvent(new CustomEvent('canvas.sliced', {
bubbles: false,
cancelable: true,
Expand All @@ -517,40 +487,22 @@
},
}));
} else {
const event: CustomEvent = new CustomEvent('canvas.canceled', {
bubbles: false,
cancelable: true,
});

this.canvas.dispatchEvent(event);
this.dispatchCanceledEvent();
}

this.controller.slice({ enabled: false });
this.mode = Mode.IDLE;
};

private onRegionSelected = (points?: number[]): void => {
if (points) {
const event: CustomEvent = new CustomEvent('canvas.regionselected', {
this.canvas.dispatchEvent(new CustomEvent('canvas.regionselected', {
bubbles: false,
cancelable: true,
detail: {
points,
},
});

this.canvas.dispatchEvent(event);
}));
} else {
const event: CustomEvent = new CustomEvent('canvas.canceled', {
bubbles: false,
cancelable: true,
});

this.canvas.dispatchEvent(event);
this.dispatchCanceledEvent();
}

this.controller.selectRegion(false);
this.mode = Mode.IDLE;
};

private onFindObject = (e: MouseEvent): void => {
Expand Down Expand Up @@ -1711,19 +1663,15 @@
if (data.enabled) {
this.canvas.style.cursor = 'copy';
this.mode = Mode.MERGE;
} else {
this.canvas.style.cursor = '';
}
this.mergeHandler.merge(data);
} else if (reason === UpdateReasons.SPLIT) {
const data: SplitData = this.controller.splitData;
if (data.enabled) {
this.canvas.style.cursor = 'copy';
this.mode = Mode.SPLIT;
} else {
this.canvas.style.cursor = '';
this.splitHandler.split(data);
}
this.splitHandler.split(data);
} else if ([UpdateReasons.JOIN, UpdateReasons.GROUP].includes(reason)) {
let data: GroupData | JoinData = null;
if (reason === UpdateReasons.GROUP) {
Expand All @@ -1745,18 +1693,12 @@
objectType: ['shape'],
});
}

if (data.enabled) {
this.canvas.style.cursor = 'copy';
} else {
this.canvas.style.cursor = '';
}
} else if (reason === UpdateReasons.SLICE) {
const data = this.controller.sliceData;
if (data.enabled && this.mode === Mode.IDLE) {
this.mode = Mode.SLICE;
this.sliceHandler.slice(data);
}
this.sliceHandler.slice(data);
} else if (reason === UpdateReasons.SELECT) {
this.objectSelector.push(this.controller.selected);
if (this.mode === Mode.MERGE) {
Expand Down Expand Up @@ -1805,8 +1747,8 @@
}),
);
}
this.mode = Mode.IDLE;
this.canvas.style.cursor = '';
this.dispatchCanceledEvent();
} else if (reason === UpdateReasons.DATA_FAILED) {
this.onError(model.exception, 'data fetching');
} else if (reason === UpdateReasons.DESTROY) {
Expand Down
Loading
Loading