Skip to content

Commit

Permalink
Validation API core (#8348)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhiltsov-max authored Oct 3, 2024
1 parent ff1849f commit 1285858
Show file tree
Hide file tree
Showing 53 changed files with 8,773 additions and 2,777 deletions.
24 changes: 24 additions & 0 deletions changelog.d/20240819_210200_mzhiltso_validation_api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
### Added

- New task mode: Honeypots (GT pool)
(<https://github.com/cvat-ai/cvat/pull/8348>)
- New task creation options for quality control: Honeypots (GT pool), GT job
(<https://github.com/cvat-ai/cvat/pull/8348>)
- New GT job frame selection method: `random_per_job`,
which guarantees each job will have GT overlap
(<https://github.com/cvat-ai/cvat/pull/8348>)
- \[Server API\] POST `/jobs/`: new frame selection parameters,
which accept percentages, instead of absolute values
(<https://github.com/cvat-ai/cvat/pull/8348>)
- \[Server API\] GET `/api/tasks/{id}/` got a new `validation_mode` field,
reflecting the current validation configuration (immutable)
(<https://github.com/cvat-ai/cvat/pull/8348>)
- \[Server API\] POST `/api/tasks/{id}/data` got a new `validation_params` field,
which allows to enable `GT` and `GT_POOL` validation for a task on its creation
(<https://github.com/cvat-ai/cvat/pull/8348>)

### Changed

- \[Server API\] POST `/jobs/` `.frames` field now expects relative frame numbers
instead of absolute (source data) ones
(<https://github.com/cvat-ai/cvat/pull/8348>)
48 changes: 31 additions & 17 deletions cvat-core/src/server-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ async function healthCheck(
} catch (error) {
lastError = error;
if (attempt < adjustedMaxRetries) {
await new Promise((resolve) => setTimeout(resolve, adjustedCheckPeriod));
await new Promise((resolve) => { setTimeout(resolve, adjustedCheckPeriod); });
}
}
}
Expand All @@ -605,7 +605,7 @@ export interface ServerRequestConfig {
fetchAll: boolean,
}

export const sleep = (time: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, time));
export const sleep = (time: number): Promise<void> => new Promise((resolve) => { setTimeout(resolve, time); });

const defaultRequestConfig = {
fetchAll: false,
Expand Down Expand Up @@ -865,7 +865,8 @@ async function importDataset(
try {
if (isCloudStorage) {
const response = await Axios.post(url,
new FormData(), {
new FormData(),
{
params,
});
return response.data.rq_id;
Expand All @@ -880,13 +881,15 @@ async function importDataset(
},
};
await Axios.post(url,
new FormData(), {
new FormData(),
{
params,
headers: { 'Upload-Start': true },
});
await chunkUpload(file as File, uploadConfig);
const response = await Axios.post(url,
new FormData(), {
new FormData(),
{
params,
headers: { 'Upload-Finish': true },
});
Expand Down Expand Up @@ -945,7 +948,8 @@ async function restoreTask(storage: Storage, file: File | string): Promise<strin
if (isCloudStorage) {
params.filename = file as string;
response = await Axios.post(url,
new FormData(), {
new FormData(),
{
params,
});
return response.data.rq_id;
Expand All @@ -957,13 +961,15 @@ async function restoreTask(storage: Storage, file: File | string): Promise<strin
totalSize: (file as File).size,
};
await Axios.post(url,
new FormData(), {
new FormData(),
{
params,
headers: { 'Upload-Start': true },
});
const { filename } = await chunkUpload(file as File, uploadConfig);
response = await Axios.post(url,
new FormData(), {
new FormData(),
{
params: { ...params, filename },
headers: { 'Upload-Finish': true },
});
Expand Down Expand Up @@ -1024,7 +1030,8 @@ async function restoreProject(storage: Storage, file: File | string): Promise<st
if (isCloudStorage) {
params.filename = file;
response = await Axios.post(url,
new FormData(), {
new FormData(),
{
params,
});
return response.data.rq_id;
Expand All @@ -1036,13 +1043,15 @@ async function restoreProject(storage: Storage, file: File | string): Promise<st
totalSize: (file as File).size,
};
await Axios.post(url,
new FormData(), {
new FormData(),
{
params,
headers: { 'Upload-Start': true },
});
const { filename } = await chunkUpload(file as File, uploadConfig);
response = await Axios.post(url,
new FormData(), {
new FormData(),
{
params: { ...params, filename },
headers: { 'Upload-Finish': true },
});
Expand Down Expand Up @@ -1088,7 +1097,7 @@ async function createTask(
value.forEach((element, idx) => {
taskData.append(`${key}[${idx}]`, element);
});
} else {
} else if (typeof value !== 'object') {
taskData.set(key, value);
}
}
Expand Down Expand Up @@ -1153,7 +1162,8 @@ async function createTask(
let rqID = null;
try {
await Axios.post(`${backendAPI}/tasks/${response.data.id}/data`,
taskData, {
{},
{
...params,
headers: { 'Upload-Start': true },
});
Expand All @@ -1178,7 +1188,8 @@ async function createTask(
await bulkUpload(response.data.id, bulkFiles);
}
const dataResponse = await Axios.post(`${backendAPI}/tasks/${response.data.id}/data`,
taskData, {
taskDataSpec,
{
...params,
headers: { 'Upload-Finish': true },
});
Expand Down Expand Up @@ -1568,7 +1579,8 @@ async function uploadAnnotations(
try {
if (isCloudStorage) {
const response = await Axios.post(url,
new FormData(), {
new FormData(),
{
params,
});
return response.data.rq_id;
Expand All @@ -1579,13 +1591,15 @@ async function uploadAnnotations(
endpoint: `${origin}${backendAPI}/${session}s/${id}/annotations/`,
};
await Axios.post(url,
new FormData(), {
new FormData(),
{
params,
headers: { 'Upload-Start': true },
});
await chunkUpload(file as File, uploadConfig);
const response = await Axios.post(url,
new FormData(), {
new FormData(),
{
params,
headers: { 'Upload-Finish': true },
});
Expand Down
3 changes: 1 addition & 2 deletions cvat-core/src/session-implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,6 @@ export function implementTask(Task: typeof TaskClass): typeof TaskClass {
if (typeof this.id !== 'undefined') {
// If the task has been already created, we update it
const taskData = {
...fields,
...this._updateTrigger.getUpdated(this, {
bugTracker: 'bug_tracker',
projectId: 'project_id',
Expand Down Expand Up @@ -680,7 +679,6 @@ export function implementTask(Task: typeof TaskClass): typeof TaskClass {
}

const taskSpec: any = {
...fields,
name: this.name,
labels: this.labels.map((el) => el.toJSON()),
};
Expand Down Expand Up @@ -723,6 +721,7 @@ export function implementTask(Task: typeof TaskClass): typeof TaskClass {
...(typeof this.dataChunkSize !== 'undefined' ? { chunk_size: this.dataChunkSize } : {}),
...(typeof this.copyData !== 'undefined' ? { copy_data: this.copyData } : {}),
...(typeof this.cloudStorageId !== 'undefined' ? { cloud_storage_id: this.cloudStorageId } : {}),
...(fields.validation_params ? { validation_params: fields.validation_params } : {}),
};

const { taskID, rqID } = await serverProxy.tasks.create(
Expand Down
9 changes: 7 additions & 2 deletions cvat-core/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -737,9 +737,9 @@ export class Task extends Session {
public readonly cloudStorageId: number;
public readonly sortingMethod: string;

public readonly validationMethod: string;
public readonly validationMode: string | null;
public readonly validationFramesPercent: number;
public readonly validationFramesPerJob: number;
public readonly validationFramesPerJobPercent: number;
public readonly frameSelectionMethod: string;

constructor(initialData: Readonly<Omit<SerializedTask, 'labels' | 'jobs'> & {
Expand Down Expand Up @@ -786,6 +786,8 @@ export class Task extends Session {
cloud_storage_id: undefined,
sorting_method: undefined,
files: undefined,

validation_mode: null,
};

const updateTrigger = new FieldUpdateTrigger();
Expand Down Expand Up @@ -1113,6 +1115,9 @@ export class Task extends Session {
progress: {
get: () => data.progress,
},
validationMode: {
get: () => data.validation_mode,
},
_internalData: {
get: () => data,
},
Expand Down
6 changes: 4 additions & 2 deletions cvat-sdk/cvat_sdk/core/proxies/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,11 @@ def get_frames_info(self) -> List[models.IFrameMeta]:
return self.get_meta().frames

def remove_frames_by_ids(self, ids: Sequence[int]) -> None:
self._client.api_client.tasks_api.jobs_partial_update_data_meta(
self.api.partial_update_data_meta(
self.id,
patched_data_meta_write_request=models.PatchedDataMetaWriteRequest(deleted_frames=ids),
patched_job_data_meta_write_request=models.PatchedJobDataMetaWriteRequest(
deleted_frames=ids
),
)

def get_issues(self) -> List[Issue]:
Expand Down
1 change: 1 addition & 0 deletions cvat-sdk/cvat_sdk/core/proxies/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ def upload_data(
"filename_pattern",
"cloud_storage_id",
"server_files_exclude",
"validation_params",
],
)
)
Expand Down
21 changes: 8 additions & 13 deletions cvat-ui/src/actions/tasks-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import { filterNull } from 'utils/filter-null';
import { ThunkDispatch, ThunkAction } from 'utils/redux';

import { ValidationMethod } from 'components/create-task-page/quality-configuration-form';
import { ValidationMode } from 'components/create-task-page/quality-configuration-form';
import { getInferenceStatusAsync } from './models-actions';
import { updateRequestProgress } from './requests-actions';

Expand Down Expand Up @@ -257,19 +257,14 @@ ThunkAction {

let extras = {};

if (data.quality.validationMethod === ValidationMethod.GT) {
if (data.quality.validationMode !== ValidationMode.NONE) {
extras = {
validation_method: ValidationMethod.GT,
validation_frames_percent: data.quality.validationFramesPercent,
frame_selection_method: data.quality.frameSelectionMethod,
};
}

if (data.quality.validationMethod === ValidationMethod.HONEYPOTS) {
extras = {
validation_method: ValidationMethod.HONEYPOTS,
validation_frames_percent: data.quality.validationFramesPercent,
validation_frames_per_job: data.quality.validationFramesPerJob,
validation_params: {
mode: data.quality.validationMode,
frame_selection_method: data.quality.frameSelectionMethod,
frame_share: data.quality.validationFramesPercent,
frames_per_job_share: data.quality.validationFramesPerJobPercent,
},
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ interface Props {
frameNumber: number;
frameFilename: string;
frameDeleted: boolean;
deleteFrameAvailable: boolean;
deleteFrameShortcut: string;
focusFrameInputShortcut: string;
inputFrameRef: React.RefObject<HTMLInputElement>;
Expand Down Expand Up @@ -77,7 +76,6 @@ function PlayerNavigation(props: Props): JSX.Element {
ranges,
keyMap,
workspace,
deleteFrameAvailable,
onSliderChange,
onInputChange,
onURLIconClick,
Expand Down Expand Up @@ -192,9 +190,7 @@ function PlayerNavigation(props: Props): JSX.Element {
<CVATTooltip title='Create frame URL'>
<LinkOutlined className='cvat-player-frame-url-icon' onClick={onURLIconClick} />
</CVATTooltip>
{
deleteFrameAvailable && deleteFrameIcon
}
{ deleteFrameIcon }
</Col>
</Row>
</Col>
Expand Down
3 changes: 0 additions & 3 deletions cvat-ui/src/components/annotation-page/top-bar/top-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ interface Props {
focusFrameInputShortcut: string;
activeControl: ActiveControl;
toolsBlockerState: ToolsBlockerState;
deleteFrameAvailable: boolean;
annotationFilters: object[];
initialOpenGuide: boolean;
keyMap: KeyMap;
Expand Down Expand Up @@ -101,7 +100,6 @@ export default function AnnotationTopBarComponent(props: Props): JSX.Element {
toolsBlockerState,
annotationFilters,
initialOpenGuide,
deleteFrameAvailable,
navigationType,
jobInstance,
keyMap,
Expand Down Expand Up @@ -176,7 +174,6 @@ export default function AnnotationTopBarComponent(props: Props): JSX.Element {
onDeleteFrame={onDeleteFrame}
onRestoreFrame={onRestoreFrame}
switchNavigationBlocked={switchNavigationBlocked}
deleteFrameAvailable={deleteFrameAvailable}
/>
), 10]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ interface Props {
onChangeUseProjectTargetStorage(value: boolean): void;
onChangeSourceStorageLocation: (value: StorageLocation) => void;
onChangeTargetStorageLocation: (value: StorageLocation) => void;
onChangeSortingMethod(value: SortingMethod): void;
projectId: number | null;
useProjectSourceStorage: boolean;
useProjectTargetStorage: boolean;
Expand Down Expand Up @@ -225,6 +226,8 @@ class AdvancedConfigurationForm extends React.PureComponent<Props> {
}

private renderSortingMethodRadio(): JSX.Element {
const { onChangeSortingMethod } = this.props;

return (
<Form.Item
label='Sorting method'
Expand All @@ -237,7 +240,7 @@ class AdvancedConfigurationForm extends React.PureComponent<Props> {
]}
help='Specify how to sort images. It is not relevant for videos.'
>
<Radio.Group buttonStyle='solid'>
<Radio.Group buttonStyle='solid' onChange={(e) => onChangeSortingMethod(e.target.value)}>
<Radio.Button value={SortingMethod.LEXICOGRAPHICAL} key={SortingMethod.LEXICOGRAPHICAL}>
Lexicographical
</Radio.Button>
Expand Down
Loading

0 comments on commit 1285858

Please sign in to comment.