Skip to content

Commit e5ea213

Browse files
pktiukbsekachev
andauthored
Add support for semi-auto annotations (#6263)
Closes: #6261 <!-- Provide a general summary of your changes in the Title above --> ### Motivation and context <!-- Why is this change required? What problem does it solve? If it fixes an open issue, please link to the issue here. Describe your changes in detail, add screenshots. --> This kind of annotations was already mentioned in docs, but it was not implemented. ### How has this been tested? <!-- Please describe in detail how you tested your changes. Include details of your testing environment, and the tests you ran to see how your change affects other areas of the code, etc. --> I just deployed it with help of the docker-compose.dev.yml you provided. ### Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply. If an item isn't applicable for some reason, then ~~explicitly strikethrough~~ the whole line. If you don't do that, GitHub will show incorrect progress for the pull request. If you're unsure about any of these, don't hesitate to ask. We're here to help! --> - [X] I submit my changes into the `develop` branch - [X] I have added a description of my changes into the [CHANGELOG](https://github.com/opencv/cvat/blob/develop/CHANGELOG.md) file - [ ] I have updated the documentation accordingly. (it was already in docs) - [ ] I have added tests to cover my changes - [X] I have linked related issues (see [GitHub docs]( https://help.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword)) - [x] I have increased versions of npm packages if it is necessary ([cvat-canvas](https://github.com/opencv/cvat/tree/develop/cvat-canvas#versioning), [cvat-core](https://github.com/opencv/cvat/tree/develop/cvat-core#versioning), [cvat-data](https://github.com/opencv/cvat/tree/develop/cvat-data#versioning) and [cvat-ui](https://github.com/opencv/cvat/tree/develop/cvat-ui#versioning)) ### License - [X] I submit _my code changes_ under the same [MIT License]( https://github.com/opencv/cvat/blob/develop/LICENSE) that covers the project. Feel free to contact the maintainers if that's a concern. --------- Co-authored-by: Boris Sekachev <[email protected]> Co-authored-by: Boris Sekachev <[email protected]>
1 parent 9fab8c5 commit e5ea213

File tree

14 files changed

+70
-25
lines changed

14 files changed

+70
-25
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## \[2.5.0] - Unreleased
99
### Added
1010
- \[Server API\] An option to supply custom file ordering for task data uploads (<https://github.com/opencv/cvat/pull/5083>)
11+
- New option ``semi-auto`` is available as annotations source (<https://github.com/opencv/cvat/pull/6263>)
1112

1213
### Changed
1314
- Allowed to use dataset manifest for the `predefined` sorting method for task data (<https://github.com/opencv/cvat/pull/5083>)

cvat-canvas/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cvat-canvas",
3-
"version": "2.16.6",
3+
"version": "2.16.7",
44
"description": "Part of Computer Vision Annotation Tool which presents its canvas library",
55
"main": "src/canvas.ts",
66
"scripts": {

cvat-canvas/src/typescript/shared.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export interface DrawnState {
4141
occluded?: boolean;
4242
hidden?: boolean;
4343
lock: boolean;
44-
source: 'AUTO' | 'MANUAL';
44+
source: 'AUTO' | 'SEMI-AUTO' | 'MANUAL';
4545
shapeType: string;
4646
points?: number[];
4747
rotation: number;

cvat-core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cvat-core",
3-
"version": "9.1.2",
3+
"version": "9.1.3",
44
"description": "Part of Computer Vision Tool which presents an interface for client-side integration",
55
"main": "src/api.ts",
66
"scripts": {

cvat-core/src/annotations-objects.ts

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ function copyShape(state: TrackedShape, data: Partial<TrackedShape> = {}): Track
3131
};
3232
}
3333

34+
function computeNewSource(currentSource: Source): Source {
35+
if ([Source.AUTO, Source.SEMI_AUTO].includes(currentSource)) {
36+
return Source.SEMI_AUTO;
37+
}
38+
39+
return Source.MANUAL;
40+
}
41+
3442
export interface BasicInjection {
3543
labels: Record<number, Label>;
3644
groups: { max: number };
@@ -596,7 +604,7 @@ export class Shape extends Drawn {
596604
const undoRotation = this.rotation;
597605
const redoRotation = rotation;
598606
const undoSource = this.source;
599-
const redoSource = this.readOnlyFields.includes('source') ? this.source : Source.MANUAL;
607+
const redoSource = this.readOnlyFields.includes('source') ? this.source : computeNewSource(this.source);
600608

601609
this.history.do(
602610
HistoryActions.CHANGED_ROTATION,
@@ -622,7 +630,7 @@ export class Shape extends Drawn {
622630
const undoPoints = this.points;
623631
const redoPoints = points;
624632
const undoSource = this.source;
625-
const redoSource = this.readOnlyFields.includes('source') ? this.source : Source.MANUAL;
633+
const redoSource = this.readOnlyFields.includes('source') ? this.source : computeNewSource(this.source);
626634

627635
this.history.do(
628636
HistoryActions.CHANGED_POINTS,
@@ -648,7 +656,7 @@ export class Shape extends Drawn {
648656
const undoOccluded = this.occluded;
649657
const redoOccluded = occluded;
650658
const undoSource = this.source;
651-
const redoSource = this.readOnlyFields.includes('source') ? this.source : Source.MANUAL;
659+
const redoSource = this.readOnlyFields.includes('source') ? this.source : computeNewSource(this.source);
652660

653661
this.history.do(
654662
HistoryActions.CHANGED_OCCLUDED,
@@ -674,7 +682,7 @@ export class Shape extends Drawn {
674682
const undoOutside = this.outside;
675683
const redoOutside = outside;
676684
const undoSource = this.source;
677-
const redoSource = this.readOnlyFields.includes('source') ? this.source : Source.MANUAL;
685+
const redoSource = this.readOnlyFields.includes('source') ? this.source : computeNewSource(this.source);
678686

679687
this.history.do(
680688
HistoryActions.CHANGED_OCCLUDED,
@@ -700,7 +708,7 @@ export class Shape extends Drawn {
700708
const undoZOrder = this.zOrder;
701709
const redoZOrder = zOrder;
702710
const undoSource = this.source;
703-
const redoSource = this.readOnlyFields.includes('source') ? this.source : Source.MANUAL;
711+
const redoSource = this.readOnlyFields.includes('source') ? this.source : computeNewSource(this.source);
704712

705713
this.history.do(
706714
HistoryActions.CHANGED_ZORDER,
@@ -1179,7 +1187,7 @@ export class Track extends Drawn {
11791187
protected saveRotation(rotation: number, frame: number): void {
11801188
const wasKeyframe = frame in this.shapes;
11811189
const undoSource = this.source;
1182-
const redoSource = this.readOnlyFields.includes('source') ? this.source : Source.MANUAL;
1190+
const redoSource = this.readOnlyFields.includes('source') ? this.source : computeNewSource(this.source);
11831191
const undoShape = wasKeyframe ? this.shapes[frame] : undefined;
11841192
const redoShape = wasKeyframe ?
11851193
{ ...this.shapes[frame], rotation } : copyShape(this.get(frame), { rotation });
@@ -1199,7 +1207,7 @@ export class Track extends Drawn {
11991207
protected savePoints(points: number[], frame: number): void {
12001208
const wasKeyframe = frame in this.shapes;
12011209
const undoSource = this.source;
1202-
const redoSource = this.readOnlyFields.includes('source') ? this.source : Source.MANUAL;
1210+
const redoSource = this.readOnlyFields.includes('source') ? this.source : computeNewSource(this.source);
12031211
const undoShape = wasKeyframe ? this.shapes[frame] : undefined;
12041212
const redoShape = wasKeyframe ?
12051213
{ ...this.shapes[frame], points } : copyShape(this.get(frame), { points });
@@ -1219,7 +1227,7 @@ export class Track extends Drawn {
12191227
protected saveOutside(frame: number, outside: boolean): void {
12201228
const wasKeyframe = frame in this.shapes;
12211229
const undoSource = this.source;
1222-
const redoSource = this.readOnlyFields.includes('source') ? this.source : Source.MANUAL;
1230+
const redoSource = this.readOnlyFields.includes('source') ? this.source : computeNewSource(this.source);
12231231
const undoShape = wasKeyframe ? this.shapes[frame] : undefined;
12241232
const redoShape = wasKeyframe ?
12251233
{ ...this.shapes[frame], outside } :
@@ -1240,7 +1248,7 @@ export class Track extends Drawn {
12401248
protected saveOccluded(occluded: boolean, frame: number): void {
12411249
const wasKeyframe = frame in this.shapes;
12421250
const undoSource = this.source;
1243-
const redoSource = this.readOnlyFields.includes('source') ? this.source : Source.MANUAL;
1251+
const redoSource = this.readOnlyFields.includes('source') ? this.source : computeNewSource(this.source);
12441252
const undoShape = wasKeyframe ? this.shapes[frame] : undefined;
12451253
const redoShape = wasKeyframe ?
12461254
{ ...this.shapes[frame], occluded } :
@@ -1261,7 +1269,7 @@ export class Track extends Drawn {
12611269
protected saveZOrder(zOrder: number, frame: number): void {
12621270
const wasKeyframe = frame in this.shapes;
12631271
const undoSource = this.source;
1264-
const redoSource = this.readOnlyFields.includes('source') ? this.source : Source.MANUAL;
1272+
const redoSource = this.readOnlyFields.includes('source') ? this.source : computeNewSource(this.source);
12651273
const undoShape = wasKeyframe ? this.shapes[frame] : undefined;
12661274
const redoShape = wasKeyframe ?
12671275
{ ...this.shapes[frame], zOrder } :
@@ -1287,7 +1295,7 @@ export class Track extends Drawn {
12871295
}
12881296

12891297
const undoSource = this.source;
1290-
const redoSource = this.readOnlyFields.includes('source') ? this.source : Source.MANUAL;
1298+
const redoSource = this.readOnlyFields.includes('source') ? this.source : computeNewSource(this.source);
12911299
const undoShape = wasKeyframe ? this.shapes[frame] : undefined;
12921300
const redoShape = keyframe ? copyShape(this.get(frame)) : undefined;
12931301

@@ -2027,7 +2035,7 @@ export class SkeletonShape extends Shape {
20272035
protected saveRotation(rotation, frame): void {
20282036
const undoSkeletonPoints = this.elements.map((element) => element.points);
20292037
const undoSource = this.source;
2030-
const redoSource = this.readOnlyFields.includes('source') ? this.source : Source.MANUAL;
2038+
const redoSource = this.readOnlyFields.includes('source') ? this.source : computeNewSource(this.source);
20312039

20322040
const bbox = computeWrappingBox(undoSkeletonPoints.flat());
20332041
const [cx, cy] = [bbox.x + bbox.width / 2, bbox.y + bbox.height / 2];
@@ -2075,7 +2083,7 @@ export class SkeletonShape extends Shape {
20752083
const updateElements = (affectedElements, action, property: 'points' | 'occluded' | 'hidden' | 'lock') => {
20762084
const undoSkeletonProperties = this.elements.map((element) => element[property]);
20772085
const undoSource = this.source;
2078-
const redoSource = this.readOnlyFields.includes('source') ? this.source : Source.MANUAL;
2086+
const redoSource = this.readOnlyFields.includes('source') ? this.source : computeNewSource(this.source);
20792087

20802088
try {
20812089
this.history.freeze(true);
@@ -2246,7 +2254,7 @@ export class MaskShape extends Shape {
22462254
const points = mask2Rle(maskPoints);
22472255

22482256
const redoPoints = points;
2249-
const redoSource = Source.MANUAL;
2257+
const redoSource = computeNewSource(this.source);
22502258

22512259
const undo = (): void => {
22522260
this.points = undoPoints;
@@ -2807,7 +2815,7 @@ export class SkeletonTrack extends Track {
28072815
protected saveRotation(rotation: number, frame: number): void {
28082816
const undoSkeletonShapes = this.elements.map((element) => element.shapes[frame]);
28092817
const undoSource = this.source;
2810-
const redoSource = this.readOnlyFields.includes('source') ? this.source : Source.MANUAL;
2818+
const redoSource = this.readOnlyFields.includes('source') ? this.source : computeNewSource(this.source);
28112819

28122820
const elementsData = this.elements.map((element) => element.get(frame));
28132821
const skeletonPoints = elementsData.map((data) => data.points);
@@ -2952,7 +2960,7 @@ export class SkeletonTrack extends Track {
29522960
const undoSkeletonProperties = this.elements.map((element) => element[property] || null);
29532961
const undoSkeletonShapes = this.elements.map((element) => element.shapes[frame]);
29542962
const undoSource = this.source;
2955-
const redoSource = this.readOnlyFields.includes('source') ? this.source : Source.MANUAL;
2963+
const redoSource = this.readOnlyFields.includes('source') ? this.source : computeNewSource(this.source);
29562964

29572965
const errors = [];
29582966
try {

cvat-core/src/enums.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ export enum ShapeType {
7979

8080
export enum Source {
8181
MANUAL = 'manual',
82+
SEMI_AUTO = 'semi-auto',
8283
AUTO = 'auto',
8384
}
8485

cvat-core/src/object-state.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ export default class ObjectState {
454454
}),
455455
);
456456

457-
if ([Source.MANUAL, Source.AUTO].includes(serialized.source)) {
457+
if ([Source.MANUAL, Source.SEMI_AUTO, Source.AUTO].includes(serialized.source)) {
458458
data.source = serialized.source;
459459
}
460460
if (typeof serialized.zOrder === 'number') {

cvat-ui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cvat-ui",
3-
"version": "1.51.1",
3+
"version": "1.51.2",
44
"description": "CVAT single-page application",
55
"main": "src/index.tsx",
66
"scripts": {

cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/opencv-control.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
278278
frame,
279279
objectType: ObjectType.SHAPE,
280280
shapeType: ShapeType.POLYGON,
281+
source: core.enums.Source.SEMI_AUTO,
281282
label: labels.filter((label: any) => label.id === activeLabelID)[0],
282283
points: openCVWrapper.contours
283284
.approxPoly(finalPoints, thresholdFromAccuracy(approxPolyAccuracy))
@@ -321,6 +322,7 @@ class OpenCVControlComponent extends React.PureComponent<Props & DispatchToProps
321322
const state = new core.classes.ObjectState({
322323
shapeType: ShapeType.RECTANGLE,
323324
objectType: ObjectType.TRACK,
325+
source: core.enums.Source.SEMI_AUTO,
324326
zOrder: curZOrder,
325327
label,
326328
points,

cvat-ui/src/components/annotation-page/standard-workspace/controls-side-bar/tools-control.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ export class ToolsControlComponent extends React.PureComponent<Props, State> {
493493
const state = new core.classes.ObjectState({
494494
shapeType: ShapeType.RECTANGLE,
495495
objectType: ObjectType.TRACK,
496+
source: core.enums.Source.SEMI_AUTO,
496497
zOrder: curZOrder,
497498
label,
498499
points,
@@ -849,6 +850,7 @@ export class ToolsControlComponent extends React.PureComponent<Props, State> {
849850
const object = new core.classes.ObjectState({
850851
frame,
851852
objectType: ObjectType.SHAPE,
853+
source: core.enums.Source.SEMI_AUTO,
852854
label: labels.length ? labels.filter((label: any) => label.id === activeLabelID)[0] : null,
853855
shapeType: ShapeType.POLYGON,
854856
points: points.flat(),
@@ -870,6 +872,7 @@ export class ToolsControlComponent extends React.PureComponent<Props, State> {
870872
const object = new core.classes.ObjectState({
871873
frame,
872874
objectType: ObjectType.SHAPE,
875+
source: core.enums.Source.SEMI_AUTO,
873876
label: labels.length ? labels.filter((label: any) => label.id === activeLabelID)[0] : null,
874877
shapeType: ShapeType.MASK,
875878
points: maskPoints,
@@ -1230,7 +1233,7 @@ export class ToolsControlComponent extends React.PureComponent<Props, State> {
12301233
objectType: ObjectType.SHAPE,
12311234
frame,
12321235
occluded: false,
1233-
source: 'auto',
1236+
source: core.enums.Source.AUTO,
12341237
attributes: (data.attributes as { name: string, value: string }[])
12351238
.reduce((acc, attr) => {
12361239
const [modelAttr] = Object.entries(body.mapping[modelLabel].attributes)

cvat/apps/dataset_manager/annotation.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,7 @@ def to_shapes(self, end_frame, end_skeleton_frame=None):
421421
):
422422
shape["label_id"] = track["label_id"]
423423
shape["group"] = track["group"]
424+
shape["source"] = track["source"]
424425
shape["track_id"] = idx
425426
shape["attributes"] += track["attributes"]
426427
shape["elements"] = []

cvat/apps/dataset_manager/bindings.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1755,7 +1755,7 @@ def reduce_fn(acc, v):
17551755

17561756
track_id = ann.attributes.pop('track_id', None)
17571757
source = ann.attributes.pop('source').lower() \
1758-
if ann.attributes.get('source', '').lower() in {'auto', 'manual'} else 'manual'
1758+
if ann.attributes.get('source', '').lower() in {'auto', 'semi-auto', 'manual'} else 'manual'
17591759

17601760
shape_type = shapes[ann.type]
17611761
if track_id is None or 'keyframe' not in ann.attributes or dm_dataset.format not in ['cvat', 'datumaro', 'sly_pointcloud']:
@@ -1769,7 +1769,7 @@ def reduce_fn(acc, v):
17691769
element_occluded = element.visibility[0] == dm.Points.Visibility.hidden
17701770
element_outside = element.visibility[0] == dm.Points.Visibility.absent
17711771
element_source = element.attributes.pop('source').lower() \
1772-
if element.attributes.get('source', '').lower() in {'auto', 'manual'} else 'manual'
1772+
if element.attributes.get('source', '').lower() in {'auto', 'semi-auto', 'manual'} else 'manual'
17731773
elements.append(instance_data.LabeledShape(
17741774
type=shapes[element.type],
17751775
frame=frame_number,
@@ -1843,7 +1843,7 @@ def reduce_fn(acc, v):
18431843
for n, v in element.attributes.items()
18441844
]
18451845
element_source = element.attributes.pop('source').lower() \
1846-
if element.attributes.get('source', '').lower() in {'auto', 'manual'} else 'manual'
1846+
if element.attributes.get('source', '').lower() in {'auto', 'semi-auto', 'manual'} else 'manual'
18471847
tracks[track_id]['elements'][element.label].shapes.append(instance_data.TrackedShape(
18481848
type=shapes[element.type],
18491849
frame=frame_number,
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Generated by Django 3.2.18 on 2023-06-08 19:15
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('engine', '0068_auto_20230418_0901'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='labeledimage',
15+
name='source',
16+
field=models.CharField(choices=[('auto', 'AUTO'), ('semi-auto', 'SEMI_AUTO'), ('manual', 'MANUAL')], default='manual', max_length=16, null=True),
17+
),
18+
migrations.AlterField(
19+
model_name='labeledshape',
20+
name='source',
21+
field=models.CharField(choices=[('auto', 'AUTO'), ('semi-auto', 'SEMI_AUTO'), ('manual', 'MANUAL')], default='manual', max_length=16, null=True),
22+
),
23+
migrations.AlterField(
24+
model_name='labeledtrack',
25+
name='source',
26+
field=models.CharField(choices=[('auto', 'AUTO'), ('semi-auto', 'SEMI_AUTO'), ('manual', 'MANUAL')], default='manual', max_length=16, null=True),
27+
),
28+
]

cvat/apps/engine/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,7 @@ def __str__(self):
656656

657657
class SourceType(str, Enum):
658658
AUTO = 'auto'
659+
SEMI_AUTO = 'semi-auto'
659660
MANUAL = 'manual'
660661

661662
@classmethod

0 commit comments

Comments
 (0)