-
Notifications
You must be signed in to change notification settings - Fork 97
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Release 0.14.35.1
- Loading branch information
Stuart Dent
committed
Dec 7, 2020
1 parent
03880fd
commit eee896b
Showing
61 changed files
with
1,963 additions
and
554 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
Sources/Integrations/Onnx/Common/ModelRunners/ImageNet/ImageNetModelOutputParser.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT license. | ||
|
||
namespace Microsoft.Psi.Onnx | ||
{ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using Microsoft.Psi; | ||
|
||
/// <summary> | ||
/// Internal class that parses the outputs from the ImageNet model into | ||
/// a set of image classification results. | ||
/// </summary> | ||
internal class ImageNetModelOutputParser | ||
{ | ||
private readonly string[] labels; | ||
private readonly int maxPredictions; | ||
private readonly bool applySoftmax; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="ImageNetModelOutputParser"/> class. | ||
/// </summary> | ||
/// <param name="imageClassesFile">The path to the file containing the list of 1000 ImageNet classes.</param> | ||
/// <param name="maxPredictions">The maximum number of predictions to return.</param> | ||
/// <param name="applySoftmax">Whether the softmax function should be applied to the raw model output.</param> | ||
/// <remarks> | ||
/// The file referenced by <paramref name="imageClassesFile"/> may be downloaded from the following location: | ||
/// https://github.com/onnx/models/raw/8d50e3f598e6d5c67c7c7253e5a203a26e731a1b/vision/classification/synset.txt. | ||
/// </remarks> | ||
public ImageNetModelOutputParser(string imageClassesFile, int maxPredictions, bool applySoftmax) | ||
{ | ||
this.labels = File.ReadAllLines(imageClassesFile); | ||
if (this.labels.Length != 1000) | ||
{ | ||
throw new ArgumentException($"The file {imageClassesFile} does not appear to be in the correct format. This file should contain exactly 1000 lines representing an ordered list of the 1000 ImageNet classes."); | ||
} | ||
|
||
this.maxPredictions = maxPredictions; | ||
this.applySoftmax = applySoftmax; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the predictions from the model output. | ||
/// </summary> | ||
/// <param name="modelOutput">The model output vector of class probabilities.</param> | ||
/// <returns>A list of the top-N predictions, in descending probability order.</returns> | ||
public List<LabeledPrediction> GetPredictions(float[] modelOutput) | ||
{ | ||
return GetTopResults(this.applySoftmax ? Softmax(modelOutput) : modelOutput, this.maxPredictions) | ||
.Select(c => new LabeledPrediction { Label = this.labels[c.Index], Confidence = c.Value }) | ||
.ToList(); | ||
} | ||
|
||
private static IEnumerable<(int Index, float Value)> GetTopResults(IEnumerable<float> predictedClasses, int count) | ||
{ | ||
return predictedClasses | ||
.Select((predictedClass, index) => (Index: index, Value: predictedClass)) | ||
.OrderByDescending(result => result.Value) | ||
.Take(count); | ||
} | ||
|
||
private static IEnumerable<float> Softmax(IEnumerable<float> values) | ||
{ | ||
var maxVal = values.Max(); | ||
var exp = values.Select(v => Math.Exp(v - maxVal)); | ||
var sumExp = exp.Sum(); | ||
|
||
return exp.Select(v => (float)(v / sumExp)); | ||
} | ||
} | ||
} |
141 changes: 141 additions & 0 deletions
141
Sources/Integrations/Onnx/Common/ModelRunners/ImageNet/ImageNetModelRunner.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT license. | ||
|
||
namespace Microsoft.Psi.Onnx | ||
{ | ||
using System; | ||
using System.Collections.Generic; | ||
using Microsoft.Psi; | ||
using Microsoft.Psi.Components; | ||
using Microsoft.Psi.Imaging; | ||
|
||
/// <summary> | ||
/// Component that runs an ImageNet image classification model. | ||
/// </summary> | ||
/// <remarks> | ||
/// This class implements a \psi component that runs an ONNX model trained | ||
/// on the ImageNet dataset that operates on 224x224 RGB images and scores | ||
/// the image for each of the 1000 ImageNet classes. It takes an input | ||
/// stream of \psi images, applies a center-crop, rescales and normalizes | ||
/// the pixel values into the input vector expected by the model. It also | ||
/// parses the model outputs into a list of <see cref="LabeledPrediction"/> | ||
/// values, corresponding to the top N predictions by the model. For | ||
/// convenience, a set of pre-defined model runner configurations are | ||
/// defined for a number of image classification models available in the | ||
/// ONNX Model Zoo (https://github.com/onnx/models/tree/master/vision/classification). | ||
/// The ONNX model file for the corresponding configuration will need to be | ||
/// downloaded locally and the path to the model file will need to be | ||
/// specified when creating the configuration. | ||
/// </remarks> | ||
public class ImageNetModelRunner : ConsumerProducer<Shared<Image>, List<LabeledPrediction>> | ||
{ | ||
private readonly float[] onnxInputVector = new float[3 * 224 * 224]; | ||
private readonly OnnxModel onnxModel; | ||
private readonly ImageNetModelOutputParser outputParser; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="ImageNetModelRunner"/> class. | ||
/// </summary> | ||
/// <param name="pipeline">The pipeline to add the component to.</param> | ||
/// <param name="configuration">The configuration for the compoinent.</param> | ||
/// <remarks> | ||
/// To run on a GPU, use the Microsoft.Psi.Onnx.ModelRunners.Gpu library instead of Microsoft.Psi.Onnx.ModelRunners.Cpu, and set | ||
/// the value of the <pararef name="gpuDeviceId"/> parameter to a valid non-negative integer. Typical device ID values are 0 or 1. | ||
/// </remarks> | ||
public ImageNetModelRunner(Pipeline pipeline, ImageNetModelRunnerConfiguration configuration) | ||
: base(pipeline) | ||
{ | ||
// create an ONNX model based on the supplied ImageNet model runner configuration | ||
this.onnxModel = new OnnxModel(new OnnxModelConfiguration() | ||
{ | ||
ModelFileName = configuration.ModelFilePath, | ||
InputVectorName = configuration.InputVectorName, | ||
InputVectorSize = 3 * 224 * 224, | ||
OutputVectorName = configuration.OutputVectorName, | ||
GpuDeviceId = configuration.GpuDeviceId, | ||
}); | ||
|
||
this.outputParser = new ImageNetModelOutputParser(configuration.ImageClassesFilePath, configuration.NumberOfPredictions, configuration.ApplySoftmaxToModelOutput); | ||
} | ||
|
||
/// <inheritdoc/> | ||
protected override void Receive(Shared<Image> data, Envelope envelope) | ||
{ | ||
// construct the ONNX model input vector (stored in this.onnxInputVector) | ||
// based on the incoming image | ||
this.ConstructOnnxInputVector(data); | ||
|
||
// run the model over the input vector | ||
var outputVector = this.onnxModel.GetPrediction(this.onnxInputVector); | ||
|
||
// parse the model output into an ordered list of the top-N predictions | ||
var results = this.outputParser.GetPredictions(outputVector); | ||
|
||
// post the results | ||
this.Out.Post(results, envelope.OriginatingTime); | ||
} | ||
|
||
/// <summary> | ||
/// Constructs the input vector for the ImageNet model for a specified image. | ||
/// </summary> | ||
/// <param name="sharedImage">The image to construct the input vector for.</param> | ||
private void ConstructOnnxInputVector(Shared<Image> sharedImage) | ||
{ | ||
var inputImage = sharedImage.Resource; | ||
var inputWidth = sharedImage.Resource.Width; | ||
var inputHeight = sharedImage.Resource.Height; | ||
|
||
// crop a center square | ||
var squareSize = Math.Min(inputWidth, inputHeight); | ||
using var squareImage = ImagePool.GetOrCreate(squareSize, squareSize, sharedImage.Resource.PixelFormat); | ||
if (inputWidth > inputHeight) | ||
{ | ||
inputImage.Crop(squareImage.Resource, (inputWidth - squareSize) / 2, 0, squareSize, squareSize); | ||
} | ||
else | ||
{ | ||
inputImage.Crop(squareImage.Resource, 0, (inputHeight - squareSize) / 2, squareSize, squareSize); | ||
} | ||
|
||
// resize the image to 224 x 224 | ||
using var resizedImage = ImagePool.GetOrCreate(224, 224, sharedImage.Resource.PixelFormat); | ||
squareImage.Resource.Resize(resizedImage.Resource, 224, 224, SamplingMode.Bilinear); | ||
|
||
// if the pixel format does not match, do a conversion before extracting the bytes | ||
var bytes = default(byte[]); | ||
if (sharedImage.Resource.PixelFormat != PixelFormat.BGR_24bpp) | ||
{ | ||
using var reformattedImage = ImagePool.GetOrCreate(224, 224, PixelFormat.BGR_24bpp); | ||
resizedImage.Resource.CopyTo(reformattedImage.Resource); | ||
bytes = reformattedImage.Resource.ReadBytes(3 * 224 * 224); | ||
} | ||
else | ||
{ | ||
// get the bytes | ||
bytes = resizedImage.Resource.ReadBytes(3 * 224 * 224); | ||
} | ||
|
||
// Now populate the onnxInputVector float array / tensor by normalizing | ||
// using mean = [0.485, 0.456, 0.406] and std = [0.229, 0.224, 0.225]. | ||
int fi = 0; | ||
|
||
// first the red bytes | ||
for (int i = 2; i < bytes.Length; i += 3) | ||
{ | ||
this.onnxInputVector[fi++] = ((bytes[i] / 255.0f) - 0.485f) / 0.229f; | ||
} | ||
|
||
// then the green bytes | ||
for (int i = 1; i < bytes.Length; i += 3) | ||
{ | ||
this.onnxInputVector[fi++] = ((bytes[i] / 255.0f) - 0.456f) / 0.224f; | ||
} | ||
|
||
// then the blue bytes | ||
for (int i = 0; i < bytes.Length; i += 3) | ||
{ | ||
this.onnxInputVector[fi++] = ((bytes[i] / 255.0f) - 0.406f) / 0.225f; | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.