GH-7: Add checkstyle and javaformat plugins
Fixes: #7 * Run `./gradlew format` * Updates from PR review suggestions
This commit is contained in:
@@ -33,8 +33,8 @@ import org.springframework.cloud.fn.object.detection.domain.ObjectDetection;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Augment the input image fromMemory detected object bounding boxes and categories.
|
||||
* For mask models and withMask set to true it draws the instance segmentation image as well.
|
||||
* Augment the input image fromMemory detected object bounding boxes and categories. For
|
||||
* mask models and withMask set to true it draws the instance segmentation image as well.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@@ -48,6 +48,7 @@ public class ObjectDetectionImageAugmenter implements BiFunction<byte[], List<Ob
|
||||
private String imageFormat = DEFAULT_IMAGE_FORMAT;
|
||||
|
||||
private final boolean withMask;
|
||||
|
||||
private boolean agnosticColors = false;
|
||||
|
||||
public ObjectDetectionImageAugmenter() {
|
||||
@@ -99,8 +100,7 @@ public class ObjectDetectionImageAugmenter implements BiFunction<byte[], List<Ob
|
||||
float[][] mask = od.getMask();
|
||||
if (mask != null) {
|
||||
Color maskColor = this.agnosticColors ? null : GraphicsUtils.getClassColor(cid);
|
||||
BufferedImage maskImage = GraphicsUtils.createMaskImage(
|
||||
mask, x2 - x1, y2 - y1, maskColor);
|
||||
BufferedImage maskImage = GraphicsUtils.createMaskImage(mask, x2 - x1, y2 - y1, maskColor);
|
||||
GraphicsUtils.overlayImages(bufferedImage, maskImage, x1, y1);
|
||||
}
|
||||
}
|
||||
@@ -116,4 +116,5 @@ public class ObjectDetectionImageAugmenter implements BiFunction<byte[], List<Ob
|
||||
// Null mend that QR image is found and not output message will be send.
|
||||
return imageBytes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -41,8 +41,10 @@ public class ObjectDetectionInputAdapter implements Function<byte[], Map<String,
|
||||
|
||||
/** Make checkstyle happy. **/
|
||||
public static final String RAW_IMAGE = "raw_image";
|
||||
|
||||
/** Make checkstyle happy. **/
|
||||
public static final String NORMALIZED_IMAGE = "normalized_image";
|
||||
|
||||
/** Make checkstyle happy. **/
|
||||
public static final long CHANNELS = 3;
|
||||
|
||||
@@ -50,14 +52,14 @@ public class ObjectDetectionInputAdapter implements Function<byte[], Map<String,
|
||||
|
||||
public ObjectDetectionInputAdapter() {
|
||||
|
||||
this.imageLoaderGraph = new GraphRunner(RAW_IMAGE, NORMALIZED_IMAGE)
|
||||
.withGraphDefinition(tf -> {
|
||||
Placeholder<String> rawImage = tf.withName(RAW_IMAGE).placeholder(String.class);
|
||||
Operand<UInt8> decodedImage = tf.dtypes.cast(
|
||||
tf.image.decodeJpeg(rawImage, DecodeJpeg.channels(CHANNELS)), UInt8.class);
|
||||
// Expand dimensions since the model expects images to have shape: [1, H, W, 3]
|
||||
tf.withName(NORMALIZED_IMAGE).expandDims(decodedImage, tf.constant(0));
|
||||
});
|
||||
this.imageLoaderGraph = new GraphRunner(RAW_IMAGE, NORMALIZED_IMAGE).withGraphDefinition(tf -> {
|
||||
Placeholder<String> rawImage = tf.withName(RAW_IMAGE).placeholder(String.class);
|
||||
Operand<UInt8> decodedImage = tf.dtypes.cast(tf.image.decodeJpeg(rawImage, DecodeJpeg.channels(CHANNELS)),
|
||||
UInt8.class);
|
||||
// Expand dimensions since the model expects images to have shape: [1, H, W,
|
||||
// 3]
|
||||
tf.withName(NORMALIZED_IMAGE).expandDims(decodedImage, tf.constant(0));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,4 +75,5 @@ public class ObjectDetectionInputAdapter implements Function<byte[], Map<String,
|
||||
this.imageLoaderGraph.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ import org.tensorflow.types.UInt8;
|
||||
import org.springframework.cloud.fn.common.tensorflow.deprecated.GraphicsUtils;
|
||||
|
||||
/**
|
||||
* Converts byte array image into a input Tensor for the Object Detection API. The computed image tensors uses the
|
||||
* 'image_tensor' model placeholder.
|
||||
* Converts byte array image into a input Tensor for the Object Detection API. The
|
||||
* computed image tensors uses the 'image_tensor' model placeholder.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@@ -95,4 +95,5 @@ public class ObjectDetectionInputConverter implements Function<byte[][], Map<Str
|
||||
data[i + 2] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,21 +37,22 @@ import org.springframework.util.StreamUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Converts the Tensorflow Object Detection result into {@link ObjectDetection} list.
|
||||
* The pre-trained Object Detection models (http://bit.ly/2osxMAY) produce 3 tensor outputs:
|
||||
* (1) detection_classes - containing the ids of detected objects, (2) detection_scores - confidence probabilities of the
|
||||
* detected object and (3) detection_boxes - the object bounding boxes withing the images.
|
||||
* Converts the Tensorflow Object Detection result into {@link ObjectDetection} list. The
|
||||
* pre-trained Object Detection models (http://bit.ly/2osxMAY) produce 3 tensor outputs:
|
||||
* (1) detection_classes - containing the ids of detected objects, (2) detection_scores -
|
||||
* confidence probabilities of the detected object and (3) detection_boxes - the object
|
||||
* bounding boxes withing the images.
|
||||
*
|
||||
* The MASK based models provide to 2 additional tensors: (4) num_detections and (5) detection_masks.
|
||||
* The MASK based models provide to 2 additional tensors: (4) num_detections and (5)
|
||||
* detection_masks.
|
||||
*
|
||||
* All outputs tensors are float arrays, having:
|
||||
* - 1 as the first dimension
|
||||
* - maxObjects as the second dimension
|
||||
* While boxesT will have 4 as the third dimension (2 sets of (x, y) coordinates).
|
||||
* This can be verified by looking at scoresT.shape() etc.
|
||||
* All outputs tensors are float arrays, having: - 1 as the first dimension - maxObjects
|
||||
* as the second dimension While boxesT will have 4 as the third dimension (2 sets of (x,
|
||||
* y) coordinates). This can be verified by looking at scoresT.shape() etc.
|
||||
*
|
||||
* The format detected classes (e.g. labels) names is defined by the 'string_int_labels_map.proto'. The input list
|
||||
* is available at: https://github.com/tensorflow/models/tree/master/research/object_detection/data
|
||||
* The format detected classes (e.g. labels) names is defined by the
|
||||
* 'string_int_labels_map.proto'. The input list is available at:
|
||||
* https://github.com/tensorflow/models/tree/master/research/object_detection/data
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@@ -61,17 +62,23 @@ public class ObjectDetectionOutputConverter implements Function<Map<String, Tens
|
||||
|
||||
/** DETECTION_CLASSES. */
|
||||
public static final String DETECTION_CLASSES = "detection_classes";
|
||||
|
||||
/** DETECTION_SCORES. */
|
||||
public static final String DETECTION_SCORES = "detection_scores";
|
||||
|
||||
/** DETECTION_BOXES. */
|
||||
public static final String DETECTION_BOXES = "detection_boxes";
|
||||
|
||||
/** DETECTION_MASKS. */
|
||||
public static final String DETECTION_MASKS = "detection_masks";
|
||||
|
||||
/** NUM_DETECTIONS. */
|
||||
public static final String NUM_DETECTIONS = "num_detections";
|
||||
|
||||
private final String[] labels;
|
||||
|
||||
private float confidence;
|
||||
|
||||
private List<String> modelFetch;
|
||||
|
||||
public ObjectDetectionOutputConverter(Resource labelsResource, float confidence, List<String> modelFetch) {
|
||||
@@ -96,15 +103,16 @@ public class ObjectDetectionOutputConverter implements Function<Map<String, Tens
|
||||
private static String[] loadLabels(Resource labelsResource) throws Exception {
|
||||
try (InputStream is = labelsResource.getInputStream()) {
|
||||
String text = StreamUtils.copyToString(is, Charset.forName("UTF-8"));
|
||||
StringIntLabelMapOuterClass.StringIntLabelMap.Builder builder =
|
||||
StringIntLabelMapOuterClass.StringIntLabelMap.newBuilder();
|
||||
StringIntLabelMapOuterClass.StringIntLabelMap.Builder builder = StringIntLabelMapOuterClass.StringIntLabelMap
|
||||
.newBuilder();
|
||||
TextFormat.merge(text, builder);
|
||||
StringIntLabelMapOuterClass.StringIntLabelMap proto = builder.build();
|
||||
|
||||
int maxLabelId = proto.getItemList().stream()
|
||||
.map(StringIntLabelMapOuterClass.StringIntLabelMapItem::getId)
|
||||
.max(Comparator.comparing(i -> i))
|
||||
.orElse(-1);
|
||||
int maxLabelId = proto.getItemList()
|
||||
.stream()
|
||||
.map(StringIntLabelMapOuterClass.StringIntLabelMapItem::getId)
|
||||
.max(Comparator.comparing(i -> i))
|
||||
.orElse(-1);
|
||||
|
||||
String[] labelIdToNameMap = new String[maxLabelId + 1];
|
||||
for (StringIntLabelMapOuterClass.StringIntLabelMapItem item : proto.getItemList()) {
|
||||
@@ -112,7 +120,8 @@ public class ObjectDetectionOutputConverter implements Function<Map<String, Tens
|
||||
labelIdToNameMap[item.getId()] = item.getDisplayName();
|
||||
}
|
||||
else {
|
||||
// Common practice is to set the name to a MID or Synsets Id. Synset is a set of synonyms that
|
||||
// Common practice is to set the name to a MID or Synsets Id. Synset
|
||||
// is a set of synonyms that
|
||||
// share a common meaning: https://en.wikipedia.org/wiki/WordNet
|
||||
labelIdToNameMap[item.getId()] = item.getName();
|
||||
}
|
||||
@@ -125,13 +134,13 @@ public class ObjectDetectionOutputConverter implements Function<Map<String, Tens
|
||||
public List<List<ObjectDetection>> apply(Map<String, Tensor<?>> tensorMap) {
|
||||
|
||||
try (Tensor<Float> scoresTensor = tensorMap.get(DETECTION_SCORES).expect(Float.class);
|
||||
Tensor<Float> classesTensor = tensorMap.get(DETECTION_CLASSES).expect(Float.class);
|
||||
Tensor<Float> boxesTensor = tensorMap.get(DETECTION_BOXES).expect(Float.class)
|
||||
) {
|
||||
Tensor<Float> classesTensor = tensorMap.get(DETECTION_CLASSES).expect(Float.class);
|
||||
Tensor<Float> boxesTensor = tensorMap.get(DETECTION_BOXES).expect(Float.class)) {
|
||||
// All these tensors have:
|
||||
// - 1 as the first dimension
|
||||
// - maxObjects as the second dimension
|
||||
// While boxesT will have 4 as the third dimension (2 sets of (x, y) coordinates).
|
||||
// While boxesT will have 4 as the third dimension (2 sets of (x, y)
|
||||
// coordinates).
|
||||
// This can be verified by looking at scoresT.shape() etc.
|
||||
int batchSize = (int) scoresTensor.shape()[0];
|
||||
int maxObjects = (int) scoresTensor.shape()[1];
|
||||
@@ -143,10 +152,10 @@ public class ObjectDetectionOutputConverter implements Function<Map<String, Tens
|
||||
|
||||
for (int batchIndex = 0; batchIndex < batchSize; batchIndex++) {
|
||||
|
||||
|
||||
List<ObjectDetection> objectDetections = new ArrayList<>();
|
||||
|
||||
// Collect only the objects whose scores are at above the configured confidence threshold.
|
||||
// Collect only the objects whose scores are at above the configured
|
||||
// confidence threshold.
|
||||
for (int i = 0; i < scores[batchIndex].length; ++i) {
|
||||
if (scores[batchIndex][i] >= confidence) {
|
||||
String category = labels[(int) classes[batchIndex][i]];
|
||||
@@ -169,7 +178,8 @@ public class ObjectDetectionOutputConverter implements Function<Map<String, Tens
|
||||
|
||||
if (masksTensor != null) {
|
||||
long[] shape = masksTensor.shape();
|
||||
float[][][][] masks = masksTensor.copyTo(new float[(int) shape[0]][(int) shape[1]][(int) shape[2]][(int) shape[3]]);
|
||||
float[][][][] masks = masksTensor
|
||||
.copyTo(new float[(int) shape[0]][(int) shape[1]][(int) shape[2]][(int) shape[3]]);
|
||||
od.setMask(masks[batchIndex][i]);
|
||||
logger.debug(String.format("Num detections: %s, Masks: %s", nd, masks));
|
||||
}
|
||||
@@ -184,4 +194,5 @@ public class ObjectDetectionOutputConverter implements Function<Map<String, Tens
|
||||
return batchObjectDetections;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,66 +29,79 @@ import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* Convenience class that leverages the the {@link ObjectDetectionInputConverter}, {@link ObjectDetectionOutputConverter} and {@link TensorFlowService}
|
||||
* in combination fromMemory the Tensorflow Object Detection API (https://github.com/tensorflow/models/tree/master/research/object_detection)
|
||||
* models for detection objects in input images.
|
||||
* Convenience class that leverages the the {@link ObjectDetectionInputConverter},
|
||||
* {@link ObjectDetectionOutputConverter} and {@link TensorFlowService} in combination
|
||||
* fromMemory the Tensorflow Object Detection API
|
||||
* (https://github.com/tensorflow/models/tree/master/research/object_detection) models for
|
||||
* detection objects in input images.
|
||||
*
|
||||
* All pre-trained models (https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md) and labels are supported.
|
||||
* All pre-trained models
|
||||
* (https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md)
|
||||
* and labels are supported.
|
||||
*
|
||||
* You can download pre-trained models directly from the zoo: https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md
|
||||
* Just use the URI notation: (zoo model tar.gz url)#(name of the frozen model file name). To speedup the bootstrap
|
||||
* performance you should consider downloading the models locally and use the file:/"path to my model" URI instead!
|
||||
* You can download pre-trained models directly from the zoo:
|
||||
* https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md
|
||||
* Just use the URI notation: (zoo model tar.gz url)#(name of the frozen model file name).
|
||||
* To speedup the bootstrap performance you should consider downloading the models locally
|
||||
* and use the file:/"path to my model" URI instead!
|
||||
*
|
||||
* The object category labels for the pre-trained models are available at: https://github.com/tensorflow/models/tree/master/research/object_detection/data
|
||||
* Use the labels applicable for the model. Also, for performance reasons you may consider to download the labels
|
||||
* and load them from file: instead.
|
||||
* The object category labels for the pre-trained models are available at:
|
||||
* https://github.com/tensorflow/models/tree/master/research/object_detection/data Use the
|
||||
* labels applicable for the model. Also, for performance reasons you may consider to
|
||||
* download the labels and load them from file: instead.
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
public class ObjectDetectionService {
|
||||
|
||||
/** Default list of fetch names for Box models. */
|
||||
public static List<String> FETCH_NAMES = Arrays.asList(
|
||||
ObjectDetectionOutputConverter.DETECTION_SCORES, ObjectDetectionOutputConverter.DETECTION_CLASSES,
|
||||
ObjectDetectionOutputConverter.DETECTION_BOXES, ObjectDetectionOutputConverter.NUM_DETECTIONS);
|
||||
|
||||
/** Default list of fetch names for mask supporting models. */
|
||||
public static List<String> FETCH_NAMES_WITH_MASKS = Arrays.asList(
|
||||
ObjectDetectionOutputConverter.DETECTION_SCORES, ObjectDetectionOutputConverter.DETECTION_CLASSES,
|
||||
ObjectDetectionOutputConverter.DETECTION_BOXES, ObjectDetectionOutputConverter.DETECTION_MASKS,
|
||||
public static List<String> FETCH_NAMES = Arrays.asList(ObjectDetectionOutputConverter.DETECTION_SCORES,
|
||||
ObjectDetectionOutputConverter.DETECTION_CLASSES, ObjectDetectionOutputConverter.DETECTION_BOXES,
|
||||
ObjectDetectionOutputConverter.NUM_DETECTIONS);
|
||||
|
||||
/** Default list of fetch names for mask supporting models. */
|
||||
public static List<String> FETCH_NAMES_WITH_MASKS = Arrays.asList(ObjectDetectionOutputConverter.DETECTION_SCORES,
|
||||
ObjectDetectionOutputConverter.DETECTION_CLASSES, ObjectDetectionOutputConverter.DETECTION_BOXES,
|
||||
ObjectDetectionOutputConverter.DETECTION_MASKS, ObjectDetectionOutputConverter.NUM_DETECTIONS);
|
||||
|
||||
private final ObjectDetectionInputConverter inputConverter;
|
||||
|
||||
private final ObjectDetectionOutputConverter outputConverter;
|
||||
|
||||
private final TensorFlowService tensorFlowService;
|
||||
|
||||
public ObjectDetectionService() {
|
||||
this("https://download.tensorflow.org/models/object_detection/ssdlite_mobilenet_v2_coco_2018_05_09.tar.gz#frozen_inference_graph.pb",
|
||||
"https://storage.googleapis.com/scdf-tensorflow-models/object-detection/mscoco_label_map.pbtxt",
|
||||
0.4f, false, true);
|
||||
"https://storage.googleapis.com/scdf-tensorflow-models/object-detection/mscoco_label_map.pbtxt", 0.4f,
|
||||
false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor that would initialize all necessary internal components.
|
||||
* @param modelUri URI of the pre-trained, frozen Tensorflow model.
|
||||
* @param labelsUri URI of the pre-trained category labels.
|
||||
* @param confidence Confidence threshold. Only objects detected wth confidence above this threshold will be returned.
|
||||
* @param withMasks If a Mask model is selected then you can use this flag to extract the instance segmentation masks as well.
|
||||
* @param confidence Confidence threshold. Only objects detected wth confidence above
|
||||
* this threshold will be returned.
|
||||
* @param withMasks If a Mask model is selected then you can use this flag to extract
|
||||
* the instance segmentation masks as well.
|
||||
*/
|
||||
public ObjectDetectionService(String modelUri, String labelsUri,
|
||||
float confidence, boolean withMasks, boolean cacheModel) {
|
||||
public ObjectDetectionService(String modelUri, String labelsUri, float confidence, boolean withMasks,
|
||||
boolean cacheModel) {
|
||||
this.inputConverter = new ObjectDetectionInputConverter();
|
||||
List<String> fetchNames = withMasks ? FETCH_NAMES_WITH_MASKS : FETCH_NAMES;
|
||||
this.outputConverter = new ObjectDetectionOutputConverter(
|
||||
new DefaultResourceLoader().getResource(labelsUri), confidence, fetchNames);
|
||||
this.tensorFlowService = new TensorFlowService(
|
||||
new DefaultResourceLoader().getResource(modelUri), fetchNames, cacheModel);
|
||||
this.outputConverter = new ObjectDetectionOutputConverter(new DefaultResourceLoader().getResource(labelsUri),
|
||||
confidence, fetchNames);
|
||||
this.tensorFlowService = new TensorFlowService(new DefaultResourceLoader().getResource(modelUri), fetchNames,
|
||||
cacheModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic constructor thea allow the converter to be pre-configured before passed to the service.
|
||||
* @param inputConverter Converter from byte array to object detection input image tensor
|
||||
* @param outputConverter Covets the object detection output tensors into {@link ObjectDetection } list
|
||||
* Generic constructor thea allow the converter to be pre-configured before passed to
|
||||
* the service.
|
||||
* @param inputConverter Converter from byte array to object detection input image
|
||||
* tensor
|
||||
* @param outputConverter Covets the object detection output tensors into
|
||||
* {@link ObjectDetection } list
|
||||
* @param tensorFlowService Java tensorflow runner instance
|
||||
*/
|
||||
public ObjectDetectionService(ObjectDetectionInputConverter inputConverter,
|
||||
@@ -100,9 +113,9 @@ public class ObjectDetectionService {
|
||||
|
||||
/**
|
||||
* Detects objects in a single input image identified by its URI.
|
||||
*
|
||||
* @param imageUri input image's URI
|
||||
* @return Returns a list of {@link ObjectDetection} domain objects representing detected objects
|
||||
* @return Returns a list of {@link ObjectDetection} domain objects representing
|
||||
* detected objects
|
||||
*/
|
||||
public List<ObjectDetection> detect(String imageUri) {
|
||||
try (InputStream is = new DefaultResourceLoader().getResource(imageUri).getInputStream()) {
|
||||
@@ -116,10 +129,11 @@ public class ObjectDetectionService {
|
||||
|
||||
/**
|
||||
* Detects objects in a single {@link BufferedImage}.
|
||||
*
|
||||
* @param image Input image to detect objects from.
|
||||
* @param format Image format (e.g. jpg, png ...) to use when converting the buffer into byte array.
|
||||
* @return Returns a list of {@link ObjectDetection} domain objects representing detected objects in the input image
|
||||
* @param format Image format (e.g. jpg, png ...) to use when converting the buffer
|
||||
* into byte array.
|
||||
* @return Returns a list of {@link ObjectDetection} domain objects representing
|
||||
* detected objects in the input image
|
||||
*/
|
||||
public List<ObjectDetection> detect(BufferedImage image, String format) {
|
||||
return this.detect(GraphicsUtils.toImageByteArray(image, format));
|
||||
@@ -127,21 +141,27 @@ public class ObjectDetectionService {
|
||||
|
||||
/**
|
||||
* Detects objects from a single input image encoded as byte array.
|
||||
*
|
||||
* @param image Input image encoded as byte array
|
||||
* @return Returns a list of {@link ObjectDetection} domain objects representing detected objects in the input image
|
||||
* @return Returns a list of {@link ObjectDetection} domain objects representing
|
||||
* detected objects in the input image
|
||||
*/
|
||||
public List<ObjectDetection> detect(byte[] image) {
|
||||
return this.inputConverter.andThen(this.tensorFlowService).andThen(this.outputConverter).apply(new byte[][] { image }).get(0);
|
||||
return this.inputConverter.andThen(this.tensorFlowService)
|
||||
.andThen(this.outputConverter)
|
||||
.apply(new byte[][] { image })
|
||||
.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses detects objects from a batch of input images encoded as byte array.
|
||||
*
|
||||
* @param batchedImages Batch of input images encoded as byte arrays. First dimension is the batch size and second the image bytes.
|
||||
* @return Returns list of lists. For every input image in the batch a list of {@link ObjectDetection} domain objects representing detected objects in the input image.
|
||||
* @param batchedImages Batch of input images encoded as byte arrays. First dimension
|
||||
* is the batch size and second the image bytes.
|
||||
* @return Returns list of lists. For every input image in the batch a list of
|
||||
* {@link ObjectDetection} domain objects representing detected objects in the input
|
||||
* image.
|
||||
*/
|
||||
public List<List<ObjectDetection>> detect(byte[][] batchedImages) {
|
||||
return this.inputConverter.andThen(this.tensorFlowService).andThen(this.outputConverter).apply(batchedImages);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,35 +40,34 @@ import org.springframework.core.io.DefaultResourceLoader;
|
||||
public class ObjectDetectionService2 implements AutoCloseable {
|
||||
|
||||
/** Default Box models fetch names. */
|
||||
public static List<String> FETCH_NAMES = Arrays.asList(
|
||||
ObjectDetectionOutputConverter.DETECTION_SCORES, ObjectDetectionOutputConverter.DETECTION_CLASSES,
|
||||
ObjectDetectionOutputConverter.DETECTION_BOXES, ObjectDetectionOutputConverter.NUM_DETECTIONS);
|
||||
|
||||
/** Default Models models fetch names. */
|
||||
public static List<String> FETCH_NAMES_WITH_MASKS = Arrays.asList(
|
||||
ObjectDetectionOutputConverter.DETECTION_SCORES, ObjectDetectionOutputConverter.DETECTION_CLASSES,
|
||||
ObjectDetectionOutputConverter.DETECTION_BOXES, ObjectDetectionOutputConverter.DETECTION_MASKS,
|
||||
public static List<String> FETCH_NAMES = Arrays.asList(ObjectDetectionOutputConverter.DETECTION_SCORES,
|
||||
ObjectDetectionOutputConverter.DETECTION_CLASSES, ObjectDetectionOutputConverter.DETECTION_BOXES,
|
||||
ObjectDetectionOutputConverter.NUM_DETECTIONS);
|
||||
|
||||
private final GraphRunner imageNormalization;
|
||||
private final GraphRunner objectDetection;
|
||||
private final ObjectDetectionOutputConverter outputConverter;
|
||||
/** Default Models models fetch names. */
|
||||
public static List<String> FETCH_NAMES_WITH_MASKS = Arrays.asList(ObjectDetectionOutputConverter.DETECTION_SCORES,
|
||||
ObjectDetectionOutputConverter.DETECTION_CLASSES, ObjectDetectionOutputConverter.DETECTION_BOXES,
|
||||
ObjectDetectionOutputConverter.DETECTION_MASKS, ObjectDetectionOutputConverter.NUM_DETECTIONS);
|
||||
|
||||
private final GraphRunner imageNormalization;
|
||||
|
||||
private final GraphRunner objectDetection;
|
||||
|
||||
private final ObjectDetectionOutputConverter outputConverter;
|
||||
|
||||
public ObjectDetectionService2(String modelUri, ObjectDetectionOutputConverter outputConverter) {
|
||||
|
||||
this.imageNormalization = new GraphRunner("raw_image", "normalized_image")
|
||||
.withGraphDefinition(tf -> {
|
||||
Placeholder<String> rawImage = tf.withName("raw_image").placeholder(String.class);
|
||||
Operand<UInt8> decodedImage = tf.dtypes.cast(
|
||||
tf.image.decodeJpeg(rawImage, DecodeJpeg.channels(3L)), UInt8.class);
|
||||
// Expand dimensions since the model expects images to have shape: [1, H, W, 3]
|
||||
tf.withName("normalized_image").expandDims(decodedImage, tf.constant(0));
|
||||
});
|
||||
this.imageNormalization = new GraphRunner("raw_image", "normalized_image").withGraphDefinition(tf -> {
|
||||
Placeholder<String> rawImage = tf.withName("raw_image").placeholder(String.class);
|
||||
Operand<UInt8> decodedImage = tf.dtypes.cast(tf.image.decodeJpeg(rawImage, DecodeJpeg.channels(3L)),
|
||||
UInt8.class);
|
||||
// Expand dimensions since the model expects images to have shape: [1, H, W,
|
||||
// 3]
|
||||
tf.withName("normalized_image").expandDims(decodedImage, tf.constant(0));
|
||||
});
|
||||
|
||||
this.objectDetection = new GraphRunner(Arrays.asList("image_tensor"), FETCH_NAMES)
|
||||
.withGraphDefinition(new ProtoBufGraphDefinition(
|
||||
new DefaultResourceLoader().getResource(modelUri), true));
|
||||
.withGraphDefinition(new ProtoBufGraphDefinition(new DefaultResourceLoader().getResource(modelUri), true));
|
||||
|
||||
this.outputConverter = outputConverter;
|
||||
}
|
||||
@@ -77,9 +76,10 @@ public class ObjectDetectionService2 implements AutoCloseable {
|
||||
try (Tensor inputTensor = Tensor.create(image); GraphRunnerMemory memorize = new GraphRunnerMemory()) {
|
||||
|
||||
List<List<ObjectDetection>> out = this.imageNormalization.andThen(memorize)
|
||||
.andThen(this.objectDetection).andThen(memorize)
|
||||
.andThen(outputConverter)
|
||||
.apply(Collections.singletonMap("raw_image", inputTensor));
|
||||
.andThen(this.objectDetection)
|
||||
.andThen(memorize)
|
||||
.andThen(outputConverter)
|
||||
.apply(Collections.singletonMap("raw_image", inputTensor));
|
||||
|
||||
return out.get(0);
|
||||
|
||||
@@ -90,7 +90,7 @@ public class ObjectDetectionService2 implements AutoCloseable {
|
||||
public void close() {
|
||||
this.imageNormalization.close();
|
||||
this.objectDetection.close();
|
||||
//this.outputConverter.close();
|
||||
// this.outputConverter.close();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
@@ -100,7 +100,8 @@ public class ObjectDetectionService2 implements AutoCloseable {
|
||||
ObjectDetectionOutputConverter outputAdapter = new ObjectDetectionOutputConverter(
|
||||
new DefaultResourceLoader().getResource(labelUri), 0.4f, FETCH_NAMES);
|
||||
|
||||
//byte[] inputImage = GraphicsUtils.loadAsByteArray("classpath:/images/object-detection.jpg");
|
||||
// byte[] inputImage =
|
||||
// GraphicsUtils.loadAsByteArray("classpath:/images/object-detection.jpg");
|
||||
byte[] inputImage = GraphicsUtils.loadAsByteArray("classpath:/images/wild-animals-15.jpg");
|
||||
|
||||
try (ObjectDetectionService2 objectDetectionService2 = new ObjectDetectionService2(modelUri, outputAdapter)) {
|
||||
@@ -110,4 +111,5 @@ public class ObjectDetectionService2 implements AutoCloseable {
|
||||
System.out.println(boza);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,12 +28,19 @@ import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
public class ObjectDetection {
|
||||
|
||||
private String name;
|
||||
|
||||
private float confidence;
|
||||
|
||||
private float x1;
|
||||
|
||||
private float y1;
|
||||
|
||||
private float x2;
|
||||
|
||||
private float y2;
|
||||
|
||||
private float[][] mask;
|
||||
|
||||
private int cid;
|
||||
|
||||
public String getName() {
|
||||
@@ -102,15 +109,8 @@ public class ObjectDetection {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ObjectDetection{" +
|
||||
"name='" + name + '\'' +
|
||||
", confidence=" + confidence +
|
||||
", x1=" + x1 +
|
||||
", y1=" + y1 +
|
||||
", x2=" + x2 +
|
||||
", y2=" + y2 +
|
||||
", mask=" + Arrays.toString(mask) +
|
||||
", cid=" + cid +
|
||||
'}';
|
||||
return "ObjectDetection{" + "name='" + name + '\'' + ", confidence=" + confidence + ", x1=" + x1 + ", y1=" + y1
|
||||
+ ", x2=" + x2 + ", y2=" + y2 + ", mask=" + Arrays.toString(mask) + ", cid=" + cid + '}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,15 +32,15 @@ import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
/**
|
||||
* 4 of the pre-trained model in the model zoo (https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md)
|
||||
* 4 of the pre-trained model in the model zoo
|
||||
* (https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md)
|
||||
* can also compute the masks of the detected objects, providing instance segmentation.
|
||||
* <p>
|
||||
* Here are the models that can be used for instance segmentation.
|
||||
* <p>
|
||||
* mask_rcnn_inception_resnet_v2_atrous_coco 771 36 Masks
|
||||
* mask_rcnn_inception_v2_coco 79 25 Masks
|
||||
* mask_rcnn_resnet101_atrous_coco 470 33 Masks
|
||||
* mask_rcnn_resnet50_atrous_coco 343 29 Masks
|
||||
* mask_rcnn_inception_resnet_v2_atrous_coco 771 36 Masks mask_rcnn_inception_v2_coco 79
|
||||
* 25 Masks mask_rcnn_resnet101_atrous_coco 470 33 Masks mask_rcnn_resnet50_atrous_coco
|
||||
* 343 29 Masks
|
||||
*
|
||||
* @author Christian Tzolov
|
||||
*/
|
||||
@@ -50,44 +50,53 @@ public class ExampleInstanceSegmentation {
|
||||
|
||||
ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
|
||||
// You can download pre-trained models directly from the zoo: https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md
|
||||
// Just use the notation <zoo model tar.gz url>#<name of the frozen model file name>
|
||||
// For performance reasons you may consider downloading the model locally and use the file:/<path to my model> URI instead!
|
||||
// You can download pre-trained models directly from the zoo:
|
||||
// https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md
|
||||
// Just use the notation <zoo model tar.gz url>#<name of the frozen model file
|
||||
// name>
|
||||
// For performance reasons you may consider downloading the model locally and use
|
||||
// the file:/<path to my model> URI instead!
|
||||
String model = "https://download.tensorflow.org/models/object_detection/mask_rcnn_inception_resnet_v2_atrous_coco_2018_01_28.tar.gz#frozen_inference_graph.pb";
|
||||
|
||||
// All labels for the pre-trained models are available at:
|
||||
// https://github.com/tensorflow/models/tree/master/research/object_detection/data
|
||||
// Use the labels applicable for the model.
|
||||
// Also, for performance reasons you may consider to download the labels and load them from file: instead.
|
||||
// Also, for performance reasons you may consider to download the labels and load
|
||||
// them from file: instead.
|
||||
String labels = "https://raw.githubusercontent.com/tensorflow/models/master/research/object_detection/data/mscoco_label_map.pbtxt";
|
||||
|
||||
// You can cache the TF model on the local file system to improve the bootstrap performance on consecutive runs!
|
||||
// You can cache the TF model on the local file system to improve the bootstrap
|
||||
// performance on consecutive runs!
|
||||
boolean CACHE_TF_MODEL = true;
|
||||
|
||||
// For the pre-trained models fromMemory mask you can set the INSTANCE_SEGMENTATION to enable object instance segmentation as well
|
||||
// For the pre-trained models fromMemory mask you can set the
|
||||
// INSTANCE_SEGMENTATION to enable object instance segmentation as well
|
||||
boolean INSTANCE_SEGMENTATION = true;
|
||||
|
||||
// Only object fromMemory confidence above the threshold are returned
|
||||
float CONFIDENCE_THRESHOLD = 0.4f;
|
||||
|
||||
ObjectDetectionService detectionService =
|
||||
new ObjectDetectionService(model, labels, CONFIDENCE_THRESHOLD, INSTANCE_SEGMENTATION, CACHE_TF_MODEL);
|
||||
ObjectDetectionService detectionService = new ObjectDetectionService(model, labels, CONFIDENCE_THRESHOLD,
|
||||
INSTANCE_SEGMENTATION, CACHE_TF_MODEL);
|
||||
|
||||
// You can use file:, http: or classpath: to provide the path to the input image.
|
||||
byte[] image = GraphicsUtils.loadAsByteArray("classpath:/images/object-detection.jpg");
|
||||
|
||||
// Returns a list ObjectDetection domain classes to allow programmatic accesses to the detected objects's metadata
|
||||
// Returns a list ObjectDetection domain classes to allow programmatic accesses to
|
||||
// the detected objects's metadata
|
||||
List<ObjectDetection> detectedObjects = detectionService.detect(image);
|
||||
|
||||
// Get JSON representation of the detected objects
|
||||
String jsonObjectDetections = new JsonMapperFunction().apply(detectedObjects);
|
||||
System.out.println(jsonObjectDetections);
|
||||
|
||||
// Draw the detected object metadata on top of the original image and store the result
|
||||
// Draw the detected object metadata on top of the original image and store the
|
||||
// result
|
||||
byte[] annotatedImage = new ObjectDetectionImageAugmenter(INSTANCE_SEGMENTATION).apply(image, detectedObjects);
|
||||
File projectDir = new File("functions/function/object-detection-function");
|
||||
File output = new File(projectDir, "target/object-detection-segmentation-augmented.jpg");
|
||||
IOUtils.write(annotatedImage, new FileOutputStream(output));
|
||||
System.out.println("Created:" + output.getPath());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,31 +37,40 @@ public class ExampleObjectDetection {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
||||
// You can download pre-trained models directly from the zoo: https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md
|
||||
// Just use the notation <zoo model tar.gz url>#<name of the frozen model file name>
|
||||
// For performance reasons you may consider downloading the model locally and use the file:/<path to my model> URI instead!
|
||||
// You can download pre-trained models directly from the zoo:
|
||||
// https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md
|
||||
// Just use the notation <zoo model tar.gz url>#<name of the frozen model file
|
||||
// name>
|
||||
// For performance reasons you may consider downloading the model locally and use
|
||||
// the file:/<path to my model> URI instead!
|
||||
String model = "https://download.tensorflow.org/models/object_detection/faster_rcnn_nas_coco_2018_01_28.tar.gz#frozen_inference_graph.pb";
|
||||
//Resource model = resourceLoader.getResource("https://download.tensorflow.org/models/object_detection/faster_rcnn_resnet101_fgvc_2018_07_19.tar.gz#frozen_inference_graph.pb");
|
||||
//Resource model = resourceLoader.getResource("https://download.tensorflow.org/models/object_detection/faster_rcnn_resnet50_fgvc_2018_07_19.tar.gz#frozen_inference_graph.pb");
|
||||
// Resource model =
|
||||
// resourceLoader.getResource("https://download.tensorflow.org/models/object_detection/faster_rcnn_resnet101_fgvc_2018_07_19.tar.gz#frozen_inference_graph.pb");
|
||||
// Resource model =
|
||||
// resourceLoader.getResource("https://download.tensorflow.org/models/object_detection/faster_rcnn_resnet50_fgvc_2018_07_19.tar.gz#frozen_inference_graph.pb");
|
||||
|
||||
// All labels for the pre-trained models are available at:
|
||||
// https://github.com/tensorflow/models/tree/master/research/object_detection/data
|
||||
// Use the labels applicable for the model.
|
||||
// Also, for performance reasons you may consider to download the labels and load them from file: instead.
|
||||
// Also, for performance reasons you may consider to download the labels and load
|
||||
// them from file: instead.
|
||||
String labels = "https://raw.githubusercontent.com/tensorflow/models/master/research/object_detection/data/mscoco_label_map.pbtxt";
|
||||
//Resource labels = resourceLoader.getResource("https://raw.githubusercontent.com/tensorflow/models/master/research/object_detection/data/fgvc_2854_classes_label_map.pbtxt");
|
||||
// Resource labels =
|
||||
// resourceLoader.getResource("https://raw.githubusercontent.com/tensorflow/models/master/research/object_detection/data/fgvc_2854_classes_label_map.pbtxt");
|
||||
|
||||
// You can cache the TF model on the local file system to improve the bootstrap performance on consecutive runs!
|
||||
// You can cache the TF model on the local file system to improve the bootstrap
|
||||
// performance on consecutive runs!
|
||||
boolean CACHE_TF_MODEL = true;
|
||||
|
||||
// For the pre-trained models fromMemory mask you can set the INSTANCE_SEGMENTATION to enable object instance segmentation as well
|
||||
// For the pre-trained models fromMemory mask you can set the
|
||||
// INSTANCE_SEGMENTATION to enable object instance segmentation as well
|
||||
boolean NO_INSTANCE_SEGMENTATION = false;
|
||||
|
||||
// Only object fromMemory confidence above the threshold are returned
|
||||
float CONFIDENCE_THRESHOLD = 0.4f;
|
||||
|
||||
ObjectDetectionService detectionService =
|
||||
new ObjectDetectionService(model, labels, CONFIDENCE_THRESHOLD, NO_INSTANCE_SEGMENTATION, CACHE_TF_MODEL);
|
||||
ObjectDetectionService detectionService = new ObjectDetectionService(model, labels, CONFIDENCE_THRESHOLD,
|
||||
NO_INSTANCE_SEGMENTATION, CACHE_TF_MODEL);
|
||||
|
||||
// You can use file:, http: or classpath: to provide the path to the input image.
|
||||
String inputImageUri = "classpath:/images/object-detection.jpg";
|
||||
@@ -69,16 +78,21 @@ public class ExampleObjectDetection {
|
||||
|
||||
byte[] image = StreamUtils.copyToByteArray(is);
|
||||
|
||||
// Returns a list ObjectDetection domain classes to allow programmatic accesses to the detected objects's metadata
|
||||
// Returns a list ObjectDetection domain classes to allow programmatic
|
||||
// accesses to the detected objects's metadata
|
||||
List<ObjectDetection> detectedObjects = detectionService.detect(image);
|
||||
|
||||
// Get JSON representation of the detected objects
|
||||
String jsonObjectDetections = new JsonMapperFunction().apply(detectedObjects);
|
||||
System.out.println(jsonObjectDetections);
|
||||
|
||||
// Draw the detected object metadata on top of the original image and store the result
|
||||
byte[] annotatedImage = new ObjectDetectionImageAugmenter(NO_INSTANCE_SEGMENTATION).apply(image, detectedObjects);
|
||||
IOUtils.write(annotatedImage, new FileOutputStream("./object-detection-function/target/object-detection-augmented.jpg"));
|
||||
// Draw the detected object metadata on top of the original image and store
|
||||
// the result
|
||||
byte[] annotatedImage = new ObjectDetectionImageAugmenter(NO_INSTANCE_SEGMENTATION).apply(image,
|
||||
detectedObjects);
|
||||
IOUtils.write(annotatedImage,
|
||||
new FileOutputStream("./object-detection-function/target/object-detection-augmented.jpg"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.fn.object.detection.examples;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cloud.fn.object.detection.ObjectDetectionService;
|
||||
@@ -28,15 +27,30 @@ import org.springframework.cloud.fn.object.detection.domain.ObjectDetection;
|
||||
public class SimpleExample {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Select a pre-trained model from the model zoo: https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md
|
||||
// Just use the notation <model zoo url>#<name of the frozen model file in the zoo's tar.gz>
|
||||
// Select a pre-trained model from the model zoo:
|
||||
// https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md
|
||||
// Just use the notation <model zoo url>#<name of the frozen model file in the
|
||||
// zoo's tar.gz>
|
||||
String model = "https://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_ppn_shared_box_predictor_300x300_coco14_sync_2018_07_03.tar.gz#frozen_inference_graph.pb";
|
||||
|
||||
// All labels for the pre-trained models are available at: https://github.com/tensorflow/models/tree/master/research/object_detection/data
|
||||
// All labels for the pre-trained models are available at:
|
||||
// https://github.com/tensorflow/models/tree/master/research/object_detection/data
|
||||
String labels = "https://raw.githubusercontent.com/tensorflow/models/master/research/object_detection/data/mscoco_label_map.pbtxt";
|
||||
|
||||
ObjectDetectionService detectionService = new ObjectDetectionService(model, labels,
|
||||
0.4f, // Only object fromMemory confidence above the threshold are returned. Confidence range is [0, 1].
|
||||
ObjectDetectionService detectionService = new ObjectDetectionService(model, labels, 0.4f, // Only
|
||||
// object
|
||||
// fromMemory
|
||||
// confidence
|
||||
// above
|
||||
// the
|
||||
// threshold
|
||||
// are
|
||||
// returned.
|
||||
// Confidence
|
||||
// range
|
||||
// is
|
||||
// [0,
|
||||
// 1].
|
||||
false, // No instance segmentation
|
||||
true); // cache the TF model locally
|
||||
|
||||
@@ -44,4 +58,5 @@ public class SimpleExample {
|
||||
List<ObjectDetection> detectedObjects = detectionService.detect("classpath:/images/object-detection.jpg");
|
||||
detectedObjects.stream().map(o -> o.toString()).forEach(System.out::println);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user