Skip to content

Commit eee896b

Browse files
author
Stuart Dent
committed
Merged PR 48123: Release 0.14.35.3
Release 0.14.35.1
1 parent 03880fd commit eee896b

File tree

61 files changed

+1963
-554
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1963
-554
lines changed

Directory.Build.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<Company>Microsoft Corporation</Company>
77
<Owners>microsoft,psi</Owners>
88
<Authors>Microsoft</Authors>
9-
<AssemblyVersion>0.13.38.2</AssemblyVersion>
9+
<AssemblyVersion>0.14.35.3</AssemblyVersion>
1010
<FileVersion>$(AssemblyVersion)</FileVersion>
1111
<Version>$(AssemblyVersion)-beta</Version>
1212
<SignAssembly>false</SignAssembly>

Sources/Audio/Microsoft.Psi.Audio/WaveFileStreamReader.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace Microsoft.Psi.Audio
66
using System;
77
using System.Collections.Generic;
88
using System.IO;
9+
using System.Runtime.Serialization;
910
using System.Threading;
1011
using Microsoft.Psi;
1112
using Microsoft.Psi.Data;
@@ -160,7 +161,7 @@ public IStreamReader OpenNew()
160161
}
161162

162163
/// <inheritdoc />
163-
public IStreamMetadata OpenStream<T>(string name, Action<T, Envelope> target, Func<T> allocator = null)
164+
public IStreamMetadata OpenStream<T>(string name, Action<T, Envelope> target, Func<T> allocator = null, Action<SerializationException> errorHandler = null)
164165
{
165166
ValidateStreamName(name);
166167

Sources/Calibration/Microsoft.Psi.Calibration/ProjectTo3D.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public sealed class ProjectTo3D : ConsumerProducer<(Shared<DepthImage>, List<Poi
2020
/// <summary>
2121
/// Initializes a new instance of the <see cref="ProjectTo3D"/> class.
2222
/// </summary>
23-
/// <param name="pipeline">Pipeline this component is a part of.</param>
23+
/// <param name="pipeline">The pipeline to add the component to.</param>
2424
public ProjectTo3D(Pipeline pipeline)
2525
: base(pipeline)
2626
{

Sources/Data/Microsoft.Psi.Data/Json/JsonGenerator.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class JsonGenerator : Generator, IDisposable
2222
/// <summary>
2323
/// Initializes a new instance of the <see cref="JsonGenerator"/> class.
2424
/// </summary>
25-
/// <param name="pipeline">Pipeline this component is a part of.</param>
25+
/// <param name="pipeline">The pipeline to add the component to.</param>
2626
/// <param name="name">The name of the application that generated the persisted files, or the root name of the files.</param>
2727
/// <param name="path">The directory in which the main persisted file resides.</param>
2828
public JsonGenerator(Pipeline pipeline, string name, string path)
@@ -33,7 +33,7 @@ public JsonGenerator(Pipeline pipeline, string name, string path)
3333
/// <summary>
3434
/// Initializes a new instance of the <see cref="JsonGenerator"/> class.
3535
/// </summary>
36-
/// <param name="pipeline">Pipeline this component is a part of.</param>
36+
/// <param name="pipeline">The pipeline to add the component to.</param>
3737
/// <param name="reader">The underlying store reader.</param>
3838
protected JsonGenerator(Pipeline pipeline, JsonStoreReader reader)
3939
: base(pipeline)

Sources/Data/Microsoft.Psi.Data/Json/JsonStreamReader.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace Microsoft.Psi.Data.Json
55
{
66
using System;
77
using System.Collections.Generic;
8+
using System.Runtime.Serialization;
89
using System.Threading;
910
using Microsoft.Psi;
1011
using Newtonsoft.Json.Linq;
@@ -125,7 +126,7 @@ public virtual IStreamReader OpenNew()
125126
}
126127

127128
/// <inheritdoc />
128-
public IStreamMetadata OpenStream<T>(string streamName, Action<T, Envelope> target, Func<T> allocator = null)
129+
public IStreamMetadata OpenStream<T>(string streamName, Action<T, Envelope> target, Func<T> allocator = null, Action<SerializationException> errorHandler = null)
129130
{
130131
if (string.IsNullOrWhiteSpace(streamName))
131132
{

Sources/Imaging/Microsoft.Psi.Imaging/ImageTransformer.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class ImageTransformer : ConsumerProducer<Shared<Image>, Shared<Image>>
2525
/// <summary>
2626
/// Initializes a new instance of the <see cref="ImageTransformer"/> class.
2727
/// </summary>
28-
/// <param name="pipeline">Pipeline this component is a part of.</param>
28+
/// <param name="pipeline">The pipeline to add the component to.</param>
2929
/// <param name="transformer">Function for transforming the source image.</param>
3030
/// <param name="pixelFormat">Pixel format for destination image.</param>
3131
/// <param name="sharedImageAllocator ">Optional image allocator for creating new shared image.</param>

Sources/Imaging/Test.Psi.Imaging.Windows/Properties/AssemblyInfo.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@
1010
[assembly: AssemblyCopyright("Copyright (c) Microsoft Corporation. All rights reserved.")]
1111
[assembly: ComVisible(false)]
1212
[assembly: Guid("191df615-3d8f-45a3-b763-dd4a604a712a")]
13-
[assembly: AssemblyVersion("0.13.38.2")]
14-
[assembly: AssemblyFileVersion("0.13.38.2")]
15-
[assembly: AssemblyInformationalVersion("0.13.38.2-beta")]
13+
[assembly: AssemblyVersion("0.14.35.3")]
14+
[assembly: AssemblyFileVersion("0.14.35.3")]
15+
[assembly: AssemblyInformationalVersion("0.14.35.3-beta")]

Sources/Integrations/CognitiveServices/Microsoft.Psi.CognitiveServices.Vision/ImageAnalyzer.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ private async Task ReceiveAsync(Shared<Image> data, Envelope e)
238238
{
239239
var analysisResult = default(ImageAnalysis);
240240

241-
if (data != null)
241+
if (data != null && data.Resource != null)
242242
{
243243
using Stream imageFileStream = new MemoryStream();
244244

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
namespace Microsoft.Psi.Onnx
5+
{
6+
using System;
7+
using System.Collections.Generic;
8+
using System.IO;
9+
using System.Linq;
10+
using Microsoft.Psi;
11+
12+
/// <summary>
13+
/// Internal class that parses the outputs from the ImageNet model into
14+
/// a set of image classification results.
15+
/// </summary>
16+
internal class ImageNetModelOutputParser
17+
{
18+
private readonly string[] labels;
19+
private readonly int maxPredictions;
20+
private readonly bool applySoftmax;
21+
22+
/// <summary>
23+
/// Initializes a new instance of the <see cref="ImageNetModelOutputParser"/> class.
24+
/// </summary>
25+
/// <param name="imageClassesFile">The path to the file containing the list of 1000 ImageNet classes.</param>
26+
/// <param name="maxPredictions">The maximum number of predictions to return.</param>
27+
/// <param name="applySoftmax">Whether the softmax function should be applied to the raw model output.</param>
28+
/// <remarks>
29+
/// The file referenced by <paramref name="imageClassesFile"/> may be downloaded from the following location:
30+
/// https://github.com/onnx/models/raw/8d50e3f598e6d5c67c7c7253e5a203a26e731a1b/vision/classification/synset.txt.
31+
/// </remarks>
32+
public ImageNetModelOutputParser(string imageClassesFile, int maxPredictions, bool applySoftmax)
33+
{
34+
this.labels = File.ReadAllLines(imageClassesFile);
35+
if (this.labels.Length != 1000)
36+
{
37+
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.");
38+
}
39+
40+
this.maxPredictions = maxPredictions;
41+
this.applySoftmax = applySoftmax;
42+
}
43+
44+
/// <summary>
45+
/// Gets the predictions from the model output.
46+
/// </summary>
47+
/// <param name="modelOutput">The model output vector of class probabilities.</param>
48+
/// <returns>A list of the top-N predictions, in descending probability order.</returns>
49+
public List<LabeledPrediction> GetPredictions(float[] modelOutput)
50+
{
51+
return GetTopResults(this.applySoftmax ? Softmax(modelOutput) : modelOutput, this.maxPredictions)
52+
.Select(c => new LabeledPrediction { Label = this.labels[c.Index], Confidence = c.Value })
53+
.ToList();
54+
}
55+
56+
private static IEnumerable<(int Index, float Value)> GetTopResults(IEnumerable<float> predictedClasses, int count)
57+
{
58+
return predictedClasses
59+
.Select((predictedClass, index) => (Index: index, Value: predictedClass))
60+
.OrderByDescending(result => result.Value)
61+
.Take(count);
62+
}
63+
64+
private static IEnumerable<float> Softmax(IEnumerable<float> values)
65+
{
66+
var maxVal = values.Max();
67+
var exp = values.Select(v => Math.Exp(v - maxVal));
68+
var sumExp = exp.Sum();
69+
70+
return exp.Select(v => (float)(v / sumExp));
71+
}
72+
}
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
namespace Microsoft.Psi.Onnx
5+
{
6+
using System;
7+
using System.Collections.Generic;
8+
using Microsoft.Psi;
9+
using Microsoft.Psi.Components;
10+
using Microsoft.Psi.Imaging;
11+
12+
/// <summary>
13+
/// Component that runs an ImageNet image classification model.
14+
/// </summary>
15+
/// <remarks>
16+
/// This class implements a \psi component that runs an ONNX model trained
17+
/// on the ImageNet dataset that operates on 224x224 RGB images and scores
18+
/// the image for each of the 1000 ImageNet classes. It takes an input
19+
/// stream of \psi images, applies a center-crop, rescales and normalizes
20+
/// the pixel values into the input vector expected by the model. It also
21+
/// parses the model outputs into a list of <see cref="LabeledPrediction"/>
22+
/// values, corresponding to the top N predictions by the model. For
23+
/// convenience, a set of pre-defined model runner configurations are
24+
/// defined for a number of image classification models available in the
25+
/// ONNX Model Zoo (https://github.com/onnx/models/tree/master/vision/classification).
26+
/// The ONNX model file for the corresponding configuration will need to be
27+
/// downloaded locally and the path to the model file will need to be
28+
/// specified when creating the configuration.
29+
/// </remarks>
30+
public class ImageNetModelRunner : ConsumerProducer<Shared<Image>, List<LabeledPrediction>>
31+
{
32+
private readonly float[] onnxInputVector = new float[3 * 224 * 224];
33+
private readonly OnnxModel onnxModel;
34+
private readonly ImageNetModelOutputParser outputParser;
35+
36+
/// <summary>
37+
/// Initializes a new instance of the <see cref="ImageNetModelRunner"/> class.
38+
/// </summary>
39+
/// <param name="pipeline">The pipeline to add the component to.</param>
40+
/// <param name="configuration">The configuration for the compoinent.</param>
41+
/// <remarks>
42+
/// To run on a GPU, use the Microsoft.Psi.Onnx.ModelRunners.Gpu library instead of Microsoft.Psi.Onnx.ModelRunners.Cpu, and set
43+
/// the value of the <pararef name="gpuDeviceId"/> parameter to a valid non-negative integer. Typical device ID values are 0 or 1.
44+
/// </remarks>
45+
public ImageNetModelRunner(Pipeline pipeline, ImageNetModelRunnerConfiguration configuration)
46+
: base(pipeline)
47+
{
48+
// create an ONNX model based on the supplied ImageNet model runner configuration
49+
this.onnxModel = new OnnxModel(new OnnxModelConfiguration()
50+
{
51+
ModelFileName = configuration.ModelFilePath,
52+
InputVectorName = configuration.InputVectorName,
53+
InputVectorSize = 3 * 224 * 224,
54+
OutputVectorName = configuration.OutputVectorName,
55+
GpuDeviceId = configuration.GpuDeviceId,
56+
});
57+
58+
this.outputParser = new ImageNetModelOutputParser(configuration.ImageClassesFilePath, configuration.NumberOfPredictions, configuration.ApplySoftmaxToModelOutput);
59+
}
60+
61+
/// <inheritdoc/>
62+
protected override void Receive(Shared<Image> data, Envelope envelope)
63+
{
64+
// construct the ONNX model input vector (stored in this.onnxInputVector)
65+
// based on the incoming image
66+
this.ConstructOnnxInputVector(data);
67+
68+
// run the model over the input vector
69+
var outputVector = this.onnxModel.GetPrediction(this.onnxInputVector);
70+
71+
// parse the model output into an ordered list of the top-N predictions
72+
var results = this.outputParser.GetPredictions(outputVector);
73+
74+
// post the results
75+
this.Out.Post(results, envelope.OriginatingTime);
76+
}
77+
78+
/// <summary>
79+
/// Constructs the input vector for the ImageNet model for a specified image.
80+
/// </summary>
81+
/// <param name="sharedImage">The image to construct the input vector for.</param>
82+
private void ConstructOnnxInputVector(Shared<Image> sharedImage)
83+
{
84+
var inputImage = sharedImage.Resource;
85+
var inputWidth = sharedImage.Resource.Width;
86+
var inputHeight = sharedImage.Resource.Height;
87+
88+
// crop a center square
89+
var squareSize = Math.Min(inputWidth, inputHeight);
90+
using var squareImage = ImagePool.GetOrCreate(squareSize, squareSize, sharedImage.Resource.PixelFormat);
91+
if (inputWidth > inputHeight)
92+
{
93+
inputImage.Crop(squareImage.Resource, (inputWidth - squareSize) / 2, 0, squareSize, squareSize);
94+
}
95+
else
96+
{
97+
inputImage.Crop(squareImage.Resource, 0, (inputHeight - squareSize) / 2, squareSize, squareSize);
98+
}
99+
100+
// resize the image to 224 x 224
101+
using var resizedImage = ImagePool.GetOrCreate(224, 224, sharedImage.Resource.PixelFormat);
102+
squareImage.Resource.Resize(resizedImage.Resource, 224, 224, SamplingMode.Bilinear);
103+
104+
// if the pixel format does not match, do a conversion before extracting the bytes
105+
var bytes = default(byte[]);
106+
if (sharedImage.Resource.PixelFormat != PixelFormat.BGR_24bpp)
107+
{
108+
using var reformattedImage = ImagePool.GetOrCreate(224, 224, PixelFormat.BGR_24bpp);
109+
resizedImage.Resource.CopyTo(reformattedImage.Resource);
110+
bytes = reformattedImage.Resource.ReadBytes(3 * 224 * 224);
111+
}
112+
else
113+
{
114+
// get the bytes
115+
bytes = resizedImage.Resource.ReadBytes(3 * 224 * 224);
116+
}
117+
118+
// Now populate the onnxInputVector float array / tensor by normalizing
119+
// using mean = [0.485, 0.456, 0.406] and std = [0.229, 0.224, 0.225].
120+
int fi = 0;
121+
122+
// first the red bytes
123+
for (int i = 2; i < bytes.Length; i += 3)
124+
{
125+
this.onnxInputVector[fi++] = ((bytes[i] / 255.0f) - 0.485f) / 0.229f;
126+
}
127+
128+
// then the green bytes
129+
for (int i = 1; i < bytes.Length; i += 3)
130+
{
131+
this.onnxInputVector[fi++] = ((bytes[i] / 255.0f) - 0.456f) / 0.224f;
132+
}
133+
134+
// then the blue bytes
135+
for (int i = 0; i < bytes.Length; i += 3)
136+
{
137+
this.onnxInputVector[fi++] = ((bytes[i] / 255.0f) - 0.406f) / 0.225f;
138+
}
139+
}
140+
}
141+
}

0 commit comments

Comments
 (0)