在 C# 中使用 ResNet50v2 進行影像識別

本示例將演示如何使用 Onnx Runtime C# API 執行預訓練的 ResNet50 v2 ONNX 模型。

此示例的原始碼可在此處獲取:here

目錄

先決條件

要執行此示例,您需要以下內容

  1. 為您的作業系統(Mac、Windows 或 Linux)安裝 .NET Core 3.1 或更高版本。
  2. ResNet50 v2 ONNX 模型下載到您的本地系統。
  3. 下載這張狗的照片以測試模型。您也可以使用任何您喜歡的影像。

開始使用

現在我們已經設定好了一切,我們可以開始新增程式碼來在影像上執行模型。為了簡單起見,我們將在程式的主方法中完成此操作。

讀取路徑

首先,讓我們透過程式引數讀取模型路徑和我們要測試的影像路徑

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