Model manager for DL models to preannotate images (#289)
parent
0dcf211d0c
commit
746cffb476
@ -0,0 +1,39 @@
|
|||||||
|
# Generated by Django 2.1.3 on 2019-01-24 14:05
|
||||||
|
|
||||||
|
import cvat.apps.auto_annotation.models
|
||||||
|
from django.conf import settings
|
||||||
|
import django.core.files.storage
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AnnotationModel',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', cvat.apps.auto_annotation.models.SafeCharField(max_length=256)),
|
||||||
|
('created_date', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_date', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('model_file', models.FileField(storage=django.core.files.storage.FileSystemStorage(), upload_to=cvat.apps.auto_annotation.models.upload_path_handler)),
|
||||||
|
('weights_file', models.FileField(storage=django.core.files.storage.FileSystemStorage(), upload_to=cvat.apps.auto_annotation.models.upload_path_handler)),
|
||||||
|
('labelmap_file', models.FileField(storage=django.core.files.storage.FileSystemStorage(), upload_to=cvat.apps.auto_annotation.models.upload_path_handler)),
|
||||||
|
('interpretation_file', models.FileField(storage=django.core.files.storage.FileSystemStorage(), upload_to=cvat.apps.auto_annotation.models.upload_path_handler)),
|
||||||
|
('shared', models.BooleanField(default=False)),
|
||||||
|
('primary', models.BooleanField(default=False)),
|
||||||
|
('framework', models.CharField(default=cvat.apps.auto_annotation.models.FrameworkChoice('openvino'), max_length=32)),
|
||||||
|
('owner', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'default_permissions': (),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,143 @@
|
|||||||
|
# Copyright (C) 2018 Intel Corporation
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import django_rq
|
||||||
|
import os
|
||||||
|
import rq
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
from django.db import transaction
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from .models import AnnotationModel, FrameworkChoice
|
||||||
|
|
||||||
|
def _remove_old_file(model_file_field):
|
||||||
|
if model_file_field and os.path.exists(model_file_field.name):
|
||||||
|
os.remove(model_file_field.name)
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def _update_dl_model_thread(dl_model_id, model_file, weights_file, labelmap_file, interpretation_file, run_tests):
|
||||||
|
def _get_file_content(filename):
|
||||||
|
return os.path.basename(filename), open(filename, "rb")
|
||||||
|
|
||||||
|
job = rq.get_current_job()
|
||||||
|
job.meta["progress"] = "Saving data"
|
||||||
|
job.save_meta()
|
||||||
|
|
||||||
|
dl_model = AnnotationModel.objects.select_for_update().get(pk=dl_model_id)
|
||||||
|
|
||||||
|
#save files in case of files should be uploaded from share
|
||||||
|
if model_file:
|
||||||
|
_remove_old_file(dl_model.model_file)
|
||||||
|
dl_model.model_file.save(*_get_file_content(model_file))
|
||||||
|
if weights_file:
|
||||||
|
_remove_old_file(dl_model.weights_file)
|
||||||
|
dl_model.weights_file.save(*_get_file_content(weights_file))
|
||||||
|
if labelmap_file:
|
||||||
|
_remove_old_file(dl_model.labelmap_file)
|
||||||
|
dl_model.labelmap_file.save(*_get_file_content(labelmap_file))
|
||||||
|
if interpretation_file:
|
||||||
|
_remove_old_file(dl_model.interpretation_file)
|
||||||
|
dl_model.interpretation_file.save(*_get_file_content(interpretation_file))
|
||||||
|
|
||||||
|
if run_tests:
|
||||||
|
#only for testing
|
||||||
|
import time
|
||||||
|
time.sleep(3)
|
||||||
|
job.meta["progress"] = "Test started"
|
||||||
|
job.save_meta()
|
||||||
|
time.sleep(5)
|
||||||
|
job.meta["progress"] = "Test finished"
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def update_model(dl_model_id, name, model_file, weights_file, labelmap_file, interpretation_file, storage, is_shared):
|
||||||
|
|
||||||
|
def get_abs_path(share_path):
|
||||||
|
if not share_path:
|
||||||
|
return share_path
|
||||||
|
share_root = settings.SHARE_ROOT
|
||||||
|
relpath = os.path.normpath(share_path).lstrip('/')
|
||||||
|
if '..' in relpath.split(os.path.sep):
|
||||||
|
raise Exception('Permission denied')
|
||||||
|
abspath = os.path.abspath(os.path.join(share_root, relpath))
|
||||||
|
if os.path.commonprefix([share_root, abspath]) != share_root:
|
||||||
|
raise Exception('Bad file path on share: ' + abspath)
|
||||||
|
return abspath
|
||||||
|
|
||||||
|
dl_model = AnnotationModel.objects.select_for_update().get(pk=dl_model_id)
|
||||||
|
|
||||||
|
if name:
|
||||||
|
dl_model.name = name
|
||||||
|
|
||||||
|
if is_shared != None:
|
||||||
|
dl_model.shared = is_shared
|
||||||
|
|
||||||
|
run_tests = bool(model_file or weights_file or labelmap_file or interpretation_file)
|
||||||
|
if storage != "local":
|
||||||
|
model_file = get_abs_path(model_file)
|
||||||
|
weights_file = get_abs_path(weights_file)
|
||||||
|
labelmap_file = get_abs_path(labelmap_file)
|
||||||
|
interpretation_file = get_abs_path(interpretation_file)
|
||||||
|
else:
|
||||||
|
if model_file:
|
||||||
|
_remove_old_file(dl_model.model_file)
|
||||||
|
dl_model.model_file = model_file
|
||||||
|
model_file = None
|
||||||
|
if weights_file:
|
||||||
|
_remove_old_file(dl_model.weights_file)
|
||||||
|
dl_model.weights_file = weights_file
|
||||||
|
weights_file = None
|
||||||
|
if labelmap_file:
|
||||||
|
_remove_old_file(dl_model.labelmap_file)
|
||||||
|
dl_model.labelmap_file = labelmap_file
|
||||||
|
labelmap_file = None
|
||||||
|
if interpretation_file:
|
||||||
|
_remove_old_file(dl_model.interpretation_file)
|
||||||
|
dl_model.interpretation_file = interpretation_file
|
||||||
|
interpretation_file = None
|
||||||
|
|
||||||
|
dl_model.updated_date = timezone.now()
|
||||||
|
dl_model.save()
|
||||||
|
|
||||||
|
rq_id = "auto_annotation.create.{}".format(dl_model_id)
|
||||||
|
queue = django_rq.get_queue('default')
|
||||||
|
queue.enqueue_call(
|
||||||
|
func = _update_dl_model_thread,
|
||||||
|
args = (dl_model_id,
|
||||||
|
model_file,
|
||||||
|
weights_file,
|
||||||
|
labelmap_file,
|
||||||
|
interpretation_file,
|
||||||
|
run_tests,
|
||||||
|
),
|
||||||
|
job_id = rq_id
|
||||||
|
)
|
||||||
|
|
||||||
|
return rq_id
|
||||||
|
|
||||||
|
def create_empty(owner, framework=FrameworkChoice.OPENVINO):
|
||||||
|
db_model = AnnotationModel(
|
||||||
|
owner=owner,
|
||||||
|
)
|
||||||
|
db_model.save()
|
||||||
|
|
||||||
|
model_path = db_model.get_dirname()
|
||||||
|
if os.path.isdir(model_path):
|
||||||
|
shutil.rmtree(model_path)
|
||||||
|
os.mkdir(model_path)
|
||||||
|
|
||||||
|
return db_model.id
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def delete(dl_model_id):
|
||||||
|
dl_model = AnnotationModel.objects.select_for_update().get(pk=dl_model_id)
|
||||||
|
if dl_model:
|
||||||
|
if dl_model.primary:
|
||||||
|
raise Exception("Can not delete primary model {}".format(dl_model_id))
|
||||||
|
|
||||||
|
dl_model.delete()
|
||||||
|
shutil.rmtree(dl_model.get_dirname(), ignore_errors=True)
|
||||||
|
else:
|
||||||
|
raise Exception("Requested DL model {} doesn't exist".format(dl_model_id))
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
# Copyright (C) 2018 Intel Corporation
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import rules
|
||||||
|
|
||||||
|
from cvat.apps.authentication.auth import has_admin_role, has_user_role
|
||||||
|
|
||||||
|
@rules.predicate
|
||||||
|
def is_model_owner(db_user, db_dl_model):
|
||||||
|
return db_dl_model.owner == db_user
|
||||||
|
|
||||||
|
@rules.predicate
|
||||||
|
def is_shared_model(_, db_dl_model):
|
||||||
|
return db_dl_model.shared
|
||||||
|
|
||||||
|
@rules.predicate
|
||||||
|
def is_primary_model(_, db_dl_model):
|
||||||
|
return db_dl_model.primary
|
||||||
|
|
||||||
|
def setup_permissions():
|
||||||
|
rules.add_perm('auto_annotation.model.create', has_admin_role | has_user_role)
|
||||||
|
|
||||||
|
rules.add_perm('auto_annotation.model.update', (has_admin_role | is_model_owner) & ~is_primary_model)
|
||||||
|
|
||||||
|
rules.add_perm('auto_annotation.model.delete', (has_admin_role | is_model_owner) & ~is_primary_model)
|
||||||
|
|
||||||
|
rules.add_perm('auto_annotation.model.access', has_admin_role | is_model_owner |
|
||||||
|
is_shared_model | is_primary_model)
|
||||||
@ -1,188 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 Intel Corporation
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* global
|
|
||||||
showMessage:false
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
window.cvat = window.cvat || {};
|
|
||||||
window.cvat.dashboard = window.cvat.dashboard || {};
|
|
||||||
window.cvat.dashboard.uiCallbacks = window.cvat.dashboard.uiCallbacks || [];
|
|
||||||
window.cvat.dashboard.uiCallbacks.push(function(newElements) {
|
|
||||||
let tids = [];
|
|
||||||
for (let el of newElements) {
|
|
||||||
tids.push(el.id.split("_")[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
type: "POST",
|
|
||||||
url: "/auto_annotation/meta/get",
|
|
||||||
data: JSON.stringify(tids),
|
|
||||||
contentType: "application/json; charset=utf-8",
|
|
||||||
success: (data) => {
|
|
||||||
newElements.each(function() {
|
|
||||||
let elem = $(this);
|
|
||||||
let tid = +elem.attr("id").split("_")[1];
|
|
||||||
|
|
||||||
const autoAnnoButton = $("<button> Run auto annotation </button>").addClass("regular dashboardButtonUI dashboardAutoAnno");
|
|
||||||
autoAnnoButton.appendTo(elem.find("div.dashboardButtonsUI")[0]);
|
|
||||||
|
|
||||||
if (tid in data && data[tid].active) {
|
|
||||||
autoAnnoButton.text("Cancel auto annotation");
|
|
||||||
autoAnnoButton.addClass("autoAnnotationProcess");
|
|
||||||
window.cvat.autoAnnotation.checkAutoAnnotationRequest(tid, autoAnnoButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
autoAnnoButton.on("click", () => {
|
|
||||||
if (autoAnnoButton.hasClass("autoAnnotationProcess")) {
|
|
||||||
$.post(`/auto_annotation/cancel/task/${tid}`).fail( (data) => {
|
|
||||||
let message = `Error during cancel auto annotation request. Code: ${data.status}. Message: ${data.responseText || data.statusText}`;
|
|
||||||
showMessage(message);
|
|
||||||
throw Error(message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
let dialogWindow = $(`#${window.cvat.autoAnnotation.modalWindowId}`);
|
|
||||||
dialogWindow.attr("current_tid", tid);
|
|
||||||
dialogWindow.removeClass("hidden");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
error: (data) => {
|
|
||||||
let message = `Can not get auto annotation meta info. Code: ${data.status}. Message: ${data.responseText || data.statusText}`;
|
|
||||||
window.cvat.autoAnnotation.badResponse(message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
window.cvat.autoAnnotation = {
|
|
||||||
modalWindowId: "autoAnnotationWindow",
|
|
||||||
autoAnnoFromId: "autoAnnotationForm",
|
|
||||||
autoAnnoModelFieldId: "autoAnnotationModelField",
|
|
||||||
autoAnnoWeightsFieldId: "autoAnnotationWeightsField",
|
|
||||||
autoAnnoConfigFieldId: "autoAnnotationConfigField",
|
|
||||||
autoAnnoConvertFieldId: "autoAnnotationConvertField",
|
|
||||||
autoAnnoCloseButtonId: "autoAnnoCloseButton",
|
|
||||||
autoAnnoSubmitButtonId: "autoAnnoSubmitButton",
|
|
||||||
|
|
||||||
checkAutoAnnotationRequest: (tid, autoAnnoButton) => {
|
|
||||||
function timeoutCallback() {
|
|
||||||
$.get(`/auto_annotation/check/task/${tid}`).done((data) => {
|
|
||||||
if (data.status === "started" || data.status === "queued") {
|
|
||||||
let progress = Math.round(data.progress) || 0;
|
|
||||||
autoAnnoButton.text(`Cancel auto annotation (${progress}%)`);
|
|
||||||
setTimeout(timeoutCallback, 1000);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
autoAnnoButton.text("Run auto annotation");
|
|
||||||
autoAnnoButton.removeClass("autoAnnotationProcess");
|
|
||||||
}
|
|
||||||
}).fail((data) => {
|
|
||||||
let message = "Error was occurred during check annotation status. " +
|
|
||||||
`Code: ${data.status}, text: ${data.responseText || data.statusText}`;
|
|
||||||
window.cvat.autoAnnotation.badResponse(message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setTimeout(timeoutCallback, 1000);
|
|
||||||
},
|
|
||||||
|
|
||||||
badResponse(message) {
|
|
||||||
showMessage(message);
|
|
||||||
throw Error(message);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function submitButtonOnClick() {
|
|
||||||
const annoWindow = $(`#${window.cvat.autoAnnotation.modalWindowId}`);
|
|
||||||
const tid = annoWindow.attr("current_tid");
|
|
||||||
const modelInput = $(`#${window.cvat.autoAnnotation.autoAnnoModelFieldId}`);
|
|
||||||
const weightsInput = $(`#${window.cvat.autoAnnotation.autoAnnoWeightsFieldId}`);
|
|
||||||
const configInput = $(`#${window.cvat.autoAnnotation.autoAnnoConfigFieldId}`);
|
|
||||||
const convFileInput = $(`#${window.cvat.autoAnnotation.autoAnnoConvertFieldId}`);
|
|
||||||
|
|
||||||
const modelFile = modelInput.prop("files")[0];
|
|
||||||
const weightsFile = weightsInput.prop("files")[0];
|
|
||||||
const configFile = configInput.prop("files")[0];
|
|
||||||
const convFile = convFileInput.prop("files")[0];
|
|
||||||
|
|
||||||
if (!modelFile || !weightsFile || !configFile || !convFile) {
|
|
||||||
showMessage("All files must be selected");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let taskData = new FormData();
|
|
||||||
taskData.append("model", modelFile);
|
|
||||||
taskData.append("weights", weightsFile);
|
|
||||||
taskData.append("config", configFile);
|
|
||||||
taskData.append("conv_script", convFile);
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: `/auto_annotation/create/task/${tid}`,
|
|
||||||
type: "POST",
|
|
||||||
data: taskData,
|
|
||||||
contentType: false,
|
|
||||||
processData: false,
|
|
||||||
}).done(() => {
|
|
||||||
annoWindow.addClass("hidden");
|
|
||||||
const autoAnnoButton = $(`#dashboardTask_${tid} div.dashboardButtonsUI button.dashboardAutoAnno`);
|
|
||||||
autoAnnoButton.addClass("autoAnnotationProcess");
|
|
||||||
window.cvat.autoAnnotation.checkAutoAnnotationRequest(tid, autoAnnoButton);
|
|
||||||
}).fail((data) => {
|
|
||||||
let message = "Error was occurred during run annotation request. " +
|
|
||||||
`Code: ${data.status}, text: ${data.responseText || data.statusText}`;
|
|
||||||
window.cvat.autoAnnotation.badResponse(message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
|
||||||
$(`<div id="${window.cvat.autoAnnotation.modalWindowId}" class="modal hidden">
|
|
||||||
<form id="${window.cvat.autoAnnotation.autoAnnoFromId}" class="modal-content" autocomplete="on" onsubmit="return false" style="width: 700px;">
|
|
||||||
<center>
|
|
||||||
<label class="semiBold h1"> Auto annotation setup </label>
|
|
||||||
</center>
|
|
||||||
|
|
||||||
<table style="width: 100%; text-align: left;">
|
|
||||||
<tr>
|
|
||||||
<td style="width: 25%"> <label class="regular h2"> Model </label> </td>
|
|
||||||
<td> <input id="${window.cvat.autoAnnotation.autoAnnoModelFieldId}" type="file" name="model" /> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="width: 25%"> <label class="regular h2"> Weights </label> </td>
|
|
||||||
<td> <input id="${window.cvat.autoAnnotation.autoAnnoWeightsFieldId}" type="file" name="weights" /> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="width: 25%"> <label class="regular h2"> Label map </label> </td>
|
|
||||||
<td> <input id="${window.cvat.autoAnnotation.autoAnnoConfigFieldId}" type="file" name="config" accept=".json" /> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="width: 25%"> <label class="regular h2"> Convertation script </label> </td>
|
|
||||||
<td> <input id="${window.cvat.autoAnnotation.autoAnnoConvertFieldId}" type="file" name="convert" /> </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<div>
|
|
||||||
<button id="${window.cvat.autoAnnotation.autoAnnoCloseButtonId}" class="regular h2"> Close </button>
|
|
||||||
<button id="${window.cvat.autoAnnotation.autoAnnoSubmitButtonId}" class="regular h2"> Submit </button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>`).appendTo("body");
|
|
||||||
|
|
||||||
const annoWindow = $(`#${window.cvat.autoAnnotation.modalWindowId}`);
|
|
||||||
const closeWindowButton = $(`#${window.cvat.autoAnnotation.autoAnnoCloseButtonId}`);
|
|
||||||
const submitButton = $(`#${window.cvat.autoAnnotation.autoAnnoSubmitButtonId}`);
|
|
||||||
|
|
||||||
|
|
||||||
closeWindowButton.on("click", () => {
|
|
||||||
annoWindow.addClass("hidden");
|
|
||||||
});
|
|
||||||
|
|
||||||
submitButton.on("click", () => {
|
|
||||||
submitButtonOnClick();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -0,0 +1,792 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global
|
||||||
|
showMessage
|
||||||
|
showOverlay
|
||||||
|
userConfirm
|
||||||
|
*/
|
||||||
|
|
||||||
|
window.cvat = window.cvat || {};
|
||||||
|
window.cvat.dashboard = window.cvat.dashboard || {};
|
||||||
|
window.cvat.dashboard.uiCallbacks = window.cvat.dashboard.uiCallbacks || [];
|
||||||
|
|
||||||
|
const AutoAnnotationServer = {
|
||||||
|
start(modelId, taskId, data, success, error, progress, check) {
|
||||||
|
$.ajax({
|
||||||
|
url: `/auto_annotation/start/${modelId}/${taskId}`,
|
||||||
|
type: 'POST',
|
||||||
|
data: JSON.stringify(data),
|
||||||
|
contentType: 'application/json',
|
||||||
|
success: (responseData) => {
|
||||||
|
check(responseData.id, success, error, progress);
|
||||||
|
},
|
||||||
|
error: (responseData) => {
|
||||||
|
const message = `Starting request has been failed. Code: ${responseData.status}. Message: ${responseData.responseText || responseData.statusText}`;
|
||||||
|
error(message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
update(data, success, error, progress, check, modelId) {
|
||||||
|
let url = '';
|
||||||
|
if (modelId === null) {
|
||||||
|
url = '/auto_annotation/create';
|
||||||
|
} else {
|
||||||
|
url = `/auto_annotation/update/${modelId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url,
|
||||||
|
type: 'POST',
|
||||||
|
data,
|
||||||
|
contentType: false,
|
||||||
|
processData: false,
|
||||||
|
success: (responseData) => {
|
||||||
|
check(responseData.id, success, error, progress);
|
||||||
|
},
|
||||||
|
error: (responseData) => {
|
||||||
|
const message = `Creating request has been failed. Code: ${responseData.status}. Message: ${responseData.responseText || responseData.statusText}`;
|
||||||
|
error(message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
delete(modelId, success, error) {
|
||||||
|
$.ajax({
|
||||||
|
url: `/auto_annotation/delete/${modelId}`,
|
||||||
|
type: 'DELETE',
|
||||||
|
success,
|
||||||
|
error: (data) => {
|
||||||
|
const message = `Deleting request has been failed. Code: ${data.status}. Message: ${data.responseText || data.statusText}`;
|
||||||
|
error(message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
check(workerId, success, error, progress) {
|
||||||
|
function updateProgress(data) {
|
||||||
|
if (data.progress && progress) {
|
||||||
|
progress(data.progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkCallback() {
|
||||||
|
$.ajax({
|
||||||
|
url: `/auto_annotation/check/${workerId}`,
|
||||||
|
type: 'GET',
|
||||||
|
success: (data) => {
|
||||||
|
updateProgress(data, progress);
|
||||||
|
|
||||||
|
switch (data.status) {
|
||||||
|
case 'failed':
|
||||||
|
error(`Checking request has returned the "${data.status}" status. Message: ${data.error}`);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'unknown':
|
||||||
|
error(`Checking request has returned the "${data.status}" status.`);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'finished':
|
||||||
|
success();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
setTimeout(checkCallback, 1000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (data) => {
|
||||||
|
const message = `Checking request has been failed. Code: ${data.status}. Message: ${data.responseText || data.statusText}`;
|
||||||
|
error(message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(checkCallback, 1000);
|
||||||
|
},
|
||||||
|
|
||||||
|
meta(tids, success, error) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/auto_annotation/meta/get',
|
||||||
|
type: 'POST',
|
||||||
|
data: JSON.stringify(tids),
|
||||||
|
contentType: 'application/json',
|
||||||
|
success,
|
||||||
|
error: (data) => {
|
||||||
|
const message = `Getting meta request has been failed. Code: ${data.status}. Message: ${data.responseText || data.statusText}`;
|
||||||
|
error(message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel(tid, success, error) {
|
||||||
|
$.ajax({
|
||||||
|
url: `/auto_annotation/cancel/${tid}`,
|
||||||
|
type: 'GET',
|
||||||
|
success,
|
||||||
|
error: (data) => {
|
||||||
|
const message = `Getting meta request has been failed. Code: ${data.status}. Message: ${data.responseText || data.statusText}`;
|
||||||
|
error(message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class AutoAnnotationModelManagerView {
|
||||||
|
constructor() {
|
||||||
|
const html = `<div class="modal hidden" id="${window.cvat.autoAnnotation.managerWindowId}">
|
||||||
|
<div class="modal-content" id="${window.cvat.autoAnnotation.managerContentId}">
|
||||||
|
<div style="float: left; width: 55%; height: 100%;">
|
||||||
|
<center>
|
||||||
|
<label class="regular h1"> Uploaded Models </label>
|
||||||
|
</center>
|
||||||
|
<div style="overflow: auto; height: 90%; margin-top: 2%;">
|
||||||
|
<table class="regular modelsTable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th> Name </th>
|
||||||
|
<th> Upload Date </th>
|
||||||
|
<th> Actions </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="${window.cvat.autoAnnotation.managerUploadedModelsId}"> </tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="regular" id="${window.cvat.autoAnnotation.uploadContentId}">
|
||||||
|
<center>
|
||||||
|
<label class="regular h1" id="${window.cvat.autoAnnotation.uploadTitleId}"> Create Model </label>
|
||||||
|
</center>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td style="width: 25%"> <label class="regular h3"> Name: </label> </td>
|
||||||
|
<td> <input type="text" id="${window.cvat.autoAnnotation.uploadNameInputId}" class="regular h3" style="width: 100%"> </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td> <label class="regular h3"> Source: </label> </td>
|
||||||
|
<td>
|
||||||
|
<input id="${window.cvat.autoAnnotation.uploadLocalSourceId}" type="radio" name="modelSourceType" value="local" checked>
|
||||||
|
<label for="${window.cvat.autoAnnotation.uploadLocalSourceId}" class="regular h3"> Local </label>
|
||||||
|
<br>
|
||||||
|
<input id="${window.cvat.autoAnnotation.uploadShareSourceId}" type="radio" name="modelSourceType" value="shared">
|
||||||
|
<label for="${window.cvat.autoAnnotation.uploadShareSourceId}" class="regular h3"> Share </label>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id="${window.cvat.autoAnnotation.uploadGloballyBlockId}">
|
||||||
|
<td> <label class="regular h3"> Upload Globally </label> </td>
|
||||||
|
<td> <input type="checkbox" id="${window.cvat.autoAnnotation.uploadGloballyId}"> </td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<div style="text-align: left;">
|
||||||
|
<div>
|
||||||
|
<button id="${window.cvat.autoAnnotation.selectFilesButtonId}" class="regular h3"> Select Files </button>
|
||||||
|
<label id="${window.cvat.autoAnnotation.selectedFilesId}" class="regular h3" style="margin-left: 10px"> No Files </label>
|
||||||
|
<input id="${window.cvat.autoAnnotation.localFileSelectorId}" type="file" accept=".bin,.xml,.json,.py" style="display: none" multiple>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div style="float: right; width: 50%; height: 50px;">
|
||||||
|
<button class="regular h3" id="${window.cvat.autoAnnotation.submitUploadButtonId}"> Submit </button>
|
||||||
|
<button class="regular h3" id="${window.cvat.autoAnnotation.cancelUploadButtonId}"> Cancel </button>
|
||||||
|
</div>
|
||||||
|
<div style="float: left; overflow-y: auto; height: 75px; overflow: auto; width: 100%; word-break: break-word;">
|
||||||
|
<label class="regular h3 selectable" style="float: left;" id="${window.cvat.autoAnnotation.uploadMessageId}"> </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
this.el = $(html);
|
||||||
|
|
||||||
|
this.table = this.el.find(`#${window.cvat.autoAnnotation.managerUploadedModelsId}`);
|
||||||
|
this.globallyBlock = this.el.find(`#${window.cvat.autoAnnotation.uploadGloballyBlockId}`);
|
||||||
|
this.uploadTitle = this.el.find(`#${window.cvat.autoAnnotation.uploadTitleId}`);
|
||||||
|
this.uploadNameInput = this.el.find(`#${window.cvat.autoAnnotation.uploadNameInputId}`);
|
||||||
|
this.uploadMessage = this.el.find(`#${window.cvat.autoAnnotation.uploadMessageId}`);
|
||||||
|
this.selectedFilesLabel = this.el.find(`#${window.cvat.autoAnnotation.selectedFilesId}`);
|
||||||
|
this.modelNameInput = this.el.find(`#${window.cvat.autoAnnotation.uploadNameInputId}`);
|
||||||
|
this.localSource = this.el.find(`#${window.cvat.autoAnnotation.uploadLocalSourceId}`);
|
||||||
|
this.shareSource = this.el.find(`#${window.cvat.autoAnnotation.uploadShareSourceId}`);
|
||||||
|
this.cancelButton = this.el.find(`#${window.cvat.autoAnnotation.cancelUploadButtonId}`);
|
||||||
|
this.submitButton = this.el.find(`#${window.cvat.autoAnnotation.submitUploadButtonId}`);
|
||||||
|
this.globallyBox = this.el.find(`#${window.cvat.autoAnnotation.uploadGloballyId}`);
|
||||||
|
this.selectButton = this.el.find(`#${window.cvat.autoAnnotation.selectFilesButtonId}`);
|
||||||
|
this.localSelector = this.el.find(`#${window.cvat.autoAnnotation.localFileSelectorId}`);
|
||||||
|
this.shareSelector = $('#dashboardShareBrowseModal');
|
||||||
|
this.shareBrowseTree = $('#dashboardShareBrowser');
|
||||||
|
this.submitShare = $('#dashboardSubmitBrowseServer');
|
||||||
|
|
||||||
|
this.id = null;
|
||||||
|
this.source = this.localSource.prop('checked') ? 'local' : 'shared';
|
||||||
|
this.files = [];
|
||||||
|
|
||||||
|
function filesLabel(source, files) {
|
||||||
|
const fileLabels = source === 'local' ? [...files].map(el => el.name) : files;
|
||||||
|
if (fileLabels.length) {
|
||||||
|
const labelStr = fileLabels.join(', ');
|
||||||
|
if (labelStr.length > 30) {
|
||||||
|
return `${labelStr.substr(0, 30)}..`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return labelStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'No Files';
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractFiles(extensions, files, source) {
|
||||||
|
const extractedFiles = {};
|
||||||
|
function getExt(file) {
|
||||||
|
return source === 'local' ? file.name.split('.').pop() : file.split('.').pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
function addFile(file, extention) {
|
||||||
|
if (extention in files) {
|
||||||
|
throw Error(`More than one file with the extension .${extention} have been found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
extractedFiles[extention] = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
files.forEach((file) => {
|
||||||
|
const fileExt = getExt(file);
|
||||||
|
if (extensions.includes(fileExt)) {
|
||||||
|
addFile(file, fileExt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return extractedFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateFiles(isUpdate, files, source) {
|
||||||
|
const extensions = ['xml', 'bin', 'py', 'json'];
|
||||||
|
const extractedFiles = extractFiles(extensions, files, source);
|
||||||
|
|
||||||
|
if (!isUpdate) {
|
||||||
|
extensions.forEach((extension) => {
|
||||||
|
if (!(extension in extractedFiles)) {
|
||||||
|
throw Error(`Please specify a .${extension} file`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return extractedFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.localSource.on('click', () => {
|
||||||
|
if (this.source !== 'local') {
|
||||||
|
this.source = 'local';
|
||||||
|
this.files = [];
|
||||||
|
this.selectedFilesLabel.text(filesLabel(this.source, this.files));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.shareSource.on('click', () => {
|
||||||
|
if (this.source !== 'shared') {
|
||||||
|
this.source = 'shared';
|
||||||
|
this.files = [];
|
||||||
|
this.selectedFilesLabel.text(filesLabel(this.source, this.files));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.selectButton.on('click', () => {
|
||||||
|
if (this.source === 'local') {
|
||||||
|
this.localSelector.click();
|
||||||
|
} else {
|
||||||
|
this.shareSelector.appendTo('body');
|
||||||
|
this.shareBrowseTree.jstree('refresh');
|
||||||
|
this.shareSelector.removeClass('hidden');
|
||||||
|
this.shareBrowseTree.jstree({
|
||||||
|
core: {
|
||||||
|
data: {
|
||||||
|
url: 'get_share_nodes',
|
||||||
|
data: node => ({ id: node.id }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: ['checkbox', 'sort'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.submitShare.on('click', () => {
|
||||||
|
if (!this.el.hasClass('hidden')) {
|
||||||
|
this.shareSelector.addClass('hidden');
|
||||||
|
this.files = this.shareBrowseTree.jstree(true).get_selected();
|
||||||
|
this.selectedFilesLabel.text(filesLabel(this.source, this.files));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.localSelector.on('change', (e) => {
|
||||||
|
this.files = Array.from(e.target.files);
|
||||||
|
this.selectedFilesLabel.text(filesLabel(this.source, this.files));
|
||||||
|
});
|
||||||
|
|
||||||
|
this.cancelButton.on('click', () => this.el.addClass('hidden'));
|
||||||
|
this.submitButton.on('click', () => {
|
||||||
|
try {
|
||||||
|
this.submitButton.prop('disabled', true);
|
||||||
|
|
||||||
|
const name = $.trim(this.modelNameInput.prop('value'));
|
||||||
|
if (!name.length) {
|
||||||
|
this.uploadMessage.css('color', 'red');
|
||||||
|
this.uploadMessage.text('Please specify a model name');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let validatedFiles = {};
|
||||||
|
try {
|
||||||
|
validatedFiles = validateFiles(this.id !== null, this.files, this.source);
|
||||||
|
} catch (err) {
|
||||||
|
this.uploadMessage.css('color', 'red');
|
||||||
|
this.uploadMessage.text(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modelData = new FormData();
|
||||||
|
modelData.append('name', name);
|
||||||
|
modelData.append('storage', this.source);
|
||||||
|
modelData.append('shared', this.globallyBox.prop('checked'));
|
||||||
|
|
||||||
|
['xml', 'bin', 'json', 'py'].filter(e => e in validatedFiles).forEach((ext) => {
|
||||||
|
modelData.append(ext, validatedFiles[ext]);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.uploadMessage.text('');
|
||||||
|
const overlay = showOverlay('Send request to the server..');
|
||||||
|
window.cvat.autoAnnotation.server.update(modelData, () => {
|
||||||
|
window.location.reload();
|
||||||
|
}, (message) => {
|
||||||
|
overlay.remove();
|
||||||
|
showMessage(message);
|
||||||
|
}, (progress) => {
|
||||||
|
overlay.setMessage(progress);
|
||||||
|
}, window.cvat.autoAnnotation.server.check, this.id);
|
||||||
|
} finally {
|
||||||
|
this.submitButton.prop('disabled', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
const setBlocked = () => {
|
||||||
|
if (window.cvat.autoAnnotation.data.admin) {
|
||||||
|
this.globallyBlock.removeClass('hidden');
|
||||||
|
} else {
|
||||||
|
this.globallyBlock.addClass('hidden');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setBlocked();
|
||||||
|
this.uploadTitle.text('Create Model');
|
||||||
|
this.uploadNameInput.prop('value', '');
|
||||||
|
this.uploadMessage.css('color', '');
|
||||||
|
this.uploadMessage.text('');
|
||||||
|
this.selectedFilesLabel.text('No Files');
|
||||||
|
this.localSource.prop('checked', true);
|
||||||
|
this.globallyBox.prop('checked', false);
|
||||||
|
this.table.empty();
|
||||||
|
|
||||||
|
this.id = null;
|
||||||
|
this.source = this.localSource.prop('checked') ? 'local' : 'share';
|
||||||
|
this.files = [];
|
||||||
|
|
||||||
|
const updateButtonClickHandler = (event) => {
|
||||||
|
this.reset();
|
||||||
|
|
||||||
|
this.uploadTitle.text('Update Model');
|
||||||
|
this.uploadNameInput.prop('value', `${event.data.model.name}`);
|
||||||
|
this.id = event.data.model.id;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteButtonClickHandler = (event) => {
|
||||||
|
userConfirm(`Do you actually want to delete the "${event.data.model.name}" model. Are you sure?`, () => {
|
||||||
|
window.cvat.autoAnnotation.server.delete(event.data.model.id, () => {
|
||||||
|
const filtered = window.cvat.autoAnnotation.data.models.filter(
|
||||||
|
item => item !== event.data.model,
|
||||||
|
);
|
||||||
|
window.cvat.autoAnnotation.data.models = filtered;
|
||||||
|
this.reset();
|
||||||
|
}, (message) => {
|
||||||
|
showMessage(message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getModelModifyButtons = (model) => {
|
||||||
|
if (model.primary) {
|
||||||
|
return '<td> <label class="h1 regular"> Primary Model </label> </td>';
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateButtonHtml = '<button class="regular h3" style="width: 7em;"> Update </button>';
|
||||||
|
const deleteButtonHtml = '<button class="regular h3" style="width: 7em; margin-top: 5%;"> Delete </button>';
|
||||||
|
|
||||||
|
return $('<td> </td>').append(
|
||||||
|
$(updateButtonHtml).on('click', { model }, updateButtonClickHandler),
|
||||||
|
$(deleteButtonHtml).on('click', { model }, deleteButtonClickHandler),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.cvat.autoAnnotation.data.models.forEach((model) => {
|
||||||
|
const rowHtml = `<tr>
|
||||||
|
<td> ${model.name} </td>
|
||||||
|
<td> ${model.uploadDate} </td>
|
||||||
|
</tr>`;
|
||||||
|
|
||||||
|
this.table.append(
|
||||||
|
$(rowHtml).append(getModelModifyButtons(model)),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
this.el.removeClass('hidden');
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
get element() {
|
||||||
|
return this.el;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AutoAnnotationModelRunnerView {
|
||||||
|
constructor() {
|
||||||
|
const html = `<div class="modal hidden" id="${window.cvat.autoAnnotation.runnerWindowId}">
|
||||||
|
<div class="modal-content" id="${window.cvat.autoAnnotation.runnerContentId}">
|
||||||
|
<div style="width: 55%; height: 100%; float: left;">
|
||||||
|
<center style="height: 10%;">
|
||||||
|
<label class="regular h1"> Uploaded Models </label>
|
||||||
|
</center>
|
||||||
|
<div style="height: 70%; overflow: auto; margin-top: 2%;">
|
||||||
|
<table class="modelsTable" id="${window.cvat.autoAnnotation.runnerUploadedModelsId}"> </table>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" id="${window.cvat.autoAnnotation.removeCurrentAnnotationId}"/>
|
||||||
|
<label class="regular h3" for="${window.cvat.autoAnnotation.removeCurrentAnnotationId}"> Remove current annotation </label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="width: 40%; height: 100%; float: left; margin-left: 3%;">
|
||||||
|
<center style="height: 10%;">
|
||||||
|
<label class="regular h1"> Annotation Labels </label>
|
||||||
|
</center>
|
||||||
|
<div style="height: 70%; overflow: auto; margin-top: 2%;">
|
||||||
|
<table class="regular" style="text-align: center; word-break: break-all; width: 100%;">
|
||||||
|
<thead>
|
||||||
|
<tr style="width: 100%;">
|
||||||
|
<th style="width: 45%;"> Task Label </th>
|
||||||
|
<th style="width: 45%;"> DL Model Label </th>
|
||||||
|
<th style="width: 10%;"> </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="${window.cvat.autoAnnotation.annotationLabelsId}">
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div style="float:right;">
|
||||||
|
<button class="regular h3" style="width: 6em;" id="${window.cvat.autoAnnotation.submitAnnotationId}"> Start </button>
|
||||||
|
<button class="regular h3" style="width: 6em;" id="${window.cvat.autoAnnotation.cancelAnnotationId}"> Cancel </button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
this.el = $(html);
|
||||||
|
this.id = null;
|
||||||
|
this.tid = null;
|
||||||
|
this.initButton = null;
|
||||||
|
this.modelsTable = this.el.find(`#${window.cvat.autoAnnotation.runnerUploadedModelsId}`);
|
||||||
|
this.labelsTable = this.el.find(`#${window.cvat.autoAnnotation.annotationLabelsId}`);
|
||||||
|
this.active = null;
|
||||||
|
|
||||||
|
this.el.find(`#${window.cvat.autoAnnotation.cancelAnnotationId}`).on('click', () => {
|
||||||
|
this.el.addClass('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.el.find(`#${window.cvat.autoAnnotation.submitAnnotationId}`).on('click', () => {
|
||||||
|
try {
|
||||||
|
if (this.id === null) {
|
||||||
|
throw Error('Please specify a model for an annotation process');
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapping = {};
|
||||||
|
$('.annotatorMappingRow').each((_, element) => {
|
||||||
|
const dlModelLabel = $(element).find('.annotatorDlLabelSelector')[0].value;
|
||||||
|
const taskLabel = $(element).find('.annotatorTaskLabelSelector')[0].value;
|
||||||
|
if (dlModelLabel in mapping) {
|
||||||
|
throw Error(`The label "${dlModelLabel}" has been specified twice or more`);
|
||||||
|
}
|
||||||
|
mapping[dlModelLabel] = taskLabel;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!Object.keys(mapping).length) {
|
||||||
|
throw Error('Labels for an annotation process haven\'t been found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const overlay = showOverlay('Request has been sent');
|
||||||
|
window.cvat.autoAnnotation.server.start(this.id, this.tid, {
|
||||||
|
reset: $(`#${window.cvat.autoAnnotation.removeCurrentAnnotationId}`).prop('checked'),
|
||||||
|
labels: mapping,
|
||||||
|
}, () => {
|
||||||
|
overlay.remove();
|
||||||
|
this.initButton[0].setupRun();
|
||||||
|
window.cvat.autoAnnotation.runner.hide();
|
||||||
|
}, (message) => {
|
||||||
|
overlay.remove();
|
||||||
|
this.initButton[0].setupRun();
|
||||||
|
showMessage(message);
|
||||||
|
}, () => {
|
||||||
|
window.location.reload();
|
||||||
|
}, window.cvat.autoAnnotation.server.check);
|
||||||
|
} catch (error) {
|
||||||
|
showMessage(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(data, initButton) {
|
||||||
|
function labelsSelect(labels, elClass) {
|
||||||
|
const select = $(`<select class="regular h3 ${elClass}" style="width:100%;"> </select>`);
|
||||||
|
labels.forEach(label => select.append($(`<option value="${label}"> ${label} </option>`)));
|
||||||
|
select.prop('value', null);
|
||||||
|
|
||||||
|
return select;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeCreator(dlSelect, taskSelect, callback) {
|
||||||
|
let dlIsFilled = false;
|
||||||
|
let taskIsFilled = false;
|
||||||
|
const creator = $('<tr style="margin-bottom: 5px;"> </tr>').append(
|
||||||
|
$('<td style="width: 45%;"> </td>').append(taskSelect),
|
||||||
|
$('<td style="width: 45%;"> </td>').append(dlSelect),
|
||||||
|
);
|
||||||
|
|
||||||
|
const onSelectHandler = () => {
|
||||||
|
$('<td style="width: 10%; position: relative;"> </td>').append(
|
||||||
|
$('<a class="close"></a>').css('top', '0px').on('click', (e) => {
|
||||||
|
$(e.target.parentNode.parentNode).remove();
|
||||||
|
}),
|
||||||
|
).appendTo(creator);
|
||||||
|
|
||||||
|
creator.addClass('annotatorMappingRow');
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
dlSelect.on('change', (e) => {
|
||||||
|
if (e.target.value && taskIsFilled) {
|
||||||
|
dlSelect.off('change');
|
||||||
|
taskSelect.off('change');
|
||||||
|
onSelectHandler();
|
||||||
|
}
|
||||||
|
dlIsFilled = Boolean(e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
taskSelect.on('change', (e) => {
|
||||||
|
if (e.target.value && dlIsFilled) {
|
||||||
|
dlSelect.off('change');
|
||||||
|
taskSelect.off('change');
|
||||||
|
onSelectHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
taskIsFilled = Boolean(e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
return creator;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.id = null;
|
||||||
|
this.initButton = initButton;
|
||||||
|
this.tid = data.taskid;
|
||||||
|
this.modelsTable.empty();
|
||||||
|
this.labelsTable.empty();
|
||||||
|
this.active = null;
|
||||||
|
|
||||||
|
const modelItemClickHandler = (event) => {
|
||||||
|
if (this.active) {
|
||||||
|
this.active.style.color = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.id = event.data.model.id;
|
||||||
|
this.active = event.target;
|
||||||
|
this.active.style.color = 'darkblue';
|
||||||
|
|
||||||
|
this.labelsTable.empty();
|
||||||
|
const labels = Object.values(event.data.data.spec.labels);
|
||||||
|
const intersection = labels.filter(el => event.data.model.labels.indexOf(el) !== -1);
|
||||||
|
intersection.forEach((label) => {
|
||||||
|
const dlSelect = labelsSelect(event.data.model.labels, 'annotatorDlLabelSelector');
|
||||||
|
dlSelect.prop('value', label);
|
||||||
|
const taskSelect = labelsSelect(labels, 'annotatorTaskLabelSelector');
|
||||||
|
taskSelect.prop('value', label);
|
||||||
|
$('<tr class="annotatorMappingRow" style="margin-bottom: 5px;"> </tr>').append(
|
||||||
|
$('<td style="width: 45%;"> </td>').append(taskSelect),
|
||||||
|
$('<td style="width: 45%;"> </td>').append(dlSelect),
|
||||||
|
$('<td style="width: 10%; position: relative;"> </td>').append(
|
||||||
|
$('<a class="close"></a>').css('top', '0px').on('click', (e) => {
|
||||||
|
$(e.target.parentNode.parentNode).remove();
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).appendTo(this.labelsTable);
|
||||||
|
});
|
||||||
|
|
||||||
|
const dlSelect = labelsSelect(event.data.model.labels, 'annotatorDlLabelSelector');
|
||||||
|
const taskSelect = labelsSelect(labels, 'annotatorTaskLabelSelector');
|
||||||
|
|
||||||
|
const callback = () => {
|
||||||
|
makeCreator(
|
||||||
|
labelsSelect(event.data.model.labels, 'annotatorDlLabelSelector'),
|
||||||
|
labelsSelect(labels, 'annotatorTaskLabelSelector'),
|
||||||
|
callback,
|
||||||
|
).appendTo(this.labelsTable);
|
||||||
|
};
|
||||||
|
|
||||||
|
makeCreator(dlSelect, taskSelect, callback).appendTo(this.labelsTable);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.cvat.autoAnnotation.data.models.forEach((model) => {
|
||||||
|
this.modelsTable.append(
|
||||||
|
$(`<tr> <td> <label class="regular h3"> ${model.name} (${model.uploadDate}) </label> </td> </tr>`).on(
|
||||||
|
'click', { model, data }, modelItemClickHandler,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
this.el.removeClass('hidden');
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this.el.addClass('hidden');
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
get element() {
|
||||||
|
return this.el;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.cvat.autoAnnotation = {
|
||||||
|
managerWindowId: 'annotatorManagerWindow',
|
||||||
|
managerContentId: 'annotatorManagerContent',
|
||||||
|
managerUploadedModelsId: 'annotatorManagerUploadedModels',
|
||||||
|
uploadContentId: 'annotatorManagerUploadModel',
|
||||||
|
uploadTitleId: 'annotatorManagerUploadTitle',
|
||||||
|
uploadNameInputId: 'annotatorManagerUploadNameInput',
|
||||||
|
uploadLocalSourceId: 'annotatorManagerUploadLocalSource',
|
||||||
|
uploadShareSourceId: 'annotatorManagerUploadShareSource',
|
||||||
|
uploadGloballyId: 'annotatorManagerUploadGlobally',
|
||||||
|
uploadGloballyBlockId: 'annotatorManagerUploadGloballyblock',
|
||||||
|
selectFilesButtonId: 'annotatorManagerUploadSelector',
|
||||||
|
selectedFilesId: 'annotatorManagerUploadSelectedFiles',
|
||||||
|
localFileSelectorId: 'annotatorManagerUploadLocalSelector',
|
||||||
|
shareFileSelectorId: 'annotatorManagerUploadShareSelector',
|
||||||
|
submitUploadButtonId: 'annotatorManagerSubmitUploadButton',
|
||||||
|
cancelUploadButtonId: 'annotatorManagerCancelUploadButton',
|
||||||
|
uploadMessageId: 'annotatorUploadStatusMessage',
|
||||||
|
|
||||||
|
runnerWindowId: 'annotatorRunnerWindow',
|
||||||
|
runnerContentId: 'annotatorRunnerContent',
|
||||||
|
runnerUploadedModelsId: 'annotatorRunnerUploadedModels',
|
||||||
|
removeCurrentAnnotationId: 'annotatorRunnerRemoveCurrentAnnotationBox',
|
||||||
|
annotationLabelsId: 'annotatorRunnerAnnotationLabels',
|
||||||
|
submitAnnotationId: 'annotatorRunnerSubmitAnnotationButton',
|
||||||
|
cancelAnnotationId: 'annotatorRunnerCancelAnnotationButton',
|
||||||
|
|
||||||
|
managerButtonId: 'annotatorManagerButton',
|
||||||
|
};
|
||||||
|
|
||||||
|
window.cvat.dashboard.uiCallbacks.push((newElements) => {
|
||||||
|
window.cvat.autoAnnotation.server = AutoAnnotationServer;
|
||||||
|
window.cvat.autoAnnotation.manager = new AutoAnnotationModelManagerView();
|
||||||
|
window.cvat.autoAnnotation.runner = new AutoAnnotationModelRunnerView();
|
||||||
|
|
||||||
|
const tids = Array.from(newElements, el => el.id.split('_')[1]);
|
||||||
|
|
||||||
|
window.cvat.autoAnnotation.server.meta(tids, (data) => {
|
||||||
|
window.cvat.autoAnnotation.data = data;
|
||||||
|
$('body').append(window.cvat.autoAnnotation.manager.element, window.cvat.autoAnnotation.runner.element);
|
||||||
|
$(`<button id="${window.cvat.autoAnnotation.managerButtonId}" class="regular h1" style=""> Model Manager</button>`)
|
||||||
|
.on('click', () => {
|
||||||
|
const overlay = showOverlay('The manager are being setup..');
|
||||||
|
window.cvat.autoAnnotation.manager.reset().show();
|
||||||
|
overlay.remove();
|
||||||
|
}).appendTo('#dashboardManageButtons');
|
||||||
|
|
||||||
|
newElements.each((_, element) => {
|
||||||
|
const elem = $(element);
|
||||||
|
const tid = +elem.attr('id').split('_')[1];
|
||||||
|
|
||||||
|
const button = $('<button> Run Auto Annotation </button>').addClass('regular dashboardButtonUI');
|
||||||
|
button[0].setupRun = function setupRun() {
|
||||||
|
const self = $(this);
|
||||||
|
self.text('Run Auto Annotation').off('click').on('click', () => {
|
||||||
|
const overlay = showOverlay('Task date are being recieved from the server..');
|
||||||
|
$.ajax({
|
||||||
|
url: `/get/task/${tid}`,
|
||||||
|
dataType: 'json',
|
||||||
|
success: (responseData) => {
|
||||||
|
overlay.setMessage('The model runner are being setup..');
|
||||||
|
window.cvat.autoAnnotation.runner.reset(responseData, self).show();
|
||||||
|
overlay.remove();
|
||||||
|
},
|
||||||
|
error: (responseData) => {
|
||||||
|
showMessage(`Can't get task data. Code: ${responseData.status}. Message: ${responseData.responseText || responseData.statusText}`);
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
overlay.remove();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
button[0].setupCancel = function setupCancel() {
|
||||||
|
const self = $(this);
|
||||||
|
self.off('click').text('Cancel Auto Annotation').on('click', () => {
|
||||||
|
userConfirm('Process will be canceled. Are you sure?', () => {
|
||||||
|
window.cvat.autoAnnotation.server.cancel(tid, () => {
|
||||||
|
this.setupRun();
|
||||||
|
}, (message) => {
|
||||||
|
showMessage(message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
window.cvat.autoAnnotation.server.check(
|
||||||
|
window.cvat.autoAnnotation.data.run[tid].rq_id,
|
||||||
|
() => {
|
||||||
|
this.setupRun();
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
button[0].setupRun();
|
||||||
|
button.text('Annotation has failed');
|
||||||
|
button.title(error);
|
||||||
|
},
|
||||||
|
(progress) => {
|
||||||
|
button.text(`Cancel Auto Annotation (${progress.toString().slice(0, 4)})%`);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const taskStatus = window.cvat.autoAnnotation.data.run[tid];
|
||||||
|
if (taskStatus && ['queued', 'started'].includes(taskStatus.status)) {
|
||||||
|
button[0].setupCancel();
|
||||||
|
} else {
|
||||||
|
button[0].setupRun();
|
||||||
|
}
|
||||||
|
|
||||||
|
button.appendTo(elem.find('div.dashboardButtonsUI')[0]);
|
||||||
|
});
|
||||||
|
}, (error) => {
|
||||||
|
showMessage(`Cannot get models meta information: ${error}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
#annotatorManagerContent, #annotatorRunnerContent {
|
||||||
|
width: 800px;
|
||||||
|
height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#annotatorManagerButton {
|
||||||
|
padding: 7px;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modelsTable {
|
||||||
|
width: 100%;
|
||||||
|
color:#666;
|
||||||
|
text-shadow: 1px 1px 0px #fff;
|
||||||
|
background:#D2D3D4;
|
||||||
|
border:#ccc 1px solid;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: 0 1px 2px black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modelsTable th {
|
||||||
|
border-top: 1px solid #fafafa;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
background: #ededed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modelsTable th:first-child {
|
||||||
|
text-align: left;
|
||||||
|
padding-left:20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modelsTable tr:first-child th:first-child {
|
||||||
|
border-top-left-radius:3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modelsTable tr:first-child th:last-child {
|
||||||
|
border-top-right-radius:3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modelsTable tr {
|
||||||
|
text-align: center;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modelsTable td:first-child {
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 20px;
|
||||||
|
border-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modelsTable td {
|
||||||
|
padding: 18px;
|
||||||
|
border-top: 1px solid #ffffff;
|
||||||
|
border-bottom:1px solid #e0e0e0;
|
||||||
|
border-left: 1px solid #e0e0e0;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modelsTable tr.even td {
|
||||||
|
background: #f6f6f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modelsTable tr:last-child td {
|
||||||
|
border-bottom:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modelsTable tr:last-child td:first-child {
|
||||||
|
border-bottom-left-radius:3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modelsTable tr:last-child td:last-child {
|
||||||
|
border-bottom-right-radius:3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modelsTable tr:hover td {
|
||||||
|
background: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#annotatorManagerUploadModel {
|
||||||
|
float: left;
|
||||||
|
padding-left: 3%;
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue