You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by te...@apache.org on 2020/05/14 14:26:44 UTC
[incubator-streampipes] 01/02: - create image labeling app -
improve image labeling tool usability - label can be drawn on top of an
existing label
This is an automated email from the ASF dual-hosted git repository.
tex pushed a commit to branch STREAMPIPES-79
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git
commit eaf736d0966847fd59caa9fe822dbc33285644f9
Author: tex <te...@fzi.de>
AuthorDate: Thu May 14 16:23:45 2020 +0200
- create image labeling app
- improve image labeling tool usability
- label can be drawn on top of an existing label
---
.../app-image-labeling.component.css} | 8 +-
.../app-image-labeling.component.html | 35 ++++++
.../app-image-labeling.component.ts | 54 ++++++++
.../app-image-labeling.module.ts} | 32 ++++-
.../app/app-overview/app-overview.component.html | 2 +
ui/src/app/app-overview/app-overview.component.ts | 21 ++--
ui/src/app/app-overview/app-overview.module.ts | 26 ++--
.../datalake/datalake-rest.service.ts | 6 +-
ui/src/app/core-ui/core-ui.module.ts | 2 +
.../image-annotations.component.html | 12 +-
.../components/image-bar/image-bar.component.css | 1 +
.../image-container/image-container.component.css | 3 +-
.../image-container/image-container.component.ts | 34 ++++--
.../image-categorize.component.html | 2 +-
.../image-labeling/image-labeling.component.html | 24 ++--
.../image-labeling/image-labeling.component.ts | 136 +++++++++++----------
.../image/image-viewer/image-viewer.component.html | 2 +-
.../image/services/BrushLabeling.service.ts | 50 ++++----
.../core-ui/image/services/LabelingMode.service.ts | 79 ++++++++++++
.../image/services/PolygonLabeling.service.ts | 85 +++++++------
.../image/services/ReactLabeling.service.ts | 65 ++++++----
21 files changed, 473 insertions(+), 206 deletions(-)
diff --git a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css b/ui/src/app/app-image-labeling/app-image-labeling.component.css
similarity index 89%
copy from ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
copy to ui/src/app/app-image-labeling/app-image-labeling.component.css
index a4868af..e9af737 100644
--- a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
+++ b/ui/src/app/app-image-labeling/app-image-labeling.component.css
@@ -16,6 +16,10 @@
*
*/
-.imageBar {
- height: 70px;
+.page-container-padding {
+ width:100%;
+}
+
+.page-container-padding-inner {
+ padding: 10px;
}
\ No newline at end of file
diff --git a/ui/src/app/app-image-labeling/app-image-labeling.component.html b/ui/src/app/app-image-labeling/app-image-labeling.component.html
new file mode 100644
index 0000000..818003e
--- /dev/null
+++ b/ui/src/app/app-image-labeling/app-image-labeling.component.html
@@ -0,0 +1,35 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<div fxLayout="column" fxFlex="100" class="page-container-connect">
+ <div fxLayout="row" style="padding:0px;background-color:#f6f6f6;">
+ <div fxLayout="fill" style="line-height:24px;border-bottom:1px solid #ccc">
+ <mat-form-field style="margin-top: 5px; margin-left: 5px;">
+ <mat-label>Select data lake measurement</mat-label>
+ <mat-select [(value)]="selectedMeasure">
+ <mat-option *ngFor="let measure of dataLakeMeasures" [value]="measure">
+ {{measure.measureName}}
+ </mat-option>
+ </mat-select>
+ </mat-form-field>
+ </div>
+ </div>
+
+ <div class="fixed-height page-container-padding-inner" fxLayout="column" fxFlex="100">
+ <sp-image-labeling [measureName]="selectedMeasure?.measureName" [eventSchema]="selectedMeasure?.eventSchema"></sp-image-labeling>
+ </div>
+</div>
\ No newline at end of file
diff --git a/ui/src/app/app-image-labeling/app-image-labeling.component.ts b/ui/src/app/app-image-labeling/app-image-labeling.component.ts
new file mode 100644
index 0000000..f6c923c
--- /dev/null
+++ b/ui/src/app/app-image-labeling/app-image-labeling.component.ts
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Component, EventEmitter, OnInit, Output } from '@angular/core';
+import { DatalakeRestService } from "../core-services/datalake/datalake-rest.service";
+import { TsonLdSerializerService } from "../platform-services/tsonld-serializer.service";
+import { DataLakeMeasure } from "../core-model/datalake/DataLakeMeasure";
+
+@Component({
+ selector: 'app-image-labeling',
+ templateUrl: './app-image-labeling.component.html',
+ styleUrls: ['./app-image-labeling.component.css']
+})
+export class AppImageLabelingComponent implements OnInit {
+
+
+ dataLakeMeasures: DataLakeMeasure[] = [];
+ selectedMeasure: DataLakeMeasure;
+
+ @Output() appOpened = new EventEmitter<boolean>();
+
+ constructor(private restService: DatalakeRestService,
+ private tsonLdSerializerService: TsonLdSerializerService) {
+
+ }
+
+ ngOnInit() {
+ this.appOpened.emit(true);
+ this.restService.getAllInfos().map(data => {
+ return this.tsonLdSerializerService.fromJsonLdContainer(data, 'sp:DataLakeMeasure');
+ }).subscribe(
+ res => {
+ this.dataLakeMeasures = res;
+ this.selectedMeasure = res[0];
+ }
+ );
+ }
+
+
+}
diff --git a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css b/ui/src/app/app-image-labeling/app-image-labeling.module.ts
similarity index 51%
copy from ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
copy to ui/src/app/app-image-labeling/app-image-labeling.module.ts
index a4868af..9895c72 100644
--- a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
+++ b/ui/src/app/app-image-labeling/app-image-labeling.module.ts
@@ -6,16 +6,38 @@
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- *
*/
-.imageBar {
- height: 70px;
-}
\ No newline at end of file
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { CoreUiModule } from '../core-ui/core-ui.module';
+import { CustomMaterialModule } from '../CustomMaterial/custom-material.module';
+import { AppImageLabelingComponent } from './app-image-labeling.component';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ CustomMaterialModule,
+ CoreUiModule,
+ ],
+ declarations: [
+ AppImageLabelingComponent
+ ],
+ providers: [
+ ],
+ entryComponents: [
+ AppImageLabelingComponent,
+ ],
+ exports: [
+ AppImageLabelingComponent
+ ]
+})
+export class AppImageLabelingModule {
+}
diff --git a/ui/src/app/app-overview/app-overview.component.html b/ui/src/app/app-overview/app-overview.component.html
index f358675..9824a55 100644
--- a/ui/src/app/app-overview/app-overview.component.html
+++ b/ui/src/app/app-overview/app-overview.component.html
@@ -49,4 +49,6 @@
</div>
<app-asset-monitoring (appOpened)="appOpened($event)"
*ngIf="currentlySelectedApp === apps[0].appId"></app-asset-monitoring>
+ <app-image-labeling (appOpened)="appOpened($event)"
+ *ngIf="currentlySelectedApp === apps[1].appId"></app-image-labeling>
</div>
diff --git a/ui/src/app/app-overview/app-overview.component.ts b/ui/src/app/app-overview/app-overview.component.ts
index 465d38b..5c7fe70 100644
--- a/ui/src/app/app-overview/app-overview.component.ts
+++ b/ui/src/app/app-overview/app-overview.component.ts
@@ -16,23 +16,28 @@
*
*/
-import {Component} from '@angular/core';
+import { Component, OnInit } from "@angular/core";
@Component({
templateUrl: './app-overview.component.html',
styleUrls: ['./app-overview.component.css']
})
-export class AppOverviewComponent {
+export class AppOverviewComponent implements OnInit {
- selectedIndex: number = 0;
+ selectedIndex = 0;
appOpen = false;
- currentlySelectedApp: string = "";
+ currentlySelectedApp = '';
apps: any[] = [
{
- appName: "Asset Dashboards",
- appDescription: "Monitor measurements of your assets by placing visualizations on an image of your asset.",
- appId: "asset-monitoring",
+ appName: 'Asset Dashboards',
+ appDescription: 'Monitor measurements of your assets by placing visualizations on an image of your asset.',
+ appId: 'asset-monitoring',
+ },
+ {
+ appName: 'Image Labeling',
+ appDescription: 'Label in data lake stored images.',
+ appId: 'image-labeling',
},
];
@@ -54,7 +59,7 @@ export class AppOverviewComponent {
appClosed() {
this.appOpen = false;
- this.currentlySelectedApp = "";
+ this.currentlySelectedApp = '';
}
selectApp(appId: string) {
diff --git a/ui/src/app/app-overview/app-overview.module.ts b/ui/src/app/app-overview/app-overview.module.ts
index 10f9ead..03dc7f7 100644
--- a/ui/src/app/app-overview/app-overview.module.ts
+++ b/ui/src/app/app-overview/app-overview.module.ts
@@ -16,18 +16,19 @@
*
*/
-import {NgModule} from '@angular/core';
-import {FlexLayoutModule} from '@angular/flex-layout';
-import {CommonModule} from '@angular/common';
-import {CustomMaterialModule} from '../CustomMaterial/custom-material.module';
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { FlexLayoutModule } from '@angular/flex-layout';
+import { CustomMaterialModule } from '../CustomMaterial/custom-material.module';
-import {MatFormFieldModule} from "@angular/material/form-field";
-import {MatGridListModule} from "@angular/material/grid-list";
-import {MatInputModule} from "@angular/material/input";
-import {FormsModule} from "@angular/forms";
-import {AppOverviewComponent} from "./app-overview.component";
-import {AppAssetMonitoringModule} from "../app-asset-monitoring/app-asset-monitoring.module";
-import {AppTransportMonitoringModule} from "../app-transport-monitoring/app-transport-monitoring.module";
+import { FormsModule } from '@angular/forms';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatGridListModule } from '@angular/material/grid-list';
+import { MatInputModule } from '@angular/material/input';
+import { AppAssetMonitoringModule } from '../app-asset-monitoring/app-asset-monitoring.module';
+import { AppImageLabelingModule } from '../app-image-labeling/app-image-labeling.module';
+import { AppTransportMonitoringModule } from '../app-transport-monitoring/app-transport-monitoring.module';
+import { AppOverviewComponent } from './app-overview.component';
@NgModule({
imports: [
@@ -39,7 +40,8 @@ import {AppTransportMonitoringModule} from "../app-transport-monitoring/app-tran
MatFormFieldModule,
FormsModule,
AppAssetMonitoringModule,
- AppTransportMonitoringModule
+ AppTransportMonitoringModule,
+ AppImageLabelingModule,
],
declarations: [
AppOverviewComponent,
diff --git a/ui/src/app/core-services/datalake/datalake-rest.service.ts b/ui/src/app/core-services/datalake/datalake-rest.service.ts
index 465cc0b..91e1184 100644
--- a/ui/src/app/core-services/datalake/datalake-rest.service.ts
+++ b/ui/src/app/core-services/datalake/datalake-rest.service.ts
@@ -18,12 +18,12 @@
import { HttpClient, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import { DataLakeMeasure } from '../../core-model/datalake/DataLakeMeasure';
import { DataResult } from '../../core-model/datalake/DataResult';
import { GroupedDataResult } from '../../core-model/datalake/GroupedDataResult';
-import { DataLakeMeasure } from '../../core-model/datalake/DataLakeMeasure';
import { PageResult } from '../../core-model/datalake/PageResult';
import { AuthStatusService } from '../../services/auth-status.service';
-import { Observable } from "rxjs/Observable";
@Injectable()
export class DatalakeRestService {
@@ -146,4 +146,4 @@ export class DatalakeRestService {
saveCocoFileForImage(imageRoute, data) {
return this.http.post(this.dataLakeUrlV3 + '/data/image/' + imageRoute + '/coco', data);
}
-}
\ No newline at end of file
+}
diff --git a/ui/src/app/core-ui/core-ui.module.ts b/ui/src/app/core-ui/core-ui.module.ts
index a1483ab..da4936e 100644
--- a/ui/src/app/core-ui/core-ui.module.ts
+++ b/ui/src/app/core-ui/core-ui.module.ts
@@ -45,6 +45,7 @@ import { ColorService } from './image/services/color.service';
import { PolygonLabelingService } from './image/services/PolygonLabeling.service';
import { ReactLabelingService } from './image/services/ReactLabeling.service';
import { CocoFormatService } from "./image/services/CocoFormat.service";
+import { LabelingModeService } from "./image/services/LabelingMode.service";
// PlotlyViaCDNModule.plotlyjs = PlotlyJS;
@NgModule({
@@ -80,6 +81,7 @@ import { CocoFormatService } from "./image/services/CocoFormat.service";
PolygonLabelingService,
BrushLabelingService,
CocoFormatService,
+ LabelingModeService,
],
entryComponents: [
],
diff --git a/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.html b/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.html
index feedf72..abfaf1d 100644
--- a/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.html
+++ b/ui/src/app/core-ui/image/components/image-annotations/image-annotations.component.html
@@ -20,19 +20,25 @@
<h2>Annotations</h2>
<mat-list>
<mat-list-item *ngFor="let annotation of annotations" (mouseover)="enterAnnotation(annotation)" (mouseout)="leaveAnnotation(annotation)"
- style="height: 30px; padding-top: 5px; padding-buttom: 5px; border-radius: 50px; margin-bottom: 5px"
+ style="height: 40px; padding-top: 5px; padding-buttom: 5px; border-radius: 50px; margin-bottom: 5px"
[style.background-color]="annotation.isHovered || annotation.isSelected ? 'lightgrey' : 'white'">
- <mat-icon matListIcon [style.color]="colorService.getColor(annotation.category_name)" style="margin-top: -8px;">color_lens</mat-icon>
<mat-form-field>
<mat-select [value]="annotation.category_name">
+
+ <mat-select-trigger>
+ <mat-icon [style.color]="colorService.getColor(annotation.category_name)">color_lens</mat-icon>
+ {{annotation.category_name}}
+ </mat-select-trigger>
+
<mat-optgroup *ngFor="let category of categories" [label]="category">
<mat-option *ngFor="let label of _labels[category]" [value]="label" (click)="changeLabel(annotation, label, category)">
+ <mat-icon [style.color]="colorService.getColor(label)">color_lens</mat-icon>
{{label}}
</mat-option>
</mat-optgroup>
</mat-select>
</mat-form-field>
- <button mat-icon-button (click)="delete(annotation)" style="margin-top: -10px; margin-left: 10px"> <mat-icon>delete_forever</mat-icon></button>
+ <button mat-icon-button (click)="delete(annotation)" style="margin-top: -10px; margin-left: -5px"> <mat-icon>delete_forever</mat-icon></button>
</mat-list-item>
</mat-list>
</div>
\ No newline at end of file
diff --git a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css b/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
index a4868af..b0f2e09 100644
--- a/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
+++ b/ui/src/app/core-ui/image/components/image-bar/image-bar.component.css
@@ -18,4 +18,5 @@
.imageBar {
height: 70px;
+ width: 1000px;
}
\ No newline at end of file
diff --git a/ui/src/app/core-ui/image/components/image-container/image-container.component.css b/ui/src/app/core-ui/image/components/image-container/image-container.component.css
index 612fce8..dfaa975 100644
--- a/ui/src/app/core-ui/image/components/image-container/image-container.component.css
+++ b/ui/src/app/core-ui/image/components/image-container/image-container.component.css
@@ -20,6 +20,5 @@
background-color: #fafafa;
border-color: lightgrey;
border-style: solid;
- height: 500px;
- width: 800px;
+
}
\ No newline at end of file
diff --git a/ui/src/app/core-ui/image/components/image-container/image-container.component.ts b/ui/src/app/core-ui/image/components/image-container/image-container.component.ts
index 7667a8f..b1c47c0 100644
--- a/ui/src/app/core-ui/image/components/image-container/image-container.component.ts
+++ b/ui/src/app/core-ui/image/components/image-container/image-container.component.ts
@@ -17,7 +17,6 @@
import { AfterViewInit, Component, EventEmitter, HostListener, Input, OnInit, Output } from '@angular/core';
import Konva from 'konva';
-import { Context } from 'konva/types/Context';
import { ICoordinates } from '../../model/coordinates';
@Component({
@@ -46,6 +45,8 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
shortCut: EventEmitter<string> = new EventEmitter<string>();
@Output()
dbclick: EventEmitter<[Konva.Layer, ICoordinates, ICoordinates]> = new EventEmitter<[Konva.Layer, ICoordinates, ICoordinates]>();
+ @Output()
+ mouseDownRight: EventEmitter<[Konva.Layer, ICoordinates, ICoordinates]> = new EventEmitter<[Konva.Layer, ICoordinates, ICoordinates]>();
private image;
@@ -62,6 +63,7 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
private lastImagePointerPosition: ICoordinates;
private isLeftMouseDown: boolean;
+ private isMiddleMouseDown: boolean;
private isRightMouseDown: boolean;
private isHoverComponent: boolean;
@@ -72,6 +74,7 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
this.scale = 1;
this.imageShift = {x: 0, y: 0};
this.isLeftMouseDown = false;
+ this.isMiddleMouseDown = false;
this.isRightMouseDown = false;
this.isHoverComponent = false;
}
@@ -106,8 +109,10 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
}
getShift() {
- const position = this.imageLayer.getChildren().toArray()[0].getPosition();
- return {x: position.x, y: position.y};
+ if (this.imageLayer !== undefined) {
+ const position = this.imageLayer.getChildren().toArray()[0].getPosition();
+ return {x: position.x, y: position.y};
+ }
}
/* mouse handler */
@@ -120,13 +125,14 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
this.drawLayer.batchDraw();
} else if (button === 2) {
// middle click
-
- } else if (button === 3) {
- // right click
- this.isRightMouseDown = true;
+ this.isMiddleMouseDown = true;
this.mainCanvasStage.container().style.cursor = 'move';
this.lastImagePointerPosition = this.getImagePointerPosition();
this.lastImageTranslation = this.imageShift;
+ } else if (button === 3) {
+ // right click
+ this.isRightMouseDown = true;
+ this.mouseDownRight.emit([this.drawLayer, this.getShift(), this.getImagePointerPosition()]);
}
}
@@ -135,7 +141,7 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
this.drawLayer.destroyChildren();
this.mouseMoveLeft.emit([this.drawLayer, this.getShift(), this.getImagePointerPosition()]);
this.drawLayer.batchDraw();
- } else if (this.isRightMouseDown) {
+ } else if (this.isMiddleMouseDown) {
const imagePointerPosition = this.getImagePointerPosition();
this.imageShift.x = this.lastImageTranslation.x + (imagePointerPosition.x - this.lastImagePointerPosition.x);
this.imageShift.y = this.lastImageTranslation.y + (imagePointerPosition.y - this.lastImagePointerPosition.y);
@@ -157,8 +163,8 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
this.drawLayer.batchDraw();
this.annotationLayer.batchDraw();
}
- if (this.isRightMouseDown) {
- this.isRightMouseDown = false;
+ if (this.isMiddleMouseDown) {
+ this.isMiddleMouseDown = false;
this.mainCanvasStage.container().style.cursor = 'default';
}
}
@@ -173,8 +179,12 @@ export class ImageContainerComponent implements OnInit, AfterViewInit {
/* Draw */
redrawAll() {
- this.drawLayer.destroyChildren();
- this.annotationLayer.destroyChildren();
+ if (this.drawLayer !== undefined) {
+ this.drawLayer.destroyChildren();
+ }
+ if (this.annotationLayer !== undefined) {
+ this.annotationLayer.destroyChildren();
+ }
this.childRedraw.emit([this.annotationLayer, this.getShift()]);
this.shiftViewContent();
}
diff --git a/ui/src/app/core-ui/image/image-categorize/image-categorize.component.html b/ui/src/app/core-ui/image/image-categorize/image-categorize.component.html
index a75852d..a5d6a0c 100644
--- a/ui/src/app/core-ui/image/image-categorize/image-categorize.component.html
+++ b/ui/src/app/core-ui/image/image-categorize/image-categorize.component.html
@@ -46,7 +46,7 @@
</div>
</div>
<br />
- <div fxLayout="row" fxLayoutAlign="center center"><div>{{pageIndex + 1}} / {{pageSum + 1}}</div></div>
+ <div fxLayout="row" fxLayoutAlign="center center"><div>{{pageIndex + 1}} / {{pageSum }}</div></div>
<sp-image-bar style="width: 100%"
[imagesSrcs]="imagesSrcs"
[selectedIndex]="imagesIndex"
diff --git a/ui/src/app/core-ui/image/image-labeling/image-labeling.component.html b/ui/src/app/core-ui/image/image-labeling/image-labeling.component.html
index 0669e12..a6390f8 100644
--- a/ui/src/app/core-ui/image/image-labeling/image-labeling.component.html
+++ b/ui/src/app/core-ui/image/image-labeling/image-labeling.component.html
@@ -21,19 +21,24 @@
<div fxLayout="column" fxLayoutAlign="space-between " >
<div fxLayout="row" fxLayoutAlign="space-around start">
- <sp-image-labels
+ <sp-image-labels style="width: 270px"
[labels]="labels"
[enableShortCuts]="isHoverComponent"
(labelChange)="handleLabelChange($event)">
</sp-image-labels>
<div fxLayout="column" fxLayoutAlign="space-between " >
- <div>
- <button mat-button (click)="setReactMode()" [style.background-color]="isReactMode() ? 'lightgrey' : 'white'"> <mat-icon>crop_3_2</mat-icon></button>
- <button mat-button (click)="setPolygonMode()" [style.background-color]="isPolygonMode() ? 'lightgrey' : 'white'"> <mat-icon>details</mat-icon></button>
- <button mat-button (click)="setBrushMode()" [style.background-color]="isBrushMode() ? 'lightgrey' : 'white'"> <mat-icon>blur_circular</mat-icon></button>
+ <div style="display: flex; position: relative">
+ <button mat-button matTooltip="Edit Annotations"
+ (click)="labelingMode.setNoneMode()" [style.background-color]="labelingMode.isNoneMode() ? 'lightgrey' : 'white'"> <mat-icon>create</mat-icon></button>
+ <button mat-button matTooltip="Create React Annotation"
+ (click)="labelingMode.setReactMode()" [style.background-color]="labelingMode.isReactMode() ? 'lightgrey' : 'white'"> <mat-icon>crop_3_2</mat-icon></button>
+ <button mat-button matTooltip="Create Polygon Annotation"
+ (click)="labelingMode.setPolygonMode()" [style.background-color]="labelingMode.isPolygonMode() ? 'lightgrey' : 'white'"> <mat-icon>details</mat-icon></button>
+ <button mat-button matTooltip="Create Brush Annotation"
+ (click)="labelingMode.setBrushMode()" [style.background-color]="labelingMode.isBrushMode() ? 'lightgrey' : 'white'"> <mat-icon>blur_circular</mat-icon></button>
<mat-slider [min]="1" [max]="50" [step]="1" [thumbLabel]="true" [(ngModel)]="brushSize"></mat-slider>
- <button mat-button (click)="save()"> <mat-icon>save</mat-icon></button>
+ <button mat-button (click)="save()" style="position: absolute; right: 0px;"> <mat-icon>save</mat-icon></button>
</div>
<sp-image-container
@@ -44,11 +49,12 @@
(mouseMoveLeft)="handleMouseMoveLeft($event[0], $event[1], $event[2])"
(mouseUpLeft)="handleMouseUpLeft($event[0], $event[1], $event[2], $event[3])"
(shortCut)="handleImageViewShortCuts($event)"
- (dbclick)="handleImageViewDBClick($event[0], $event[1], $event[2])">
+ (dbclick)="handleImageViewDBClick($event[0], $event[1], $event[2])"
+ (mouseDownRight)="handleMouseDownRight($event[0], $event[1], $event[2])">
</sp-image-container>
</div>
- <sp-image-annotations
+ <sp-image-annotations style="width: 270px"
[annotations]="this.cocoFiles[this.imagesIndex]?.annotations"
[labels]="labels"
(changeAnnotationLabel)="handleChangeAnnotationLabel($event)"
@@ -56,7 +62,7 @@
</sp-image-annotations>
</div>
<br />
- <div fxLayout="row" fxLayoutAlign="center center"><div>{{pageIndex + 1}} / {{pageSum + 1}}</div></div>
+ <div fxLayout="row" fxLayoutAlign="center center"><div>{{pageIndex + 1}} / {{pageSum}}</div></div>
<sp-image-bar style="width: 100%"
[imagesSrcs]="imagesSrcs"
[selectedIndex]="imagesIndex"
diff --git a/ui/src/app/core-ui/image/image-labeling/image-labeling.component.ts b/ui/src/app/core-ui/image/image-labeling/image-labeling.component.ts
index 2cd0b24..c2c68f7 100644
--- a/ui/src/app/core-ui/image/image-labeling/image-labeling.component.ts
+++ b/ui/src/app/core-ui/image/image-labeling/image-labeling.component.ts
@@ -17,27 +17,28 @@
*/
-import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
+import { AfterViewInit, Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import Konva from 'konva';
import { Annotation } from '../../../core-model/coco/Annotation';
import { CocoFormat } from '../../../core-model/coco/Coco.format';
import { DatalakeRestService } from '../../../core-services/datalake/datalake-rest.service';
+import { TsonLdSerializerService } from '../../../platform-services/tsonld-serializer.service';
import { ImageContainerComponent } from '../components/image-container/image-container.component';
import { ICoordinates } from '../model/coordinates';
import { LabelingMode } from '../model/labeling-mode';
import { BrushLabelingService } from '../services/BrushLabeling.service';
import { CocoFormatService } from '../services/CocoFormat.service';
+import { LabelingModeService } from '../services/LabelingMode.service';
import { PolygonLabelingService } from '../services/PolygonLabeling.service';
import { ReactLabelingService } from '../services/ReactLabeling.service';
-import { TsonLdSerializerService } from '../../../platform-services/tsonld-serializer.service';
@Component({
selector: 'sp-image-labeling',
templateUrl: './image-labeling.component.html',
styleUrls: ['./image-labeling.component.css']
})
-export class ImageLabelingComponent implements OnInit, AfterViewInit {
+export class ImageLabelingComponent implements OnInit, AfterViewInit, OnChanges {
// label
public labels;
@@ -54,8 +55,8 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
@ViewChild(ImageContainerComponent) imageView: ImageContainerComponent;
- measureName = 'image'; // TODO: Remove hard coded Index, should be injected
- eventSchema = undefined; // TODO: event schema should be also injected
+ @Input() measureName = 'image'; // TODO: Remove default value for production
+ @Input() eventSchema = undefined; // TODO: event schema should be always injected by production
imageField = undefined;
pageIndex = undefined;
pageSum = undefined;
@@ -65,38 +66,57 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
private setImagesIndexToLast = false;
- public labelingMode: LabelingMode = LabelingMode.ReactLabeling;
constructor(private restService: DatalakeRestService, private reactLabelingService: ReactLabelingService,
private polygonLabelingService: PolygonLabelingService, private brushLabelingService: BrushLabelingService,
private snackBar: MatSnackBar, private cocoFormatService: CocoFormatService,
- private tsonLdSerializerService: TsonLdSerializerService) { }
+ private tsonLdSerializerService: TsonLdSerializerService,
+ public labelingMode: LabelingModeService) { }
ngOnInit(): void {
this.isHoverComponent = false;
this.brushSize = 5;
+ this.imagesIndex = 0;
+
- // TODO Get Labels
this.labels = this.restService.getLabels();
- this.restService.getAllInfos().map(data => {
- return this.tsonLdSerializerService.fromJsonLdContainer(data, 'sp:DataLakeMeasure');
- }).subscribe(
- res => {
- this.eventSchema = res.find(elem => elem.measureName === this.measureName).eventSchema;
- const properties = this.eventSchema.eventProperties;
- for (const prop of properties) {
- // if (prop.domainProperties.find(type => type === 'https://image.com')) {
+ // TODO remove for production, if default dev values are not necessary
+ if (this.eventSchema === undefined) {
+ this.restService.getAllInfos().map(data => {
+ return this.tsonLdSerializerService.fromJsonLdContainer(data, 'sp:DataLakeMeasure');
+ }).subscribe(
+ res => {
+ this.eventSchema = res.find(elem => elem.measureName === this.measureName).eventSchema;
+ const properties = this.eventSchema.eventProperties;
+ for (const prop of properties) {
+ // if (prop.domainProperties.find(type => type === 'https://image.com')) {
if (prop.domainProperty === 'https://image.com') {
- this.imageField = prop;
- break;
+ this.imageField = prop;
+ break;
+ }
}
+ this.loadData();
+ }
+ );
+ }
+ }
+
+ ngOnChanges() {
+ if (this.eventSchema !== null) {
+ const properties = this.eventSchema.eventProperties;
+ for (const prop of properties) {
+ if (prop.domainProperty === 'https://image.com') {
+ this.imageField = prop;
+ break;
}
- this.loadData();
}
- );
+ this.pageIndex = undefined;
+ this.pageSum = undefined;
+ this.imagesIndex = 0;
- this.imagesIndex = 0;
+ this.loadData();
+ }
}
ngAfterViewInit(): void {
@@ -159,7 +179,7 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
/* sp-image-view handler */
handleMouseDownLeft(layer: Konva.Layer, shift: ICoordinates, position: ICoordinates) {
if (this.labelingEnabled()) {
- switch (this.labelingMode) {
+ switch (this.labelingMode.getMode()) {
case LabelingMode.ReactLabeling: this.reactLabelingService.startLabeling(position);
break;
case LabelingMode.PolygonLabeling: this.polygonLabelingService.startLabeling(position);
@@ -170,7 +190,7 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
}
handleMouseMove(layer: Konva.Layer, shift: ICoordinates, position: ICoordinates) {
- switch (this.labelingMode) {
+ switch (this.labelingMode.getMode()) {
case LabelingMode.PolygonLabeling: {
this.polygonLabelingService.executeLabeling(position);
this.polygonLabelingService.tempDraw(layer, shift, this.selectedLabel.label);
@@ -180,7 +200,7 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
handleMouseMoveLeft(layer: Konva.Layer, shift: ICoordinates, position: ICoordinates) {
if (this.labelingEnabled()) {
- switch (this.labelingMode) {
+ switch (this.labelingMode.getMode()) {
case LabelingMode.ReactLabeling: {
this.reactLabelingService.executeLabeling(position);
this.reactLabelingService.tempDraw(layer, shift, this.selectedLabel.label);
@@ -191,12 +211,12 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
this.brushLabelingService.tempDraw(layer, shift, this.selectedLabel.label);
}
}
- }
+ }
}
handleMouseUpLeft(annotationLayer: Konva.Layer, drawLayer: Konva.Layer, shift: ICoordinates, position: ICoordinates) {
if (this.labelingEnabled()) {
- switch (this.labelingMode) {
+ switch (this.labelingMode.getMode()) {
case LabelingMode.ReactLabeling: {
const result = this.reactLabelingService.endLabeling(position);
const coco = this.cocoFiles[this.imagesIndex];
@@ -220,10 +240,14 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
}
}
+ handleMouseDownRight(layer: Konva.Layer, shift: ICoordinates, position: ICoordinates) {
+ this.labelingMode.toggleNoneMode();
+ }
+
handleImageViewDBClick(layer: Konva.Layer, shift: ICoordinates, position: ICoordinates) {
if (this.labelingEnabled()) {
- switch (this.labelingMode) {
+ switch (this.labelingMode.getMode()) {
case LabelingMode.PolygonLabeling:
const points = this.polygonLabelingService.endLabeling(position);
const coco = this.cocoFiles[this.imagesIndex];
@@ -236,15 +260,17 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
handleChildRedraw(layer: Konva.Layer, shift: ICoordinates) {
const coco = this.cocoFiles[this.imagesIndex];
- for (const annotation of coco.annotations) {
- annotation.isHovered = false;
- annotation.isSelected = false;
- if (this.cocoFormatService.isBoxAnnonation(annotation)) {
- this.reactLabelingService.draw(layer, shift, annotation, this.imageView);
- } else if (this.cocoFormatService.isPolygonAnnonation(annotation) && !this.cocoFormatService.isBrushAnnonation(annotation)) {
- this.polygonLabelingService.draw(layer, shift, annotation, this.imageView);
- } else if (this.cocoFormatService.isBrushAnnonation(annotation)) {
- this.brushLabelingService.draw(layer, shift, annotation, this.imageView);
+ if (coco !== undefined) {
+ for (const annotation of coco.annotations) {
+ annotation.isHovered = false;
+ annotation.isSelected = false;
+ if (this.cocoFormatService.isBoxAnnonation(annotation)) {
+ this.reactLabelingService.draw(layer, shift, annotation, this.imageView);
+ } else if (this.cocoFormatService.isPolygonAnnonation(annotation) && !this.cocoFormatService.isBrushAnnonation(annotation)) {
+ this.polygonLabelingService.draw(layer, shift, annotation, this.imageView);
+ } else if (this.cocoFormatService.isBrushAnnonation(annotation)) {
+ this.brushLabelingService.draw(layer, shift, annotation, this.imageView);
+ }
}
}
}
@@ -306,7 +332,7 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
private labelingEnabled() {
const coco = this.cocoFiles[this.imagesIndex];
- const annotation = coco.annotations.find(anno => anno.isHovered);
+ const annotation = coco.annotations.find(anno => anno.isHovered && anno.isSelected);
if (annotation !== undefined) {
return false;
} else {
@@ -317,11 +343,13 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
save() {
// TODO
const coco = this.cocoFiles[this.imagesIndex];
- const imageSrcSplitted = this.imagesSrcs[this.imagesIndex].split('/');
- const imageRoute = imageSrcSplitted[imageSrcSplitted.length - 2];
- this.restService.saveCocoFileForImage(imageRoute, JSON.stringify(coco)).subscribe(
- res => this.openSnackBar('Saved')
- );
+ if (coco !== undefined) {
+ const imageSrcSplitted = this.imagesSrcs[this.imagesIndex].split('/');
+ const imageRoute = imageSrcSplitted[imageSrcSplitted.length - 2];
+ this.restService.saveCocoFileForImage(imageRoute, JSON.stringify(coco)).subscribe(
+ res => this.openSnackBar('Saved')
+ );
+ }
}
private openSnackBar(message: string) {
@@ -332,28 +360,4 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
});
}
- /* UI */
- isReactMode() {
- return this.labelingMode === LabelingMode.ReactLabeling;
- }
-
- setReactMode() {
- this.labelingMode = LabelingMode.ReactLabeling;
- }
-
- isPolygonMode() {
- return this.labelingMode === LabelingMode.PolygonLabeling;
- }
-
- setPolygonMode() {
- this.labelingMode = LabelingMode.PolygonLabeling;
- }
-
- isBrushMode() {
- return this.labelingMode === LabelingMode.BrushLabeling;
- }
-
- setBrushMode() {
- this.labelingMode = LabelingMode.BrushLabeling;
- }
}
diff --git a/ui/src/app/core-ui/image/image-viewer/image-viewer.component.html b/ui/src/app/core-ui/image/image-viewer/image-viewer.component.html
index d373a2b..60ca4a6 100644
--- a/ui/src/app/core-ui/image/image-viewer/image-viewer.component.html
+++ b/ui/src/app/core-ui/image/image-viewer/image-viewer.component.html
@@ -26,7 +26,7 @@
</div>
</div>
<br />
- <div fxLayout="row" fxLayoutAlign="center center"><div>{{pageIndex + 1}} / {{pageSum + 1}}</div></div>
+ <div fxLayout="row" fxLayoutAlign="center center"><div>{{pageIndex + 1}} / {{pageSum }}</div></div>
<sp-image-bar style="width: 100%"
[imagesSrcs]="imagesSrcs"
[selectedIndex]="imagesIndex"
diff --git a/ui/src/app/core-ui/image/services/BrushLabeling.service.ts b/ui/src/app/core-ui/image/services/BrushLabeling.service.ts
index d97b0dc..07ffd3b 100644
--- a/ui/src/app/core-ui/image/services/BrushLabeling.service.ts
+++ b/ui/src/app/core-ui/image/services/BrushLabeling.service.ts
@@ -20,6 +20,7 @@ import Konva from 'konva';
import { Annotation } from '../../../core-model/coco/Annotation';
import { ICoordinates } from '../model/coordinates';
import { ColorService } from './color.service';
+import { LabelingModeService } from './LabelingMode.service';
@Injectable()
export class BrushLabelingService {
@@ -29,7 +30,8 @@ export class BrushLabelingService {
private isLabeling: boolean;
- constructor(private colorService: ColorService) {
+ constructor(private colorService: ColorService,
+ private labelingMode: LabelingModeService) {
}
@@ -100,43 +102,49 @@ export class BrushLabelingService {
transformer.attachTo(line);
}
- this.addMouseHandler(line, annotation, layer, transformer);
- this.addClickHandler(line, annotation, layer, transformer);
+ this.addMouseHandler(line, annotation, layer, transformer, this.labelingMode);
+ this.addClickHandler(line, annotation, layer, transformer, this.labelingMode);
layer.add(line);
layer.add(transformer);
}
- private addClickHandler(rect, annotation, layer, transformer) {
- rect.on('click', function() {
- annotation.isSelected = true;
- transformer.attachTo(this);
- layer.batchDraw();
- });
+ private addClickHandler(brush, annotation, layer, transformer, labelingMode) {
+ brush.on('click', function() {
+ if (labelingMode.isNoneMode()) {
+ annotation.isSelected = !annotation.isSelected;
- rect.on('dblclick', function() {
- annotation.isSelected = false;
- transformer.detach();
- layer.batchDraw();
+ if (annotation.isSelected) {
+ transformer.attachTo(this);
+ } else {
+ transformer.detach();
+ }
+
+ layer.batchDraw();
+ }
});
}
- private addMouseHandler(rect, annotation, layer, transformer) {
- rect.on('mouseover', function() {
- annotation.isHovered = true;
- rect.opacity(0.8);
- layer.batchDraw();
+ private addMouseHandler(brush, annotation, layer, transformer, labelingMode) {
+ brush.on('mouseover', function() {
+ if (labelingMode.isNoneMode()) {
+ annotation.isHovered = true;
+ brush.opacity(0.8);
+ layer.batchDraw();
+ }
});
- rect.on('mouseout', function() {
+ brush.on('mouseout', function() {
annotation.isHovered = false;
- rect.opacity(0.5);
+ brush.opacity(0.5);
layer.batchDraw();
});
transformer.on('mouseover', function() {
- annotation.isHovered = true;
+ if (labelingMode.isNoneMode()) {
+ annotation.isHovered = true;
+ }
});
transformer.on('mouseout', function() {
diff --git a/ui/src/app/core-ui/image/services/LabelingMode.service.ts b/ui/src/app/core-ui/image/services/LabelingMode.service.ts
new file mode 100644
index 0000000..fe5307e
--- /dev/null
+++ b/ui/src/app/core-ui/image/services/LabelingMode.service.ts
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Injectable } from '@angular/core';
+import { LabelingMode } from '../model/labeling-mode';
+
+@Injectable()
+export class LabelingModeService {
+
+ private labelingMode: LabelingMode;
+ private lastLabelingMode: LabelingMode;
+
+ constructor() {
+ this.labelingMode = LabelingMode.ReactLabeling;
+ this.lastLabelingMode = LabelingMode.ReactLabeling;
+ }
+
+ isReactMode() {
+ return this.labelingMode === LabelingMode.ReactLabeling;
+ }
+
+ setReactMode() {
+ this.labelingMode = LabelingMode.ReactLabeling;
+ this.lastLabelingMode = LabelingMode.ReactLabeling;
+ }
+
+ isPolygonMode() {
+ return this.labelingMode === LabelingMode.PolygonLabeling;
+ }
+
+ setPolygonMode() {
+ this.labelingMode = LabelingMode.PolygonLabeling;
+ this.lastLabelingMode = LabelingMode.PolygonLabeling;
+ }
+
+ isBrushMode() {
+ return this.labelingMode === LabelingMode.BrushLabeling;
+ }
+
+ setBrushMode() {
+ this.labelingMode = LabelingMode.BrushLabeling;
+ this.lastLabelingMode = LabelingMode.BrushLabeling;
+ }
+
+ isNoneMode() {
+ return this.labelingMode === LabelingMode.NoneLabeling;
+ }
+
+ setNoneMode() {
+ this.labelingMode = LabelingMode.NoneLabeling;
+ }
+
+ getMode() {
+ return this.labelingMode;
+ }
+
+ toggleNoneMode() {
+ if (this.labelingMode === LabelingMode.NoneLabeling) {
+ this.labelingMode = this.lastLabelingMode;
+ } else {
+ this.labelingMode = LabelingMode.NoneLabeling;
+ }
+ }
+
+}
diff --git a/ui/src/app/core-ui/image/services/PolygonLabeling.service.ts b/ui/src/app/core-ui/image/services/PolygonLabeling.service.ts
index 153e8fd..2c65fee 100644
--- a/ui/src/app/core-ui/image/services/PolygonLabeling.service.ts
+++ b/ui/src/app/core-ui/image/services/PolygonLabeling.service.ts
@@ -20,6 +20,7 @@ import Konva from 'konva';
import { Annotation } from '../../../core-model/coco/Annotation';
import { ICoordinates } from '../model/coordinates';
import { ColorService } from './color.service';
+import { LabelingModeService } from './LabelingMode.service';
@Injectable()
export class PolygonLabelingService {
@@ -29,7 +30,8 @@ export class PolygonLabelingService {
private isLabeling: boolean;
- constructor(private colorService: ColorService) {
+ constructor(private colorService: ColorService,
+ private labelingMode: LabelingModeService) {
this.isLabeling = false;
}
@@ -182,36 +184,41 @@ export class PolygonLabelingService {
PolygonLabelingService.buildAnchors(layer, annotation, imageView, poly);
}
- this.addDragHandler(poly, annotation, layer, imageView);
- this.addMouseHandler(poly, annotation, layer, transformer);
- this.addClickHandler(poly, annotation, layer, transformer, imageView);
+ this.addDragHandler(poly, annotation, layer, imageView, this.labelingMode);
+ this.addMouseHandler(poly, annotation, layer, transformer, this.labelingMode);
+ this.addClickHandler(poly, annotation, layer, transformer, imageView, this.labelingMode);
layer.add(poly);
layer.add(transformer);
}
- private addClickHandler(poly, annotation, layer, transformer, imageView) {
+ private addClickHandler(poly, annotation, layer, transformer, imageView, labelingMode) {
poly.on('click', function() {
- annotation.isSelected = true;
- transformer.attachTo(this);
- PolygonLabelingService.buildAnchors(layer, annotation, imageView, poly);
- layer.batchDraw();
- });
-
- poly.on('dblclick', function() {
- annotation.isSelected = false;
- PolygonLabelingService.removeAnchors(layer, annotation.id);
- transformer.detach();
- layer.batchDraw();
+ if (labelingMode.isNoneMode()) {
+
+ annotation.isSelected = !annotation.isSelected;
+
+ if (annotation.isSelected) {
+ annotation.isSelected = true;
+ transformer.attachTo(this);
+ PolygonLabelingService.buildAnchors(layer, annotation, imageView, poly);
+ } else {
+ PolygonLabelingService.removeAnchors(layer, annotation.id);
+ transformer.detach();
+ }
+ layer.batchDraw();
+ }
});
}
- private addMouseHandler(poly, annotation, layer, transformer) {
+ private addMouseHandler(poly, annotation, layer, transformer, labelingMode) {
poly.on('mouseover', function() {
- annotation.isHovered = true;
- this.opacity(0.8);
- layer.batchDraw();
+ if (labelingMode.isNoneMode()) {
+ annotation.isHovered = true;
+ poly.opacity(0.8);
+ layer.batchDraw();
+ }
});
poly.on('mouseout', function() {
@@ -222,28 +229,36 @@ export class PolygonLabelingService {
}
- private addDragHandler(poly, annotation, layer, imageView) {
+ private addDragHandler(poly, annotation, layer, imageView, labelingMode) {
let offset: number[];
poly.on('dragstart', function() {
- const position = imageView.getImagePointerPosition();
- offset = [];
- for (let i = 0; i < annotation.segmentation[0].length; i += 2) {
- offset.push(annotation.segmentation[0][i] - position.x);
- offset.push(annotation.segmentation[0][i + 1] - position.y);
+ if (!labelingMode.isNoneMode()) {
+ poly.stopDrag();
+ } else {
+ const position = imageView.getImagePointerPosition();
+ offset = [];
+ for (let i = 0; i < annotation.segmentation[0].length; i += 2) {
+ offset.push(annotation.segmentation[0][i] - position.x);
+ offset.push(annotation.segmentation[0][i + 1] - position.y);
+ }
}
});
poly.on('dragmove', function() {
- const position = imageView.getImagePointerPosition();
- const tmp = [];
- for (let i = 0; i < annotation.segmentation[0].length; i += 2) {
- tmp.push(position.x + offset[i]);
- tmp.push(position.y + offset[i + 1]);
- }
- annotation.segmentation[0] = tmp;
- if (annotation.isSelected) {
- PolygonLabelingService.buildAnchors(layer, annotation, imageView, poly);
+ if (!labelingMode.isNoneMode()) {
+ poly.stopDrag();
+ } else {
+ const position = imageView.getImagePointerPosition();
+ const tmp = [];
+ for (let i = 0; i < annotation.segmentation[0].length; i += 2) {
+ tmp.push(position.x + offset[i]);
+ tmp.push(position.y + offset[i + 1]);
+ }
+ annotation.segmentation[0] = tmp;
+ if (annotation.isSelected) {
+ PolygonLabelingService.buildAnchors(layer, annotation, imageView, poly);
+ }
}
});
diff --git a/ui/src/app/core-ui/image/services/ReactLabeling.service.ts b/ui/src/app/core-ui/image/services/ReactLabeling.service.ts
index 1059dba..1c795b2 100644
--- a/ui/src/app/core-ui/image/services/ReactLabeling.service.ts
+++ b/ui/src/app/core-ui/image/services/ReactLabeling.service.ts
@@ -20,6 +20,7 @@ import Konva from 'konva';
import { Annotation } from '../../../core-model/coco/Annotation';
import { ICoordinates } from '../model/coordinates';
import { ColorService } from './color.service';
+import { LabelingModeService } from './LabelingMode.service';
@Injectable()
export class ReactLabelingService {
@@ -29,8 +30,8 @@ export class ReactLabelingService {
private isLabeling: boolean;
- constructor(private colorService: ColorService) {
-
+ constructor(private colorService: ColorService,
+ private labelingMode: LabelingModeService) {
}
@@ -94,35 +95,38 @@ export class ReactLabelingService {
transformer.attachTo(rect);
}
- this.addDragHandler(rect, annotation, imageView);
+ this.addDragHandler(rect, annotation, imageView, this.labelingMode);
this.addTransformHandler(rect, annotation, imageView);
- this.addMouseHandler(rect, annotation, layer, transformer);
- this.addClickHandler(rect, annotation, layer, transformer);
+ this.addMouseHandler(rect, annotation, layer, transformer, this.labelingMode);
+ this.addClickHandler(rect, annotation, layer, transformer, this.labelingMode);
layer.add(rect);
layer.add(transformer);
}
- private addClickHandler(rect, annotation, layer, transformer) {
+ private addClickHandler(rect, annotation, layer, transformer, labelingMode) {
rect.on('click', function() {
- annotation.isSelected = true;
- transformer.attachTo(this);
- layer.batchDraw();
- });
+ if (labelingMode.isNoneMode()) {
+ annotation.isSelected = !annotation.isSelected;
- rect.on('dblclick', function() {
- annotation.isSelected = false;
- transformer.detach();
- layer.batchDraw();
- });
+ if (annotation.isSelected) {
+ transformer.attachTo(this);
+ } else {
+ transformer.detach();
+ }
+ layer.batchDraw();
+ }
+ });
}
- private addMouseHandler(rect, annotation, layer, transformer) {
+ private addMouseHandler(rect, annotation, layer, transformer, labelingMode) {
rect.on('mouseover', function() {
- annotation.isHovered = true;
- rect.opacity(0.8);
- layer.batchDraw();
+ if (labelingMode.isNoneMode()) {
+ annotation.isHovered = true;
+ rect.opacity(0.8);
+ layer.batchDraw();
+ }
});
rect.on('mouseout', function() {
@@ -132,7 +136,9 @@ export class ReactLabelingService {
});
transformer.on('mouseover', function() {
- annotation.isHovered = true;
+ if (labelingMode.isNoneMode()) {
+ annotation.isHovered = true;
+ }
});
transformer.on('mouseout', function() {
@@ -179,18 +185,25 @@ export class ReactLabelingService {
});
}
- private addDragHandler(rect, annotation, imageView) {
+ private addDragHandler(rect, annotation, imageView, labelingMode) {
let offset: ICoordinates;
rect.on('dragstart', function() {
- const position = imageView.getImagePointerPosition();
- offset = {x: annotation.bbox[0] - position.x, y: annotation.bbox[1] - position.y};
+ if (!labelingMode.isNoneMode()) {
+ rect.stopDrag();
+ } else {
+ const position = imageView.getImagePointerPosition();
+ offset = {x: annotation.bbox[0] - position.x, y: annotation.bbox[1] - position.y};
+ }
});
rect.on('dragmove', function() {
- const position = imageView.getImagePointerPosition();
- annotation.bbox[0] = imageView.getImagePointerPosition().x + offset.x;
- annotation.bbox[1] = imageView.getImagePointerPosition().y + offset.y;
+ if (!labelingMode.isNoneMode()) {
+ rect.stopDrag();
+ } else {
+ const position = imageView.getImagePointerPosition();
+ annotation.bbox[0] = imageView.getImagePointerPosition().x + offset.x;
+ annotation.bbox[1] = imageView.getImagePointerPosition().y + offset.y; }
});
}