Skip to content

Commit e047118

Browse files
2 parents abe833f + 6276012 commit e047118

30 files changed

+475
-425
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System.Drawing;
2+
3+
namespace OnnxObjectDetection
4+
{
5+
public class BoundingBoxDimensions
6+
{
7+
public float X { get; set; }
8+
public float Y { get; set; }
9+
public float Height { get; set; }
10+
public float Width { get; set; }
11+
}
12+
13+
public class BoundingBox
14+
{
15+
public BoundingBoxDimensions Dimensions { get; set; }
16+
17+
public string Label { get; set; }
18+
19+
public float Confidence { get; set; }
20+
21+
public RectangleF Rect
22+
{
23+
get { return new RectangleF(Dimensions.X, Dimensions.Y, Dimensions.Width, Dimensions.Height); }
24+
}
25+
26+
public Color BoxColor { get; set; }
27+
28+
public string Description => $"{Label} ({(Confidence * 100).ToString("0")}%)";
29+
30+
private static readonly Color[] classColors = new Color[]
31+
{
32+
Color.Khaki, Color.Fuchsia, Color.Silver, Color.RoyalBlue,
33+
Color.Green, Color.DarkOrange, Color.Purple, Color.Gold,
34+
Color.Red, Color.Aquamarine, Color.Lime, Color.AliceBlue,
35+
Color.Sienna, Color.Orchid, Color.Tan, Color.LightPink,
36+
Color.Yellow, Color.HotPink, Color.OliveDrab, Color.SandyBrown,
37+
Color.DarkTurquoise
38+
};
39+
40+
public static Color GetColor(int index) => index < classColors.Length ? classColors[index] : classColors[index % classColors.Length];
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System;
2+
using System.IO;
3+
using System.IO.Compression;
4+
using System.Linq;
5+
6+
namespace OnnxObjectDetection
7+
{
8+
public class CustomVisionModel : IOnnxModel
9+
{
10+
const string modelName = "model.onnx", labelsName = "labels.txt";
11+
12+
private readonly string labelsPath;
13+
14+
public string ModelPath { get; private set; }
15+
16+
public string ModelInput { get; } = "data";
17+
public string ModelOutput { get; } = "model_outputs0";
18+
19+
public string[] Labels { get; private set; }
20+
public (float,float)[] Anchors { get; } = { (0.573f,0.677f), (1.87f,2.06f), (3.34f,5.47f), (7.88f,3.53f), (9.77f,9.17f) };
21+
22+
public CustomVisionModel(string modelPath)
23+
{
24+
var extractPath = Path.GetFullPath(modelPath.Replace(".zip", Path.DirectorySeparatorChar.ToString()));
25+
26+
if (!Directory.Exists(extractPath))
27+
Directory.CreateDirectory(extractPath);
28+
29+
ModelPath = Path.GetFullPath(Path.Combine(extractPath, modelName));
30+
labelsPath = Path.GetFullPath(Path.Combine(extractPath, labelsName));
31+
32+
if (!File.Exists(ModelPath) || !File.Exists(labelsPath))
33+
ExtractArchive(modelPath);
34+
35+
Labels = File.ReadAllLines(labelsPath);
36+
}
37+
38+
void ExtractArchive(string modelPath)
39+
{
40+
using (ZipArchive archive = ZipFile.OpenRead(modelPath))
41+
{
42+
var modelEntry = archive.Entries.FirstOrDefault(e => e.Name.Equals(modelName, StringComparison.OrdinalIgnoreCase))
43+
?? throw new FormatException("The exported .zip archive is missing the model.onnx file");
44+
45+
modelEntry.ExtractToFile(ModelPath);
46+
47+
var labelsEntry = archive.Entries.FirstOrDefault(e => e.Name.Equals(labelsName, StringComparison.OrdinalIgnoreCase))
48+
?? throw new FormatException("The exported .zip archive is missing the labels.txt file");
49+
50+
labelsEntry.ExtractToFile(labelsPath);
51+
}
52+
}
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using Microsoft.ML.Data;
2+
3+
namespace OnnxObjectDetection
4+
{
5+
public class CustomVisionPrediction : IOnnxObjectPrediction
6+
{
7+
[ColumnName("model_outputs0")]
8+
public float[] PredictedLabels { get; set; }
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace OnnxObjectDetection
2+
{
3+
public interface IOnnxModel
4+
{
5+
string ModelPath { get; }
6+
7+
// To check Model input and output parameter names, you can
8+
// use tools like Netron: https://github.com/lutzroeder/netron
9+
string ModelInput { get; }
10+
string ModelOutput { get; }
11+
12+
string[] Labels { get; }
13+
(float, float)[] Anchors { get; }
14+
}
15+
16+
public interface IOnnxObjectPrediction
17+
{
18+
float[] PredictedLabels { get; set; }
19+
}
20+
}

samples/csharp/end-to-end-apps/ObjectDetection-Onnx/OnnxObjectDetection/ML/DataModels/ImageInputData.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@
33

44
namespace OnnxObjectDetection
55
{
6+
public struct ImageSettings
7+
{
8+
public const int imageHeight = 416;
9+
public const int imageWidth = 416;
10+
}
11+
612
public class ImageInputData
713
{
8-
[ImageType(416, 416)]
14+
[ImageType(ImageSettings.imageHeight, ImageSettings.imageWidth)]
915
public Bitmap Image { get; set; }
1016
}
1117
}

samples/csharp/end-to-end-apps/ObjectDetection-Onnx/OnnxObjectDetection/ML/DataModels/ImageObjectPrediction.cs

-10
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
namespace OnnxObjectDetection
2+
{
3+
public class TinyYoloModel : IOnnxModel
4+
{
5+
public string ModelPath { get; private set; }
6+
7+
public string ModelInput { get; } = "image";
8+
public string ModelOutput { get; } = "grid";
9+
10+
public string[] Labels { get; } =
11+
{
12+
"aeroplane", "bicycle", "bird", "boat", "bottle",
13+
"bus", "car", "cat", "chair", "cow",
14+
"diningtable", "dog", "horse", "motorbike", "person",
15+
"pottedplant", "sheep", "sofa", "train", "tvmonitor"
16+
};
17+
18+
public (float,float)[] Anchors { get; } = { (1.08f,1.19f), (3.42f,4.41f), (6.63f,11.38f), (9.42f,5.11f), (16.62f,10.52f) };
19+
20+
public TinyYoloModel(string modelPath)
21+
{
22+
ModelPath = modelPath;
23+
}
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using Microsoft.ML.Data;
2+
3+
namespace OnnxObjectDetection
4+
{
5+
public class TinyYoloPrediction : IOnnxObjectPrediction
6+
{
7+
[ColumnName("grid")]
8+
public float[] PredictedLabels { get; set; }
9+
}
10+
}

samples/csharp/end-to-end-apps/ObjectDetection-Onnx/OnnxObjectDetection/ML/OnnxModelConfigurator.cs

+14-31
Original file line numberDiff line numberDiff line change
@@ -7,57 +7,40 @@ namespace OnnxObjectDetection
77
{
88
public class OnnxModelConfigurator
99
{
10-
private readonly MLContext _mlContext;
11-
private readonly ITransformer _mlModel;
10+
private readonly MLContext mlContext;
11+
private readonly ITransformer mlModel;
1212

13-
public OnnxModelConfigurator(string onnxModelFilePath)
13+
public OnnxModelConfigurator(IOnnxModel onnxModel)
1414
{
15-
_mlContext = new MLContext();
15+
mlContext = new MLContext();
1616
// Model creation and pipeline definition for images needs to run just once,
1717
// so calling it from the constructor:
18-
_mlModel = SetupMlNetModel(onnxModelFilePath);
18+
mlModel = SetupMlNetModel(onnxModel);
1919
}
2020

21-
public struct ImageSettings
21+
private ITransformer SetupMlNetModel(IOnnxModel onnxModel)
2222
{
23-
public const int imageHeight = 416;
24-
public const int imageWidth = 416;
25-
}
26-
27-
public struct TinyYoloModelSettings
28-
{
29-
// To check Tiny Yolo2 Model input and output parameter names,
30-
// you can use tools like Netron: https://github.com/lutzroeder/netron
31-
32-
// Input tensor name
33-
public const string ModelInput = "image";
34-
35-
// Output tensor name
36-
public const string ModelOutput = "grid";
37-
}
38-
39-
public ITransformer SetupMlNetModel(string onnxModelFilePath)
40-
{
41-
var dataView = _mlContext.Data.LoadFromEnumerable(new List<ImageInputData>());
23+
var dataView = mlContext.Data.LoadFromEnumerable(new List<ImageInputData>());
4224

43-
var pipeline = _mlContext.Transforms.ResizeImages(resizing: ImageResizingEstimator.ResizingKind.Fill, outputColumnName: "image", imageWidth: ImageSettings.imageWidth, imageHeight: ImageSettings.imageHeight, inputColumnName: nameof(ImageInputData.Image))
44-
.Append(_mlContext.Transforms.ExtractPixels(outputColumnName: "image"))
45-
.Append(_mlContext.Transforms.ApplyOnnxModel(modelFile: onnxModelFilePath, outputColumnNames: new[] { TinyYoloModelSettings.ModelOutput }, inputColumnNames: new[] { TinyYoloModelSettings.ModelInput }));
25+
var pipeline = mlContext.Transforms.ResizeImages(resizing: ImageResizingEstimator.ResizingKind.Fill, outputColumnName: onnxModel.ModelInput, imageWidth: ImageSettings.imageWidth, imageHeight: ImageSettings.imageHeight, inputColumnName: nameof(ImageInputData.Image))
26+
.Append(mlContext.Transforms.ExtractPixels(outputColumnName: onnxModel.ModelInput))
27+
.Append(mlContext.Transforms.ApplyOnnxModel(modelFile: onnxModel.ModelPath, outputColumnName: onnxModel.ModelOutput, inputColumnName: onnxModel.ModelInput));
4628

4729
var mlNetModel = pipeline.Fit(dataView);
4830

4931
return mlNetModel;
5032
}
5133

52-
public PredictionEngine<ImageInputData, ImageObjectPrediction> GetMlNetPredictionEngine()
34+
public PredictionEngine<ImageInputData, T> GetMlNetPredictionEngine<T>()
35+
where T : class, IOnnxObjectPrediction, new()
5336
{
54-
return _mlContext.Model.CreatePredictionEngine<ImageInputData, ImageObjectPrediction>(_mlModel);
37+
return mlContext.Model.CreatePredictionEngine<ImageInputData, T>(mlModel);
5538
}
5639

5740
public void SaveMLNetModel(string mlnetModelFilePath)
5841
{
5942
// Save/persist the model to a .ZIP file to be loaded by the PredictionEnginePool
60-
_mlContext.Model.Save(_mlModel, null, mlnetModelFilePath);
43+
mlContext.Model.Save(mlModel, null, mlnetModelFilePath);
6144
}
6245
}
6346
}

samples/csharp/end-to-end-apps/ObjectDetection-Onnx/OnnxObjectDetection/OnnxObjectDetection.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
</ItemGroup>
1212

1313
<ItemGroup>
14-
<None Update="ML\OnnxModel\TinyYolo2_model.onnx">
14+
<None Update="ML\OnnxModels\TinyYolo2_model.onnx">
1515
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
1616
</None>
1717
</ItemGroup>

0 commit comments

Comments
 (0)