コンテンツにスキップ

C#でのFaster RCNNによる物体検出

C#でのFaster RCNNディープラーニングによる物体検出

Section titled “C#でのFaster RCNNディープラーニングによる物体検出”

このサンプルでは、ONNX Runtime C# APIを使用して、事前トレーニング済みのFaster R-CNN物体検出ONNXモデルを実行する方法を順を追って説明します。

このサンプルのソースコードはこちらで入手できます。

このサンプルを実行するには、次のものが必要です。

  1. お使いのOS(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 };
// 画像の前処理
// 多次元アクセスにはDenseTensorを使用します
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を使用して赤い線を描画してボックスを作成し、ラベルと信頼度のテキストを描画しています。

プログラムが作成されたので、次のコマンドで実行できます。

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

例:実行中:

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

画像内の次のオブジェクトを検出します。