Tensorflow functions and applications
* initial step
* Tensorflow models functional model redesign
-- Based on https://tzolov.github.io/mind-model-services
-- Resolves #5
* Add object detection processor README
* Add image recognition processor README
* Initial Tensorflow commonn README
* Initial Tensorflow commonn README
* Tensorflow common diagram
* Tensorflow docs code
* Tensorflow docs code snippets improve
* Tensorflow docs code snippets improve
* Tensorflow docs code snippets improve
* Tensorflow docs code snippets improve
* Add semantic segmentation function. add object detecteion function readme
* oo images
* Furether oo readme improvments
* Final obj detection readme fixes
* Add image recognition readme
* Add image recognition readme 2
* Semantic segmentation readme
* Segmentation readme
* Semantic segmentation readme 3
* Fix image recognition and object detcion app starter dependecies
* Add metadata for Tensorflow apps
This commit is contained in:
committed by
Soby Chacko
parent
052f86cf91
commit
ebe05bc315
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2020-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.app.processor.image.recognition;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.fn.common.tensorflow.deprecated.JsonMapperFunction;
|
||||
import org.springframework.cloud.fn.image.recognition.ImageRecognition;
|
||||
import org.springframework.cloud.fn.image.recognition.ImageRecognitionAugmenter;
|
||||
import org.springframework.cloud.fn.image.recognition.RecognitionResponse;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(ImageRecognitionProcessorProperties.class)
|
||||
public class ImageRecognitionProcessorConfiguration {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ImageRecognitionProcessorConfiguration.class);
|
||||
|
||||
/**
|
||||
* Name of the Message header containing the JSON encoded recognition response.
|
||||
*/
|
||||
public static final String RECOGNIZED_OBJECTS_HEADER = "recognized_objects";
|
||||
|
||||
@Bean
|
||||
public Function<Message<byte[]>, Message<byte[]>> imageRecognitionFunction(ImageRecognitionProcessorProperties properties) {
|
||||
|
||||
return input -> {
|
||||
// You can use file:, http: or classpath: to provide the path to the input image.
|
||||
byte[] inputImage = input.getPayload();
|
||||
|
||||
try (ImageRecognition imageRecognition = createImageRecognitionFunction(properties)) {
|
||||
|
||||
List<RecognitionResponse> recognizedObjects =
|
||||
ImageRecognition.toRecognitionResponse(imageRecognition.recognizeTopK(inputImage));
|
||||
|
||||
// Draw the predicted labels on top of the input image.
|
||||
byte[] augmentedImage = new ImageRecognitionAugmenter().apply(inputImage, recognizedObjects);
|
||||
|
||||
String jsonRecognizedObjects = new JsonMapperFunction().apply(recognizedObjects);
|
||||
|
||||
Message<byte[]> outMessage = MessageBuilder
|
||||
.withPayload(augmentedImage)
|
||||
.setHeader(RECOGNIZED_OBJECTS_HEADER, jsonRecognizedObjects)
|
||||
.build();
|
||||
|
||||
if (properties.isDebugOutput()) {
|
||||
try {
|
||||
logger.info("recognized objects = " + jsonRecognizedObjects);
|
||||
IOUtils.write(augmentedImage, new FileOutputStream(properties.getDebugOutputPath()));
|
||||
}
|
||||
catch (IOException e) {
|
||||
logger.warn("Cloud not produce debug output", e);
|
||||
}
|
||||
}
|
||||
|
||||
return outMessage;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static ImageRecognition createImageRecognitionFunction(ImageRecognitionProcessorProperties properties) {
|
||||
switch (properties.getModelType()) {
|
||||
case inception:
|
||||
return ImageRecognition.inception(
|
||||
properties.getModel(),
|
||||
properties.getNormalizedImageSize(),
|
||||
properties.getResponseSize(),
|
||||
properties.isCacheModel());
|
||||
case mobilenetv1:
|
||||
return ImageRecognition.mobileNetV1(
|
||||
properties.getModel(),
|
||||
properties.getNormalizedImageSize(),
|
||||
properties.getResponseSize(),
|
||||
properties.isCacheModel());
|
||||
case mobilenetv2:
|
||||
return ImageRecognition.mobileNetV2(
|
||||
properties.getModel(),
|
||||
properties.getNormalizedImageSize(),
|
||||
properties.getResponseSize(),
|
||||
properties.isCacheModel());
|
||||
default:
|
||||
throw new RuntimeException("Not supported Model Type: " + properties.getModelType());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2020-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.app.processor.image.recognition;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@ConfigurationProperties("image.recognition")
|
||||
@Validated
|
||||
public class ImageRecognitionProcessorProperties {
|
||||
|
||||
enum ModelType {
|
||||
inception,
|
||||
mobilenetv1,
|
||||
mobilenetv2
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports three different pre-trained tensorflow image recognition models: Inception, MobileNetV1 and MobileNetV2
|
||||
*
|
||||
* 1. Inception graph uses "input" as input and "output" as output.
|
||||
* 2. MobileNetV2 pre-trained models: https://github.com/tensorflow/models/tree/master/research/slim/nets/mobilenet#pretrained-models
|
||||
* - normalized image size is always square (e.g. H=W)
|
||||
* - graph uses "input" as input and "MobilenetV2/Predictions/Reshape_1" as output.
|
||||
* 3. MobileNetV1 pre-trained models: https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet_v1.md#pre-trained-models
|
||||
* - graph uses "input" as input and "MobilenetV1/Predictions/Reshape_1" as output.
|
||||
*/
|
||||
private ModelType modelType = ModelType.mobilenetv2;
|
||||
|
||||
/**
|
||||
* pre-trained tensorflow image recognition model. Note that the model must match the selected model type!
|
||||
*/
|
||||
private String model = "https://storage.googleapis.com/mobilenet_v2/checkpoints/mobilenet_v2_1.4_224.tgz#mobilenet_v2_1.4_224_frozen.pb";
|
||||
|
||||
/**
|
||||
* cache the pre-trained tensorflow model.
|
||||
*/
|
||||
private boolean cacheModel = true;
|
||||
|
||||
/**
|
||||
* Normalized image size.
|
||||
*/
|
||||
private int normalizedImageSize = 224;
|
||||
|
||||
/**
|
||||
* number of recognized images.
|
||||
*/
|
||||
private int responseSize = 5;
|
||||
|
||||
private boolean debugOutput = false;
|
||||
|
||||
private String debugOutputPath = "image-recognition-result.png";
|
||||
|
||||
public ModelType getModelType() {
|
||||
return modelType;
|
||||
}
|
||||
|
||||
public void setModelType(ModelType modelType) {
|
||||
this.modelType = modelType;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public boolean isCacheModel() {
|
||||
return cacheModel;
|
||||
}
|
||||
|
||||
public void setCacheModel(boolean cacheModel) {
|
||||
this.cacheModel = cacheModel;
|
||||
}
|
||||
|
||||
public int getNormalizedImageSize() {
|
||||
return normalizedImageSize;
|
||||
}
|
||||
|
||||
public void setNormalizedImageSize(int normalizedImageSize) {
|
||||
this.normalizedImageSize = normalizedImageSize;
|
||||
}
|
||||
|
||||
public int getResponseSize() {
|
||||
return responseSize;
|
||||
}
|
||||
|
||||
public void setResponseSize(int responseSize) {
|
||||
this.responseSize = responseSize;
|
||||
}
|
||||
|
||||
public boolean isDebugOutput() {
|
||||
return debugOutput;
|
||||
}
|
||||
|
||||
public void setDebugOutput(boolean debugOutput) {
|
||||
this.debugOutput = debugOutput;
|
||||
}
|
||||
|
||||
public String getDebugOutputPath() {
|
||||
return debugOutputPath;
|
||||
}
|
||||
|
||||
public void setDebugOutputPath(String debugOutputPath) {
|
||||
this.debugOutputPath = debugOutputPath;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
configuration-properties.classes=\
|
||||
org.springframework.cloud.stream.app.processor.image.recognition.ImageRecognitionProcessorProperties
|
||||
@@ -0,0 +1,11 @@
|
||||
image:
|
||||
recognition:
|
||||
model: https://storage.googleapis.com/mobilenet_v2/checkpoints/mobilenet_v2_1.4_224.tgz#mobilenet_v2_1.4_224_frozen.pb
|
||||
modelType: mobilenetv2
|
||||
responseSize: 3
|
||||
normalizedImageSize: 224
|
||||
cacheModel: true
|
||||
spring:
|
||||
cloud:
|
||||
function:
|
||||
definition: imageRecognitionFunction
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2020-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.stream.app.processor.image.recognition;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.cloud.fn.common.tensorflow.deprecated.GraphicsUtils;
|
||||
import org.springframework.cloud.stream.binder.test.InputDestination;
|
||||
import org.springframework.cloud.stream.binder.test.OutputDestination;
|
||||
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
public class ImageRecognitionProcessorTests {
|
||||
|
||||
@Test
|
||||
public void testImageRecognitionProcessorMobileNetV2() throws IOException {
|
||||
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(
|
||||
TestChannelBinderConfiguration.getCompleteConfiguration(ImageRecognitionProcessorTestApplication.class))
|
||||
.web(WebApplicationType.NONE)
|
||||
.run("--spring.cloud.function.definition=imageRecognitionFunction",
|
||||
"--image.recognition.modelType=mobilenetv2",
|
||||
"--image.recognition.responseSize=3",
|
||||
"--image.recognition.debugOutput=true",
|
||||
"--image.recognition.debugOutputPath=./target/image-recognition-mobilenetv2.png")) {
|
||||
|
||||
InputDestination processorInput = context.getBean(InputDestination.class);
|
||||
OutputDestination processorOutput = context.getBean(OutputDestination.class);
|
||||
|
||||
byte[] inputImage = GraphicsUtils.loadAsByteArray("classpath:/images/giant_panda_in_beijing_zoo_1.jpg");
|
||||
processorInput.send(new GenericMessage<>(inputImage));
|
||||
Message<byte[]> sourceMessage = processorOutput.receive(10000);
|
||||
String jsonRecognizedObjects = (String) sourceMessage.getHeaders().get(ImageRecognitionProcessorConfiguration.RECOGNIZED_OBJECTS_HEADER);
|
||||
assertThat(jsonRecognizedObjects)
|
||||
.isEqualTo("[{\"label\":\"giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca\",\"probability\":0.962329626083374}," +
|
||||
"{\"label\":\"badger\",\"probability\":0.006058811210095882}," +
|
||||
"{\"label\":\"ram, tup\",\"probability\":0.0010668420000001788}]");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImageRecognitionProcessorMobileNetV1() throws IOException {
|
||||
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(
|
||||
TestChannelBinderConfiguration.getCompleteConfiguration(ImageRecognitionProcessorTestApplication.class))
|
||||
.web(WebApplicationType.NONE)
|
||||
.run("--image.recognition.model=https://download.tensorflow.org/models/mobilenet_v1_2018_08_02/mobilenet_v1_1.0_224.tgz#mobilenet_v1_1.0_224_frozen.pb",
|
||||
"--image.recognition.modelType=mobilenetv1",
|
||||
"--image.recognition.responseSize=3",
|
||||
"--image.recognition.debugOutput=true",
|
||||
"--image.recognition.debugOutputPath=./target/image-recognition-mobilenetv1.png")) {
|
||||
|
||||
InputDestination processorInput = context.getBean(InputDestination.class);
|
||||
OutputDestination processorOutput = context.getBean(OutputDestination.class);
|
||||
|
||||
byte[] inputImage = GraphicsUtils.loadAsByteArray("classpath:/images/giant_panda_in_beijing_zoo_1.jpg");
|
||||
processorInput.send(new GenericMessage<>(inputImage));
|
||||
Message<byte[]> sourceMessage = processorOutput.receive(10000);
|
||||
String jsonRecognizedObjects = (String) sourceMessage.getHeaders().get(ImageRecognitionProcessorConfiguration.RECOGNIZED_OBJECTS_HEADER);
|
||||
assertThat(jsonRecognizedObjects)
|
||||
.isEqualTo("[{\"label\":\"giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca\",\"probability\":0.984053909778595},{\"label\":\"ram, tup\",\"probability\":0.0019619385711848736},{\"label\":\"Staffordshire bullterrier, Staffordshire bull terrier\",\"probability\":0.0018697341438382864}]");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImageRecognitionProcessorInception() throws IOException {
|
||||
try (ConfigurableApplicationContext context = new SpringApplicationBuilder(
|
||||
TestChannelBinderConfiguration.getCompleteConfiguration(ImageRecognitionProcessorTestApplication.class))
|
||||
.web(WebApplicationType.NONE)
|
||||
.run("--image.recognition.model=https://storage.googleapis.com/scdf-tensorflow-models/image-recognition/tensorflow_inception_graph.pb",
|
||||
"--image.recognition.modelType=inception",
|
||||
"--image.recognition.responseSize=3",
|
||||
"--image.recognition.debugOutput=true",
|
||||
"--image.recognition.debugOutputPath=./target/image-recognition-inception.png")) {
|
||||
|
||||
InputDestination processorInput = context.getBean(InputDestination.class);
|
||||
OutputDestination processorOutput = context.getBean(OutputDestination.class);
|
||||
|
||||
byte[] inputImage = GraphicsUtils.loadAsByteArray("classpath:/images/giant_panda_in_beijing_zoo_1.jpg");
|
||||
processorInput.send(new GenericMessage<>(inputImage));
|
||||
Message<byte[]> sourceMessage = processorOutput.receive(10000);
|
||||
String jsonRecognizedObjects = (String) sourceMessage.getHeaders().get(ImageRecognitionProcessorConfiguration.RECOGNIZED_OBJECTS_HEADER);
|
||||
assertThat(jsonRecognizedObjects)
|
||||
.isEqualTo("[{\"label\":\"giant panda\",\"probability\":0.9946685433387756},{\"label\":\"Arctic fox\",\"probability\":0.0036631159018725157},{\"label\":\"ice bear\",\"probability\":3.378273395355791E-4}]");
|
||||
}
|
||||
}
|
||||
|
||||
@SpringBootApplication
|
||||
@Import({ ImageRecognitionProcessorConfiguration.class })
|
||||
public static class ImageRecognitionProcessorTestApplication {
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 113 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Reference in New Issue
Block a user