使用 C# 透過 Faster RCNN 深度學習進行目標檢測

本示例將演示如何使用 ONNX Runtime C# API 執行預訓練的 Faster R-CNN 目標檢測 ONNX 模型。

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

目錄

先決條件

要執行此示例,您需要具備以下條件:

  1. 為您的作業系統(Mac、Windows 或 Linux)安裝 .NET Core 3.1 或更高版本。
  2. Faster R-CNN ONNX 模型下載到本地系統。
  3. 下載此演示影像以測試模型。您也可以使用任何您喜歡的影像。

開始使用

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

讀取路徑

首先,讓我們讀取模型路徑、要測試的影像路徑和輸出影像路徑

string modelFilePath = args[0];
string imageFilePath = args[1];
string outImageFilePath = args[2];

讀取影像

接下來,我們將使用跨平臺影像庫 ImageSharp 讀取影像。

using Image<Rgb24> image = Image.Load<Rgb24>(imageFilePath, out IImageFormat format);

請注意,我們專門讀取 Rgb24 型別,以便在後續步驟中高效地預處理影像。

調整影像大小

接下來,我們將影像調整到模型期望的合適大小;建議將影像調整為高度和寬度都在 [800, 1333] 範圍內的尺寸。

float ratio = 800f / Math.Min(image.Width, image.Height);
using Stream imageStream = new MemoryStream();
image.Mutate(x => x.Resize((int)(ratio * image.Width), (int)(ratio * image.Height)));
image.Save(imageStream, format);

預處理影像

接下來,我們將根據模型要求預處理影像。

var paddedHeight = (int)(Math.Ceiling(image.Height / 32f) * 32f);
var paddedWidth = (int)(Math.Ceiling(image.Width / 32f) * 32f);
var mean = new[] { 102.9801f, 115.9465f, 122.7717f };

// Preprocessing image
// We use DenseTensor for multi-dimensional access
DenseTensor<float> input = new(new[] { 3, paddedHeight, paddedWidth });
image.ProcessPixelRows(accessor =>
{
    for (int y = paddedHeight - accessor.Height; y < accessor.Height; y++)
    {
        Span<Rgb24> pixelSpan = accessor.GetRowSpan(y);
        for (int x = paddedWidth - accessor.Width; x < accessor.Width; x++)
        {
            input[0, y, x] = pixelSpan[x].B - mean[0];
            input[1, y, x] = pixelSpan[x].G - mean[1];
            input[2, y, x] = pixelSpan[x].R - mean[2];
        }
    }
});

這裡,我們建立了一個所需大小 (channels, paddedHeight, paddedWidth) 的張量,訪問畫素值,對其進行預處理,最後將其分配到張量的相應索引處。

設定輸入

// 鎖定 DenseTensor 記憶體並在 OrtValue 張量中直接使用 // 它將在 OrtValue 釋放時解除鎖定

using var inputOrtValue = OrtValue.CreateTensorValueFromMemory(OrtMemoryInfo.DefaultInstance,
    input.Buffer, new long[] { 3, paddedHeight, paddedWidth });

接下來,我們將建立模型的輸入


var inputs = new Dictionary<string, OrtValue>
{
    { "image", inputOrtValue }
};

要檢查 ONNX 模型的輸入節點名稱,您可以使用 Netron 視覺化模型並檢視輸入/輸出名稱。在本例中,該模型的輸入節點名稱為 image

執行推理

接下來,我們將建立一個推理會話並透過它執行輸入

using var session = new InferenceSession(modelFilePath);
using var runOptions = new RunOptions();
using IDisposableReadOnlyCollection<OrtValue> results = session.Run(runOptions, inputs, session.OutputNames);

後處理輸出

接下來,我們需要後處理輸出,以獲取每個框的邊界框、關聯的標籤和置信度分數。

var boxesSpan = results[0].GetTensorDataAsSpan<float>();
var labelsSpan = results[1].GetTensorDataAsSpan<long>();
var confidencesSpan = results[2].GetTensorDataAsSpan<float>();

const float minConfidence = 0.7f;
var predictions = new List<Prediction>();

for (int i = 0; i < boxesSpan.Length - 4; i += 4)
{
    var index = i / 4;
    if (confidencesSpan[index] >= minConfidence)
    {
        predictions.Add(new Prediction
        {
            Box = new Box(boxesSpan[i], boxesSpan[i + 1], boxesSpan[i + 2], boxesSpan[i + 3]),
            Label = LabelMap.Labels[labelsSpan[index]],
            Confidence = confidencesSpan[index]
        });
    }
}

請注意,我們只選擇置信度高於 0.7 的框,以消除誤報。

檢視預測

接下來,我們將在影像上繪製邊界框以及關聯的標籤和置信度分數,以檢視模型的表現。

using var outputImage = File.OpenWrite(outImageFilePath);
Font font = SystemFonts.CreateFont("Arial", 16);
foreach (var p in predictions)
{
    image.Mutate(x =>
    {
        x.DrawLines(Color.Red, 2f, new PointF[] {

            new PointF(p.Box.Xmin, p.Box.Ymin),
            new PointF(p.Box.Xmax, p.Box.Ymin),

            new PointF(p.Box.Xmax, p.Box.Ymin),
            new PointF(p.Box.Xmax, p.Box.Ymax),

            new PointF(p.Box.Xmax, p.Box.Ymax),
            new PointF(p.Box.Xmin, p.Box.Ymax),

            new PointF(p.Box.Xmin, p.Box.Ymax),
            new PointF(p.Box.Xmin, p.Box.Ymin)
        });
        x.DrawText($"{p.Label}, {p.Confidence:0.00}", font, Color.White, new PointF(p.Box.Xmin, p.Box.Ymin));
    });
}
image.Save(outputImage, format);

對於每個邊界框預測,我們使用 ImageSharp 繪製紅線以建立邊界框,並繪製標籤和置信度文字。

執行程式

現在程式已建立,我們可以使用以下命令執行它

dotnet run [path-to-model] [path-to-image] [path-to-output-image]

例如,執行

dotnet run ~/Downloads/FasterRCNN-10.onnx ~/Downloads/demo.jpg ~/Downloads/out.jpg

檢測影像中的以下物件