在 C# 中使用 ResNet50v2 進行影像識別
本示例將演示如何使用 Onnx Runtime C# API 執行預訓練的 ResNet50 v2 ONNX 模型。
此示例的原始碼可在此處獲取:here。
目錄
先決條件
要執行此示例,您需要以下內容
- 為您的作業系統(Mac、Windows 或 Linux)安裝 .NET Core 3.1 或更高版本。
- 將 ResNet50 v2 ONNX 模型下載到您的本地系統。
- 下載這張狗的照片以測試模型。您也可以使用任何您喜歡的影像。
開始使用
現在我們已經設定好了一切,我們可以開始新增程式碼來在影像上執行模型。為了簡單起見,我們將在程式的主方法中完成此操作。
讀取路徑
首先,讓我們透過程式引數讀取模型路徑和我們要測試的影像路徑
string modelFilePath = args[0];
string imageFilePath = args[1];
讀取影像
接下來,我們將使用跨平臺影像庫 ImageSharp 讀取影像
using Image<Rgb24> image = Image.Load<Rgb24>(imageFilePath, out IImageFormat format);
請注意,我們專門讀取 Rgb24 型別,以便我們可以在後續步驟中高效地預處理影像。
調整影像大小
接下來,我們將把影像大小調整到模型期望的適當大小:224 畫素 x 224 畫素
using Stream imageStream = new MemoryStream();
image.Mutate(x =>
{
x.Resize(new ResizeOptions
{
Size = new Size(224, 224),
Mode = ResizeMode.Crop
});
});
image.Save(imageStream, format);
請注意,我們正在進行中心裁剪大小調整以保持寬高比。
影像預處理
接下來,我們將根據模型要求對影像進行預處理
// We use DenseTensor for multi-dimensional access to populate the image data
var mean = new[] { 0.485f, 0.456f, 0.406f };
var stddev = new[] { 0.229f, 0.224f, 0.225f };
DenseTensor<float> processedImage = new(new[] { 1, 3, 224, 224 });
image.ProcessPixelRows(accessor =>
{
for (int y = 0; y < accessor.Height; y++)
{
Span<Rgb24> pixelSpan = accessor.GetRowSpan(y);
for (int x = 0; x < accessor.Width; x++)
{
processedImage[0, 0, y, x] = ((pixelSpan[x].R / 255f) - mean[0]) / stddev[0];
processedImage[0, 1, y, x] = ((pixelSpan[x].G / 255f) - mean[1]) / stddev[1];
processedImage[0, 2, y, x] = ((pixelSpan[x].B / 255f) - mean[2]) / stddev[2];
}
}
});
在這裡,我們正在建立一個所需大小的 Tensor (批大小, 通道數, 高度, 寬度),訪問畫素值,對其進行預處理,最後將它們分配給 Tensor 的相應索引。
設定輸入
接下來,我們將建立模型的輸入
// Pin tensor buffer and create a OrtValue with native tensor that makes use of
// DenseTensor buffer directly. This avoids extra data copy within OnnxRuntime.
// It will be unpinned on ortValue disposal
using var inputOrtValue = OrtValue.CreateTensorValueFromMemory(OrtMemoryInfo.DefaultInstance,
processedImage.Buffer, new long[] { 1, 3, 224, 224 });
var inputs = new Dictionary<string, OrtValue>
{
{ "data", inputOrtValue }
}
要檢查 ONNX 模型的輸入節點名稱,您可以使用 Netron 視覺化模型並檢視輸入/輸出名稱。在此示例中,此模型將 data 作為輸入節點名稱。
執行推理
接下來,我們將建立一個推理會話並透過它執行輸入
using var session = new InferenceSession(modelFilePath);
using var runOptions = new RunOptions();
using IDisposableReadOnlyCollection<OrtValue> results = session.Run(runOptions, inputs, session.OutputNames);
後處理輸出
接下來,我們需要對輸出進行後處理以獲得 softmax 向量,因為這並非模型本身處理。
// We copy results to array only to apply algorithms, otherwise data can be accessed directly
// from the native buffer via ReadOnlySpan<T> or Span<T>
var output = results[0].GetTensorDataAsSpan<float>().ToArray();
float sum = output.Sum(x => (float)Math.Exp(x));
IEnumerable<float> softmax = output.Select(x => (float)Math.Exp(x) / sum);
其他模型可能會在輸出之前應用 Softmax 節點,在這種情況下您不需要此步驟。同樣,您可以使用 Netron 檢視模型輸出。
提取前 10 名
接下來,我們將提取前 10 個類別預測
IEnumerable<Prediction> top10 = softmax.Select((x, i) => new Prediction { Label = LabelMap.Labels[i], Confidence = x })
.OrderByDescending(x => x.Confidence)
.Take(10);
列印結果
接下來,我們將前 10 個結果列印到控制檯
Console.WriteLine("Top 10 predictions for ResNet50 v2...");
Console.WriteLine("--------------------------------------------------------------");
foreach (var t in top10)
{
Console.WriteLine($"Label: {t.Label}, Confidence: {t.Confidence}");
}
執行程式
現在程式已建立,我們可以使用以下命令執行它
dotnet run [path-to-model] [path-to-image]
例如:
dotnet run ~/Downloads/resnet50-v2-7.onnx ~/Downloads/dog.jpeg
在以下影像上執行此程式

我們得到以下輸出
Top 10 predictions for ResNet50 v2...
--------------------------------------------------------------
Label: Golden Retriever, Confidence: 0.9212826
Label: Kuvasz, Confidence: 0.026514154
Label: Clumber Spaniel, Confidence: 0.012455719
Label: Labrador Retriever, Confidence: 0.004103844
Label: Saluki, Confidence: 0.0033182495
Label: Flat-Coated Retriever, Confidence: 0.0032045357
Label: English Setter, Confidence: 0.002513516
Label: Brittany, Confidence: 0.0023459378
Label: Cocker Spaniels, Confidence: 0.0019343802
Label: Sussex Spaniel, Confidence: 0.0019247672