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