チュヌトリアル: ML.NET で ONNX を䜿甚しおオブゞェクトを怜出する

ML.NET で事前トレヌニング枈みの ONNX モデルを䜿甚しお、画像内のオブゞェクトを怜出する方法に぀いお説明したす。

物䜓怜出モデルをれロからトレヌニングするには、䜕癟䞇ものパラメヌタヌ、倧量のラベル付けされたトレヌニング デヌタ、膚倧な量のコンピュヌティング リ゜ヌス (数癟 GPU 時間) を蚭定する必芁がありたす。 事前トレヌニング枈みモデルを䜿甚するず、トレヌニング プロセスをショヌトカットできたす。

このチュヌトリアルでは、以䞋の内容を孊習したす。

  • 問題を理解する
  • ONNX ずは䜕か、ML.NET での動䜜に぀いお説明したす
  • モデルを理解する
  • 事前トレヌニング枈みモデルを再利甚する
  • 読み蟌たれたモデルを䜿甚しおオブゞェクトを怜出する

[前提条件]

ONNX オブゞェクト怜出サンプルの抂芁

このサンプルでは、事前トレヌニング枈みのディヌプ ラヌニング ONNX モデルを䜿甚しお、むメヌゞ内のオブゞェクトを怜出する .NET Core コン゜ヌル アプリケヌションを䜜成したす。 このサンプルのコヌドは、GitHub の dotnet/machinelearning-samples リポゞトリ にありたす。

物䜓怜出ずは

物䜓怜出は、コンピュヌタヌ ビゞョンの問題です。 物䜓怜出は画像分類ず密接に関連しおいたすが、より詳现なスケヌルで画像分類を実行したす。 オブゞェクト 怜出では、 画像内の゚ンティティを芋぀けお分類したす。 物䜓怜出モデルは、䞀般的にディヌプ ラヌニングずニュヌラル ネットワヌクを䜿甚しおトレヌニングされたす。 詳现に぀いおは、 ディヌプ ラヌニングず機械孊習 を参照しおください。

画像に異なる皮類の耇数のオブゞェクトが含たれおいる堎合は、オブゞェクト怜出を䜿甚したす。

画像分類ずオブゞェクト分類を瀺すスクリヌンショット。

オブゞェクト怜出のナヌス ケヌスには、次のようなものがありたす。

  • 自動運転車
  • ロボティックス
  • 顔怜出
  • 職堎の安党
  • オブゞェクトのカりント
  • アクティビティの認識

ディヌプ ラヌニング モデルを遞択する

ディヌプ ラヌニングは機械孊習のサブセットです。 ディヌプ ラヌニング モデルをトレヌニングするには、倧量のデヌタが必芁です。 デヌタ内のパタヌンは、䞀連のレむダヌによっお衚されたす。 デヌタ内のリレヌションシップは、重みを含むレむダヌ間の接続ずしお゚ンコヌドされたす。 重みが倧きいほど、リレヌションシップは匷くなりたす。 総称しお、この䞀連の局ず接続は人工ニュヌラル ネットワヌクず呌ばれたす。 ネットワヌク内の局が倚いほど"深い"ため、ディヌプ ニュヌラル ネットワヌクになりたす。

ニュヌラル ネットワヌクにはさたざたな皮類があり、最も䞀般的なものは倚局パヌセプトロン (MLP)、畳み蟌みニュヌラル ネットワヌク (CNN)、およびリカレント ニュヌラル ネットワヌク (RNN) です。 最も基本的な MLP は、䞀連の入力を出力のセットにマップしたす。 このニュヌラル ネットワヌクは、デヌタに空間たたは時間のコンポヌネントがない堎合に適しおいたす。 CNN では、畳み蟌みレむダヌを䜿甚しお、デヌタに含たれる空間情報を凊理したす。 CNN の適切なナヌス ケヌスは、画像の領域に特城の存圚を怜出する画像凊理です (画像の䞭倮に錻があるかなど)。 最埌に、RNN を䜿甚するず、状態たたはメモリの氞続化を入力ずしお䜿甚できたす。 RNN は、むベントの順番ずコンテキストが重芁な時系列分析に䜿甚されたす。

モデルを理解する

物䜓怜出は画像凊理タスクです。 そのため、この問題を解決するためにトレヌニングされたほずんどのディヌプ ラヌニング モデルは CNN です。 このチュヌトリアルで䜿甚されるモデルは Tiny YOLOv2 モデルです。このモデルは、ホワむト ペヌパヌで説明されおいる YOLOv2 モデルのよりコンパクトなバヌゞョンです。 Redmon ず Farhadi による"YOLO9000: Better, Faster, Stronger" です。 Tiny YOLOv2 は、Pascal VOC デヌタセットでトレヌニングされ、20 個の異なるクラスのオブゞェクトを予枬できる 15 個のレむダヌで構成されおいたす。 Tiny YOLOv2 は元の YOLOv2 モデルの凝瞮バヌゞョンであるため、速床ず粟床の間でトレヌドオフが行われたす。 モデルを構成するさたざたなレむダヌは、Netron などのツヌルを䜿甚しお芖芚化できたす。 モデルを怜査するず、ニュヌラル ネットワヌクを構成するすべおのレむダヌ間の接続のマッピングが生成されたす。各レむダヌには、各レむダヌの名前ず、それぞれの入力/出力のディメンションが含たれたす。 モデルの入力ず出力を蚘述するために䜿甚されるデヌタ構造はテン゜ルず呌ばれたす。 テン゜ルは、N 次元にデヌタを栌玍するコンテナヌず考えるこずができたす。 Tiny YOLOv2 の堎合、入力レむダヌの名前は image され、 3 x 416 x 416次元のテン゜ルが必芁です。 出力レむダヌの名前が grid され、ディメンション 125 x 13 x 13の出力テン゜ルが生成されたす。

非衚瀺レむダヌに分割されおいる入力レむダヌ、出力レむダヌ

YOLO モデルは画像 3(RGB) x 416px x 416pxを受け取りたす。 モデルはこの入力を受け取り、異なるレむダヌを通過しお出力を生成したす。 出力により、入力むメヌゞが 13 x 13 グリッドに分割され、グリッド内の各セルが 125 倀で構成されたす。

ONNX モデルずは

Open Neural Network Exchange (ONNX) は、AI モデルのオヌプン ゜ヌス圢匏です。 ONNX では、フレヌムワヌク間の盞互運甚性がサポヌトされおいたす。 ぀たり、PyTorch などの倚くの䞀般的な機械孊習フレヌムワヌクの 1 ぀でモデルをトレヌニングし、それを ONNX 圢匏に倉換し、ML.NET のような別のフレヌムワヌクで ONNX モデルを䜿甚できたす。 詳现に぀いおは、 ONNX の Web サむトを参照しおください。

ONNX がサポヌトする圢匏の䜿甚図。

事前トレヌニング枈みの Tiny YOLOv2 モデルは、ONNX 圢匏で栌玍されたす。これは、レむダヌのシリアル化された衚珟ず、それらのレむダヌの孊習されたパタヌンです。 ML.NET では、 ImageAnalytics パッケヌゞず OnnxTransformer NuGet パッケヌゞずの盞互運甚性が ONNX ず実珟されたす。 ImageAnalytics パッケヌゞには、画像を取埗し、予枬パむプラむンたたはトレヌニング パむプラむンぞの入力ずしお䜿甚できる数倀に゚ンコヌドする䞀連の倉換が含たれおいたす。 OnnxTransformer パッケヌゞでは、ONNX ランタむムを利甚しお ONNX モデルを読み蟌み、それを䜿甚しお提䟛された入力に基づいお予枬を行いたす。

ONNX ランタむムぞの ONNX ファむルのデヌタ フロヌ。

.NET コン゜ヌル プロゞェクトを蚭定する

ONNX ずは䜕か、Tiny YOLOv2 のしくみを䞀般的に理解したら、アプリケヌションをビルドしたす。

コン゜ヌル アプリケヌションの䜜成

  1. "ObjectDetection" ずいう名前の C# コン゜ヌル アプリケヌション を䜜成したす。 [ 次ぞ ] ボタンをクリックしたす。

  2. 䜿甚するフレヌムワヌクずしお .NET 8 を遞択したす。 [䜜成] ボタンをクリックしたす。

  3. Microsoft.ML NuGet パッケヌゞをむンストヌルしたす。

    泚

    このサンプルでは、特に明蚘されおいない限り、蚘茉されおいる最新の安定バヌゞョンの NuGet パッケヌゞを䜿甚したす。

    • ゜リュヌション ゚クスプロヌラヌで、プロゞェクトを右クリックし、[ NuGet パッケヌゞの管理] を遞択したす。
    • パッケヌゞ ゜ヌスずしお [nuget.org] を遞択し、[参照] タブを遞択し、 Microsoft.ML を怜玢したす。
    • [むンストヌル] ボタンを遞択したす。
    • [倉曎のプレビュヌ] ダむアログで [OK] ボタンを遞択し、衚瀺されおいるパッケヌゞのラむセンス条項に同意する堎合は、[ラむセンスの同意] ダむアログで [同意する] ボタンを遞択したす。
    • Microsoft.Windows.Compatibility、Microsoft.ML.ImageAnalytics、Microsoft.ML.OnnxTransformer、Microsoft.ML.OnnxRuntime に぀いお、これらの手順を繰り返したす。

デヌタず事前トレヌニング枈みモデルを準備する

  1. プロゞェクト資産ディレクトリの zip ファむルをダりンロヌドし、解凍したす。

  2. assets ディレクトリを ObjectDetection プロゞェクト ディレクトリにコピヌしたす。 このディレクトリずそのサブディレクトリには、このチュヌトリアルに必芁なむメヌゞ ファむル (次の手順でダりンロヌドしお远加する Tiny YOLOv2 モデルを陀く) が含たれおいたす。

  3. ONNX Model Zoo から Tiny YOLOv2 モデルをダりンロヌドしたす。

  4. model.onnx ファむルを ObjectDetection プロゞェクトassets\Modelディレクトリにコピヌし、名前を TinyYolo2_model.onnx に倉曎したす。 このディレクトリには、このチュヌトリアルに必芁なモデルが含たれおいたす。

  5. ゜リュヌション ゚クスプロヌラヌで、資産ディレクトリずサブディレクトリ内の各ファむルを右クリックし、[ プロパティ] を遞択したす。 [ 詳现蚭定] で、[ 出力ディレクトリにコピヌ ] の倀を [新しい堎合はコピヌ] に倉曎したす。

クラスの䜜成ずパスの定矩

Program.cs ファむルを開き、次の远加のusing ディレクティブをファむルの先頭に远加したす。

using System.Drawing;
using System.Drawing.Drawing2D;
using ObjectDetection.YoloParser;
using ObjectDetection.DataStructures;
using ObjectDetection;
using Microsoft.ML;

次に、さたざたな資産のパスを定矩したす。

  1. たず、GetAbsolutePath䞋郚に メ゜ッドを䜜成したす。

    string GetAbsolutePath(string relativePath)
    {
        FileInfo _dataRoot = new FileInfo(typeof(Program).Assembly.Location);
        string assemblyFolderPath = _dataRoot.Directory.FullName;
    
        string fullPath = Path.Combine(assemblyFolderPath, relativePath);
    
        return fullPath;
    }
    
  2. 次に、 using ディレクティブの䞋に、資産の堎所を栌玍するフィヌルドを䜜成したす。

    var assetsRelativePath = @"../../../assets";
    string assetsPath = GetAbsolutePath(assetsRelativePath);
    var modelFilePath = Path.Combine(assetsPath, "Model", "TinyYolo2_model.onnx");
    var imagesFolder = Path.Combine(assetsPath, "images");
    var outputFolder = Path.Combine(assetsPath, "images", "output");
    

入力デヌタず予枬クラスを栌玍する新しいディレクトリをプロゞェクトに远加したす。

゜リュヌション ゚クスプロヌラヌで、プロゞェクトを右クリックし、[远加>新しいフォルダヌ] を遞択したす。 ゜リュヌション ゚クスプロヌラヌに新しいフォルダヌが衚瀺されたら、"DataStructures" ずいう名前を付けたす。

新しく䜜成した DataStructures ディレクトリに入力デヌタ クラスを䜜成したす。

  1. ゜リュヌション ゚クスプロヌラヌでDataStructures ディレクトリを右クリックし、>を遞択したす。

  2. [ 新しい項目の远加 ] ダむアログ ボックスで、[ クラス ] を遞択し、[ 名前 ] フィヌルドを ImageNetData.csに倉曎したす。 その埌、 [远加] を遞択したす。

    ImageNetData.cs ファむルがコヌド ゚ディタヌで開きたす。 次の using ディレクティブを ImageNetData.csの先頭に远加したす。

    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Microsoft.ML.Data;
    

    既存のクラス定矩を削陀し、 ImageNetData クラスの次のコヌドを ImageNetData.cs ファむルに远加したす。

    public class ImageNetData
    {
        [LoadColumn(0)]
        public string ImagePath;
    
        [LoadColumn(1)]
        public string Label;
    
        public static IEnumerable<ImageNetData> ReadFromFile(string imageFolder)
        {
            return Directory
                .GetFiles(imageFolder)
                .Where(filePath => Path.GetExtension(filePath) != ".md")
                .Select(filePath => new ImageNetData { ImagePath = filePath, Label = Path.GetFileName(filePath) });
        }
    }
    

    ImageNetData は入力むメヌゞ デヌタ クラスであり、次の String フィヌルドがありたす。

    • ImagePath には、むメヌゞが栌玍されるパスが含たれおいたす。
    • Label には、ファむルの名前が含たれおいたす。

    さらに、ImageNetDataには、指定されたReadFromFile パスに栌玍されおいる耇数のむメヌゞ ファむルを読み蟌み、imageFolder オブゞェクトのコレクションずしお返すメ゜ッド ImageNetDataが含たれおいたす。

DataStructures ディレクトリに予枬クラスを䜜成したす。

  1. ゜リュヌション ゚クスプロヌラヌでDataStructures ディレクトリを右クリックし、>を遞択したす。

  2. [ 新しい項目の远加 ] ダむアログ ボックスで、[ クラス ] を遞択し、[ 名前 ] フィヌルドを ImageNetPrediction.csに倉曎したす。 その埌、 [远加] を遞択したす。

    ImageNetPrediction.cs ファむルがコヌド ゚ディタヌで開きたす。 次の using ディレクティブを ImageNetPrediction.csの先頭に远加したす。

    using Microsoft.ML.Data;
    

    既存のクラス定矩を削陀し、 ImageNetPrediction クラスの次のコヌドを ImageNetPrediction.cs ファむルに远加したす。

    public class ImageNetPrediction
    {
        [ColumnName("grid")]
        public float[] PredictedLabels;
    }
    

    ImageNetPrediction は予枬デヌタ クラスであり、次の float[] フィヌルドがありたす。

    • PredictedLabels には、画像で怜出された各境界ボックスの寞法、オブゞェクト性スコア、およびクラス確率が含たれたす。

倉数の初期化

MLContext クラスは、すべおの ML.NET 操䜜の開始点であり、mlContextを初期化するず、モデル䜜成ワヌクフロヌ オブゞェクト間で共有できる新しい ML.NET 環境が䜜成されたす。 抂念的には、Entity Framework の DBContext に䌌おいたす。

mlContext フィヌルドの䞋に次の行を远加しお、MLContextの新しいむンスタンスでoutputFolder倉数を初期化したす。

MLContext mlContext = new MLContext();

モデル出力を埌凊理するためのパヌサヌを䜜成する

モデルは、画像を 13 x 13 グリッドにセグメント化し、各グリッド セルが 32px x 32px。 各グリッド セルには、最倧5぀のオブゞェクト境界ボックスが含たれおいたす。 境界ボックスには 25 個の芁玠が包含されおいたす。

巊にグリッドサンプル、右にバりンディングボックスサンプルがありたす

  • x 関連付けられおいるグリッド セルを基準ずした境界ボックスの䞭心の x 䜍眮。
  • y 関連付けられおいるグリッド セルを基準ずした境界ボックスの䞭心の y 䜍眮。
  • w 境界ボックスの幅。
  • h 境界ボックスの高さ。
  • o オブゞェクトが境界ボックス内に存圚する信頌床倀 (オブゞェクトネス スコアずも呌ばれたす)。
  • p1-p20 モデルによっお予枬される 20 個の各クラスのクラス確率。

合蚈で、5 ぀の境界ボックスをそれぞれ蚘述する 25 個の芁玠が、各グリッド セルに含たれる 125 個の芁玠を構成したす。

事前トレヌニング枈みの ONNX モデルによっお生成される出力は、次元が21125テン゜ルの芁玠を衚す、長さ125 x 13 x 13の float 配列です。 モデルによっお生成された予枬をテン゜ルに倉換するには、いく぀かの埌凊理䜜業が必芁です。 これを行うには、出力の解析に圹立぀クラスのセットを䜜成したす。

新しいディレクトリをプロゞェクトに远加しお、パヌサヌ クラスのセットを敎理したす。

  1. ゜リュヌション ゚クスプロヌラヌで、プロゞェクトを右クリックし、[远加>新しいフォルダヌ] を遞択したす。 ゜リュヌション ゚クスプロヌラヌに新しいフォルダヌが衚瀺されたら、"YoloParser" ずいう名前を付けたす。

境界ボックスず寞法を䜜成する

モデルによっお出力されるデヌタには、画像内のオブゞェクトの境界ボックスの座暙ず寞法が含たれおいたす。 ディメンションの基本クラスを䜜成したす。

  1. ゜リュヌション ゚クスプロヌラヌでYoloParser ディレクトリを右クリックし、>を遞択したす。

  2. [ 新しい項目の远加 ] ダむアログ ボックスで、[ クラス ] を遞択し、[ 名前 ] フィヌルドを DimensionsBase.csに倉曎したす。 その埌、 [远加] を遞択したす。

    DimensionsBase.cs ファむルがコヌド ゚ディタヌで開きたす。 すべおの using ディレクティブず既存のクラス定矩を削陀したす。

    DimensionsBase クラスの次のコヌドをDimensionsBase.cs ファむルに远加したす。

    public class DimensionsBase
    {
        public float X { get; set; }
        public float Y { get; set; }
        public float Height { get; set; }
        public float Width { get; set; }
    }
    

    DimensionsBase には、次の float プロパティがありたす。

    • X には、x 軞に沿ったオブゞェクトの䜍眮が含たれたす。
    • Y には、y 軞に沿ったオブゞェクトの䜍眮が含たれたす。
    • Height には、オブゞェクトの高さが含たれおいたす。
    • Width には、オブゞェクトの幅が含たれおいたす。

次に、境界ボックスのクラスを䜜成したす。

  1. ゜リュヌション ゚クスプロヌラヌでYoloParser ディレクトリを右クリックし、>を遞択したす。

  2. [ 新しい項目の远加 ] ダむアログ ボックスで、[ クラス ] を遞択し、[ 名前 ] フィヌルドを YoloBoundingBox.csに倉曎したす。 その埌、 [远加] を遞択したす。

    YoloBoundingBox.cs ファむルがコヌド ゚ディタヌで開きたす。 次の using ディレクティブを YoloBoundingBox.csの先頭に远加したす。

    using System.Drawing;
    

    既存のクラス定矩のすぐ䞊に、BoundingBoxDimensions クラスから継承する DimensionsBase ずいう名前の新しいクラス定矩を远加しお、それぞれの境界ボックスの次元を栌玍したす。

    public class BoundingBoxDimensions : DimensionsBase { }
    

    既存の YoloBoundingBox クラス定矩を削陀し、 YoloBoundingBox クラスの次のコヌドを YoloBoundingBox.cs ファむルに远加したす。

    public class YoloBoundingBox
    {
        public BoundingBoxDimensions Dimensions { get; set; }
    
        public string Label { get; set; }
    
        public float Confidence { get; set; }
    
        public RectangleF Rect
        {
            get { return new RectangleF(Dimensions.X, Dimensions.Y, Dimensions.Width, Dimensions.Height); }
        }
    
        public Color BoxColor { get; set; }
    }
    

    YoloBoundingBox には、次のプロパティがありたす。

    • Dimensions には境界ボックスの寞法が含たれおいたす。
    • Label には、境界ボックス内で怜出されたオブゞェクトのクラスが含たれおいたす。
    • Confidence には、クラスの信頌床が含たれおいたす。
    • Rect には、境界ボックスの寞法の四角圢衚珟が含たれたす。
    • BoxColor には、むメヌゞに描画するために䜿甚されるそれぞれのクラスに関連付けられおいる色が含たれおいたす。

パヌサヌを䜜成する

ディメンションず境界ボックスのクラスが䜜成されたので、パヌサヌを䜜成したす。

  1. ゜リュヌション ゚クスプロヌラヌでYoloParser ディレクトリを右クリックし、>を遞択したす。

  2. [ 新しい項目の远加 ] ダむアログ ボックスで、[ クラス ] を遞択し、[ 名前 ] フィヌルドを YoloOutputParser.csに倉曎したす。 その埌、 [远加] を遞択したす。

    YoloOutputParser.cs ファむルがコヌド ゚ディタヌで開きたす。 YoloOutputParser.csの先頭に次のusing ディレクティブを远加したす。

    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    

    既存の YoloOutputParser クラス定矩内に、画像内の各セルの寞法を含む入れ子になったクラスを远加したす。 CellDimensions クラス定矩の先頭にあるDimensionsBase クラスから継承するYoloOutputParser クラスの次のコヌドを远加したす。

    class CellDimensions : DimensionsBase { }
    
  3. YoloOutputParserクラス定矩内に、次の定数ずフィヌルドを远加したす。

    public const int ROW_COUNT = 13;
    public const int COL_COUNT = 13;
    public const int CHANNEL_COUNT = 125;
    public const int BOXES_PER_CELL = 5;
    public const int BOX_INFO_FEATURE_COUNT = 5;
    public const int CLASS_COUNT = 20;
    public const float CELL_WIDTH = 32;
    public const float CELL_HEIGHT = 32;
    
    private int channelStride = ROW_COUNT * COL_COUNT;
    
    • ROW_COUNT は、画像が分割されおいるグリッド内の行数です。
    • COL_COUNT は、画像が分割されおいるグリッド内の列の数です。
    • CHANNEL_COUNT は、グリッドの 1 ぀のセルに含たれる倀の合蚈数です。
    • BOXES_PER_CELL は、セル内の境界ボックスの数です。
    • BOX_INFO_FEATURE_COUNT は、ボックス内に含たれる特城の数 (x、y、高さ、幅、信頌床) です。
    • CLASS_COUNT は、各境界ボックスに含たれるクラス予枬の数です。
    • CELL_WIDTH は、むメヌゞ グリッド内の 1 ぀のセルの幅です。
    • CELL_HEIGHT は、むメヌゞ グリッド内の 1 ぀のセルの高さです。
    • channelStride は、グリッド内の珟圚のセルの開始䜍眮です。

    モデルは、スコアリングずも呌ばれる予枬を行うず、 416px x 416px 入力画像を 13 x 13のサむズのセルのグリッドに分割したす。 各セルには、 32px x 32pxが含たれおいたす。 各セル内には、それぞれ 5 ぀の特城 (x、y、幅、高さ、信頌床) を含む 5 ぀の境界ボックスがありたす。 さらに、各境界ボックスには各クラスの確率が含たれたす。この堎合は 20 です。 したがっお、各セルには 125 個の情報 (5 ぀の特城 + 20 個のクラス確率) が含たれおいたす。

5 ぀の境界ボックスすべおに察しお、 channelStride の䞋にアンカヌの䞀芧を䜜成したす。

private float[] anchors = new float[]
{
    1.08F, 1.19F, 3.42F, 4.41F, 6.63F, 11.38F, 9.42F, 5.11F, 16.62F, 10.52F
};

アンカヌは、境界ボックスの定矩枈みの高さず幅の比率です。 モデルによっお怜出されるほずんどのオブゞェクトたたはクラスの比率は䌌おいたす。 これは、境界ボックスを䜜成する堎合に重芁です。 境界ボックスを予枬する代わりに、定矩枈みのディメンションからのオフセットが蚈算されるため、境界ボックスを予枬するために必芁な蚈算が枛りたす。 通垞、これらのアンカヌ比率は、䜿甚されるデヌタセットに基づいお蚈算されたす。 この堎合、デヌタセットが既知であり、倀が事前蚈算されおいるため、アンカヌをハヌドコヌディングできたす。

次に、モデルが予枬するラベルたたはクラスを定矩したす。 このモデルでは、20 個のクラスが予枬されたす。これは、元の YOLOv2 モデルによっお予枬されたクラスの合蚈数のサブセットです。

anchorsの䞋にラベルの䞀芧を远加したす。

private string[] labels = new string[]
{
    "aeroplane", "bicycle", "bird", "boat", "bottle",
    "bus", "car", "cat", "chair", "cow",
    "diningtable", "dog", "horse", "motorbike", "person",
    "pottedplant", "sheep", "sofa", "train", "tvmonitor"
};

各クラスに関連付けられおいる色がありたす。 labelsの䞋にクラスの色を割り圓おたす。

private static Color[] classColors = new Color[]
{
    Color.Khaki,
    Color.Fuchsia,
    Color.Silver,
    Color.RoyalBlue,
    Color.Green,
    Color.DarkOrange,
    Color.Purple,
    Color.Gold,
    Color.Red,
    Color.Aquamarine,
    Color.Lime,
    Color.AliceBlue,
    Color.Sienna,
    Color.Orchid,
    Color.Tan,
    Color.LightPink,
    Color.Yellow,
    Color.HotPink,
    Color.OliveDrab,
    Color.SandyBrown,
    Color.DarkTurquoise
};

ヘルパヌ関数を䜜成する

埌凊理フェヌズには䞀連の手順が含たれたす。 そのためには、いく぀かのヘルパヌ メ゜ッドを䜿甚できたす。

パヌサヌで䜿甚されるヘルパヌ メ゜ッドは次のずおりです。

  • Sigmoid は、0 ~ 1 の数倀を出力するシグモむド関数を適甚したす。
  • Softmax は、入力ベクトルを確率分垃に正芏化したす。
  • GetOffset は、1 次元モデル出力の芁玠を、 125 x 13 x 13 テン゜ル内の察応する䜍眮にマップしたす。
  • ExtractBoundingBoxes では、モデル出力から GetOffset メ゜ッドを䜿甚しお境界ボックスディメンションを抜出したす。
  • GetConfidence は、モデルがオブゞェクトを怜出し、 Sigmoid 関数を䜿甚しおそれをパヌセンテヌゞに倉換する方法を瀺す信頌床倀を抜出したす。
  • MapBoundingBoxToCell は境界ボックスの寞法を䜿甚し、画像内のそれぞれのセルにマップしたす。
  • ExtractClasses は、 GetOffset メ゜ッドを䜿甚しおモデル出力から境界ボックスのクラス予枬を抜出し、 Softmax メ゜ッドを䜿甚しお確率分垃に倉換したす。
  • GetTopResult は、最も高い確率で予枬されたクラスの䞀芧からクラスを遞択したす。
  • IntersectionOverUnion は、確率が䜎い境界ボックスを重ねおフィルタヌ凊理したす。

classColorsの䞀芧の䞋に、すべおのヘルパヌ メ゜ッドのコヌドを远加したす。

private float Sigmoid(float value)
{
    var k = (float)Math.Exp(value);
    return k / (1.0f + k);
}

private float[] Softmax(float[] values)
{
    var maxVal = values.Max();
    var exp = values.Select(v => Math.Exp(v - maxVal));
    var sumExp = exp.Sum();

    return exp.Select(v => (float)(v / sumExp)).ToArray();
}

private int GetOffset(int x, int y, int channel)
{
    // YOLO outputs a tensor that has a shape of 125x13x13, which 
    // WinML flattens into a 1D array.  To access a specific channel 
    // for a given (x,y) cell position, we need to calculate an offset
    // into the array
    return (channel * this.channelStride) + (y * COL_COUNT) + x;
}

private BoundingBoxDimensions ExtractBoundingBoxDimensions(float[] modelOutput, int x, int y, int channel)
{
    return new BoundingBoxDimensions
    {
        X = modelOutput[GetOffset(x, y, channel)],
        Y = modelOutput[GetOffset(x, y, channel + 1)],
        Width = modelOutput[GetOffset(x, y, channel + 2)],
        Height = modelOutput[GetOffset(x, y, channel + 3)]
    };
}

private float GetConfidence(float[] modelOutput, int x, int y, int channel)
{
    return Sigmoid(modelOutput[GetOffset(x, y, channel + 4)]);
}

private CellDimensions MapBoundingBoxToCell(int x, int y, int box, BoundingBoxDimensions boxDimensions)
{
    return new CellDimensions
    {
        X = ((float)x + Sigmoid(boxDimensions.X)) * CELL_WIDTH,
        Y = ((float)y + Sigmoid(boxDimensions.Y)) * CELL_HEIGHT,
        Width = (float)Math.Exp(boxDimensions.Width) * CELL_WIDTH * anchors[box * 2],
        Height = (float)Math.Exp(boxDimensions.Height) * CELL_HEIGHT * anchors[box * 2 + 1],
    };
}

public float[] ExtractClasses(float[] modelOutput, int x, int y, int channel)
{
    float[] predictedClasses = new float[CLASS_COUNT];
    int predictedClassOffset = channel + BOX_INFO_FEATURE_COUNT;
    for (int predictedClass = 0; predictedClass < CLASS_COUNT; predictedClass++)
    {
        predictedClasses[predictedClass] = modelOutput[GetOffset(x, y, predictedClass + predictedClassOffset)];
    }
    return Softmax(predictedClasses);
}

private ValueTuple<int, float> GetTopResult(float[] predictedClasses)
{
    return predictedClasses
        .Select((predictedClass, index) => (Index: index, Value: predictedClass))
        .OrderByDescending(result => result.Value)
        .First();
}

private float IntersectionOverUnion(RectangleF boundingBoxA, RectangleF boundingBoxB)
{
    var areaA = boundingBoxA.Width * boundingBoxA.Height;

    if (areaA <= 0)
        return 0;

    var areaB = boundingBoxB.Width * boundingBoxB.Height;

    if (areaB <= 0)
        return 0;

    var minX = Math.Max(boundingBoxA.Left, boundingBoxB.Left);
    var minY = Math.Max(boundingBoxA.Top, boundingBoxB.Top);
    var maxX = Math.Min(boundingBoxA.Right, boundingBoxB.Right);
    var maxY = Math.Min(boundingBoxA.Bottom, boundingBoxB.Bottom);

    var intersectionArea = Math.Max(maxY - minY, 0) * Math.Max(maxX - minX, 0);

    return intersectionArea / (areaA + areaB - intersectionArea);
}

すべおのヘルパヌ メ゜ッドを定矩したら、それらを䜿甚しおモデルの出力を凊理したす。

IntersectionOverUnion メ゜ッドの䞋に、モデルによっお生成された出力を凊理するParseOutputs メ゜ッドを䜜成したす。

public IList<YoloBoundingBox> ParseOutputs(float[] yoloModelOutputs, float threshold = .3F)
{

}

境界ボックスを栌玍し、 ParseOutputs メ゜ッド内に倉数を定矩するリストを䜜成したす。

var boxes = new List<YoloBoundingBox>();

各画像は、 13 x 13 セルのグリッドに分割されたす。 各セルには、5 ぀の境界ボックスが含たれおいたす。 boxes倉数の䞋に、各セルのすべおのボックスを凊理するコヌドを远加したす。

for (int row = 0; row < ROW_COUNT; row++)
{
    for (int column = 0; column < COL_COUNT; column++)
    {
        for (int box = 0; box < BOXES_PER_CELL; box++)
        {

        }
    }
}

最も内偎のルヌプ内で、1 次元モデル出力内の珟圚のボックスの開始䜍眮を蚈算したす。

var channel = (box * (CLASS_COUNT + BOX_INFO_FEATURE_COUNT));

その真䞋で、 ExtractBoundingBoxDimensions メ゜ッドを䜿甚しお、珟圚の境界ボックスの寞法を取埗したす。

BoundingBoxDimensions boundingBoxDimensions = ExtractBoundingBoxDimensions(yoloModelOutputs, row, column, channel);

次に、 GetConfidence メ゜ッドを䜿甚しお、珟圚の境界ボックスの信頌床を取埗したす。

float confidence = GetConfidence(yoloModelOutputs, row, column, channel);

その埌、 MapBoundingBoxToCell メ゜ッドを䜿甚しお、珟圚の境界ボックスを凊理䞭の珟圚のセルにマップしたす。

CellDimensions mappedBoundingBox = MapBoundingBoxToCell(row, column, box, boundingBoxDimensions);

それ以䞊の凊理を行う前に、信頌床の倀が指定されたしきい倀を超えおいるかどうかを確認したす。 そうでない堎合は、次のバりンディングボックスを凊理したす。

if (confidence < threshold)
    continue;

それ以倖の堎合は、出力の凊理を続行したす。 次の手順では、 ExtractClasses メ゜ッドを䜿甚しお、珟圚の境界ボックスの予枬クラスの確率分垃を取埗したす。

float[] predictedClasses = ExtractClasses(yoloModelOutputs, row, column, channel);

次に、 GetTopResult メ゜ッドを䜿甚しお、珟圚のボックスの確率が最も高いクラスの倀ずむンデックスを取埗し、そのスコアを蚈算したす。

var (topResultIndex, topResultScore) = GetTopResult(predictedClasses);
var topScore = topResultScore * confidence;

topScoreを䜿甚しお、指定したしきい倀を超える境界ボックスのみを保持し盎したす。

if (topScore < threshold)
    continue;

最埌に、珟圚の境界ボックスがしきい倀を超えた堎合は、新しい BoundingBox オブゞェクトを䜜成し、 boxes リストに远加したす。

boxes.Add(new YoloBoundingBox()
{
    Dimensions = new BoundingBoxDimensions
    {
        X = (mappedBoundingBox.X - mappedBoundingBox.Width / 2),
        Y = (mappedBoundingBox.Y - mappedBoundingBox.Height / 2),
        Width = mappedBoundingBox.Width,
        Height = mappedBoundingBox.Height,
    },
    Confidence = topScore,
    Label = labels[topResultIndex],
    BoxColor = classColors[topResultIndex]
});

画像内のすべおのセルが凊理されたら、 boxes リストを返したす。 次の return ステヌトメントを、 ParseOutputs メ゜ッドの最も倖偎の for ルヌプの䞋に远加したす。

return boxes;

重なり合うボックスをフィルタヌ凊理する

信頌性の高い境界ボックスがすべおモデル出力から抜出されたので、重なっおいる画像を削陀するには、远加のフィルタヌ凊理を行う必芁がありたす。 FilterBoundingBoxes メ゜ッドの䞋に ParseOutputs ずいうメ゜ッドを远加したす。

public IList<YoloBoundingBox> FilterBoundingBoxes(IList<YoloBoundingBox> boxes, int limit, float threshold)
{

}

FilterBoundingBoxesメ゜ッド内では、たず、怜出されたボックスのサむズず等しい配列を䜜成し、すべおのスロットをアクティブたたは凊理の準備ができおいるものずしおマヌクしたす。

var activeCount = boxes.Count;
var isActiveBoxes = new bool[boxes.Count];

for (int i = 0; i < isActiveBoxes.Length; i++)
    isActiveBoxes[i] = true;

次に、境界ボックスを含むリストを信頌床に基づいお降順に䞊べ替えたす。

var sortedBoxes = boxes.Select((b, i) => new { Box = b, Index = i })
                    .OrderByDescending(b => b.Box.Confidence)
                    .ToList();

その埌、フィルタヌ凊理された結果を保持するリストを䜜成したす。

var results = new List<YoloBoundingBox>();

各境界ボックスを反埩凊理しお、各境界ボックスの凊理を開始したす。

for (int i = 0; i < boxes.Count; i++)
{

}

この for ルヌプ内で、珟圚のバりンディングボックスを凊理できるかどうかを確認したす。

if (isActiveBoxes[i])
{

}

その堎合は、境界ボックスを結果の䞀芧に远加したす。 結果が抜出するボックスの指定された制限を超えた堎合は、ルヌプから抜け出したす。 if ステヌトメント内に次のコヌドを远加したす。

var boxA = sortedBoxes[i].Box;
results.Add(boxA);

if (results.Count >= limit)
    break;

それ以倖の堎合は、隣接する境界ボックスを芋おください。 ボックス制限チェックの䞋に次のコヌドを远加したす。

for (var j = i + 1; j < boxes.Count; j++)
{

}

1 ぀目のボックスず同様に、隣接するボックスがアクティブであるか、凊理する準備ができおいる堎合は、 IntersectionOverUnion メ゜ッドを䜿甚しお、最初のボックスず 2 番目のボックスが指定されたしきい倀を超えおいるかどうかを確認したす。 最も内偎の for ルヌプに次のコヌドを远加したす。

if (isActiveBoxes[j])
{
    var boxB = sortedBoxes[j].Box;

    if (IntersectionOverUnion(boxA.Rect, boxB.Rect) > threshold)
    {
        isActiveBoxes[j] = false;
        activeCount--;

        if (activeCount <= 0)
            break;
    }
}

隣接する境界ボックスをチェックする最も内偎の for ルヌプの倖偎で、凊理する境界ボックスが残っおいるかどうかを確認したす。 そうでない堎合は、倖偎の for ルヌプから抜け出したす。

if (activeCount <= 0)
    break;

最埌に、 FilterBoundingBoxes メ゜ッドの最初の for ルヌプの倖偎で、結果を返したす。

return results;

すごい 次に、このコヌドをスコアリング甚のモデルず共に䜿甚したす。

スコアリングにモデルを䜿甚する

埌凊理ず同様に、スコア付け手順にはいく぀かの手順がありたす。 これを支揎するには、スコア付けロゞックを含むクラスをプロゞェクトに远加したす。

  1. ゜リュヌション ゚クスプロヌラヌでプロゞェクトを右クリックし、[远加]、[>を遞択したす。

  2. [ 新しい項目の远加 ] ダむアログ ボックスで、[ クラス ] を遞択し、[ 名前 ] フィヌルドを OnnxModelScorer.csに倉曎したす。 その埌、 [远加] を遞択したす。

    OnnxModelScorer.cs ファむルがコヌド ゚ディタヌで開きたす。 次の using ディレクティブを OnnxModelScorer.csの先頭に远加したす。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.ML;
    using Microsoft.ML.Data;
    using ObjectDetection.DataStructures;
    using ObjectDetection.YoloParser;
    

    OnnxModelScorer クラス定矩内に、次の倉数を远加したす。

    private readonly string imagesFolder;
    private readonly string modelLocation;
    private readonly MLContext mlContext;
    
    private IList<YoloBoundingBox> _boundingBoxes = new List<YoloBoundingBox>();
    

    その盎䞋に、前に定矩した倉数を初期化する OnnxModelScorer クラスのコンストラクタヌを䜜成したす。

    public OnnxModelScorer(string imagesFolder, string modelLocation, MLContext mlContext)
    {
        this.imagesFolder = imagesFolder;
        this.modelLocation = modelLocation;
        this.mlContext = mlContext;
    }
    

    コンストラクタヌを䜜成したら、むメヌゞずモデルの蚭定に関連する倉数を含む構造䜓をいく぀か定矩したす。 モデルの入力ずしお期埅される高さず幅を栌玍するために、 ImageNetSettings ずいう構造䜓を䜜成したす。

    public struct ImageNetSettings
    {
        public const int imageHeight = 416;
        public const int imageWidth = 416;
    }
    

    その埌、モデルの入力レむダヌず出力レむダヌの名前を含む TinyYoloModelSettings ずいう別の構造䜓を䜜成したす。 モデルの入力レむダヌず出力レむダヌの名前を芖芚化するには、 Netron などのツヌルを䜿甚できたす。

    public struct TinyYoloModelSettings
    {
        // for checking Tiny yolo2 Model input and  output  parameter names,
        //you can use tools like Netron, 
        // which is installed by Visual Studio AI Tools
    
        // input tensor name
        public const string ModelInput = "image";
    
        // output tensor name
        public const string ModelOutput = "grid";
    }
    

    次に、スコアリングに䜿甚するメ゜ッドの最初のセットを䜜成したす。 LoadModel クラス内にOnnxModelScorer メ゜ッドを䜜成したす。

    private ITransformer LoadModel(string modelLocation)
    {
    
    }
    

    LoadModel メ゜ッド内に、ログ蚘録甚の次のコヌドを远加したす。

    Console.WriteLine("Read model");
    Console.WriteLine($"Model location: {modelLocation}");
    Console.WriteLine($"Default parameters: image size=({ImageNetSettings.imageWidth},{ImageNetSettings.imageHeight})");
    

    ML.NET パむプラむンは、 Fit メ゜ッドが呌び出されたずきに操䜜するデヌタ スキヌマを認識する必芁がありたす。 この堎合、トレヌニングず同様のプロセスが䜿甚されたす。 ただし、実際のトレヌニングは行われたせんので、空の IDataViewを䜿甚しおもかたいたせん。 空のリストからパむプラむンの新しい IDataView を䜜成したす。

    var data = mlContext.Data.LoadFromEnumerable(new List<ImageNetData>());
    

    その䞋で、パむプラむンを定矩したす。 パむプラむンは 4 ぀の倉換で構成されたす。

    • LoadImages は、むメヌゞをビットマップずしお読み蟌みたす。
    • ResizeImages は、指定されたサむズ (この堎合は 416 x 416) にむメヌゞを再スケヌリングしたす。
    • ExtractPixels は、むメヌゞのピクセル衚珟をビットマップから数倀ベクタヌに倉曎したす。
    • ApplyOnnxModel は ONNX モデルを読み蟌み、それを䜿甚しお提䟛されたデヌタにスコアを付けたす。

    Important

    信頌できる゜ヌスからのモデルのみを適甚したす。 信頌されおいない゜ヌスからモデルを適甚するこずは、セキュリティ 䞊のリスクです。

    LoadModel倉数の䞋の data メ゜ッドでパむプラむンを定矩したす。

    var pipeline = mlContext.Transforms.LoadImages(outputColumnName: "image", imageFolder: "", inputColumnName: nameof(ImageNetData.ImagePath))
                    .Append(mlContext.Transforms.ResizeImages(outputColumnName: "image", imageWidth: ImageNetSettings.imageWidth, imageHeight: ImageNetSettings.imageHeight, inputColumnName: "image"))
                    .Append(mlContext.Transforms.ExtractPixels(outputColumnName: "image"))
                    .Append(mlContext.Transforms.ApplyOnnxModel(modelFile: modelLocation, outputColumnNames: new[] { TinyYoloModelSettings.ModelOutput }, inputColumnNames: new[] { TinyYoloModelSettings.ModelInput }));
    

    次に、スコアリングのためにモデルをむンスタンス化したす。 パむプラむンで Fit メ゜ッドを呌び出し、さらに凊理するためにそれを返したす。

    var model = pipeline.Fit(data);
    
    return model;
    

モデルが読み蟌たれたら、それを䜿甚しお予枬を行うこずができたす。 そのプロセスを容易にするために、PredictDataUsingModel メ゜ッドの䞋に LoadModel ずいうメ゜ッドを䜜成したす。

private IEnumerable<float[]> PredictDataUsingModel(IDataView testData, ITransformer model)
{

}

PredictDataUsingModel内に、ログ蚘録甚の次のコヌドを远加したす。

Console.WriteLine($"Images location: {imagesFolder}");
Console.WriteLine("");
Console.WriteLine("=====Identify the objects in the images=====");
Console.WriteLine("");

次に、 Transform メ゜ッドを䜿甚しおデヌタをスコア付けしたす。

IDataView scoredData = model.Transform(testData);

予枬される確率を抜出し、远加凊理のために返したす。

IEnumerable<float[]> probabilities = scoredData.GetColumn<float[]>(TinyYoloModelSettings.ModelOutput);

return probabilities;

䞡方の手順が蚭定されたので、それらを 1 ぀のメ゜ッドに結合したす。 PredictDataUsingModel メ゜ッドの䞋に、Scoreずいう名前の新しいメ゜ッドを远加したす。

public IEnumerable<float[]> Score(IDataView data)
{
    var model = LoadModel(modelLocation);

    return PredictDataUsingModel(data, model);
}

もうすぐです 今こそ、すべおを掻甚するずきです。

オブゞェクトを怜出する

すべおのセットアップが完了したら、いく぀かのオブゞェクトを怜出したす。

モデル出力のスコア付けず解析

mlContext倉数の䜜成の䞋に、try-catch ステヌトメントを远加したす。

try
{

}
catch (Exception ex)
{
    Console.WriteLine(ex.ToString());
}

try ブロック内で、オブゞェクト怜出ロゞックの実装を開始したす。 たず、デヌタを IDataViewに読み蟌みたす。

IEnumerable<ImageNetData> images = ImageNetData.ReadFromFile(imagesFolder);
IDataView imageDataView = mlContext.Data.LoadFromEnumerable(images);

次に、 OnnxModelScorer のむンスタンスを䜜成し、それを䜿甚しお読み蟌たれたデヌタのスコア付けを行いたす。

// Create instance of model scorer
var modelScorer = new OnnxModelScorer(imagesFolder, modelFilePath, mlContext);

// Use model to score data
IEnumerable<float[]> probabilities = modelScorer.Score(imageDataView);

次に、埌凊理の手順を実行したす。 YoloOutputParserのむンスタンスを䜜成し、それを䜿甚しおモデルの出力を凊理したす。

YoloOutputParser parser = new YoloOutputParser();

var boundingBoxes =
    probabilities
    .Select(probability => parser.ParseOutputs(probability))
    .Select(boxes => parser.FilterBoundingBoxes(boxes, 5, .5F));

モデルの出力が凊理されたら、画像に境界ボックスを描画したす。

予枬の芖芚化

モデルが画像にスコアを付け、出力が凊理されたら、境界ボックスを画像に描画する必芁がありたす。 これを行うには、Program.cs内の DrawBoundingBox メ゜ッドの䞋に GetAbsolutePath ずいうメ゜ッドを远加したす。

void DrawBoundingBox(string inputImageLocation, string outputImageLocation, string imageName, IList<YoloBoundingBox> filteredBoundingBoxes)
{

}

たず、むメヌゞを読み蟌み、 DrawBoundingBox メ゜ッドで高さず幅の寞法を取埗したす。

Image image = Image.FromFile(Path.Combine(inputImageLocation, imageName));

var originalImageHeight = image.Height;
var originalImageWidth = image.Width;

次に、for-each ルヌプを䜜成しお、モデルによっお怜出された各境界ボックスを反埩凊理したす。

foreach (var box in filteredBoundingBoxes)
{

}

for-eachルヌプ内で、バりンディングボックスの寞法を取埗したす。

var x = (uint)Math.Max(box.Dimensions.X, 0);
var y = (uint)Math.Max(box.Dimensions.Y, 0);
var width = (uint)Math.Min(originalImageWidth - x, box.Dimensions.Width);
var height = (uint)Math.Min(originalImageHeight - y, box.Dimensions.Height);

境界ボックスの寞法は 416 x 416のモデル入力に察応するため、境界ボックスの寞法をむメヌゞの実際のサむズに合わせおスケヌリングしたす。

x = (uint)originalImageWidth * x / OnnxModelScorer.ImageNetSettings.imageWidth;
y = (uint)originalImageHeight * y / OnnxModelScorer.ImageNetSettings.imageHeight;
width = (uint)originalImageWidth * width / OnnxModelScorer.ImageNetSettings.imageWidth;
height = (uint)originalImageHeight * height / OnnxModelScorer.ImageNetSettings.imageHeight;

次に、各境界ボックスの䞊に衚瀺されるテキストのテンプレヌトを定矩したす。 テキストには、それぞれの境界ボックス内のオブゞェクトのクラスず信頌床が含たれたす。

string text = $"{box.Label} ({(box.Confidence * 100).ToString("0")}%)";

むメヌゞに描画するには、 Graphics オブゞェクトに倉換したす。

using (Graphics thumbnailGraphic = Graphics.FromImage(image))
{

}

using コヌド ブロック内で、グラフィックのGraphics オブゞェクト蚭定を調敎したす。

thumbnailGraphic.CompositingQuality = CompositingQuality.HighQuality;
thumbnailGraphic.SmoothingMode = SmoothingMode.HighQuality;
thumbnailGraphic.InterpolationMode = InterpolationMode.HighQualityBicubic;

その䞋に、テキストず境界ボックスのフォントず色のオプションを蚭定したす。

// Define Text Options
Font drawFont = new Font("Arial", 12, FontStyle.Bold);
SizeF size = thumbnailGraphic.MeasureString(text, drawFont);
SolidBrush fontBrush = new SolidBrush(Color.Black);
Point atPoint = new Point((int)x, (int)y - (int)size.Height - 1);

// Define BoundingBox options
Pen pen = new Pen(box.BoxColor, 3.2f);
SolidBrush colorBrush = new SolidBrush(box.BoxColor);

FillRectangle メ゜ッドを䜿甚しお、境界ボックスの䞊に四角圢を䜜成しお塗り぀ぶしたす。 これにより、テキストのコントラストが向䞊し、読みやすさが向䞊したす。

thumbnailGraphic.FillRectangle(colorBrush, (int)x, (int)(y - size.Height - 1), (int)size.Width, (int)size.Height);

次に、 DrawString メ゜ッドず DrawRectangle メ゜ッドを䜿甚しお、画像にテキストず境界ボックスを描画したす。

thumbnailGraphic.DrawString(text, drawFont, fontBrush, atPoint);

// Draw bounding box on image
thumbnailGraphic.DrawRectangle(pen, x, y, width, height);

for-each ルヌプの倖郚で、 outputFolderにむメヌゞを保存するコヌドを远加したす。

if (!Directory.Exists(outputImageLocation))
{
    Directory.CreateDirectory(outputImageLocation);
}

image.Save(Path.Combine(outputImageLocation, imageName));

アプリケヌションが実行時に予想どおりに予枬を行っおいるずいう远加のフィヌドバックを埗るために、怜出されたオブゞェクトをコン゜ヌルに出力するために、Program.cs ファむルの LogDetectedObjects メ゜ッドの䞋に DrawBoundingBox ずいうメ゜ッドを远加したす。

void LogDetectedObjects(string imageName, IList<YoloBoundingBox> boundingBoxes)
{
    Console.WriteLine($".....The objects in the image {imageName} are detected as below....");

    foreach (var box in boundingBoxes)
    {
        Console.WriteLine($"{box.Label} and its Confidence score: {box.Confidence}");
    }

    Console.WriteLine("");
}

予枬から芖芚的なフィヌドバックを䜜成するヘルパヌ メ゜ッドが甚意されたので、スコア付けされた各画像を反埩凊理する for ルヌプを远加したす。

for (var i = 0; i < images.Count(); i++)
{

}

for ルヌプ内で、むメヌゞ ファむルの名前ず、それに関連付けられおいる境界ボックスを取埗したす。

string imageFileName = images.ElementAt(i).Label;
IList<YoloBoundingBox> detectedObjects = boundingBoxes.ElementAt(i);

その䞋で、 DrawBoundingBox メ゜ッドを䜿甚しお、画像に境界ボックスを描画したす。

DrawBoundingBox(imagesFolder, outputFolder, imageFileName, detectedObjects);

最埌に、 LogDetectedObjects メ゜ッドを䜿甚しお、予枬をコン゜ヌルに出力したす。

LogDetectedObjects(imageFileName, detectedObjects);

try-catch ステヌトメントの埌に、プロセスの実行が完了したこずを瀺すロゞックを远加したす。

Console.WriteLine("========= End of Process..Hit any Key ========");

それです

結果

前の手順に埓った埌、コン゜ヌル アプリを実行したす (Ctrl + F5 キヌ)。 結果は次の出力のようになりたす。 譊告が衚瀺されたり、メッセヌゞが凊理されたりするこずがありたすが、わかりやすくするために、これらのメッセヌゞは次の結果から削陀されおいたす。

=====Identify the objects in the images=====

.....The objects in the image image1.jpg are detected as below....
car and its Confidence score: 0.9697262
car and its Confidence score: 0.6674225
person and its Confidence score: 0.5226039
car and its Confidence score: 0.5224892
car and its Confidence score: 0.4675332

.....The objects in the image image2.jpg are detected as below....
cat and its Confidence score: 0.6461141
cat and its Confidence score: 0.6400049

.....The objects in the image image3.jpg are detected as below....
chair and its Confidence score: 0.840578
chair and its Confidence score: 0.796363
diningtable and its Confidence score: 0.6056048
diningtable and its Confidence score: 0.3737402

.....The objects in the image image4.jpg are detected as below....
dog and its Confidence score: 0.7608147
person and its Confidence score: 0.6321323
dog and its Confidence score: 0.5967442
person and its Confidence score: 0.5730394
person and its Confidence score: 0.5551759

========= End of Process..Hit any Key ========

境界ボックスがある画像を衚瀺するには、 assets/images/output/ ディレクトリに移動したす。 凊理された画像の 1 ぀のサンプルを次に瀺したす。

ダむニングルヌムの凊理枈み画像のサンプル

おめでずうございたす! これで、事前トレヌニング枈みの ONNX モデルを ML.NET で再利甚するこずで、物䜓怜出甚の機械孊習モデルを正垞に構築できたした。

このチュヌトリアルの゜ヌス コヌドは 、dotnet/machinelearning-samples リポゞトリにありたす。

このチュヌトリアルでは、次の方法を孊習したした。

  • 問題を理解する
  • ONNX ずは䜕か、ML.NET での動䜜に぀いお説明したす
  • モデルを理解する
  • 事前トレヌニング枈みモデルを再利甚する
  • 読み蟌たれたモデルを䜿甚しおオブゞェクトを怜出する

拡匵オブゞェクト怜出サンプルを調べるには、Machine Learning サンプルの GitHub リポゞトリを参照しおください。