Created
February 12, 2022 02:37
-
-
Save ImagingSolution/90ec12588930bf2429dc397ebba09e25 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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