Skip to content

Commit 26ac753

Browse files
authored
ITEP-68021 - background annotations interface (#341)
1 parent 2126cdd commit 26ac753

File tree

14 files changed

+239
-37
lines changed

14 files changed

+239
-37
lines changed

web_ui/packages/core/src/feature-flags/services/feature-flag-service.interface.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const DEV_FEATURE_FLAGS = {
3636
FEATURE_FLAG_REQ_ACCESS: false,
3737
FEATURE_FLAG_NEW_CONFIGURABLE_PARAMETERS: false,
3838
FEATURE_FLAG_TELEMETRY_STACK: true,
39-
39+
FEATURE_FLAG_ANNOTATION_HOLE: false,
4040
// Only used for unit testing
4141
DEBUG: false,
4242
};

web_ui/src/core/labels/dtos/label.interface.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ interface LabelCommon {
77
group: string;
88
hotkey?: string;
99
is_empty: boolean;
10+
is_background: boolean;
1011
parent_id: string | null;
1112
}
1213

13-
export type LabelCreation = Omit<LabelCommon, 'is_empty'>;
14+
export type LabelCreation = Omit<LabelCommon, 'is_empty' | 'is_background'>;
1415

1516
export interface LabelDTO extends LabelCommon {
1617
id: string;

web_ui/src/core/labels/label.interface.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ export enum LABEL_BEHAVIOUR {
5656
// Used to guarantee that when an anomalous annotation is added, its roi is
5757
// also given a global anomalous label
5858
ANOMALOUS = 1 << 4,
59+
60+
BACKGROUND = 1 << 5,
5961
}
6062

6163
export enum LabelsRelationType {

web_ui/src/core/labels/utils.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ describe('Label behaviour based on task type', () => {
8181
hotkey: 'ctrl+1',
8282
parent_id: null,
8383
is_empty: false,
84+
is_background: false,
8485
is_anomalous: false,
8586
};
8687

web_ui/src/core/labels/utils.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (C) 2022-2025 Intel Corporation
22
// LIMITED EDGE SOFTWARE DISTRIBUTION LICENSE
33

4-
import { negate } from 'lodash-es';
4+
import { negate, overSome } from 'lodash-es';
55

66
import { idMatchingFormat } from '../../test-utils/id-utils';
77
import { AnnotationLabel } from '../annotations/annotation.interface';
@@ -61,9 +61,30 @@ export const isAnomalous = <T extends { behaviour: LABEL_BEHAVIOUR }>(label: T):
6161
return Boolean(label.behaviour & LABEL_BEHAVIOUR.ANOMALOUS);
6262
};
6363

64-
export const isEmptyLabel = <T extends { isEmpty: boolean }>(label: T): boolean => label.isEmpty;
64+
export const isBackgroundBehavior = <T extends { behaviour: LABEL_BEHAVIOUR }>(label: T): boolean => {
65+
return Boolean(label.behaviour & LABEL_BEHAVIOUR.BACKGROUND);
66+
};
67+
68+
interface EmptyOrBackground {
69+
isEmpty: boolean;
70+
behaviour: LABEL_BEHAVIOUR;
71+
}
72+
73+
export const isEmptyLabel = <T extends EmptyOrBackground>(label: T): boolean => label.isEmpty;
74+
export const isNonEmptyLabel = negate(isEmptyLabel);
75+
76+
export const isBackgroundLabel = <T extends EmptyOrBackground>(label: T): boolean => {
77+
return isBackgroundBehavior(label);
78+
};
6579

66-
export const filterOutEmptyLabel = (labels: readonly Label[]): readonly Label[] => labels.filter(negate(isEmptyLabel));
80+
export const isNonBackgroundLabel = negate(isBackgroundLabel);
81+
82+
export const isEmptyOrBackgroundLabel = overSome([isEmptyLabel, isBackgroundLabel]);
83+
84+
export const filterOutEmptyLabel = (labels: readonly Label[]): readonly Label[] => labels.filter(isNonEmptyLabel);
85+
86+
export const filterOutEmptyAndBackgroundLabel = (labels: readonly Label[]): readonly Label[] =>
87+
labels.filter((label) => isNonEmptyLabel(label) && isNonBackgroundLabel(label));
6788

6889
// Predictions come from a model but are not yet accepted (userId).
6990
export const isPrediction = (label?: AnnotationLabel) =>
@@ -100,6 +121,10 @@ export const getBehaviourFromDTO = (
100121
return LABEL_BEHAVIOUR.GLOBAL;
101122
}
102123

124+
if (label.is_background) {
125+
return LABEL_BEHAVIOUR.BACKGROUND;
126+
}
127+
103128
return LABEL_BEHAVIOUR.LOCAL;
104129
};
105130

web_ui/src/core/projects/services/test-utils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ const FLAT_LABELS_BODY = [
114114
hotkey: 'ctrl+1',
115115
is_empty: false,
116116
is_anomalous: false,
117+
is_background: false,
117118
},
118119
{
119120
id: '2',
@@ -124,6 +125,7 @@ const FLAT_LABELS_BODY = [
124125
hotkey: 'ctrl+2',
125126
is_empty: false,
126127
is_anomalous: false,
128+
is_background: false,
127129
},
128130
];
129131

@@ -291,6 +293,7 @@ const HIERARCHY_LABELS_BODY = [
291293
hotkey: 'ctrl+1',
292294
is_empty: false,
293295
is_anomalous: false,
296+
is_background: false,
294297
},
295298
{
296299
id: 'label2',
@@ -301,6 +304,7 @@ const HIERARCHY_LABELS_BODY = [
301304
hotkey: 'ctrl+2',
302305
is_empty: false,
303306
is_anomalous: false,
307+
is_background: false,
304308
},
305309
{
306310
id: 'label3',
@@ -311,6 +315,7 @@ const HIERARCHY_LABELS_BODY = [
311315
hotkey: 'ctrl+3',
312316
is_empty: false,
313317
is_anomalous: false,
318+
is_background: false,
314319
},
315320
{
316321
id: 'label4',
@@ -321,6 +326,7 @@ const HIERARCHY_LABELS_BODY = [
321326
hotkey: 'ctrl+4',
322327
is_empty: false,
323328
is_anomalous: false,
329+
is_background: false,
324330
},
325331
];
326332

@@ -614,6 +620,7 @@ export const PROJECT_RESPONSE = (): ProjectDTO => {
614620
hotkey: 'ctrl+1',
615621
is_empty: false,
616622
is_anomalous: false,
623+
is_background: false,
617624
},
618625
],
619626
title: 'Detection',

web_ui/src/core/projects/services/utils.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ describe('Project service utils', () => {
2121
parent_id: 'parentId-test',
2222
is_empty: false,
2323
is_anomalous: false,
24+
is_background: false,
2425
});
2526
expect(newLabel.behaviour).toBe(LABEL_BEHAVIOUR.LOCAL);
2627
});
@@ -36,6 +37,7 @@ describe('Project service utils', () => {
3637
group: 'group 1',
3738
is_anomalous: false,
3839
is_empty: false,
40+
is_background: false,
3941
parent_id: 'label-1',
4042
},
4143
{
@@ -45,6 +47,7 @@ describe('Project service utils', () => {
4547
group: 'group 1',
4648
is_anomalous: false,
4749
is_empty: false,
50+
is_background: false,
4851
parent_id: null,
4952
},
5053
{
@@ -54,6 +57,7 @@ describe('Project service utils', () => {
5457
group: 'group 1',
5558
is_anomalous: false,
5659
is_empty: false,
60+
is_background: false,
5761
parent_id: 'label-3',
5862
},
5963
{
@@ -63,6 +67,7 @@ describe('Project service utils', () => {
6367
group: 'group 1',
6468
is_anomalous: false,
6569
is_empty: false,
70+
is_background: false,
6671
parent_id: 'label-1',
6772
},
6873
]);
@@ -116,6 +121,7 @@ describe('Project service utils', () => {
116121
group: 'group 1',
117122
is_anomalous: false,
118123
is_empty: false,
124+
is_background: false,
119125
parent_id: 'label-1',
120126
};
121127

web_ui/src/core/projects/services/utils.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ import {
2424
RevisitLabel,
2525
} from '../../labels/label.interface';
2626
import {
27-
filterOutEmptyLabel,
27+
filterOutEmptyAndBackgroundLabel,
2828
getBehaviourFromDTO,
2929
getFlattenedItems,
3030
getFlattenedLabels,
3131
GROUP_SEPARATOR,
3232
isAnomalous,
33+
isBackgroundLabel,
3334
isEmptyLabel,
3435
} from '../../labels/utils';
3536
import { DOMAIN } from '../core.interface';
@@ -235,23 +236,26 @@ const getCommonTaskStructure = (task: TaskDTO, domain: DOMAIN) => {
235236

236237
return getLabel(label, domain);
237238
});
238-
const emptyLabel = labels.find(isEmptyLabel);
239239

240240
// NOTE: creation of tree transform Label to LabelTreeViewLabel, we have to translate to Label again
241241
const labelsSortedByStructure: LabelTreeLabelProps[] = getFlattenedLabels(
242-
fetchLabelsTree(filterOutEmptyLabel(labels))
242+
fetchLabelsTree(filterOutEmptyAndBackgroundLabel(labels))
243243
);
244244

245245
const commonStructure = {
246246
id: task.id,
247247
title: task.title,
248248
domain,
249-
labels: emptyLabel === undefined ? labelsSortedByStructure : [...labelsSortedByStructure, emptyLabel],
249+
labels: [...labelsSortedByStructure, ...getEmptyAndBackgroundLabels(labels)],
250250
};
251251

252252
return commonStructure;
253253
};
254254

255+
const getEmptyAndBackgroundLabels = (labels: Label[]) => {
256+
return labels.filter((label) => isBackgroundLabel(label) || isEmptyLabel(label));
257+
};
258+
255259
const isKeypointType = (otherTask: TaskDTO | KeypointTaskDTO): otherTask is KeypointTaskDTO => {
256260
const domain = getDomain(otherTask.task_type);
257261
return domain !== undefined && isKeypointDetection(domain);
@@ -323,7 +327,7 @@ export const getProjectEntity = (serverProject: ProjectDTO, router = API_URLS):
323327
};
324328

325329
const getLabelDTO = (label: EditedLabel): EditedLabelDTO => {
326-
const { name, color, group, parentLabelId, hotkey, isEmpty } = label;
330+
const { name, color, group, parentLabelId, hotkey, isEmpty, behaviour } = label;
327331

328332
const common = {
329333
name,
@@ -332,6 +336,7 @@ const getLabelDTO = (label: EditedLabel): EditedLabelDTO => {
332336
hotkey,
333337
parent_id: parentLabelId,
334338
is_empty: isEmpty,
339+
is_background: behaviour === LABEL_BEHAVIOUR.BACKGROUND,
335340
is_anomalous: isAnomalous(label),
336341
};
337342

web_ui/src/pages/annotator/annotator-layout.component.tsx

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { FocusScope } from 'react-aria';
99

1010
import { hasMaxAllowedAnnotations } from '../../core/annotations/utils';
1111
import { Label } from '../../core/labels/label.interface';
12-
import { isExclusive } from '../../core/labels/utils';
1312
import { isVideo, isVideoFrame } from '../../core/media/video.interface';
1413
import { isKeypointTask } from '../../core/projects/utils';
1514
import {
@@ -22,7 +21,6 @@ import { SuccessfullyAutotrainedNotification } from '../../shared/components/coa
2221
import { TutorialCardBuilder } from '../../shared/components/tutorial-card/tutorial-card-builder.component';
2322
import { getFuxSetting } from '../../shared/components/tutorials/utils';
2423
import { useTutorialEnablement } from '../../shared/hooks/use-tutorial-enablement.hook';
25-
import { hasEqualId } from '../../shared/utils';
2624
import { ErrorBoundary } from '../errors/error-boundary.component';
2725
import { useProject } from '../project-details/providers/project-provider/project-provider.component';
2826
import { EmptyAnnotationsNotification } from './annotation/annotation-list/annotation-list-thumbnail-grid/empty-annotations-notification.component';
@@ -36,6 +34,7 @@ import { Sidebar } from './components/sidebar/sidebar.component';
3634
import { VideoPlayer } from './components/video-player/video-player.component';
3735
import { AnnotationScene } from './core/annotation-scene.interface';
3836
import { useCopyPasteAnnotation } from './hooks/use-copy-paste-annotation/use-copy-paste-annotation.hook';
37+
import { useLabelShortcuts } from './hooks/use-label-shortcuts.hook';
3938
import { useSelectedAnnotations } from './hooks/use-selected-annotations.hook';
4039
import { useVisibleAnnotations } from './hooks/use-visible-annotations.hook';
4140
import { AutoTrainingCreditsModalFactory } from './notification/auto-training-credits-modal/auto-training-credits-modal.component';
@@ -48,7 +47,6 @@ import { useAnnotator } from './providers/annotator-provider/annotator-provider.
4847
import { useROI } from './providers/region-of-interest-provider/region-of-interest-provider.component';
4948
import { useSelectedMediaItem } from './providers/selected-media-item-provider/selected-media-item-provider.component';
5049
import { SelectedMediaItem } from './providers/selected-media-item-provider/selected-media-item.interface';
51-
import { useTask } from './providers/task-provider/task-provider.component';
5250

5351
const GRID_AREAS = [
5452
'backHome navigationToolbar navigationToolbar',
@@ -73,28 +71,6 @@ const ErrorFallback = ({ error }: { error: { message: string } }) => {
7371
);
7472
};
7573

76-
// For now we'll remove any empty labels from a sub task if we're in the "All tasks" view
77-
const useLabelShortcuts = (): Label[] => {
78-
const { labels: taskLabels, tasks, selectedTask } = useTask();
79-
80-
if (tasks.length < 2 || selectedTask !== null) {
81-
return taskLabels.filter((label) => tasks.some((task) => task.labels.some(hasEqualId(label.id))));
82-
}
83-
84-
const secondTask = tasks[1];
85-
86-
return taskLabels.filter((label) => {
87-
if (!isExclusive(label)) {
88-
return true;
89-
}
90-
91-
return (
92-
!secondTask.labels.some(hasEqualId(label.id)) &&
93-
tasks.some((task) => task.labels.some(hasEqualId(label.id)))
94-
);
95-
});
96-
};
97-
9874
interface CopyPasteProps {
9975
labels: Label[];
10076
scene: AnnotationScene;

0 commit comments

Comments
 (0)