You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
257 lines
9.6 KiB
TypeScript
257 lines
9.6 KiB
TypeScript
// Copyright (C) 2020 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
import getCore from 'cvat-core';
|
|
import { Canvas } from 'cvat-canvas';
|
|
import { ShapeType, RQStatus } from 'reducers/interfaces';
|
|
|
|
const core = getCore();
|
|
const baseURL = core.config.backendAPI.slice(0, -7);
|
|
|
|
interface DEXTRPlugin {
|
|
name: string;
|
|
description: string;
|
|
cvat: {
|
|
classes: {
|
|
Job: {
|
|
prototype: {
|
|
annotations: {
|
|
put: {
|
|
enter(self: any, objects: any[]): Promise<void>;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
data: {
|
|
canceled: boolean;
|
|
enabled: boolean;
|
|
};
|
|
}
|
|
|
|
interface Point {
|
|
x: number;
|
|
y: number;
|
|
}
|
|
|
|
const antModalRoot = document.createElement('div');
|
|
const antModalMask = document.createElement('div');
|
|
antModalMask.classList.add('ant-modal-mask');
|
|
const antModalWrap = document.createElement('div');
|
|
antModalWrap.classList.add('ant-modal-wrap');
|
|
antModalWrap.setAttribute('role', 'dialog');
|
|
const antModal = document.createElement('div');
|
|
antModal.classList.add('ant-modal');
|
|
antModal.style.width = '300px';
|
|
antModal.style.top = '40%';
|
|
antModal.setAttribute('role', 'document');
|
|
const antModalContent = document.createElement('div');
|
|
antModalContent.classList.add('ant-modal-content');
|
|
const antModalBody = document.createElement('div');
|
|
antModalBody.classList.add('ant-modal-body');
|
|
antModalBody.style.textAlign = 'center';
|
|
const antModalSpan = document.createElement('span');
|
|
antModalSpan.innerText = 'Segmentation request is being processed';
|
|
antModalSpan.style.display = 'block';
|
|
const antModalButton = document.createElement('button');
|
|
antModalButton.disabled = true;
|
|
antModalButton.classList.add('ant-btn', 'ant-btn-primary');
|
|
antModalButton.style.width = '100px';
|
|
antModalButton.style.margin = '10px auto';
|
|
const antModalButtonSpan = document.createElement('span');
|
|
antModalButtonSpan.innerText = 'Cancel';
|
|
|
|
antModalBody.append(antModalSpan, antModalButton);
|
|
antModalButton.append(antModalButtonSpan);
|
|
antModalContent.append(antModalBody);
|
|
antModal.append(antModalContent);
|
|
antModalWrap.append(antModal);
|
|
antModalRoot.append(antModalMask, antModalWrap);
|
|
|
|
|
|
function serverRequest(
|
|
plugin: DEXTRPlugin,
|
|
jid: number,
|
|
frame: number,
|
|
points: number[],
|
|
): Promise<number[]> {
|
|
return new Promise((resolve, reject) => {
|
|
const reducer = (acc: Point[], _: number, index: number, array: number[]): Point[] => {
|
|
if (!(index % 2)) { // 0, 2, 4
|
|
acc.push({
|
|
x: array[index],
|
|
y: array[index + 1],
|
|
});
|
|
}
|
|
|
|
return acc;
|
|
};
|
|
|
|
const reducedPoints = points.reduce(reducer, []);
|
|
core.server.request(
|
|
`${baseURL}/dextr/create/${jid}`, {
|
|
method: 'POST',
|
|
data: JSON.stringify({
|
|
frame,
|
|
points: reducedPoints,
|
|
}),
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
},
|
|
).then(() => {
|
|
const timeoutCallback = (): void => {
|
|
core.server.request(
|
|
`${baseURL}/dextr/check/${jid}`, {
|
|
method: 'GET',
|
|
},
|
|
).then((response: any) => {
|
|
const { status } = response;
|
|
if (status === RQStatus.finished) {
|
|
resolve(response.result.split(/\s|,/).map((coord: string) => +coord));
|
|
} else if (status === RQStatus.failed) {
|
|
reject(new Error(response.stderr));
|
|
} else if (status === RQStatus.unknown) {
|
|
reject(new Error('Unknown DEXTR status has been received'));
|
|
} else {
|
|
if (status === RQStatus.queued) {
|
|
antModalButton.disabled = false;
|
|
}
|
|
if (!plugin.data.canceled) {
|
|
setTimeout(timeoutCallback, 1000);
|
|
} else {
|
|
core.server.request(
|
|
`${baseURL}/dextr/cancel/${jid}`, {
|
|
method: 'GET',
|
|
},
|
|
).then(() => {
|
|
resolve(points);
|
|
}).catch((error: Error) => {
|
|
reject(error);
|
|
});
|
|
}
|
|
}
|
|
}).catch((error: Error) => {
|
|
reject(error);
|
|
});
|
|
};
|
|
|
|
setTimeout(timeoutCallback, 1000);
|
|
}).catch((error: Error) => {
|
|
reject(error);
|
|
});
|
|
});
|
|
|
|
// start checking
|
|
}
|
|
|
|
const plugin: DEXTRPlugin = {
|
|
name: 'Deep extreme cut',
|
|
description: 'Plugin allows to get a polygon from extreme points using AI',
|
|
cvat: {
|
|
classes: {
|
|
Job: {
|
|
prototype: {
|
|
annotations: {
|
|
put: {
|
|
async enter(self: DEXTRPlugin, objects: any[]): Promise<void> {
|
|
try {
|
|
if (self.data.enabled) {
|
|
document.body.append(antModalRoot);
|
|
const promises: Record<number, Promise<number[]>> = {};
|
|
for (let i = 0; i < objects.length; i++) {
|
|
if (objects[i].points.length >= 8) {
|
|
promises[i] = serverRequest(
|
|
self,
|
|
(this as any).id,
|
|
objects[i].frame,
|
|
objects[i].points,
|
|
);
|
|
} else {
|
|
promises[i] = new Promise((resolve) => {
|
|
resolve(objects[i].points);
|
|
});
|
|
}
|
|
}
|
|
|
|
const transformed = await Promise
|
|
.all(Object.values(promises));
|
|
for (let i = 0; i < objects.length; i++) {
|
|
// eslint-disable-next-line no-param-reassign
|
|
objects[i] = new core.classes.ObjectState({
|
|
frame: objects[i].frame,
|
|
objectType: objects[i].objectType,
|
|
label: objects[i].label,
|
|
shapeType: ShapeType.POLYGON,
|
|
points: transformed[i],
|
|
occluded: objects[i].occluded,
|
|
zOrder: objects[i].zOrder,
|
|
});
|
|
}
|
|
}
|
|
|
|
return;
|
|
} catch (error) {
|
|
throw new core.exceptions.PluginError(error.toString());
|
|
} finally {
|
|
// eslint-disable-next-line no-param-reassign
|
|
self.data.canceled = false;
|
|
antModalButton.disabled = true;
|
|
if (antModalRoot.parentElement === document.body) {
|
|
document.body.removeChild(antModalRoot);
|
|
}
|
|
}
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
data: {
|
|
canceled: false,
|
|
enabled: false,
|
|
},
|
|
};
|
|
|
|
|
|
antModalButton.onclick = () => {
|
|
plugin.data.canceled = true;
|
|
};
|
|
|
|
export function activate(canvasInstance: Canvas): void {
|
|
if (!plugin.data.enabled) {
|
|
// eslint-disable-next-line no-param-reassign
|
|
canvasInstance.draw = (drawData: any): void => {
|
|
if (drawData.enabled && drawData.shapeType === ShapeType.POLYGON
|
|
&& (typeof (drawData.numberOfPoints) === 'undefined' || drawData.numberOfPoints >= 4)
|
|
&& (typeof (drawData.initialState) === 'undefined')
|
|
) {
|
|
const patchedData = { ...drawData };
|
|
patchedData.shapeType = ShapeType.POINTS;
|
|
patchedData.crosshair = true;
|
|
Object.getPrototypeOf(canvasInstance)
|
|
.draw.call(canvasInstance, patchedData);
|
|
} else {
|
|
Object.getPrototypeOf(canvasInstance)
|
|
.draw.call(canvasInstance, drawData);
|
|
}
|
|
};
|
|
plugin.data.enabled = true;
|
|
}
|
|
}
|
|
|
|
export function deactivate(canvasInstance: Canvas): void {
|
|
if (plugin.data.enabled) {
|
|
// eslint-disable-next-line no-param-reassign
|
|
canvasInstance.draw = Object.getPrototypeOf(canvasInstance).draw;
|
|
plugin.data.enabled = false;
|
|
}
|
|
}
|
|
|
|
export function registerDEXTRPlugin(): void {
|
|
core.plugins.register(plugin);
|
|
}
|