使用 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. 下载此演示图像以测试模型。您也可以使用任何您喜欢的图像。

开始使用

现在我们已经完成所有设置,我们可以开始添加代码以在图像上运行模型。为了简单起见,我们将在程序的主方法中执行此操作。

读取路径

首先,让我们读取模型路径、要测试的图像路径以及输出图像路径

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];
        }
    }
});

在这里,我们正在创建一个所需大小的张量 (通道数, 填充高度, 填充宽度),访问像素值,对其进行预处理,最后将它们分配给张量中的适当索引。

设置输入

// 固定 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

检测图像中的以下对象