Skip to content

Instantly share code, notes, and snippets.

@ImagingSolution
Created February 12, 2022 02:37
Show Gist options
  • Save ImagingSolution/90ec12588930bf2429dc397ebba09e25 to your computer and use it in GitHub Desktop.
Save ImagingSolution/90ec12588930bf2429dc397ebba09e25 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
// 追加(NuGetでMicrosoft.ML.OnnxRuntimeの追加が必要)
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
private string[] _category_names;
private Color[] _category_colors;
private Font _labelFont;
public Form1()
{
InitializeComponent();
// カテゴリ名
// 参考 https://pytorch.org/vision/main/models.html#object-detection-instance-segmentation-and-person-keypoint-detection
_category_names = new string[] {
"__background__", "person", "bicycle", "car", "motorcycle", "airplane", "bus",
"train", "truck", "boat", "traffic light", "fire hydrant", "N/A", "stop sign",
"parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow",
"elephant", "bear", "zebra", "giraffe", "N/A", "backpack", "umbrella", "N/A", "N/A",
"handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball",
"kite", "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket",
"bottle", "N/A", "wine glass", "cup", "fork", "knife", "spoon", "bowl",
"banana", "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza",
"donut", "cake", "chair", "couch", "potted plant", "bed", "N/A", "dining table",
"N/A", "N/A", "toilet", "N/A", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone",
"microwave", "oven", "toaster", "sink", "refrigerator", "N/A", "book",
"clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush"
};
// 表示色を用意
_category_colors = new Color[91];
var rnd = new System.Random();
for (int i = 0; i < _category_colors.Length; i++)
{
_category_colors[i] = Color.FromArgb(rnd.Next(0, 256), rnd.Next(0, 256), rnd.Next(0, 256));
}
// ラベルのフォント
_labelFont = new Font("Arial", 20);
}
/// <summary>
/// 画像データをHWC→CHWへ変換し0~1のfloatの一次元配列に変換する
/// </summary>
/// <param name="filename">画像ファイルのファイル名</param>
/// <param name="bmp">Bitmapクラスオブジェクト</param>
/// <param name="width">Bitmapの幅</param>
/// <param name="height">Bitmapの高さ</param>
/// <param name="channel">Bitmapのチャンネル数</param>
/// <returns>floatの一次元配列</returns>
private float[] GetImageFloatData(string filename, out Bitmap bmp, out int width, out int height, out int channel)
{
float[] imageData;
bmp = new Bitmap(filename);
imageData = GetImageFloatData(bmp, out width, out height, out channel);
return imageData;
}
/// <summary>
/// 画像データをHWC→CHWへ変換し0~1のfloatの一次元配列に変換する
/// </summary>
/// <param name="bmp">Bitmapクラスオブジェクト</param>
/// <param name="width">Bitmapの幅</param>
/// <param name="height">Bitmapの高さ</param>
/// <param name="channel">Bitmapのチャンネル数</param>
/// <returns>floatの一次元配列</returns>
private float[] GetImageFloatData(Bitmap bmp, out int width, out int height, out int channel)
{
width = bmp.Width;
height = bmp.Height;
// 画像の1画素あたりのビット数の取得(8,24,32など)
var bitCount = Bitmap.GetPixelFormatSize(bmp.PixelFormat);
channel = bitCount / 8;
// Bitmapをロック
var bmpData = bmp.LockBits(
new Rectangle(0, 0, width, height),
System.Drawing.Imaging.ImageLockMode.ReadOnly,
bmp.PixelFormat
);
// メモリの幅のバイト数を取得
var stride = Math.Abs(bmpData.Stride);
// 画像データ(輝度値データ)のバイト配列
var byteData = new byte[stride * height];
// Bitmapデータを配列へコピー
System.Runtime.InteropServices.Marshal.Copy(
bmpData.Scan0,
byteData,
0,
stride * height
);
// アンロック
bmp.UnlockBits(bmpData);
var imageSize = height * width;
// データの並び替え、1/255にする
float[] imageFloatData = new float[channel * imageSize];
var w = width;
var c = channel;
Parallel.For(0, height, y =>
{
for (int x = 0; x < w; x++)
{
for (int ch = 0; ch < c; ch++)
{
// BGRをR,G,Bのプレーンの順番に変更し、0~255を0~1のfloatへ
imageFloatData[imageSize * (c - 1 - ch) + w * y + x]
= byteData[stride * y + c * x + ch] / 255.0f;
}
}
});
return imageFloatData;
}
/// <summary>
/// 物体認識の結果表示
/// </summary>
/// <param name="pic">表示先のPictureBox</param>
/// <param name="bmp">画像のBitmapオブジェクト</param>
/// <param name="results">推論結果</param>
private void DrawResult(PictureBox pic, Bitmap bmp, IDisposableReadOnlyCollection<DisposableNamedOnnxValue> results)
{
var g = Graphics.FromImage(bmp);
// 結果をListに変換
var values = results.ToList<DisposableNamedOnnxValue>();
var boxes = values[0].AsTensor<float>().ToArray(); // 左、上、右、下、・・・
var labels = values[1].AsTensor<Int64>().ToArray();// ラベルのインデックス番号
var scores = values[2].AsTensor<float>().ToArray();// スコア
for (int i = 0; i < scores.Length; i++)
{
if (scores[i] < 0.5) continue;
// 検出領域の描画
g.DrawRectangle(
new Pen(_category_colors[labels[i]], 3),
new Rectangle(
(int)boxes[i * 4],
(int)boxes[i * 4 + 1],
(int)(boxes[i * 4 + 2] - boxes[i * 4]),
(int)(boxes[i * 4 + 3] - boxes[i * 4 + 1]
)));
// カテゴリ名の描画
g.DrawString(
_category_names[labels[i]],
_labelFont,
new SolidBrush(_category_colors[labels[i]]),
new PointF(boxes[i * 4], boxes[i * 4 + 1])
);
}
pic.Image = bmp;
}
private void button1_Click(object sender, EventArgs e)
{
// 画像データをプレーン分離したRGBのfloat(0.0~1.0)の配列で取得
int width, height, channel;
Bitmap bmp;
// 画像データをHWC→CHWへ変換し0~1のfloatの一次元配列に変換する
var imageFloatData = GetImageFloatData("image.jpg", out bmp, out width, out height, out channel);
// オプションの設定
var opts = new SessionOptions();
opts.ExecutionMode = ExecutionMode.ORT_PARALLEL;
// ONNXファイルの読み込み
using (var session = new InferenceSession("fasterrcnn_mobilenet_v3_large_320_fpn.onnx", opts)) // Microsoft.ML.OnnxRuntime.InferenceSession
{
var inputName = session.InputMetadata.First().Key;  // 入力データの名前
var inputDimensions = session.InputMetadata.First().Value.Dimensions; // 入力データの次元 1, 3, 480, 640
var inputTensor = new DenseTensor<float>( //Microsoft.ML.OnnxRuntime.Tensors.DenseTensor
imageFloatData, // 画像データ(テンソル相当、floatの一次元配列)
inputDimensions // 入力データの次元(今回は1, 3, 480, 640)
);
var namedOnnxValues = new List<NamedOnnxValue> // Name と Value のリスト
{
NamedOnnxValue.CreateFromTensor(inputName, inputTensor)
};
// 推論の実行
using (var results = session.Run(namedOnnxValues))
{
// 結果の表示
DrawResult(pictureBox1, bmp, results);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment