diff --git a/app/Http/Controllers/Api/ImageAnnotationController.php b/app/Http/Controllers/Api/ImageAnnotationController.php index 94caebec5b..b77dbd16af 100644 --- a/app/Http/Controllers/Api/ImageAnnotationController.php +++ b/app/Http/Controllers/Api/ImageAnnotationController.php @@ -204,7 +204,7 @@ public function show($id) * } * * @param StoreImageAnnotation $request - * @return ImageAnnotation + * @return ImageAnnotation|\Illuminate\Http\Response */ public function store(StoreImageAnnotation $request, LabelBotService $labelBotService) { @@ -223,6 +223,9 @@ public function store(StoreImageAnnotation $request, LabelBotService $labelBotSe // Add labelBOTlabels attribute to the response. $annotation->append('labelBOTLabels'); $label = array_shift($labels); + if (is_null($label)) { + return response('', 204); + } if (!empty($labels)) { // Attach the remaining labels (if any). $annotation->labelBOTLabels = $labels; diff --git a/app/Http/Controllers/Api/VideoAnnotationController.php b/app/Http/Controllers/Api/VideoAnnotationController.php index f3a6a71aee..416947bba5 100644 --- a/app/Http/Controllers/Api/VideoAnnotationController.php +++ b/app/Http/Controllers/Api/VideoAnnotationController.php @@ -198,7 +198,7 @@ public function show($id) * } * * @param StoreVideoAnnotation $request - * @return VideoAnnotation + * @return VideoAnnotation|\Illuminate\Http\Response */ public function store(StoreVideoAnnotation $request, LabelBotService $labelBotService) { @@ -227,6 +227,9 @@ public function store(StoreVideoAnnotation $request, LabelBotService $labelBotSe // Add labelBOTlabels attribute to the response. $annotation->append('labelBOTLabels'); $label = array_shift($labels); + if (is_null($label)) { + return response('', 204); + } if (!empty($labels)) { // Attach the remaining labels (if any). $annotation->labelBOTLabels = $labels; diff --git a/app/Services/LabelBot/LabelBotService.php b/app/Services/LabelBot/LabelBotService.php index 804504bed6..ca801d5682 100644 --- a/app/Services/LabelBot/LabelBotService.php +++ b/app/Services/LabelBot/LabelBotService.php @@ -15,7 +15,6 @@ use Illuminate\Http\Request; use InvalidArgumentException; use Pgvector\Laravel\Vector; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; class LabelBotService @@ -53,7 +52,7 @@ public function getLabelsForAnnotation( } if (empty($topNLabels)) { - throw new NotFoundHttpException("LabelBOT could not find similar annotations."); + return []; } $labelModels = Label::whereIn('id', $topNLabels)->get()->keyBy('id'); diff --git a/resources/assets/js/annotations/annotatorContainer.vue b/resources/assets/js/annotations/annotatorContainer.vue index 04aa8e7a03..81144c5cb2 100644 --- a/resources/assets/js/annotations/annotatorContainer.vue +++ b/resources/assets/js/annotations/annotatorContainer.vue @@ -439,8 +439,11 @@ export default { annotation.confidence = 1; let promise; - - if (this.labelbotIsActive) { + let pendingAnnotation = false; + // We check for label_id in case LabelBOT returns no results + // and the user input a label in the typeahead, so we skip this step + // and save the annotation with its label. + if (!annotation.label_id && this.labelbotIsActive) { let imageId = this.imageId; promise = this.saveLabelbotAnnotation( annotation, @@ -449,18 +452,26 @@ export default { promise.then((annotation) => { if (imageId === this.imageId) { + pendingAnnotation = annotation.labels?.length === 0 this.showLabelbotPopup(annotation); } }); } else { - annotation.label_id = this.selectedLabel.id; + if (!annotation.label_id) { + annotation.label_id = this.selectedLabel.id; + } promise = AnnotationsStore.create(this.imageId, annotation); } promise.then(this.setLastCreatedAnnotation) .catch(handleErrorResponse) - // Remove the temporary annotation if saving succeeded or failed. - .finally(removeCallback); + // Remove the temporary annotation if saving succeeded or failed, + // if it's not a pending annotation + .finally(() => { + if (!pendingAnnotation) { + removeCallback(); + } + }); }, handleAttachLabel(annotation, label) { label = label || this.selectedLabel; @@ -545,6 +556,9 @@ export default { Promise.all(toCache).catch(function () {}); }, setLastCreatedAnnotation(annotation) { + if (!annotation.id) { + return; + } if (this.lastCreatedAnnotationTimeout) { window.clearTimeout(this.lastCreatedAnnotationTimeout); } diff --git a/resources/assets/js/annotations/components/annotationCanvas.vue b/resources/assets/js/annotations/components/annotationCanvas.vue index b3d5d3ccbd..953363bfcc 100644 --- a/resources/assets/js/annotations/components/annotationCanvas.vue +++ b/resources/assets/js/annotations/components/annotationCanvas.vue @@ -590,6 +590,8 @@ export default { e.feature.set('color', '5bc0de'); e.feature.setStyle(Styles.editing); + newAnnotation.feature = e.feature, // we need this temporarily if LabelBOT returns no results + // Move feature to the LabelBOT layer so it has opacity=1 while LabelBOT // is computing. this.labelbotSource.addFeature(e.feature); diff --git a/resources/assets/js/annotations/components/annotationCanvas/labelBot.vue b/resources/assets/js/annotations/components/annotationCanvas/labelBot.vue index 8a8b1bb765..e454a67187 100644 --- a/resources/assets/js/annotations/components/annotationCanvas/labelBot.vue +++ b/resources/assets/js/annotations/components/annotationCanvas/labelBot.vue @@ -16,6 +16,8 @@ export default { 'change-labelbot-focused-popup', 'close-labelbot-popup', 'swap', + 'new', + 'delete-pending', ], props: { labelbotState: { @@ -55,6 +57,9 @@ export default { }, }, methods: { + createNewLabelBOTAnnotation(event) { + this.$emit('new', event.newAnnotation); + }, updateLabelbotLabel(event) { this.$emit('swap', event.annotation, event.label); }, @@ -67,6 +72,9 @@ export default { handleDeleteLabelbotAnnotation(annotation) { this.$emit('delete', [annotation]); }, + handleDeleteLabelbotPendingAnnotation(annotation) { + this.$emit('delete-pending', annotation); + }, getBoundingBox(imageWidth, imageHeight, points) { let minX = imageWidth; let minY = imageHeight; diff --git a/resources/assets/js/annotations/components/labelbotPopup.vue b/resources/assets/js/annotations/components/labelbotPopup.vue index 18fc6800f3..6066235e6d 100644 --- a/resources/assets/js/annotations/components/labelbotPopup.vue +++ b/resources/assets/js/annotations/components/labelbotPopup.vue @@ -10,7 +10,12 @@