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/04/15 17:07:07 UTC

[incubator-streampipes] 02/02: [STREAMPIPES-79] connect image labeling tool with datalake - reading image from datalake - store coco file in datalake - refactor ui

This is an automated email from the ASF dual-hosted git repository.

tex pushed a commit to branch image-labeling
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git

commit 4e6c71bc5d1f6088e67a2b2d7fbf2b4df9076448
Author: tex <te...@fzi.de>
AuthorDate: Wed Apr 15 19:05:18 2020 +0200

    [STREAMPIPES-79] connect image labeling tool with datalake
    - reading image from datalake
    - store coco file in datalake
    - refactor ui
---
 .../rest/impl/datalake/DataLakeManagementV3.java   |  51 ++++++++++
 .../rest/impl/datalake/DataLakeResourceV3.java     |  25 ++++-
 .../app/CustomMaterial/custom-material.module.ts   |   2 +-
 ui/src/app/core-model/coco/Annotation.ts           |  12 ---
 ui/src/app/core-model/coco/Coco.format.ts          |  60 -----------
 .../datalake/datalake-rest.service.ts              |  10 ++
 ui/src/app/core-ui/core-ui.module.ts               |  14 ++-
 .../image-labeling/image-labeling.component.html   |   3 +-
 .../image-labeling/image-labeling.component.ts     | 110 ++++++++++++++++----
 .../core-ui/image/services/CocoFormat.service.ts   | 112 +++++++++++++++++++++
 ui/src/app/core-ui/image/util/color.util.ts        |  33 ++++++
 11 files changed, 329 insertions(+), 103 deletions(-)

diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeManagementV3.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeManagementV3.java
index edcc375..65786d2 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeManagementV3.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeManagementV3.java
@@ -20,6 +20,7 @@ package org.apache.streampipes.rest.impl.datalake;
 
 import com.google.gson.Gson;
 import okhttp3.OkHttpClient;
+import org.apache.commons.io.FileUtils;
 import org.influxdb.InfluxDB;
 import org.influxdb.InfluxDBFactory;
 import org.influxdb.dto.Query;
@@ -31,6 +32,7 @@ import org.apache.streampipes.rest.impl.datalake.model.GroupedDataResult;
 import org.apache.streampipes.rest.impl.datalake.model.PageResult;
 import org.apache.streampipes.storage.management.StorageDispatcher;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.text.ParseException;
@@ -521,4 +523,53 @@ public class DataLakeManagementV3 {
     }
   }
 
+
+  public byte[] getImage(String fileRoute) throws IOException {
+    fileRoute = getImageFileRoute(fileRoute);
+    File file = new File(fileRoute);
+    return FileUtils.readFileToByteArray(file);
+  }
+
+
+  public String getImageCoco(String fileRoute) throws IOException {
+    fileRoute = getImageFileRoute(fileRoute);
+    String cocoRoute = getCocoFileRoute(fileRoute);
+
+    File file = new File(cocoRoute);
+    if (!file.exists()) {
+      return "";
+    } else {
+      return FileUtils.readFileToString(file, "UTF-8");
+    }
+  }
+
+
+  public void saveImageCoco(String fileRoute, String data) throws IOException {
+    fileRoute = getImageFileRoute(fileRoute);
+    String cocoRoute = getCocoFileRoute(fileRoute);
+
+    File file = new File(cocoRoute);
+    file.getParentFile().mkdirs();
+    FileUtils.writeStringToFile(file, data, "UTF-8");
+
+  }
+
+  private String getImageFileRoute(String fileRoute) {
+    fileRoute = fileRoute.replace("_", "/");
+    fileRoute = fileRoute.replace("/png", ".png");
+    return fileRoute;
+  }
+
+  private String getCocoFileRoute(String imageRoute) {
+    String[] splitedRoute = imageRoute.split("/");
+    String route = "";
+    for (int i = 0; splitedRoute.length - 2  >= i; i++) {
+      route += "/" + splitedRoute[i];
+    }
+    route += "Coco";
+    route += "/" + splitedRoute[splitedRoute.length - 1];
+    route = route.replace(".png", ".json");
+    return route;
+  }
+
 }
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResourceV3.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResourceV3.java
index 2eb79f9..abb0a69 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResourceV3.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeResourceV3.java
@@ -31,11 +31,7 @@ import java.io.IOException;
 import java.text.ParseException;
 import java.util.List;
 
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
+import javax.ws.rs.*;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
@@ -208,4 +204,23 @@ public class DataLakeResourceV3 extends AbstractRestInterface {
             .build();
   }
 
+  @GET
+  @Path("/data/image/{route}/file")
+  @Produces("image/png")
+  public Response getImage(@PathParam("route") String fileRoute) throws IOException {
+    return ok(dataLakeManagement.getImage(fileRoute));
+  }
+
+  @GET
+  @Path("/data/image/{route}/coco")
+  @Produces("application/json")
+  public Response getImageCoco(@PathParam("route") String fileRoute) throws IOException {
+    return ok(dataLakeManagement.getImageCoco(fileRoute));
+  }
+
+  @POST
+  @Path("/data/image/{route}/coco")
+  public void saveImageCoco(@PathParam("route") String fileRoute, String data) throws IOException {
+    dataLakeManagement.saveImageCoco(fileRoute, data);
+  }
 }
diff --git a/ui/src/app/CustomMaterial/custom-material.module.ts b/ui/src/app/CustomMaterial/custom-material.module.ts
index 42b9a11..8c76b28 100644
--- a/ui/src/app/CustomMaterial/custom-material.module.ts
+++ b/ui/src/app/CustomMaterial/custom-material.module.ts
@@ -16,7 +16,7 @@
  *
  */
 
-import {NgModule} from '@angular/core';
+import { NgModule } from '@angular/core';
 
 import { MatAutocompleteModule } from '@angular/material/autocomplete';
 import { MatButtonModule } from '@angular/material/button';
diff --git a/ui/src/app/core-model/coco/Annotation.ts b/ui/src/app/core-model/coco/Annotation.ts
index ce76d22..d92082b 100644
--- a/ui/src/app/core-model/coco/Annotation.ts
+++ b/ui/src/app/core-model/coco/Annotation.ts
@@ -20,16 +20,4 @@ export class Annotation {
     this.brushSize = undefined;
   }
 
-  isBox() {
-    return this.bbox !== undefined;
-  }
-
-  isPolygon() {
-    return this.segmentation !== undefined && this.brushSize === undefined;
-  }
-
-  isBrush() {
-    return this.brushSize !== undefined;
-  }
-
 }
diff --git a/ui/src/app/core-model/coco/Coco.format.ts b/ui/src/app/core-model/coco/Coco.format.ts
index 160c6b1..c18821f 100644
--- a/ui/src/app/core-model/coco/Coco.format.ts
+++ b/ui/src/app/core-model/coco/Coco.format.ts
@@ -19,64 +19,4 @@ export class CocoFormat {
 
   constructor() { }
 
-  addImage(fileName) {
-      const image = new Image();
-      image.file_name = fileName;
-      image.id = this.images.length + 1;
-      this.images.push(image);
-  }
-
-  getLabelById(id) {
-    return this.categories.find(elem => elem.id === id).name;
-  }
-
-  getLabelId(supercategory, name): number {
-    let category = this.categories.find(elem => elem.name === name && elem.supercategory === supercategory);
-    if (category === undefined) {
-      category = new Category(this.categories.length + 1, name, supercategory);
-      this.categories.push(category);
-    }
-    return category.id;
-  }
-
-  addReactAnnotationToFirstImage(cords, size, supercategory, category): Annotation {
-    const annotation = new Annotation();
-    annotation.id = this.annotations.length + 1;
-    annotation.iscrowd = 0;
-    annotation.image_id = 1;
-    annotation.bbox = [cords.x, cords.y, size.x, size.y];
-    annotation.category_id = this.getLabelId(supercategory, category);
-    annotation.category_name = category;
-    this.annotations.push(annotation);
-    return annotation;
-  }
-
-  addPolygonAnnotationFirstImage(points, supercategory, category): Annotation {
-    const annotation = new Annotation();
-    annotation.id = this.annotations.length + 1;
-    annotation.iscrowd = 0;
-    annotation.image_id = 1;
-    annotation.segmentation = [points];
-    annotation.category_id = this.getLabelId(supercategory, category);
-    annotation.category_name = category;    this.annotations.push(annotation);
-    return annotation;
-  }
-
-  addBrushAnnotationFirstImage(points, brushSize, supercategory, category): Annotation {
-    const annotation = new Annotation();
-    annotation.id = this.annotations.length + 1;
-    annotation.iscrowd = 0;
-    annotation.image_id = 1;
-    annotation.segmentation = [points];
-    annotation.brushSize = brushSize;
-    annotation.category_id = this.getLabelId(supercategory, category);
-    annotation.category_name = category;
-    this.annotations.push(annotation);
-    return annotation;
-  }
-
-  removeAnnotation(id) {
-    this.annotations = this.annotations.filter(anno => anno.id !== id);
-  }
-
 }
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 5550dbf..f94cbb3 100644
--- a/ui/src/app/core-services/datalake/datalake-rest.service.ts
+++ b/ui/src/app/core-services/datalake/datalake-rest.service.ts
@@ -134,5 +134,15 @@ export class DatalakeRestService {
         };
     }
 
+    getImageUrl(imageRoute) {
+      return this.dataLakeUrlV3 + '/data/image/' + imageRoute + '/file';
+    }
+
+    getCocoFileForImage(imageRoute) {
+      return this.http.get(this.dataLakeUrlV3 + '/data/image/' + imageRoute + '/coco');
+    }
 
+    saveCocoFileForImage(imageRoute, data) {
+      return this.http.post(this.dataLakeUrlV3 + '/data/image/' + imageRoute + '/coco', data);
+    }
 }
diff --git a/ui/src/app/core-ui/core-ui.module.ts b/ui/src/app/core-ui/core-ui.module.ts
index 961d6cb..b6d8546 100644
--- a/ui/src/app/core-ui/core-ui.module.ts
+++ b/ui/src/app/core-ui/core-ui.module.ts
@@ -16,9 +16,9 @@
  *
  */
 
-import {NgModule} from '@angular/core';
-import {FlexLayoutModule} from '@angular/flex-layout';
-import {CommonModule} from '@angular/common';
+import { CommonModule } from '@angular/common';
+import { NgModule } from '@angular/core';
+import { FlexLayoutModule } from '@angular/flex-layout';
 
 import { CdkTableModule } from '@angular/cdk/table';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@@ -31,6 +31,8 @@ import { LineChartComponent } from './linechart/lineChart.component';
 import { TableComponent } from './table/table.component';
 
 // import * as PlotlyJS from 'plotly.js/dist/plotly.js';
+import { MatChipsModule } from '@angular/material/chips';
+import { MatSliderModule } from '@angular/material/slider';
 import { PlotlyViaWindowModule } from 'angular-plotly.js';
 import { ImageAnnotationsComponent } from './image/components/image-annotations/image-annotations.component';
 import { ImageBarComponent } from './image/components/image-bar/image-bar.component';
@@ -38,12 +40,13 @@ import { ImageContainerComponent } from './image/components/image-container/imag
 import { ImageLabelsComponent } from './image/components/image-labels/image-labels.component';
 import { ImageCategorizeComponent } from './image/image-categorize/image-categorize.component';
 import { ImageLabelingComponent } from './image/image-labeling/image-labeling.component';
+import { ImageViewerComponent } from './image/image-viewer/image-viewer.component';
 import { ImageComponent } from './image/image.component';
 import { BrushLabelingService } from './image/services/BrushLabeling.service';
 import { ColorService } from './image/services/color.service';
 import { PolygonLabelingService } from './image/services/PolygonLabeling.service';
 import { ReactLabelingService } from './image/services/ReactLabeling.service';
-import { ImageViewerComponent } from './image/image-viewer/image-viewer.component';
+import { CocoFormatService } from "./image/services/CocoFormat.service";
 // PlotlyViaCDNModule.plotlyjs = PlotlyJS;
 
 @NgModule({
@@ -59,6 +62,8 @@ import { ImageViewerComponent } from './image/image-viewer/image-viewer.componen
         MatDatepickerModule,
         MatNativeDateModule,
         PlotlyViaWindowModule,
+        MatSliderModule,
+        MatChipsModule
     ],
     declarations: [
         TableComponent,
@@ -78,6 +83,7 @@ import { ImageViewerComponent } from './image/image-viewer/image-viewer.componen
         ReactLabelingService,
         PolygonLabelingService,
         BrushLabelingService,
+        CocoFormatService,
     ],
     entryComponents: [
     ],
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 954c33a..08801cc 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
@@ -33,6 +33,7 @@
                     <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>
                     <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>
                 </div>
 
                 <sp-image-container
@@ -48,7 +49,7 @@
             </div>
 
             <sp-image-annotations
-                    [annotations]="this.cocoFiles[this.imagesIndex].annotations"
+                    [annotations]="this.cocoFiles[this.imagesIndex]?.annotations"
                     [labels]="labels"
                     (changeAnnotationLabel)="handleChangeAnnotationLabel($event)"
                     (deleteAnnotation)="handleDeleteAnnotation($event)">
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 42fbd2d..f430817 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
@@ -25,10 +25,11 @@ import { CocoFormat } from '../../../core-model/coco/Coco.format';
 import { DatalakeRestService } from '../../../core-services/datalake/datalake-rest.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 { PolygonLabelingService } from '../services/PolygonLabeling.service';
 import { ReactLabelingService } from '../services/ReactLabeling.service';
-import { LabelingMode } from '../model/labeling-mode';
+import { CocoFormatService } from "../services/CocoFormat.service";
 
 @Component({
   selector: 'sp-image-labeling',
@@ -52,37 +53,103 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
 
   @ViewChild(ImageContainerComponent) imageView: ImageContainerComponent;
 
+  measureName = 'testsix'; // TODO: Remove hard coded Index, should be injected
+  eventSchema = undefined; // TODO: event schema should be also injected
+  imageField = undefined;
+  pageIndex = undefined;
+  pageSum = undefined;
+
 
   public labelingMode: LabelingMode = LabelingMode.ReactLabeling;
 
   constructor(private restService: DatalakeRestService, private reactLabelingService: ReactLabelingService,
               private polygonLabelingService: PolygonLabelingService, private brushLabelingService: BrushLabelingService,
-              private snackBar: MatSnackBar) { }
+              private snackBar: MatSnackBar, private cocoFormatService: CocoFormatService) { }
 
   ngOnInit(): void {
+
+
     this.isHoverComponent = false;
     this.brushSize = 5;
 
+
+
+
     // 1. get labels
     this.labels = this.restService.getLabels();
 
     // 2. get Images
-    this.imagesSrcs = this.restService.getImageSrcs();
+    this.restService.getAllInfos().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')) {
+            this.imageField = prop;
+            break;
+          }
+        }
+        this.loadData();
+      }
+    );
+
     this.imagesIndex = 0;
 
     // 3. get Coco files
-    this.cocoFiles = [];
-    for (const src of this.imagesSrcs) {
-      const coco = new CocoFormat();
-      coco.addImage(src);
-      this.cocoFiles.push(coco);
-    }
+    // this.cocoFiles = [];
+    // for (const src of this.imagesSrcs) {
+      // const coco = new CocoFormat();
+      // this.cocoFormatService.addImage(coco, scr)
+      // coco.addImage(src);
+      // this.cocoFiles.push(coco);
+    // }
   }
 
   ngAfterViewInit(): void {
     this.imagesIndex = 0;
   }
 
+  loadData() {
+    if (this.pageIndex === undefined) {
+      this.restService.getDataPageWithoutPage(this.measureName, 10).subscribe(
+        res => this.processData(res)
+      );
+    } else {
+      this.restService.getDataPage(this.measureName, 10, this.pageIndex).subscribe(
+        res => this.processData(res)
+      );
+    }
+  }
+
+  processData(pageResult) {
+    this.pageIndex = pageResult.page;
+    this.pageSum = pageResult.pageSum;
+    const imageIndex = pageResult.headers.findIndex(name => name === this.imageField.runtimeName);
+    const tmp = [];
+    this.cocoFiles = [];
+    pageResult.rows.forEach(row => {
+      tmp.push(this.restService.getImageUrl(row[imageIndex]))
+      this.restService.getCocoFileForImage(row[imageIndex]).subscribe(
+        coco => {
+          console.log('------------------------------' +
+            '--------------------------------')
+                  if (coco === null) {
+                    const cocoFile = new CocoFormat();
+                    this.cocoFormatService.addImage(cocoFile, (row[imageIndex]));
+                    this.cocoFiles.push(cocoFile);
+                  } else {
+                    this.cocoFiles.push(coco as CocoFormat);
+                  }
+                  console.log(this.cocoFiles);
+
+
+        }
+      );
+
+    });
+    this.imagesSrcs = tmp;
+  }
+
 
   /* sp-image-view handler */
   handleMouseDownLeft(layer: Konva.Layer, shift: ICoordinates, position: ICoordinates) {
@@ -128,7 +195,7 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
         case LabelingMode.ReactLabeling: {
           const result = this.reactLabelingService.endLabeling(position);
           const coco = this.cocoFiles[this.imagesIndex];
-          const annotation = coco.addReactAnnotationToFirstImage(result[0], result[1],
+          const annotation = this.cocoFormatService.addReactAnnotationToFirstImage(coco, result[0], result[1],
             this.selectedLabel.category, this.selectedLabel.label);
           this.reactLabelingService.draw(annotationLayer, shift, annotation, this.imageView);
         }
@@ -140,7 +207,7 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
         case LabelingMode.BrushLabeling: {
           const result = this.brushLabelingService.endLabeling(position);
           const coco = this.cocoFiles[this.imagesIndex];
-          const annotation = coco.addBrushAnnotationFirstImage(result[0], result[1],
+          const annotation = this.cocoFormatService.addBrushAnnotationFirstImage(coco, result[0], result[1],
             this.selectedLabel.category, this.selectedLabel.label);
           this.brushLabelingService.draw(annotationLayer, shift, annotation, this.imageView);
         }
@@ -155,7 +222,7 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
         case LabelingMode.PolygonLabeling:
           const points = this.polygonLabelingService.endLabeling(position);
           const coco = this.cocoFiles[this.imagesIndex];
-          const annotation = coco.addPolygonAnnotationFirstImage(points,
+          const annotation = this.cocoFormatService.addPolygonAnnotationFirstImage(coco, points,
             this.selectedLabel.category, this.selectedLabel.label);
           this.polygonLabelingService.draw(layer, shift, annotation, this.imageView);
       }
@@ -167,11 +234,11 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
     for (const annotation of coco.annotations) {
       annotation.isHovered = false;
       annotation.isSelected = false;
-      if (annotation.isBox()) {
+      if (this.cocoFormatService.isBoxAnnonation(annotation)) {
         this.reactLabelingService.draw(layer, shift, annotation, this.imageView);
-      } else if (annotation.isPolygon() && !annotation.isBrush()) {
+      } else if (this.cocoFormatService.isPolygonAnnonation(annotation) && !this.cocoFormatService.isBrushAnnonation(annotation)) {
         this.polygonLabelingService.draw(layer, shift, annotation, this.imageView);
-      } else if (annotation.isBrush()) {
+      } else if (this.cocoFormatService.isBrushAnnonation(annotation)) {
         this.brushLabelingService.draw(layer, shift, annotation, this.imageView);
       }
     }
@@ -210,7 +277,7 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
   /* sp-image-annotations handlers */
   handleChangeAnnotationLabel(change: [Annotation, string, string]) {
     const coco = this.cocoFiles[this.imagesIndex];
-    const categoryId = coco.getLabelId(change[1], change[2]);
+    const categoryId = this.cocoFormatService.getLabelId(coco, change[1], change[2]);
     change[0].category_id = categoryId;
     change[0].category_name = change[2];
     this.imageView.redrawAll();
@@ -219,7 +286,7 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
   handleDeleteAnnotation(annotation) {
     if (annotation !== undefined) {
       const coco = this.cocoFiles[this.imagesIndex];
-      coco.removeAnnotation(annotation.id);
+      this.cocoFormatService.removeAnnotation(coco, annotation.id);
       this.imageView.redrawAll();
     }
   }
@@ -236,11 +303,14 @@ export class ImageLabelingComponent implements OnInit, AfterViewInit {
     }
   }
 
-  private save() {
+  save() {
     // TODO
     const coco = this.cocoFiles[this.imagesIndex];
-    console.log(coco);
-    this.openSnackBar('TODO: Save coco file');
+    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) {
diff --git a/ui/src/app/core-ui/image/services/CocoFormat.service.ts b/ui/src/app/core-ui/image/services/CocoFormat.service.ts
new file mode 100644
index 0000000..8c20ca4
--- /dev/null
+++ b/ui/src/app/core-ui/image/services/CocoFormat.service.ts
@@ -0,0 +1,112 @@
+/*
+ * 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 { Annotation } from '../../../core-model/coco/Annotation';
+import { Category } from '../../../core-model/coco/Category';
+import { CocoFormat } from '../../../core-model/coco/Coco.format';
+import { Image } from '../../../core-model/coco/Image';
+
+@Injectable()
+export class CocoFormatService {
+
+
+  addImage(coco: CocoFormat, fileName) {
+    const image = new Image();
+    image.file_name = fileName;
+    image.id = coco.images.length + 1;
+    coco.images.push(image);
+  }
+
+  getLabelById(coco: CocoFormat, id) {
+    return coco.categories.find(elem => elem.id === id).name;
+  }
+
+  getLabelId(coco: CocoFormat, supercategory, name): number {
+    let category = coco.categories.find(elem => elem.name === name && elem.supercategory === supercategory);
+    if (category === undefined) {
+      category = new Category(coco.categories.length + 1, name, supercategory);
+      coco.categories.push(category);
+    }
+    return category.id;
+  }
+
+  static getLabelId(coco: CocoFormat, supercategory, name): number {
+    // TODO: Find  better solution instead of copy same code
+    let category = coco.categories.find(elem => elem.name === name && elem.supercategory === supercategory);
+    if (category === undefined) {
+      category = new Category(coco.categories.length + 1, name, supercategory);
+      coco.categories.push(category);
+    }
+    return category.id;
+  }
+
+  addReactAnnotationToFirstImage(coco: CocoFormat, cords, size, supercategory, category): Annotation {
+    const annotation = new Annotation();
+    annotation.id = coco.annotations.length + 1;
+    annotation.iscrowd = 0;
+    annotation.image_id = 1;
+    annotation.bbox = [cords.x, cords.y, size.x, size.y];
+    annotation.category_id = CocoFormatService.getLabelId(coco, supercategory, category);
+    annotation.category_name = category;
+    coco.annotations.push(annotation);
+    return annotation;
+  }
+
+  addPolygonAnnotationFirstImage(coco: CocoFormat, points, supercategory, category): Annotation {
+    const annotation = new Annotation();
+    annotation.id = coco.annotations.length + 1;
+    annotation.iscrowd = 0;
+    annotation.image_id = 1;
+    annotation.segmentation = [points];
+    annotation.category_id = CocoFormatService.getLabelId(coco, supercategory, category);
+    annotation.category_name = category;
+    coco.annotations.push(annotation);
+    return annotation;
+  }
+
+  addBrushAnnotationFirstImage(coco: CocoFormat, points, brushSize, supercategory, category): Annotation {
+    const annotation = new Annotation();
+    annotation.id = coco.annotations.length + 1;
+    annotation.iscrowd = 0;
+    annotation.image_id = 1;
+    annotation.segmentation = [points];
+    annotation.brushSize = brushSize;
+    annotation.category_id = coco.getLabelId(supercategory, category);
+    annotation.category_name = category;
+    coco.annotations.push(annotation);
+    return annotation;
+  }
+
+  removeAnnotation(coco: CocoFormat, id) {
+    coco.annotations = coco.annotations.filter(anno => anno.id !== id);
+  }
+
+  isBoxAnnonation(annotation: Annotation) {
+    return annotation.bbox !== undefined;
+  }
+
+  isPolygonAnnonation(annotation: Annotation) {
+    return annotation.segmentation !== undefined && annotation.brushSize === undefined;
+  }
+
+  isBrushAnnonation(annotation: Annotation) {
+    return annotation.brushSize !== undefined;
+  }
+
+
+}
diff --git a/ui/src/app/core-ui/image/util/color.util.ts b/ui/src/app/core-ui/image/util/color.util.ts
new file mode 100644
index 0000000..65c4526
--- /dev/null
+++ b/ui/src/app/core-ui/image/util/color.util.ts
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+export class ColorUtil {
+
+  static getColor(label) {
+    var hash = 0;
+    for (var i = 0; i < label.length; i++) {
+      hash = label.charCodeAt(i) + ((hash << 5) - hash);
+    }
+    var colour = '#';
+    for (var i = 0; i < 3; i++) {
+      var value = (hash >> (i * 8)) & 0xFF;
+      colour += ('00' + value.toString(16)).substr(-2);
+    }
+    return colour;
+  }
+
+}
\ No newline at end of file