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 @@
Instance Segmentation -
- - - @if($errors->has('is_epochs_head')) - {{ $errors->first('is_epochs_head') }} - @else - - Time spent on training only the head layers of Mask R-CNN for instance segmentation. This is faster and should be a higher number than epochs (all). - - @endif -
-
- - - @if($errors->has('is_epochs_all')) - {{ $errors->first('is_epochs_all') }} +
+ +
+ + + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ + + +
+ @if($errors->has('is_train_scheme')) + {{ $errors->first('is_train_scheme') }} @else - Time spent on training all layers of Mask R-CNN for instance segmentation. This is slower and should be a lower number than epochs (head). + The scheme to use for training of Mask R-CNN for instance segmentation. The learning rate should decrease with subsequent steps. @endif
@@ -194,5 +218,7 @@ biigle.$declare('maia.volumeId', {!! $volume->id !!}); biigle.$declare('maia.useExistingAnnotations', {!! old('use_existing') ? 'true' : 'false' !!}); biigle.$declare('maia.skipNoveltyDetection', {!! old('skip_nd') ? 'true' : 'false' !!}); + biigle.$declare('maia.trainScheme', {!! old('is_train_scheme', $defaultTrainScheme) !!}); + biigle.$declare('maia.hasErrors', {!! $errors->any() ? 'true' : 'false' !!}); @endpush diff --git a/src/resources/views/manual/tutorials/instance-segmentation.blade.php b/src/resources/views/manual/tutorials/instance-segmentation.blade.php index 603af75..105673c 100644 --- a/src/resources/views/manual/tutorials/instance-segmentation.blade.php +++ b/src/resources/views/manual/tutorials/instance-segmentation.blade.php @@ -21,25 +21,16 @@ The configurable parameters for this stage are not shown by default in the form to submit a new MAIA job. Click on the button below the form to show the parameters for the instance segmentation stage.

-

Number of training epochs (head)

+

Training scheme

- Integer greater than or equal to 1. Default 20 + By default, the training scheme of UnKnoT [2] is used.

- Time spent on training only the head layers of Mask R-CNN for instance segmentation. This is faster and should be a higher number than epochs (all). + A series of training steps consisting of layers to train, the number of epochs and the learning rate to use. Training should begin with the heads layers. The learning rate should decrease with subsequent steps.

-

Number of training epochs (all)

- -

- Integer greater than or equal to 1. Default 10 -

- -

- Time spent on training all layers of Mask R-CNN for instance segmentation. This is slower and should be a lower number than epochs (head). -

Further reading

@endsection diff --git a/src/resources/views/manualReferences.blade.php b/src/resources/views/manualReferences.blade.php index 5ac3c12..7bed8bd 100644 --- a/src/resources/views/manualReferences.blade.php +++ b/src/resources/views/manualReferences.blade.php @@ -2,3 +2,7 @@ MAIA
Zurowietz, M., Langenkämper, D., Hosking, B., Ruhl, H. A., & Nattkemper, T. W. (2018). MAIA—A machine learning assisted image annotation method for environmental monitoring and exploration.
PloS one, 13(11), e0207498. doi: 10.1371/journal.pone.0207498

+

+ 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')) + + + + + + + + + + + + + @foreach(Arr::get($job->params, 'is_train_scheme', []) as $index => $step) + + + + + + @endforeach + +
Instance Segmentation
Training scheme
LayersEpochsLearning rate
{{$step['layers']}}{{$step['epochs']}}{{$step['learning_rate']}}
+ @else + + + + + + + + + + + + + + + + +
Instance Segmentation
Training epochs (head){{Arr::get($job->params, 'is_epochs_head')}}
Training epochs (all){{Arr::get($job->params, 'is_epochs_all')}}
+ @endif
id}") }}" method="POST" onsubmit="return confirm('Are you sure that you want to delete this job?')"> From f11e87e711555e12beb12c96e88ae07236b26b97 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Wed, 21 Oct 2020 16:07:10 +0200 Subject: [PATCH 05/22] WIP Start to implement separate training data methods --- .../Controllers/Api/MaiaJobController.php | 11 +- src/Http/Requests/StoreMaiaJob.php | 25 ++-- .../DispatchNoveltyDetectionRequest.php | 5 +- src/MaiaJob.php | 22 ++- src/public/assets/scripts/main.js | 2 +- src/public/mix-manifest.json | 2 +- src/resources/assets/js/form.vue | 15 +- src/resources/views/index/job-form.blade.php | 135 ++++++++++-------- src/resources/views/show/info-tab.blade.php | 44 +++--- .../Controllers/Api/MaiaJobControllerTest.php | 70 +++------ tests/Jobs/UseExistingAnnotationsTest.php | 9 ++ 11 files changed, 173 insertions(+), 167 deletions(-) diff --git a/src/Http/Controllers/Api/MaiaJobController.php b/src/Http/Controllers/Api/MaiaJobController.php index d936a34..8199a20 100644 --- a/src/Http/Controllers/Api/MaiaJobController.php +++ b/src/Http/Controllers/Api/MaiaJobController.php @@ -46,14 +46,12 @@ public function store(StoreMaiaJob $request) $job->user_id = $request->user()->id; $job->state_id = State::noveltyDetectionId(); $paramKeys = [ - 'use_existing', - 'restrict_labels', - 'skip_nd', + 'training_data_method', // is_* are parameters for instance segmentation. 'is_train_scheme', ]; - if (!$request->has('skip_nd')) { + if ($request->input('training_data_method') === MaiaJob::TRAIN_NOVELTY_DETECTION) { $paramKeys = array_merge($paramKeys, [ // nd_* are parameters for novelty detection. 'nd_clusters', @@ -65,6 +63,11 @@ public function store(StoreMaiaJob $request) 'nd_stride', 'nd_ignore_radius', ]); + } else if ($request->input('training_data_method') === MaiaJob::TRAIN_OWN_ANNOTATIONS) { + $paramKeys = array_merge($paramKeys, [ + // oa_* are parameters for own annotations. + 'oa_restrict_labels', + ]); } $job->params = $request->only($paramKeys); diff --git a/src/Http/Requests/StoreMaiaJob.php b/src/Http/Requests/StoreMaiaJob.php index b1e8b74..0b024dc 100644 --- a/src/Http/Requests/StoreMaiaJob.php +++ b/src/Http/Requests/StoreMaiaJob.php @@ -37,18 +37,17 @@ public function authorize() public function rules() { return [ - 'use_existing' => 'required_with:restrict_labels,skip_nd|boolean', - 'restrict_labels' => 'array', - 'restrict_labels.*' => 'integer|exists:labels,id', - 'skip_nd' => 'boolean', - 'nd_clusters' => 'required_unless:skip_nd,true|integer|min:1|max:100', - 'nd_patch_size' => ['required_unless:skip_nd,true', 'integer', 'min:3', 'max:99', new OddNumber], - 'nd_threshold' => 'required_unless:skip_nd,true|integer|min:0|max:99', - 'nd_latent_size' => 'required_unless:skip_nd,true|numeric|min:0.05|max:0.75', - 'nd_trainset_size' => 'required_unless:skip_nd,true|integer|min:1000|max:100000', - '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', + 'training_data_method' => 'required|in:novelty_detection,own_annotations', + 'oa_restrict_labels' => 'array', + 'oa_restrict_labels.*' => 'integer|exists:labels,id', + 'nd_clusters' => 'required_if:training_data_method,novelty_detection|integer|min:1|max:100', + 'nd_patch_size' => ['required_if:training_data_method,novelty_detection', 'integer', 'min:3', 'max:99', new OddNumber], + 'nd_threshold' => 'required_if:training_data_method,novelty_detection|integer|min:0|max:99', + 'nd_latent_size' => 'required_if:training_data_method,novelty_detection|numeric|min:0.05|max:0.75', + 'nd_trainset_size' => 'required_if:training_data_method,novelty_detection|integer|min:1000|max:100000', + 'nd_epochs' => 'required_if:training_data_method,novelty_detection|integer|min:50|max:1000', + 'nd_stride' => 'required_if:training_data_method,novelty_detection|integer|min:1|max:10', + 'nd_ignore_radius' => 'required_if:training_data_method,novelty_detection|integer|min:0', 'is_train_scheme' => 'required|array|min:1', 'is_train_scheme.*' => 'array', 'is_train_scheme.*.layers' => 'required|in:heads,all', @@ -86,7 +85,7 @@ public function withValidator($validator) $validator->errors()->add('volume', 'New MAIA jobs cannot be created for volumes with very large images.'); } - if (!$this->input('skip_nd') && $this->volume->images()->count() < $this->input('nd_clusters')) { + if ($this->input('training_data_method') === MaiaJob::TRAIN_NOVELTY_DETECTION && $this->volume->images()->count() < $this->input('nd_clusters')) { $validator->errors()->add('nd_clusters', 'The number of image clusters must not be greater than the number of images in the volume.'); } }); diff --git a/src/Listeners/DispatchNoveltyDetectionRequest.php b/src/Listeners/DispatchNoveltyDetectionRequest.php index 3f9f314..4026f13 100644 --- a/src/Listeners/DispatchNoveltyDetectionRequest.php +++ b/src/Listeners/DispatchNoveltyDetectionRequest.php @@ -22,10 +22,7 @@ public function handle(MaiaJobCreated $event) { if ($event->job->shouldUseExistingAnnotations()) { UseExistingAnnotations::dispatch($event->job); - } - - - if (!$event->job->shouldSkipNoveltyDetection()) { + } else if ($event->job->shouldUseNoveltyDetection()) { $request = new NoveltyDetectionRequest($event->job); Queue::connection(config('maia.request_connection')) ->pushOn(config('maia.request_queue'), $request); diff --git a/src/MaiaJob.php b/src/MaiaJob.php index 133bf65..18229e1 100644 --- a/src/MaiaJob.php +++ b/src/MaiaJob.php @@ -13,6 +13,16 @@ class MaiaJob extends Model { use HasJsonAttributes; + /** + * @var string + */ + const TRAIN_NOVELTY_DETECTION = 'novelty_detection'; + + /** + * @var string + */ + const TRAIN_OWN_ANNOTATIONS = 'own_annotations'; + /** * The attributes that should be casted to native types. * @@ -155,23 +165,23 @@ public function setErrorAttribute(array $error) } /** - * Determine if this job should use existing annotations. + * Determine if this job should use existing annotations to get training data. * * @return bool */ public function shouldUseExistingAnnotations() { - return (bool) $this->getJsonAttr('params.use_existing', false); + return $this->getJsonAttr('params.training_data_method') === self::TRAIN_OWN_ANNOTATIONS; } /** - * Determine if this job should skip novelty detection. + * Determine if this job should use novelty detection to get training data. * * @return bool */ - public function shouldSkipNoveltyDetection() + public function shouldUseNoveltyDetection() { - return $this->shouldUseExistingAnnotations() - && (bool) $this->getJsonAttr('params.skip_nd', false); + // Handle fallback where old jobs don't have a training_data_method yet. + return $this->getJsonAttr('params.training_data_method') === self::TRAIN_NOVELTY_DETECTION || $this->getJsonAttr('params.training_data_method') === null; } } diff --git a/src/public/assets/scripts/main.js b/src/public/assets/scripts/main.js index d8799cb..1f5a6ec 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,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 +!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,showAdvanced:!1,shouldFetchLabels:!1,labels:[],selectedLabels:[],submitted:!1,trainScheme:[],trainingDataMethod:""}},computed:{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},useExistingAnnotations:function(){return"own_annotations"===this.trainingDataMethod},useNoveltyDetection:function(){return"novelty_detection"===this.trainingDataMethod}},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.trainScheme=biigle.$require("maia.trainScheme"),this.showAdvanced=biigle.$require("maia.hasErrors"),this.trainingDataMethod=biigle.$require("maia.trainingDataMethod"),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",R="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]=K,++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 kt,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)}),K)}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 Ne({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 Ne({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 d91c37a..042e695 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=9fffd3a46e7a78d4894f", + "/assets/scripts/main.js": "/assets/scripts/main.js?id=47b958007d5949752c93", "/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 3dac8e2..9626869 100644 --- a/src/resources/assets/js/form.vue +++ b/src/resources/assets/js/form.vue @@ -14,20 +14,16 @@ export default { data() { return { volumeId: null, - useExistingAnnotations: false, - skipNoveltyDetection: false, showAdvanced: false, shouldFetchLabels: false, labels: [], selectedLabels: [], submitted: false, trainScheme: [], + trainingDataMethod: '', }; }, computed: { - canSkipNoveltyDetection() { - return this.useExistingAnnotations && this.hasLabels; - }, hasLabels() { return this.labels.length > 0; }, @@ -40,6 +36,12 @@ export default { hasNoExistingAnnotations() { return this.useExistingAnnotations && !this.hasLabels && !this.loading; }, + useExistingAnnotations() { + return this.trainingDataMethod === 'own_annotations'; + }, + useNoveltyDetection() { + return this.trainingDataMethod === 'novelty_detection'; + }, }, methods: { toggle() { @@ -93,10 +95,9 @@ export default { }, created() { 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.trainingDataMethod = biigle.$require('maia.trainingDataMethod'); 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 7e10aec..18be8c9 100644 --- a/src/resources/views/index/job-form.blade.php +++ b/src/resources/views/index/job-form.blade.php @@ -1,11 +1,51 @@

Create a new MAIA job

- A job can run for many hours or even a day. Please choose your parameters carefully before you submit a new job. + A job can run for many hours or even a day. Please choose your settings carefully before you submit a new job.

id}/maia-jobs") }}" v-on:submit="submit">
+ Training data method +
+ The instance segmentation stage of MAIA requires training data with objects of interest. You can choose from several methods to obtain the training data: +
+
+
+ +
+
+ +
+
+ +
+
+
+ +
Novelty Detection -
+
@if($errors->has('nd_clusters')) @@ -17,7 +57,7 @@ @endif
-
+
@if($errors->has('nd_patch_size')) @@ -29,58 +69,8 @@ @endif
-
- - @if($errors->has('use_existing')) - {{ $errors->first('use_existing') }} - @else - - Use existing annotations as training proposals. All annotations will be converted to circles. - - @endif -

- There are no existing annotations. -

-
- -
- - - - Only use existing annotations with these selected labels. - - -
    -
  • - - -
  • -
  • No restriction
  • -
- - -
- -
- - @if($errors->has('skip_nd')) - {{ $errors->first('skip_nd') }} - @else - - You can choose to skip the novelty detection stage if you use existing annotations as training proposals. Make sure that you have enough training proposals in this case. - - @endif -
- -
-
+
@if($errors->has('nd_threshold')) @@ -92,7 +82,7 @@ @endif
-
+
@if($errors->has('nd_latent_size')) @@ -104,7 +94,7 @@ @endif
-
+
@if($errors->has('nd_trainset_size')) @@ -116,7 +106,7 @@ @endif
-
+
@if($errors->has('nd_epochs')) @@ -128,7 +118,7 @@ @endif
-
+
@if($errors->has('nd_stride')) @@ -140,7 +130,7 @@ @endif
-
+
@if($errors->has('nd_ignore_radius')) @@ -153,6 +143,29 @@
+ +
+ Existing Annotations + +
+ + + + Only use existing annotations with these selected labels. + + +
    +
  • + + +
  • +
  • No restriction
  • +
+ + +
+
+
Instance Segmentation @@ -204,7 +217,6 @@
-
@@ -216,9 +228,8 @@ @push('scripts') @endpush diff --git a/src/resources/views/show/info-tab.blade.php b/src/resources/views/show/info-tab.blade.php index de95cb0..46ce7d4 100644 --- a/src/resources/views/show/info-tab.blade.php +++ b/src/resources/views/show/info-tab.blade.php @@ -6,19 +6,16 @@

created {{$job->created_at->diffForHumans()}} by {{$job->user->firstname}} {{$job->user->lastname}}

- - - - - - - - @unless ($job->shouldSkipNoveltyDetection()) + @if ($job->shouldUseNoveltyDetection()) +
- Novelty Detection - @if ($job->shouldSkipNoveltyDetection()) - (skipped) - @endif -
+ + + + + + @@ -51,16 +48,19 @@ - @endif - @if ($job->shouldUseExistingAnnotations()) - - + +
+ Novelty Detection +
Clusters {{Arr::get($job->params, 'nd_clusters')}}Ignore radius {{Arr::get($job->params, 'nd_ignore_radius')}}
- used existing annotations -
+ @elseif($job->shouldUseExistingAnnotations()) + + + + - @endif - -
+ Used existing annotations +
+ + + @endif @if (Arr::has($job->params, 'is_train_scheme')) diff --git a/tests/Http/Controllers/Api/MaiaJobControllerTest.php b/tests/Http/Controllers/Api/MaiaJobControllerTest.php index e65a3a7..74fbb3a 100644 --- a/tests/Http/Controllers/Api/MaiaJobControllerTest.php +++ b/tests/Http/Controllers/Api/MaiaJobControllerTest.php @@ -19,6 +19,7 @@ public function setUp(): void { parent::setUp(); $this->defaultParams = [ + 'training_data_method' => 'novelty_detection', 'nd_clusters' => 1, 'nd_patch_size' => 39, 'nd_threshold' => 99, @@ -35,7 +36,7 @@ public function setUp(): void ImageTest::create(['volume_id' => $this->volume()->id]); } - public function testStore() + public function testStoreNoveltyDetection() { $id = $this->volume()->id; $this->doTestApiRoute('POST', "/api/v1/volumes/{$id}/maia-jobs"); @@ -49,6 +50,7 @@ public function testStore() // patch size must be an odd number $this->postJson("/api/v1/volumes/{$id}/maia-jobs", [ + 'training_data_method' => 'novelty_detection', 'nd_clusters' => 5, 'nd_patch_size' => 40, 'nd_threshold' => 99, @@ -65,6 +67,7 @@ public function testStore() // empty train scheme $this->postJson("/api/v1/volumes/{$id}/maia-jobs", [ + 'training_data_method' => 'novelty_detection', 'nd_clusters' => 5, 'nd_patch_size' => 40, 'nd_threshold' => 99, @@ -139,74 +142,52 @@ public function testStoreVideoVolume() ->assertStatus(422); } - public function testStoreUseExisting() + public function testStoreUseExistingAnnotations() { $id = $this->volume()->id; $this->beEditor(); - $this->defaultParams['use_existing'] = 'abc'; - $this->postJson("/api/v1/volumes/{$id}/maia-jobs", $this->defaultParams) - // Parameter must be bool. - ->assertStatus(422); - - $this->defaultParams['use_existing'] = true; - $this->postJson("/api/v1/volumes/{$id}/maia-jobs", $this->defaultParams) + $params = [ + 'training_data_method' => 'own_annotations', + '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) ->assertSuccessful(); $job = MaiaJob::first(); $this->assertTrue($job->shouldUseExistingAnnotations()); } public function testStoreRestrictLabels() - { - $id = $this->volume()->id; - $this->beEditor(); - $this->defaultParams['restrict_labels'] = [$this->labelChild()->id]; - $this->postJson("/api/v1/volumes/{$id}/maia-jobs", $this->defaultParams) - // Requires 'use_existing'. - ->assertStatus(422); - - $this->defaultParams['use_existing'] = true; - $this->defaultParams['restrict_labels'] = [999]; - $this->postJson("/api/v1/volumes/{$id}/maia-jobs", $this->defaultParams) - // Must contain valid label IDs. - ->assertStatus(422); - - $this->defaultParams['restrict_labels'] = [$this->labelChild()->id]; - $this->postJson("/api/v1/volumes/{$id}/maia-jobs", $this->defaultParams) - ->assertSuccessful(); - $job = MaiaJob::first(); - $this->assertArrayHasKey('restrict_labels', $job->params); - $this->assertEquals([$this->labelChild()->id], $job->params['restrict_labels']); - } - - public function testStoreSkipNd() { $id = $this->volume()->id; $this->beEditor(); $params = [ - 'skip_nd' => true, - 'nd_clusters' => 10, + 'training_data_method' => 'novelty_detection', + 'oa_restrict_labels' => [$this->labelChild()->id], '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'. + // Requires 'own_annotations'. ->assertStatus(422); - $params['use_existing'] = true; - $params['skip_nd'] = 'abc'; + $params['training_data_method'] = 'own_annotations'; + $params['oa_restrict_labels'] = [999]; $this->postJson("/api/v1/volumes/{$id}/maia-jobs", $params) - // Must be bool. + // Must contain valid label IDs. ->assertStatus(422); - $params['skip_nd'] = true; + $params['oa_restrict_labels'] = [$this->labelChild()->id]; $this->postJson("/api/v1/volumes/{$id}/maia-jobs", $params) - // nd_* parameters are no longer required. ->assertSuccessful(); $job = MaiaJob::first(); - $this->assertTrue($job->shouldSkipNoveltyDetection()); - $this->assertArrayNotHasKey('nd_clusters', $job->params); + $this->assertArrayHasKey('oa_restrict_labels', $job->params); + $this->assertEquals([$this->labelChild()->id], $job->params['oa_restrict_labels']); } public function testStoreNdClustersTooFewImages() @@ -216,11 +197,6 @@ public function testStoreNdClustersTooFewImages() $this->defaultParams['nd_clusters'] = 2; $this->postJson("/api/v1/volumes/{$id}/maia-jobs", $this->defaultParams) ->assertStatus(422); - - $this->defaultParams['skip_nd'] = true; - $this->defaultParams['use_existing'] = true; - $this->postJson("/api/v1/volumes/{$id}/maia-jobs", $this->defaultParams) - ->assertSuccessful(); } public function testDestroy() diff --git a/tests/Jobs/UseExistingAnnotationsTest.php b/tests/Jobs/UseExistingAnnotationsTest.php index fb0ac87..9a4df9a 100644 --- a/tests/Jobs/UseExistingAnnotationsTest.php +++ b/tests/Jobs/UseExistingAnnotationsTest.php @@ -19,6 +19,10 @@ class UseExistingAnnotationsTest extends TestCase { public function testHandle() { + // TODO: Update this job to make it completely independent of novelty detection. + // Instance segmentation should immediately proceed without the additional step + // of training proposal selection and refinement. + $this->markTestIncomplete(); $a = ImageAnnotationTest::create(['shape_id' => Shape::circleId()]); $al1 = ImageAnnotationLabelTest::create(['annotation_id' => $a->id]); $al2 = ImageAnnotationLabelTest::create(); @@ -34,6 +38,7 @@ public function testHandle() public function testHandleRestrictLabels() { + $this->markTestIncomplete('Its now oa_restrict_labels'); $a1 = ImageAnnotationTest::create(['shape_id' => Shape::circleId()]); $al1 = ImageAnnotationLabelTest::create(['annotation_id' => $a1->id]); // Create only one proposal even though the annotation has two matching labels. @@ -61,6 +66,7 @@ public function testHandleRestrictLabels() public function testHandleShapeConversion() { + $this->markTestIncomplete(); $a1 = ImageAnnotationTest::create([ 'shape_id' => Shape::pointId(), 'points' => [10, 20], @@ -115,6 +121,7 @@ public function testHandleShapeConversion() public function testHandleSkipNd() { + $this->markTestIncomplete(); $a = ImageAnnotationTest::create(); $job = MaiaJobTest::create([ 'volume_id' => $a->image->volume_id, @@ -132,6 +139,7 @@ public function testHandleSkipNd() public function testHandleSkipNdAndRestrictLabels() { + $this->markTestIncomplete(); $al = ImageAnnotationLabelTest::create(); $job = MaiaJobTest::create([ 'volume_id' => $al->annotation->image->volume_id, @@ -150,6 +158,7 @@ public function testHandleSkipNdAndRestrictLabels() public function testHandleSkipNdNoAnnotations() { + $this->markTestIncomplete(); $job = MaiaJobTest::create([ 'params' => ['use_existing' => true, 'skip_nd' => true], ]); From 998e25381cd5934df155d66f15b431d9e7499c32 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Fri, 23 Oct 2020 08:19:42 +0200 Subject: [PATCH 06/22] Implement existing annotations as method to generate training data --- .../Controllers/Api/MaiaJobController.php | 27 +++---- src/Jobs/UseExistingAnnotations.php | 46 +++++------- .../InstanceSegmentationFailed.php | 2 +- src/resources/views/show.blade.php | 22 +++--- src/resources/views/show/info-tab.blade.php | 15 +++- .../Controllers/Api/MaiaJobControllerTest.php | 1 + tests/Jobs/UseExistingAnnotationsTest.php | 70 ++++--------------- .../DispatchNoveltyDetectionRequestTest.php | 20 +----- tests/MaiaJobTest.php | 28 +++----- 9 files changed, 85 insertions(+), 146 deletions(-) diff --git a/src/Http/Controllers/Api/MaiaJobController.php b/src/Http/Controllers/Api/MaiaJobController.php index 8199a20..bc67dd0 100644 --- a/src/Http/Controllers/Api/MaiaJobController.php +++ b/src/Http/Controllers/Api/MaiaJobController.php @@ -23,18 +23,20 @@ class MaiaJobController extends Controller * * @apiParam {Number} id The volume ID. * - * @apiParam (Required parameters) {number} nd_clusters Number of different kinds of images to expect. Images are of the same kind if they have similar lighting conditions or show similar patterns (e.g. sea floor, habitat types). Increase this number if you expect many different kinds of images. Lower the number to 1 if you have very few images and/or the content is largely uniform. - * @apiParam (Required parameters) {number} nd_patch_size Size in pixels of the image patches used determine the training proposals. Increase the size if the images contain larger objects of interest, decrease the size if the objects are smaller. Larger patch sizes take longer to compute. Must be an odd number. - * @apiParam (Required parameters) {number} nd_threshold Percentile of pixel saliency values used to determine the saliency threshold. Lower this value to get more training proposals. The default value should be fine for most cases. - * @apiParam (Required parameters) {number} nd_latent_size Learning capability used to determine training proposals. Increase this number to ignore more complex objects and patterns. - * @apiParam (Required parameters) {number} nd_trainset_size Number of training image patches used to determine training proposals. You can increase this number for a large volume but it will take longer to compute. - * @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) {string} training_data_method One of `novelty_detection` (to perform novelty detection to generate training data) or `own_annotations` (to use existing annotations of the same volume as training data). + * + * @apiParam (Required parameters for novelty detection) {number} nd_clusters Number of different kinds of images to expect. Images are of the same kind if they have similar lighting conditions or show similar patterns (e.g. sea floor, habitat types). Increase this number if you expect many different kinds of images. Lower the number to 1 if you have very few images and/or the content is largely uniform. + * @apiParam (Required parameters for novelty detection) {number} nd_patch_size Size in pixels of the image patches used determine the training proposals. Increase the size if the images contain larger objects of interest, decrease the size if the objects are smaller. Larger patch sizes take longer to compute. Must be an odd number. + * @apiParam (Required parameters for novelty detection) {number} nd_threshold Percentile of pixel saliency values used to determine the saliency threshold. Lower this value to get more training proposals. The default value should be fine for most cases. + * @apiParam (Required parameters for novelty detection) {number} nd_latent_size Learning capability used to determine training proposals. Increase this number to ignore more complex objects and patterns. + * @apiParam (Required parameters for novelty detection) {number} nd_trainset_size Number of training image patches used to determine training proposals. You can increase this number for a large volume but it will take longer to compute. + * @apiParam (Required parameters for novelty detection) {number} nd_epochs Time spent on training when determining the training proposals. + * @apiParam (Required parameters for novelty detection) {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 for novelty detection) {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) {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. + * + * @apiParam (Optional parameters for existing annotations) {Array} oa_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. * * @param StoreMaiaJob $request * @return \Illuminate\Http\Response @@ -44,7 +46,6 @@ public function store(StoreMaiaJob $request) $job = new MaiaJob; $job->volume_id = $request->volume->id; $job->user_id = $request->user()->id; - $job->state_id = State::noveltyDetectionId(); $paramKeys = [ 'training_data_method', // is_* are parameters for instance segmentation. @@ -52,6 +53,7 @@ public function store(StoreMaiaJob $request) ]; if ($request->input('training_data_method') === MaiaJob::TRAIN_NOVELTY_DETECTION) { + $job->state_id = State::noveltyDetectionId(); $paramKeys = array_merge($paramKeys, [ // nd_* are parameters for novelty detection. 'nd_clusters', @@ -64,6 +66,7 @@ public function store(StoreMaiaJob $request) 'nd_ignore_radius', ]); } else if ($request->input('training_data_method') === MaiaJob::TRAIN_OWN_ANNOTATIONS) { + $job->state_id = State::instanceSegmentationId(); $paramKeys = array_merge($paramKeys, [ // oa_* are parameters for own annotations. 'oa_restrict_labels', diff --git a/src/Jobs/UseExistingAnnotations.php b/src/Jobs/UseExistingAnnotations.php index e40c368..5f442d9 100644 --- a/src/Jobs/UseExistingAnnotations.php +++ b/src/Jobs/UseExistingAnnotations.php @@ -5,11 +5,10 @@ use Arr; use Biigle\ImageAnnotation; use Biigle\Jobs\Job; -use Biigle\Modules\Largo\Jobs\GenerateAnnotationPatch; +use Biigle\Modules\Maia\Events\MaiaJobContinued; use Biigle\Modules\Maia\MaiaJob; use Biigle\Modules\Maia\MaiaJobState as State; -use Biigle\Modules\Maia\Notifications\NoveltyDetectionComplete; -use Biigle\Modules\Maia\Notifications\NoveltyDetectionFailed; +use Biigle\Modules\Maia\Notifications\InstanceSegmentationFailed; use Biigle\Modules\Maia\TrainingProposal; use Biigle\Shape; use DB; @@ -26,6 +25,13 @@ class UseExistingAnnotations extends Job */ protected $job; + /** + * Ignore this job if the MAIA job does not exist any more. + * + * @var bool + */ + protected $deleteWhenMissingModels = true; + /** * Create a new isntance. * @@ -43,23 +49,17 @@ public function __construct(MaiaJob $job) */ public function handle() { - if ($this->job->shouldSkipNoveltyDetection() && !$this->hasAnnotations()) { - $this->job->error = ['message' => 'Novelty detection should be skipped but there are no existing annotations to take as training proposals.']; - $this->job->state_id = State::failedNoveltyDetectionId(); + if (!$this->hasAnnotations()) { + $this->job->error = ['message' => 'Existing annotations should be used but there are no existing annotations to take as training proposals.']; + $this->job->state_id = State::failedInstanceSegmentationId(); $this->job->save(); - $this->job->user->notify(new NoveltyDetectionFailed($this->job)); + $this->job->user->notify(new InstanceSegmentationFailed($this->job)); return; } $this->convertAnnotations(); - $this->dispatchAnnotationPatchJobs(); - - if ($this->job->shouldSkipNoveltyDetection()) { - $this->job->state_id = State::trainingProposalsId(); - $this->job->save(); - $this->job->user->notify(new NoveltyDetectionComplete($this->job)); - } + event(new MaiaJobContinued($this->job)); } /** @@ -69,7 +69,7 @@ public function handle() */ protected function getAnnotationsQuery() { - $restrictLabels = Arr::get($this->job->params, 'restrict_labels', []); + $restrictLabels = Arr::get($this->job->params, 'oa_restrict_labels', []); return ImageAnnotation::join('images', 'image_annotations.image_id', '=', 'images.id') ->where('images.volume_id', $this->job->volume_id) @@ -117,6 +117,8 @@ public function convertAnnotationChunk($chunk) 'image_id' => $annotation->image_id, 'shape_id' => Shape::circleId(), 'job_id' => $this->job->id, + // All these proposals should be taken for instance segmentation. + 'selected' => true, // score should be null in this case. ]; }); @@ -181,18 +183,4 @@ protected function convertPolygonToCirlce($points) return [$x, $y, $r]; } - - /** - * Dispatch the jobs to generate image patches for the new training proposals. - */ - protected function dispatchAnnotationPatchJobs() - { - $disk = config('maia.training_proposal_storage_disk'); - $this->job->trainingProposals()->chunk(1000, function ($chunk) use ($disk) { - $chunk->each(function ($proposal) use ($disk) { - GenerateAnnotationPatch::dispatch($proposal, $disk) - ->onQueue(config('largo.generate_annotation_patch_queue')); - }); - }); - } } diff --git a/src/Notifications/InstanceSegmentationFailed.php b/src/Notifications/InstanceSegmentationFailed.php index 14aa00a..b7ea8b1 100644 --- a/src/Notifications/InstanceSegmentationFailed.php +++ b/src/Notifications/InstanceSegmentationFailed.php @@ -23,6 +23,6 @@ protected function getTitle($job) */ protected function getMessage($job) { - return "MAIA job {$job->id} failed during instance segmentation. Please notify the BIIGLE administrators."; + return "MAIA job {$job->id} failed during instance segmentation."; } } diff --git a/src/resources/views/show.blade.php b/src/resources/views/show.blade.php index 3a13ea6..dcdaeb7 100644 --- a/src/resources/views/show.blade.php +++ b/src/resources/views/show.blade.php @@ -48,16 +48,18 @@ @include('maia::show.info-tab') - @if ($job->state_id < $states['training-proposals']) - - - @else - - @include('maia::show.select-proposals-tab') - - - @include('maia::show.refine-proposals-tab') - + @if ($job->shouldUseNoveltyDetection()) + @if ($job->state_id < $states['training-proposals']) + + + @else + + @include('maia::show.select-proposals-tab') + + + @include('maia::show.refine-proposals-tab') + + @endif @endif @if ($job->state_id < $states['annotation-candidates']) diff --git a/src/resources/views/show/info-tab.blade.php b/src/resources/views/show/info-tab.blade.php index 46ce7d4..cd3cab8 100644 --- a/src/resources/views/show/info-tab.blade.php +++ b/src/resources/views/show/info-tab.blade.php @@ -54,11 +54,22 @@
- + + @if (Arr::has($job->params, 'oa_restrict_labels')) + + + + @else + + + + @endif +
- Used existing annotations + + Existing annotations
Restricted to label IDs: {{implode(', ', Arr::get($job->params, 'oa_restrict_labels', []))}}
Using all annotations of this volume.
@endif @if (Arr::has($job->params, 'is_train_scheme')) diff --git a/tests/Http/Controllers/Api/MaiaJobControllerTest.php b/tests/Http/Controllers/Api/MaiaJobControllerTest.php index 74fbb3a..e4112c4 100644 --- a/tests/Http/Controllers/Api/MaiaJobControllerTest.php +++ b/tests/Http/Controllers/Api/MaiaJobControllerTest.php @@ -157,6 +157,7 @@ public function testStoreUseExistingAnnotations() ->assertSuccessful(); $job = MaiaJob::first(); $this->assertTrue($job->shouldUseExistingAnnotations()); + $this->assertEquals(State::instanceSegmentationId(), $job->state_id); } public function testStoreRestrictLabels() diff --git a/tests/Jobs/UseExistingAnnotationsTest.php b/tests/Jobs/UseExistingAnnotationsTest.php index 9a4df9a..7693129 100644 --- a/tests/Jobs/UseExistingAnnotationsTest.php +++ b/tests/Jobs/UseExistingAnnotationsTest.php @@ -3,14 +3,16 @@ namespace Biigle\Tests\Modules\Maia\Jobs; use Biigle\Modules\Largo\Jobs\GenerateAnnotationPatch; +use Biigle\Modules\Maia\Events\MaiaJobContinued; use Biigle\Modules\Maia\Jobs\UseExistingAnnotations; use Biigle\Modules\Maia\MaiaJobState as State; +use Biigle\Modules\Maia\Notifications\InstanceSegmentationFailed; use Biigle\Modules\Maia\Notifications\NoveltyDetectionComplete; -use Biigle\Modules\Maia\Notifications\NoveltyDetectionFailed; use Biigle\Shape; use Biigle\Tests\ImageAnnotationLabelTest; use Biigle\Tests\ImageAnnotationTest; use Biigle\Tests\Modules\Maia\MaiaJobTest; +use Event; use Illuminate\Support\Facades\Notification; use Queue; use TestCase; @@ -19,26 +21,23 @@ class UseExistingAnnotationsTest extends TestCase { public function testHandle() { - // TODO: Update this job to make it completely independent of novelty detection. - // Instance segmentation should immediately proceed without the additional step - // of training proposal selection and refinement. - $this->markTestIncomplete(); $a = ImageAnnotationTest::create(['shape_id' => Shape::circleId()]); $al1 = ImageAnnotationLabelTest::create(['annotation_id' => $a->id]); $al2 = ImageAnnotationLabelTest::create(); $job = MaiaJobTest::create(['volume_id' => $a->image->volume_id]); + Event::fake(); Queue::fake(); (new UseExistingAnnotations($job))->handle(); - Queue::assertPushed(GenerateAnnotationPatch::class); - $this->assertEquals(1, $job->trainingProposals()->count()); + Event::assertDispatched(MaiaJobContinued::class); + Queue::assertNotPushed(GenerateAnnotationPatch::class); + $this->assertEquals(1, $job->trainingProposals()->selected()->count()); $proposal = $job->trainingProposals()->first(); $this->assertEquals($a->points, $proposal->points); } public function testHandleRestrictLabels() { - $this->markTestIncomplete('Its now oa_restrict_labels'); $a1 = ImageAnnotationTest::create(['shape_id' => Shape::circleId()]); $al1 = ImageAnnotationLabelTest::create(['annotation_id' => $a1->id]); // Create only one proposal even though the annotation has two matching labels. @@ -54,11 +53,11 @@ public function testHandleRestrictLabels() $job = MaiaJobTest::create([ 'volume_id' => $a1->image->volume_id, - 'params' => ['restrict_labels' => [$al1->label_id]], + 'params' => ['oa_restrict_labels' => [$al1->label_id]], ]); (new UseExistingAnnotations($job))->handle(); - $this->assertEquals(1, $job->trainingProposals()->count()); + $this->assertEquals(1, $job->trainingProposals()->selected()->count()); $proposal = $job->trainingProposals()->first(); $this->assertEquals($a1->points, $proposal->points); $this->assertNull($proposal->score); @@ -66,7 +65,6 @@ public function testHandleRestrictLabels() public function testHandleShapeConversion() { - $this->markTestIncomplete(); $a1 = ImageAnnotationTest::create([ 'shape_id' => Shape::pointId(), 'points' => [10, 20], @@ -99,7 +97,7 @@ public function testHandleShapeConversion() $job = MaiaJobTest::create(['volume_id' => $a1->image->volume_id]); (new UseExistingAnnotations($job))->handle(); - $this->assertEquals(5, $job->trainingProposals()->count()); + $this->assertEquals(5, $job->trainingProposals()->selected()->count()); $proposals = $job->trainingProposals; $this->assertEquals(Shape::circleId(), $proposals[0]->shape_id); @@ -119,57 +117,15 @@ public function testHandleShapeConversion() $this->assertEquals([10, 15, 11.18], $proposals[4]->points); } - public function testHandleSkipNd() - { - $this->markTestIncomplete(); - $a = ImageAnnotationTest::create(); - $job = MaiaJobTest::create([ - 'volume_id' => $a->image->volume_id, - 'params' => ['use_existing' => true, 'skip_nd' => true], - ]); - - Queue::fake(); - Notification::fake(); - (new UseExistingAnnotations($job))->handle(); - Queue::assertPushed(GenerateAnnotationPatch::class); - Notification::assertSentTo($job->user, NoveltyDetectionComplete::class); - $this->assertEquals(1, $job->trainingProposals()->count()); - $this->assertEquals(State::trainingProposalsId(), $job->fresh()->state_id); - } - - public function testHandleSkipNdAndRestrictLabels() - { - $this->markTestIncomplete(); - $al = ImageAnnotationLabelTest::create(); - $job = MaiaJobTest::create([ - 'volume_id' => $al->annotation->image->volume_id, - 'params' => [ - 'use_existing' => true, - 'skip_nd' => true, - 'restrict_labels' => [$al->label_id], - ], - ]); - - Queue::fake(); - Notification::fake(); - (new UseExistingAnnotations($job))->handle(); - $this->assertEquals(1, $job->trainingProposals()->count()); - } - public function testHandleSkipNdNoAnnotations() { - $this->markTestIncomplete(); - $job = MaiaJobTest::create([ - 'params' => ['use_existing' => true, 'skip_nd' => true], - ]); + $job = MaiaJobTest::create(); - Queue::fake(); Notification::fake(); (new UseExistingAnnotations($job))->handle(); - Queue::assertNotPushed(GenerateAnnotationPatch::class); - Notification::assertSentTo($job->user, NoveltyDetectionFailed::class); + Notification::assertSentTo($job->user, InstanceSegmentationFailed::class); $this->assertEquals(0, $job->trainingProposals()->count()); - $this->assertEquals(State::failedNoveltyDetectionId(), $job->fresh()->state_id); + $this->assertEquals(State::failedInstanceSegmentationId(), $job->fresh()->state_id); $this->assertNotEmpty($job->error['message']); } } diff --git a/tests/Listeners/DispatchNoveltyDetectionRequestTest.php b/tests/Listeners/DispatchNoveltyDetectionRequestTest.php index ba172b8..3bbe224 100644 --- a/tests/Listeners/DispatchNoveltyDetectionRequestTest.php +++ b/tests/Listeners/DispatchNoveltyDetectionRequestTest.php @@ -17,7 +17,7 @@ class DispatchNoveltyDetectionRequestTest extends TestCase { public function testHandle() { - $job = MaiaJobTest::create(); + $job = MaiaJobTest::create(['params' => ['training_data_method' => 'novelty_detection']]); $event = new MaiaJobCreated($job); $listener = new DispatchNoveltyDetectionRequest; @@ -28,23 +28,7 @@ public function testHandle() public function testHandleUseExisting() { - $job = MaiaJobTest::create(['params' => ['use_existing' => true]]); - $event = new MaiaJobCreated($job); - $listener = new DispatchNoveltyDetectionRequest; - - Queue::fake(); - Bus::fake(); - $listener->handle($event); - Queue::assertPushed(NoveltyDetectionRequest::class); - Bus::assertDispatched(UseExistingAnnotations::class); - } - - public function testHandleSkipNd() - { - $job = MaiaJobTest::create(['params' => [ - 'use_existing' => true, - 'skip_nd' => true, - ]]); + $job = MaiaJobTest::create(['params' => ['training_data_method' => 'own_annotations']]); $event = new MaiaJobCreated($job); $listener = new DispatchNoveltyDetectionRequest; diff --git a/tests/MaiaJobTest.php b/tests/MaiaJobTest.php index 189e260..6ec70e9 100644 --- a/tests/MaiaJobTest.php +++ b/tests/MaiaJobTest.php @@ -94,27 +94,21 @@ public function testDispatchesDeletingEvent() Event::assertDispatched(MaiaJobDeleting::class); } + public function testShouldUseNoveltyDetection() + { + $this->assertTrue($this->model->shouldUseNoveltyDetection()); + $this->model->params = ['training_data_method' => 'own_annotations']; + $this->assertFalse($this->model->shouldUseNoveltyDetection()); + $this->model->params = ['training_data_method' => 'novelty_detection']; + $this->assertTrue($this->model->shouldUseNoveltyDetection()); + } + public function testShouldUseExistingAnnotations() { $this->assertFalse($this->model->shouldUseExistingAnnotations()); - $this->model->params = ['use_existing' => false]; + $this->model->params = ['training_data_method' => 'novelty_detection']; $this->assertFalse($this->model->shouldUseExistingAnnotations()); - $this->model->params = ['use_existing' => true]; - $this->assertTrue($this->model->shouldUseExistingAnnotations()); - $this->model->params = ['use_existing' => "1"]; + $this->model->params = ['training_data_method' => 'own_annotations']; $this->assertTrue($this->model->shouldUseExistingAnnotations()); } - - public function testShouldSkipNoveltyDetection() - { - $this->assertFalse($this->model->shouldSkipNoveltyDetection()); - $this->model->params = ['skip_nd' => true]; - $this->assertFalse($this->model->shouldSkipNoveltyDetection()); - $this->model->params = ['skip_nd' => true, 'use_existing' => true]; - $this->assertTrue($this->model->shouldSkipNoveltyDetection()); - $this->model->params = ['skip_nd' => "1", 'use_existing' => true]; - $this->assertTrue($this->model->shouldSkipNoveltyDetection()); - $this->model->params = ['skip_nd' => false, 'use_existing' => true]; - $this->assertFalse($this->model->shouldSkipNoveltyDetection()); - } } From 1e7f89dd56bcd2c537af9e59f81926d22ea46b22 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Fri, 23 Oct 2020 08:49:15 +0200 Subject: [PATCH 07/22] Implement validation for existing annotations --- src/Http/Requests/StoreMaiaJob.php | 19 +++++++++++++ src/Jobs/UseExistingAnnotations.php | 10 ++----- src/Traits/QueriesExistingAnnotations.php | 25 +++++++++++++++++ .../Controllers/Api/MaiaJobControllerTest.php | 28 +++++++++++++++++++ 4 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 src/Traits/QueriesExistingAnnotations.php diff --git a/src/Http/Requests/StoreMaiaJob.php b/src/Http/Requests/StoreMaiaJob.php index 0b024dc..558248d 100644 --- a/src/Http/Requests/StoreMaiaJob.php +++ b/src/Http/Requests/StoreMaiaJob.php @@ -7,9 +7,12 @@ use Biigle\Modules\Maia\Rules\OddNumber; use Biigle\Volume; use Illuminate\Foundation\Http\FormRequest; +use Biigle\Modules\Maia\Traits\QueriesExistingAnnotations; class StoreMaiaJob extends FormRequest { + use QueriesExistingAnnotations; + /** * The volume to create the MAIA job for. * @@ -88,6 +91,22 @@ public function withValidator($validator) if ($this->input('training_data_method') === MaiaJob::TRAIN_NOVELTY_DETECTION && $this->volume->images()->count() < $this->input('nd_clusters')) { $validator->errors()->add('nd_clusters', 'The number of image clusters must not be greater than the number of images in the volume.'); } + + if ($this->input('training_data_method') === MaiaJob::TRAIN_OWN_ANNOTATIONS && $this->hasNoExistingAnnotations()) { + $validator->errors()->add('training_data_method', 'There are no existing annotations (with the chosen labels) in this volume.'); + } }); } + + /** + * Determine if there are existing annotations that can be used as training data. + * + * @return boolean + */ + protected function hasNoExistingAnnotations() + { + $restrictLabels = $this->input('oa_restrict_labels', []); + + return !$this->getExistingAnnotationsQuery($this->volume->id, $restrictLabels)->exists(); + } } diff --git a/src/Jobs/UseExistingAnnotations.php b/src/Jobs/UseExistingAnnotations.php index 5f442d9..e070b38 100644 --- a/src/Jobs/UseExistingAnnotations.php +++ b/src/Jobs/UseExistingAnnotations.php @@ -7,6 +7,7 @@ use Biigle\Jobs\Job; use Biigle\Modules\Maia\Events\MaiaJobContinued; use Biigle\Modules\Maia\MaiaJob; +use Biigle\Modules\Maia\Traits\QueriesExistingAnnotations; use Biigle\Modules\Maia\MaiaJobState as State; use Biigle\Modules\Maia\Notifications\InstanceSegmentationFailed; use Biigle\Modules\Maia\TrainingProposal; @@ -16,7 +17,7 @@ class UseExistingAnnotations extends Job { - use SerializesModels; + use SerializesModels, QueriesExistingAnnotations; /** * The job to use existing annotations for. @@ -71,12 +72,7 @@ protected function getAnnotationsQuery() { $restrictLabels = Arr::get($this->job->params, 'oa_restrict_labels', []); - return ImageAnnotation::join('images', 'image_annotations.image_id', '=', 'images.id') - ->where('images.volume_id', $this->job->volume_id) - ->when(!empty($restrictLabels), function ($query) use ($restrictLabels) { - return $query->join('image_annotation_labels', 'image_annotation_labels.annotation_id', '=', 'image_annotations.id') - ->whereIn('image_annotation_labels.label_id', $restrictLabels); - }); + return $this->getExistingAnnotationsQuery($this->job->volume_id, $restrictLabels); } /** diff --git a/src/Traits/QueriesExistingAnnotations.php b/src/Traits/QueriesExistingAnnotations.php new file mode 100644 index 0000000..f326c98 --- /dev/null +++ b/src/Traits/QueriesExistingAnnotations.php @@ -0,0 +1,25 @@ +where('images.volume_id', $volumeId) + ->when(!empty($restrictLabels), function ($query) use ($restrictLabels) { + return $query->join('image_annotation_labels', 'image_annotation_labels.annotation_id', '=', 'image_annotations.id') + ->whereIn('image_annotation_labels.label_id', $restrictLabels); + }); + } +} diff --git a/tests/Http/Controllers/Api/MaiaJobControllerTest.php b/tests/Http/Controllers/Api/MaiaJobControllerTest.php index e4112c4..46b2c06 100644 --- a/tests/Http/Controllers/Api/MaiaJobControllerTest.php +++ b/tests/Http/Controllers/Api/MaiaJobControllerTest.php @@ -7,6 +7,8 @@ use Biigle\Modules\Maia\Jobs\InstanceSegmentationRequest; use Biigle\Modules\Maia\MaiaJob; use Biigle\Modules\Maia\MaiaJobState as State; +use Biigle\Tests\ImageAnnotationLabelTest; +use Biigle\Tests\ImageAnnotationTest; use Biigle\Tests\ImageTest; use Biigle\Tests\Modules\Maia\MaiaJobTest; use Biigle\Tests\Modules\Maia\TrainingProposalTest; @@ -153,6 +155,17 @@ public function testStoreUseExistingAnnotations() ['layers' => 'all', 'epochs' => 10, 'learning_rate' => 0.0001], ], ]; + $this->postJson("/api/v1/volumes/{$id}/maia-jobs", $params) + // No existing annotations. + ->assertStatus(422); + + ImageAnnotationTest::create([ + 'image_id' => ImageTest::create([ + 'volume_id' => $this->volume()->id, + 'filename' => 'abc.jpg', + ])->id, + ]); + $this->postJson("/api/v1/volumes/{$id}/maia-jobs", $params) ->assertSuccessful(); $job = MaiaJob::first(); @@ -183,6 +196,21 @@ public function testStoreRestrictLabels() // Must contain valid label IDs. ->assertStatus(422); + ImageAnnotationLabelTest::create([ + 'label_id' => $this->labelChild()->id, + 'annotation_id' => ImageAnnotationTest::create([ + 'image_id' => ImageTest::create([ + 'volume_id' => $this->volume()->id, + 'filename' => 'abc.jpg', + ])->id, + ])->id, + ]); + + $params['oa_restrict_labels'] = [$this->labelRoot()->id]; + $this->postJson("/api/v1/volumes/{$id}/maia-jobs", $params) + // No annotations with the chosen label. + ->assertStatus(422); + $params['oa_restrict_labels'] = [$this->labelChild()->id]; $this->postJson("/api/v1/volumes/{$id}/maia-jobs", $params) ->assertSuccessful(); From 36e9ff82b47d345c639a902e237872ac74ae210f Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Fri, 23 Oct 2020 11:27:28 +0200 Subject: [PATCH 08/22] Update the manual articles for multiple methods for training data --- src/resources/views/index/job-form.blade.php | 8 ++-- .../views/manual/tutorials/about.blade.php | 28 ++++++++----- .../tutorials/annotation-candidates.blade.php | 16 ++++---- .../tutorials/existing-annotations.blade.php | 40 +++++++++++++++++++ .../tutorials/instance-segmentation.blade.php | 12 +++--- .../tutorials/knowledge-transfer.blade.php | 26 ++++++++++++ .../tutorials/novelty-detection.blade.php | 36 +++++------------ .../tutorials/training-proposals.blade.php | 12 +++--- src/resources/views/manualTutorial.blade.php | 22 ++++++++-- 9 files changed, 137 insertions(+), 63 deletions(-) create mode 100644 src/resources/views/manual/tutorials/existing-annotations.blade.php create mode 100644 src/resources/views/manual/tutorials/knowledge-transfer.blade.php diff --git a/src/resources/views/index/job-form.blade.php b/src/resources/views/index/job-form.blade.php index 18be8c9..e1eb1f4 100644 --- a/src/resources/views/index/job-form.blade.php +++ b/src/resources/views/index/job-form.blade.php @@ -14,7 +14,7 @@ Novelty detection
- Novelty detection attempts to find objects of interest in images where the background (e.g. sea floor) is rather uniform. It does not require prior annotations but has many parameters that can be tuned. + Novelty detection attempts to find objects of interest in images where the background (e.g. sea floor) is rather uniform. It does not require prior annotations but has many parameters that can be tuned. More information.
@@ -27,7 +27,7 @@ There are no existing annotations. Please choose another method.
- This method uses existing annotations in this volume as training data. All annotations will be converted to circles. + This method uses existing annotations in this volume as training data. All annotations will be converted to circles. More information.
@@ -36,7 +36,7 @@ Knowledge transfer Coming soon
- Knowlegde transfer uses existing annotations of another volume as training data. The images should be very similar to the ones of this volume and the classes of objects of interest should be the same. This method requires that the distance to ground metadata is present for every image of the two volumes. + Knowlegde transfer uses existing annotations of another volume as training data. The images should be very similar to the ones of this volume and the classes of objects of interest should be the same. This method requires that the distance to ground metadata is present for every image of the two volumes. More information.
@@ -145,7 +145,7 @@
- Existing Annotations + Existing Annotations
diff --git a/src/resources/views/manual/tutorials/about.blade.php b/src/resources/views/manual/tutorials/about.blade.php index 69c5cfe..0e1428b 100644 --- a/src/resources/views/manual/tutorials/about.blade.php +++ b/src/resources/views/manual/tutorials/about.blade.php @@ -22,11 +22,15 @@

- In the third step (instance segmentation), the manually filtered set of training proposals is used to train a machine learning model for the automatic detection of the selected interesting objects. The model is highly specialized for this task and can usually detect most (if not all) instances of the interesting objects in the images. In the tests reported by the MAIA paper, 84% of the interesting objects were detected on average [1]. The detections are passed on as "annotation candidates" to the fourth step. + In addition to the novelty detection of the original MAIA method, BIIGLE offers alternative ways to obtain training proposals. Read more in the articles to use existing annotations or knowledge transfer.

- As with the training proposals, the annotation candidates can contain detections that are not actually interesting objects. In addition, the machine learning model only detects the objects and does not attempt to automatically assign labels to them. In the fourth step, the annotation candidates are again manually filtered to select only the actually interesting objects. Furthermore, labels are manually attached to the selected candidates which are subsequently transformed to actual annotations. + In the third step (instance segmentation), the manually filtered or automatically obtained set of training proposals is used to train a machine learning model for the automatic detection of the selected interesting objects. The model is highly specialized for this task and can usually detect most (if not all) instances of the interesting objects in the images. In the tests reported by the MAIA paper, 84% of the interesting objects were detected on average [1]. The detections are passed on as "annotation candidates" to the fourth step. +

+ +

+ As with the training proposals of the novelty detection, the annotation candidates can contain detections that are not actually interesting objects. In addition, the machine learning model only detects the objects and does not attempt to automatically assign labels to them. In the fourth step, the annotation candidates are again manually filtered to select only the actually interesting objects. Furthermore, labels are manually attached to the selected candidates which are subsequently transformed to actual annotations.

@@ -39,28 +43,30 @@ To create new annotations with MAIA in BIIGLE, project editors, experts or admins can start a new MAIA "job" for a volume of a project. To start a new MAIA job, click on the button in the sidebar of the volume overview. This will open up the MAIA overview for the volume, which lists any running or finished jobs, as well as a form to create a new MAIA job for the volume. New jobs can only be created when no other job is currently running for the volume.

- The form to create a new MAIA job initially shows only the parameters that are most likely to be modified for each job. To show all available parameters, click on the button below the form. There are quite a lot parameters that can be configured for a MAIA job. Although sensible defaults are set, a careful configuration may be crucial for a good quality of the resulting annotations. You can read more on the configuration parameters of the novelty detection stage and those of the instance segmentation stage in the respective articles. + The form to create a new MAIA job presents you a choice between several methods to obtain training data (training proposals). Choose one that best fits to your use case. The form initially shows only the parameters that are most likely to be modified for each job. To show all available parameters, click on the button below the form. There can be quite a lot parameters that can be configured for a MAIA job. Although sensible defaults are set, a careful configuration may be crucial for a good quality of the resulting annotations. You can read more on the configuration parameters for novelty detection, existing annotations, knowledge transfer and instance segmentation in the respective articles.

- A MAIA job can run for many hours or even a day. Please choose your parameters carefully before you submit a new job. + A MAIA job can run for many hours or even a day. Please choose your settings carefully before you submit a new job.

- A MAIA job runs through the four consecutive stages outlined above. The first and the third stages perform automatic processing of the images. The second and the fourth stages require manual interaction by you. Once you have created a new job, it will immediately start the automatic processing of the first stage. BIIGLE will notify you when this stage is finished and the job is waiting for your manual interaction in the second stage. In the same way you are notified when the automatic processing of the third stage is finished. You can change the way you receive new MAIA notifications in the notification settings of your user account. + If novelty detection is chosen as the method to obtain training data, a MAIA job runs through the four consecutive stages outlined above. The first and the third stages perform automatic processing of the images. The second and the fourth stages require manual interaction by you. Once you have created a new job, it will immediately start the automatic processing of the first stage. BIIGLE will notify you when this stage is finished and the job is waiting for your manual interaction in the second stage. In the same way you are notified when the automatic processing of the third stage is finished. If existing annotations or knowledge transfer were chosen as the method to obtain training data, the job will directly proceed with the third stage, skipping the first two. You can change the way you receive new MAIA notifications in the notification settings of your user account.

- The overview page of a MAIA job shows a main content area and a sidebar with five different tabs. The first tab shows general information about the job, including all the parameters that were used. The second and third tabs belong to the training proposals stage and are enabled once the job progresses to this stage. The fourth and fifth tabs belong to the annotation candidates stage and are enabled once the job progresses to this stage. + The overview page of a MAIA job shows a main content area and a sidebar with multiple tabs. The first tab shows general information about the job, including all the parameters that were used. The second and third tabs belong to the training proposals stage and are enabled once the job progresses to this stage. These tabs are visible only if novelty detection was chosen as the method to obtain training data. The fourth and fifth tabs belong to the annotation candidates stage and are enabled once the job progresses to this stage.

- Continue reading about MAIA in the articles about the individual stages. You can start with the first stage: novelty detection. + Continue reading about MAIA in the articles about the methods to obtain training data. You can start with the first method: novelty detection.

Further reading

diff --git a/src/resources/views/manual/tutorials/annotation-candidates.blade.php b/src/resources/views/manual/tutorials/annotation-candidates.blade.php index cd8f19c..a66038c 100644 --- a/src/resources/views/manual/tutorials/annotation-candidates.blade.php +++ b/src/resources/views/manual/tutorials/annotation-candidates.blade.php @@ -5,17 +5,17 @@ @section('manual-content')

- A description of the last MAIA stage. + Reviewing the annotation candidates from instance segmentation.

- A MAIA job is finished when it proceeded from the instance segmentation to the annotation candidates stage. In this stage you can review the annotation candidates that were generated in the previous stage and convert them to real annotations. Similar to the training proposals stage, this is done in two steps the selection of annotation candidates and the refinement of annotation candidates. + A MAIA job is finished when it proceeded from the instance segmentation to the annotation candidates stage. In this stage you can review the annotation candidates that were generated in the previous stage and convert them to real annotations. Similar to the training proposals stage of novelty detection, this is done in two steps the selection of annotation candidates and the refinement of annotation candidates.

Selection of annotation candidates

- The selection of annotation candidates is very similar to the selection of training proposals. When you open the  select annotation candidates tab in the sidebar, the annotation candidates are loaded and their image thumbnails are displayed in a regular grid. Please refer to the respective manual article to learn how to interact with the thumbnail grid. + The selection of annotation candidates is very similar to the selection of training proposals of the novelty detection method. When you open the  select annotation candidates tab in the sidebar, the annotation candidates are loaded and their image thumbnails are displayed in a regular grid. Please refer to the respective manual article to learn how to interact with the thumbnail grid.

@@ -39,7 +39,7 @@

Refinement of annotation candidates

- The refinement step for annotation candidates is also very similar to the refinement step for training proposals. You cycle through all selected annotation candidates and modify the circle of each annotation candidate to fit to the object or region that it should mark. Please refer to the respective manual article to learn how to interact with the refinement tool. + The refinement step for annotation candidates is also very similar to the refinement step for training proposals of the novelty detection method. You cycle through all selected annotation candidates and modify the circle of each annotation candidate to fit to the object or region that it should mark. Please refer to the respective manual article to learn how to interact with the refinement tool.

@@ -67,9 +67,11 @@

Further reading

@endsection diff --git a/src/resources/views/manual/tutorials/existing-annotations.blade.php b/src/resources/views/manual/tutorials/existing-annotations.blade.php new file mode 100644 index 0000000..47a9051 --- /dev/null +++ b/src/resources/views/manual/tutorials/existing-annotations.blade.php @@ -0,0 +1,40 @@ +@extends('manual.base') + +@section('manual-title', 'Existing annotations') + +@section('manual-content') +
+

+ Using existing annotations to obtain training data. +

+

+ This method allows you to choose existing annotations in the same volume as training data for the instance segmentation stage. All annotations will be converted to circles and the new MAIA job will immediately proceed to the instance segmentation stage. +

+ +

Configurable parameters

+ +

+ To show the configurable parameters, click on the button below the form. +

+ +

Restrict to labels

+ +

+ By default, all annotations are used. +

+ +

+ Use the input field to select one or more labels. If present, only annotations with the chosen labels are used as training data for the MAIA job. If no labels are chosen, all annotations are used. +

+ +

Further reading

+ +
+@endsection diff --git a/src/resources/views/manual/tutorials/instance-segmentation.blade.php b/src/resources/views/manual/tutorials/instance-segmentation.blade.php index 105673c..e5525d6 100644 --- a/src/resources/views/manual/tutorials/instance-segmentation.blade.php +++ b/src/resources/views/manual/tutorials/instance-segmentation.blade.php @@ -5,10 +5,10 @@ @section('manual-content')

- A description of the third MAIA stage and the configurable parameters. + The automatic instance segmentation.

- The third stage of a MAIA job processes all images of a volume with a supervised instance segmentation method (Mask R-CNN). This method uses the training proposals that you have selected and refined in the previous stage of the MAIA job to learn a model for what you determined to be interesting objects or regions in the images. The instance segmentation method produces a set of "annotation candidates", which are image regions that the method found to be interesting based on your provided training proposals. When the instance segmentation is finished, the MAIA job will continue to the next stage in which you can manually review the annotation candidates. + The third stage of a MAIA job processes all images of a volume with a supervised instance segmentation method (Mask R-CNN). This method uses the training proposals that were obtained with one of the three methods (novelty detection, existing annotations or knowledge transfer) to learn a model for what you determined to be interesting objects or regions in the images. The instance segmentation method produces a set of "annotation candidates", which are image regions that the method found to be interesting based on your provided training proposals. When the instance segmentation is finished, the MAIA job will continue to the next stage in which you can manually review the annotation candidates.

@@ -34,9 +34,11 @@

Further reading

diff --git a/src/resources/views/manual/tutorials/knowledge-transfer.blade.php b/src/resources/views/manual/tutorials/knowledge-transfer.blade.php new file mode 100644 index 0000000..8a7c25f --- /dev/null +++ b/src/resources/views/manual/tutorials/knowledge-transfer.blade.php @@ -0,0 +1,26 @@ +@extends('manual.base') + +@section('manual-title', 'Knowledge transfer') + +@section('manual-content') + +@endsection diff --git a/src/resources/views/manual/tutorials/novelty-detection.blade.php b/src/resources/views/manual/tutorials/novelty-detection.blade.php index ca86112..6edb9a8 100644 --- a/src/resources/views/manual/tutorials/novelty-detection.blade.php +++ b/src/resources/views/manual/tutorials/novelty-detection.blade.php @@ -5,13 +5,13 @@ @section('manual-content')

- A description of the first MAIA stage and the configurable parameters. + Using novelty detection to obtain training data.

- The first stage of a MAIA job processes all images of a volume with an unsupervised novelty detection method. The novelty detection method attempts to find "interesting" objects or regions in the images, which are called "training proposals". The novelty detection acts without any prior knowledge of what is actually defined as interesting by you or anyone who wants to explore the images. Hence, the quality or meaningfulness of the training proposals may vary dramatically, depending on the images themselves and on what you are looking for. + This method to obtain training data processes all images of a volume with an unsupervised novelty detection method. The novelty detection method attempts to find "interesting" objects or regions in the images, which are called "training proposals". The novelty detection acts without any prior knowledge of what is actually defined as interesting by you or anyone who wants to explore the images. Hence, the quality or meaningfulness of the training proposals may vary dramatically, depending on the images themselves and on what you are looking for.

- To make the novelty detection as flexible as possible, there are many parameters that can be configured before a new MAIA job is submitted. You might have to try out a few parameter combinations before the novelty detection produces meaningful training proposals. In cases where the novelty detection produces too few meaningful training proposals or does not work at all, you can augment the training proposals with your own annotations or skip the novelty detection altogether. + To make the novelty detection as flexible as possible, there are many parameters that can be configured before a new MAIA job is submitted. You might have to try out a few parameter combinations before the novelty detection produces meaningful training proposals. In cases where the novelty detection produces too few meaningful training proposals or does not work at all, you can try one of the other methods to obtain training data: existing annotations or knowledge transfer.

@@ -21,7 +21,7 @@

Configurable parameters

- By default only the two parameters for the novelty detection are shown, that are the most likely to be modified for each new job. To show all configurable parameters, click on the button below the form. + By default only the one parameter for the novelty detection is shown, that is the most likely to be modified for each new job. To show all configurable parameters, click on the button below the form.

Number of image clusters

@@ -144,32 +144,14 @@ In the MAIA paper [1] no training proposals were ignored which is equivalent to an ignore radius of 0.

-

Use existing annotations

- -

- If you already have existing annotations for the volume or the novelty detection does not produce (enough) meaningful training proposals, you can select the checkbox "Use existing annotations" before you submit a new MAIA job. When this checkbox is checked, the existing annotations are also presented as training proposals in the second MAIA stage. -

- -

- If the checkbox is selected, a new input appears that allows you to limit the used existing annotations to one or more labels. This way you can select only those annotations that make sense as training proposals. If you do not choose any label, all existing annotations are used as training proposals. -

- -

Skip novelty detection

- -

- If you chose to use existing annotations as training proposals, a new checkbox to "Skip novelty detection" appears. Select this checkbox if you do not want to run the novelty detection at all and only want to use the existing annotations as training proposals. -

- -

- Please note that the new MAIA job is shown as "running novelty detection" in the job overview page even if you chose to skip novelty detection. Just refresh the job overview page after a few seconds and the job should have proceeded to the second MAIA stage. -

-

Further reading

diff --git a/src/resources/views/manual/tutorials/training-proposals.blade.php b/src/resources/views/manual/tutorials/training-proposals.blade.php index 1499bb8..2e51c93 100644 --- a/src/resources/views/manual/tutorials/training-proposals.blade.php +++ b/src/resources/views/manual/tutorials/training-proposals.blade.php @@ -5,7 +5,7 @@ @section('manual-content')

- A description of the second MAIA stage. + Reviewing the training proposals from novelty detection.

When one of your MAIA jobs proceeds from novelty detection to the training proposals stage, you will get a notification from BIIGLE. In this stage, the MAIA job requires manual interaction from you before it can proceed to the next stage. This is done in two steps, the selection of training proposals and the refinement of training proposals. @@ -127,12 +127,14 @@

When the training proposals have been submitted, the MAIA job automatically proceeds to the instance segmentation stage.

-

Further reading

+

Further reading

@endsection diff --git a/src/resources/views/manualTutorial.blade.php b/src/resources/views/manualTutorial.blade.php index 0d732ae..952a18f 100644 --- a/src/resources/views/manualTutorial.blade.php +++ b/src/resources/views/manualTutorial.blade.php @@ -10,26 +10,40 @@ Novelty detection

- A description of the first MAIA stage and the configurable parameters. + Using novelty detection to obtain training data. +

+ +

+ Existing annotations +

+

+ Using existing annotations to obtain training data. +

+ +

+ Knowledge transfer +

+

+ Using knowledge transfer to obtain training data.

Training proposals

- A description of the second MAIA stage. + Reviewing the training proposals from novelty detection.

Instance segmentation

- A description of the third MAIA stage and the configurable parameters. + The automatic instance segmentation.

Annotation candidates

- A description of the last MAIA stage. + Reviewing the annotation candidates from instance segmentation.

From 3c786cdd143535fbe5b16df8960e41a19c2e9ab8 Mon Sep 17 00:00:00 2001 From: Martin Zurowietz Date: Fri, 23 Oct 2020 14:49:14 +0200 Subject: [PATCH 09/22] Implement the selection of a volume for knowledge transfer --- .../Api/KnowledgeTransferVolumeController.php | 57 +++++++++++++++++ .../Controllers/Api/MaiaJobController.php | 25 +++++--- .../Controllers/Views/MaiaJobController.php | 13 +++- src/Http/Requests/StoreMaiaJob.php | 12 +++- src/Http/routes.php | 2 + src/MaiaJob.php | 15 +++++ src/Rules/KnowledgeTransferVolume.php | 41 +++++++++++++ src/public/assets/scripts/main.js | 2 +- src/public/mix-manifest.json | 2 +- .../assets/js/api/knowledgeTransferVolume.js | 6 ++ src/resources/assets/js/form.vue | 47 ++++++++++++-- src/resources/views/index/job-form.blade.php | 43 +++++++++---- .../KnowledgeTransferVolumeControllerTest.php | 45 ++++++++++++++ .../Controllers/Api/MaiaJobControllerTest.php | 61 +++++++++++++++++++ tests/MaiaJobTest.php | 9 +++ 15 files changed, 347 insertions(+), 33 deletions(-) create mode 100644 src/Http/Controllers/Api/KnowledgeTransferVolumeController.php create mode 100644 src/Rules/KnowledgeTransferVolume.php create mode 100644 src/resources/assets/js/api/knowledgeTransferVolume.js create mode 100644 tests/Http/Controllers/Api/KnowledgeTransferVolumeControllerTest.php diff --git a/src/Http/Controllers/Api/KnowledgeTransferVolumeController.php b/src/Http/Controllers/Api/KnowledgeTransferVolumeController.php new file mode 100644 index 0000000..719e8fe --- /dev/null +++ b/src/Http/Controllers/Api/KnowledgeTransferVolumeController.php @@ -0,0 +1,57 @@ +user()) + ->select('id', 'name') + ->whereHas('images') + ->whereNotExists(function ($query) { + $query->select(DB::raw(1)) + ->from('images') + ->whereRaw('images.volume_id = volumes.id') + ->whereNull('attrs->metadata->distance_to_ground'); + }) + ->with(['projects' => function ($query) { + $query->select('id', 'name'); + }]) + ->get() + ->each(function ($volume) { + $volume->setHidden(['doi', 'video_link', 'gis_link']); + }); + } +} diff --git a/src/Http/Controllers/Api/MaiaJobController.php b/src/Http/Controllers/Api/MaiaJobController.php index bc67dd0..995d497 100644 --- a/src/Http/Controllers/Api/MaiaJobController.php +++ b/src/Http/Controllers/Api/MaiaJobController.php @@ -23,7 +23,8 @@ class MaiaJobController extends Controller * * @apiParam {Number} id The volume ID. * - * @apiParam (Required parameters) {string} training_data_method One of `novelty_detection` (to perform novelty detection to generate training data) or `own_annotations` (to use existing annotations of the same volume as training data). + * @apiParam (Required parameters) {string} training_data_method One of `novelty_detection` (to perform novelty detection to generate training data), `own_annotations` (to use existing annotations of the same volume as training data) or `knowledge_transfer` (to use knowlegde transfer to get training data from another volume). + * @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 (Required parameters for novelty detection) {number} nd_clusters Number of different kinds of images to expect. Images are of the same kind if they have similar lighting conditions or show similar patterns (e.g. sea floor, habitat types). Increase this number if you expect many different kinds of images. Lower the number to 1 if you have very few images and/or the content is largely uniform. * @apiParam (Required parameters for novelty detection) {number} nd_patch_size Size in pixels of the image patches used determine the training proposals. Increase the size if the images contain larger objects of interest, decrease the size if the objects are smaller. Larger patch sizes take longer to compute. Must be an odd number. @@ -34,10 +35,11 @@ class MaiaJobController extends Controller * @apiParam (Required parameters for novelty detection) {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 for novelty detection) {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) {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 for existing annotations) {Array} oa_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 (Required parameters for knowledge transfer) {number} kt_volume_id The ID of the volume from which to get the annotations for knowledge transfer. + * * @param StoreMaiaJob $request * @return \Illuminate\Http\Response */ @@ -52,10 +54,19 @@ public function store(StoreMaiaJob $request) 'is_train_scheme', ]; - if ($request->input('training_data_method') === MaiaJob::TRAIN_NOVELTY_DETECTION) { + if ($request->input('training_data_method') === MaiaJob::TRAIN_OWN_ANNOTATIONS) { + $job->state_id = State::instanceSegmentationId(); + $paramKeys = array_merge($paramKeys, [ + 'oa_restrict_labels', + ]); + } else if ($request->input('training_data_method') === MaiaJob::TRAIN_KNOWLEDGE_TRANSFER) { + $job->state_id = State::instanceSegmentationId(); + $paramKeys = array_merge($paramKeys, [ + 'kt_volume_id', + ]); + } else { $job->state_id = State::noveltyDetectionId(); $paramKeys = array_merge($paramKeys, [ - // nd_* are parameters for novelty detection. 'nd_clusters', 'nd_patch_size', 'nd_threshold', @@ -65,12 +76,6 @@ public function store(StoreMaiaJob $request) 'nd_stride', 'nd_ignore_radius', ]); - } else if ($request->input('training_data_method') === MaiaJob::TRAIN_OWN_ANNOTATIONS) { - $job->state_id = State::instanceSegmentationId(); - $paramKeys = array_merge($paramKeys, [ - // oa_* are parameters for own annotations. - 'oa_restrict_labels', - ]); } $job->params = $request->only($paramKeys); diff --git a/src/Http/Controllers/Views/MaiaJobController.php b/src/Http/Controllers/Views/MaiaJobController.php index 2dd58db..b7df2f9 100644 --- a/src/Http/Controllers/Views/MaiaJobController.php +++ b/src/Http/Controllers/Views/MaiaJobController.php @@ -3,6 +3,7 @@ namespace Biigle\Modules\Maia\Http\Controllers\Views; use Biigle\Http\Controllers\Views\Controller; +use Biigle\ImageAnnotation; use Biigle\LabelTree; use Biigle\Modules\Maia\MaiaJob; use Biigle\Modules\Maia\MaiaJobState as State; @@ -61,13 +62,23 @@ public function index($id) ['layers' => 'all', 'epochs' => 10, 'learning_rate' => 0.00001], ]); + $canUseExistingAnnotations = ImageAnnotation::join('images', 'images.id', '=', 'image_annotations.image_id') + ->where('images.volume_id', $volume->id) + ->exists(); + + $canUseKnowledgeTransfer = !$volume->images() + ->whereNull('attrs->metadata->distance_to_ground') + ->exists(); + return view('maia::index', compact( 'volume', 'jobs', 'hasJobsInProgress', 'hasJobsRunning', 'newestJobHasFailed', - 'defaultTrainScheme' + 'defaultTrainScheme', + 'canUseExistingAnnotations', + 'canUseKnowledgeTransfer' )); } diff --git a/src/Http/Requests/StoreMaiaJob.php b/src/Http/Requests/StoreMaiaJob.php index 558248d..7034c2e 100644 --- a/src/Http/Requests/StoreMaiaJob.php +++ b/src/Http/Requests/StoreMaiaJob.php @@ -4,6 +4,7 @@ use Biigle\Modules\Maia\MaiaJob; use Biigle\Modules\Maia\MaiaJobState as State; +use Biigle\Modules\Maia\Rules\KnowledgeTransferVolume; use Biigle\Modules\Maia\Rules\OddNumber; use Biigle\Volume; use Illuminate\Foundation\Http\FormRequest; @@ -40,9 +41,8 @@ public function authorize() public function rules() { return [ - 'training_data_method' => 'required|in:novelty_detection,own_annotations', - 'oa_restrict_labels' => 'array', - 'oa_restrict_labels.*' => 'integer|exists:labels,id', + 'training_data_method' => 'required|in:novelty_detection,own_annotations,knowledge_transfer', + 'nd_clusters' => 'required_if:training_data_method,novelty_detection|integer|min:1|max:100', 'nd_patch_size' => ['required_if:training_data_method,novelty_detection', 'integer', 'min:3', 'max:99', new OddNumber], 'nd_threshold' => 'required_if:training_data_method,novelty_detection|integer|min:0|max:99', @@ -51,6 +51,12 @@ public function rules() 'nd_epochs' => 'required_if:training_data_method,novelty_detection|integer|min:50|max:1000', 'nd_stride' => 'required_if:training_data_method,novelty_detection|integer|min:1|max:10', 'nd_ignore_radius' => 'required_if:training_data_method,novelty_detection|integer|min:0', + + 'oa_restrict_labels' => 'array', + 'oa_restrict_labels.*' => 'integer|exists:labels,id', + + 'kt_volume_id' => ['required_if:training_data_method,knowledge_transfer', 'integer', 'exists:volumes,id', new KnowledgeTransferVolume], + 'is_train_scheme' => 'required|array|min:1', 'is_train_scheme.*' => 'array', 'is_train_scheme.*.layers' => 'required|in:heads,all', diff --git a/src/Http/routes.php b/src/Http/routes.php index 5424c53..f72f454 100644 --- a/src/Http/routes.php +++ b/src/Http/routes.php @@ -40,4 +40,6 @@ $router->get('maia-jobs/{id}/images/{id2}/training-proposals', 'MaiaJobImagesController@indexTrainingProposals'); $router->get('maia-jobs/{id}/images/{id2}/annotation-candidates', 'MaiaJobImagesController@indexAnnotationCandidates'); + + $router->get('volumes/filter/knowledge-transfer', 'KnowledgeTransferVolumeController@index'); }); diff --git a/src/MaiaJob.php b/src/MaiaJob.php index 18229e1..9be6a1e 100644 --- a/src/MaiaJob.php +++ b/src/MaiaJob.php @@ -23,6 +23,11 @@ class MaiaJob extends Model */ const TRAIN_OWN_ANNOTATIONS = 'own_annotations'; + /** + * @var string + */ + const TRAIN_KNOWLEDGE_TRANSFER = 'knowledge_transfer'; + /** * The attributes that should be casted to native types. * @@ -184,4 +189,14 @@ public function shouldUseNoveltyDetection() // Handle fallback where old jobs don't have a training_data_method yet. return $this->getJsonAttr('params.training_data_method') === self::TRAIN_NOVELTY_DETECTION || $this->getJsonAttr('params.training_data_method') === null; } + + /** + * Determine if this job should use knowledge transfer to get training data. + * + * @return bool + */ + public function shouldUseKnowledgeTransfer() + { + return $this->getJsonAttr('params.training_data_method') === self::TRAIN_KNOWLEDGE_TRANSFER; + } } diff --git a/src/Rules/KnowledgeTransferVolume.php b/src/Rules/KnowledgeTransferVolume.php new file mode 100644 index 0000000..017e360 --- /dev/null +++ b/src/Rules/KnowledgeTransferVolume.php @@ -0,0 +1,41 @@ +user()) + ->where('id', $value) + ->whereHas('images') + ->whereNotExists(function ($query) { + $query->select(DB::raw(1)) + ->from('images') + ->whereRaw('images.volume_id = volumes.id') + ->whereNull('attrs->metadata->distance_to_ground'); + }) + ->exists(); + } + + /** + * Get the validation error message. + * + * @return string + */ + public function message() + { + return 'The :attribute is not suited for knowledge transfer. You must be authorized to access the volume and all images must have distance to ground information.'; + } +} diff --git a/src/public/assets/scripts/main.js b/src/public/assets/scripts/main.js index 1f5a6ec..93ee63a 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,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,showAdvanced:!1,shouldFetchLabels:!1,labels:[],selectedLabels:[],submitted:!1,trainScheme:[],trainingDataMethod:""}},computed:{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},useExistingAnnotations:function(){return"own_annotations"===this.trainingDataMethod},useNoveltyDetection:function(){return"novelty_detection"===this.trainingDataMethod}},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.trainScheme=biigle.$require("maia.trainScheme"),this.showAdvanced=biigle.$require("maia.hasErrors"),this.trainingDataMethod=biigle.$require("maia.trainingDataMethod"),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",R="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]=K,++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 kt,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)}),K)}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 Ne({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 Ne({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 +!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 m(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,l,d,p,f,g,m,_,v;for(h=l=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,m=void 0,_=void 0,v=void 0,g=Math.max(p.minX,f.minX),m=Math.max(p.minY,f.minY),_=Math.min(p.maxX,f.maxX),v=Math.min(p.maxY,f.maxY),r=Math.max(0,_-g)*Math.max(0,v-m),u=c(o)+c(s),r=e;o--)s=t.children[o],u(l,t.leaf?r(s):s),c+=d(l);return c},_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=Vue.resource("api/v1/volumes/filter/knowledge-transfer"),o=biigle.$require("annotations.components.annotationCanvas"),s=biigle.$require("largo.mixins.annotationPatch"),r=biigle.$require("annotations.ol.AttachLabelInteraction"),a=biigle.$require("messages").handleErrorResponse,u=biigle.$require("volumes.components.imageGrid"),h=biigle.$require("volumes.components.imageGridImage"),l=biigle.$require("annotations.stores.images"),c=biigle.$require("keyboard"),d=biigle.$require("labelTrees.components.labelTrees"),p=biigle.$require("labelTrees.components.labelTypeahead"),f=biigle.$require("core.mixins.loader"),g=biigle.$require("messages"),m=biigle.$require("core.components.sidebar"),_=biigle.$require("core.components.sidebarTab"),v=biigle.$require("annotations.stores.styles");function y(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 l=h.render;h.render=function(t,e){return u.call(e),l(t,e)}}else{var c=h.beforeCreate;h.beforeCreate=c?[].concat(c,u):[u]}return{exports:t,options:h}}var I=y({mixins:[f],components:{typeahead:p},data:function(){return{volumeId:null,showAdvanced:!1,shouldFetchLabels:!1,labels:[],selectedLabels:[],submitted:!1,trainScheme:[],trainingDataMethod:"",knowledgeTransferVolumes:[],knowledgeTransferVolume:null,shouldFetchKnowledgeTransferVolumes:!1,knowledgeTransferTypeaheadTemplate:"{{item.name}}
({{item.description}})"}},computed:{hasLabels:function(){return this.labels.length>0},hasSelectedLabels:function(){return this.selectedLabels.length>0},useExistingAnnotations:function(){return"own_annotations"===this.trainingDataMethod},useNoveltyDetection:function(){return"novelty_detection"===this.trainingDataMethod},useKnowledgeTransfer:function(){return"knowledge_transfer"===this.trainingDataMethod},canSubmit:function(){return this.submitted||this.useKnowledgeTransfer&&!this.knowledgeTransferVolume},hasNoKnowledgeTransferVolumes:function(){return this.shouldFetchKnowledgeTransferVolumes&&!this.loading&&0===this.knowledgeTransferVolumes.length}},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)},handleSelectedKnowledgeTransferVolume:function(t){this.knowledgeTransferVolume=t},setKnowledgeTransferVolumes:function(t){var e=this;this.knowledgeTransferVolumes=t.body.filter((function(t){return t.id!==e.volumeId})).map((function(t){return t.description=t.projects.map((function(t){return t.name})).join(", "),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,a).finally(this.finishLoading))},useKnowledgeTransfer:function(t){t&&(this.shouldFetchKnowledgeTransferVolumes=!0)},shouldFetchKnowledgeTransferVolumes:function(t){t&&(this.startLoading(),i.get().then(this.setKnowledgeTransferVolumes,a).finally(this.finishLoading))}},created:function(){this.volumeId=biigle.$require("maia.volumeId"),this.trainScheme=biigle.$require("maia.trainScheme"),this.showAdvanced=biigle.$require("maia.hasErrors"),this.trainingDataMethod=biigle.$require("maia.trainingDataMethod"),this.useExistingAnnotations&&(this.shouldFetchLabels=!0)}},void 0,void 0,!1,null,null,null).exports,x=Vue.resource("api/v1/maia/annotation-candidates{/id}"),C=y({mixins:[h,s],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),b=y({mixins:[u],components:{imageGridImage:C.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,P=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"}}),S=Vue.resource("api/v1/maia/training-proposals{/id}"),w=y({mixins:[h,s],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),A=y({mixins:[u],components:{imageGridImage:w.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 E(){return function(){throw new Error("Unimplemented abstract method.")}()}var T=0;function F(t){return t.ol_uid||(t.ol_uid=String(++T))}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),M="add",L="remove",k="propertychange",R="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}(K),Q="change",tt="clear";var et=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(Q)},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(tt)},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}(Z);new Array(6);var Dt=[0,0,0,1],Xt=[0,0,0,1],Gt=new jt,$t={},qt=null,Bt={};!function(){var t,e,n=$t,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==ue||s==he?o=a.responseText:s==le?(o=a.responseXML)||(o=(new DOMParser).parseFromString(a.responseText,"application/xml")):s==ae&&(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 de(t,e){return[[-1/0,-1/0,1/0,1/0]]}var pe={DEGREES:"degrees",FEET:"ft",METERS:"m",PIXELS:"pixels",TILE_PIXELS:"tile-pixels",USFEET:"us-ft"},fe={};fe[pe.DEGREES]=2*Math.PI*6370997/360,fe[pe.FEET]=.3048,fe[pe.METERS]=1,fe[pe.USFEET]=1200/3937;var ge=pe,me=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};me.prototype.canWrapX=function(){return this.canWrapX_},me.prototype.getCode=function(){return this.code_},me.prototype.getExtent=function(){return this.extent_},me.prototype.getUnits=function(){return this.units_},me.prototype.getMetersPerUnit=function(){return this.metersPerUnit_||fe[this.units_]},me.prototype.getWorldExtent=function(){return this.worldExtent_},me.prototype.getAxisOrientation=function(){return this.axisOrientation_},me.prototype.isGlobal=function(){return this.global_},me.prototype.setGlobal=function(t){this.global_=t,this.canWrapX_=!(!t||!this.extent_)},me.prototype.getDefaultTileGrid=function(){return this.defaultTileGrid_},me.prototype.setDefaultTileGrid=function(t){this.defaultTileGrid_=t},me.prototype.setExtent=function(t){this.extent_=t,this.canWrapX_=!(!this.global_||!t)},me.prototype.setWorldExtent=function(t){this.worldExtent_=t},me.prototype.setGetPointResolution=function(t){this.getPointResolutionFunc_=t},me.prototype.getPointResolutionFunc=function(){return this.getPointResolutionFunc_};var _e=me,ve=6378137*Math.PI,ye=[-ve,-ve,ve,ve],Ie=[-180,-85,180,85],xe=function(t){function e(e){t.call(this,{code:e,units:ge.METERS,extent:ye,global:!0,worldExtent:Ie,getPointResolution:function(t,e){return t/yt(e[1]/6378137)}})}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e}(_e),Ce=[new xe("EPSG:3857"),new xe("EPSG:102100"),new xe("EPSG:102113"),new xe("EPSG:900913"),new xe("urn:ogc:def:crs:EPSG:6.18:3:3857"),new xe("urn:ogc:def:crs:EPSG::3857"),new xe("http://www.opengis.net/gml/srs/epsg.xml#3857")];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=ve,a=0;ar?u=r:u<-r&&(u=-r),s[a+1]=u}return s}function Pe(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 ut,this.unselectedAnnotationSource=new Je({features:this.unselectedAnnotationFeatures}),this.unselectedAnnotationLayer=new oe({source:this.unselectedAnnotationSource,zIndex:99,updateWhileAnimating:!0,updateWhileInteracting:!0,style:v.editing,opacity:.5})},createSelectMaiaAnnotationInteraction:function(t){this.selectMaiaAnnotationInteraction=new r({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,Ze=y({mixins:[He],props:{convertedAnnotations:{type:Array,default:function(){return[]}}},methods:{createConvertedAnnotationsLayer:function(){this.convertedAnnotationFeatures=new ut,this.convertedAnnotationSource=new Je({features:this.convertedAnnotationFeatures});var t=new st;t.set("color","999999"),this.convertedAnnotationLayer=new oe({source:this.convertedAnnotationSource,zIndex:98,updateWhileAnimating:!0,updateWhileInteracting:!0,style:v.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,Qe=y({components:{labelTrees:d},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,tn=y({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,en=y({components:{labelTrees:d},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,nn=y({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,on=[],sn={},rn=[],an={},un=y({mixins:[f],components:{sidebar:m,sidebarTab:_,selectProposalsTab:nn,proposalsImageGrid:A,refineProposalsTab:tn,refineCanvas:He,refineCandidatesCanvas:Ze,selectCandidatesTab:en,candidatesImageGrid:b,refineCandidatesTab:Qe},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?on:[]},selectedProposals:function(){var t=this.selectedProposalIds;return Object.keys(t).map((function(t){return sn[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?rn:[]},selectedCandidates:function(){var t=this.selectedCandidateIds;return Object.keys(t).map((function(t){return an[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;(on=t.body).forEach((function(t){sn[t.id]=t,e.setSelectedProposalId(t)})),this.hasProposals=on.length>0},fetchProposals:function(){return this.fetchProposalsPromise||(this.startLoading(),this.fetchProposalsPromise=P.getTrainingProposals({id:this.job.id}),this.fetchProposalsPromise.then(this.setProposals,a).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=S.update({id:t.id},{selected:e});return i.catch((function(i){a(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]=P.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&&l.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(a)},updateProposalPoints:function(t){var e=this.currentProposalsById[t.id];return S.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){g.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;(rn=t.body).forEach((function(t){an[t.id]=t,e.setSelectedCandidateId(t),e.setConvertedCandidateId(t)})),this.hasCandidates=rn.length>0},fetchCandidates:function(){return this.fetchCandidatesPromise||(this.startLoading(),this.fetchCandidatesPromise=P.getAnnotationCandidates({id:this.job.id}),this.fetchCandidatesPromise.then(this.setCandidates,a).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):g.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=x.update({id:t.id},{label_id:o});return s.catch((function(e){a(e),t.label=i,n.setSelectedCandidateId(t)})),s},fetchCandidateAnnotations:function(t){return this.candidateAnnotationCache.hasOwnProperty(t)||(this.candidateAnnotationCache[t]=P.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),l=.5*Math.exp(2*h/3),c=.5*Math.sqrt(h*l*(a-l)/a)*(u-a/2<0?-1:1),d=Math.max(o,Math.floor(i-u*l/a+c)),p=Math.min(s,Math.floor(i+(a-u)*l/a+c));e(n,i,d,p,r)}var f=n[i],g=o,m=s;for(t(n,o,i),r(n[s],f)>0&&t(n,o,s);g0;)m--}0===r(n[o],f)?t(n,o,m):(m++,t(n,m,s)),m<=i&&(o=m+1),i<=m&&(s=m-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 042e695..d0b1c70 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=47b958007d5949752c93", + "/assets/scripts/main.js": "/assets/scripts/main.js?id=98d10d347843b5ea99e3", "/assets/styles/main.css": "/assets/styles/main.css?id=e0eebb85d62abf60780d" } diff --git a/src/resources/assets/js/api/knowledgeTransferVolume.js b/src/resources/assets/js/api/knowledgeTransferVolume.js new file mode 100644 index 0000000..4ab2a11 --- /dev/null +++ b/src/resources/assets/js/api/knowledgeTransferVolume.js @@ -0,0 +1,6 @@ +/** + * Resource for knowledge transfer volumes. + * + * @type {Vue.resource} + */ +export default Vue.resource('api/v1/volumes/filter/knowledge-transfer'); diff --git a/src/resources/assets/js/form.vue b/src/resources/assets/js/form.vue index 9626869..0dbc79f 100644 --- a/src/resources/assets/js/form.vue +++ b/src/resources/assets/js/form.vue @@ -1,4 +1,5 @@