Skip to content

Commit

Permalink
Added honeypots support on UI in review mode
Browse files Browse the repository at this point in the history
  • Loading branch information
bsekachev committed Oct 7, 2024
1 parent df6d3b9 commit f6fa826
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 17 deletions.
2 changes: 2 additions & 0 deletions cvat-core/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import QualityReport from './quality-report';
import QualityConflict from './quality-conflict';
import QualitySettings from './quality-settings';
import AnalyticsReport from './analytics-report';
import ValidationLayout from './validation-layout';
import { Request } from './request';

import * as enums from './enums';
Expand Down Expand Up @@ -426,6 +427,7 @@ function build(): CVATCore {
QualityReport,
Request,
FramesMetaData,
ValidationLayout,
},
utils: {
mask2Rle,
Expand Down
2 changes: 2 additions & 0 deletions cvat-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import QualityConflict from './quality-conflict';
import QualitySettings from './quality-settings';
import AnalyticsReport from './analytics-report';
import AnnotationGuide from './guide';
import ValidationLayout from './validation-layout';
import { Request } from './request';
import BaseSingleFrameAction, { listActions, registerAction, runActions } from './annotations-actions';
import {
Expand Down Expand Up @@ -215,6 +216,7 @@ export default interface CVATCore {
AnalyticsReport: typeof AnalyticsReport;
Request: typeof Request;
FramesMetaData: typeof FramesMetaData;
ValidationLayout: typeof ValidationLayout;
};
utils: {
mask2Rle: typeof mask2Rle;
Expand Down
22 changes: 21 additions & 1 deletion cvat-core/src/server-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
SerializedInvitationData, SerializedCloudStorage, SerializedFramesMetaData, SerializedCollection,
SerializedQualitySettingsData, APIQualitySettingsFilter, SerializedQualityConflictData, APIQualityConflictsFilter,
SerializedQualityReportData, APIQualityReportsFilter, SerializedAnalyticsReport, APIAnalyticsReportFilter,
SerializedRequest,
SerializedRequest, SerializedValidationLayout,
} from './server-response-types';
import { PaginatedResource } from './core-types';
import { Request } from './request';
Expand Down Expand Up @@ -1382,6 +1382,24 @@ async function deleteJob(jobID: number): Promise<void> {
}
}

const validationLayout = (instance: 'tasks' | 'jobs') => async (
id: number,
): Promise<SerializedValidationLayout | null> => {
const { backendAPI } = config;

try {
const response = await Axios.get(`${backendAPI}/${instance}/${id}/validation_layout`, {
params: {
...enableOrganization(),
},
});

return response.data;
} catch (errorData) {
throw generateError(errorData);
}
};

async function getUsers(filter = { page_size: 'all' }): Promise<SerializedUser[]> {
const { backendAPI } = config;

Expand Down Expand Up @@ -2376,6 +2394,7 @@ export default Object.freeze({
getPreview: getPreview('tasks'),
backup: backupTask,
restore: restoreTask,
validationLayout: validationLayout('tasks'),
}),

labels: Object.freeze({
Expand All @@ -2391,6 +2410,7 @@ export default Object.freeze({
create: createJob,
delete: deleteJob,
exportDataset: exportDataset('jobs'),
validationLayout: validationLayout('jobs'),
}),

users: Object.freeze({
Expand Down
6 changes: 6 additions & 0 deletions cvat-core/src/server-response-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,3 +523,9 @@ export interface SerializedRequest {
expiry_date?: string;
owner?: any;
}

export interface SerializedValidationLayout {
honeypot_count?: number;
honeypot_frames?: number[];
honeypot_real_frames?: number[];
}
29 changes: 28 additions & 1 deletion cvat-core/src/session-implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
decodePreview,
} from './frames';
import Issue from './issue';
import { SerializedLabel, SerializedTask } from './server-response-types';
import { SerializedLabel, SerializedTask, SerializedValidationLayout } from './server-response-types';
import { checkInEnum, checkObjectType } from './common';
import {
getCollection, getSaver, clearAnnotations, getAnnotations,
Expand All @@ -37,6 +37,7 @@ import AnnotationGuide from './guide';
import requestsManager from './requests-manager';
import { Request } from './request';
import User from './user';
import ValidationLayout from './validation-layout';

// must be called with task/job context
async function deleteFrameWrapper(jobID, frame): Promise<void> {
Expand Down Expand Up @@ -164,6 +165,19 @@ export function implementJob(Job: typeof JobClass): typeof JobClass {
},
});

Object.defineProperty(Job.prototype.validationLayout, 'implementation', {
value: async function validationLayoutImplementation(
this: JobClass,
): ReturnType<typeof JobClass.prototype.validationLayout> {
const result = await serverProxy.jobs.validationLayout(this.id);
if (Object.keys(result).length) {
return new ValidationLayout(result as Required<SerializedValidationLayout>);
}

return null;
},
});

Object.defineProperty(Job.prototype.frames.get, 'implementation', {
value: function getFrameImplementation(
this: JobClass,
Expand Down Expand Up @@ -624,6 +638,19 @@ export function implementTask(Task: typeof TaskClass): typeof TaskClass {
},
});

Object.defineProperty(Task.prototype.validationLayout, 'implementation', {
value: async function validationLayoutImplementation(
this: TaskClass,
): ReturnType<typeof TaskClass.prototype.validationLayout> {
const result = await serverProxy.tasks.validationLayout(this.id);
if (Object.keys(result).length) {
return new ValidationLayout(result as Required<SerializedValidationLayout>);
}

return null;
},
});

Object.defineProperty(Task.prototype.save, 'implementation', {
value: async function saveImplementation(
this: TaskClass,
Expand Down
11 changes: 11 additions & 0 deletions cvat-core/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { Request } from './request';
import logger from './logger';
import Issue from './issue';
import ObjectState from './object-state';
import ValidationLayout from './validation-layout';

function buildDuplicatedAPI(prototype) {
Object.defineProperties(prototype, {
Expand Down Expand Up @@ -685,6 +686,11 @@ export class Job extends Session {
return result;
}

async validationLayout(): Promise<ValidationLayout | null> {
const result = await PluginRegistry.apiWrapper.call(this, Job.prototype.validationLayout);
return result;
}

async openIssue(issue: Issue, message: string): Promise<Issue> {
const result = await PluginRegistry.apiWrapper.call(this, Job.prototype.openIssue, issue, message);
return result;
Expand Down Expand Up @@ -1179,6 +1185,11 @@ export class Task extends Session {
const result = await PluginRegistry.apiWrapper.call(this, Task.prototype.guide);
return result;
}

async validationLayout(): Promise<ValidationLayout | null> {
const result = await PluginRegistry.apiWrapper.call(this, Task.prototype.validationLayout);
return result;
}
}

buildDuplicatedAPI(Job.prototype);
Expand Down
44 changes: 44 additions & 0 deletions cvat-core/src/validation-layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (C) 2024 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT

import { SerializedValidationLayout } from 'server-response-types';
import PluginRegistry from './plugins';

export default class ValidationLayout {
#honeypotFrames: number[];
#honeypotRealFrames: number[];

public constructor(data: Required<SerializedValidationLayout>) {
this.#honeypotFrames = [...data.honeypot_frames];
this.#honeypotRealFrames = [...data.honeypot_real_frames];
}

public get honeypotFrames() {
return [...this.#honeypotFrames];
}

public get honeypotRealFrames() {
return [...this.#honeypotRealFrames];
}

async getRealFrame(frame: number): Promise<number | null> {
const result = await PluginRegistry.apiWrapper.call(this, ValidationLayout.prototype.getRealFrame, frame);
return result;
}
}

Object.defineProperties(ValidationLayout.prototype.getRealFrame, {
implementation: {
writable: false,
enumerable: false,
value: function implementation(this: ValidationLayout, frame: number): number | null {
const index = this.honeypotFrames.indexOf(frame);
if (index !== -1) {
return this.honeypotRealFrames[index];
}

return null;
},
},
});
29 changes: 21 additions & 8 deletions cvat-ui/src/actions/annotation-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import {
RectDrawingMethod, CuboidDrawingMethod, Canvas, CanvasMode as Canvas2DMode,
} from 'cvat-canvas-wrapper';
import {
getCore, MLModel, JobType, Job,
QualityConflict, ObjectState, JobState,
getCore, MLModel, JobType, Job, QualityConflict,
ObjectState, JobState, ValidationLayout,
} from 'cvat-core-wrapper';
import logger, { EventScope } from 'cvat-logger';
import { getCVATStore } from 'cvat-store';
Expand All @@ -38,6 +38,7 @@ interface AnnotationsParameters {
showGroundTruth: boolean;
jobInstance: Job;
groundTruthInstance: Job | null;
validationLayout: ValidationLayout | null;
}

const cvat = getCore();
Expand All @@ -58,7 +59,7 @@ export function receiveAnnotationsParameters(): AnnotationsParameters {
player: {
frame: { number: frame },
},
job: { instance: jobInstance, groundTruthInstance },
job: { instance: jobInstance, groundTruthInfo: { groundTruthInstance, validationLayout } },
},
settings: {
workspace: { showAllInterpolationTracks },
Expand All @@ -71,6 +72,7 @@ export function receiveAnnotationsParameters(): AnnotationsParameters {
frame,
jobInstance: jobInstance as Job,
groundTruthInstance,
validationLayout,
showAllInterpolationTracks,
showGroundTruth,
};
Expand Down Expand Up @@ -261,8 +263,8 @@ async function fetchAnnotations(predefinedFrame?: number): Promise<{
maxZ: number;
}> {
const {
filters, frame, showAllInterpolationTracks,
jobInstance, showGroundTruth, groundTruthInstance,
filters, frame, showAllInterpolationTracks, jobInstance,
showGroundTruth, groundTruthInstance, validationLayout,
} = receiveAnnotationsParameters();

const fetchFrame = typeof predefinedFrame === 'undefined' ? frame : predefinedFrame;
Expand All @@ -272,8 +274,16 @@ async function fetchAnnotations(predefinedFrame?: number): Promise<{
if (jobInstance.type === JobType.GROUND_TRUTH) {
states = wrapAnnotationsInGTJob(states);
} else if (showGroundTruth && groundTruthInstance) {
const gtStates = await groundTruthInstance.annotations.get(fetchFrame, showAllInterpolationTracks, filters);
states.push(...gtStates);
let gtFrame: number | null = fetchFrame;

if (validationLayout) {
gtFrame = await validationLayout.getRealFrame(gtFrame);
}

if (gtFrame) {
const gtStates = await groundTruthInstance.annotations.get(gtFrame, showAllInterpolationTracks, filters);
states.push(...gtStates);
}
}

const history = await jobInstance.actions.get();
Expand Down Expand Up @@ -364,7 +374,7 @@ export function removeAnnotationsAsync(
dispatch(fetchAnnotationsAsync());

const state = getState();
if (!state.annotation.job.groundTruthInstance) {
if (!state.annotation.job.groundTruthInfo.groundTruthInstance) {
getCore().config.globalObjectsCounter = 0;
}

Expand Down Expand Up @@ -926,9 +936,11 @@ export function getJobAsync({
const colors = [...cvat.enums.colors];

let groundTruthJobFramesMeta = null;
let validationLayout = null;
if (gtJob) {
await gtJob.annotations.clear({ reload: true }); // fetch gt annotations from the server
groundTruthJobFramesMeta = await cvat.frames.getMeta('job', gtJob.id);
validationLayout = await job.validationLayout();
}

let conflicts: QualityConflict[] = [];
Expand All @@ -950,6 +962,7 @@ export function getJobAsync({
queryParameters,
groundTruthInstance: gtJob || null,
groundTruthJobFramesMeta,
validationLayout,
issues,
conflicts,
frameNumber,
Expand Down
2 changes: 2 additions & 0 deletions cvat-ui/src/cvat-core-wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import Comment from 'cvat-core/src/comment';
import User from 'cvat-core/src/user';
import Organization, { Membership, Invitation } from 'cvat-core/src/organization';
import AnnotationGuide from 'cvat-core/src/guide';
import ValidationLayout from 'cvat-core/src/validation-layout';
import AnalyticsReport, { AnalyticsEntryViewType, AnalyticsEntry } from 'cvat-core/src/analytics-report';
import { Dumper } from 'cvat-core/src/annotation-formats';
import { Event } from 'cvat-core/src/event';
Expand Down Expand Up @@ -105,6 +106,7 @@ export {
ActionParameterType,
FrameSelectionType,
Request,
ValidationLayout,
};

export type {
Expand Down
15 changes: 11 additions & 4 deletions cvat-ui/src/reducers/annotation-reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,16 @@ const defaultState: AnnotationState = {
openTime: null,
labels: [],
requestedId: null,
groundTruthJobFramesMeta: null,
groundTruthInstance: null,
queryParameters: {
initialOpenGuide: false,
defaultLabel: null,
defaultPointsCount: null,
},
groundTruthInfo: {
validationLayout: null,
groundTruthJobFramesMeta: null,
groundTruthInstance: null,
},
instance: null,
attributes: {},
fetching: false,
Expand Down Expand Up @@ -165,6 +168,7 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
queryParameters,
groundTruthInstance,
groundTruthJobFramesMeta,
validationLayout,
} = action.payload;

const defaultLabel = job.labels.length ? job.labels[0] : null;
Expand Down Expand Up @@ -207,8 +211,11 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
acc[label.id] = label.attributes;
return acc;
}, {}),
groundTruthInstance,
groundTruthJobFramesMeta,
groundTruthInfo: {
validationLayout,
groundTruthInstance,
groundTruthJobFramesMeta,
},
queryParameters: {
initialOpenGuide: queryParameters.initialOpenGuide,
defaultLabel: queryParameters.defaultLabel,
Expand Down
Loading

0 comments on commit f6fa826

Please sign in to comment.