You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by bo...@apache.org on 2023/03/01 13:09:53 UTC

[streampipes] branch dev updated: Refactor Image Processors (#1331)

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

bossenti pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new 433fe9190 Refactor Image Processors (#1331)
433fe9190 is described below

commit 433fe9190a30fa0b498d64c71b61814a1ede5aa6
Author: Chinmay <ch...@iiitd.ac.in>
AuthorDate: Wed Mar 1 18:39:45 2023 +0530

    Refactor Image Processors (#1331)
    
    * refactor processers in streampipes-processors-image-processing-jvm
    
    * fix checkstyle violations
    
    * rename streampipes-processors-image-processing-jvm controllers to processors
---
 .../jvm/ImageProcessingJvmInit.java                |  16 +--
 .../processor/commons/ImagePropertyConstants.java  |  56 +++++++++
 .../jvm/processor/commons/ImageTransformer.java    |  35 +++---
 .../processor/commons/PlainImageTransformer.java   |   7 +-
 .../GenericImageClassificationController.java      |  73 -----------
 .../GenericImageClassificationParameters.java      |  35 ------
 ...va => GenericImageClassificationProcessor.java} |  80 +++++++++----
 .../jvm/processor/imagecropper/ImageCropper.java   |  76 ------------
 .../imagecropper/ImageCropperController.java       |  67 -----------
 .../imagecropper/ImageCropperParameters.java       |  29 -----
 .../imagecropper/ImageCropperProcessor.java        | 116 ++++++++++++++++++
 .../imageenrichment/ImageEnrichmentController.java |  66 ----------
 .../imageenrichment/ImageEnrichmentParameters.java | 101 ----------------
 ...Enricher.java => ImageEnrichmentProcessor.java} |  71 +++++++----
 .../jvm/processor/qrreader/QrCodeReader.java       |  98 ---------------
 .../processor/qrreader/QrCodeReaderController.java |  77 ------------
 .../processor/qrreader/QrCodeReaderParameters.java |  48 --------
 .../processor/qrreader/QrCodeReaderProcessor.java  | 133 +++++++++++++++++++++
 18 files changed, 435 insertions(+), 749 deletions(-)

diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/ImageProcessingJvmInit.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/ImageProcessingJvmInit.java
index d992816dc..645ad3a8a 100644
--- a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/ImageProcessingJvmInit.java
+++ b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/ImageProcessingJvmInit.java
@@ -27,10 +27,10 @@ import org.apache.streampipes.extensions.management.model.SpServiceDefinitionBui
 import org.apache.streampipes.messaging.jms.SpJmsProtocolFactory;
 import org.apache.streampipes.messaging.kafka.SpKafkaProtocolFactory;
 import org.apache.streampipes.messaging.mqtt.SpMqttProtocolFactory;
-import org.apache.streampipes.processors.imageprocessing.jvm.processor.genericclassification.GenericImageClassificationController;
-import org.apache.streampipes.processors.imageprocessing.jvm.processor.imagecropper.ImageCropperController;
-import org.apache.streampipes.processors.imageprocessing.jvm.processor.imageenrichment.ImageEnrichmentController;
-import org.apache.streampipes.processors.imageprocessing.jvm.processor.qrreader.QrCodeReaderController;
+import org.apache.streampipes.processors.imageprocessing.jvm.processor.genericclassification.GenericImageClassificationProcessor;
+import org.apache.streampipes.processors.imageprocessing.jvm.processor.imagecropper.ImageCropperProcessor;
+import org.apache.streampipes.processors.imageprocessing.jvm.processor.imageenrichment.ImageEnrichmentProcessor;
+import org.apache.streampipes.processors.imageprocessing.jvm.processor.qrreader.QrCodeReaderProcessor;
 import org.apache.streampipes.service.extensions.ExtensionsModelSubmitter;
 
 public class ImageProcessingJvmInit extends ExtensionsModelSubmitter {
@@ -42,10 +42,10 @@ public class ImageProcessingJvmInit extends ExtensionsModelSubmitter {
             "",
             8090)
         .registerPipelineElements(
-            new ImageEnrichmentController(),
-            new ImageCropperController(),
-            new QrCodeReaderController(),
-            new GenericImageClassificationController())
+            new ImageEnrichmentProcessor(),
+            new ImageCropperProcessor(),
+            new QrCodeReaderProcessor(),
+            new GenericImageClassificationProcessor())
         .registerMessagingFormats(
             new JsonDataFormatFactory(),
             new CborDataFormatFactory(),
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/commons/ImagePropertyConstants.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/commons/ImagePropertyConstants.java
new file mode 100644
index 000000000..796da2c9a
--- /dev/null
+++ b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/commons/ImagePropertyConstants.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ *
+ */
+package org.apache.streampipes.processors.imageprocessing.jvm.processor.commons;
+
+public enum ImagePropertyConstants {
+  BOX_WIDTH(Constants.boxWidth),
+  BOX_HEIGHT(Constants.boxHeight),
+  BOX_X(Constants.boxX),
+  BOX_Y(Constants.boxY),
+  SCORE(Constants.score),
+  CLASS_INDEX(Constants.classesindex),
+  IMAGE(Constants.image),
+  CLASS_NAME(Constants.classname),
+  TIMESTAMP(Constants.timestamp),
+  IMAGE_MAPPING(Constants.imageMapping);
+
+
+  private final String property;
+
+  ImagePropertyConstants(String property) {
+    this.property = property;
+  }
+
+  public String getProperty() {
+    return property;
+  }
+
+  private static final class Constants {
+    private static final String boxWidth = "box_width";
+    private static final String boxHeight = "box_height";
+    private static final String boxX = "box_x";
+    private static final String boxY = "box_y";
+    private static final String score = "score";
+    private static final String classesindex = "classesindex";
+    private static final String image = "image";
+    private static final String classname = "classname";
+    private static final String timestamp = "timestamp";
+    private static final String imageMapping = "image-mapping";
+
+  }
+}
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/commons/ImageTransformer.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/commons/ImageTransformer.java
index 199ded104..c4ec892f4 100644
--- a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/commons/ImageTransformer.java
+++ b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/commons/ImageTransformer.java
@@ -20,7 +20,6 @@ package org.apache.streampipes.processors.imageprocessing.jvm.processor.commons;
 import org.apache.streampipes.model.runtime.Event;
 import org.apache.streampipes.model.runtime.field.AbstractField;
 import org.apache.streampipes.processors.imageprocessing.jvm.processor.imageenrichment.BoxCoordinates;
-import org.apache.streampipes.processors.imageprocessing.jvm.processor.imageenrichment.ImageEnrichmentParameters;
 
 import javax.imageio.ImageIO;
 
@@ -33,19 +32,19 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 
-public class ImageTransformer extends PlainImageTransformer<ImageEnrichmentParameters> {
+public class ImageTransformer extends PlainImageTransformer {
 
-  public ImageTransformer(Event in, ImageEnrichmentParameters params) {
-    super(in, params);
+  public ImageTransformer(Event in) {
+    super(in);
   }
 
-  public Optional<BufferedImage> getImage() {
+  public Optional<BufferedImage> getImage(String imageProperty) {
 
-    return getImage(params.getImageProperty());
+    return getImage(imageProperty);
   }
 
-  public List<Map<String, Object>> getAllBoxCoordinates() {
-    List<Map<String, AbstractField>> allBoxes = in.getFieldBySelector(params.getBoxArray())
+  public List<Map<String, Object>> getAllBoxCoordinates(String boxArrayProperty) {
+    List<Map<String, AbstractField>> allBoxes = in.getFieldBySelector(boxArrayProperty)
         .getAsList()
         .parseAsCustomType(value -> value.getAsComposite().getRawValue());
 
@@ -60,22 +59,22 @@ public class ImageTransformer extends PlainImageTransformer<ImageEnrichmentParam
   }
 
   public BoxCoordinates getBoxCoordinates(BufferedImage image, Map<String, Object> box) {
-    Float x = toFloat(box.get(params.getBoxX()));
-    Float y = toFloat(box.get(params.getBoxY()));
-    Float width = toFloat(box.get(params.getBoxWidth()));
-    Float height = toFloat(box.get(params.getBoxHeight()));
+    Float x = toFloat(box.get(ImagePropertyConstants.BOX_X.getProperty()));
+    Float y = toFloat(box.get(ImagePropertyConstants.BOX_Y.getProperty()));
+    Float width = toFloat(box.get(ImagePropertyConstants.BOX_WIDTH.getProperty()));
+    Float height = toFloat(box.get(ImagePropertyConstants.BOX_HEIGHT.getProperty()));
 
 
     return BoxCoordinates.make(width, height, x, y);
   }
 
   public BoxCoordinates getBoxCoordinatesWithAnnotations(BufferedImage image, Map<String, Object> box) {
-    Float x = toFloat(box.get(params.getBoxX()));
-    Float y = toFloat(box.get(params.getBoxY()));
-    Float width = toFloat(box.get(params.getBoxWidth()));
-    Float height = toFloat(box.get(params.getBoxHeight()));
-    Float score = toFloat(box.get(params.getScore()));
-    String classesindex = toString(box.get(params.getClassesindex()));
+    Float x = toFloat(box.get(ImagePropertyConstants.BOX_X.getProperty()));
+    Float y = toFloat(box.get(ImagePropertyConstants.BOX_Y.getProperty()));
+    Float width = toFloat(box.get(ImagePropertyConstants.BOX_WIDTH.getProperty()));
+    Float height = toFloat(box.get(ImagePropertyConstants.BOX_WIDTH.getProperty()));
+    Float score = toFloat(box.get(ImagePropertyConstants.SCORE.getProperty()));
+    String classesindex = toString(box.get(ImagePropertyConstants.CLASS_INDEX.getProperty()));
 
     return BoxCoordinates.make(width, height, x, y, score, classesindex);
   }
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/commons/PlainImageTransformer.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/commons/PlainImageTransformer.java
index 7fdc755da..192cdbed0 100644
--- a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/commons/PlainImageTransformer.java
+++ b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/commons/PlainImageTransformer.java
@@ -18,7 +18,6 @@
 package org.apache.streampipes.processors.imageprocessing.jvm.processor.commons;
 
 import org.apache.streampipes.model.runtime.Event;
-import org.apache.streampipes.wrapper.params.binding.EventProcessorBindingParams;
 
 import javax.imageio.ImageIO;
 
@@ -29,14 +28,12 @@ import java.io.InputStream;
 import java.util.Base64;
 import java.util.Optional;
 
-public class PlainImageTransformer<T extends EventProcessorBindingParams> {
+public class PlainImageTransformer {
 
   protected Event in;
-  protected T params;
 
-  public PlainImageTransformer(Event in, T params) {
+  public PlainImageTransformer(Event in) {
     this.in = in;
-    this.params = params;
   }
 
   public Optional<BufferedImage> getImage(String imagePropertyName) {
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/genericclassification/GenericImageClassificationController.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/genericclassification/GenericImageClassificationController.java
deleted file mode 100644
index 339e4cd05..000000000
--- a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/genericclassification/GenericImageClassificationController.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.
- *
- */
-package org.apache.streampipes.processors.imageprocessing.jvm.processor.genericclassification;
-
-import org.apache.streampipes.model.DataProcessorType;
-import org.apache.streampipes.model.graph.DataProcessorDescription;
-import org.apache.streampipes.model.graph.DataProcessorInvocation;
-import org.apache.streampipes.model.schema.PropertyScope;
-import org.apache.streampipes.sdk.builder.ProcessingElementBuilder;
-import org.apache.streampipes.sdk.builder.StreamRequirementsBuilder;
-import org.apache.streampipes.sdk.extractor.ProcessingElementParameterExtractor;
-import org.apache.streampipes.sdk.helpers.EpProperties;
-import org.apache.streampipes.sdk.helpers.EpRequirements;
-import org.apache.streampipes.sdk.helpers.Labels;
-import org.apache.streampipes.sdk.helpers.Locales;
-import org.apache.streampipes.sdk.helpers.OutputStrategies;
-import org.apache.streampipes.sdk.utils.Assets;
-import org.apache.streampipes.wrapper.standalone.ConfiguredEventProcessor;
-import org.apache.streampipes.wrapper.standalone.declarer.StandaloneEventProcessingDeclarer;
-
-public class GenericImageClassificationController
-    extends StandaloneEventProcessingDeclarer<GenericImageClassificationParameters> {
-
-  private static final String IMAGE = "image-mapping";
-
-  @Override
-  public DataProcessorDescription declareModel() {
-    return ProcessingElementBuilder.create(
-            "org.apache.streampipes.processor.imageclassification.jvm.generic-image-classification")
-        .category(DataProcessorType.IMAGE_PROCESSING)
-        .withAssets(Assets.DOCUMENTATION)
-        .withLocales(Locales.EN)
-        .requiredStream(StreamRequirementsBuilder
-            .create()
-            .requiredPropertyWithUnaryMapping(EpRequirements
-                    .domainPropertyReq("https://image.com"), Labels.withId(IMAGE),
-                PropertyScope.NONE)
-            .build())
-        .outputStrategy(OutputStrategies.append(
-            EpProperties.doubleEp(Labels.empty(), "score", "https://schema.org/score"),
-            EpProperties.stringEp(Labels.empty(), "category", "https://schema.org/category")
-
-        ))
-        .build();
-  }
-
-  @Override
-  public ConfiguredEventProcessor<GenericImageClassificationParameters> onInvocation(
-      DataProcessorInvocation graph,
-      ProcessingElementParameterExtractor extractor) {
-
-    String imageProperty = extractor.mappingPropertyValue(IMAGE);
-
-    GenericImageClassificationParameters staticParam = new GenericImageClassificationParameters(graph, imageProperty);
-
-    return new ConfiguredEventProcessor<>(staticParam, GenericImageClassification::new);
-  }
-}
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/genericclassification/GenericImageClassificationParameters.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/genericclassification/GenericImageClassificationParameters.java
deleted file mode 100644
index 940aa8222..000000000
--- a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/genericclassification/GenericImageClassificationParameters.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- *
- */
-package org.apache.streampipes.processors.imageprocessing.jvm.processor.genericclassification;
-
-import org.apache.streampipes.model.graph.DataProcessorInvocation;
-import org.apache.streampipes.wrapper.params.binding.EventProcessorBindingParams;
-
-public class GenericImageClassificationParameters extends EventProcessorBindingParams {
-
-  private String imagePropertyName;
-
-  public GenericImageClassificationParameters(DataProcessorInvocation graph, String imagePropertyName) {
-    super(graph);
-    this.imagePropertyName = imagePropertyName;
-  }
-
-  public String getImagePropertyName() {
-    return imagePropertyName;
-  }
-}
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/genericclassification/GenericImageClassification.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/genericclassification/GenericImageClassificationProcessor.java
similarity index 50%
rename from streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/genericclassification/GenericImageClassification.java
rename to streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/genericclassification/GenericImageClassificationProcessor.java
index 72eebe85b..a40b180e1 100644
--- a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/genericclassification/GenericImageClassification.java
+++ b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/genericclassification/GenericImageClassificationProcessor.java
@@ -17,11 +17,26 @@
  */
 package org.apache.streampipes.processors.imageprocessing.jvm.processor.genericclassification;
 
+import org.apache.streampipes.commons.exceptions.SpRuntimeException;
+import org.apache.streampipes.model.DataProcessorType;
+import org.apache.streampipes.model.graph.DataProcessorDescription;
 import org.apache.streampipes.model.runtime.Event;
+import org.apache.streampipes.model.schema.PropertyScope;
+import org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.ImagePropertyConstants;
 import org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.PlainImageTransformer;
+import org.apache.streampipes.sdk.builder.ProcessingElementBuilder;
+import org.apache.streampipes.sdk.builder.StreamRequirementsBuilder;
+import org.apache.streampipes.sdk.extractor.ProcessingElementParameterExtractor;
+import org.apache.streampipes.sdk.helpers.EpProperties;
+import org.apache.streampipes.sdk.helpers.EpRequirements;
+import org.apache.streampipes.sdk.helpers.Labels;
+import org.apache.streampipes.sdk.helpers.Locales;
+import org.apache.streampipes.sdk.helpers.OutputStrategies;
+import org.apache.streampipes.sdk.utils.Assets;
 import org.apache.streampipes.wrapper.context.EventProcessorRuntimeContext;
 import org.apache.streampipes.wrapper.routing.SpOutputCollector;
-import org.apache.streampipes.wrapper.runtime.EventProcessor;
+import org.apache.streampipes.wrapper.standalone.ProcessorParams;
+import org.apache.streampipes.wrapper.standalone.StreamPipesDataProcessor;
 
 import boofcv.abst.scene.ImageClassifier;
 import boofcv.factory.scene.ClassifierAndSource;
@@ -34,23 +49,47 @@ import deepboof.io.DeepBoofDataBaseOps;
 import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.IOException;
-import java.util.Comparator;
 import java.util.List;
 import java.util.Optional;
 
-public class GenericImageClassification implements EventProcessor<GenericImageClassificationParameters> {
+public class GenericImageClassificationProcessor extends StreamPipesDataProcessor {
+
+  private String imagePropertyName;
 
-  private GenericImageClassificationParameters params;
   private ClassifierAndSource cs;
 
   private ImageClassifier<Planar<GrayF32>> classifier;
+
   private List<String> categories;
 
   @Override
-  public void onInvocation(GenericImageClassificationParameters genericImageClassificationParameters,
-                           SpOutputCollector spOutputCollector, EventProcessorRuntimeContext runtimeContext) {
-    this.params = genericImageClassificationParameters;
-    //this.cs = FactoryImageClassifier.vgg_cifar10();  // Test set 89.9% for 10 categories
+  public DataProcessorDescription declareModel() {
+    return ProcessingElementBuilder.create(
+            "org.apache.streampipes.processor.imageclassification.jvm.generic-image-classification")
+        .category(DataProcessorType.IMAGE_PROCESSING)
+        .withAssets(Assets.DOCUMENTATION)
+        .withLocales(Locales.EN)
+        .requiredStream(StreamRequirementsBuilder
+            .create()
+            .requiredPropertyWithUnaryMapping(EpRequirements
+                .domainPropertyReq("https://image.com"), Labels.withId(
+                ImagePropertyConstants.IMAGE_MAPPING.getProperty()), PropertyScope.NONE)
+            .build())
+        .outputStrategy(OutputStrategies.append(
+            EpProperties.doubleEp(Labels.empty(), "score", "https://schema.org/score"),
+            EpProperties.stringEp(Labels.empty(), "category", "https://schema.org/category")
+
+        ))
+        .build();
+  }
+
+  @Override
+  public void onInvocation(ProcessorParams parameters, SpOutputCollector spOutputCollector,
+                           EventProcessorRuntimeContext runtimeContext) throws SpRuntimeException {
+    ProcessingElementParameterExtractor extractor = parameters.extractor();
+
+    imagePropertyName = extractor.mappingPropertyValue(ImagePropertyConstants.IMAGE_MAPPING.getProperty());
+    // this.cs = FactoryImageClassifier.vgg_cifar10();  // Test set 89.9% for 10 categories
     ClassifierAndSource cs = FactoryImageClassifier.nin_imagenet(); // Test set 62.6% for 1000 categories
 
     File path = DeepBoofDataBaseOps.downloadModel(cs.getSource(), new File("download_data"));
@@ -59,19 +98,16 @@ public class GenericImageClassification implements EventProcessor<GenericImageCl
     try {
       this.classifier.loadModel(path);
     } catch (IOException e) {
-      e.printStackTrace();
+      throw new SpRuntimeException(e.getMessage());
     }
     this.categories = classifier.getCategories();
   }
 
   @Override
-  public void onEvent(Event in, SpOutputCollector out) {
-    PlainImageTransformer<GenericImageClassificationParameters> imageTransformer = new
-        PlainImageTransformer<>(in,
-        params);
+  public void onEvent(Event in, SpOutputCollector out) throws SpRuntimeException {
+    PlainImageTransformer imageTransformer = new PlainImageTransformer(in);
 
-
-    Optional<BufferedImage> imageOpt = imageTransformer.getImage(params.getImagePropertyName());
+    Optional<BufferedImage> imageOpt = imageTransformer.getImage(imagePropertyName);
     if (imageOpt.isPresent()) {
       BufferedImage buffered = imageOpt.get();
       Planar<GrayF32> image = new Planar<>(GrayF32.class, buffered.getWidth(), buffered.getHeight(), 3);
@@ -79,15 +115,9 @@ public class GenericImageClassification implements EventProcessor<GenericImageCl
 
       classifier.classify(image);
       List<ImageClassifier.Score> scores = classifier.getAllResults();
-      scores.sort(new Comparator<ImageClassifier.Score>() {
-        @Override
-        public int compare(ImageClassifier.Score o1, ImageClassifier.Score o2) {
-          return (o1.score - o2.score) >= 0 ? -1 : 1;
-        }
-      });
-      //Collections.reverse(scores);
-
-      if (scores.size() > 0) {
+      scores.sort((o1, o2) -> Double.compare(o2.score, o1.score));
+
+      if (!scores.isEmpty()) {
         System.out.println(scores.get(0).score + ":" + categories.get(scores.get(0).category));
         //scores.forEach(score -> System.out.println(score.category +":" +categories.get(score.category) +":" +score));
         in.addField("score", scores.get(0).score);
@@ -98,7 +128,7 @@ public class GenericImageClassification implements EventProcessor<GenericImageCl
   }
 
   @Override
-  public void onDetach() {
+  public void onDetach() throws SpRuntimeException {
 
   }
 }
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imagecropper/ImageCropper.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imagecropper/ImageCropper.java
deleted file mode 100644
index cbb1d64f4..000000000
--- a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imagecropper/ImageCropper.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.
- *
- */
-package org.apache.streampipes.processors.imageprocessing.jvm.processor.imagecropper;
-
-import org.apache.streampipes.model.runtime.Event;
-import org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.ImageTransformer;
-import org.apache.streampipes.processors.imageprocessing.jvm.processor.imageenrichment.BoxCoordinates;
-import org.apache.streampipes.wrapper.context.EventProcessorRuntimeContext;
-import org.apache.streampipes.wrapper.routing.SpOutputCollector;
-import org.apache.streampipes.wrapper.runtime.EventProcessor;
-
-import java.awt.image.BufferedImage;
-import java.util.Base64;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-public class ImageCropper implements EventProcessor<ImageCropperParameters> {
-
-  private ImageCropperParameters params;
-
-  @Override
-  public void onInvocation(ImageCropperParameters imageCropperParameters, SpOutputCollector spOutputCollector,
-                           EventProcessorRuntimeContext runtimeContext) {
-    this.params = imageCropperParameters;
-  }
-
-  @Override
-  public void onEvent(Event in, SpOutputCollector out) {
-    ImageTransformer imageTransformer = new ImageTransformer(in, params);
-    Optional<BufferedImage> imageOpt = imageTransformer.getImage();
-
-    if (imageOpt.isPresent()) {
-      BufferedImage image = imageOpt.get();
-      List<Map<String, Object>> allBoxCoordinates = imageTransformer.getAllBoxCoordinates();
-
-      for (Map<String, Object> box : allBoxCoordinates) {
-        BoxCoordinates boxCoordinates = imageTransformer.getBoxCoordinates(image, box);
-
-        BufferedImage dest = image.getSubimage(boxCoordinates.getX(), boxCoordinates.getY(), boxCoordinates.getWidth(),
-            boxCoordinates.getHeight());
-
-        Optional<byte[]> finalImage = imageTransformer.makeImage(dest);
-
-        if (finalImage.isPresent()) {
-          Event outEvent = new Event();
-          outEvent.addField("timestamp", in.getFieldByRuntimeName("timestamp").getAsPrimitive().getAsLong());
-          outEvent.addField("image", Base64.getEncoder().encodeToString(finalImage.get()));
-          outEvent.addField("classname", box.get("classname"));
-          outEvent.addField("score", box.get("score"));
-          out.collect(outEvent);
-        }
-      }
-    }
-  }
-
-  @Override
-  public void onDetach() {
-
-  }
-}
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imagecropper/ImageCropperController.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imagecropper/ImageCropperController.java
deleted file mode 100644
index 97868ff41..000000000
--- a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imagecropper/ImageCropperController.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.
- *
- */
-package org.apache.streampipes.processors.imageprocessing.jvm.processor.imagecropper;
-
-
-import org.apache.streampipes.model.DataProcessorType;
-import org.apache.streampipes.model.graph.DataProcessorDescription;
-import org.apache.streampipes.model.graph.DataProcessorInvocation;
-import org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.RequiredBoxStream;
-import org.apache.streampipes.sdk.builder.ProcessingElementBuilder;
-import org.apache.streampipes.sdk.extractor.ProcessingElementParameterExtractor;
-import org.apache.streampipes.sdk.helpers.EpProperties;
-import org.apache.streampipes.sdk.helpers.Labels;
-import org.apache.streampipes.sdk.helpers.Locales;
-import org.apache.streampipes.sdk.helpers.OutputStrategies;
-import org.apache.streampipes.sdk.utils.Assets;
-import org.apache.streampipes.wrapper.standalone.ConfiguredEventProcessor;
-import org.apache.streampipes.wrapper.standalone.declarer.StandaloneEventProcessingDeclarer;
-
-import static org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.RequiredBoxStream.IMAGE_PROPERTY;
-
-
-public class ImageCropperController extends StandaloneEventProcessingDeclarer<ImageCropperParameters> {
-
-  @Override
-  public DataProcessorDescription declareModel() {
-    return ProcessingElementBuilder.create("org.apache.streampipes.processor.imageclassification.jvm.image-cropper")
-        .withAssets(Assets.DOCUMENTATION, Assets.ICON)
-        .withLocales(Locales.EN)
-        .category(DataProcessorType.IMAGE_PROCESSING)
-        .requiredStream(RequiredBoxStream.getBoxStream())
-        .outputStrategy(OutputStrategies.append(
-            EpProperties.integerEp(Labels.empty(), "classname", "https://streampipes.org/classname"),
-            EpProperties.doubleEp(Labels.empty(), "score", "https://streampipes.org/Label")
-        ))
-        .build();
-  }
-
-  @Override
-  public ConfiguredEventProcessor<ImageCropperParameters> onInvocation(DataProcessorInvocation dataProcessorInvocation,
-                                                                       ProcessingElementParameterExtractor extractor) {
-
-    String imageProperty = extractor.mappingPropertyValue(IMAGE_PROPERTY);
-    String boxArray = extractor.mappingPropertyValue(RequiredBoxStream.BOX_ARRAY_PROPERTY);
-
-    ImageCropperParameters params = new ImageCropperParameters(dataProcessorInvocation, imageProperty,
-        boxArray, "box_width", "box_height", "box_x", "box_y");
-
-    return new ConfiguredEventProcessor<>(params, ImageCropper::new);
-  }
-
-}
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imagecropper/ImageCropperParameters.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imagecropper/ImageCropperParameters.java
deleted file mode 100644
index 183a57e8d..000000000
--- a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imagecropper/ImageCropperParameters.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.
- *
- */
-package org.apache.streampipes.processors.imageprocessing.jvm.processor.imagecropper;
-
-import org.apache.streampipes.model.graph.DataProcessorInvocation;
-import org.apache.streampipes.processors.imageprocessing.jvm.processor.imageenrichment.ImageEnrichmentParameters;
-
-public class ImageCropperParameters extends ImageEnrichmentParameters {
-
-  public ImageCropperParameters(DataProcessorInvocation graph, String imageProperty, String boxArray, String boxWidth,
-                                String boxHeight, String boxX, String boxY) {
-    super(graph, imageProperty, boxArray, boxWidth, boxHeight, boxX, boxY);
-  }
-}
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imagecropper/ImageCropperProcessor.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imagecropper/ImageCropperProcessor.java
new file mode 100644
index 000000000..6aa45afd7
--- /dev/null
+++ b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imagecropper/ImageCropperProcessor.java
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ *
+ */
+package org.apache.streampipes.processors.imageprocessing.jvm.processor.imagecropper;
+
+
+import org.apache.streampipes.commons.exceptions.SpRuntimeException;
+import org.apache.streampipes.model.DataProcessorType;
+import org.apache.streampipes.model.graph.DataProcessorDescription;
+import org.apache.streampipes.model.runtime.Event;
+import org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.ImagePropertyConstants;
+import org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.ImageTransformer;
+import org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.RequiredBoxStream;
+import org.apache.streampipes.processors.imageprocessing.jvm.processor.imageenrichment.BoxCoordinates;
+import org.apache.streampipes.sdk.builder.ProcessingElementBuilder;
+import org.apache.streampipes.sdk.extractor.ProcessingElementParameterExtractor;
+import org.apache.streampipes.sdk.helpers.EpProperties;
+import org.apache.streampipes.sdk.helpers.Labels;
+import org.apache.streampipes.sdk.helpers.Locales;
+import org.apache.streampipes.sdk.helpers.OutputStrategies;
+import org.apache.streampipes.sdk.utils.Assets;
+import org.apache.streampipes.wrapper.context.EventProcessorRuntimeContext;
+import org.apache.streampipes.wrapper.routing.SpOutputCollector;
+import org.apache.streampipes.wrapper.standalone.ProcessorParams;
+import org.apache.streampipes.wrapper.standalone.StreamPipesDataProcessor;
+
+import java.awt.image.BufferedImage;
+import java.util.Base64;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public class ImageCropperProcessor extends StreamPipesDataProcessor {
+
+  private String imageProperty;
+  private String boxArray;
+
+  @Override
+  public DataProcessorDescription declareModel() {
+    return ProcessingElementBuilder.create("org.apache.streampipes.processor.imageclassification.jvm.image-cropper")
+        .withAssets(Assets.DOCUMENTATION, Assets.ICON)
+        .withLocales(Locales.EN)
+        .category(DataProcessorType.IMAGE_PROCESSING)
+        .requiredStream(RequiredBoxStream.getBoxStream())
+        .outputStrategy(OutputStrategies.append(EpProperties.integerEp(Labels.empty(),
+                ImagePropertyConstants.CLASS_NAME.getProperty(), "https://streampipes.org/classname"),
+            EpProperties.doubleEp(Labels.empty(), ImagePropertyConstants.SCORE.getProperty(),
+                "https://streampipes.org/Label")))
+        .build();
+  }
+
+  @Override
+  public void onInvocation(ProcessorParams parameters, SpOutputCollector spOutputCollector,
+                           EventProcessorRuntimeContext runtimeContext) {
+    ProcessingElementParameterExtractor extractor = parameters.extractor();
+    this.imageProperty = extractor.mappingPropertyValue(RequiredBoxStream.IMAGE_PROPERTY);
+    this.boxArray = extractor.mappingPropertyValue(RequiredBoxStream.BOX_ARRAY_PROPERTY);
+  }
+
+  @Override
+  public void onEvent(Event in, SpOutputCollector out) throws SpRuntimeException {
+    ImageTransformer imageTransformer = new ImageTransformer(in);
+    Optional<BufferedImage> imageOpt = imageTransformer.getImage(imageProperty);
+
+    if (imageOpt.isPresent()) {
+      BufferedImage image = imageOpt.get();
+      List<Map<String, Object>> allBoxCoordinates = imageTransformer.getAllBoxCoordinates(boxArray);
+
+      for (Map<String, Object> box : allBoxCoordinates) {
+        BoxCoordinates boxCoordinates = imageTransformer.getBoxCoordinates(image, box);
+
+        BufferedImage dest = image.getSubimage(boxCoordinates.getX(), boxCoordinates.getY(), boxCoordinates.getWidth(),
+            boxCoordinates.getHeight());
+
+        Optional<byte[]> finalImage = imageTransformer.makeImage(dest);
+
+        if (finalImage.isPresent()) {
+          Event outEvent = new Event();
+
+          outEvent.addField(ImagePropertyConstants.TIMESTAMP.getProperty(),
+              in.getFieldByRuntimeName(ImagePropertyConstants.TIMESTAMP.getProperty()).getAsPrimitive().getAsLong());
+
+          outEvent.addField(ImagePropertyConstants.IMAGE.getProperty(),
+              Base64.getEncoder().encodeToString(finalImage.get()));
+
+          outEvent.addField(ImagePropertyConstants.CLASS_NAME.getProperty(),
+              box.get(ImagePropertyConstants.CLASS_NAME.getProperty()));
+
+          outEvent.addField(ImagePropertyConstants.SCORE.getProperty(),
+              box.get(ImagePropertyConstants.SCORE.getProperty()));
+
+          out.collect(outEvent);
+        }
+      }
+    }
+  }
+
+  @Override
+  public void onDetach() throws SpRuntimeException {
+
+  }
+}
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imageenrichment/ImageEnrichmentController.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imageenrichment/ImageEnrichmentController.java
deleted file mode 100644
index 95cdf17a1..000000000
--- a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imageenrichment/ImageEnrichmentController.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.
- *
- */
-package org.apache.streampipes.processors.imageprocessing.jvm.processor.imageenrichment;
-
-import org.apache.streampipes.model.DataProcessorType;
-import org.apache.streampipes.model.graph.DataProcessorDescription;
-import org.apache.streampipes.model.graph.DataProcessorInvocation;
-import org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.RequiredBoxStream;
-import org.apache.streampipes.sdk.builder.ProcessingElementBuilder;
-import org.apache.streampipes.sdk.extractor.ProcessingElementParameterExtractor;
-import org.apache.streampipes.sdk.helpers.EpProperties;
-import org.apache.streampipes.sdk.helpers.Labels;
-import org.apache.streampipes.sdk.helpers.Locales;
-import org.apache.streampipes.sdk.helpers.OutputStrategies;
-import org.apache.streampipes.sdk.utils.Assets;
-import org.apache.streampipes.wrapper.standalone.ConfiguredEventProcessor;
-import org.apache.streampipes.wrapper.standalone.declarer.StandaloneEventProcessingDeclarer;
-
-import static org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.RequiredBoxStream.IMAGE_PROPERTY;
-
-public class ImageEnrichmentController extends StandaloneEventProcessingDeclarer<ImageEnrichmentParameters> {
-
-  @Override
-  public DataProcessorDescription declareModel() {
-    return ProcessingElementBuilder.create("org.apache.streampipes.processor.imageclassification.jvm.image-enricher")
-        .withAssets(Assets.DOCUMENTATION, Assets.ICON)
-        .withLocales(Locales.EN)
-        .category(DataProcessorType.IMAGE_PROCESSING)
-        .requiredStream(RequiredBoxStream.getBoxStream())
-        .outputStrategy(OutputStrategies.fixed(
-            EpProperties.stringEp(Labels.empty(), "image", "https://image.com")
-        ))
-        .build();
-  }
-
-  @Override
-  public ConfiguredEventProcessor<ImageEnrichmentParameters> onInvocation(
-      DataProcessorInvocation dataProcessorInvocation, ProcessingElementParameterExtractor extractor) {
-    String imageProperty = extractor.mappingPropertyValue(IMAGE_PROPERTY);
-    String boxArray = extractor.mappingPropertyValue(RequiredBoxStream.BOX_ARRAY_PROPERTY);
-
-    ImageEnrichmentParameters params = new ImageEnrichmentParameters(dataProcessorInvocation, imageProperty,
-        boxArray, "box_width", "box_height", "box_x", "box_y", "score", "classesindex");
-
-    return new ConfiguredEventProcessor<>(params, ImageEnricher::new);
-
-
-  }
-
-
-}
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imageenrichment/ImageEnrichmentParameters.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imageenrichment/ImageEnrichmentParameters.java
deleted file mode 100644
index af8089b63..000000000
--- a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imageenrichment/ImageEnrichmentParameters.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.
- *
- */
-package org.apache.streampipes.processors.imageprocessing.jvm.processor.imageenrichment;
-
-import org.apache.streampipes.model.graph.DataProcessorInvocation;
-import org.apache.streampipes.wrapper.params.binding.EventProcessorBindingParams;
-
-public class ImageEnrichmentParameters extends EventProcessorBindingParams {
-
-  private String imageProperty;
-  private String boxArray;
-
-  private String boxWidth;
-  private String boxHeight;
-  private String boxX;
-  private String boxY;
-  private String score;
-  private String classesindex;
-
-  public ImageEnrichmentParameters(DataProcessorInvocation graph, String imageProperty, String boxArray,
-                                   String boxWidth, String boxHeight, String boxX, String boxY, String score,
-                                   String classesindex) {
-    super(graph);
-    this.imageProperty = imageProperty;
-    this.boxArray = boxArray;
-    this.boxWidth = boxWidth;
-    this.boxHeight = boxHeight;
-    this.boxX = boxX;
-    this.boxY = boxY;
-    this.score = score;
-    this.classesindex = classesindex;
-  }
-
-  public ImageEnrichmentParameters(DataProcessorInvocation graph, String imageProperty, String boxArray,
-                                   String boxWidth, String boxHeight, String boxX, String boxY) {
-    super(graph);
-    this.imageProperty = imageProperty;
-    this.boxArray = boxArray;
-    this.boxWidth = boxWidth;
-    this.boxHeight = boxHeight;
-    this.boxX = boxX;
-    this.boxY = boxY;
-  }
-
-  public ImageEnrichmentParameters(DataProcessorInvocation graph, String imageProperty, String boxWidth,
-                                   String boxHeight, String boxX, String boxY) {
-    super(graph);
-    this.imageProperty = imageProperty;
-    this.boxWidth = boxWidth;
-    this.boxHeight = boxHeight;
-    this.boxX = boxX;
-    this.boxY = boxY;
-  }
-
-  public String getImageProperty() {
-    return imageProperty;
-  }
-
-  public String getBoxArray() {
-    return boxArray;
-  }
-
-  public String getBoxWidth() {
-    return boxWidth;
-  }
-
-  public String getBoxHeight() {
-    return boxHeight;
-  }
-
-  public String getBoxX() {
-    return boxX;
-  }
-
-  public String getBoxY() {
-    return boxY;
-  }
-
-  public String getScore() {
-    return score;
-  }
-
-  public String getClassesindex() {
-    return classesindex;
-  }
-}
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imageenrichment/ImageEnricher.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imageenrichment/ImageEnrichmentProcessor.java
similarity index 51%
rename from streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imageenrichment/ImageEnricher.java
rename to streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imageenrichment/ImageEnrichmentProcessor.java
index 6e6318002..827df8fbb 100644
--- a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imageenrichment/ImageEnricher.java
+++ b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/imageenrichment/ImageEnrichmentProcessor.java
@@ -17,10 +17,24 @@
  */
 package org.apache.streampipes.processors.imageprocessing.jvm.processor.imageenrichment;
 
+import org.apache.streampipes.commons.exceptions.SpRuntimeException;
+import org.apache.streampipes.model.DataProcessorType;
+import org.apache.streampipes.model.graph.DataProcessorDescription;
+import org.apache.streampipes.model.runtime.Event;
+import org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.ImagePropertyConstants;
 import org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.ImageTransformer;
+import org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.RequiredBoxStream;
+import org.apache.streampipes.sdk.builder.ProcessingElementBuilder;
+import org.apache.streampipes.sdk.extractor.ProcessingElementParameterExtractor;
+import org.apache.streampipes.sdk.helpers.EpProperties;
+import org.apache.streampipes.sdk.helpers.Labels;
+import org.apache.streampipes.sdk.helpers.Locales;
+import org.apache.streampipes.sdk.helpers.OutputStrategies;
+import org.apache.streampipes.sdk.utils.Assets;
 import org.apache.streampipes.wrapper.context.EventProcessorRuntimeContext;
 import org.apache.streampipes.wrapper.routing.SpOutputCollector;
-import org.apache.streampipes.wrapper.runtime.EventProcessor;
+import org.apache.streampipes.wrapper.standalone.ProcessorParams;
+import org.apache.streampipes.wrapper.standalone.StreamPipesDataProcessor;
 
 import java.awt.BasicStroke;
 import java.awt.Color;
@@ -34,44 +48,56 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 
-public class ImageEnricher implements EventProcessor<ImageEnrichmentParameters> {
-
-  private ImageEnrichmentParameters params;
+public class ImageEnrichmentProcessor extends StreamPipesDataProcessor {
+  private String imageProperty;
+  private String boxArray;
 
   @Override
-  public void onInvocation(ImageEnrichmentParameters params, SpOutputCollector spOutputCollector,
-                           EventProcessorRuntimeContext runtimeContext) {
-    this.params = params;
+  public DataProcessorDescription declareModel() {
+    return ProcessingElementBuilder.create("org.apache.streampipes.processor.imageclassification.jvm.image-enricher")
+        .withAssets(Assets.DOCUMENTATION, Assets.ICON)
+        .withLocales(Locales.EN)
+        .category(DataProcessorType.IMAGE_PROCESSING)
+        .requiredStream(RequiredBoxStream.getBoxStream())
+        .outputStrategy(OutputStrategies.fixed(
+            EpProperties.stringEp(Labels.empty(), ImagePropertyConstants.IMAGE.getProperty(),
+                "https://image.com")
+        ))
+        .build();
   }
 
   @Override
-  public void onEvent(org.apache.streampipes.model.runtime.Event in, SpOutputCollector out) {
-    ImageTransformer imageTransformer = new ImageTransformer(in, params);
-
-    Optional<BufferedImage> imageOpt =
-        imageTransformer.getImage();
+  public void onInvocation(ProcessorParams parameters, SpOutputCollector spOutputCollector,
+                           EventProcessorRuntimeContext runtimeContext) throws SpRuntimeException {
+    ProcessingElementParameterExtractor extractor = parameters.extractor();
+    this.imageProperty = extractor.mappingPropertyValue(RequiredBoxStream.IMAGE_PROPERTY);
+    this.boxArray = extractor.mappingPropertyValue(RequiredBoxStream.BOX_ARRAY_PROPERTY);
+  }
 
+  @Override
+  public void onEvent(Event in, SpOutputCollector out) throws SpRuntimeException {
+    ImageTransformer imageTransformer = new ImageTransformer(in);
+    Optional<BufferedImage> imageOpt = imageTransformer.getImage(imageProperty);
 
     if (imageOpt.isPresent()) {
       BufferedImage image = imageOpt.get();
-      List<Map<String, Object>> allBoxesMap = imageTransformer.getAllBoxCoordinates();
+      List<Map<String, Object>> allBoxesMap = imageTransformer.getAllBoxCoordinates(boxArray);
 
       for (Map<String, Object> box : allBoxesMap) {
-
         BoxCoordinates boxCoordinates = imageTransformer.getBoxCoordinatesWithAnnotations(image, box);
 
         Graphics2D graph = image.createGraphics();
 
-        //set color
+        // set color
         Color color = ColorUtil.getColor(boxCoordinates.getClassesindex().hashCode());
         graph.setColor(color);
 
-        //Box
+        // Box
         graph.setStroke(new BasicStroke(5));
         graph.draw(new Rectangle(boxCoordinates.getX(), boxCoordinates.getY(), boxCoordinates.getWidth(),
             boxCoordinates.getHeight()));
 
-        //Label
+        // Label
         String str = boxCoordinates.getClassesindex() + ": " + boxCoordinates.getScore();
 
         FontMetrics fm = graph.getFontMetrics();
@@ -86,22 +112,21 @@ public class ImageEnricher implements EventProcessor<ImageEnrichmentParameters>
         graph.drawString(str, boxCoordinates.getX(), boxCoordinates.getY());
 
         graph.dispose();
-
       }
 
       Optional<byte[]> finalImage = imageTransformer.makeImage(image);
 
       if (finalImage.isPresent()) {
-        org.apache.streampipes.model.runtime.Event event = new org.apache.streampipes.model.runtime.Event();
-        event.addField("image", Base64.getEncoder().encodeToString(finalImage.get()));
-        out.collect(event);
+        Event outEvent = new Event();
+        outEvent.addField(ImagePropertyConstants.IMAGE.getProperty(),
+            Base64.getEncoder().encodeToString(finalImage.get()));
+        out.collect(outEvent);
       }
     }
-
   }
 
   @Override
-  public void onDetach() {
+  public void onDetach() throws SpRuntimeException {
 
   }
 }
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/qrreader/QrCodeReader.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/qrreader/QrCodeReader.java
deleted file mode 100644
index 581be5d0e..000000000
--- a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/qrreader/QrCodeReader.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.
- *
- */
-package org.apache.streampipes.processors.imageprocessing.jvm.processor.qrreader;
-
-import org.apache.streampipes.model.runtime.Event;
-import org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.PlainImageTransformer;
-import org.apache.streampipes.wrapper.context.EventProcessorRuntimeContext;
-import org.apache.streampipes.wrapper.routing.SpOutputCollector;
-import org.apache.streampipes.wrapper.runtime.EventProcessor;
-
-import boofcv.abst.fiducial.QrCodeDetector;
-import boofcv.alg.fiducial.qrcode.QrCode;
-import boofcv.factory.fiducial.FactoryFiducial;
-import boofcv.io.image.ConvertBufferedImage;
-import boofcv.struct.image.GrayU8;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.awt.image.BufferedImage;
-import java.util.List;
-import java.util.Optional;
-
-public class QrCodeReader implements EventProcessor<QrCodeReaderParameters> {
-
-  private QrCodeReaderParameters params;
-  private Boolean sendIfNoResult;
-  private String placeholderValue;
-  private static final Logger LOG = LoggerFactory.getLogger(QrCodeReader.class);
-
-  @Override
-  public void onInvocation(QrCodeReaderParameters qrCodeReaderParameters,
-                           SpOutputCollector spOutputCollector,
-                           EventProcessorRuntimeContext runtimeContext) {
-    this.params = qrCodeReaderParameters;
-    this.sendIfNoResult = qrCodeReaderParameters.getSendIfNoResult();
-    this.placeholderValue = qrCodeReaderParameters.getPlaceholderValue();
-  }
-
-  @Override
-  public void onEvent(Event in, SpOutputCollector out) {
-    PlainImageTransformer<QrCodeReaderParameters> imageTransformer = new PlainImageTransformer<>(in, params);
-    Optional<BufferedImage> imageOpt = imageTransformer.getImage(params.getImagePropertyName());
-
-    if (imageOpt.isPresent()) {
-      BufferedImage input = imageOpt.get();
-
-      GrayU8 gray = ConvertBufferedImage.convertFrom(input, (GrayU8) null);
-
-      QrCodeDetector<GrayU8> detector = FactoryFiducial.qrcode(null, GrayU8.class);
-
-      detector.process(gray);
-      List<QrCode> detections = detector.getDetections();
-      List<QrCode> failures = detector.getFailures();
-
-      if (detections.size() > 0) {
-        LOG.info(detections.get(0).message);
-        Event event = makeEvent(detections.get(0).message);
-        out.collect(event);
-      } else {
-        LOG.info("Could not find any QR code");
-        if (sendIfNoResult) {
-          Event event = makeEvent(placeholderValue);
-          out.collect(event);
-        }
-      }
-
-
-    }
-  }
-
-  private Event makeEvent(String qrCodeValue) {
-    Event event = new Event();
-    event.addField("qrvalue", qrCodeValue);
-    event.addField("timestamp", System.currentTimeMillis());
-    return event;
-  }
-
-
-  @Override
-  public void onDetach() {
-
-  }
-}
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/qrreader/QrCodeReaderController.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/qrreader/QrCodeReaderController.java
deleted file mode 100644
index 2226e3bbd..000000000
--- a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/qrreader/QrCodeReaderController.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.
- *
- */
-package org.apache.streampipes.processors.imageprocessing.jvm.processor.qrreader;
-
-import org.apache.streampipes.model.DataProcessorType;
-import org.apache.streampipes.model.graph.DataProcessorDescription;
-import org.apache.streampipes.model.graph.DataProcessorInvocation;
-import org.apache.streampipes.model.schema.PropertyScope;
-import org.apache.streampipes.sdk.builder.ProcessingElementBuilder;
-import org.apache.streampipes.sdk.builder.StreamRequirementsBuilder;
-import org.apache.streampipes.sdk.extractor.ProcessingElementParameterExtractor;
-import org.apache.streampipes.sdk.helpers.EpProperties;
-import org.apache.streampipes.sdk.helpers.EpRequirements;
-import org.apache.streampipes.sdk.helpers.Labels;
-import org.apache.streampipes.sdk.helpers.Locales;
-import org.apache.streampipes.sdk.helpers.Options;
-import org.apache.streampipes.sdk.helpers.OutputStrategies;
-import org.apache.streampipes.sdk.utils.Assets;
-import org.apache.streampipes.wrapper.standalone.ConfiguredEventProcessor;
-import org.apache.streampipes.wrapper.standalone.declarer.StandaloneEventProcessingDeclarer;
-
-import static org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.RequiredBoxStream.IMAGE_PROPERTY;
-
-public class QrCodeReaderController extends StandaloneEventProcessingDeclarer<QrCodeReaderParameters> {
-
-  private static final String PLACEHOLDER_VALUE = "placeholder-value";
-  private static final String SEND_IF_NO_RESULT = "send-if-no-result";
-  private static final String QR_VALUE = "qr-value";
-
-  @Override
-  public DataProcessorDescription declareModel() {
-    return ProcessingElementBuilder.create("org.apache.streampipes.processor.imageclassification.qrcode")
-        .category(DataProcessorType.IMAGE_PROCESSING)
-        .withAssets(Assets.DOCUMENTATION, Assets.ICON)
-        .withLocales(Locales.EN)
-        .requiredStream(StreamRequirementsBuilder.create().requiredPropertyWithUnaryMapping(EpRequirements
-                .domainPropertyReq("https://image.com"), Labels
-                .withId(IMAGE_PROPERTY),
-            PropertyScope.NONE).build())
-        .requiredSingleValueSelection(Labels.withId(SEND_IF_NO_RESULT), Options.from("Yes", "No"))
-        .requiredTextParameter(Labels.withId(PLACEHOLDER_VALUE))
-        .outputStrategy(OutputStrategies.fixed(EpProperties.timestampProperty("timestamp"),
-            EpProperties.stringEp(Labels.withId(QR_VALUE),
-                "qrvalue", "http://schema.org/text")))
-        .build();
-  }
-
-  @Override
-  public ConfiguredEventProcessor<QrCodeReaderParameters> onInvocation(DataProcessorInvocation dataProcessorInvocation,
-                                                                       ProcessingElementParameterExtractor extractor) {
-    String imagePropertyName = extractor.mappingPropertyValue(IMAGE_PROPERTY);
-    String placeholderValue = extractor.singleValueParameter(PLACEHOLDER_VALUE, String.class);
-    Boolean sendIfNoResult = extractor.selectedSingleValue(SEND_IF_NO_RESULT, String.class)
-        .equals("Yes");
-
-    QrCodeReaderParameters params = new QrCodeReaderParameters(dataProcessorInvocation,
-        imagePropertyName, placeholderValue, sendIfNoResult);
-
-    return new ConfiguredEventProcessor<>(params, QrCodeReader::new);
-  }
-
-}
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/qrreader/QrCodeReaderParameters.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/qrreader/QrCodeReaderParameters.java
deleted file mode 100644
index 675889ea3..000000000
--- a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/qrreader/QrCodeReaderParameters.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.
- *
- */
-package org.apache.streampipes.processors.imageprocessing.jvm.processor.qrreader;
-
-import org.apache.streampipes.model.graph.DataProcessorInvocation;
-import org.apache.streampipes.wrapper.params.binding.EventProcessorBindingParams;
-
-public class QrCodeReaderParameters extends EventProcessorBindingParams {
-
-  private String imagePropertyName;
-  private String placeholderValue;
-  private Boolean sendIfNoResult;
-
-  public QrCodeReaderParameters(DataProcessorInvocation graph, String imagePropertyName, String
-      placeholderValue, Boolean sendIfNoResult) {
-    super(graph);
-    this.imagePropertyName = imagePropertyName;
-    this.placeholderValue = placeholderValue;
-    this.sendIfNoResult = sendIfNoResult;
-  }
-
-  public String getImagePropertyName() {
-    return imagePropertyName;
-  }
-
-  public String getPlaceholderValue() {
-    return placeholderValue;
-  }
-
-  public Boolean getSendIfNoResult() {
-    return sendIfNoResult;
-  }
-}
diff --git a/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/qrreader/QrCodeReaderProcessor.java b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/qrreader/QrCodeReaderProcessor.java
new file mode 100644
index 000000000..1e9d7b5a3
--- /dev/null
+++ b/streampipes-extensions/streampipes-processors-image-processing-jvm/src/main/java/org/apache/streampipes/processors/imageprocessing/jvm/processor/qrreader/QrCodeReaderProcessor.java
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ *
+ */
+package org.apache.streampipes.processors.imageprocessing.jvm.processor.qrreader;
+
+import org.apache.streampipes.commons.exceptions.SpRuntimeException;
+import org.apache.streampipes.model.DataProcessorType;
+import org.apache.streampipes.model.graph.DataProcessorDescription;
+import org.apache.streampipes.model.runtime.Event;
+import org.apache.streampipes.model.schema.PropertyScope;
+import org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.PlainImageTransformer;
+import org.apache.streampipes.processors.imageprocessing.jvm.processor.commons.RequiredBoxStream;
+import org.apache.streampipes.sdk.builder.ProcessingElementBuilder;
+import org.apache.streampipes.sdk.builder.StreamRequirementsBuilder;
+import org.apache.streampipes.sdk.extractor.ProcessingElementParameterExtractor;
+import org.apache.streampipes.sdk.helpers.EpProperties;
+import org.apache.streampipes.sdk.helpers.EpRequirements;
+import org.apache.streampipes.sdk.helpers.Labels;
+import org.apache.streampipes.sdk.helpers.Locales;
+import org.apache.streampipes.sdk.helpers.Options;
+import org.apache.streampipes.sdk.helpers.OutputStrategies;
+import org.apache.streampipes.sdk.utils.Assets;
+import org.apache.streampipes.wrapper.context.EventProcessorRuntimeContext;
+import org.apache.streampipes.wrapper.routing.SpOutputCollector;
+import org.apache.streampipes.wrapper.standalone.ProcessorParams;
+import org.apache.streampipes.wrapper.standalone.StreamPipesDataProcessor;
+
+import boofcv.abst.fiducial.QrCodeDetector;
+import boofcv.alg.fiducial.qrcode.QrCode;
+import boofcv.factory.fiducial.FactoryFiducial;
+import boofcv.io.image.ConvertBufferedImage;
+import boofcv.struct.image.GrayU8;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.awt.image.BufferedImage;
+import java.util.List;
+import java.util.Optional;
+
+public class QrCodeReaderProcessor extends StreamPipesDataProcessor {
+
+  private static final String PLACEHOLDER_VALUE = "placeholder-value";
+  private static final String SEND_IF_NO_RESULT = "send-if-no-result";
+  private static final String QR_VALUE = "qr-value";
+  private static final Logger LOG = LoggerFactory.getLogger(QrCodeReaderProcessor.class);
+  private String imagePropertyName;
+  private String placeholderValue;
+  private Boolean sendIfNoResult;
+
+  @Override
+  public DataProcessorDescription declareModel() {
+    return ProcessingElementBuilder.create("org.apache.streampipes.processor.imageclassification.qrcode")
+        .category(DataProcessorType.IMAGE_PROCESSING)
+        .withAssets(Assets.DOCUMENTATION, Assets.ICON)
+        .withLocales(Locales.EN)
+        .requiredStream(StreamRequirementsBuilder.create()
+            .requiredPropertyWithUnaryMapping(EpRequirements.domainPropertyReq("https://image.com"),
+                Labels.withId(RequiredBoxStream.IMAGE_PROPERTY), PropertyScope.NONE)
+            .build())
+        .requiredSingleValueSelection(Labels.withId(SEND_IF_NO_RESULT), Options.from("Yes", "No"))
+        .requiredTextParameter(Labels.withId(PLACEHOLDER_VALUE))
+        .outputStrategy(OutputStrategies.fixed(
+            EpProperties.timestampProperty("timestamp"),
+            EpProperties.stringEp(Labels.withId(QR_VALUE), "qrvalue", "http://schema.org/text")))
+        .build();
+  }
+
+  @Override
+  public void onInvocation(ProcessorParams parameters, SpOutputCollector spOutputCollector,
+                           EventProcessorRuntimeContext runtimeContext) throws SpRuntimeException {
+    ProcessingElementParameterExtractor extractor = parameters.extractor();
+
+    imagePropertyName = extractor.mappingPropertyValue(RequiredBoxStream.IMAGE_PROPERTY);
+    placeholderValue = extractor.singleValueParameter(PLACEHOLDER_VALUE, String.class);
+    sendIfNoResult = extractor.selectedSingleValue(SEND_IF_NO_RESULT, String.class).equals("Yes");
+  }
+
+  @Override
+  public void onEvent(Event in, SpOutputCollector out) throws SpRuntimeException {
+    PlainImageTransformer imageTransformer = new PlainImageTransformer(in);
+    Optional<BufferedImage> imageOpt = imageTransformer.getImage(imagePropertyName);
+
+    if (imageOpt.isPresent()) {
+      BufferedImage input = imageOpt.get();
+
+      GrayU8 gray = ConvertBufferedImage.convertFrom(input, (GrayU8) null);
+
+      QrCodeDetector<GrayU8> detector = FactoryFiducial.qrcode(null, GrayU8.class);
+
+      detector.process(gray);
+      List<QrCode> detections = detector.getDetections();
+      List<QrCode> failures = detector.getFailures();
+
+      if (detections.size() > 0) {
+        LOG.info(detections.get(0).message);
+        Event event = makeEvent(detections.get(0).message);
+        out.collect(event);
+      } else {
+        LOG.info("Could not find any QR code");
+        if (sendIfNoResult) {
+          Event event = makeEvent(placeholderValue);
+          out.collect(event);
+        }
+      }
+    }
+  }
+
+  private Event makeEvent(String qrCodeValue) {
+    Event event = new Event();
+    event.addField("qrvalue", qrCodeValue);
+    event.addField("timestamp", System.currentTimeMillis());
+    return event;
+  }
+
+  @Override
+  public void onDetach() throws SpRuntimeException {
+
+  }
+}