From 216a0b5f6b557f532517312b2595c5e087b6963d Mon Sep 17 00:00:00 2001
From: Martin Zurowietz
Date: Mon, 19 Oct 2020 10:29:22 +0200
Subject: [PATCH 01/22] Implement optimizations from UnKnoT
---
requirements.txt | 19 +++---
.../scripts/instance-segmentation/Config.py | 16 +++--
.../scripts/instance-segmentation/Dataset.py | 36 ++++-------
.../instance-segmentation/DatasetGenerator.py | 64 ++++++++++---------
4 files changed, 67 insertions(+), 68 deletions(-)
diff --git a/requirements.txt b/requirements.txt
index c76fa6b..5a55155 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,12 +1,13 @@
-numpy==1.16.*
-scipy==1.1.*
-scikit-learn==0.22.*
-scikit-image==0.16.*
-Pillow==7.1.*
cython
-matplotlib==3.1.*
-tensorflow==1.15.4
-keras==2.1.*
-opencv-python-headless>=3.4.5,<4.0
h5py==2.10.*
imgaug==0.3.*
+keras==2.1.*
+matplotlib==3.1.*
+numpy==1.16.*
+opencv-python-headless>=3.4.5,<4.0
+Pillow==7.1.*
+pyvips==2.1.*
+scikit-image==0.16.*
+scikit-learn==0.22.*
+scipy==1.1.*
+tensorflow==1.15.4
diff --git a/src/resources/scripts/instance-segmentation/Config.py b/src/resources/scripts/instance-segmentation/Config.py
index 82a1633..df983af 100644
--- a/src/resources/scripts/instance-segmentation/Config.py
+++ b/src/resources/scripts/instance-segmentation/Config.py
@@ -1,6 +1,6 @@
import numpy as np
import mrcnn.config
-import imgaug
+import imgaug.augmenters as iaa
import math
class Config(mrcnn.config.Config):
@@ -13,16 +13,18 @@ def __init__(self, trainset, name='no_name'):
self.VALIDATION_STEPS = 0
# This is the mean pixel of the training images.
self.MEAN_PIXEL = np.array(trainset['mean_pixel'])
- self.AUGMENTATION = imgaug.augmenters.Sometimes(0.5, [
- imgaug.augmenters.Fliplr(0.5),
- imgaug.augmenters.Flipud(0.5),
- imgaug.augmenters.Affine(rotate=(-180, 180)),
- imgaug.augmenters.GaussianBlur(sigma=(0.0, 2.0)),
- ])
+ self.AUGMENTATION = iaa.SomeOf((0, None), [
+ iaa.Fliplr(1.0),
+ iaa.Flipud(1.0),
+ iaa.Affine(rotate=[90, 180, 270]),
+ iaa.GaussianBlur(sigma=(1.0, 2.0)),
+ iaa.JpegCompression(compression=(25, 50)),
+ ], random_order=True)
super().__init__()
class TrainingConfig(Config):
def __init__(self, params, trainset):
+ self.RPN_NMS_THRESHOLD = 0.85
self.IMAGE_MAX_DIM = trainset['crop_dimension']
# Determine the number of images per batch that the GPU can handle. From the
# Mask R-CNN config: "A 12GB GPU can typically handle 2 images of 1024x1024px."
diff --git a/src/resources/scripts/instance-segmentation/Dataset.py b/src/resources/scripts/instance-segmentation/Dataset.py
index 7ebf539..d397c8d 100644
--- a/src/resources/scripts/instance-segmentation/Dataset.py
+++ b/src/resources/scripts/instance-segmentation/Dataset.py
@@ -25,32 +25,24 @@ def prepare(self):
def load_mask(self, image_index):
file = self.masks[image_index]
- mask = imread(file)
- _, contours, _ = cv2.findContours(np.copy(mask), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
-
- # The class to which a contour belongs is defined by the values of its pixels
- # (class_id).
- class_ids = []
- class_masks = []
- for c in contours:
- c = c.squeeze(axis=1)
- source_class_id = mask[c[0][1], c[0][0]]
- # Map the source ID to the internal continuous IDs.
- class_id = self.map_source_class_id('{}.{}'.format(self.name, source_class_id))
+ data = np.load(file, allow_pickle=True)
+ classes = []
+ masks = []
+ for mask in data['masks']:
+ # Only one class "Interesting" is supported for now.
+ source_class_id = 1
if source_class_id not in self.ignore_classes:
- class_mask = np.zeros((mask.shape[0], mask.shape[1]))
- cv2.drawContours(class_mask, [c], -1, 1, -1)
- class_ids.append(class_id)
- class_masks.append(class_mask)
+ classes.append(self.map_source_class_id('{}.{}'.format(self.name, source_class_id)))
+ masks.append(mask)
+
+ if len(classes) == 0:
+ return super().load_mask(image_index)
- if len(class_ids) == 0:
- shape = mask.shape
- class_masks = np.zeros((shape[0], shape[1], 0), dtype=np.bool)
- else:
- class_masks = np.stack(class_masks, axis = 2).astype(np.bool)
+ classes = np.array(classes, dtype=np.int32)
+ masks = np.stack(masks, axis = 2).astype(np.bool)
- return class_masks, np.array(class_ids).astype(np.int32)
+ return masks, classes
class TrainingDataset(Dataset):
def __init__(self, trainset):
diff --git a/src/resources/scripts/instance-segmentation/DatasetGenerator.py b/src/resources/scripts/instance-segmentation/DatasetGenerator.py
index e24d109..f503756 100644
--- a/src/resources/scripts/instance-segmentation/DatasetGenerator.py
+++ b/src/resources/scripts/instance-segmentation/DatasetGenerator.py
@@ -2,8 +2,8 @@
import sys
import json
import cv2
+from pyvips import Image as VipsImage
import numpy as np
-from PIL import Image
from concurrent.futures import ThreadPoolExecutor, as_completed
class DatasetGenerator(object):
@@ -66,48 +66,45 @@ def ensure_train_masks_dirs(self):
os.makedirs(self.training_masks_path)
def process_image(self, imageId, proposals):
- image = Image.open(self.images[imageId])
try:
- image.load()
- except IOError as e:
+ image = VipsImage.new_from_file(self.images[imageId], fail=True)
+ except Exception as e:
print('Image #{} is corrupt! Skipping...'.format(imageId))
return False, False, False
- mask = np.zeros((image.height, image.width), dtype=np.uint8)
+ masks = []
for proposal in proposals:
- # 1 is the class ID of 'Interesting'.
- # Observe order of drawing the circles if multiple classes should be trained.
- # One solution might be to draw the circle of the current proposal again when
- # its crop is generated in the loop below, so the current proposal is always
- # "on top".
+ mask = np.zeros((image.height, image.width), dtype=np.uint8)
cv2.circle(mask, (proposal[0], proposal[1]), proposal[2], 1, -1)
+ masks.append(mask.astype(np.bool))
- mask = Image.fromarray(mask)
- images = []
- masks = []
+ image_paths = []
+ mask_paths = []
mean_pixels = []
for i, proposal in enumerate(proposals):
image_file = '{}_{}.jpg'.format(imageId, i)
- mask_file = '{}.png'.format(image_file)
- image_crop, mask_crop = self.generate_crop(image, mask, proposal)
- image_crop.save('{}/{}'.format(self.training_images_path, image_file))
- mask_crop.save('{}/{}'.format(self.training_masks_path, mask_file))
- images.append(image_file)
- masks.append(mask_file)
- mean_pixels.append(np.array(image_crop).reshape((-1, 3)).mean(axis = 0))
+ image_crop, mask_crops = self.generate_crop(image, masks, proposal)
+ mask_file = self.save_mask(mask_crops, image_file, self.training_masks_path)
+ image_crop.write_to_file(os.path.join(self.training_images_path, image_file), strip=True, Q=95)
+ image_paths.append(image_file)
+ mask_paths.append(mask_file)
+ np_crop = np.ndarray(buffer=image_crop.write_to_memory(), shape=[image_crop.height, image_crop.width, image_crop.bands], dtype=np.uint8)
+ mean_pixels.append(np_crop.reshape((-1, 3)).mean(axis = 0))
- return images, masks, mean_pixels
+ return image_paths, mask_paths, mean_pixels
- def generate_crop(self, image, mask, proposal):
- width, height = image.size
- if width < self.crop_dimension or height < self.crop_dimension:
- raise Exception('Image is smaller than the crop dimension!')
+ def generate_crop(self, image, masks, proposal):
+ width, height = image.width, image.height
+
+ crop_width = min(width, self.crop_dimension)
+ crop_height = min(height, self.crop_dimension)
+ current_crop_dimension = np.array([crop_width, crop_height])
center = np.array([proposal[0], proposal[1]])
- topLeft = np.round(center - self.crop_dimension / 2).astype(np.int32)
- bottomRight = np.round(center + self.crop_dimension / 2).astype(np.int32)
+ topLeft = np.round(center - current_crop_dimension / 2).astype(np.int32)
+ bottomRight = np.round(center + current_crop_dimension / 2).astype(np.int32)
offset = [0, 0]
if topLeft[0] < 0: offset[0] = abs(topLeft[0])
if topLeft[1] < 0: offset[1] = abs(topLeft[1])
@@ -117,10 +114,17 @@ def generate_crop(self, image, mask, proposal):
topLeft += offset
bottomRight += offset
- image_crop = image.crop((topLeft[0], topLeft[1], bottomRight[0], bottomRight[1]))
- mask_crop = mask.crop((topLeft[0], topLeft[1], bottomRight[0], bottomRight[1]))
+ image_crop = image.extract_area(topLeft[0], topLeft[1], current_crop_dimension[0], current_crop_dimension[1])
+ mask_crops = [mask[topLeft[1]:bottomRight[1], topLeft[0]:bottomRight[0]] for mask in masks]
+
+ return image_crop, mask_crops
+
+ def save_mask(self, masks, filename, path):
+ mask_store = [mask for mask in masks if np.any(mask)]
+ mask_file = '{}.npz'.format(filename)
+ np.savez_compressed(os.path.join(path, mask_file), masks=mask_store)
- return image_crop, mask_crop
+ return mask_file
with open(sys.argv[1]) as f:
params = json.load(f)
From f5ef516588f62e088a8ad7e2e3181c41d9c878c2 Mon Sep 17 00:00:00 2001
From: Martin Zurowietz
Date: Mon, 19 Oct 2020 10:29:36 +0200
Subject: [PATCH 02/22] WIP Start to implement support for a train scheme
---
src/Jobs/InstanceSegmentationRequest.php | 1 +
.../instance-segmentation/TrainingRunner.py | 36 +++++++------------
2 files changed, 13 insertions(+), 24 deletions(-)
diff --git a/src/Jobs/InstanceSegmentationRequest.php b/src/Jobs/InstanceSegmentationRequest.php
index d503e47..334d638 100644
--- a/src/Jobs/InstanceSegmentationRequest.php
+++ b/src/Jobs/InstanceSegmentationRequest.php
@@ -176,6 +176,7 @@ protected function createTrainingJson($outputJsonPath)
{
$path = "{$this->tmpDir}/input-training.json";
$content = [
+ // TODO implement is_train_scheme instead of epochs_head and epochs_all
'is_epochs_head' => intval($this->jobParams['is_epochs_head']),
'is_epochs_all' => intval($this->jobParams['is_epochs_all']),
'tmp_dir' => $this->tmpDir,
diff --git a/src/resources/scripts/instance-segmentation/TrainingRunner.py b/src/resources/scripts/instance-segmentation/TrainingRunner.py
index 3ceec3e..f07b00c 100644
--- a/src/resources/scripts/instance-segmentation/TrainingRunner.py
+++ b/src/resources/scripts/instance-segmentation/TrainingRunner.py
@@ -8,10 +8,7 @@
class TrainingRunner(object):
def __init__(self, params, trainset):
- # Number of epochs to train head layers.
- self.epochs_head = params['is_epochs_head']
- # Number of epochs to train all layers.
- self.epochs_all = params['is_epochs_all']
+ self.train_scheme = params['is_train_scheme']
# Path to the directory to store temporary files.
self.tmp_dir = params['tmp_dir']
# Path to the COCO pretrained weights for Mask R-CNN
@@ -42,26 +39,17 @@ def run(self):
"mrcnn_mask",
])
- # Train the head branches
- # Passing layers="heads" freezes all layers except the head layers.
- model.train(self.dataset,
- val_dataset=None,
- learning_rate=self.config.LEARNING_RATE,
- augmentation=self.config.AUGMENTATION,
- workers=self.max_workers,
- epochs=self.epochs_head,
- layers='heads')
-
- # Fine tune all layers
- # The epochs *include* those of the previous training. So set 20 if you want to
- # train for 10 epochs and already trained for 10 epochs.
- model.train(self.dataset,
- val_dataset=None,
- learning_rate=self.config.LEARNING_RATE / 10,
- augmentation=self.config.AUGMENTATION,
- workers=self.max_workers,
- epochs=self.epochs_head + self.epochs_all,
- layers='all')
+ epochs = 0
+ for train_step in scheme:
+ print('Train step: ', train_step)
+ epochs += train_step['epochs']
+ model.train(train_dataset,
+ val_dataset=None,
+ learning_rate=train_step['learning_rate'],
+ augmentation=self.config.AUGMENTATION,
+ workers=self.max_workers,
+ epochs=epochs,
+ layers=train_step['layers'])
model_path = os.path.join(self.model_dir, "mask_rcnn_final.h5")
model.keras_model.save_weights(model_path)
From 35449a1b97b4599442f349d9b02009f281f96a2d Mon Sep 17 00:00:00 2001
From: Martin Zurowietz
Date: Tue, 20 Oct 2020 13:09:32 +0200
Subject: [PATCH 03/22] Implement use of instance segmentation train scheme in
the backend
---
.../Controllers/Api/MaiaJobController.php | 6 ++--
src/Http/Requests/StoreMaiaJob.php | 7 +++--
src/Jobs/InstanceSegmentationRequest.php | 4 +--
.../instance-segmentation/TrainingRunner.py | 4 +--
.../Controllers/Api/MaiaJobControllerTest.php | 31 +++++++++++++++----
.../Jobs/InstanceSegmentationRequestTest.php | 10 +++---
6 files changed, 41 insertions(+), 21 deletions(-)
diff --git a/src/Http/Controllers/Api/MaiaJobController.php b/src/Http/Controllers/Api/MaiaJobController.php
index befbd68..d936a34 100644
--- a/src/Http/Controllers/Api/MaiaJobController.php
+++ b/src/Http/Controllers/Api/MaiaJobController.php
@@ -31,8 +31,7 @@ class MaiaJobController extends Controller
* @apiParam (Required parameters) {number} nd_epochs Time spent on training when determining the training proposals.
* @apiParam (Required parameters) {number} nd_stride A higher stride increases the speed of the novelty detection but reduces the sensitivity to small regions or objects.
* @apiParam (Required parameters) {number} nd_ignore_radius Ignore training proposals or annotation candidates which have a radius smaller or equal than this value in pixels.
- * @apiParam (Required parameters) {number} is_epochs_head Time spent on training only the head layers of Mask R-CNN for instance segmentation.
- * @apiParam (Required parameters) {number} is_epochs_all Time spent on training all layers of Mask R-CNN for instance segmentation.
+ * @apiParam (Required parameters) {array} is_train_scheme An array containing objects with the following properties. `layers`: Either `heads` or `all`, `epochs`: Number of epochs to train this step, `learing_rate`: Learing rate to use in this step.
* @apiParam (Optional parameters) {booolean} use_existing Set to `true` to use existing annotations as training proposals.
* @apiParam (Optional parameters) {Array} restrict_labels Array of label IDs to restrict the existing annotations to, which should be used as training proposals. `use_existing` must be set if this parameter is present.
* @apiParam (Optional parameters) {boolean} skip_nd Set to `true` to skip the novelty detection stage and take only existing annotations as training proposals. `use_existing` must be set if this parameter is present. Also, all `nd_*` parameters are ignored and no longer required if this parameter is set.
@@ -51,8 +50,7 @@ public function store(StoreMaiaJob $request)
'restrict_labels',
'skip_nd',
// is_* are parameters for instance segmentation.
- 'is_epochs_head',
- 'is_epochs_all',
+ 'is_train_scheme',
];
if (!$request->has('skip_nd')) {
diff --git a/src/Http/Requests/StoreMaiaJob.php b/src/Http/Requests/StoreMaiaJob.php
index f653d8a..b1e8b74 100644
--- a/src/Http/Requests/StoreMaiaJob.php
+++ b/src/Http/Requests/StoreMaiaJob.php
@@ -49,8 +49,11 @@ public function rules()
'nd_epochs' => 'required_unless:skip_nd,true|integer|min:50|max:1000',
'nd_stride' => 'required_unless:skip_nd,true|integer|min:1|max:10',
'nd_ignore_radius' => 'required_unless:skip_nd,true|integer|min:0',
- 'is_epochs_head' => 'required|integer|min:1',
- 'is_epochs_all' => 'required|integer|min:1',
+ 'is_train_scheme' => 'required|array|min:1',
+ 'is_train_scheme.*' => 'array',
+ 'is_train_scheme.*.layers' => 'required|in:heads,all',
+ 'is_train_scheme.*.epochs' => 'required|integer|min:1',
+ 'is_train_scheme.*.learning_rate' => 'required|numeric|min:0|max:1',
];
}
diff --git a/src/Jobs/InstanceSegmentationRequest.php b/src/Jobs/InstanceSegmentationRequest.php
index 334d638..5c985fb 100644
--- a/src/Jobs/InstanceSegmentationRequest.php
+++ b/src/Jobs/InstanceSegmentationRequest.php
@@ -176,9 +176,7 @@ protected function createTrainingJson($outputJsonPath)
{
$path = "{$this->tmpDir}/input-training.json";
$content = [
- // TODO implement is_train_scheme instead of epochs_head and epochs_all
- 'is_epochs_head' => intval($this->jobParams['is_epochs_head']),
- 'is_epochs_all' => intval($this->jobParams['is_epochs_all']),
+ 'is_train_scheme' => $this->jobParams['is_train_scheme'],
'tmp_dir' => $this->tmpDir,
'available_bytes' => intval(config('maia.available_bytes')),
'max_workers' => intval(config('maia.max_workers')),
diff --git a/src/resources/scripts/instance-segmentation/TrainingRunner.py b/src/resources/scripts/instance-segmentation/TrainingRunner.py
index f07b00c..3165dc8 100644
--- a/src/resources/scripts/instance-segmentation/TrainingRunner.py
+++ b/src/resources/scripts/instance-segmentation/TrainingRunner.py
@@ -40,10 +40,10 @@ def run(self):
])
epochs = 0
- for train_step in scheme:
+ for train_step in self.train_scheme:
print('Train step: ', train_step)
epochs += train_step['epochs']
- model.train(train_dataset,
+ model.train(self.dataset,
val_dataset=None,
learning_rate=train_step['learning_rate'],
augmentation=self.config.AUGMENTATION,
diff --git a/tests/Http/Controllers/Api/MaiaJobControllerTest.php b/tests/Http/Controllers/Api/MaiaJobControllerTest.php
index b296069..e65a3a7 100644
--- a/tests/Http/Controllers/Api/MaiaJobControllerTest.php
+++ b/tests/Http/Controllers/Api/MaiaJobControllerTest.php
@@ -27,8 +27,10 @@ public function setUp(): void
'nd_epochs' => 100,
'nd_stride' => 2,
'nd_ignore_radius' => 5,
- 'is_epochs_head' => 20,
- 'is_epochs_all' => 10,
+ 'is_train_scheme' => [
+ ['layers' => 'heads', 'epochs' => 10, 'learning_rate' => 0.001],
+ ['layers' => 'all', 'epochs' => 10, 'learning_rate' => 0.0001],
+ ],
];
ImageTest::create(['volume_id' => $this->volume()->id]);
}
@@ -55,8 +57,23 @@ public function testStore()
'nd_epochs' => 100,
'nd_stride' => 2,
'nd_ignore_radius' => 5,
- 'is_epochs_head' => 20,
- 'is_epochs_all' => 10,
+ 'is_train_scheme' => [
+ ['layers' => 'heads', 'epochs' => 10, 'learning_rate' => 0.001],
+ ['layers' => 'all', 'epochs' => 10, 'learning_rate' => 0.0001],
+ ],
+ ])->assertStatus(422);
+
+ // empty train scheme
+ $this->postJson("/api/v1/volumes/{$id}/maia-jobs", [
+ 'nd_clusters' => 5,
+ 'nd_patch_size' => 40,
+ 'nd_threshold' => 99,
+ 'nd_latent_size' => 0.1,
+ 'nd_trainset_size' => 10000,
+ 'nd_epochs' => 100,
+ 'nd_stride' => 2,
+ 'nd_ignore_radius' => 5,
+ 'is_train_scheme' => [],
])->assertStatus(422);
$this->postJson("/api/v1/volumes/{$id}/maia-jobs", $this->defaultParams)
@@ -168,8 +185,10 @@ public function testStoreSkipNd()
$params = [
'skip_nd' => true,
'nd_clusters' => 10,
- 'is_epochs_head' => 1,
- 'is_epochs_all' => 1,
+ 'is_train_scheme' => [
+ ['layers' => 'heads', 'epochs' => 10, 'learning_rate' => 0.001],
+ ['layers' => 'all', 'epochs' => 10, 'learning_rate' => 0.0001],
+ ],
];
$this->postJson("/api/v1/volumes/{$id}/maia-jobs", $params)
// Requires 'use_existing'.
diff --git a/tests/Jobs/InstanceSegmentationRequestTest.php b/tests/Jobs/InstanceSegmentationRequestTest.php
index b35de17..80fd422 100644
--- a/tests/Jobs/InstanceSegmentationRequestTest.php
+++ b/tests/Jobs/InstanceSegmentationRequestTest.php
@@ -23,8 +23,9 @@ public function testHandle()
FileCache::fake();
$params = [
- 'is_epochs_head' => 20,
- 'is_epochs_all' => 10,
+ 'is_train_scheme' => [
+ ['layers' => 'all', 'epochs' => 10, 'learning_rate' => 0.001],
+ ],
'available_bytes' => 8E+9,
'max_workers' => 2,
];
@@ -61,8 +62,9 @@ public function testHandle()
];
$expectTrainingJson = [
- 'is_epochs_head' => 20,
- 'is_epochs_all' => 10,
+ 'is_train_scheme' => [
+ ['layers' => 'all', 'epochs' => 10, 'learning_rate' => 0.001],
+ ],
'available_bytes' => 8E+9,
'max_workers' => 2,
'tmp_dir' => $tmpDir,
From 337f5ab4cde8f1139a9b4f8951798054f4e3638e Mon Sep 17 00:00:00 2001
From: Martin Zurowietz
Date: Tue, 20 Oct 2020 14:20:25 +0200
Subject: [PATCH 04/22] Implement instance segmentation training scheme in the
frontend
---
.../Controllers/Views/MaiaJobController.php | 12 +++-
src/public/assets/scripts/main.js | 2 +-
src/public/mix-manifest.json | 2 +-
src/resources/assets/js/form.vue | 16 +++++
src/resources/views/index/job-form.blade.php | 60 +++++++++++++-----
.../tutorials/instance-segmentation.blade.php | 16 ++---
.../views/manualReferences.blade.php | 4 ++
src/resources/views/show/info-tab.blade.php | 62 +++++++++++++------
8 files changed, 123 insertions(+), 51 deletions(-)
diff --git a/src/Http/Controllers/Views/MaiaJobController.php b/src/Http/Controllers/Views/MaiaJobController.php
index 1384937..2dd58db 100644
--- a/src/Http/Controllers/Views/MaiaJobController.php
+++ b/src/Http/Controllers/Views/MaiaJobController.php
@@ -52,12 +52,22 @@ public function index($id)
$newestJobHasFailed = $jobs->isNotEmpty() ? $jobs[0]->hasFailed() : false;
+ $defaultTrainScheme = collect([
+ ['layers' => 'heads', 'epochs' => 10, 'learning_rate' => 0.001],
+ ['layers' => 'heads', 'epochs' => 10, 'learning_rate' => 0.0005],
+ ['layers' => 'heads', 'epochs' => 10, 'learning_rate' => 0.0001],
+ ['layers' => 'all', 'epochs' => 10, 'learning_rate' => 0.0001],
+ ['layers' => 'all', 'epochs' => 10, 'learning_rate' => 0.00005],
+ ['layers' => 'all', 'epochs' => 10, 'learning_rate' => 0.00001],
+ ]);
+
return view('maia::index', compact(
'volume',
'jobs',
'hasJobsInProgress',
'hasJobsRunning',
- 'newestJobHasFailed'
+ 'newestJobHasFailed',
+ 'defaultTrainScheme'
));
}
diff --git a/src/public/assets/scripts/main.js b/src/public/assets/scripts/main.js
index deb4179..d8799cb 100644
--- a/src/public/assets/scripts/main.js
+++ b/src/public/assets/scripts/main.js
@@ -1 +1 @@
-!function(t){var e={};function n(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return t[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(i,o,function(e){return t[e]}.bind(null,o));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="/",n(n.s=0)}({0:function(t,e,n){n("WfG0"),t.exports=n("zcrr")},"A1R+":function(t,e,n){"use strict";t.exports=o,t.exports.default=o;var i=n("YcpW");function o(t,e){if(!(this instanceof o))return new o(t,e);this._maxEntries=Math.max(4,t||9),this._minEntries=Math.max(2,Math.ceil(.4*this._maxEntries)),e&&this._initFormat(e),this.clear()}function s(t,e,n){if(!n)return e.indexOf(t);for(var i=0;i=t.minX&&e.maxY>=t.minY}function g(t){return{children:t,height:1,leaf:!0,minX:1/0,minY:1/0,maxX:-1/0,maxY:-1/0}}function _(t,e,n,o,s){for(var r,a=[e,n];a.length;)(n=a.pop())-(e=a.pop())<=o||(r=e+Math.ceil((n-e)/o/2)*o,i(t,r,e,n,s),a.push(e,r,r,n))}o.prototype={all:function(){return this._all(this.data,[])},search:function(t){var e=this.data,n=[],i=this.toBBox;if(!f(t,e))return n;for(var o,s,r,a,u=[];e;){for(o=0,s=e.children.length;o=0&&s[e].children.length>this._maxEntries;)this._split(s,e),e--;this._adjustParentBBoxes(o,s,e)},_split:function(t,e){var n=t[e],i=n.children.length,o=this._minEntries;this._chooseSplitAxis(n,o,i);var s=this._chooseSplitIndex(n,o,i),a=g(n.children.splice(s,n.children.length-s));a.height=n.height,a.leaf=n.leaf,r(n,this.toBBox),r(a,this.toBBox),e?t[e-1].children.push(a):this._splitRoot(n,a)},_splitRoot:function(t,e){this.data=g([t,e]),this.data.height=t.height+1,this.data.leaf=!1,r(this.data,this.toBBox)},_chooseSplitIndex:function(t,e,n){var i,o,s,r,u,h,c,d,p,f,g,_,m,v;for(h=c=1/0,i=e;i<=n-e;i++)o=a(t,0,i,this.toBBox),s=a(t,i,n,this.toBBox),p=o,f=s,g=void 0,_=void 0,m=void 0,v=void 0,g=Math.max(p.minX,f.minX),_=Math.max(p.minY,f.minY),m=Math.min(p.maxX,f.maxX),v=Math.min(p.maxY,f.maxY),r=Math.max(0,m-g)*Math.max(0,v-_),u=l(o)+l(s),r=e;o--)s=t.children[o],u(c,t.leaf?r(s):s),l+=d(c);return l},_adjustParentBBoxes:function(t,e,n){for(var i=n;i>=0;i--)u(e[i],t)},_condense:function(t){for(var e,n=t.length-1;n>=0;n--)0===t[n].children.length?n>0?(e=t[n-1].children).splice(e.indexOf(t[n]),1):this.clear():r(t[n],this.toBBox)},_initFormat:function(t){var e=["return a"," - b",";"];this.compareMinX=new Function("a","b",e.join(t[0])),this.compareMinY=new Function("a","b",e.join(t[1])),this.toBBox=new Function("a","return {minX: a"+t[0]+", minY: a"+t[1]+", maxX: a"+t[2]+", maxY: a"+t[3]+"};")}}},WfG0:function(t,e,n){"use strict";n.r(e);var i=biigle.$require("annotations.components.annotationCanvas"),o=biigle.$require("largo.mixins.annotationPatch"),s=biigle.$require("annotations.ol.AttachLabelInteraction"),r=biigle.$require("messages").handleErrorResponse,a=biigle.$require("volumes.components.imageGrid"),u=biigle.$require("volumes.components.imageGridImage"),h=biigle.$require("annotations.stores.images"),c=biigle.$require("keyboard"),l=biigle.$require("labelTrees.components.labelTrees"),d=biigle.$require("labelTrees.components.labelTypeahead"),p=biigle.$require("core.mixins.loader"),f=biigle.$require("messages"),g=biigle.$require("core.components.sidebar"),_=biigle.$require("core.components.sidebarTab"),m=biigle.$require("annotations.stores.styles");function v(t,e,n,i,o,s,r,a){var u,h="function"==typeof t?t.options:t;if(e&&(h.render=e,h.staticRenderFns=n,h._compiled=!0),i&&(h.functional=!0),s&&(h._scopeId="data-v-"+s),r?(u=function(t){(t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),o&&o.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(r)},h._ssrRegister=u):o&&(u=a?function(){o.call(this,(h.functional?this.parent:this).$root.$options.shadowRoot)}:o),u)if(h.functional){h._injectStyles=u;var c=h.render;h.render=function(t,e){return u.call(e),c(t,e)}}else{var l=h.beforeCreate;h.beforeCreate=l?[].concat(l,u):[u]}return{exports:t,options:h}}var y=v({mixins:[p],components:{typeahead:d},data:function(){return{volumeId:null,useExistingAnnotations:!1,skipNoveltyDetection:!1,showAdvanced:!1,shouldFetchLabels:!1,labels:[],selectedLabels:[],submitted:!1}},computed:{canSkipNoveltyDetection:function(){return this.useExistingAnnotations&&this.hasLabels},hasLabels:function(){return this.labels.length>0},hasSelectedLabels:function(){return this.selectedLabels.length>0},showRestrictLabelsInput:function(){return this.showAdvanced&&this.useExistingAnnotations&&this.hasLabels},hasNoExistingAnnotations:function(){return this.useExistingAnnotations&&!this.hasLabels&&!this.loading}},methods:{toggle:function(){this.showAdvanced=!this.showAdvanced},setLabels:function(t){this.labels=t.body},handleSelectedLabel:function(t){-1===this.selectedLabels.indexOf(t)&&this.selectedLabels.push(t)},handleUnselectLabel:function(t){var e=this.selectedLabels.indexOf(t);e>=0&&this.selectedLabels.splice(e,1)},submit:function(){this.submitted=!0}},watch:{useExistingAnnotations:function(t){t&&(this.shouldFetchLabels=!0)},shouldFetchLabels:function(t){t&&(this.startLoading(),this.$http.get("api/v1/volumes{/id}/annotation-labels",{params:{id:this.volumeId}}).then(this.setLabels,r).finally(this.finishLoading))}},created:function(){this.volumeId=biigle.$require("maia.volumeId"),this.useExistingAnnotations=biigle.$require("maia.useExistingAnnotations"),this.skipNoveltyDetection=biigle.$require("maia.skipNoveltyDetection"),this.useExistingAnnotations&&(this.shouldFetchLabels=!0)}},void 0,void 0,!1,null,null,null).exports,I=Vue.resource("api/v1/maia/annotation-candidates{/id}"),x=v({mixins:[u,o],computed:{label:function(){return this.selected?this.image.label:null},selected:function(){return this.$parent.isSelected(this.image)},converted:function(){return this.$parent.isConverted(this.image)},classObject:function(){return{"image-grid__image--selected":this.selected||this.converted,"image-grid__image--selectable":this.selectable,"image-grid__image--fade":this.selectedFade,"image-grid__image--small-icon":this.smallIcon}},iconClass:function(){return this.converted?"fa-lock":"fa-"+this.selectedIcon},showIcon:function(){return this.selectable||this.selected||this.converted},title:function(){return this.converted?"This annotation candidate has been converted":this.selected?"Detach label":"Attach selected label"},labelStyle:function(){return{"background-color":"#"+this.label.color}},id:function(){return this.image.id},uuid:function(){return this.image.uuid},urlTemplate:function(){return biigle.$require("maia.acUrlTemplate")}}},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("figure",{staticClass:"image-grid__image image-grid__image--annotation-candidate",class:t.classObject,attrs:{title:t.title}},[t.showIcon?n("div",{staticClass:"image-icon"},[n("i",{staticClass:"fas",class:t.iconClass})]):t._e(),t._v(" "),n("img",{attrs:{src:t.srcUrl},on:{click:t.toggleSelect,error:t.showEmptyImage}}),t._v(" "),t.selected?n("div",{staticClass:"attached-label"},[n("span",{staticClass:"attached-label__color",style:t.labelStyle}),t._v(" "),n("span",{staticClass:"attached-label__name",domProps:{textContent:t._s(t.label.name)}})]):t._e()])}),[],!1,null,null,null),C=v({mixins:[a],components:{imageGridImage:x.exports},props:{selectedCandidateIds:{type:Object,required:!0},convertedCandidateIds:{type:Object,required:!0}},methods:{isSelected:function(t){return this.selectedCandidateIds.hasOwnProperty(t.id)},isConverted:function(t){return this.convertedCandidateIds.hasOwnProperty(t.id)}}},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"image-grid",on:{wheel:function(e){return e.preventDefault(),t.scroll(e)}}},[n("div",{ref:"images",staticClass:"image-grid__images"},t._l(t.displayedImages,(function(e){return n("image-grid-image",{key:e.id,attrs:{image:e,"empty-url":t.emptyUrl,selectable:!t.isConverted(e),"selected-icon":t.selectedIcon},on:{select:t.emitSelect}})})),1),t._v(" "),t.canScroll?n("image-grid-progress",{attrs:{progress:t.progress},on:{top:t.jumpToStart,"prev-page":t.reversePage,"prev-row":t.reverseRow,jump:t.jumpToPercent,"next-row":t.advanceRow,"next-page":t.advancePage,bottom:t.jumpToEnd}}):t._e()],1)}),[],!1,null,null,null).exports,b=Vue.resource("api/v1/maia-jobs{/id}",{},{save:{method:"POST",url:"api/v1/volumes{/id}/maia-jobs"},getTrainingProposals:{method:"GET",url:"api/v1/maia-jobs{/id}/training-proposals"},getTrainingProposalPoints:{method:"GET",url:"api/v1/maia-jobs{/jobId}/images{/imageId}/training-proposals"},getAnnotationCandidates:{method:"GET",url:"api/v1/maia-jobs{/id}/annotation-candidates"},getAnnotationCandidatePoints:{method:"GET",url:"api/v1/maia-jobs{/jobId}/images{/imageId}/annotation-candidates"},convertAnnotationCandidates:{method:"POST",url:"api/v1/maia-jobs{/id}/annotation-candidates"}}),P=Vue.resource("api/v1/maia/training-proposals{/id}"),S=v({mixins:[u,o],computed:{selected:function(){return this.$parent.selectedProposalIds.hasOwnProperty(this.image.id)},title:function(){return this.selectable?this.selected?"Unselect as interesting":"Select as interesting":""},id:function(){return this.image.id},uuid:function(){return this.image.uuid},urlTemplate:function(){return biigle.$require("maia.tpUrlTemplate")}}},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("figure",{staticClass:"image-grid__image",class:t.classObject,attrs:{title:t.title}},[t.showIcon?n("div",{staticClass:"image-icon"},[n("i",{staticClass:"fas",class:t.iconClass})]):t._e(),t._v(" "),n("img",{attrs:{src:t.srcUrl},on:{click:t.toggleSelect,error:t.showEmptyImage}})])}),[],!1,null,null,null),w=v({mixins:[a],components:{imageGridImage:S.exports},props:{selectedProposalIds:{type:Object,required:!0}}},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"image-grid",on:{wheel:function(e){return e.preventDefault(),t.scroll(e)}}},[n("div",{ref:"images",staticClass:"image-grid__images"},t._l(t.displayedImages,(function(e){return n("image-grid-image",{key:e.id,attrs:{image:e,"empty-url":t.emptyUrl,selectable:t.selectable,"selected-fade":t.selectable,"small-icon":!t.selectable,"selected-icon":t.selectedIcon},on:{select:t.emitSelect}})})),1),t._v(" "),t.canScroll?n("image-grid-progress",{attrs:{progress:t.progress},on:{top:t.jumpToStart,"prev-page":t.reversePage,"prev-row":t.reverseRow,jump:t.jumpToPercent,"next-row":t.advanceRow,"next-page":t.advancePage,bottom:t.jumpToEnd}}):t._e()],1)}),[],!1,null,null,null).exports;function A(){return function(){throw new Error("Unimplemented abstract method.")}()}var E=0;function F(t){return t.ol_uid||(t.ol_uid=String(++E))}var O=function(t){function e(e){var n="Assertion failed. See https://openlayers.org/en/"+("v"+"5.3.3".split("-")[0])+"/doc/errors/#"+e+" for details.";t.call(this,n),this.code=e,this.name="AssertionError",this.message=n}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e}(Error),T="add",M="remove",L="propertychange",k="function"==typeof Object.assign?Object.assign:function(t,e){var n=arguments;if(null==t)throw new TypeError("Cannot convert undefined or null to object");for(var i=Object(t),o=1,s=arguments.length;o0},e.prototype.removeEventListener=function(t,e){var n=this.listeners_[t];if(n){var i=n.indexOf(e);t in this.pendingRemovals_?(n[i]=N,++this.pendingRemovals_[t]):(n.splice(i,1),0===n.length&&delete this.listeners_[t])}},e}(U),Z="change",Q="clear";var tt=function(t){function e(){t.call(this),this.revision_=0}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.changed=function(){++this.revision_,this.dispatchEvent(Z)},e.prototype.getRevision=function(){return this.revision_},e.prototype.on=function(t,e){if(Array.isArray(t)){for(var n=t.length,i=new Array(n),o=0;o0;)this.pop()},e.prototype.extend=function(t){for(var e=0,n=t.length;ethis.highWaterMark},e.prototype.clear=function(){this.count_=0,this.entries_={},this.oldest_=null,this.newest_=null,this.dispatchEvent(Q)},e.prototype.containsKey=function(t){return this.entries_.hasOwnProperty(t)},e.prototype.forEach=function(t,e){for(var n=this.oldest_;n;)t.call(e,n.value_,n.key_,this),n=n.newer},e.prototype.get=function(t){var e=this.entries_[t];return _t(void 0!==e,15),e===this.newest_||(e===this.oldest_?(this.oldest_=this.oldest_.newer,this.oldest_.older=null):(e.newer.older=e.older,e.older.newer=e.newer),e.newer=null,e.older=this.newest_,this.newest_.newer=e,this.newest_=e),e.value_},e.prototype.remove=function(t){var e=this.entries_[t];return _t(void 0!==e,15),e===this.newest_?(this.newest_=e.older,this.newest_&&(this.newest_.newer=null)):e===this.oldest_?(this.oldest_=e.newer,this.oldest_&&(this.oldest_.older=null)):(e.newer.older=e.older,e.older.newer=e.newer),delete this.entries_[t],--this.count_,e.value_},e.prototype.getCount=function(){return this.count_},e.prototype.getKeys=function(){var t,e=new Array(this.count_),n=0;for(t=this.newest_;t;t=t.older)e[n++]=t.key_;return e},e.prototype.getValues=function(){var t,e=new Array(this.count_),n=0;for(t=this.newest_;t;t=t.older)e[n++]=t.value_;return e},e.prototype.peekLast=function(){return this.oldest_.value_},e.prototype.peekLastKey=function(){return this.oldest_.key_},e.prototype.peekFirstKey=function(){return this.newest_.key_},e.prototype.pop=function(){var t=this.oldest_;return delete this.entries_[t.key_],t.newer&&(t.newer.older=null),this.oldest_=t.newer,this.oldest_||(this.newest_=null),--this.count_,t.value_},e.prototype.replace=function(t,e){this.get(t),this.entries_[t].value_=e},e.prototype.set=function(t,e){_t(!(t in this.entries_),16);var n={key_:t,newer:null,older:this.newest_,value_:e};this.newest_?this.newest_.newer=n:this.oldest_=n,this.newest_=n,this.entries_[t]=n,++this.count_},e.prototype.setSize=function(t){this.highWaterMark=t},e.prototype.prune=function(){for(;this.canExpireCache();)this.pop()},e}(H);new Array(6);var jt=[0,0,0,1],Dt=[0,0,0,1],Xt=new Rt,Gt={},$t=null,Bt={};!function(){var t,e,n=Gt,i=["monospace","serif"],o=i.length,s="wmytzilWMYTZIL@#/&?$%10";function r(t){for(var n=Wt(),r=100;r<=700;r+=300){for(var a=r+" ",u=!0,h=0;h=200&&a.status<300){var o,s=e.getType();s==ae||s==ue?o=a.responseText:s==he?(o=a.responseXML)||(o=(new DOMParser).parseFromString(a.responseText,"application/xml")):s==re&&(o=a.response),o?n.call(this,e.readFeatures(o,{featureProjection:r}),e.readProjection(o),e.getLastExtent()):i.call(this)}else i.call(this)}.bind(this),a.onerror=function(){i.call(this)}.bind(this),a.send()}}(t,e,(function(t,e){"function"==typeof this.addFeatures&&this.addFeatures(t)}),N)}function le(t,e){return[[-1/0,-1/0,1/0,1/0]]}var de={DEGREES:"degrees",FEET:"ft",METERS:"m",PIXELS:"pixels",TILE_PIXELS:"tile-pixels",USFEET:"us-ft"},pe={};pe[de.DEGREES]=2*Math.PI*6370997/360,pe[de.FEET]=.3048,pe[de.METERS]=1,pe[de.USFEET]=1200/3937;var fe=de,ge=function(t){this.code_=t.code,this.units_=t.units,this.extent_=void 0!==t.extent?t.extent:null,this.worldExtent_=void 0!==t.worldExtent?t.worldExtent:null,this.axisOrientation_=void 0!==t.axisOrientation?t.axisOrientation:"enu",this.global_=void 0!==t.global&&t.global,this.canWrapX_=!(!this.global_||!this.extent_),this.getPointResolutionFunc_=t.getPointResolution,this.defaultTileGrid_=null,this.metersPerUnit_=t.metersPerUnit};ge.prototype.canWrapX=function(){return this.canWrapX_},ge.prototype.getCode=function(){return this.code_},ge.prototype.getExtent=function(){return this.extent_},ge.prototype.getUnits=function(){return this.units_},ge.prototype.getMetersPerUnit=function(){return this.metersPerUnit_||pe[this.units_]},ge.prototype.getWorldExtent=function(){return this.worldExtent_},ge.prototype.getAxisOrientation=function(){return this.axisOrientation_},ge.prototype.isGlobal=function(){return this.global_},ge.prototype.setGlobal=function(t){this.global_=t,this.canWrapX_=!(!t||!this.extent_)},ge.prototype.getDefaultTileGrid=function(){return this.defaultTileGrid_},ge.prototype.setDefaultTileGrid=function(t){this.defaultTileGrid_=t},ge.prototype.setExtent=function(t){this.extent_=t,this.canWrapX_=!(!this.global_||!t)},ge.prototype.setWorldExtent=function(t){this.worldExtent_=t},ge.prototype.setGetPointResolution=function(t){this.getPointResolutionFunc_=t},ge.prototype.getPointResolutionFunc=function(){return this.getPointResolutionFunc_};var _e=ge,me=6378137*Math.PI,ve=[-me,-me,me,me],ye=[-180,-85,180,85],Ie=function(t){function e(e){t.call(this,{code:e,units:fe.METERS,extent:ve,global:!0,worldExtent:ye,getPointResolution:function(t,e){return t/vt(e[1]/6378137)}})}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e}(_e),xe=[new Ie("EPSG:3857"),new Ie("EPSG:102100"),new Ie("EPSG:102113"),new Ie("EPSG:900913"),new Ie("urn:ogc:def:crs:EPSG:6.18:3:3857"),new Ie("urn:ogc:def:crs:EPSG::3857"),new Ie("http://www.opengis.net/gml/srs/epsg.xml#3857")];function Ce(t,e,n){var i=t.length,o=n>1?n:2,s=e;void 0===s&&(s=o>2?t.slice():new Array(i));for(var r=me,a=0;ar?u=r:u<-r&&(u=-r),s[a+1]=u}return s}function be(t,e,n){var i=t.length,o=n>1?n:2,s=e;void 0===s&&(s=o>2?t.slice():new Array(i));for(var r=0;r0}},methods:{handlePreviousImage:function(){this.$emit("previous-image")},handleNextImage:function(){this.$emit("next-image")},toggleSelectingMaiaAnnotation:function(){this.selectingMaiaAnnotation=!this.selectingMaiaAnnotation},createUnselectedAnnotationsLayer:function(){this.unselectedAnnotationFeatures=new at,this.unselectedAnnotationSource=new Ke({features:this.unselectedAnnotationFeatures}),this.unselectedAnnotationLayer=new ie({source:this.unselectedAnnotationSource,zIndex:99,updateWhileAnimating:!0,updateWhileInteracting:!0,style:m.editing,opacity:.5})},createSelectMaiaAnnotationInteraction:function(t){this.selectMaiaAnnotationInteraction=new s({map:this.map,features:t}),this.selectMaiaAnnotationInteraction.setActive(!1),this.selectMaiaAnnotationInteraction.on("attach",this.handleSelectMaiaAnnotation)},handleSelectMaiaAnnotation:function(t){this.$emit("select",t.feature.get("annotation"))},handleUnselectMaiaAnnotation:function(){!this.modifyInProgress&&this.selectedAnnotations.length>0&&this.$emit("unselect",this.selectedAnnotations[0])}},watch:{unselectedAnnotations:function(t){this.refreshAnnotationSource(t,this.unselectedAnnotationSource)},selectingMaiaAnnotation:function(t){this.selectMaiaAnnotationInteraction.setActive(t)}},created:function(){this.createUnselectedAnnotationsLayer(),this.map.addLayer(this.unselectedAnnotationLayer),this.selectInteraction.setActive(!1),this.canModify&&(this.createSelectMaiaAnnotationInteraction(this.unselectedAnnotationFeatures),this.map.addInteraction(this.selectMaiaAnnotationInteraction),c.on("Delete",this.handleUnselectMaiaAnnotation,0,this.listenerSet)),c.off("Shift+f",this.toggleMeasuring,this.listenerSet)},mounted:function(){c.off("m",this.toggleTranslating,this.listenerSet)}},void 0,void 0,!1,null,null,null).exports,He=v({mixins:[Je],props:{convertedAnnotations:{type:Array,default:function(){return[]}}},methods:{createConvertedAnnotationsLayer:function(){this.convertedAnnotationFeatures=new at,this.convertedAnnotationSource=new Ke({features:this.convertedAnnotationFeatures});var t=new ot;t.set("color","999999"),this.convertedAnnotationLayer=new ie({source:this.convertedAnnotationSource,zIndex:98,updateWhileAnimating:!0,updateWhileInteracting:!0,style:m.features(t)})}},watch:{convertedAnnotations:function(t){this.refreshAnnotationSource(t,this.convertedAnnotationSource)}},created:function(){this.createConvertedAnnotationsLayer(),this.map.addLayer(this.convertedAnnotationLayer)}},void 0,void 0,!1,null,null,null).exports,Ze=v({components:{labelTrees:l},props:{selectedCandidates:{type:Array,required:!0},labelTrees:{type:Array,required:!0}},computed:{hasNoSelectedCandidates:function(){return 0===this.selectedCandidates.length}},methods:{handleSelectedLabel:function(t){this.$emit("select",t)},handleDeselectedLabel:function(){this.$emit("select",null)},handleConvertCandidates:function(){this.hasNoSelectedCandidates||this.$emit("convert")}}},void 0,void 0,!1,null,null,null).exports,Qe=v({props:{selectedProposals:{type:Array,required:!0},seenProposals:{type:Array,required:!0}},computed:{numberSelectedProposals:function(){return this.selectedProposals.length},numberSeenProposals:function(){return this.seenProposals.length},hasNoSelectedProposals:function(){return 0===this.numberSelectedProposals},hasSeenAllSelectedProposals:function(){return this.numberSelectedProposals>0&&this.numberSelectedProposals===this.numberSeenProposals},textClass:function(){return this.hasSeenAllSelectedProposals?"text-success":""},buttonClass:function(){return this.hasSeenAllSelectedProposals?"btn-success":"btn-default"}}},void 0,void 0,!1,null,null,null).exports,tn=v({components:{labelTrees:l},props:{labelTrees:{type:Array,required:!0}},methods:{handleSelectedLabel:function(t){this.$emit("select",t)},handleDeselectedLabel:function(){this.$emit("select",null)},proceed:function(){this.$emit("proceed")}}},void 0,void 0,!1,null,null,null).exports,en=v({props:{proposals:{type:Array,required:!0},selectedProposals:{type:Array,required:!0}},computed:{selectedProposalsCount:function(){return this.selectedProposals.length},proposalsCount:function(){return this.proposals.length}},methods:{proceed:function(){this.$emit("proceed")}}},void 0,void 0,!1,null,null,null).exports,nn=[],on={},sn=[],rn={},an=v({mixins:[p],components:{sidebar:g,sidebarTab:_,selectProposalsTab:en,proposalsImageGrid:w,refineProposalsTab:Qe,refineCanvas:Je,refineCandidatesCanvas:He,selectCandidatesTab:tn,candidatesImageGrid:C,refineCandidatesTab:Ze},data:function(){return{job:null,states:null,labelTrees:[],visitedSelectProposalsTab:!1,visitedRefineProposalsTab:!1,visitedSelectCandidatesTab:!1,visitedRefineCandidatesTab:!1,openTab:"info",fetchProposalsPromise:null,hasProposals:!1,selectedProposalIds:{},seenProposalIds:{},lastSelectedProposal:null,currentProposalImage:null,currentProposalImageIndex:null,currentProposals:[],currentProposalsById:{},focussedProposal:null,proposalAnnotationCache:{},fetchCandidatesPromise:null,hasCandidates:!1,selectedCandidateIds:{},convertedCandidateIds:{},lastSelectedCandidate:null,currentCandidateImage:null,currentCandidateImageIndex:null,currentCandidates:[],currentCandidatesById:{},focussedCandidate:null,candidateAnnotationCache:{},selectedLabel:null,sequenceCounter:0}},computed:{infoTabOpen:function(){return"info"===this.openTab},selectProposalsTabOpen:function(){return"select-proposals"===this.openTab},refineProposalsTabOpen:function(){return"refine-proposals"===this.openTab},selectCandidatesTabOpen:function(){return"select-candidates"===this.openTab},refineCandidatesTabOpen:function(){return"refine-candidates"===this.openTab},isInTrainingProposalState:function(){return this.job.state_id===this.states["training-proposals"]},isInAnnotationCandidateState:function(){return this.job.state_id===this.states["annotation-candidates"]},proposals:function(){return this.hasProposals?nn:[]},selectedProposals:function(){var t=this.selectedProposalIds;return Object.keys(t).map((function(t){return on[t]})).sort((function(e,n){return e.image_id===n.image_id?t[e.id]-t[n.id]:e.image_id-n.image_id}))},proposalsForSelectView:function(){return this.isInTrainingProposalState?this.proposals:this.selectedProposals},hasSelectedProposals:function(){return this.selectedProposals.length>0},proposalImageIds:function(){var t={};return this.proposals.forEach((function(e){return t[e.image_id]=void 0})),Object.keys(t).map((function(t){return parseInt(t,10)}))},currentProposalImageId:function(){return this.proposalImageIds[this.currentProposalImageIndex]},nextProposalImageIndex:function(){return(this.currentProposalImageIndex+1)%this.proposalImageIds.length},nextProposalImageId:function(){return this.proposalImageIds[this.nextProposalImageIndex]},nextFocussedProposalImageId:function(){return this.nextFocussedProposal?this.nextFocussedProposal.image_id:this.nextProposalImageId},previousProposalImageIndex:function(){return(this.currentProposalImageIndex-1+this.proposalImageIds.length)%this.proposalImageIds.length},previousProposalImageId:function(){return this.proposalImageIds[this.previousProposalImageIndex]},hasCurrentProposalImage:function(){return null!==this.currentProposalImage},currentSelectedProposals:function(){var t=this;return this.currentProposals.filter((function(e){return t.selectedProposalIds.hasOwnProperty(e.id)}))},currentUnselectedProposals:function(){var t=this;return this.currentProposals.filter((function(e){return!t.selectedProposalIds.hasOwnProperty(e.id)}))},previousFocussedProposal:function(){var t=(this.selectedProposals.indexOf(this.focussedProposal)-1+this.selectedProposals.length)%this.selectedProposals.length;return this.selectedProposals[t]},nextFocussedProposal:function(){var t=(this.selectedProposals.indexOf(this.focussedProposal)+1)%this.selectedProposals.length;return this.selectedProposals[t]},focussedProposalToShow:function(){return this.refineProposalsTabOpen?this.focussedProposal:null},focussedProposalArray:function(){var t=this;return this.focussedProposalToShow?this.currentSelectedProposals.filter((function(e){return e.id===t.focussedProposalToShow.id})):[]},selectedAndSeenProposals:function(){var t=this;return this.selectedProposals.filter((function(e){return t.seenProposalIds.hasOwnProperty(e.id)}))},candidates:function(){return this.hasCandidates?sn:[]},selectedCandidates:function(){var t=this.selectedCandidateIds;return Object.keys(t).map((function(t){return rn[t]})).sort((function(e,n){return e.image_id===n.image_id?t[e.id]-t[n.id]:e.image_id-n.image_id}))},hasSelectedCandidates:function(){return this.selectedCandidates.length>0},candidateImageIds:function(){var t={};return this.candidates.forEach((function(e){return t[e.image_id]=void 0})),Object.keys(t).map((function(t){return parseInt(t,10)}))},currentCandidateImageId:function(){return this.candidateImageIds[this.currentCandidateImageIndex]},nextCandidateImageIndex:function(){return(this.currentCandidateImageIndex+1)%this.candidateImageIds.length},nextCandidateImageId:function(){return this.candidateImageIds[this.nextCandidateImageIndex]},previousCandidateImageIndex:function(){return(this.currentCandidateImageIndex-1+this.candidateImageIds.length)%this.candidateImageIds.length},previousCandidateImageId:function(){return this.candidateImageIds[this.previousCandidateImageIndex]},hasCurrentCandidateImage:function(){return null!==this.currentCandidateImage},currentSelectedCandidates:function(){var t=this;return this.currentCandidates.filter((function(e){return t.selectedCandidateIds.hasOwnProperty(e.id)}))},currentUnselectedCandidates:function(){var t=this;return this.currentCandidates.filter((function(e){return!t.selectedCandidateIds.hasOwnProperty(e.id)&&!t.convertedCandidateIds.hasOwnProperty(e.id)}))},currentConvertedCandidates:function(){var t=this;return this.currentCandidates.filter((function(e){return t.convertedCandidateIds.hasOwnProperty(e.id)}))},previousFocussedCandidate:function(){var t=(this.selectedCandidates.indexOf(this.focussedCandidate)-1+this.selectedCandidates.length)%this.selectedCandidates.length;return this.selectedCandidates[t]},nextFocussedCandidate:function(){var t=(this.selectedCandidates.indexOf(this.focussedCandidate)+1)%this.selectedCandidates.length;return this.selectedCandidates[t]},nextFocussedCandidateImageId:function(){return this.nextFocussedCandidate?this.nextFocussedCandidate.image_id:this.nextCandidateImageId},focussedCandidateToShow:function(){return this.refineCandidatesTabOpen?this.focussedCandidate:null},focussedCandidateArray:function(){var t=this;return this.focussedCandidateToShow?this.currentSelectedCandidates.filter((function(e){return e.id===t.focussedCandidateToShow.id})):[]}},methods:{handleSidebarToggle:function(){var t=this;this.$nextTick((function(){t.$refs.proposalsImageGrid&&t.$refs.proposalsImageGrid.$emit("resize"),t.$refs.candidatesImageGrid&&t.$refs.candidatesImageGrid.$emit("resize")}))},handleTabOpened:function(t){this.openTab=t},setProposals:function(t){var e=this;(nn=t.body).forEach((function(t){on[t.id]=t,e.setSelectedProposalId(t)})),this.hasProposals=nn.length>0},fetchProposals:function(){return this.fetchProposalsPromise||(this.startLoading(),this.fetchProposalsPromise=b.getTrainingProposals({id:this.job.id}),this.fetchProposalsPromise.then(this.setProposals,r).finally(this.finishLoading)),this.fetchProposalsPromise},openRefineProposalsTab:function(){this.openTab="refine-proposals"},openRefineCandidatesTab:function(){this.openTab="refine-candidates"},updateSelectProposal:function(t,e){var n=this;t.selected=e,this.setSelectedProposalId(t);var i=P.update({id:t.id},{selected:e});return i.catch((function(i){r(i),t.selected=!e,n.setSelectedProposalId(t)})),i},setSelectedProposalId:function(t){t.selected?Vue.set(this.selectedProposalIds,t.id,this.getSequenceId()):Vue.delete(this.selectedProposalIds,t.id)},setSeenProposalId:function(t){Vue.set(this.seenProposalIds,t.id,!0)},fetchProposalAnnotations:function(t){return this.proposalAnnotationCache.hasOwnProperty(t)||(this.proposalAnnotationCache[t]=b.getTrainingProposalPoints({jobId:this.job.id,imageId:t}).then(this.parseAnnotations)),this.proposalAnnotationCache[t]},parseAnnotations:function(t){return Object.keys(t.body).map((function(e){return{id:parseInt(e,10),shape:"Circle",points:t.body[e]}}))},setCurrentProposalImageAndAnnotations:function(t){var e=this;this.currentProposalImage=t[0],this.currentProposals=t[1],this.currentProposalsById={},this.currentProposals.forEach((function(t){e.currentProposalsById[t.id]=t}))},cacheNextProposalImage:function(){this.currentProposalImageId!==this.nextFocussedProposalImageId&&h.fetchImage(this.nextFocussedProposalImageId).catch((function(){}))},cacheNextProposalAnnotations:function(){this.currentProposalImageId!==this.nextFocussedProposalImageId&&this.fetchProposalAnnotations(this.nextFocussedProposalImageId).catch((function(){}))},handlePreviousProposalImage:function(){this.currentProposalImageIndex=this.previousProposalImageIndex},handlePreviousProposal:function(){this.previousFocussedProposal?this.focussedProposal=this.previousFocussedProposal:this.handlePreviousProposalImage()},handleNextProposalImage:function(){this.currentProposalImageIndex=this.nextProposalImageIndex},handleNextProposal:function(){this.nextFocussedProposal?this.focussedProposal=this.nextFocussedProposal:this.handleNextProposalImage()},handleRefineProposal:function(t){Vue.Promise.all(t.map(this.updateProposalPoints)).catch(r)},updateProposalPoints:function(t){var e=this.currentProposalsById[t.id];return P.update({id:t.id},{points:t.points}).then((function(){e.points=t.points}))},focusProposalToShow:function(){if(this.focussedProposalToShow){var t=this.currentProposalsById[this.focussedProposalToShow.id];t&&this.$refs.refineProposalsCanvas.focusAnnotation(t,!0,!1)}},handleSelectedProposal:function(t,e){t.selected?this.unselectProposal(t):e.shiftKey&&this.lastSelectedProposal?this.doForEachBetween(this.proposals,t,this.lastSelectedProposal,this.selectProposal):(this.lastSelectedProposal=t,this.selectProposal(t))},selectProposal:function(t){this.updateSelectProposal(t,!0).then(this.maybeInitFocussedProposal)},unselectProposal:function(t){var e=this.nextFocussedProposal;this.updateSelectProposal(t,!1).bind(this).then((function(){this.maybeUnsetFocussedProposal(t,e)}))},maybeInitFocussedProposal:function(){!this.focussedProposal&&this.hasSelectedProposals&&(this.focussedProposal=this.selectedProposals[0])},maybeUnsetFocussedProposal:function(t,e){this.focussedProposal&&this.focussedProposal.id===t.id&&(e&&e.id!==t.id?this.focussedProposal=e:this.focussedProposal=null)},maybeInitCurrentProposalImage:function(){null===this.currentProposalImageIndex&&(this.currentProposalImageIndex=0)},maybeInitCurrentCandidateImage:function(){null===this.currentCandidateImageIndex&&(this.currentCandidateImageIndex=0)},handleLoadingError:function(t){f.danger(t)},setSelectedCandidateId:function(t){t.label&&!t.annotation_id?Vue.set(this.selectedCandidateIds,t.id,this.getSequenceId()):Vue.delete(this.selectedCandidateIds,t.id)},setConvertedCandidateId:function(t){t.annotation_id?Vue.set(this.convertedCandidateIds,t.id,t.annotation_id):Vue.delete(this.convertedCandidateIds,t.id)},setCandidates:function(t){var e=this;(sn=t.body).forEach((function(t){rn[t.id]=t,e.setSelectedCandidateId(t),e.setConvertedCandidateId(t)})),this.hasCandidates=sn.length>0},fetchCandidates:function(){return this.fetchCandidatesPromise||(this.startLoading(),this.fetchCandidatesPromise=b.getAnnotationCandidates({id:this.job.id}),this.fetchCandidatesPromise.then(this.setCandidates,r).finally(this.finishLoading)),this.fetchCandidatesPromise},handleSelectedCandidate:function(t,e){t.label?this.unselectCandidate(t):e.shiftKey&&this.lastSelectedCandidate&&this.selectedLabel?this.doForEachBetween(this.candidates,t,this.lastSelectedCandidate,this.selectCandidate):(this.lastSelectedCandidate=t,this.selectCandidate(t))},selectCandidate:function(t){this.selectedLabel?t.annotation_id||this.updateSelectCandidate(t,this.selectedLabel).then(this.maybeInitFocussedCandidate):f.info("Please select a label first.")},unselectCandidate:function(t){var e=this.nextFocussedCandidate;this.updateSelectCandidate(t,null).bind(this).then((function(){this.maybeUnsetFocussedCandidate(t,e)}))},updateSelectCandidate:function(t,e){var n=this,i=t.label;t.label=e,this.setSelectedCandidateId(t);var o=e?e.id:null,s=I.update({id:t.id},{label_id:o});return s.catch((function(e){r(e),t.label=i,n.setSelectedCandidateId(t)})),s},fetchCandidateAnnotations:function(t){return this.candidateAnnotationCache.hasOwnProperty(t)||(this.candidateAnnotationCache[t]=b.getAnnotationCandidatePoints({jobId:this.job.id,imageId:t}).then(this.parseAnnotations)),this.candidateAnnotationCache[t]},setCurrentCandidateImageAndAnnotations:function(t){var e=this;this.currentCandidateImage=t[0],this.currentCandidates=t[1],this.currentCandidatesById={},this.currentCandidates.forEach((function(t){return e.currentCandidatesById[t.id]=t}))},handlePreviousCandidateImage:function(){this.currentCandidateImageIndex=this.previousCandidateImageIndex},handlePreviousCandidate:function(){this.previousFocussedCandidate?this.focussedCandidate=this.previousFocussedCandidate:this.handlePreviousCandidateImage()},handleNextCandidateImage:function(){this.currentCandidateImageIndex=this.nextCandidateImageIndex},handleNextCandidate:function(){this.nextFocussedCandidate?this.focussedCandidate=this.nextFocussedCandidate:this.handleNextCandidateImage()},focusCandidateToShow:function(){if(this.focussedCandidateToShow){var t=this.currentCandidatesById[this.focussedCandidateToShow.id];t&&this.$refs.refineCandidatesCanvas.focusAnnotation(t,!0,!1)}},maybeInitFocussedCandidate:function(){!this.focussedCandidate&&this.hasSelectedCandidates&&(this.focussedCandidate=this.selectedCandidates[0])},maybeUnsetFocussedCandidate:function(t,e){this.focussedCandidate&&this.focussedCandidate.id===t.id&&(e&&e.id!==t.id?this.focussedCandidate=e:this.focussedCandidate=null)},handleSelectedLabel:function(t){this.selectedLabel=t},doForEachBetween:function(t,e,n,i){var o=t.indexOf(e),s=t.indexOf(n);if(se?1:0}return function(n,i,o,s,r){!function e(n,i,o,s,r){for(;s>o;){if(s-o>600){var a=s-o+1,u=i-o+1,h=Math.log(a),c=.5*Math.exp(2*h/3),l=.5*Math.sqrt(h*c*(a-c)/a)*(u-a/2<0?-1:1),d=Math.max(o,Math.floor(i-u*c/a+l)),p=Math.min(s,Math.floor(i+(a-u)*c/a+l));e(n,i,d,p,r)}var f=n[i],g=o,_=s;for(t(n,o,i),r(n[s],f)>0&&t(n,o,s);g<_;){for(t(n,g,_),g++,_--;r(n[g],f)<0;)g++;for(;r(n[_],f)>0;)_--}0===r(n[o],f)?t(n,o,_):(_++,t(n,_,s)),_<=i&&(o=_+1),i<=_&&(s=_-1)}}(n,i,o||0,s||n.length-1,r||e)}}()},zcrr:function(t,e){}});
\ No newline at end of file
+!function(t){var e={};function n(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return t[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(i,o,function(e){return t[e]}.bind(null,o));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="/",n(n.s=0)}({0:function(t,e,n){n("WfG0"),t.exports=n("zcrr")},"A1R+":function(t,e,n){"use strict";t.exports=o,t.exports.default=o;var i=n("YcpW");function o(t,e){if(!(this instanceof o))return new o(t,e);this._maxEntries=Math.max(4,t||9),this._minEntries=Math.max(2,Math.ceil(.4*this._maxEntries)),e&&this._initFormat(e),this.clear()}function s(t,e,n){if(!n)return e.indexOf(t);for(var i=0;i=t.minX&&e.maxY>=t.minY}function g(t){return{children:t,height:1,leaf:!0,minX:1/0,minY:1/0,maxX:-1/0,maxY:-1/0}}function _(t,e,n,o,s){for(var r,a=[e,n];a.length;)(n=a.pop())-(e=a.pop())<=o||(r=e+Math.ceil((n-e)/o/2)*o,i(t,r,e,n,s),a.push(e,r,r,n))}o.prototype={all:function(){return this._all(this.data,[])},search:function(t){var e=this.data,n=[],i=this.toBBox;if(!f(t,e))return n;for(var o,s,r,a,h=[];e;){for(o=0,s=e.children.length;o=0&&s[e].children.length>this._maxEntries;)this._split(s,e),e--;this._adjustParentBBoxes(o,s,e)},_split:function(t,e){var n=t[e],i=n.children.length,o=this._minEntries;this._chooseSplitAxis(n,o,i);var s=this._chooseSplitIndex(n,o,i),a=g(n.children.splice(s,n.children.length-s));a.height=n.height,a.leaf=n.leaf,r(n,this.toBBox),r(a,this.toBBox),e?t[e-1].children.push(a):this._splitRoot(n,a)},_splitRoot:function(t,e){this.data=g([t,e]),this.data.height=t.height+1,this.data.leaf=!1,r(this.data,this.toBBox)},_chooseSplitIndex:function(t,e,n){var i,o,s,r,h,u,c,d,p,f,g,_,m,v;for(u=c=1/0,i=e;i<=n-e;i++)o=a(t,0,i,this.toBBox),s=a(t,i,n,this.toBBox),p=o,f=s,g=void 0,_=void 0,m=void 0,v=void 0,g=Math.max(p.minX,f.minX),_=Math.max(p.minY,f.minY),m=Math.min(p.maxX,f.maxX),v=Math.min(p.maxY,f.maxY),r=Math.max(0,m-g)*Math.max(0,v-_),h=l(o)+l(s),r=e;o--)s=t.children[o],h(c,t.leaf?r(s):s),l+=d(c);return l},_adjustParentBBoxes:function(t,e,n){for(var i=n;i>=0;i--)h(e[i],t)},_condense:function(t){for(var e,n=t.length-1;n>=0;n--)0===t[n].children.length?n>0?(e=t[n-1].children).splice(e.indexOf(t[n]),1):this.clear():r(t[n],this.toBBox)},_initFormat:function(t){var e=["return a"," - b",";"];this.compareMinX=new Function("a","b",e.join(t[0])),this.compareMinY=new Function("a","b",e.join(t[1])),this.toBBox=new Function("a","return {minX: a"+t[0]+", minY: a"+t[1]+", maxX: a"+t[2]+", maxY: a"+t[3]+"};")}}},WfG0:function(t,e,n){"use strict";n.r(e);var i=biigle.$require("annotations.components.annotationCanvas"),o=biigle.$require("largo.mixins.annotationPatch"),s=biigle.$require("annotations.ol.AttachLabelInteraction"),r=biigle.$require("messages").handleErrorResponse,a=biigle.$require("volumes.components.imageGrid"),h=biigle.$require("volumes.components.imageGridImage"),u=biigle.$require("annotations.stores.images"),c=biigle.$require("keyboard"),l=biigle.$require("labelTrees.components.labelTrees"),d=biigle.$require("labelTrees.components.labelTypeahead"),p=biigle.$require("core.mixins.loader"),f=biigle.$require("messages"),g=biigle.$require("core.components.sidebar"),_=biigle.$require("core.components.sidebarTab"),m=biigle.$require("annotations.stores.styles");function v(t,e,n,i,o,s,r,a){var h,u="function"==typeof t?t.options:t;if(e&&(u.render=e,u.staticRenderFns=n,u._compiled=!0),i&&(u.functional=!0),s&&(u._scopeId="data-v-"+s),r?(h=function(t){(t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),o&&o.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(r)},u._ssrRegister=h):o&&(h=a?function(){o.call(this,(u.functional?this.parent:this).$root.$options.shadowRoot)}:o),h)if(u.functional){u._injectStyles=h;var c=u.render;u.render=function(t,e){return h.call(e),c(t,e)}}else{var l=u.beforeCreate;u.beforeCreate=l?[].concat(l,h):[h]}return{exports:t,options:u}}var y=v({mixins:[p],components:{typeahead:d},data:function(){return{volumeId:null,useExistingAnnotations:!1,skipNoveltyDetection:!1,showAdvanced:!1,shouldFetchLabels:!1,labels:[],selectedLabels:[],submitted:!1,trainScheme:[]}},computed:{canSkipNoveltyDetection:function(){return this.useExistingAnnotations&&this.hasLabels},hasLabels:function(){return this.labels.length>0},hasSelectedLabels:function(){return this.selectedLabels.length>0},showRestrictLabelsInput:function(){return this.showAdvanced&&this.useExistingAnnotations&&this.hasLabels},hasNoExistingAnnotations:function(){return this.useExistingAnnotations&&!this.hasLabels&&!this.loading}},methods:{toggle:function(){this.showAdvanced=!this.showAdvanced},setLabels:function(t){this.labels=t.body},handleSelectedLabel:function(t){-1===this.selectedLabels.indexOf(t)&&this.selectedLabels.push(t)},handleUnselectLabel:function(t){var e=this.selectedLabels.indexOf(t);e>=0&&this.selectedLabels.splice(e,1)},submit:function(){this.submitted=!0},removeTrainStep:function(t){this.trainScheme.splice(t,1)},addTrainStep:function(){var t={layers:"heads",epochs:10,learning_rate:.001};if(this.trainScheme.length>0){var e=this.trainScheme[this.trainScheme.length-1];t.layers=e.layers,t.epochs=e.epochs,t.learning_rate=e.learning_rate}this.trainScheme.push(t)}},watch:{useExistingAnnotations:function(t){t&&(this.shouldFetchLabels=!0)},shouldFetchLabels:function(t){t&&(this.startLoading(),this.$http.get("api/v1/volumes{/id}/annotation-labels",{params:{id:this.volumeId}}).then(this.setLabels,r).finally(this.finishLoading))}},created:function(){this.volumeId=biigle.$require("maia.volumeId"),this.useExistingAnnotations=biigle.$require("maia.useExistingAnnotations"),this.skipNoveltyDetection=biigle.$require("maia.skipNoveltyDetection"),this.trainScheme=biigle.$require("maia.trainScheme"),this.showAdvanced=biigle.$require("maia.hasErrors"),this.useExistingAnnotations&&(this.shouldFetchLabels=!0)}},void 0,void 0,!1,null,null,null).exports,I=Vue.resource("api/v1/maia/annotation-candidates{/id}"),x=v({mixins:[h,o],computed:{label:function(){return this.selected?this.image.label:null},selected:function(){return this.$parent.isSelected(this.image)},converted:function(){return this.$parent.isConverted(this.image)},classObject:function(){return{"image-grid__image--selected":this.selected||this.converted,"image-grid__image--selectable":this.selectable,"image-grid__image--fade":this.selectedFade,"image-grid__image--small-icon":this.smallIcon}},iconClass:function(){return this.converted?"fa-lock":"fa-"+this.selectedIcon},showIcon:function(){return this.selectable||this.selected||this.converted},title:function(){return this.converted?"This annotation candidate has been converted":this.selected?"Detach label":"Attach selected label"},labelStyle:function(){return{"background-color":"#"+this.label.color}},id:function(){return this.image.id},uuid:function(){return this.image.uuid},urlTemplate:function(){return biigle.$require("maia.acUrlTemplate")}}},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("figure",{staticClass:"image-grid__image image-grid__image--annotation-candidate",class:t.classObject,attrs:{title:t.title}},[t.showIcon?n("div",{staticClass:"image-icon"},[n("i",{staticClass:"fas",class:t.iconClass})]):t._e(),t._v(" "),n("img",{attrs:{src:t.srcUrl},on:{click:t.toggleSelect,error:t.showEmptyImage}}),t._v(" "),t.selected?n("div",{staticClass:"attached-label"},[n("span",{staticClass:"attached-label__color",style:t.labelStyle}),t._v(" "),n("span",{staticClass:"attached-label__name",domProps:{textContent:t._s(t.label.name)}})]):t._e()])}),[],!1,null,null,null),C=v({mixins:[a],components:{imageGridImage:x.exports},props:{selectedCandidateIds:{type:Object,required:!0},convertedCandidateIds:{type:Object,required:!0}},methods:{isSelected:function(t){return this.selectedCandidateIds.hasOwnProperty(t.id)},isConverted:function(t){return this.convertedCandidateIds.hasOwnProperty(t.id)}}},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"image-grid",on:{wheel:function(e){return e.preventDefault(),t.scroll(e)}}},[n("div",{ref:"images",staticClass:"image-grid__images"},t._l(t.displayedImages,(function(e){return n("image-grid-image",{key:e.id,attrs:{image:e,"empty-url":t.emptyUrl,selectable:!t.isConverted(e),"selected-icon":t.selectedIcon},on:{select:t.emitSelect}})})),1),t._v(" "),t.canScroll?n("image-grid-progress",{attrs:{progress:t.progress},on:{top:t.jumpToStart,"prev-page":t.reversePage,"prev-row":t.reverseRow,jump:t.jumpToPercent,"next-row":t.advanceRow,"next-page":t.advancePage,bottom:t.jumpToEnd}}):t._e()],1)}),[],!1,null,null,null).exports,b=Vue.resource("api/v1/maia-jobs{/id}",{},{save:{method:"POST",url:"api/v1/volumes{/id}/maia-jobs"},getTrainingProposals:{method:"GET",url:"api/v1/maia-jobs{/id}/training-proposals"},getTrainingProposalPoints:{method:"GET",url:"api/v1/maia-jobs{/jobId}/images{/imageId}/training-proposals"},getAnnotationCandidates:{method:"GET",url:"api/v1/maia-jobs{/id}/annotation-candidates"},getAnnotationCandidatePoints:{method:"GET",url:"api/v1/maia-jobs{/jobId}/images{/imageId}/annotation-candidates"},convertAnnotationCandidates:{method:"POST",url:"api/v1/maia-jobs{/id}/annotation-candidates"}}),P=Vue.resource("api/v1/maia/training-proposals{/id}"),S=v({mixins:[h,o],computed:{selected:function(){return this.$parent.selectedProposalIds.hasOwnProperty(this.image.id)},title:function(){return this.selectable?this.selected?"Unselect as interesting":"Select as interesting":""},id:function(){return this.image.id},uuid:function(){return this.image.uuid},urlTemplate:function(){return biigle.$require("maia.tpUrlTemplate")}}},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("figure",{staticClass:"image-grid__image",class:t.classObject,attrs:{title:t.title}},[t.showIcon?n("div",{staticClass:"image-icon"},[n("i",{staticClass:"fas",class:t.iconClass})]):t._e(),t._v(" "),n("img",{attrs:{src:t.srcUrl},on:{click:t.toggleSelect,error:t.showEmptyImage}})])}),[],!1,null,null,null),w=v({mixins:[a],components:{imageGridImage:S.exports},props:{selectedProposalIds:{type:Object,required:!0}}},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"image-grid",on:{wheel:function(e){return e.preventDefault(),t.scroll(e)}}},[n("div",{ref:"images",staticClass:"image-grid__images"},t._l(t.displayedImages,(function(e){return n("image-grid-image",{key:e.id,attrs:{image:e,"empty-url":t.emptyUrl,selectable:t.selectable,"selected-fade":t.selectable,"small-icon":!t.selectable,"selected-icon":t.selectedIcon},on:{select:t.emitSelect}})})),1),t._v(" "),t.canScroll?n("image-grid-progress",{attrs:{progress:t.progress},on:{top:t.jumpToStart,"prev-page":t.reversePage,"prev-row":t.reverseRow,jump:t.jumpToPercent,"next-row":t.advanceRow,"next-page":t.advancePage,bottom:t.jumpToEnd}}):t._e()],1)}),[],!1,null,null,null).exports;function A(){return function(){throw new Error("Unimplemented abstract method.")}()}var E=0;function F(t){return t.ol_uid||(t.ol_uid=String(++E))}var T=function(t){function e(e){var n="Assertion failed. See https://openlayers.org/en/"+("v"+"5.3.3".split("-")[0])+"/doc/errors/#"+e+" for details.";t.call(this,n),this.code=e,this.name="AssertionError",this.message=n}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e}(Error),O="add",M="remove",L="propertychange",k="function"==typeof Object.assign?Object.assign:function(t,e){var n=arguments;if(null==t)throw new TypeError("Cannot convert undefined or null to object");for(var i=Object(t),o=1,s=arguments.length;o0},e.prototype.removeEventListener=function(t,e){var n=this.listeners_[t];if(n){var i=n.indexOf(e);t in this.pendingRemovals_?(n[i]=N,++this.pendingRemovals_[t]):(n.splice(i,1),0===n.length&&delete this.listeners_[t])}},e}(U),Z="change",Q="clear";var tt=function(t){function e(){t.call(this),this.revision_=0}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.changed=function(){++this.revision_,this.dispatchEvent(Z)},e.prototype.getRevision=function(){return this.revision_},e.prototype.on=function(t,e){if(Array.isArray(t)){for(var n=t.length,i=new Array(n),o=0;o0;)this.pop()},e.prototype.extend=function(t){for(var e=0,n=t.length;ethis.highWaterMark},e.prototype.clear=function(){this.count_=0,this.entries_={},this.oldest_=null,this.newest_=null,this.dispatchEvent(Q)},e.prototype.containsKey=function(t){return this.entries_.hasOwnProperty(t)},e.prototype.forEach=function(t,e){for(var n=this.oldest_;n;)t.call(e,n.value_,n.key_,this),n=n.newer},e.prototype.get=function(t){var e=this.entries_[t];return _t(void 0!==e,15),e===this.newest_||(e===this.oldest_?(this.oldest_=this.oldest_.newer,this.oldest_.older=null):(e.newer.older=e.older,e.older.newer=e.newer),e.newer=null,e.older=this.newest_,this.newest_.newer=e,this.newest_=e),e.value_},e.prototype.remove=function(t){var e=this.entries_[t];return _t(void 0!==e,15),e===this.newest_?(this.newest_=e.older,this.newest_&&(this.newest_.newer=null)):e===this.oldest_?(this.oldest_=e.newer,this.oldest_&&(this.oldest_.older=null)):(e.newer.older=e.older,e.older.newer=e.newer),delete this.entries_[t],--this.count_,e.value_},e.prototype.getCount=function(){return this.count_},e.prototype.getKeys=function(){var t,e=new Array(this.count_),n=0;for(t=this.newest_;t;t=t.older)e[n++]=t.key_;return e},e.prototype.getValues=function(){var t,e=new Array(this.count_),n=0;for(t=this.newest_;t;t=t.older)e[n++]=t.value_;return e},e.prototype.peekLast=function(){return this.oldest_.value_},e.prototype.peekLastKey=function(){return this.oldest_.key_},e.prototype.peekFirstKey=function(){return this.newest_.key_},e.prototype.pop=function(){var t=this.oldest_;return delete this.entries_[t.key_],t.newer&&(t.newer.older=null),this.oldest_=t.newer,this.oldest_||(this.newest_=null),--this.count_,t.value_},e.prototype.replace=function(t,e){this.get(t),this.entries_[t].value_=e},e.prototype.set=function(t,e){_t(!(t in this.entries_),16);var n={key_:t,newer:null,older:this.newest_,value_:e};this.newest_?this.newest_.newer=n:this.oldest_=n,this.newest_=n,this.entries_[t]=n,++this.count_},e.prototype.setSize=function(t){this.highWaterMark=t},e.prototype.prune=function(){for(;this.canExpireCache();)this.pop()},e}(H);new Array(6);var jt=[0,0,0,1],Dt=[0,0,0,1],Xt=new Rt,Gt={},$t=null,qt={};!function(){var t,e,n=Gt,i=["monospace","serif"],o=i.length,s="wmytzilWMYTZIL@#/&?$%10";function r(t){for(var n=Bt(),r=100;r<=700;r+=300){for(var a=r+" ",h=!0,u=0;u=200&&a.status<300){var o,s=e.getType();s==ae||s==he?o=a.responseText:s==ue?(o=a.responseXML)||(o=(new DOMParser).parseFromString(a.responseText,"application/xml")):s==re&&(o=a.response),o?n.call(this,e.readFeatures(o,{featureProjection:r}),e.readProjection(o),e.getLastExtent()):i.call(this)}else i.call(this)}.bind(this),a.onerror=function(){i.call(this)}.bind(this),a.send()}}(t,e,(function(t,e){"function"==typeof this.addFeatures&&this.addFeatures(t)}),N)}function le(t,e){return[[-1/0,-1/0,1/0,1/0]]}var de={DEGREES:"degrees",FEET:"ft",METERS:"m",PIXELS:"pixels",TILE_PIXELS:"tile-pixels",USFEET:"us-ft"},pe={};pe[de.DEGREES]=2*Math.PI*6370997/360,pe[de.FEET]=.3048,pe[de.METERS]=1,pe[de.USFEET]=1200/3937;var fe=de,ge=function(t){this.code_=t.code,this.units_=t.units,this.extent_=void 0!==t.extent?t.extent:null,this.worldExtent_=void 0!==t.worldExtent?t.worldExtent:null,this.axisOrientation_=void 0!==t.axisOrientation?t.axisOrientation:"enu",this.global_=void 0!==t.global&&t.global,this.canWrapX_=!(!this.global_||!this.extent_),this.getPointResolutionFunc_=t.getPointResolution,this.defaultTileGrid_=null,this.metersPerUnit_=t.metersPerUnit};ge.prototype.canWrapX=function(){return this.canWrapX_},ge.prototype.getCode=function(){return this.code_},ge.prototype.getExtent=function(){return this.extent_},ge.prototype.getUnits=function(){return this.units_},ge.prototype.getMetersPerUnit=function(){return this.metersPerUnit_||pe[this.units_]},ge.prototype.getWorldExtent=function(){return this.worldExtent_},ge.prototype.getAxisOrientation=function(){return this.axisOrientation_},ge.prototype.isGlobal=function(){return this.global_},ge.prototype.setGlobal=function(t){this.global_=t,this.canWrapX_=!(!t||!this.extent_)},ge.prototype.getDefaultTileGrid=function(){return this.defaultTileGrid_},ge.prototype.setDefaultTileGrid=function(t){this.defaultTileGrid_=t},ge.prototype.setExtent=function(t){this.extent_=t,this.canWrapX_=!(!this.global_||!t)},ge.prototype.setWorldExtent=function(t){this.worldExtent_=t},ge.prototype.setGetPointResolution=function(t){this.getPointResolutionFunc_=t},ge.prototype.getPointResolutionFunc=function(){return this.getPointResolutionFunc_};var _e=ge,me=6378137*Math.PI,ve=[-me,-me,me,me],ye=[-180,-85,180,85],Ie=function(t){function e(e){t.call(this,{code:e,units:fe.METERS,extent:ve,global:!0,worldExtent:ye,getPointResolution:function(t,e){return t/vt(e[1]/6378137)}})}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e}(_e),xe=[new Ie("EPSG:3857"),new Ie("EPSG:102100"),new Ie("EPSG:102113"),new Ie("EPSG:900913"),new Ie("urn:ogc:def:crs:EPSG:6.18:3:3857"),new Ie("urn:ogc:def:crs:EPSG::3857"),new Ie("http://www.opengis.net/gml/srs/epsg.xml#3857")];function Ce(t,e,n){var i=t.length,o=n>1?n:2,s=e;void 0===s&&(s=o>2?t.slice():new Array(i));for(var r=me,a=0;ar?h=r:h<-r&&(h=-r),s[a+1]=h}return s}function be(t,e,n){var i=t.length,o=n>1?n:2,s=e;void 0===s&&(s=o>2?t.slice():new Array(i));for(var r=0;r0}},methods:{handlePreviousImage:function(){this.$emit("previous-image")},handleNextImage:function(){this.$emit("next-image")},toggleSelectingMaiaAnnotation:function(){this.selectingMaiaAnnotation=!this.selectingMaiaAnnotation},createUnselectedAnnotationsLayer:function(){this.unselectedAnnotationFeatures=new at,this.unselectedAnnotationSource=new Ke({features:this.unselectedAnnotationFeatures}),this.unselectedAnnotationLayer=new ie({source:this.unselectedAnnotationSource,zIndex:99,updateWhileAnimating:!0,updateWhileInteracting:!0,style:m.editing,opacity:.5})},createSelectMaiaAnnotationInteraction:function(t){this.selectMaiaAnnotationInteraction=new s({map:this.map,features:t}),this.selectMaiaAnnotationInteraction.setActive(!1),this.selectMaiaAnnotationInteraction.on("attach",this.handleSelectMaiaAnnotation)},handleSelectMaiaAnnotation:function(t){this.$emit("select",t.feature.get("annotation"))},handleUnselectMaiaAnnotation:function(){!this.modifyInProgress&&this.selectedAnnotations.length>0&&this.$emit("unselect",this.selectedAnnotations[0])}},watch:{unselectedAnnotations:function(t){this.refreshAnnotationSource(t,this.unselectedAnnotationSource)},selectingMaiaAnnotation:function(t){this.selectMaiaAnnotationInteraction.setActive(t)}},created:function(){this.createUnselectedAnnotationsLayer(),this.map.addLayer(this.unselectedAnnotationLayer),this.selectInteraction.setActive(!1),this.canModify&&(this.createSelectMaiaAnnotationInteraction(this.unselectedAnnotationFeatures),this.map.addInteraction(this.selectMaiaAnnotationInteraction),c.on("Delete",this.handleUnselectMaiaAnnotation,0,this.listenerSet)),c.off("Shift+f",this.toggleMeasuring,this.listenerSet)},mounted:function(){c.off("m",this.toggleTranslating,this.listenerSet)}},void 0,void 0,!1,null,null,null).exports,He=v({mixins:[Je],props:{convertedAnnotations:{type:Array,default:function(){return[]}}},methods:{createConvertedAnnotationsLayer:function(){this.convertedAnnotationFeatures=new at,this.convertedAnnotationSource=new Ke({features:this.convertedAnnotationFeatures});var t=new ot;t.set("color","999999"),this.convertedAnnotationLayer=new ie({source:this.convertedAnnotationSource,zIndex:98,updateWhileAnimating:!0,updateWhileInteracting:!0,style:m.features(t)})}},watch:{convertedAnnotations:function(t){this.refreshAnnotationSource(t,this.convertedAnnotationSource)}},created:function(){this.createConvertedAnnotationsLayer(),this.map.addLayer(this.convertedAnnotationLayer)}},void 0,void 0,!1,null,null,null).exports,Ze=v({components:{labelTrees:l},props:{selectedCandidates:{type:Array,required:!0},labelTrees:{type:Array,required:!0}},computed:{hasNoSelectedCandidates:function(){return 0===this.selectedCandidates.length}},methods:{handleSelectedLabel:function(t){this.$emit("select",t)},handleDeselectedLabel:function(){this.$emit("select",null)},handleConvertCandidates:function(){this.hasNoSelectedCandidates||this.$emit("convert")}}},void 0,void 0,!1,null,null,null).exports,Qe=v({props:{selectedProposals:{type:Array,required:!0},seenProposals:{type:Array,required:!0}},computed:{numberSelectedProposals:function(){return this.selectedProposals.length},numberSeenProposals:function(){return this.seenProposals.length},hasNoSelectedProposals:function(){return 0===this.numberSelectedProposals},hasSeenAllSelectedProposals:function(){return this.numberSelectedProposals>0&&this.numberSelectedProposals===this.numberSeenProposals},textClass:function(){return this.hasSeenAllSelectedProposals?"text-success":""},buttonClass:function(){return this.hasSeenAllSelectedProposals?"btn-success":"btn-default"}}},void 0,void 0,!1,null,null,null).exports,tn=v({components:{labelTrees:l},props:{labelTrees:{type:Array,required:!0}},methods:{handleSelectedLabel:function(t){this.$emit("select",t)},handleDeselectedLabel:function(){this.$emit("select",null)},proceed:function(){this.$emit("proceed")}}},void 0,void 0,!1,null,null,null).exports,en=v({props:{proposals:{type:Array,required:!0},selectedProposals:{type:Array,required:!0}},computed:{selectedProposalsCount:function(){return this.selectedProposals.length},proposalsCount:function(){return this.proposals.length}},methods:{proceed:function(){this.$emit("proceed")}}},void 0,void 0,!1,null,null,null).exports,nn=[],on={},sn=[],rn={},an=v({mixins:[p],components:{sidebar:g,sidebarTab:_,selectProposalsTab:en,proposalsImageGrid:w,refineProposalsTab:Qe,refineCanvas:Je,refineCandidatesCanvas:He,selectCandidatesTab:tn,candidatesImageGrid:C,refineCandidatesTab:Ze},data:function(){return{job:null,states:null,labelTrees:[],visitedSelectProposalsTab:!1,visitedRefineProposalsTab:!1,visitedSelectCandidatesTab:!1,visitedRefineCandidatesTab:!1,openTab:"info",fetchProposalsPromise:null,hasProposals:!1,selectedProposalIds:{},seenProposalIds:{},lastSelectedProposal:null,currentProposalImage:null,currentProposalImageIndex:null,currentProposals:[],currentProposalsById:{},focussedProposal:null,proposalAnnotationCache:{},fetchCandidatesPromise:null,hasCandidates:!1,selectedCandidateIds:{},convertedCandidateIds:{},lastSelectedCandidate:null,currentCandidateImage:null,currentCandidateImageIndex:null,currentCandidates:[],currentCandidatesById:{},focussedCandidate:null,candidateAnnotationCache:{},selectedLabel:null,sequenceCounter:0}},computed:{infoTabOpen:function(){return"info"===this.openTab},selectProposalsTabOpen:function(){return"select-proposals"===this.openTab},refineProposalsTabOpen:function(){return"refine-proposals"===this.openTab},selectCandidatesTabOpen:function(){return"select-candidates"===this.openTab},refineCandidatesTabOpen:function(){return"refine-candidates"===this.openTab},isInTrainingProposalState:function(){return this.job.state_id===this.states["training-proposals"]},isInAnnotationCandidateState:function(){return this.job.state_id===this.states["annotation-candidates"]},proposals:function(){return this.hasProposals?nn:[]},selectedProposals:function(){var t=this.selectedProposalIds;return Object.keys(t).map((function(t){return on[t]})).sort((function(e,n){return e.image_id===n.image_id?t[e.id]-t[n.id]:e.image_id-n.image_id}))},proposalsForSelectView:function(){return this.isInTrainingProposalState?this.proposals:this.selectedProposals},hasSelectedProposals:function(){return this.selectedProposals.length>0},proposalImageIds:function(){var t={};return this.proposals.forEach((function(e){return t[e.image_id]=void 0})),Object.keys(t).map((function(t){return parseInt(t,10)}))},currentProposalImageId:function(){return this.proposalImageIds[this.currentProposalImageIndex]},nextProposalImageIndex:function(){return(this.currentProposalImageIndex+1)%this.proposalImageIds.length},nextProposalImageId:function(){return this.proposalImageIds[this.nextProposalImageIndex]},nextFocussedProposalImageId:function(){return this.nextFocussedProposal?this.nextFocussedProposal.image_id:this.nextProposalImageId},previousProposalImageIndex:function(){return(this.currentProposalImageIndex-1+this.proposalImageIds.length)%this.proposalImageIds.length},previousProposalImageId:function(){return this.proposalImageIds[this.previousProposalImageIndex]},hasCurrentProposalImage:function(){return null!==this.currentProposalImage},currentSelectedProposals:function(){var t=this;return this.currentProposals.filter((function(e){return t.selectedProposalIds.hasOwnProperty(e.id)}))},currentUnselectedProposals:function(){var t=this;return this.currentProposals.filter((function(e){return!t.selectedProposalIds.hasOwnProperty(e.id)}))},previousFocussedProposal:function(){var t=(this.selectedProposals.indexOf(this.focussedProposal)-1+this.selectedProposals.length)%this.selectedProposals.length;return this.selectedProposals[t]},nextFocussedProposal:function(){var t=(this.selectedProposals.indexOf(this.focussedProposal)+1)%this.selectedProposals.length;return this.selectedProposals[t]},focussedProposalToShow:function(){return this.refineProposalsTabOpen?this.focussedProposal:null},focussedProposalArray:function(){var t=this;return this.focussedProposalToShow?this.currentSelectedProposals.filter((function(e){return e.id===t.focussedProposalToShow.id})):[]},selectedAndSeenProposals:function(){var t=this;return this.selectedProposals.filter((function(e){return t.seenProposalIds.hasOwnProperty(e.id)}))},candidates:function(){return this.hasCandidates?sn:[]},selectedCandidates:function(){var t=this.selectedCandidateIds;return Object.keys(t).map((function(t){return rn[t]})).sort((function(e,n){return e.image_id===n.image_id?t[e.id]-t[n.id]:e.image_id-n.image_id}))},hasSelectedCandidates:function(){return this.selectedCandidates.length>0},candidateImageIds:function(){var t={};return this.candidates.forEach((function(e){return t[e.image_id]=void 0})),Object.keys(t).map((function(t){return parseInt(t,10)}))},currentCandidateImageId:function(){return this.candidateImageIds[this.currentCandidateImageIndex]},nextCandidateImageIndex:function(){return(this.currentCandidateImageIndex+1)%this.candidateImageIds.length},nextCandidateImageId:function(){return this.candidateImageIds[this.nextCandidateImageIndex]},previousCandidateImageIndex:function(){return(this.currentCandidateImageIndex-1+this.candidateImageIds.length)%this.candidateImageIds.length},previousCandidateImageId:function(){return this.candidateImageIds[this.previousCandidateImageIndex]},hasCurrentCandidateImage:function(){return null!==this.currentCandidateImage},currentSelectedCandidates:function(){var t=this;return this.currentCandidates.filter((function(e){return t.selectedCandidateIds.hasOwnProperty(e.id)}))},currentUnselectedCandidates:function(){var t=this;return this.currentCandidates.filter((function(e){return!t.selectedCandidateIds.hasOwnProperty(e.id)&&!t.convertedCandidateIds.hasOwnProperty(e.id)}))},currentConvertedCandidates:function(){var t=this;return this.currentCandidates.filter((function(e){return t.convertedCandidateIds.hasOwnProperty(e.id)}))},previousFocussedCandidate:function(){var t=(this.selectedCandidates.indexOf(this.focussedCandidate)-1+this.selectedCandidates.length)%this.selectedCandidates.length;return this.selectedCandidates[t]},nextFocussedCandidate:function(){var t=(this.selectedCandidates.indexOf(this.focussedCandidate)+1)%this.selectedCandidates.length;return this.selectedCandidates[t]},nextFocussedCandidateImageId:function(){return this.nextFocussedCandidate?this.nextFocussedCandidate.image_id:this.nextCandidateImageId},focussedCandidateToShow:function(){return this.refineCandidatesTabOpen?this.focussedCandidate:null},focussedCandidateArray:function(){var t=this;return this.focussedCandidateToShow?this.currentSelectedCandidates.filter((function(e){return e.id===t.focussedCandidateToShow.id})):[]}},methods:{handleSidebarToggle:function(){var t=this;this.$nextTick((function(){t.$refs.proposalsImageGrid&&t.$refs.proposalsImageGrid.$emit("resize"),t.$refs.candidatesImageGrid&&t.$refs.candidatesImageGrid.$emit("resize")}))},handleTabOpened:function(t){this.openTab=t},setProposals:function(t){var e=this;(nn=t.body).forEach((function(t){on[t.id]=t,e.setSelectedProposalId(t)})),this.hasProposals=nn.length>0},fetchProposals:function(){return this.fetchProposalsPromise||(this.startLoading(),this.fetchProposalsPromise=b.getTrainingProposals({id:this.job.id}),this.fetchProposalsPromise.then(this.setProposals,r).finally(this.finishLoading)),this.fetchProposalsPromise},openRefineProposalsTab:function(){this.openTab="refine-proposals"},openRefineCandidatesTab:function(){this.openTab="refine-candidates"},updateSelectProposal:function(t,e){var n=this;t.selected=e,this.setSelectedProposalId(t);var i=P.update({id:t.id},{selected:e});return i.catch((function(i){r(i),t.selected=!e,n.setSelectedProposalId(t)})),i},setSelectedProposalId:function(t){t.selected?Vue.set(this.selectedProposalIds,t.id,this.getSequenceId()):Vue.delete(this.selectedProposalIds,t.id)},setSeenProposalId:function(t){Vue.set(this.seenProposalIds,t.id,!0)},fetchProposalAnnotations:function(t){return this.proposalAnnotationCache.hasOwnProperty(t)||(this.proposalAnnotationCache[t]=b.getTrainingProposalPoints({jobId:this.job.id,imageId:t}).then(this.parseAnnotations)),this.proposalAnnotationCache[t]},parseAnnotations:function(t){return Object.keys(t.body).map((function(e){return{id:parseInt(e,10),shape:"Circle",points:t.body[e]}}))},setCurrentProposalImageAndAnnotations:function(t){var e=this;this.currentProposalImage=t[0],this.currentProposals=t[1],this.currentProposalsById={},this.currentProposals.forEach((function(t){e.currentProposalsById[t.id]=t}))},cacheNextProposalImage:function(){this.currentProposalImageId!==this.nextFocussedProposalImageId&&u.fetchImage(this.nextFocussedProposalImageId).catch((function(){}))},cacheNextProposalAnnotations:function(){this.currentProposalImageId!==this.nextFocussedProposalImageId&&this.fetchProposalAnnotations(this.nextFocussedProposalImageId).catch((function(){}))},handlePreviousProposalImage:function(){this.currentProposalImageIndex=this.previousProposalImageIndex},handlePreviousProposal:function(){this.previousFocussedProposal?this.focussedProposal=this.previousFocussedProposal:this.handlePreviousProposalImage()},handleNextProposalImage:function(){this.currentProposalImageIndex=this.nextProposalImageIndex},handleNextProposal:function(){this.nextFocussedProposal?this.focussedProposal=this.nextFocussedProposal:this.handleNextProposalImage()},handleRefineProposal:function(t){Vue.Promise.all(t.map(this.updateProposalPoints)).catch(r)},updateProposalPoints:function(t){var e=this.currentProposalsById[t.id];return P.update({id:t.id},{points:t.points}).then((function(){e.points=t.points}))},focusProposalToShow:function(){if(this.focussedProposalToShow){var t=this.currentProposalsById[this.focussedProposalToShow.id];t&&this.$refs.refineProposalsCanvas.focusAnnotation(t,!0,!1)}},handleSelectedProposal:function(t,e){t.selected?this.unselectProposal(t):e.shiftKey&&this.lastSelectedProposal?this.doForEachBetween(this.proposals,t,this.lastSelectedProposal,this.selectProposal):(this.lastSelectedProposal=t,this.selectProposal(t))},selectProposal:function(t){this.updateSelectProposal(t,!0).then(this.maybeInitFocussedProposal)},unselectProposal:function(t){var e=this.nextFocussedProposal;this.updateSelectProposal(t,!1).bind(this).then((function(){this.maybeUnsetFocussedProposal(t,e)}))},maybeInitFocussedProposal:function(){!this.focussedProposal&&this.hasSelectedProposals&&(this.focussedProposal=this.selectedProposals[0])},maybeUnsetFocussedProposal:function(t,e){this.focussedProposal&&this.focussedProposal.id===t.id&&(e&&e.id!==t.id?this.focussedProposal=e:this.focussedProposal=null)},maybeInitCurrentProposalImage:function(){null===this.currentProposalImageIndex&&(this.currentProposalImageIndex=0)},maybeInitCurrentCandidateImage:function(){null===this.currentCandidateImageIndex&&(this.currentCandidateImageIndex=0)},handleLoadingError:function(t){f.danger(t)},setSelectedCandidateId:function(t){t.label&&!t.annotation_id?Vue.set(this.selectedCandidateIds,t.id,this.getSequenceId()):Vue.delete(this.selectedCandidateIds,t.id)},setConvertedCandidateId:function(t){t.annotation_id?Vue.set(this.convertedCandidateIds,t.id,t.annotation_id):Vue.delete(this.convertedCandidateIds,t.id)},setCandidates:function(t){var e=this;(sn=t.body).forEach((function(t){rn[t.id]=t,e.setSelectedCandidateId(t),e.setConvertedCandidateId(t)})),this.hasCandidates=sn.length>0},fetchCandidates:function(){return this.fetchCandidatesPromise||(this.startLoading(),this.fetchCandidatesPromise=b.getAnnotationCandidates({id:this.job.id}),this.fetchCandidatesPromise.then(this.setCandidates,r).finally(this.finishLoading)),this.fetchCandidatesPromise},handleSelectedCandidate:function(t,e){t.label?this.unselectCandidate(t):e.shiftKey&&this.lastSelectedCandidate&&this.selectedLabel?this.doForEachBetween(this.candidates,t,this.lastSelectedCandidate,this.selectCandidate):(this.lastSelectedCandidate=t,this.selectCandidate(t))},selectCandidate:function(t){this.selectedLabel?t.annotation_id||this.updateSelectCandidate(t,this.selectedLabel).then(this.maybeInitFocussedCandidate):f.info("Please select a label first.")},unselectCandidate:function(t){var e=this.nextFocussedCandidate;this.updateSelectCandidate(t,null).bind(this).then((function(){this.maybeUnsetFocussedCandidate(t,e)}))},updateSelectCandidate:function(t,e){var n=this,i=t.label;t.label=e,this.setSelectedCandidateId(t);var o=e?e.id:null,s=I.update({id:t.id},{label_id:o});return s.catch((function(e){r(e),t.label=i,n.setSelectedCandidateId(t)})),s},fetchCandidateAnnotations:function(t){return this.candidateAnnotationCache.hasOwnProperty(t)||(this.candidateAnnotationCache[t]=b.getAnnotationCandidatePoints({jobId:this.job.id,imageId:t}).then(this.parseAnnotations)),this.candidateAnnotationCache[t]},setCurrentCandidateImageAndAnnotations:function(t){var e=this;this.currentCandidateImage=t[0],this.currentCandidates=t[1],this.currentCandidatesById={},this.currentCandidates.forEach((function(t){return e.currentCandidatesById[t.id]=t}))},handlePreviousCandidateImage:function(){this.currentCandidateImageIndex=this.previousCandidateImageIndex},handlePreviousCandidate:function(){this.previousFocussedCandidate?this.focussedCandidate=this.previousFocussedCandidate:this.handlePreviousCandidateImage()},handleNextCandidateImage:function(){this.currentCandidateImageIndex=this.nextCandidateImageIndex},handleNextCandidate:function(){this.nextFocussedCandidate?this.focussedCandidate=this.nextFocussedCandidate:this.handleNextCandidateImage()},focusCandidateToShow:function(){if(this.focussedCandidateToShow){var t=this.currentCandidatesById[this.focussedCandidateToShow.id];t&&this.$refs.refineCandidatesCanvas.focusAnnotation(t,!0,!1)}},maybeInitFocussedCandidate:function(){!this.focussedCandidate&&this.hasSelectedCandidates&&(this.focussedCandidate=this.selectedCandidates[0])},maybeUnsetFocussedCandidate:function(t,e){this.focussedCandidate&&this.focussedCandidate.id===t.id&&(e&&e.id!==t.id?this.focussedCandidate=e:this.focussedCandidate=null)},handleSelectedLabel:function(t){this.selectedLabel=t},doForEachBetween:function(t,e,n,i){var o=t.indexOf(e),s=t.indexOf(n);if(se?1:0}return function(n,i,o,s,r){!function e(n,i,o,s,r){for(;s>o;){if(s-o>600){var a=s-o+1,h=i-o+1,u=Math.log(a),c=.5*Math.exp(2*u/3),l=.5*Math.sqrt(u*c*(a-c)/a)*(h-a/2<0?-1:1),d=Math.max(o,Math.floor(i-h*c/a+l)),p=Math.min(s,Math.floor(i+(a-h)*c/a+l));e(n,i,d,p,r)}var f=n[i],g=o,_=s;for(t(n,o,i),r(n[s],f)>0&&t(n,o,s);g<_;){for(t(n,g,_),g++,_--;r(n[g],f)<0;)g++;for(;r(n[_],f)>0;)_--}0===r(n[o],f)?t(n,o,_):(_++,t(n,_,s)),_<=i&&(o=_+1),i<=_&&(s=_-1)}}(n,i,o||0,s||n.length-1,r||e)}}()},zcrr:function(t,e){}});
\ No newline at end of file
diff --git a/src/public/mix-manifest.json b/src/public/mix-manifest.json
index 42a53ee..d91c37a 100644
--- a/src/public/mix-manifest.json
+++ b/src/public/mix-manifest.json
@@ -1,4 +1,4 @@
{
- "/assets/scripts/main.js": "/assets/scripts/main.js?id=e4afcffaa37615dfae9d",
+ "/assets/scripts/main.js": "/assets/scripts/main.js?id=9fffd3a46e7a78d4894f",
"/assets/styles/main.css": "/assets/styles/main.css?id=e0eebb85d62abf60780d"
}
diff --git a/src/resources/assets/js/form.vue b/src/resources/assets/js/form.vue
index f5bd1c4..3dac8e2 100644
--- a/src/resources/assets/js/form.vue
+++ b/src/resources/assets/js/form.vue
@@ -21,6 +21,7 @@ export default {
labels: [],
selectedLabels: [],
submitted: false,
+ trainScheme: [],
};
},
computed: {
@@ -61,6 +62,19 @@ export default {
submit() {
this.submitted = true;
},
+ removeTrainStep(index) {
+ this.trainScheme.splice(index, 1);
+ },
+ addTrainStep() {
+ let step = {layers: 'heads', epochs: 10, learning_rate: 0.001};
+ if (this.trainScheme.length > 0) {
+ let last = this.trainScheme[this.trainScheme.length - 1];
+ step.layers = last.layers;
+ step.epochs = last.epochs;
+ step.learning_rate = last.learning_rate;
+ }
+ this.trainScheme.push(step);
+ },
},
watch: {
useExistingAnnotations(use) {
@@ -81,6 +95,8 @@ export default {
this.volumeId = biigle.$require('maia.volumeId');
this.useExistingAnnotations = biigle.$require('maia.useExistingAnnotations');
this.skipNoveltyDetection = biigle.$require('maia.skipNoveltyDetection');
+ this.trainScheme = biigle.$require('maia.trainScheme');
+ this.showAdvanced = biigle.$require('maia.hasErrors');
if (this.useExistingAnnotations) {
this.shouldFetchLabels = true;
diff --git a/src/resources/views/index/job-form.blade.php b/src/resources/views/index/job-form.blade.php
index 40d515a..7e10aec 100644
--- a/src/resources/views/index/job-form.blade.php
+++ b/src/resources/views/index/job-form.blade.php
@@ -155,26 +155,50 @@
+
+ UnKnoT
+ M. Zurowietz and T. W. Nattkemper, "Unsupervised Knowledge Transfer for Object Detection in Marine Environmental Monitoring and Exploration,"
in IEEE Access, vol. 8, pp. 143558-143568, 2020, doi: 10.1109/ACCESS.2020.3014441
.
+
diff --git a/src/resources/views/show/info-tab.blade.php b/src/resources/views/show/info-tab.blade.php
index 6adb22b..de95cb0 100644
--- a/src/resources/views/show/info-tab.blade.php
+++ b/src/resources/views/show/info-tab.blade.php
@@ -8,8 +8,8 @@
-
-
+ |
+
Novelty Detection
@if ($job->shouldSkipNoveltyDetection())
(skipped)
@@ -61,23 +61,47 @@
@endif
|
-
-
-
- Instance Segmentation |
-
-
-
-
- Training epochs (head) |
- {{Arr::get($job->params, 'is_epochs_head')}} |
-
-
- Training epochs (all) |
- {{Arr::get($job->params, 'is_epochs_all')}} |
-
-
-
+ @if (Arr::has($job->params, 'is_train_scheme'))
+
+
+
+ Instance Segmentation Training scheme |
+
+
+
+
+ Layers |
+ Epochs |
+ Learning rate |
+
+ @foreach(Arr::get($job->params, 'is_train_scheme', []) as $index => $step)
+
+ {{$step['layers']}} |
+ {{$step['epochs']}} |
+ {{$step['learning_rate']}} |
+
+ @endforeach
+
+
+ @else
+
+
+
+ Instance Segmentation |
+
+
+
+
+ Training epochs (head) |
+ {{Arr::get($job->params, 'is_epochs_head')}} |
+
+
+ Training epochs (all) |
+ {{Arr::get($job->params, 'is_epochs_all')}} |
+
+
+
+ @endif