asMap() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Object get(String key) {
+ throw new IllegalStateException("No key \"" + key + "\" exists");
+ }
+
+ @Override
+ public Object getOrDefault(String key, Object defaultValue) {
+ return defaultValue;
+ }
+}
diff --git a/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/deeplearning4j/serde/DL4JJsonSubTypesMapping.java b/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/models/deeplearning4j/serde/DL4JJsonSubTypesMapping.java
similarity index 88%
rename from konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/deeplearning4j/serde/DL4JJsonSubTypesMapping.java
rename to konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/models/deeplearning4j/serde/DL4JJsonSubTypesMapping.java
index 6317980ed..e935abcfc 100644
--- a/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/deeplearning4j/serde/DL4JJsonSubTypesMapping.java
+++ b/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/models/deeplearning4j/serde/DL4JJsonSubTypesMapping.java
@@ -13,10 +13,10 @@
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
-package ai.konduit.serving.deeplearning4j.serde;
+package ai.konduit.serving.models.deeplearning4j.serde;
-import ai.konduit.serving.deeplearning4j.DL4JConfiguration;
-import ai.konduit.serving.deeplearning4j.step.DL4JModelPipelineStep;
+import ai.konduit.serving.models.deeplearning4j.DL4JConfiguration;
+import ai.konduit.serving.models.deeplearning4j.step.DL4JModelPipelineStep;
import ai.konduit.serving.pipeline.api.Configuration;
import ai.konduit.serving.pipeline.api.serde.JsonSubType;
import ai.konduit.serving.pipeline.api.serde.JsonSubTypesMapping;
diff --git a/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/deeplearning4j/step/DL4JModelPipelineStep.java b/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/models/deeplearning4j/step/DL4JModelPipelineStep.java
similarity index 93%
rename from konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/deeplearning4j/step/DL4JModelPipelineStep.java
rename to konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/models/deeplearning4j/step/DL4JModelPipelineStep.java
index 7eede741e..c3cb95f27 100644
--- a/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/deeplearning4j/step/DL4JModelPipelineStep.java
+++ b/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/models/deeplearning4j/step/DL4JModelPipelineStep.java
@@ -13,9 +13,9 @@
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
-package ai.konduit.serving.deeplearning4j.step;
+package ai.konduit.serving.models.deeplearning4j.step;
-import ai.konduit.serving.deeplearning4j.DL4JConfiguration;
+import ai.konduit.serving.models.deeplearning4j.DL4JConfiguration;
import ai.konduit.serving.pipeline.api.BaseModelPipelineStep;
import lombok.Data;
import lombok.EqualsAndHashCode;
diff --git a/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/deeplearning4j/step/DL4JPipelineStepRunner.java b/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/models/deeplearning4j/step/DL4JPipelineStepRunner.java
similarity index 97%
rename from konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/deeplearning4j/step/DL4JPipelineStepRunner.java
rename to konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/models/deeplearning4j/step/DL4JPipelineStepRunner.java
index e5eb68bda..60d159679 100644
--- a/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/deeplearning4j/step/DL4JPipelineStepRunner.java
+++ b/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/models/deeplearning4j/step/DL4JPipelineStepRunner.java
@@ -13,8 +13,9 @@
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
-package ai.konduit.serving.deeplearning4j.step;
+package ai.konduit.serving.models.deeplearning4j.step;
+import ai.konduit.serving.pipeline.api.context.Context;
import ai.konduit.serving.pipeline.api.data.Data;
import ai.konduit.serving.pipeline.api.data.NDArray;
import ai.konduit.serving.pipeline.api.exception.ModelLoadingException;
@@ -89,7 +90,7 @@ public PipelineStep getPipelineStep() {
}
@Override
- public Data exec(Data data) {
+ public Data exec(Context ctx, Data data) {
//First: Get array
//TODO HANDLE DIFFERENT NAMES (Not hardcoded)
diff --git a/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/deeplearning4j/step/DL4JPipelineStepRunnerFactory.java b/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/models/deeplearning4j/step/DL4JPipelineStepRunnerFactory.java
similarity index 96%
rename from konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/deeplearning4j/step/DL4JPipelineStepRunnerFactory.java
rename to konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/models/deeplearning4j/step/DL4JPipelineStepRunnerFactory.java
index 945c71e35..8b2a06646 100644
--- a/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/deeplearning4j/step/DL4JPipelineStepRunnerFactory.java
+++ b/konduit-serving-models/konduit-serving-deeplearning4j/src/main/java/ai/konduit/serving/models/deeplearning4j/step/DL4JPipelineStepRunnerFactory.java
@@ -13,7 +13,7 @@
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
-package ai.konduit.serving.deeplearning4j.step;
+package ai.konduit.serving.models.deeplearning4j.step;
import ai.konduit.serving.pipeline.api.step.PipelineStep;
import ai.konduit.serving.pipeline.api.step.PipelineStepRunner;
diff --git a/konduit-serving-models/konduit-serving-deeplearning4j/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.serde.JsonSubTypesMapping b/konduit-serving-models/konduit-serving-deeplearning4j/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.serde.JsonSubTypesMapping
index d207284fe..3335c11b2 100644
--- a/konduit-serving-models/konduit-serving-deeplearning4j/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.serde.JsonSubTypesMapping
+++ b/konduit-serving-models/konduit-serving-deeplearning4j/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.serde.JsonSubTypesMapping
@@ -14,4 +14,4 @@
# SPDX-License-Identifier: Apache-2.0
################################################################################
-ai.konduit.serving.deeplearning4j.serde.DL4JJsonSubTypesMapping
\ No newline at end of file
+ai.konduit.serving.models.deeplearning4j.serde.DL4JJsonSubTypesMapping
\ No newline at end of file
diff --git a/konduit-serving-models/konduit-serving-deeplearning4j/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.step.PipelineStepRunnerFactory b/konduit-serving-models/konduit-serving-deeplearning4j/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.step.PipelineStepRunnerFactory
index fe93ae4a3..8e9bb990b 100644
--- a/konduit-serving-models/konduit-serving-deeplearning4j/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.step.PipelineStepRunnerFactory
+++ b/konduit-serving-models/konduit-serving-deeplearning4j/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.step.PipelineStepRunnerFactory
@@ -14,4 +14,4 @@
# SPDX-License-Identifier: Apache-2.0
################################################################################
-ai.konduit.serving.deeplearning4j.step.DL4JPipelineStepRunnerFactory
\ No newline at end of file
+ai.konduit.serving.models.deeplearning4j.step.DL4JPipelineStepRunnerFactory
\ No newline at end of file
diff --git a/konduit-serving-models/konduit-serving-deeplearning4j/src/test/java/ai/konduit/serving/deeplearning4j/TestDL4JModelStep.java b/konduit-serving-models/konduit-serving-deeplearning4j/src/test/java/ai/konduit/serving/deeplearning4j/TestDL4JModelStep.java
index af5366697..9eb451be5 100644
--- a/konduit-serving-models/konduit-serving-deeplearning4j/src/test/java/ai/konduit/serving/deeplearning4j/TestDL4JModelStep.java
+++ b/konduit-serving-models/konduit-serving-deeplearning4j/src/test/java/ai/konduit/serving/deeplearning4j/TestDL4JModelStep.java
@@ -15,7 +15,7 @@
******************************************************************************/
package ai.konduit.serving.deeplearning4j;
-import ai.konduit.serving.deeplearning4j.step.DL4JModelPipelineStep;
+import ai.konduit.serving.models.deeplearning4j.step.DL4JModelPipelineStep;
import ai.konduit.serving.pipeline.api.data.Data;
import ai.konduit.serving.pipeline.api.data.NDArray;
import ai.konduit.serving.pipeline.api.pipeline.Pipeline;
@@ -73,21 +73,21 @@ public void testSimpleMLN() throws Exception {
Data d = Data.singleton("in", NDArray.create(arr));
- Data out = e.exec(d);
- INDArray actual = (INDArray) out.getNDArray(outName).get(); //TODO NO CAST
+ Data out = e.exec(null, d);
+ INDArray actual = out.getNDArray(outName).getAs(INDArray.class);
assertEquals(exp, actual);
String json = p.toJson();
System.out.println(json);
Pipeline pJson = Pipeline.fromJson(json);
- INDArray outJson = (INDArray) pJson.executor().exec(d).getNDArray(outName).get(); //TODO NO CAST
+ INDArray outJson = pJson.executor().exec(null, d).getNDArray(outName).getAs(INDArray.class);
assertEquals(exp, outJson);
String yaml = p.toYaml();
System.out.println(yaml);
Pipeline pYaml = Pipeline.fromYaml(yaml);
- INDArray outYaml = (INDArray) pYaml.executor().exec(d).getNDArray(outName).get(); //TODO NO CAST
+ INDArray outYaml = pYaml.executor().exec(null, d).getNDArray(outName).getAs(INDArray.class);
assertEquals(exp, outYaml);
}
}
@@ -113,22 +113,22 @@ public void testSimpleCompGraph() throws Exception {
INDArray arr = Nd4j.rand(DataType.FLOAT, 3, 4);
INDArray exp = predictFromFileCG(netFile, arr)[0];
- Data d = Data.singleton("in", NDArray.create(arr)); //TODO NO CAST
+ Data d = Data.singleton("in", NDArray.create(arr));
- Data out = e.exec(d);
- INDArray actual = (INDArray) out.getNDArray(outName).get(); //TODO NO CAST
+ Data out = e.exec(null, d);
+ INDArray actual = out.getNDArray(outName).getAs(INDArray.class);
assertEquals(exp, actual);
String json = p.toJson();
Pipeline pJson = Pipeline.fromJson(json);
- INDArray outJson = (INDArray) pJson.executor().exec(d).getNDArray(outName).get(); //TODO NO CAST
+ INDArray outJson = pJson.executor().exec(null, d).getNDArray(outName).getAs(INDArray.class);
assertEquals(exp, outJson);
String yaml = p.toYaml();
Pipeline pYaml = Pipeline.fromYaml(yaml);
- INDArray outYaml = (INDArray) pYaml.executor().exec(d).getNDArray(outName).get(); //TODO NO CAST
+ INDArray outYaml = pYaml.executor().exec(null, d).getNDArray(outName).getAs(INDArray.class);
assertEquals(exp, outYaml);
}
}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/context/Context.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/context/Context.java
new file mode 100644
index 000000000..8e56438bf
--- /dev/null
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/context/Context.java
@@ -0,0 +1,35 @@
+/*
+ * ******************************************************************************
+ * * Copyright (c) 2020 Konduit K.K.
+ * *
+ * * This program and the accompanying materials are made available under the
+ * * terms of the Apache License, Version 2.0 which is available at
+ * * https://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.
+ * *
+ * * SPDX-License-Identifier: Apache-2.0
+ * *****************************************************************************
+ */
+
+package ai.konduit.serving.pipeline.api.context;
+
+
+/**
+ * Context is used for features such as profiling and metrics.
+ *
+ * See {@link Profiler} and {@link Metrics} for more details
+ *
+ * @author Alex Black
+ */
+public interface Context {
+
+ Metrics metrics();
+
+ Profiler profiler();
+
+}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/context/Metrics.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/context/Metrics.java
new file mode 100644
index 000000000..c130a16b4
--- /dev/null
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/context/Metrics.java
@@ -0,0 +1,33 @@
+/*
+ * ******************************************************************************
+ * * Copyright (c) 2020 Konduit K.K.
+ * *
+ * * This program and the accompanying materials are made available under the
+ * * terms of the Apache License, Version 2.0 which is available at
+ * * https://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.
+ * *
+ * * SPDX-License-Identifier: Apache-2.0
+ * *****************************************************************************
+ */
+
+package ai.konduit.serving.pipeline.api.context;
+
+/**
+ * Metrics are used to record values, for debugging and visualization.
+ *
+ * Instances of the Metrics interface are available within a PipelineStepRunner's exec method via the {@link Context#metrics()}
+ * method.
+ *
+ * @author Alex Black
+ */
+public interface Metrics {
+
+
+
+}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/context/Profiler.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/context/Profiler.java
new file mode 100644
index 000000000..10fd2be60
--- /dev/null
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/context/Profiler.java
@@ -0,0 +1,76 @@
+/*
+ * ******************************************************************************
+ * * Copyright (c) 2020 Konduit K.K.
+ * *
+ * * This program and the accompanying materials are made available under the
+ * * terms of the Apache License, Version 2.0 which is available at
+ * * https://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.
+ * *
+ * * SPDX-License-Identifier: Apache-2.0
+ * *****************************************************************************
+ */
+
+package ai.konduit.serving.pipeline.api.context;
+
+/**
+ * The Profiler interface is used within each PipelineStep (technically, within each {@link ai.konduit.serving.pipeline.api.step.PipelineStepRunner})
+ * for performance analysis and debugging purposes.
+ * Specifically, for each event within a PipelineStepRunner such as "load data", "convert input array", etc - developers
+ * can profile events based on their start and end times.
+ * Events are identified by a String key, and have both start and end times.
+ *
+ *
+ * For example:
+ *
+ * {@code
+ * @Override
+ * public Data exec(Context ctx, Data data){
+ * Profiler p = ctx.profiler();
+ * p.eventStart("DataConversion");
+ * data.getNDArray("myArray").getAs(float[][].class);
+ * p.eventEnd("DataConversion");
+ * ...
+ * }}
+ *
+ * If the profiler is not enabled globally for the Pipeline execution, profiler calls are a no-op hence have virtually
+ * no overhead.
+ *
+ * Profiler eventStart/End calls can be nested and events need not end before earlier ones start - for example, both
+ * {@code eventStart("X"), eventStart("Y"), eventEnd("Y"), eventEnd("X")} and
+ * {@code eventStart("X"), eventStart("Y"), eventEnd("X"), eventEnd("Y")} are valid. However,
+ * {@code eventEnd("X"), eventStart("X")} is not valid (end before start), which wil log a warning.
+ *
+ * Note that every eventStart call should have a corresponding eventEnd call some time before the PipelineStepRunner.exec
+ * method returns. Any profiler eventStart calls without a corresponding eventEnd call will have eventEnd called once
+ * the exec method returns.
+ *
+ * Event keys are usually the same on all calls of a given PipelineStepRunner's exec method, but this need not be the case
+ * in general
+ *
+ * @author Alex Black
+ */
+public interface Profiler {
+
+ /**
+ * Returns true if the profiler is enabled globally, or false otherwise
+ */
+ boolean profilerEnabled();
+
+ /**
+ * Start the timer for the event with the specified key.
+ */
+ void eventStart(String key);
+
+ /**
+ * End the timer for the event with the specified key.
+ * Should be called some time after {@link #eventStart(String)}
+ */
+ void eventEnd(String key);
+
+}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/data/Data.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/data/Data.java
index fa027a633..f429085d1 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/data/Data.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/data/Data.java
@@ -18,7 +18,8 @@
package ai.konduit.serving.pipeline.api.data;
import ai.konduit.serving.pipeline.impl.data.*;
-import ai.konduit.serving.pipeline.impl.format.SerializedNDArray;
+import ai.konduit.serving.pipeline.impl.data.image.Png;
+import ai.konduit.serving.pipeline.impl.data.ndarray.SerializedNDArray;
import ai.konduit.serving.pipeline.impl.serde.DataJsonDeserializer;
import ai.konduit.serving.pipeline.impl.serde.DataJsonSerializer;
import ai.konduit.serving.pipeline.util.ObjectMappers;
@@ -172,10 +173,19 @@ static boolean equals(@NonNull Data d1, @NonNull Data d2){
ValueType vt = d1.type(s);
switch (vt){
case LIST:
- case IMAGE:
default:
//TODO
throw new UnsupportedOperationException(vt + " equality not yet implemented");
+ case IMAGE:
+ Png png1 = d1.getImage(s).getAs(Png.class);
+ Png png2 = d1.getImage(s).getAs(Png.class);
+
+ byte[] pngBytes1 = png1.getBytes();
+ byte[] pngBytes2 = png2.getBytes();
+
+ if(!Arrays.equals(pngBytes1, pngBytes2))
+ return false;
+ break;
case NDARRAY:
//TODO this is inefficient - but robust...
NDArray a1 = d1.getNDArray(s);
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/data/Image.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/data/Image.java
index dc9ac3a25..832259b99 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/data/Image.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/data/Image.java
@@ -18,8 +18,11 @@
package ai.konduit.serving.pipeline.api.data;
+import ai.konduit.serving.pipeline.api.format.ImageFactory;
import ai.konduit.serving.pipeline.api.format.ImageFormat;
import ai.konduit.serving.pipeline.registry.ImageFactoryRegistry;
+import lombok.NonNull;
+import org.nd4j.common.base.Preconditions;
public interface Image {
@@ -27,11 +30,22 @@ public interface Image {
T getAs(ImageFormat format);
- static Image create(Object from){
- throw new UnsupportedOperationException("Not yet implemented");
+ T getAs(Class type);
+
+ boolean canGetAs(ImageFormat> format);
+
+ boolean canGetAs(Class> type);
+
+ //TODO how will this work for PNG, JPG etc files?
+ static Image create(@NonNull Object from){
+ ImageFactory f = ImageFactoryRegistry.getFactoryFor(from);
+ Preconditions.checkState(f != null, "Unable to create Image from object of %s - no ImageFactory instances" +
+ " are available that can convert this type to Konduit Serving Image", from.getClass());
+
+ return f.create(from);
}
- static boolean canCreateFrom(Object from){
+ static boolean canCreateFrom(@NonNull Object from){
return ImageFactoryRegistry.getFactoryFor(from) != null;
}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/data/ValueType.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/data/ValueType.java
new file mode 100644
index 000000000..603e66346
--- /dev/null
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/data/ValueType.java
@@ -0,0 +1,30 @@
+/*
+ * ******************************************************************************
+ * * Copyright (c) 2020 Konduit K.K.
+ * *
+ * * This program and the accompanying materials are made available under the
+ * * terms of the Apache License, Version 2.0 which is available at
+ * * https://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.
+ * *
+ * * SPDX-License-Identifier: Apache-2.0
+ * *****************************************************************************
+ */
+package ai.konduit.serving.pipeline.api.data;
+
+public enum ValueType {
+ NDARRAY,
+ STRING,
+ BYTES,
+ IMAGE,
+ DOUBLE,
+ INT64,
+ BOOLEAN,
+ DATA,
+ LIST
+}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/exception/DataConversionException.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/exception/DataConversionException.java
new file mode 100644
index 000000000..66ca60ceb
--- /dev/null
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/exception/DataConversionException.java
@@ -0,0 +1,36 @@
+/*
+ * ******************************************************************************
+ * * Copyright (c) 2020 Konduit K.K.
+ * *
+ * * This program and the accompanying materials are made available under the
+ * * terms of the Apache License, Version 2.0 which is available at
+ * * https://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.
+ * *
+ * * SPDX-License-Identifier: Apache-2.0
+ * *****************************************************************************
+ */
+
+package ai.konduit.serving.pipeline.api.exception;
+
+public class DataConversionException extends RuntimeException {
+ public DataConversionException() {
+ }
+
+ public DataConversionException(String message) {
+ super(message);
+ }
+
+ public DataConversionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public DataConversionException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/exception/DataLoadingException.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/exception/DataLoadingException.java
new file mode 100644
index 000000000..6488bd26d
--- /dev/null
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/exception/DataLoadingException.java
@@ -0,0 +1,37 @@
+/*
+ * ******************************************************************************
+ * * Copyright (c) 2020 Konduit K.K.
+ * *
+ * * This program and the accompanying materials are made available under the
+ * * terms of the Apache License, Version 2.0 which is available at
+ * * https://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.
+ * *
+ * * SPDX-License-Identifier: Apache-2.0
+ * *****************************************************************************
+ */
+
+package ai.konduit.serving.pipeline.api.exception;
+
+public class DataLoadingException extends RuntimeException {
+
+ public DataLoadingException() {
+ }
+
+ public DataLoadingException(String message) {
+ super(message);
+ }
+
+ public DataLoadingException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public DataLoadingException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/format/ImageConverter.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/format/ImageConverter.java
index b05318274..158f7a605 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/format/ImageConverter.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/format/ImageConverter.java
@@ -22,8 +22,11 @@
public interface ImageConverter {
- boolean canConvert(Image from, ImageFormat to);
+ boolean canConvert(Image from, ImageFormat> to);
- Image convert(Image from, ImageFormat to);
+ boolean canConvert(Image from, Class> to);
+ T convert(Image from, ImageFormat to);
+
+ T convert(Image from, Class to);
}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/format/NDArrayConverter.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/format/NDArrayConverter.java
index f6933c14f..3a90107ef 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/format/NDArrayConverter.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/format/NDArrayConverter.java
@@ -23,7 +23,7 @@
public interface NDArrayConverter {
- boolean canConvert(NDArray from, NDArrayFormat to);
+ boolean canConvert(NDArray from, NDArrayFormat> to);
boolean canConvert(NDArray from, Class> to);
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/pipeline/PipelineExecutor.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/pipeline/PipelineExecutor.java
index 735fd4ac6..73cd0d55b 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/pipeline/PipelineExecutor.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/pipeline/PipelineExecutor.java
@@ -15,6 +15,7 @@
******************************************************************************/
package ai.konduit.serving.pipeline.api.pipeline;
+import ai.konduit.serving.pipeline.api.context.Context;
import ai.konduit.serving.pipeline.api.data.Data;
import ai.konduit.serving.pipeline.api.step.PipelineStepRunner;
import org.slf4j.Logger;
@@ -24,6 +25,8 @@
/**
* A pipeline executor implements the actual execution behind the {@link Data} -> {@link Data} mapping that is defined
* by a {@link Pipeline}
+ *
+ * @author Alex Black
*/
public interface PipelineExecutor {
@@ -40,15 +43,15 @@ public interface PipelineExecutor {
/**
* Execute the pipeline on the specified Data instance
*/
- Data exec(Data data);
+ Data exec(Context context, Data data);
/**
* Execute the pipeline on all of the specified Data instances
*/
- default Data[] exec(Data... data) {
+ default Data[] exec(Context context, Data... data) {
Data[] out = new Data[data.length];
for (int i = 0; i < data.length; i++) {
- out[i] = exec(data[i]);
+ out[i] = exec(context, data[i]);
}
return out;
}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/step/PipelineStepRunner.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/step/PipelineStepRunner.java
index a98eac503..8f3407e79 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/step/PipelineStepRunner.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/step/PipelineStepRunner.java
@@ -15,6 +15,7 @@
******************************************************************************/
package ai.konduit.serving.pipeline.api.step;
+import ai.konduit.serving.pipeline.api.context.Context;
import ai.konduit.serving.pipeline.api.data.Data;
import java.io.Closeable;
@@ -42,15 +43,15 @@ public interface PipelineStepRunner extends Closeable {
/**
* Execute the pipeline on the specified Data instance
*/
- Data exec(Data data);
+ Data exec(Context ctx, Data data);
/**
* Execute the pipeline on all of the specified Data instances
*/
- default Data[] exec(Data... data) {
+ default Data[] exec(Context ctx, Data... data) {
Data[] out = new Data[data.length];
for (int i = 0; i < data.length; i++) {
- out[i] = exec(data[i]);
+ out[i] = exec(ctx, data[i]);
}
return out;
}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/JData.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/JData.java
index 8bd7e9986..c0402477c 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/JData.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/JData.java
@@ -18,6 +18,7 @@
import ai.konduit.serving.pipeline.api.data.Data;
import ai.konduit.serving.pipeline.api.data.Image;
import ai.konduit.serving.pipeline.api.data.NDArray;
+import ai.konduit.serving.pipeline.api.data.ValueType;
import ai.konduit.serving.pipeline.impl.data.wrappers.*;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/Value.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/Value.java
index 2284d5399..36437425c 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/Value.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/Value.java
@@ -15,6 +15,8 @@
******************************************************************************/
package ai.konduit.serving.pipeline.impl.data;
+import ai.konduit.serving.pipeline.api.data.ValueType;
+
public interface Value {
ValueType type();
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/ValueType.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/ValueType.java
deleted file mode 100644
index 5bc204d99..000000000
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/ValueType.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/* ******************************************************************************
- * Copyright (c) 2020 Konduit K.K.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Apache License, Version 2.0 which is available at
- * https://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.
- *
- * SPDX-License-Identifier: Apache-2.0
- ******************************************************************************/
-package ai.konduit.serving.pipeline.impl.data;
-
-public enum ValueType {
- NDARRAY,
- STRING,
- BYTES,
- IMAGE,
- DOUBLE,
- INT64,
- BOOLEAN,
- DATA,
- LIST
-}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/image/BImage.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/image/BImage.java
new file mode 100644
index 000000000..373b2d9be
--- /dev/null
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/image/BImage.java
@@ -0,0 +1,27 @@
+/*
+ * ******************************************************************************
+ * * Copyright (c) 2020 Konduit K.K.
+ * *
+ * * This program and the accompanying materials are made available under the
+ * * terms of the Apache License, Version 2.0 which is available at
+ * * https://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.
+ * *
+ * * SPDX-License-Identifier: Apache-2.0
+ * *****************************************************************************
+ */
+
+package ai.konduit.serving.pipeline.impl.data.image;
+
+import java.awt.image.BufferedImage;
+
+public class BImage extends BaseImage {
+ public BImage(BufferedImage image) {
+ super(image);
+ }
+}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/image/BaseImage.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/image/BaseImage.java
new file mode 100644
index 000000000..b7dc04a67
--- /dev/null
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/image/BaseImage.java
@@ -0,0 +1,79 @@
+/*
+ * ******************************************************************************
+ * * Copyright (c) 2020 Konduit K.K.
+ * *
+ * * This program and the accompanying materials are made available under the
+ * * terms of the Apache License, Version 2.0 which is available at
+ * * https://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.
+ * *
+ * * SPDX-License-Identifier: Apache-2.0
+ * *****************************************************************************
+ */
+
+package ai.konduit.serving.pipeline.impl.data.image;
+
+import ai.konduit.serving.pipeline.api.data.Image;
+import ai.konduit.serving.pipeline.api.format.ImageConverter;
+import ai.konduit.serving.pipeline.api.format.ImageFormat;
+import ai.konduit.serving.pipeline.registry.ImageConverterRegistry;
+import lombok.AllArgsConstructor;
+import org.nd4j.common.base.Preconditions;
+
+import java.util.Arrays;
+
+@AllArgsConstructor
+public abstract class BaseImage implements Image {
+
+ private final T image;
+
+ @Override
+ public Object get() {
+ return image;
+ }
+
+ @Override
+ public T getAs(ImageFormat format) {
+ return ImageConverterRegistry.getConverterFor(this, format).convert(this, format);
+ }
+
+ @Override
+ public T getAs(Class type) {
+ ImageConverter converter = ImageConverterRegistry.getConverterFor(this, type);
+ Preconditions.checkState(converter != null, "No converter found for converting from %s to %s", image.getClass(), type);
+ return converter.convert(this, type);
+ }
+
+ @Override
+ public boolean canGetAs(ImageFormat> format) {
+ ImageConverter converter = ImageConverterRegistry.getConverterFor(this, format);
+ return converter != null;
+ }
+
+ @Override
+ public boolean canGetAs(Class> type) {
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object o){
+ if(!(o instanceof Image))
+ return false;
+
+ Image o2 = (Image)o;
+
+ //TODO is this actually reliable for checks?
+ Png png1 = getAs(Png.class);
+ Png png2 = o2.getAs(Png.class);
+
+ byte[] b1 = png1.getBytes();
+ byte[] b2 = png2.getBytes();
+
+ return Arrays.equals(b1, b2);
+ }
+}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/image/Png.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/image/Png.java
new file mode 100644
index 000000000..d2b6e8906
--- /dev/null
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/image/Png.java
@@ -0,0 +1,74 @@
+/*
+ * ******************************************************************************
+ * * Copyright (c) 2020 Konduit K.K.
+ * *
+ * * This program and the accompanying materials are made available under the
+ * * terms of the Apache License, Version 2.0 which is available at
+ * * https://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.
+ * *
+ * * SPDX-License-Identifier: Apache-2.0
+ * *****************************************************************************
+ */
+
+package ai.konduit.serving.pipeline.impl.data.image;
+
+import ai.konduit.serving.pipeline.api.exception.DataLoadingException;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.apache.commons.io.FileUtils;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+
+@Data
+@AllArgsConstructor
+public class Png {
+
+ private ByteBuffer pngFileBytes;
+
+ public Png(File file){
+ try {
+ pngFileBytes = ByteBuffer.wrap(FileUtils.readFileToByteArray(file));
+ } catch (IOException e){
+ throw new DataLoadingException("Unable to load PNG image from file " + file.getAbsolutePath());
+ }
+ }
+
+ public Png(byte[] bytes){
+ this.pngFileBytes = ByteBuffer.wrap(bytes);
+ }
+
+ public byte[] getBytes(){
+ if(pngFileBytes.hasArray()){
+ return pngFileBytes.array();
+ } else {
+ byte[] bytes = new byte[pngFileBytes.capacity()];
+ pngFileBytes.position(0);
+ pngFileBytes.get(bytes);
+ return bytes;
+ }
+
+ }
+
+ public void save(File f) throws IOException {
+ FileUtils.writeByteArrayToFile(f, getBytes());
+ }
+
+ public void write(OutputStream os) throws IOException {
+ boolean buffered = os instanceof BufferedOutputStream;
+ if(!buffered)
+ os = new BufferedOutputStream(os);
+ try(OutputStream o = os){
+ o.write(getBytes());
+ }
+ }
+}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/java/JavaImage.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/image/PngImage.java
similarity index 83%
rename from konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/java/JavaImage.java
rename to konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/image/PngImage.java
index 9e6e65880..bf9a14157 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/java/JavaImage.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/image/PngImage.java
@@ -16,7 +16,11 @@
* *****************************************************************************
*/
-package ai.konduit.serving.pipeline.impl.data.java;
+package ai.konduit.serving.pipeline.impl.data.image;
-public class JavaImage {
+public class PngImage extends BaseImage {
+
+ public PngImage(Png image) {
+ super(image);
+ }
}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/data/BaseNDArray.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/ndarray/BaseNDArray.java
similarity index 92%
rename from konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/data/BaseNDArray.java
rename to konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/ndarray/BaseNDArray.java
index f62ad205e..1378c7d28 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/api/data/BaseNDArray.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/ndarray/BaseNDArray.java
@@ -16,17 +16,17 @@
* *****************************************************************************
*/
-package ai.konduit.serving.pipeline.api.data;
+package ai.konduit.serving.pipeline.impl.data.ndarray;
+import ai.konduit.serving.pipeline.api.data.NDArray;
import ai.konduit.serving.pipeline.api.format.NDArrayConverter;
import ai.konduit.serving.pipeline.api.format.NDArrayFormat;
-import ai.konduit.serving.pipeline.impl.format.SerializedNDArray;
import ai.konduit.serving.pipeline.registry.NDArrayConverterRegistry;
import lombok.AllArgsConstructor;
import org.nd4j.common.base.Preconditions;
@AllArgsConstructor
-public class BaseNDArray implements NDArray {
+public abstract class BaseNDArray implements NDArray {
private final T array;
@@ -42,7 +42,6 @@ public T getAs(NDArrayFormat format) {
@Override
public T getAs(Class type) {
- //TODO check to avoid NPE
NDArrayConverter converter = NDArrayConverterRegistry.getConverterFor(this, type);
Preconditions.checkState(converter != null, "No converter found for converting from %s to %s", array.getClass(), type);
return converter.convert(this, type);
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/SerializedNDArray.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/ndarray/SerializedNDArray.java
similarity index 97%
rename from konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/SerializedNDArray.java
rename to konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/ndarray/SerializedNDArray.java
index 6cc52ce08..a79086aa1 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/SerializedNDArray.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/ndarray/SerializedNDArray.java
@@ -16,7 +16,7 @@
* *****************************************************************************
*/
-package ai.konduit.serving.pipeline.impl.format;
+package ai.konduit.serving.pipeline.impl.data.ndarray;
import ai.konduit.serving.pipeline.api.data.NDArrayType;
import lombok.AllArgsConstructor;
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/BaseValue.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/BaseValue.java
index 62d3f6acf..a3ba09fd9 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/BaseValue.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/BaseValue.java
@@ -19,7 +19,7 @@
package ai.konduit.serving.pipeline.impl.data.wrappers;
import ai.konduit.serving.pipeline.impl.data.Value;
-import ai.konduit.serving.pipeline.impl.data.ValueType;
+import ai.konduit.serving.pipeline.api.data.ValueType;
import lombok.AllArgsConstructor;
@AllArgsConstructor
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/BooleanValue.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/BooleanValue.java
index e53f0400e..054bd52d5 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/BooleanValue.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/BooleanValue.java
@@ -15,7 +15,7 @@
******************************************************************************/
package ai.konduit.serving.pipeline.impl.data.wrappers;
-import ai.konduit.serving.pipeline.impl.data.ValueType;
+import ai.konduit.serving.pipeline.api.data.ValueType;
public class BooleanValue extends BaseValue {
public BooleanValue(Boolean value) {
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/BytesValue.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/BytesValue.java
index 64b4680c9..d6d290656 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/BytesValue.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/BytesValue.java
@@ -15,7 +15,7 @@
******************************************************************************/
package ai.konduit.serving.pipeline.impl.data.wrappers;
-import ai.konduit.serving.pipeline.impl.data.ValueType;
+import ai.konduit.serving.pipeline.api.data.ValueType;
public class BytesValue extends BaseValue {
public BytesValue(byte[] value) {
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/DataValue.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/DataValue.java
index 5615cf83e..3a032266b 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/DataValue.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/DataValue.java
@@ -19,7 +19,7 @@
package ai.konduit.serving.pipeline.impl.data.wrappers;
import ai.konduit.serving.pipeline.api.data.Data;
-import ai.konduit.serving.pipeline.impl.data.ValueType;
+import ai.konduit.serving.pipeline.api.data.ValueType;
public class DataValue extends BaseValue {
public DataValue(Data value) {
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/DoubleValue.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/DoubleValue.java
index 59c984d98..0dc210b29 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/DoubleValue.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/DoubleValue.java
@@ -15,7 +15,7 @@
******************************************************************************/
package ai.konduit.serving.pipeline.impl.data.wrappers;
-import ai.konduit.serving.pipeline.impl.data.ValueType;
+import ai.konduit.serving.pipeline.api.data.ValueType;
public class DoubleValue extends BaseValue {
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/ImageValue.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/ImageValue.java
index 8ddd9c036..2a8345680 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/ImageValue.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/ImageValue.java
@@ -16,7 +16,7 @@
package ai.konduit.serving.pipeline.impl.data.wrappers;
import ai.konduit.serving.pipeline.api.data.Image;
-import ai.konduit.serving.pipeline.impl.data.ValueType;
+import ai.konduit.serving.pipeline.api.data.ValueType;
public class ImageValue extends BaseValue {
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/IntValue.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/IntValue.java
index 87e57ae73..d3400e3d2 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/IntValue.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/IntValue.java
@@ -15,7 +15,7 @@
******************************************************************************/
package ai.konduit.serving.pipeline.impl.data.wrappers;
-import ai.konduit.serving.pipeline.impl.data.ValueType;
+import ai.konduit.serving.pipeline.api.data.ValueType;
public class IntValue extends BaseValue {
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/NDArrayValue.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/NDArrayValue.java
index 83044a796..8ee8a1e5c 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/NDArrayValue.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/NDArrayValue.java
@@ -16,9 +16,7 @@
package ai.konduit.serving.pipeline.impl.data.wrappers;
import ai.konduit.serving.pipeline.api.data.NDArray;
-import ai.konduit.serving.pipeline.impl.data.Value;
-import ai.konduit.serving.pipeline.impl.data.ValueType;
-import lombok.AllArgsConstructor;
+import ai.konduit.serving.pipeline.api.data.ValueType;
public class NDArrayValue extends BaseValue {
public NDArrayValue(NDArray value) {
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/StringValue.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/StringValue.java
index 4ecd5a10f..06f4010c9 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/StringValue.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/wrappers/StringValue.java
@@ -15,7 +15,7 @@
******************************************************************************/
package ai.konduit.serving.pipeline.impl.data.wrappers;
-import ai.konduit.serving.pipeline.impl.data.ValueType;
+import ai.konduit.serving.pipeline.api.data.ValueType;
public class StringValue extends BaseValue {
public StringValue(String value) {
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaImageConverters.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaImageConverters.java
new file mode 100644
index 000000000..92217ae9e
--- /dev/null
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaImageConverters.java
@@ -0,0 +1,132 @@
+/*
+ * ******************************************************************************
+ * * Copyright (c) 2020 Konduit K.K.
+ * *
+ * * This program and the accompanying materials are made available under the
+ * * terms of the Apache License, Version 2.0 which is available at
+ * * https://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.
+ * *
+ * * SPDX-License-Identifier: Apache-2.0
+ * *****************************************************************************
+ */
+
+package ai.konduit.serving.pipeline.impl.format;
+
+import ai.konduit.serving.pipeline.api.data.Image;
+import ai.konduit.serving.pipeline.api.exception.DataConversionException;
+import ai.konduit.serving.pipeline.api.exception.DataLoadingException;
+import ai.konduit.serving.pipeline.api.format.ImageConverter;
+import ai.konduit.serving.pipeline.api.format.ImageFormat;
+import ai.konduit.serving.pipeline.impl.data.image.Png;
+import lombok.AllArgsConstructor;
+import org.nd4j.common.base.Preconditions;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class JavaImageConverters {
+
+ private JavaImageConverters(){ }
+
+ public static class IdentityConverter implements ImageConverter {
+
+ @Override
+ public boolean canConvert(Image from, ImageFormat> to) {
+ return false;
+ }
+
+ @Override
+ public boolean canConvert(Image from, Class> to) {
+ return to.isAssignableFrom(from.get().getClass());
+ }
+
+ @Override
+ public T convert(Image from, ImageFormat to) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ @Override
+ public T convert(Image from, Class to) {
+ Preconditions.checkState(canConvert(from, to), "Unable to convert %s to %s", from.get().getClass(), to);
+ return (T)from.get();
+ }
+ }
+
+ @AllArgsConstructor
+ public static abstract class BaseConverter implements ImageConverter {
+ protected Class> cFrom;
+ protected Class> cTo;
+
+ @Override
+ public boolean canConvert(Image from, ImageFormat> to) {
+ return false;
+ }
+
+ @Override
+ public boolean canConvert(Image from, Class> to) {
+ return cFrom.isAssignableFrom(from.get().getClass()) && cTo.isAssignableFrom(to);
+ }
+
+ @Override
+ public T convert(Image from, ImageFormat to) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ @Override
+ public T convert(Image from, Class to) {
+ Preconditions.checkState(canConvert(from, to), "Unable to convert image to format %s", to);
+ return doConversion(from, to);
+ }
+
+ protected abstract T doConversion(Image from, Class to);
+ }
+
+ public static class PngToBufferedImageConverter extends BaseConverter {
+
+ public PngToBufferedImageConverter() {
+ super(Png.class, BufferedImage.class);
+ }
+
+ @Override
+ protected T doConversion(Image from, Class to) {
+ Png png = (Png) from.get();
+ byte[] bytes = png.getBytes();
+ try(ByteArrayInputStream is = new ByteArrayInputStream(bytes)){
+ BufferedImage bi = ImageIO.read(is);
+ return (T) bi;
+ } catch (IOException e){
+ throw new DataLoadingException("Error converting PNG to BufferedImage", e);
+ }
+ }
+ }
+
+ public static class BufferedImageToPngConverter extends BaseConverter {
+ public BufferedImageToPngConverter() {
+ super(BufferedImage.class, Png.class);
+ }
+
+ @Override
+ protected T doConversion(Image from, Class to) {
+ BufferedImage bi = (BufferedImage) from.get();
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ try {
+ ImageIO.write(bi, "png", os);
+ } catch (IOException e){
+ throw new DataConversionException("Error converting BufferedImage to PNG", e);
+ }
+ byte[] bytes = os.toByteArray();
+ return (T) new Png(bytes);
+ }
+ }
+
+}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaImageFactory.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaImageFactory.java
new file mode 100644
index 000000000..469a2052d
--- /dev/null
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaImageFactory.java
@@ -0,0 +1,81 @@
+/*
+ * ******************************************************************************
+ * * Copyright (c) 2020 Konduit K.K.
+ * *
+ * * This program and the accompanying materials are made available under the
+ * * terms of the Apache License, Version 2.0 which is available at
+ * * https://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.
+ * *
+ * * SPDX-License-Identifier: Apache-2.0
+ * *****************************************************************************
+ */
+
+package ai.konduit.serving.pipeline.impl.format;
+
+import ai.konduit.serving.pipeline.api.data.Image;
+import ai.konduit.serving.pipeline.api.exception.DataLoadingException;
+import ai.konduit.serving.pipeline.api.format.ImageFactory;
+import ai.konduit.serving.pipeline.impl.data.image.BImage;
+import ai.konduit.serving.pipeline.impl.data.image.Png;
+import ai.konduit.serving.pipeline.impl.data.image.PngImage;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+public class JavaImageFactory implements ImageFactory {
+
+ private static final Set> supportedClasses = new HashSet<>();
+ static {
+ Set> s = supportedClasses;
+ s.add(File.class);
+ s.add(Path.class);
+ s.add(Png.class);
+ s.add(BufferedImage.class);
+ }
+
+ @Override
+ public Set> supportedTypes() {
+ return Collections.unmodifiableSet(supportedClasses);
+ }
+
+ @Override
+ public boolean canCreateFrom(Object o) {
+ //TODO what about interfaces, subtypes etc?
+ return supportedClasses.contains(o.getClass());
+ }
+
+ @Override
+ public Image create(Object o) {
+ if(o instanceof File || o instanceof Path){
+ //Try to infer file type from
+ File f;
+ if(o instanceof File){
+ f = (File) o;
+ } else {
+ f = ((Path)o).toFile();
+ }
+ String name = f.getName();
+ if(name.toLowerCase().endsWith(".png")){
+ return new PngImage(new Png(f));
+ }
+ throw new DataLoadingException("Unable to create Image object: unable to guess image file format from File" +
+ " path/filename, or format not supported - " + f.getAbsolutePath());
+ } else if(o instanceof Png){
+ return new PngImage((Png) o);
+ } else if(o instanceof BufferedImage){
+ return new BImage((BufferedImage) o);
+ } else {
+ throw new UnsupportedOperationException("Unable to create Image from object of type: " + o.getClass());
+ }
+ }
+}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaNDArrayConverters.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaNDArrayConverters.java
index 03b50561c..dd0a163e2 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaNDArrayConverters.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaNDArrayConverters.java
@@ -20,6 +20,7 @@
import ai.konduit.serving.pipeline.api.data.NDArray;
import ai.konduit.serving.pipeline.api.data.NDArrayType;
+import ai.konduit.serving.pipeline.impl.data.ndarray.SerializedNDArray;
import ai.konduit.serving.pipeline.api.format.NDArrayConverter;
import ai.konduit.serving.pipeline.api.format.NDArrayFormat;
import lombok.AllArgsConstructor;
@@ -37,7 +38,7 @@ public static class IdentityConverter implements NDArrayConverter {
@Override
- public boolean canConvert(NDArray from, NDArrayFormat to) {
+ public boolean canConvert(NDArray from, NDArrayFormat> to) {
return false;
}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/java/JavaNDArrayFactory.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaNDArrayFactory.java
similarity index 95%
rename from konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/java/JavaNDArrayFactory.java
rename to konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaNDArrayFactory.java
index 56cd82bd9..27b69f249 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/java/JavaNDArrayFactory.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaNDArrayFactory.java
@@ -16,11 +16,11 @@
* *****************************************************************************
*/
-package ai.konduit.serving.pipeline.impl.data.java;
+package ai.konduit.serving.pipeline.impl.format;
import ai.konduit.serving.pipeline.api.data.NDArray;
+import ai.konduit.serving.pipeline.impl.data.ndarray.SerializedNDArray;
import ai.konduit.serving.pipeline.api.format.NDArrayFactory;
-import ai.konduit.serving.pipeline.impl.format.SerializedNDArray;
import java.util.HashSet;
import java.util.Set;
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/java/JavaNDArrays.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaNDArrays.java
similarity index 86%
rename from konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/java/JavaNDArrays.java
rename to konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaNDArrays.java
index 88dfe055c..c0f2f27f7 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/data/java/JavaNDArrays.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/format/JavaNDArrays.java
@@ -16,12 +16,10 @@
* *****************************************************************************
*/
-package ai.konduit.serving.pipeline.impl.data.java;
+package ai.konduit.serving.pipeline.impl.format;
-import ai.konduit.serving.pipeline.api.data.BaseNDArray;
-import ai.konduit.serving.pipeline.api.data.NDArray;
-import ai.konduit.serving.pipeline.api.format.NDArrayFormat;
-import ai.konduit.serving.pipeline.impl.format.SerializedNDArray;
+import ai.konduit.serving.pipeline.impl.data.ndarray.BaseNDArray;
+import ai.konduit.serving.pipeline.impl.data.ndarray.SerializedNDArray;
public class JavaNDArrays {
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/pipeline/SequencePipelineExecutor.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/pipeline/SequencePipelineExecutor.java
index 89015a6c4..d9812ba8b 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/pipeline/SequencePipelineExecutor.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/pipeline/SequencePipelineExecutor.java
@@ -15,6 +15,7 @@
******************************************************************************/
package ai.konduit.serving.pipeline.impl.pipeline;
+import ai.konduit.serving.pipeline.api.context.Context;
import ai.konduit.serving.pipeline.api.data.Data;
import ai.konduit.serving.pipeline.api.pipeline.Pipeline;
import ai.konduit.serving.pipeline.api.pipeline.PipelineExecutor;
@@ -97,10 +98,10 @@ public List getRunners() {
}
@Override
- public Data exec(Data data) {
+ public Data exec(Context ctx, Data data) {
Data current = data;
for(PipelineStepRunner psr : runners){
- current = psr.exec(current);
+ current = psr.exec(ctx, current);
}
return current;
}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/serde/DataJsonDeserializer.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/serde/DataJsonDeserializer.java
index cdaaceb4f..f5db2519b 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/serde/DataJsonDeserializer.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/serde/DataJsonDeserializer.java
@@ -19,10 +19,12 @@
package ai.konduit.serving.pipeline.impl.serde;
import ai.konduit.serving.pipeline.api.data.Data;
+import ai.konduit.serving.pipeline.api.data.Image;
import ai.konduit.serving.pipeline.api.data.NDArray;
import ai.konduit.serving.pipeline.api.data.NDArrayType;
import ai.konduit.serving.pipeline.impl.data.JData;
-import ai.konduit.serving.pipeline.impl.format.SerializedNDArray;
+import ai.konduit.serving.pipeline.impl.data.image.Png;
+import ai.konduit.serving.pipeline.impl.data.ndarray.SerializedNDArray;
import org.nd4j.shade.jackson.core.JsonParser;
import org.nd4j.shade.jackson.core.JsonProcessingException;
import org.nd4j.shade.jackson.databind.DeserializationContext;
@@ -108,7 +110,14 @@ public Data deserialize(JsonParser jp, JsonNode n) {
d.put(s, NDArray.create(ndArray));
} else if (n2.has(Data.RESERVED_KEY_IMAGE_DATA)) {
//Image
- throw new UnsupportedOperationException("Image deserialization not yet implemented");
+ String format = n2.get(Data.RESERVED_KEY_IMAGE_FORMAT).textValue();
+ if(!"PNG".equalsIgnoreCase(format)){
+ throw new UnsupportedOperationException("Deserialization of formats other than PNG not yet implemented");
+ }
+ String base64Data = n2.get(Data.RESERVED_KEY_IMAGE_DATA).textValue();
+ byte[] bytes = Base64.getDecoder().decode(base64Data);
+ Png png = new Png(bytes);
+ d.put(s, Image.create(png));
} else {
//Must be data
Data dInner = deserialize(jp, n2);
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/serde/DataJsonSerializer.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/serde/DataJsonSerializer.java
index 2553932c4..c42328250 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/serde/DataJsonSerializer.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/serde/DataJsonSerializer.java
@@ -22,8 +22,9 @@
import ai.konduit.serving.pipeline.api.data.Image;
import ai.konduit.serving.pipeline.api.data.NDArray;
import ai.konduit.serving.pipeline.api.data.NDArrayType;
-import ai.konduit.serving.pipeline.impl.data.ValueType;
-import ai.konduit.serving.pipeline.impl.format.SerializedNDArray;
+import ai.konduit.serving.pipeline.api.data.ValueType;
+import ai.konduit.serving.pipeline.impl.data.image.Png;
+import ai.konduit.serving.pipeline.impl.data.ndarray.SerializedNDArray;
import org.nd4j.shade.jackson.core.JsonGenerator;
import org.nd4j.shade.jackson.databind.JsonSerializer;
import org.nd4j.shade.jackson.databind.ObjectMapper;
@@ -134,7 +135,15 @@ private void writeLong(JsonGenerator jg, long l) throws IOException {
}
private void writeImage(JsonGenerator jg, Image i) throws IOException {
- throw new UnsupportedOperationException("Not yet implemented: writing image JSON");
+ Png png = i.getAs(Png.class);
+ byte[] imgData = png.getBytes();
+ jg.writeStartObject();
+ jg.writeFieldName(Data.RESERVED_KEY_IMAGE_FORMAT);
+ jg.writeString("PNG"); //TODO No magic constant
+ jg.writeFieldName(Data.RESERVED_KEY_IMAGE_DATA);
+ String base64 = Base64.getEncoder().encodeToString(imgData);
+ jg.writeString(base64);
+ jg.writeEndObject();
}
private void writeNDArray(JsonGenerator jg, NDArray n) throws IOException {
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/step/logging/LoggingPipelineStepRunner.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/step/logging/LoggingPipelineStepRunner.java
index 38356174f..c0bacc985 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/step/logging/LoggingPipelineStepRunner.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/impl/step/logging/LoggingPipelineStepRunner.java
@@ -15,6 +15,7 @@
******************************************************************************/
package ai.konduit.serving.pipeline.impl.step.logging;
+import ai.konduit.serving.pipeline.api.context.Context;
import ai.konduit.serving.pipeline.api.data.Data;
import ai.konduit.serving.pipeline.api.step.PipelineStep;
import ai.konduit.serving.pipeline.api.step.PipelineStepRunner;
@@ -52,7 +53,7 @@ public PipelineStep getPipelineStep() {
}
@Override
- public Data exec(Data data) {
+ public Data exec(Context ctx, Data data) {
Level logLevel = step.getLogLevel();
LoggingPipelineStep.Log toLog = step.getLog();
boolean keysOnly = toLog == LoggingPipelineStep.Log.KEYS;
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/AbstractRegistry.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/AbstractRegistry.java
index eeec34cbe..e52851c5f 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/AbstractRegistry.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/AbstractRegistry.java
@@ -91,4 +91,10 @@ protected synchronized void init(){
factoriesMap = m;
factories = l;
}
+
+ public void addFactoryInstance(T factory){
+ if(factories == null)
+ init();
+ this.factories.add(factory);
+ }
}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/ImageConverteRegistry.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/ImageConverteRegistry.java
deleted file mode 100644
index 1eaf448b9..000000000
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/ImageConverteRegistry.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * ******************************************************************************
- * * Copyright (c) 2020 Konduit K.K.
- * *
- * * This program and the accompanying materials are made available under the
- * * terms of the Apache License, Version 2.0 which is available at
- * * https://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.
- * *
- * * SPDX-License-Identifier: Apache-2.0
- * *****************************************************************************
- */
-
-package ai.konduit.serving.pipeline.registry;
-
-import ai.konduit.serving.pipeline.api.data.Image;
-import ai.konduit.serving.pipeline.api.format.ImageConverter;
-import ai.konduit.serving.pipeline.api.format.ImageFormat;
-import lombok.NonNull;
-import org.nd4j.common.primitives.Pair;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-public class ImageConverteRegistry extends AbstractRegistry {
-
- private static final ImageConverteRegistry INSTANCE = new ImageConverteRegistry();
-
- protected ImageConverteRegistry(){
- super(ImageConverter.class);
- }
-
- public static int numFactories(){
- return INSTANCE.registryNumFactories();
- }
-
- public static List getFactories(){
- return INSTANCE.registryGetFactories();
- }
-
- public static ImageConverter getFactoryFor(@NonNull Object o){
- return INSTANCE.registryGetFactoryFor(o);
- }
-
- @Override
- public boolean acceptFactory(ImageConverter factory, Object o) {
- Pair p = (Pair) o;
- return factory.canConvert(p.getFirst(), p.getSecond());
- }
-
- @Override
- public Set> supportedForFactory(ImageConverter factory) {
- return Collections.emptySet();
- }
-}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/ImageConverterRegistry.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/ImageConverterRegistry.java
new file mode 100644
index 000000000..96b81f777
--- /dev/null
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/ImageConverterRegistry.java
@@ -0,0 +1,144 @@
+/*
+ * ******************************************************************************
+ * * Copyright (c) 2020 Konduit K.K.
+ * *
+ * * This program and the accompanying materials are made available under the
+ * * terms of the Apache License, Version 2.0 which is available at
+ * * https://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.
+ * *
+ * * SPDX-License-Identifier: Apache-2.0
+ * *****************************************************************************
+ */
+
+package ai.konduit.serving.pipeline.registry;
+
+import ai.konduit.serving.pipeline.api.data.Image;
+import ai.konduit.serving.pipeline.api.data.NDArray;
+import ai.konduit.serving.pipeline.api.format.*;
+import ai.konduit.serving.pipeline.api.format.ImageConverter;
+import ai.konduit.serving.pipeline.impl.data.image.Png;
+import lombok.AllArgsConstructor;
+import lombok.NonNull;
+import org.nd4j.common.primitives.Pair;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+public class ImageConverterRegistry extends AbstractRegistry {
+
+ private static final ImageConverterRegistry INSTANCE = new ImageConverterRegistry();
+
+ protected ImageConverterRegistry(){
+ super(ImageConverter.class);
+ }
+
+ public static int numFactories(){
+ return INSTANCE.registryNumFactories();
+ }
+
+ public static List getFactories(){
+ return INSTANCE.registryGetFactories();
+ }
+
+ public static ImageConverter getFactoryFor(@NonNull Object o){
+ return INSTANCE.registryGetFactoryFor(o);
+ }
+
+ @Override
+ public boolean acceptFactory(ImageConverter factory, Object o) {
+ Pair p = (Pair) o;
+ return factory.canConvert(p.getFirst(), p.getSecond());
+ }
+
+ @Override
+ public Set> supportedForFactory(ImageConverter factory) {
+ return Collections.emptySet();
+ }
+
+ public static ImageConverter getConverterFor(Image img, Class> type ){
+ return INSTANCE.getConverterForClass(img, type);
+ }
+
+ public static ImageConverter getConverterFor(Image img, ImageFormat> type ){
+ return INSTANCE.getConverterForType(img, type);
+ }
+
+ public ImageConverter getConverterForClass(Image img, Class> type ){
+ if(factories == null)
+ init();
+
+ if(factoriesMap.containsKey(type)){
+ return factoriesMap.get(type).get(0); //TODO multiple converters available
+ }
+
+ for(ImageConverter c : factories){
+ if(c.canConvert(img, type)){
+ return c;
+ }
+ }
+
+ //No factory is available. Try to fall back on X -> PNG -> Y
+ if(type != Png.class){
+ ImageConverter c1 = getConverterForClass(img, Png.class);
+ if(c1 != null){
+ Image i2 = Image.create(c1.convert(img, Png.class)); //TODO this is ugly - we throw this result away!
+ ImageConverter c2 = getConverterForClass(i2, type);
+ return new TwoStepImageConverter(img.get().getClass(), type, c1, c2);
+ }
+ }
+
+ return null;
+ }
+
+ public ImageConverter getConverterForType(Image img, ImageFormat> type ){
+ if(factories == null)
+ init();
+
+ for(ImageConverter c : factories){
+ if(c.canConvert(img, type)){
+ return c;
+ }
+ }
+ return null;
+ }
+
+ public static void addConverter(ImageConverter f){
+ INSTANCE.addFactoryInstance(f);
+ }
+
+ @AllArgsConstructor
+ private static class TwoStepImageConverter implements ImageConverter {
+ private Class> cFrom;
+ private Class> cTo;
+ private ImageConverter c1;
+ private ImageConverter c2;
+
+ @Override
+ public boolean canConvert(Image from, ImageFormat> to) {
+ return false;
+ }
+
+ @Override
+ public boolean canConvert(Image from, Class> to) {
+ return cFrom.isAssignableFrom(from.get().getClass()) && to.isAssignableFrom(cTo);
+ }
+
+ @Override
+ public T convert(Image from, ImageFormat to) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ @Override
+ public T convert(Image from, Class to) {
+ Image png = Image.create(c1.convert(from, Png.class));
+ return (T) c2.convert(png, cTo);
+ }
+ }
+}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/ImageFactoryRegistry.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/ImageFactoryRegistry.java
index 92ba47b68..2421af0a4 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/ImageFactoryRegistry.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/ImageFactoryRegistry.java
@@ -42,4 +42,8 @@ public static List getFactories(){
public static ImageFactory getFactoryFor(@NonNull Object o){
return INSTANCE.registryGetFactoryFor(o);
}
+
+ public static void addFactory(ImageFactory f){
+ INSTANCE.addFactoryInstance(f);
+ }
}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/NDArrayConverterRegistry.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/NDArrayConverterRegistry.java
index d7a5bb81b..1f5c33222 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/NDArrayConverterRegistry.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/NDArrayConverterRegistry.java
@@ -18,10 +18,12 @@
package ai.konduit.serving.pipeline.registry;
+import ai.konduit.serving.pipeline.api.data.Image;
import ai.konduit.serving.pipeline.api.data.NDArray;
-import ai.konduit.serving.pipeline.api.format.NDArrayConverter;
-import ai.konduit.serving.pipeline.api.format.NDArrayFactory;
-import ai.konduit.serving.pipeline.api.format.NDArrayFormat;
+import ai.konduit.serving.pipeline.api.format.*;
+import ai.konduit.serving.pipeline.impl.data.image.Png;
+import ai.konduit.serving.pipeline.impl.data.ndarray.SerializedNDArray;
+import lombok.AllArgsConstructor;
import lombok.NonNull;
import org.nd4j.common.primitives.Pair;
@@ -81,6 +83,17 @@ public NDArrayConverter getConverterForClass(NDArray arr, Class> type ){
return c;
}
}
+
+ //No factory is available. Try to fall back on X -> SerializedNDArray -> Y
+ if(type != SerializedNDArray.class){
+ NDArrayConverter c1 = getConverterForClass(arr, SerializedNDArray.class);
+ if(c1 != null){
+ NDArray arr2 = NDArray.create(c1.convert(arr, SerializedNDArray.class)); //TODO this is ugly - we throw this result away!
+ NDArrayConverter c2 = getConverterForClass(arr2, type);
+ return new TwoStepNDArrayConverter(arr.get().getClass(), type, c1, c2);
+ }
+ }
+
return null;
}
@@ -95,4 +108,37 @@ public NDArrayConverter getConverterForType(NDArray arr, NDArrayFormat> type )
}
return null;
}
+
+ public static void addConverter(NDArrayConverter f){
+ INSTANCE.addFactoryInstance(f);
+ }
+
+ @AllArgsConstructor
+ private static class TwoStepNDArrayConverter implements NDArrayConverter {
+ private Class> cFrom;
+ private Class> cTo;
+ private NDArrayConverter c1;
+ private NDArrayConverter c2;
+
+ @Override
+ public boolean canConvert(NDArray from, NDArrayFormat> to) {
+ return false;
+ }
+
+ @Override
+ public boolean canConvert(NDArray from, Class> to) {
+ return cFrom.isAssignableFrom(from.get().getClass()) && to.isAssignableFrom(cTo);
+ }
+
+ @Override
+ public T convert(NDArray from, NDArrayFormat to) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ @Override
+ public T convert(NDArray from, Class to) {
+ NDArray sArr = NDArray.create(c1.convert(from, SerializedNDArray.class));
+ return (T) c2.convert(sArr, cTo);
+ }
+ }
}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/NDArrayFactoryRegistry.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/NDArrayFactoryRegistry.java
index 4d4c03a42..046238e46 100644
--- a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/NDArrayFactoryRegistry.java
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/registry/NDArrayFactoryRegistry.java
@@ -18,6 +18,7 @@
package ai.konduit.serving.pipeline.registry;
+import ai.konduit.serving.pipeline.api.format.ImageFactory;
import ai.konduit.serving.pipeline.api.format.NDArrayFactory;
import lombok.NonNull;
@@ -42,4 +43,8 @@ public static List getFactories(){
public static NDArrayFactory getFactoryFor(@NonNull Object o){
return INSTANCE.registryGetFactoryFor(o);
}
+
+ public static void addFactory(NDArrayFactory f){
+ INSTANCE.addFactoryInstance(f);
+ }
}
diff --git a/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/util/FileUtils.java b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/util/FileUtils.java
new file mode 100644
index 000000000..80bc07d3a
--- /dev/null
+++ b/konduit-serving-pipeline/src/main/java/ai/konduit/serving/pipeline/util/FileUtils.java
@@ -0,0 +1,41 @@
+/*
+ * ******************************************************************************
+ * * Copyright (c) 2020 Konduit K.K.
+ * *
+ * * This program and the accompanying materials are made available under the
+ * * terms of the Apache License, Version 2.0 which is available at
+ * * https://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.
+ * *
+ * * SPDX-License-Identifier: Apache-2.0
+ * *****************************************************************************
+ */
+
+package ai.konduit.serving.pipeline.util;
+
+import java.io.File;
+
+public class FileUtils {
+
+ private FileUtils(){ }
+
+ public static File getTempFileBaseDir(){
+ File f = new File(System.getProperty("java.io.tmpdir"), "konduit-serving");
+ if(!f.exists())
+ f.mkdirs();
+ return f;
+ }
+
+ public static File getTempFileDir(String subdirectory){
+ File f = new File(getTempFileBaseDir(), subdirectory);
+ if(!f.exists())
+ f.mkdirs();
+ return f;
+ }
+
+}
diff --git a/konduit-serving-pipeline/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.format.ImageConverter b/konduit-serving-pipeline/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.format.ImageConverter
new file mode 100644
index 000000000..d234e6632
--- /dev/null
+++ b/konduit-serving-pipeline/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.format.ImageConverter
@@ -0,0 +1,21 @@
+#
+# /* ******************************************************************************
+# * Copyright (c) 2020 Konduit K.K.
+# *
+# * This program and the accompanying materials are made available under the
+# * terms of the Apache License, Version 2.0 which is available at
+# * https://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.
+# *
+# * SPDX-License-Identifier: Apache-2.0
+# ******************************************************************************/
+#
+
+ai.konduit.serving.pipeline.impl.format.JavaImageConverters$IdentityConverter
+ai.konduit.serving.pipeline.impl.format.JavaImageConverters$PngToBufferedImageConverter
+ai.konduit.serving.pipeline.impl.format.JavaImageConverters$BufferedImageToPngConverter
\ No newline at end of file
diff --git a/konduit-serving-pipeline/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.format.ImageFactory b/konduit-serving-pipeline/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.format.ImageFactory
new file mode 100644
index 000000000..fc72cc94d
--- /dev/null
+++ b/konduit-serving-pipeline/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.format.ImageFactory
@@ -0,0 +1,20 @@
+#
+# /* ******************************************************************************
+# * Copyright (c) 2020 Konduit K.K.
+# *
+# * This program and the accompanying materials are made available under the
+# * terms of the Apache License, Version 2.0 which is available at
+# * https://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.
+# *
+# * SPDX-License-Identifier: Apache-2.0
+# ******************************************************************************/
+#
+
+
+ai.konduit.serving.pipeline.impl.format.JavaImageFactory
\ No newline at end of file
diff --git a/konduit-serving-pipeline/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.format.NDArrayFactory b/konduit-serving-pipeline/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.format.NDArrayFactory
index cd3e24f4c..82f3d0e94 100644
--- a/konduit-serving-pipeline/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.format.NDArrayFactory
+++ b/konduit-serving-pipeline/src/main/resources/META-INF/services/ai.konduit.serving.pipeline.api.format.NDArrayFactory
@@ -17,4 +17,4 @@
#
-ai.konduit.serving.pipeline.impl.data.java.JavaNDArrayFactory
\ No newline at end of file
+ai.konduit.serving.pipeline.impl.format.JavaNDArrayFactory
\ No newline at end of file
diff --git a/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/data/DataJsonTest.java b/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/data/DataJsonTest.java
index 5c948bfa9..a86795514 100644
--- a/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/data/DataJsonTest.java
+++ b/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/data/DataJsonTest.java
@@ -19,9 +19,12 @@
package ai.konduit.serving.pipeline.impl.data;
import ai.konduit.serving.pipeline.api.data.Data;
+import ai.konduit.serving.pipeline.api.data.Image;
import ai.konduit.serving.pipeline.api.data.NDArray;
+import ai.konduit.serving.pipeline.api.data.ValueType;
import org.junit.Ignore;
import org.junit.Test;
+import org.nd4j.common.resources.Resources;
import java.util.Arrays;
import java.util.List;
@@ -35,7 +38,7 @@ public class DataJsonTest {
public void testBasic(){
for(ValueType vt : ValueType.values()){
- if(vt == ValueType.IMAGE || vt == ValueType.LIST ){ //TODO NO WAY TO PUT LISTS INTO DATA YET - WIP TO BE MERGED SOON
+ if(vt == ValueType.LIST ){ //TODO NO WAY TO PUT LISTS INTO DATA YET - WIP TO BE MERGED SOON
System.out.println("SKIPPING: " + vt);
continue;
}
@@ -54,7 +57,7 @@ public void testBasic(){
d = Data.singleton("myKey", new byte[]{0,1,2});
break;
case IMAGE:
- d = null;
+ d = Data.singleton("myKey", Image.create(Resources.asFile("data/5_32x32.png")));
break;
case DOUBLE:
d = Data.singleton("myKey", 1.0);
diff --git a/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/data/ImageTests.java b/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/data/ImageTests.java
new file mode 100644
index 000000000..c944e93e2
--- /dev/null
+++ b/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/data/ImageTests.java
@@ -0,0 +1,216 @@
+/*
+ * ******************************************************************************
+ * * Copyright (c) 2020 Konduit K.K.
+ * *
+ * * This program and the accompanying materials are made available under the
+ * * terms of the Apache License, Version 2.0 which is available at
+ * * https://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.
+ * *
+ * * SPDX-License-Identifier: Apache-2.0
+ * *****************************************************************************
+ */
+
+package ai.konduit.serving.pipeline.impl.data;
+
+import ai.konduit.serving.pipeline.api.data.Data;
+import ai.konduit.serving.pipeline.api.data.Image;
+import ai.konduit.serving.pipeline.api.format.ImageConverter;
+import ai.konduit.serving.pipeline.api.format.ImageFactory;
+import ai.konduit.serving.pipeline.impl.data.image.BaseImage;
+import ai.konduit.serving.pipeline.impl.data.image.Png;
+import ai.konduit.serving.pipeline.impl.data.image.PngImage;
+import ai.konduit.serving.pipeline.impl.format.JavaImageConverters;
+import ai.konduit.serving.pipeline.registry.ImageConverterRegistry;
+import ai.konduit.serving.pipeline.registry.ImageFactoryRegistry;
+import lombok.AllArgsConstructor;
+import org.apache.commons.io.IOUtils;
+import org.junit.Test;
+import org.nd4j.common.base.Preconditions;
+import org.nd4j.common.resources.Resources;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.util.Collections;
+import java.util.Set;
+
+import static org.junit.Assert.*;
+
+public class ImageTests {
+
+ @Test
+ public void testBasicLoadingConversion() throws Exception {
+
+ File f = Resources.asFile("data/5_32x32.png");
+ System.out.println(f.getAbsolutePath());
+
+ Image i = Image.create(f);
+ assertTrue(i instanceof PngImage);
+ assertTrue(i.get() instanceof Png);
+
+ Png p = i.getAs(Png.class);
+
+ byte[] bytes = p.getBytes();
+ byte[] expBytes;
+ try(InputStream is = new BufferedInputStream(new FileInputStream(f))){
+ expBytes = IOUtils.toByteArray(is);
+ }
+
+ assertArrayEquals(expBytes, bytes);
+
+
+ //Data and JSON serialization
+ Data d = Data.singleton("myImage", i);
+ String json = d.toJson();
+ System.out.println(json);
+ Data dJson = Data.fromJson(json);
+ assertEquals(d, dJson);
+
+
+ //PNG -> BufferedImage
+ BufferedImage bi = i.getAs(BufferedImage.class);
+ BufferedImage biExp = ImageIO.read(f);
+ boolean eq = bufferedImagesEqual(biExp, bi);
+ assertTrue("BufferedImage instances should be equal", eq);
+
+ //BufferedImage creation, Data, JSON and BufferedImage -> PNG
+ Image i2 = Image.create(bi);
+ Data d2 = Data.singleton("myImage", i2);
+ String json2 = d2.toJson();
+ Data d2Json = Data.fromJson(json2);
+
+ assertEquals(d2, d2Json);
+
+ Png png2 = d2Json.getImage("myImage").getAs(Png.class);
+ //assertEquals(p, png2); //TODO - this fails - but that doesn't necessarily mean it's a different image given byte[]
+ boolean eq2 = equalPngs(p, png2);
+ assertTrue("Images differ after conversion to/from PNG", eq2);
+ }
+
+ protected static boolean equalPngs(Png png1, Png png2){
+ try {
+ BufferedImage bi1 = ImageIO.read(new ByteArrayInputStream(png1.getBytes()));
+ BufferedImage bi2 = ImageIO.read(new ByteArrayInputStream(png2.getBytes()));
+ return bufferedImagesEqual(bi1, bi2);
+ } catch (Throwable t){
+ throw new RuntimeException(t);
+ }
+ }
+
+ protected static boolean bufferedImagesEqual(BufferedImage img1, BufferedImage img2) {
+ if (img1.getHeight() != img2.getHeight() || img1.getWidth() != img2.getWidth()) {
+ return false;
+ }
+ int w = img1.getWidth();
+ int h = img1.getHeight();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ if (img1.getRGB(i,j) != img2.getRGB(i,j)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+
+
+ @Test
+ public void test2StepConversion(){
+ //The idea: We add some new module with a new image format, X
+ //In that module, we implement only conversion of X->PNG and PNG->X
+ //What if we want to do X->Y?
+ //ImageConverterRegistry will try to do X->PNG->Y - as PNG is something all image types should implement
+ // conversions to/from for
+
+ File f = Resources.asFile("data/5_32x32.png");
+ System.out.println(f.getAbsolutePath());
+
+ Image i = Image.create(f);
+ assertTrue(i instanceof PngImage);
+ assertTrue(i.get() instanceof Png);
+ Png p = (Png) i.get();
+
+
+
+ ImageFactoryRegistry.addFactory(new TestImageFactory());
+ ImageConverterRegistry.addConverter(new TIToPng());
+ ImageConverterRegistry.addConverter(new PngToTI());
+ Image img = Image.create(new TestImageObject(p));
+
+ //TestImage -> PNG -> BufferedImage
+ BufferedImage bi = img.getAs(BufferedImage.class);
+ BufferedImage exp = i.getAs(BufferedImage.class);
+ assertTrue(bufferedImagesEqual(exp, bi));
+
+ //BufferedImage -> PNG -> TestImage
+ Image i2 = Image.create(exp);
+ TestImageObject out = i2.getAs(TestImageObject.class);
+ assertTrue(equalPngs(p, out.getPng()));
+
+ }
+
+ @AllArgsConstructor
+ @lombok.Data
+ public static class TestImageObject {
+ private Png png;
+ }
+
+ public static class TestImage extends BaseImage {
+ public TestImage(TestImageObject image) {
+ super(image);
+ }
+ }
+
+ public static class TestImageFactory implements ImageFactory {
+
+ @Override
+ public Set> supportedTypes() {
+ return Collections.singleton(TestImageObject.class);
+ }
+
+ @Override
+ public boolean canCreateFrom(Object o) {
+ return o instanceof TestImageObject;
+ }
+
+ @Override
+ public Image create(Object o) {
+ Preconditions.checkState(canCreateFrom(o));
+ return new TestImage((TestImageObject)o);
+ }
+ }
+
+ public static class TIToPng extends JavaImageConverters.BaseConverter {
+ public TIToPng() {
+ super(TestImageObject.class, Png.class);
+ }
+
+ @Override
+ protected T doConversion(Image from, Class to) {
+ TestImageObject o = (TestImageObject) from.get();
+ return (T) o.png;
+ }
+ }
+
+ public static class PngToTI extends JavaImageConverters.BaseConverter {
+ public PngToTI() {
+ super(Png.class, TestImageObject.class);
+ }
+
+ @Override
+ protected T doConversion(Image from, Class to) {
+ Png o = (Png) from.get();
+ return (T) new TestImageObject(o);
+ }
+ }
+
+
+}
diff --git a/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/data/JavaFormatsTest.java b/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/data/JavaFormatsTest.java
index 29232380e..22d12b8d0 100644
--- a/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/data/JavaFormatsTest.java
+++ b/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/data/JavaFormatsTest.java
@@ -20,12 +20,10 @@
import ai.konduit.serving.pipeline.api.data.Data;
import ai.konduit.serving.pipeline.api.data.NDArray;
-import ai.konduit.serving.pipeline.impl.format.SerializedNDArray;
-import org.junit.Ignore;
+import ai.konduit.serving.pipeline.impl.data.ndarray.SerializedNDArray;
import org.junit.Test;
import java.util.Arrays;
-import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
diff --git a/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/data/NDArrayTests.java b/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/data/NDArrayTests.java
new file mode 100644
index 000000000..b7dd2bb69
--- /dev/null
+++ b/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/data/NDArrayTests.java
@@ -0,0 +1,157 @@
+/*
+ * ******************************************************************************
+ * * Copyright (c) 2020 Konduit K.K.
+ * *
+ * * This program and the accompanying materials are made available under the
+ * * terms of the Apache License, Version 2.0 which is available at
+ * * https://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.
+ * *
+ * * SPDX-License-Identifier: Apache-2.0
+ * *****************************************************************************
+ */
+
+package ai.konduit.serving.pipeline.impl.data;
+
+import ai.konduit.serving.pipeline.api.data.NDArray;
+import ai.konduit.serving.pipeline.api.data.NDArrayType;
+import ai.konduit.serving.pipeline.api.format.NDArrayConverter;
+import ai.konduit.serving.pipeline.api.format.NDArrayFactory;
+import ai.konduit.serving.pipeline.api.format.NDArrayFormat;
+import ai.konduit.serving.pipeline.impl.data.ndarray.BaseNDArray;
+import ai.konduit.serving.pipeline.impl.data.ndarray.SerializedNDArray;
+import ai.konduit.serving.pipeline.registry.NDArrayConverterRegistry;
+import ai.konduit.serving.pipeline.registry.NDArrayFactoryRegistry;
+import lombok.AllArgsConstructor;
+import org.junit.Test;
+import org.nd4j.common.base.Preconditions;
+
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.Collections;
+import java.util.Set;
+
+import static org.junit.Assert.assertArrayEquals;
+
+public class NDArrayTests {
+
+ @Test
+ public void test2StepConversion(){
+ //The idea: We add some new module with a new NDArray format, X
+ //In that module, we implement only conversion of X->SerializedNDArray and SerializedNDArray->X
+ //What if we want to do X->Y?
+ //NDArrayConverterRegistry will try to do X->SerializedNDArray->Y - as SerializedNDArray is something all image types should implement
+ // conversions to/from for
+
+ float[] f = new float[]{1,2,3};
+
+
+ NDArrayFactoryRegistry.addFactory(new TestNDArrayFactory());
+ NDArrayConverterRegistry.addConverter(new TNDToSerializedNDArray());
+ NDArrayConverterRegistry.addConverter(new SerializedNDArrayToTND());
+ NDArray nd = NDArray.create(new TestNDArrayObject(f));
+
+ //TestNDArray -> SerializedNDArray -> float[]
+ float[] fArr = nd.getAs(float[].class);
+ assertArrayEquals(f, fArr, 0.0f);
+
+ //float[] -> SerializedNDArray -> TestNDArray
+ NDArray i2 = NDArray.create(f);
+ TestNDArrayObject out = i2.getAs(TestNDArrayObject.class);
+ float[] outF = out.getF();
+ assertArrayEquals(f, outF, 0.0f);
+ }
+
+ @AllArgsConstructor
+ @lombok.Data
+ public static class TestNDArrayObject {
+ private float[] f;
+ }
+
+ public static class TestNDArray extends BaseNDArray {
+ public TestNDArray(TestNDArrayObject o) {
+ super(o);
+ }
+ }
+
+ public static class TestNDArrayFactory implements NDArrayFactory {
+
+ @Override
+ public Set> supportedTypes() {
+ return Collections.singleton(TestNDArrayObject.class);
+ }
+
+ @Override
+ public boolean canCreateFrom(Object o) {
+ return o instanceof TestNDArrayObject;
+ }
+
+ @Override
+ public NDArray create(Object o) {
+ Preconditions.checkState(canCreateFrom(o));
+ return new NDArrayTests.TestNDArray((TestNDArrayObject)o);
+ }
+ }
+
+ public static class TNDToSerializedNDArray implements NDArrayConverter {
+ @Override
+ public boolean canConvert(NDArray from, NDArrayFormat> to) {
+ return false;
+ }
+
+ @Override
+ public boolean canConvert(NDArray from, Class> to) {
+ return from.get().getClass() == TestNDArrayObject.class && to == SerializedNDArray.class;
+ }
+
+ @Override
+ public T convert(NDArray from, NDArrayFormat to) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public T convert(NDArray from, Class to) {
+ Preconditions.checkState(canConvert(from, to));
+ TestNDArrayObject arr = (TestNDArrayObject) from.get();
+ float[] fArr = arr.getF();
+ ByteBuffer bb = ByteBuffer.allocateDirect(fArr.length * 4);
+ bb.asFloatBuffer().put(fArr);
+ return (T) new SerializedNDArray(NDArrayType.FLOAT, new long[]{fArr.length}, bb);
+ }
+ }
+
+ public static class SerializedNDArrayToTND implements NDArrayConverter {
+
+ @Override
+ public boolean canConvert(NDArray from, NDArrayFormat> to) {
+ return false;
+ }
+
+ @Override
+ public boolean canConvert(NDArray from, Class> to) {
+ return to == TestNDArrayObject.class && from.get().getClass() == SerializedNDArray.class;
+ }
+
+ @Override
+ public T convert(NDArray from, NDArrayFormat to) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public T convert(NDArray from, Class to) {
+ Preconditions.checkState(canConvert(from, to));
+ SerializedNDArray s = (SerializedNDArray)from.get();
+ FloatBuffer fb = s.getBuffer().asFloatBuffer();
+ int len = fb.capacity();
+ float[] out = new float[len];
+ fb.get(out);
+ return (T) new TestNDArrayObject(out);
+ }
+ }
+
+}
diff --git a/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/step/TestPipelineSteps.java b/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/step/TestPipelineSteps.java
index 686958f41..d815981b5 100644
--- a/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/step/TestPipelineSteps.java
+++ b/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/step/TestPipelineSteps.java
@@ -46,10 +46,10 @@ public void testLoggingPipeline(){
Data d = Data.singleton("someDouble", 1.0);
d.put("someKey", "someValue");
- pe.exec(d);
+ pe.exec(null, d);
assertEquals(1, count1.get());
assertEquals(1, count2.get());
- pe.exec(d);
+ pe.exec(null, d);
assertEquals(2, count1.get());
assertEquals(2, count2.get());
}
diff --git a/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/util/CallbackPipelineStep.java b/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/util/CallbackPipelineStep.java
index c8801d19f..e71343e23 100644
--- a/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/util/CallbackPipelineStep.java
+++ b/konduit-serving-pipeline/src/test/java/ai/konduit/serving/pipeline/impl/util/CallbackPipelineStep.java
@@ -15,6 +15,7 @@
******************************************************************************/
package ai.konduit.serving.pipeline.impl.util;
+import ai.konduit.serving.pipeline.api.context.Context;
import ai.konduit.serving.pipeline.api.data.Data;
import ai.konduit.serving.pipeline.api.step.PipelineStep;
import ai.konduit.serving.pipeline.api.step.PipelineStepRunner;
@@ -63,7 +64,7 @@ public PipelineStep getPipelineStep() {
}
@Override
- public Data exec(Data data) {
+ public Data exec(Context ctx, Data data) {
step.consumer.accept(data);
return data;
}
diff --git a/konduit-serving-pipeline/src/test/resources/data/5_32x32.png b/konduit-serving-pipeline/src/test/resources/data/5_32x32.png
new file mode 100644
index 000000000..a21967b8a
Binary files /dev/null and b/konduit-serving-pipeline/src/test/resources/data/5_32x32.png differ