Tutorial: Erkennen von Objekten mithilfe von ONNX in ML.NETTutorial: Detect objects using ONNX in ML.NET

Erfahren Sie, wie Sie ein vortrainiertes ONNX-Modell in ML.NET verwenden, um Objekte in Bildern zu erkennen.Learn how to use a pre-trained ONNX model in ML.NET to detect objects in images.

Das von Grund auf neue Trainieren eines Objekterkennungsmodells erfordert das Festlegen von Millionen von Parametern, zahlreiche bezeichnete Trainingsdaten und eine große Menge an Computeressourcen (Hunderte von GPU-Stunden).Training an object detection model from scratch requires setting millions of parameters, a large amount of labeled training data and a vast amount of compute resources (hundreds of GPU hours). Die Verwendung eines vortrainierten Modells ermöglicht es Ihnen, den Trainingsprozess zu verkürzen.Using a pre-trained model allows you to shortcut the training process.

In diesem Tutorial lernen Sie, wie die folgenden Aufgaben ausgeführt werden:In this tutorial, you learn how to:

  • Das Problem verstehenUnderstand the problem
  • Erfahren Sie, was ONNX ist und wie ONNX mit ML.NET funktioniert.Learn what ONNX is and how it works with ML.NET
  • Verstehen des ModellsUnderstand the model
  • Wiederverwenden des vortrainierten ModellsReuse the pre-trained model
  • Erkennen von Objekten mit einem geladenen ModellDetect objects with a loaded model

VoraussetzungenPre-requisites

ONNX-Objekterkennungsbeispiel: ÜbersichtONNX object detection sample overview

Dieses Beispiel erstellt eine .NET Core-Konsolenanwendung, die Objekte in einem Bild mithilfe eines vortrainierten Deep Learning ONNX-Modells erkennt.This sample creates a .NET core console application that detects objects within an image using a pre-trained deep learning ONNX model. Den Code für dieses Beispiel finden Sie im dotnet/machinelearning-Beispielrepository auf GitHub.The code for this sample can be found on the dotnet/machinelearning-samples repository on GitHub.

Was ist Objekterkennung?What is object detection?

Objekterkennung ist ein Problem des maschinellen Sehens.Object detection is a computer vision problem. Obwohl die Objekterkennung eng mit der Bildklassifizierung verwandt ist, führt sie die Bildklassifizierung auf einer detaillierteren Ebene durch.While closely related to image classification, object detection performs image classification at a more granular scale. Die Objekterkennung ermittelt und kategorisiert Entitäten in Bildern.Object detection both locates and categorizes entities within images. Verwenden Sie die Objekterkennung, wenn Bilder mehrere Objekte verschiedener Typen enthalten.Use object detection when images contain multiple objects of different types.

Screenshots zum Vergleich der Bildklassifizierung mit der Objektklassifizierung

Einige Anwendungsfälle für die Objekterkennung sind:Some use cases for object detection include:

  • Autonomes FahrenSelf-Driving Cars
  • RobotikRobotics
  • GesichtserfassungFace Detection
  • ArbeitsplatzsicherheitWorkplace Safety
  • ObjektzählungObject Counting
  • AktivitätserkennungActivity Recognition

Auswählen eines Deep Learning-ModellsSelect a deep learning model

Deep Learning ist eine Teilmenge von Machine Learning.Deep learning is a subset of machine learning. Zum Trainieren von Deep Learning-Modellen sind große Mengen von Daten erforderlich.To train deep learning models, large quantities of data are required. Muster in den Daten werden durch eine Reihe von Schichten dargestellt.Patterns in the data are represented by a series of layers. Die Beziehungen in den Daten werden als Verbindungen zwischen den Schichten mit Gewichtungen codiert.The relationships in the data are encoded as connections between the layers containing weights. Je höher die Gewichtung, desto stärker die Beziehung.The higher the weight, the stronger the relationship. Zusammen werden diese Schichten und Verbindungen als künstliche neuronale Netze bezeichnet.Collectively, this series of layers and connections are known as artificial neural networks. Je mehr Schichten in einem Netzwerk vorhanden sind, desto „tiefer“ ist es, was es zu einem Deep Neural Network macht.The more layers in a network, the "deeper" it is, making it a deep neural network.

Es gibt verschiedene Arten von neuronalen Netzen, wobei die häufigsten MLP (Multi-Layered Perceptron), CNN (Convolutional Neural Network) und RNN (Recurrent Neural Network) sind.There are different types of neural networks, the most common being Multi-Layered Perceptron (MLP), Convolutional Neural Network (CNN) and Recurrent Neural Network (RNN). Das einfachste neuronale Netz ist MLP, das eine Reihe von Eingaben einem Satz von Ausgaben zuordnet.The most basic is the MLP, which maps a set of inputs to a set of outputs. Dieses neuronale Netz eignet sich gut, wenn die Daten nicht über eine räumliche oder zeitliche Komponente verfügen.This neural network is good when the data does not have a spatial or time component. Das CNN nutzt Faltungsschichten, um die in den Daten enthaltenen räumlichen Informationen zu verarbeiten.The CNN makes use of convolutional layers to process spatial information contained in the data. Ein guter Anwendungsfall für CNNs ist die Bildverarbeitung, um das Vorhandensein eines Merkmals in einer Region eines Bilds zu erkennen (Beispiel: befindet sich eine Nase in der Mitte eines Bilds?).A good use case for CNNs is image processing to detect the presence of a feature in a region of an image (for example, is there a nose in the center of an image?). RNNs schließlich ermöglichen die Verwendung von Persistenz von Zustand oder Speicher als Eingabe.Finally, RNNs allow for the persistence of state or memory to be used as input. RNNs werden für die Zeitreihenanalyse verwendet, bei der die sequenzielle Reihenfolge und der Kontext der Ereignisse wichtig sind.RNNs are used for time-series analysis, where the sequential ordering and context of events is important.

Verstehen des ModellsUnderstand the model

Objekterkennung ist eine Bildverarbeitungsaufgabe.Object detection is an image processing task. Daher sind die meisten Deep Learning-Modelle, die zur Lösung dieses Problems trainiert werden, CNNs.Therefore, most deep learning models trained to solve this problem are CNNs. Das in diesem Tutorial verwendete Modell ist das Tiny YOLOv2-Modell, eine kompaktere Version des YOLOv2-Modells, das im folgenden Dokument beschrieben wird: „YOLO9000: Better, Faster, Stronger“ von Redmon und Fadhari.The model used in this tutorial is the Tiny YOLOv2 model, a more compact version of the YOLOv2 model described in the paper: "YOLO9000: Better, Faster, Stronger" by Redmon and Fadhari. Tiny YOLOv2 wird mit dem Pascal VOC-Dataset trainiert und besteht aus 15 Schichten, die 20 verschiedene Klassen von Objekten vorhersagen können.Tiny YOLOv2 is trained on the Pascal VOC dataset and is made up of 15 layers that can predict 20 different classes of objects. Da Tiny YOLOv2 eine komprimierte Version des ursprünglichen YOLOv2-Modells ist, wird ein Kompromiss zwischen Geschwindigkeit und Genauigkeit erzielt.Because Tiny YOLOv2 is a condensed version of the original YOLOv2 model, a tradeoff is made between speed and accuracy. Die verschiedenen Schichten, die das Modell bilden, können mithilfe von Tools wie etwa Netron visualisiert werden.The different layers that make up the model can be visualized using tools like Netron. Die Untersuchung des Modells würde eine Zuordnung der Verbindungen zwischen allen Schichten ergeben, aus denen sich das neuronale Netz zusammensetzt, wobei jede Schicht den Namen der Schicht zusammen mit den Dimensionen der jeweiligen Ein- bzw. Ausgabe enthalten würde.Inspecting the model would yield a mapping of the connections between all the layers that make up the neural network, where each layer would contain the name of the layer along with the dimensions of the respective input / output. Die Datenstrukturen, die zum Beschreiben der Eingaben und Ausgaben des Modells verwendet werden, werden als Tensoren bezeichnet.The data structures used to describe the inputs and outputs of the model are known as tensors. Tensoren können als Container betrachtet werden, in denen Daten in N Dimensionen gespeichert werden.Tensors can be thought of as containers that store data in N-dimensions. Im Fall von Tiny YOLOv2 ist der Name der Eingabeschicht image, und es wird ein Tensor mit den Dimensionen 3 x 416 x 416 erwartet.In the case of Tiny YOLOv2, the name of the input layer is image and it expects a tensor of dimensions 3 x 416 x 416. Der Name der Ausgabeschicht ist grid, und es wird ein Tensor mit den Dimensionen 125 x 13 x 13 generiert.The name of the output layer is grid and generates an output tensor of dimensions 125 x 13 x 13.

Die Eingabeschicht wird in verborgene Schichten aufgeteilt und führt zur Ausgabeschicht.

Das Yolo-Modell verwendet ein Bild mit 3(RGB) x 416px x 416px.The YOLO model takes an image 3(RGB) x 416px x 416px. Das Modell nimmt diese Eingabe an und übergibt sie durch die verschiedenen Schichten, um eine Ausgabe zu generieren.The model takes this input and passes it through the different layers to produce an output. Die Ausgabe teilt das Eingabebild in ein 13 x 13-Raster auf, wobei jede Zelle im Raster aus 125 Werten besteht.The output divides the input image into a 13 x 13 grid, with each cell in the grid consisting of 125 values.

Was ist ein ONNX-Modell?What is an ONNX model?

ONNX (Open Neural Network Exchange) ist ein Open-Source-Format für KI-Modelle.The Open Neural Network Exchange (ONNX) is an open source format for AI models. ONNX unterstützt die Interoperabilität zwischen Frameworks.ONNX supports interoperability between frameworks. Das bedeutet, dass Sie ein Modell in einem der vielen gängigen Machine Learning-Frameworks wie PyTorch trainieren, es in das ONNX-Format konvertieren und das ONNX-Modell in einem anderen Framework wie ML.NET verwenden können.This means you can train a model in one of the many popular machine learning frameworks like PyTorch, convert it into ONNX format and consume the ONNX model in a different framework like ML.NET. Weitere Informationen finden Sie auf der ONNX-Website.To learn more, visit the ONNX website.

Diagramm der verwendeten unterstützten ONNX-Formate

Das vortrainierte Tiny YOLOv2-Modell wird im ONNX-Format gespeichert, einer serialisierten Darstellung der Schichten und erlernten Muster dieser Schichten.The pre-trained Tiny YOLOv2 model is stored in ONNX format, a serialized representation of the layers and learned patterns of those layers. In ML.NET wird die Interoperabilität mit ONNX mit den NuGet-Paketen ImageAnalytics und OnnxTransformer erreicht.In ML.NET, interoperability with ONNX is achieved with the ImageAnalytics and OnnxTransformer NuGet packages. Das Paket ImageAnalytics enthält eine Reihe von Transformationen, die ein Bild annehmen und in numerische Werte codieren, die als Eingabe in eine Vorhersage- oder Trainingspipeline verwendet werden können.The ImageAnalytics package contains a series of transforms that take an image and encode it into numerical values that can be used as input into a prediction or training pipeline. Das Paket OnnxTransformer nutzt die ONNX Runtime, um ein ONNX-Modell zu laden und damit Vorhersagen basierend auf bereitgestellten Eingaben zu treffen.The OnnxTransformer package leverages the ONNX Runtime to load an ONNX model and use it to make predictions based on input provided.

Datenfluss der ONNX-Datei in die ONNX-Runtime

Einrichten des .NET Core-ProjektsSet up the .NET Core project

Da Sie nun ein allgemeines Verständnis davon haben, was ONNX ist und wie Tiny YOLOv2 funktioniert, ist es an der Zeit, die Anwendung zu erstellen.Now that you have a general understanding of what ONNX is and how Tiny YOLOv2 works, it's time to build the application.

Erstellen einer KonsolenanwendungCreate a console application

  1. Erstellen Sie eine .NET Core-Konsolenanwendung mit dem Namen „ObjectDetection“.Create a .NET Core Console Application called "ObjectDetection".

  2. Installieren des Microsoft.ML NuGet-Pakets:Install the Microsoft.ML NuGet Package:

    • Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen Sie NuGet-Pakete verwalten aus.In Solution Explorer, right-click on your project and select Manage NuGet Packages.
    • Wählen Sie „nuget.org“ als Paketquelle aus, wählen Sie die Registerkarte „Durchsuchen“ aus, und suchen Sie nach Microsoft.ML.Choose "nuget.org" as the Package source, select the Browse tab, search for Microsoft.ML.
    • Wählen Sie die Schaltfläche Installieren aus.Select the Install button.
    • Wählen Sie die Schaltfläche OK im Dialogfeld Vorschau der Änderungen und dann die Schaltfläche Ich stimme zu im Dialogfeld Zustimmung zur Lizenz aus, wenn Sie den Lizenzbedingungen für die aufgelisteten Pakete zustimmen.Select the OK button on the Preview Changes dialog and then select the I Accept button on the License Acceptance dialog if you agree with the license terms for the packages listed.
    • Wiederholen Sie diese Schritte für Microsoft.ML.ImageAnalytics und Microsoft.ML.OnnxTransformer.Repeat these steps for Microsoft.ML.ImageAnalytics and Microsoft.ML.OnnxTransformer.

Vorbereiten der Daten und des vortrainierten ModellsPrepare your data and pre-trained model

  1. Laden Sie die ZIP-Datei aus dem Projektressourcenverzeichnis herunter, und entzippen Sie sie.Download The project assets directory zip file and unzip.

  2. Kopieren Sie das Verzeichnis assets in Ihr ObjectDetection-Projektverzeichnis.Copy the assets directory into your ObjectDetection project directory. Dieses Verzeichnis und seine Unterverzeichnisse enthalten die für dieses Tutorial erforderlichen Bilddateien (mit Ausnahme des Tiny YOLOv2-Modells, das Sie im nächsten Schritt herunterladen und hinzufügen).This directory and its subdirectories contain the image files (except for the Tiny YOLOv2 model, which you'll download and add in the next step) needed for this tutorial.

  3. Laden Sie das Tiny YOLOv2-Modell aus dem ONNX Model Zoo herunter, und entzippen Sie es.Download the Tiny YOLOv2 model from the ONNX Model Zoo, and unzip.

    Öffnen Sie die Eingabeaufforderung, und geben Sie den folgenden Befehl ein:Open the command prompt and enter the following command:

    tar -xvzf tiny_yolov2.tar.gz
    
  4. Kopieren Sie die extrahierte Datei model.onnx aus dem soeben entzippten Verzeichnis in das Verzeichnis assets\Model Ihres ObjectDetection-Projekts, und benennen Sie sie in TinyYolo2_model.onnx um.Copy the extracted model.onnx file from the directory just unzipped into your ObjectDetection project assets\Model directory and rename it to TinyYolo2_model.onnx. Dieses Verzeichnis enthält das für dieses Tutorial erforderliche Modell.This directory contains the model needed for this tutorial.

  5. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf alle Dateien im Assetverzeichnis und den Unterverzeichnissen, und wählen Sie Eigenschaften aus.In Solution Explorer, right-click each of the files in the asset directory and subdirectories and select Properties. Ändern Sie unter Erweitert den Wert von In Ausgabeverzeichnis kopieren in Kopieren, wenn neuer.Under Advanced, change the value of Copy to Output Directory to Copy if newer.

Erstellen von Klassen und Definieren von PfadenCreate classes and define paths

Öffnen Sie die Datei Program.cs, und fügen Sie am Anfang der Datei folgende zusätzliche using-Anweisungen hinzu:Open the Program.cs file and add the following additional using statements to the top of the file:

using System;
using System.IO;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using Microsoft.ML;

Definieren Sie als nächstes die Pfade der verschiedenen Ressourcen.Next, define the paths of the various assets.

  1. Fügen Sie zunächst die GetAbsolutePath-Methode unter der Main-Methode in der Program-Klasse hinzu.First, add the GetAbsolutePath method below the Main method in the Program class.

    public static 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. Erstellen Sie dann in der Methode Main Felder, um den Speicherort Ihrer Ressourcen zu speichern.Then, inside the Main method, create fields to store the location of your assets.

    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");
    

Fügen Sie Ihrem Projekt ein neues Verzeichnis hinzu, um die Eingabedaten und Vorhersageklassen zu speichern.Add a new directory to your project to store your input data and prediction classes.

Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen Sie dann Hinzufügen > Neuer Ordner aus.In Solution Explorer, right-click the project, and then select Add > New Folder. Wenn der neue Ordner im Projektmappen-Explorer angezeigt wird, nennen Sie ihn „DataStructures“.When the new folder appears in the Solution Explorer, name it "DataStructures".

Erstellen Sie die Eingabedatenklasse im neu erstellten Verzeichnis DataStructures.Create your input data class in the newly created DataStructures directory.

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Verzeichnis DataStructures, und wählen Sie dann Hinzufügen > Neues Element aus.In Solution Explorer, right-click the DataStructures directory, and then select Add > New Item.

  2. Wählen Sie im Dialogfeld Neues Element hinzufügen die Option Klasse aus, und ändern Sie das Feld Name in ImageNetData.cs.In the Add New Item dialog box, select Class and change the Name field to ImageNetData.cs. Wählen Sie dann die Schaltfläche Hinzufügen aus.Then, select the Add button.

    Die Datei ImageNetData.cs wird im Code-Editor geöffnet.The ImageNetData.cs file opens in the code editor. Fügen Sie die folgende using-Anweisung am Anfang der Datei ImageNetData.cs hinzu:Add the following using statement to the top of ImageNetData.cs:

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

    Entfernen Sie die vorhandene Klassendefinition, und fügen Sie der Datei ImageNetData.cs den folgenden Code für die ImageNetData-Klasse hinzu:Remove the existing class definition and add the following code for the ImageNetData class to the ImageNetData.cs file:

    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 ist die Eingabebilddaten-Klasse mit den folgenden String-Feldern:ImageNetData is the input image data class and has the following String fields:

    • ImagePath enthält den Pfad, in dem das Bild gespeichert ist.ImagePath contains the path where the image is stored.
    • Label enthält den Namen der Datei.Label contains the name of the file.

    Darüber hinaus enthält ImageNetData eine Methode ReadFromFile, die mehrere Bilddateien lädt, die im angegebenen Pfad imageFolder gespeichert sind, und sie als eine Sammlung von ImageNetData-Objekten zurückgibt.Additionally, ImageNetData contains a method ReadFromFile which loads multiple image files stored in the imageFolder path specified and returns them as a collection of ImageNetData objects.

Erstellen Sie die Vorhersageklasse im Verzeichnis DataStructures.Create your prediction class in the DataStructures directory.

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Verzeichnis DataStructures, und wählen Sie dann Hinzufügen > Neues Element aus.In Solution Explorer, right-click the DataStructures directory, and then select Add > New Item.

  2. Wählen Sie im Dialogfeld Neues Element hinzufügen die Option Klasse aus, und ändern Sie das Feld Name in ImageNetPrediction.cs.In the Add New Item dialog box, select Class and change the Name field to ImageNetPrediction.cs. Wählen Sie dann die Schaltfläche Hinzufügen aus.Then, select the Add button.

    Die Datei ImageNetPrediction.cs wird im Code-Editor geöffnet.The ImageNetPrediction.cs file opens in the code editor. Fügen Sie die folgende using-Anweisung am Anfang der Datei ImageNetPrediction.cs hinzu:Add the following using statement to the top of ImageNetPrediction.cs:

    using Microsoft.ML.Data;
    

    Entfernen Sie die vorhandene Klassendefinition, und fügen Sie der Datei ImageNetPrediction.cs den folgenden Code für die ImageNetPrediction-Klasse hinzu:Remove the existing class definition and add the following code for the ImageNetPrediction class to the ImageNetPrediction.cs file:

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

    ImageNetPrediction ist die Vorhersagedatenklasse und weist das folgende float[]-Feld auf:ImageNetPrediction is the prediction data class and has the following float[] field:

    • PredictedLabel enthält die Dimensionen, die Objektbewertung und die Klassenwahrscheinlichkeiten für jeden der Begrenzungsrahmen, der in einem Bild erkannt wird.PredictedLabel contains the dimensions, objectness score and class probabilities for each of the bounding boxes detected in an image.

Initialisieren von Variablen in MainInitialize variables in Main

Die MLContext-Klasse ist der Startpunkt für alle ML.NET-Vorgänge. Durch das Initialisieren von mlContext wird eine neue ML.NET-Umgebung erstellt, die für mehrere Objekte des Modellerstellungsworkflows verwendet werden kann.The MLContext class is a starting point for all ML.NET operations, and initializing mlContext creates a new ML.NET environment that can be shared across the model creation workflow objects. Die Klasse ähnelt dem Konzept von DBContext in Entity Framework.It's similar, conceptually, to DBContext in Entity Framework.

Initialisieren Sie die mlContext-Variable mit einer neuen Instanz von MLContext, indem Sie der Main-Methode von Program.cs unterhalb des Felds outputFolder die folgende Zeile hinzufügen.Initialize the mlContext variable with a new instance of MLContext by adding the following line to the Main method of Program.cs below the outputFolder field.

MLContext mlContext = new MLContext();

Erstellen eines Parsers für die Nachverarbeitung von ModellausgabenCreate a parser to post-process model outputs

Das Modell segmentiert ein Bild in ein 13 x 13-Raster, in dem jede Rasterzelle 32px x 32px groß ist.The model segments an image into a 13 x 13 grid, where each grid cell is 32px x 32px. Jede Rasterzelle enthält fünf potenzielle Objektbegrenzungsrahmen.Each grid cell contains 5 potential object bounding boxes. Ein Begrenzungsrahmen enthält 25 Elemente:A bounding box has 25 elements:

Rasterbeispiel auf der linken Seite und Begrenzungsfeldbeispiel auf der rechten Seite

  • x: die x-Position der Mitte des Begrenzungsrahmens relativ zu der Rasterzelle, der er zugeordnet ist.x the x position of the bounding box center relative to the grid cell it's associated with.
  • y: die y-Position der Mitte des Begrenzungsrahmens relativ zu der Rasterzelle, der er zugeordnet ist.y the y position of the bounding box center relative to the grid cell it's associated with.
  • w: Die Breite des Begrenzungsrahmens.w the width of the bounding box.
  • h: Die Höhe des Begrenzungsrahmens.h the height of the bounding box.
  • o: Der Konfidenzwert, mit dem ein Objekt im Begrenzungsrahmen vorhanden ist (auch als Objectness Score bezeichnet).o the confidence value that an object exists within the bounding box, also known as objectness score.
  • p1-p20: Klassenwahrscheinlichkeiten für jede der 20 vom Modell vorhergesagten Klassen.p1-p20 class probabilities for each of the 20 classes predicted by the model.

Insgesamt bilden die 25 Elemente, die jeden der 5 Begrenzungsrahmen beschreiben, die 125 Elemente, die in jeder Rasterzelle enthalten sind.In total, the 25 elements describing each of the 5 bounding boxes make up the 125 elements contained in each grid cell.

Die Ausgabe, die vom vortrainierten ONNX-Modell generiert wird, ist ein float-Array der Länge 21125, das die Elemente eines Tensors mit Dimensionen 125 x 13 x 13 darstellt.The output generated by the pre-trained ONNX model is a float array of length 21125, representing the elements of a tensor with dimensions 125 x 13 x 13. Um die vom Modell generierten Vorhersagen in einen Tensor zu transformieren, sind einige Nachverarbeitungsaufgaben erforderlich.In order to transform the predictions generated by the model into a tensor, some post-processing work is required. Erstellen Sie zu diesem Zweck eine Reihe von Klassen, um die Analyse der Ausgabe zu unterstützen.To do so, create a set of classes to help parse the output.

Fügen Sie Ihrem Projekt ein neues Verzeichnis hinzu, um den Satz von Parserklassen zu organisieren.Add a new directory to your project to organize the set of parser classes.

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen Sie dann Hinzufügen > Neuer Ordner aus.In Solution Explorer, right-click the project, and then select Add > New Folder. Wenn der neue Ordner im Projektmappen-Explorer angezeigt wird, nennen Sie ihn „YoloParser“.When the new folder appears in the Solution Explorer, name it "YoloParser".

Erstellen von Begrenzungsrahmen und DimensionenCreate bounding boxes and dimensions

Die Daten, die vom Modell ausgegeben werden, enthalten Koordinaten und Dimensionen der Begrenzungsrahmen von Objekten im Bild.The data output by the model contains coordinates and dimensions of the bounding boxes of objects within the image. Erstellen Sie eine Basisklasse für Dimensionen.Create a base class for dimensions.

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Verzeichnis YoloParser, und wählen Sie dann Hinzufügen > Neues Element aus.In Solution Explorer, right-click the YoloParser directory, and then select Add > New Item.

  2. Wählen Sie im Dialogfeld Neues Element hinzufügen die Option Klasse aus, und ändern Sie das Feld Name in DimensionsBase.cs.In the Add New Item dialog box, select Class and change the Name field to DimensionsBase.cs. Wählen Sie dann die Schaltfläche Hinzufügen aus.Then, select the Add button.

    Die Datei DimensionsBase.cs wird im Code-Editor geöffnet.The DimensionsBase.cs file opens in the code editor. Entfernen Sie alle using-Anweisungen und die vorhandene Klassendefinition.Remove all using statements and existing class definition.

    Fügen Sie der Datei DimensionsBase.cs den folgenden Code für die DimensionsBase-Klasse hinzu:Add the following code for the DimensionsBase class to the DimensionsBase.cs file:

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

    DimensionsBase verfügt über die folgenden float-Felder:DimensionsBase has the following float fields:

    • X enthält die Position des Objekts auf der X-Achse.X contains the position of the object along the x-axis.
    • Y enthält die Position des Objekts auf der Y-Achse.Y contains the position of the object along the y-axis.
    • Height enthält die Höhe des Objekts.Height contains the height of the object.
    • Width enthält die Breite des Objekts.Width contains the width of the object.

Erstellen Sie nun eine Klasse für Ihre Begrenzungsrahmen.Next, create a class for your bounding boxes.

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Verzeichnis YoloParser, und wählen Sie dann Hinzufügen > Neues Element aus.In Solution Explorer, right-click the YoloParser directory, and then select Add > New Item.

  2. Wählen Sie im Dialogfeld Neues Element hinzufügen die Option Klasse aus, und ändern Sie das Feld Name in YoloBoundingBox.cs.In the Add New Item dialog box, select Class and change the Name field to YoloBoundingBox.cs. Wählen Sie dann die Schaltfläche Hinzufügen aus.Then, select the Add button.

    Die Datei YoloBoundingBox.cs wird im Code-Editor geöffnet.The YoloBoundingBox.cs file opens in the code editor. Fügen Sie die folgende using-Anweisung am Anfang der Datei YoloBoundingBox.cs hinzu:Add the following using statement to the top of YoloBoundingBox.cs:

    using System.Drawing;
    

    Fügen Sie direkt oberhalb der vorhandenen Klassendefinition eine neue Klassendefinition namens BoundingBoxDimensions hinzu, die von der DimensionsBase-Klasse erbt, um die Dimensionen des entsprechenden Begrenzungsrahmens zu enthalten.Just above the existing class definition, add a new class definition called BoundingBoxDimensions which inherits from the DimensionsBase class to contain the dimensions of the respective bounding box.

    public class BoundingBoxDimensions : DimensionsBase { }
    

    Entfernen Sie die vorhandene YoloBoundingBox-Klassendefinition, und fügen Sie der Datei YoloBoundingBox.cs den folgenden Code für die YoloBoundingBox-Klasse hinzu:Remove the existing YoloBoundingBox class definition and add the following code for the YoloBoundingBox class to the YoloBoundingBox.cs file:

    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 verfügt über die folgenden Felder:YoloBoundingBox has the following fields:

    • Dimensions enthält Dimensionen des Begrenzungsrahmens.Dimensions contains dimensions of the bounding box.
    • Label enthält die Klasse des Objekts, das innerhalb des Begrenzungsrahmens erkannt wurde.Label contains the class of object detected within the bounding box.
    • Confidence enthält die Konfidenz der Klasse.Confidence contains the confidence of the class.
    • Rect enthält die Rechteckdarstellung der Abmessungen des Begrenzungsrahmens.Rect contains the rectangle representation of the bounding box's dimensions.
    • BoxColor enthält die Farbe, die der jeweiligen Klasse zugeordnet ist, die zum Zeichnen des Bilds verwendet wird.BoxColor contains the color associated with the respective class used to draw on the image.

Erstellen des ParsersCreate the parser

Da nun die Klassen für Dimensionen und Begrenzungsrahmen erstellt wurden, ist es an der Zeit, den Parser zu erstellen.Now that the classes for dimensions and bounding boxes are created, it's time to create the parser.

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Verzeichnis YoloParser, und wählen Sie dann Hinzufügen > Neues Element aus.In Solution Explorer, right-click the YoloParser directory, and then select Add > New Item.

  2. Wählen Sie im Dialogfeld Neues Element hinzufügen die Option Klasse aus, und ändern Sie das Feld Name in YoloOutputParser.cs.In the Add New Item dialog box, select Class and change the Name field to YoloOutputParser.cs. Wählen Sie dann die Schaltfläche Hinzufügen aus.Then, select the Add button.

    Die Datei YoloOutputParser.cs wird im Code-Editor geöffnet.The YoloOutputParser.cs file opens in the code editor. Fügen Sie die folgende using-Anweisung am Anfang der Datei YoloOutputParser.cs hinzu:Add the following using statement to the top of YoloOutputParser.cs:

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

    Fügen Sie innerhalb der vorhandenen YoloOutputParser-Klassendefinition eine geschachtelte Klasse hinzu, die die Dimensionen der einzelnen Zellen im Bild enthält.Inside the existing YoloOutputParser class definition, add a nested class that contains the dimensions of each of the cells in the image. Fügen Sie den folgenden Code am Anfang der YoloOutputParser-Klassendefinition für die CellDimensions-Klasse hinzu, die von der DimensionsBase-Klasse erbt.Add the following code for the CellDimensions class which inherits from the DimensionsBase class at the top of the YoloOutputParser class definition.

    class CellDimensions : DimensionsBase { }
    
  3. Fügen Sie in der YoloOutputParser-Klassendefinition die folgende Konstante und die folgenden Felder hinzu.Inside the YoloOutputParser class definition, add the following constant and fields.

    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 ist die Anzahl der Zeilen im Raster, in die das Bild aufgeteilt wird.ROW_COUNT is the number of rows in the grid the image is divided into.
    • COL_COUNT ist die Anzahl der Spalten im Raster, in die das Bild aufgeteilt wird.COL_COUNT is the number of columns in the grid the image is divided into.
    • CHANNEL_COUNT ist die Gesamtzahl der Werte, die in einer Zelle des Rasters enthalten sind.CHANNEL_COUNT is the total number of values contained in one cell of the grid.
    • BOXES_PER_CELL ist die Anzahl der Begrenzungsrahmen in einer Zelle.BOXES_PER_CELL is the number of bounding boxes in a cell,
    • BOX_INFO_FEATURE_COUNT ist die Anzahl der in einem Feld enthaltenen Merkmale (x, y, Höhe, Breite, Konfidenz).BOX_INFO_FEATURE_COUNT is the number of features contained within a box (x,y,height,width,confidence).
    • CLASS_COUNT ist die Anzahl der Klassenvorhersagen, die in jedem Begrenzungsrahmen enthalten sind.CLASS_COUNT is the number of class predictions contained in each bounding box.
    • CELL_WIDTH ist die Breite einer Zelle im Bildraster.CELL_WIDTH is the width of one cell in the image grid.
    • CELL_HEIGHT ist die Höhe einer Zelle im Bildraster.CELL_HEIGHT is the height of one cell in the image grid.
    • channelStride ist die Anfangsposition der aktuellen Zelle im Raster.channelStride is the starting position of the current cell in the grid.

    Wenn das Modell eine Vorhersage erstellt (auch als Bewertung bezeichnet), unterteilt es das Eingabebild 416px x 416px in ein Raster von Zellen mit einer Größe von 13 x 13.When the model makes a prediction, also known as scoring, it divides the 416px x 416px input image into a grid of cells the size of 13 x 13. Jede enthaltene Zelle ist 32px x 32px groß.Each cell contains is 32px x 32px. In jeder Zelle sind fünf Begrenzungsrahmen enthalten, die jeweils fünf Merkmale enthalten (x, y, Breite, Höhe, Konfidenz).Within each cell, there are 5 bounding boxes each containing 5 features (x, y, width, height, confidence). Außerdem enthält jeder Begrenzungsrahmen die Wahrscheinlichkeit der einzelnen Klassen (in diesem Fall ist sie 20).In addition, each bounding box contains the probability of each of the classes which in this case is 20. Daher enthält jede Zelle 125 Informationen (5 Merkmale und 20 Klassenwahrscheinlichkeiten).Therefore, each cell contains 125 pieces of information (5 features + 20 class probabilities).

Erstellen Sie eine Liste der Anker unter channelStride für alle 5 Begrenzungsrahmen:Create a list of anchors below channelStride for all 5 bounding boxes:

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

Anker sind vordefinierte Höhen- und Breitenverhältnisse für Begrenzungsrahmen.Anchors are pre-defined height and width ratios of bounding boxes. Die meisten von einem Modell erkannten Objekte oder Klassen weisen ähnliche Verhältnisse auf.Most object or classes detected by a model have similar ratios. Dies ist nützlich, wenn es um das Erstellen von Begrenzungsrahmen geht.This is valuable when it comes to creating bounding boxes. Anstatt die Begrenzungsrahmen vorherzusagen, wird der Offset zu den vordefinierten Abmessungen berechnet, wodurch die für die Vorhersage des Begrenzungsrahmens erforderliche Berechnung verringert wird.Instead of predicting the bounding boxes, the offset from the pre-defined dimensions is calculated therefore reducing the computation required to predict the bounding box. Normalerweise werden diese Ankerverhältnisse basierend auf dem verwendeten Dataset berechnet.Typically these anchor ratios are calculated based on the dataset used. In diesem Fall können die Anker hartcodiert werden, da das Dataset bekannt ist und die Werte vorberechnet wurden.In this case because the dataset is known and the values have been pre-computed, the anchors can be hard-coded.

Definieren Sie nun die Bezeichnungen oder Klassen, die vom Modell vorhergesagt werden.Next, define the labels or classes that the model will predict. Dieses Modell sagt 20 Klassen voraus, was eine Teilmenge der Gesamtzahl der vom ursprünglichen YOLOv2-Modell vorhergesagten Klassen ist.This model predicts 20 classes which is a subset of the total number of classes predicted by the original YOLOv2 model.

Fügen Sie die Liste der Bezeichnungen unterhalb von anchors hinzu.Add your list of labels below the 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"
};

Jeder der Klassen sind Farben zugeordnet.There are colors associated with each of the classes. Weisen Sie Ihre Klassenfarben unter labels zu:Assign your class colors below your 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
};

Erstellen von HilfsfunktionenCreate helper functions

In der Nachverarbeitungsphase sind mehrere Schritte erforderlich.There are a series of steps involved in the post-processing phase. Zur Unterstützung können mehrere Hilfsmethoden eingesetzt werden.To help with that, several helper methods can be employed.

Die folgenden Hilfsmethoden werden vom Parser verwendet:The helper methods used in by the parser are:

  • Sigmoid wendet die Sigmoidfunktion an, die eine Zahl zwischen 0 und 1 ausgibt.Sigmoid applies the sigmoid function that outputs a number between 0 and 1.
  • Softmax normalisiert einen Eingabevektor in eine Wahrscheinlichkeitsverteilung.Softmax normalizes an input vector into a probability distribution.
  • GetOffset ordnet Elemente in der eindimensionalen Modellausgabe der entsprechenden Position in einem 125 x 13 x 13-Tensor zu.GetOffset maps elements in the one-dimensional model output to the corresponding position in a 125 x 13 x 13 tensor.
  • ExtractBoundingBoxes extrahiert die Abmessungen des Begrenzungsrahmens mithilfe der GetOffset-Methode aus der Modellausgabe.ExtractBoundingBoxes extracts the bounding box dimensions using the GetOffset method from the model output.
  • GetConfidence extrahiert den Konfidenzwert, der angibt, wie sicher sich das Modell ist, dass ein Objekt erkannt wurde, und verwendet die Sigmoid-Funktion, um diesen Wert in eine Prozentangabe umzuwandeln.GetConfidence extracts the confidence value which states how sure the model is that it has detected an object and uses the Sigmoid function to turn it into a percentage.
  • MapBoundingBoxToCell verwendet die Abmessungen des Begrenzungsrahmens und ordnet diese der entsprechenden Zelle im Bild zu.MapBoundingBoxToCell uses the bounding box dimensions and maps them onto its respective cell within the image.
  • ExtractClasses extrahiert die Klassenvorhersagen für den Begrenzungsrahmen aus der Modellausgabe unter Verwendung der GetOffset-Methode und wandelt sie mithilfe der Softmax-Methode in eine Wahrscheinlichkeitsverteilung um.ExtractClasses extracts the class predictions for the bounding box from the model output using the GetOffset method and turns them into a probability distribution using the Softmax method.
  • GetTopResult wählt die Klasse aus der Liste der vorhergesagten Klassen mit der höchsten Wahrscheinlichkeit aus.GetTopResult selects the class from the list of predicted classes with the highest probability.
  • IntersectionOverUnion filtert sich überlappende Begrenzungsrahmen mit geringeren Wahrscheinlichkeiten.IntersectionOverUnion filters overlapping bounding boxes with lower probabilities.

Fügen Sie den Code für alle Hilfsmethoden unterhalb der Liste der classColors hinzu.Add the code for all the helper methods below your list of 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);
}

Nachdem Sie alle Hilfsmethoden definiert haben, ist es an der Zeit, Sie zum Verarbeiten der Modellausgabe zu verwenden.Once you have defined all of the helper methods, it's time to use them to process the model output.

Erstellen Sie unter der IntersectionOverUnion-Methode die ParseOutputs-Methode, um die vom Modell generierte Ausgabe zu verarbeiten.Below the IntersectionOverUnion method, create the ParseOutputs method to process the output generated by the model.

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

}

Erstellen Sie eine Liste, um die Begrenzungsrahmen zu speichern, und definieren Sie Variablen in der ParseOutputs-Methode.Create a list to store your bounding boxes and define variables inside the ParseOutputs method.

var boxes = new List<YoloBoundingBox>();

Jedes Bild wird in ein Raster von 13 x 13-Zellen aufgeteilt.Each image is divided into a grid of 13 x 13 cells. Jede Zelle enthält fünf Begrenzungsrahmen.Each cell contains five bounding boxes. Fügen Sie unterhalb der Variablen boxes Code hinzu, um alle Rahmen jeder Zelle zu verarbeiten.Below the boxes variable, add code to process all of the boxes in each of the cells.

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++)
        {

        }
    }
}

Berechnen Sie in der innersten Schleife die Anfangsposition des aktuellen Rahmens innerhalb der eindimensionalen Modellausgabe.Inside the inner-most loop, calculate the starting position of the current box within the one-dimensional model output.

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

Verwenden Sie direkt darunter die ExtractBoundingBoxDimensions-Methode, um die Abmessungen des aktuellen Begrenzungsrahmens abzurufen.Directly below that, use the ExtractBoundingBoxDimensions method to get the dimensions of the current bounding box.

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

Verwenden Sie dann die GetConfidence-Methode, um die Konfidenz für den aktuellen Begrenzungsrahmen abzurufen.Then, use the GetConfidence method to get the confidence for the current bounding box.

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

Verwenden Sie anschließend die MapBoundingBoxToCell-Methode, um der aktuellen Zelle, die verarbeitet wird, den aktuellen Begrenzungsrahmen zuzuordnen.After that, use the MapBoundingBoxToCell method to map the current bounding box to the current cell being processed.

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

Überprüfen Sie vor der weiteren Verarbeitung, ob Ihr Konfidenzwert größer als der angegebene Schwellenwert ist.Before doing any further processing, check whether your confidence value is greater than the threshold provided. Wenn dies nicht der Fall ist, verarbeiten Sie den nächsten Begrenzungsrahmen.If not, process the next bounding box.

if (confidence < threshold)
    continue;

Setzen Sie andernfalls die Verarbeitung der Ausgabe fort.Otherwise, continue processing the output. Der nächste Schritt besteht darin, die Wahrscheinlichkeitsverteilung der vorhergesagten Klassen für den aktuellen Begrenzungsrahmen mithilfe der ExtractClasses-Methode abzurufen.The next step is to get the probability distribution of the predicted classes for the current bounding box using the ExtractClasses method.

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

Verwenden Sie dann die GetTopResult-Methode, um den Wert und den Index der Klasse mit der höchsten Wahrscheinlichkeit für den aktuellen Rahmen abzurufen, und berechnen Sie die Bewertung.Then, use the GetTopResult method to get the value and index of the class with the highest probability for the current box and compute its score.

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

Verwenden Sie topScore, um erneut nur die Begrenzungsrahmen beizubehalten, die über dem angegebenen Schwellenwert liegen.Use the topScore to once again keep only those bounding boxes that are above the specified threshold.

if (topScore < threshold)
    continue;

Wenn der aktuelle Begrenzungsrahmen den Schwellenwert überschreitet, erstellen Sie ein neues BoundingBox-Objekt, und fügen Sie es der boxes-Liste hinzu.Finally, if the current bounding box exceeds the threshold, create a new BoundingBox object and add it to the boxes list.

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

Nachdem alle Zellen im Bild verarbeitet wurden, geben Sie die boxes-Liste zurück.Once all cells in the image have been processed, return the boxes list. Fügen Sie die folgende Rückgabeanweisung unterhalb der äußersten for-Schleife in der ParseOutputs-Methode hinzu.Add the following return statement below the outer-most for-loop in the ParseOutputs method.

return boxes;

Filtern sich überlappender RahmenFilter overlapping boxes

Nachdem nun alle Begrenzungsrahmen mit einem hohen Konfidenzwert aus der Modellausgabe extrahiert wurden, muss eine zusätzliche Filterung durchgeführt werden, um sich überlappende Bilder zu entfernen.Now that all of the highly confident bounding boxes have been extracted from the model output, additional filtering needs to be done to remove overlapping images. Fügen Sie unter der ParseOutputs-Methode eine Methode namens FilterBoundingBoxes hinzu:Add a method called FilterBoundingBoxes below the ParseOutputs method:

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

}

Erstellen Sie innerhalb der FilterBoundingBoxes-Methode zunächst ein Array in der Größe der erkannten Rahmen, und markieren Sie alle Slots als aktiv oder bereit zur Verarbeitung.Inside the FilterBoundingBoxes method, start off by creating an array equal to the size of detected boxes and marking all slots as active or ready for processing.

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

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

Sortieren Sie die Liste mit den Begrenzungsrahmen dann in absteigender Reihenfolge basierend auf dem Konfidenzwert.Then, sort the list containing your bounding boxes in descending order based on confidence.

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

Erstellen Sie anschließend eine Liste, in der die gefilterten Ergebnisse gespeichert werden.After that, create a list to hold the filtered results.

var results = new List<YoloBoundingBox>();

Beginnen Sie mit der Verarbeitung der einzelnen Begrenzungsrahmen, indem Sie die einzelnen Begrenzungsrahmen durchlaufen.Begin processing each bounding box by iterating over each of the bounding boxes.

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

}

Überprüfen Sie in dieser for-Schleife, ob der aktuelle Begrenzungsrahmen verarbeitet werden kann.Inside of this for-loop, check whether the current bounding box can be processed.

if (isActiveBoxes[i])
{

}

Wenn dies der Fall ist, fügen Sie den Begrenzungsrahmen der Ergebnisliste hinzu.If so, add the bounding box to the list of results. Wenn die Ergebnisse den angegebenen Grenzwert für die zu extrahierenden Rahmen überschreiten, unterbrechen Sie die Schleife.If the results exceeds the specified limit of boxes to be extracted, break out of the loop. Fügen Sie den folgenden Code in der if-Anweisung hinzu.Add the following code inside the if-statement.

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

if (results.Count >= limit)
    break;

Untersuchen Sie andernfalls die angrenzenden Begrenzungsrahmen.Otherwise, look at the adjacent bounding boxes. Fügen Sie unter der Überprüfung der Rahmengrenze den folgenden Code hinzu.Add the following code below the box limit check.

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

}

Wenn der benachbarte Rahmen aktiv oder bereit zur Verarbeitung ist, verwenden Sie wie beim ersten Rahmen die IntersectionOverUnion-Methode, um zu überprüfen, ob der erste Rahmen und der zweite Rahmen den angegebenen Schwellenwert überschreiten.Like the first box, if the adjacent box is active or ready to be processed, use the IntersectionOverUnion method to check whether the first box and the second box exceed the specified threshold. Fügen Sie der innersten for-Schleife den folgenden Code hinzu.Add the following code to your inner-most for-loop.

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

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

        if (activeCount <= 0)
            break;
    }
}

Überprüfen Sie außerhalb der inneren for-Schleife, die angrenzende Begrenzungsrahmen überprüft, ob noch verbleibende Begrenzungsrahmen verarbeitet werden müssen.Outside of the inner-most for-loop that checks adjacent bounding boxes, see whether there are any remaining bounding boxes to be processed. Wenn dies nicht der Fall ist, unterbrechen Sie die äußere for-Schleife.If not, break out of the outer for-loop.

if (activeCount <= 0)
    break;

Geben Sie schließlich außerhalb der anfänglichen for-Schleife der FilterBoundingBoxes-Methode die Ergebnisse zurück:Finally, outside of the initial for-loop of the FilterBoundingBoxes method, return the results:

return results;

Prima.Great! Nun ist es an der Zeit, diesen Code zusammen mit dem Modell für die Bewertung zu verwenden.Now it's time to use this code along with the model for scoring.

Verwenden des Modells für die BewertungUse the model for scoring

Genau wie bei der Nachverarbeitung gibt sind einige Bewertungsschritte erforderlich.Just like with post-processing, there are a few steps in the scoring steps. Fügen Sie dem Projekt zur Unterstützung dieser Aufgaben eine Klasse hinzu, die die Bewertungslogik enthält.To help with this, add a class that will contain the scoring logic to your project.

  1. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen Sie dann Hinzufügen > Neues Element aus.In Solution Explorer, right-click the project, and then select Add > New Item.

  2. Wählen Sie im Dialogfeld Neues Element hinzufügen die Option Klasse aus, und ändern Sie das Feld Name in OnnxModelScorer.cs.In the Add New Item dialog box, select Class and change the Name field to OnnxModelScorer.cs. Wählen Sie dann die Schaltfläche Hinzufügen aus.Then, select the Add button.

    Die Datei OnnxModelScorer.cs wird im Code-Editor geöffnet.The OnnxModelScorer.cs file opens in the code editor. Fügen Sie die folgende using-Anweisung am Anfang der Datei OnnxModelScorer.cs hinzu:Add the following using statement to the top of OnnxModelScorer.cs:

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

    Fügen Sie innerhalb der OnnxModelScorer-Klassendefinition die folgenden Variablen hinzu.Inside the OnnxModelScorer class definition, add the following variables.

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

    Erstellen Sie direkt darunter einen Konstruktor für die OnnxModelScorer-Klasse, die die zuvor definierten Variablen initialisiert.Directly below that, create a constructor for the OnnxModelScorer class that will initialize the previously defined variables.

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

    Nachdem Sie den Konstruktor erstellt haben, definieren Sie einige Strukturen, die Variablen enthalten, die sich auf das Bild und die Modelleinstellungen beziehen.Once you have created the constructor, define a couple of structs that contain variables related to the image and model settings. Erstellen Sie eine Struktur namens ImageNetSettings, die die Höhe und Breite enthält, die als Eingabe für das Modell erwartet wird.Create a struct called ImageNetSettings to contain the height and width expected as input for the model.

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

    Erstellen Sie anschließend eine weitere Struktur namens TinyYoloModelSettings, die die Namen der Eingabe- und Ausgabeschichten des Modells enthält.After that, create another struct called TinyYoloModelSettings which contains the names of the input and output layers of the model. Um den Namen der Eingabe- und Ausgabeschichten des Modells zu visualisieren, können Sie ein Tool wie Netron verwenden.To visualize the name of the input and output layers of the model, you can use a tool like 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";
    }
    

    Erstellen Sie nun den ersten Satz von Methoden, die für die Bewertung verwendet werden.Next, create the first set of methods use for scoring. Erstellen Sie die LoadModel-Methode in der OnnxModelScorer-Klasse.Create the LoadModel method inside of your OnnxModelScorer class.

    private ITransformer LoadModel(string modelLocation)
    {
    
    }
    

    Fügen Sie in der LoadModel-Methode den folgenden Code für die Protokollierung hinzu.Inside the LoadModel method, add the following code for logging.

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

    ML.NET-Pipelines müssen das Datenschema kennen, das verwendet werden soll, wenn die Methode Fit aufgerufen wird.ML.NET pipelines need to know the data schema to operate on when the Fit method is called. In diesem Fall wird ein Prozess ähnlich dem Training verwendet.In this case, a process similar to training will be used. Da jedoch kein tatsächliches Training stattfindet, ist es akzeptabel, eine leere IDataView zu verwenden.However, because no actual training is happening, it is acceptable to use an empty IDataView. Erstellen Sie eine neue IDataView für die Pipeline aus einer leeren Liste.Create a new IDataView for the pipeline from an empty list.

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

    Definieren Sie darunter die Pipeline.Below that, define the pipeline. Die Pipeline besteht aus vier Transformationen.The pipeline will consist of four transforms.

    • LoadImages lädt das Bild als Bitmap.LoadImages loads the image as a Bitmap.
    • ResizeImages wandelt das Bild um die angegebene Größe um (in diesem Fall 416 x 416).ResizeImages rescales the image to the size specified (in this case, 416 x 416).
    • ExtractPixels ändert die Pixeldarstellung des Bilds aus einer Bitmap in einen numerischen Vektor.ExtractPixels changes the pixel representation of the image from a Bitmap to a numerical vector.
    • ApplyOnnxModel lädt das ONNX-Modell und verwendet es, um die bereitgestellten Daten zu bewerten.ApplyOnnxModel loads the ONNX model and uses it to score on the data provided.

    Definieren Sie die Pipeline in der LoadModel-Methode unter der data-Variablen.Define your pipeline in the LoadModel method below the data variable.

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

    Jetzt ist es an der Zeit, das Modell für die Bewertung zu instanziieren.Now it's time to instantiate the model for scoring. Rufen Sie die Fit-Methode für die Pipeline auf, und geben Sie sie zur weiteren Verarbeitung zurück.Call the Fit method on the pipeline and return it for further processing.

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

Nachdem das Modell geladen wurde, kann es verwendet werden, um Vorhersagen zu treffen.Once the model is loaded, it can then be used to make predictions. Um diesen Prozess zu ermöglichen, erstellen Sie eine Methode namens PredictDataUsingModel unter der LoadModel-Methode.To facilitate that process, create a method called PredictDataUsingModel below the LoadModel method.

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

}

Fügen Sie in PredictDataUsingModel den folgenden Code für die Protokollierung hinzu.Inside the PredictDataUsingModel, add the following code for logging.

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

Verwenden Sie dann die Transform-Methode, um die Daten zu bewerten.Then, use the Transform method to score the data.

IDataView scoredData = model.Transform(testData);

Extrahieren Sie die vorhergesagten Wahrscheinlichkeiten, und geben Sie sie zur weiteren Verarbeitung zurück.Extract the predicted probabilities and return them for additional processing.

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

return probabilities;

Nachdem Sie nun beide Schritte eingerichtet haben, kombinieren Sie sie in einer einzigen Methode.Now that both steps are set up, combine them into a single method. Fügen Sie unter der PredictDataUsingModel-Methode eine Methode namens Score hinzu.Below the PredictDataUsingModel method, add a new method called Score.

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

    return PredictDataUsingModel(data, model);
}

Fast geschafft!Almost there! Jetzt ist es an der Zeit, alles zusammen einzusetzen.Now it's time to put it all to use.

Erkennen von ObjektenDetect objects

Nachdem das gesamte Setup nun vollständig ist, ist es an der Zeit, einige Objekte zu erkennen.Now that all of the setup is complete, it's time to detect some objects. Fügen Sie zu Beginn Verweise auf den Scorer und den Parser zur Klasse Program.cs hinzu.Start off by adding references to the scorer and parser in your Program.cs class.

using ObjectDetection.YoloParser;
using ObjectDetection.DataStructures;

Bewerten und Analysieren von ModellausgabenScore and parse model outputs

Fügen Sie innerhalb der Main-Methode der Klasse Program.cs eine try-catch-Anweisung hinzu.Inside the Main method of your Program.cs class, add a try-catch statement.

try
{

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

Beginnen Sie innerhalb des try-Blocks mit der Implementierung der Objekterkennungslogik.Inside of the try block, start implementing the object detection logic. Laden Sie die Daten zunächst in eine IDataView.First, load the data into an IDataView.

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

Erstellen Sie dann eine Instanz von OnnxModelScorer, und verwenden Sie diese zum Bewerten der geladenen Daten.Then, create an instance of OnnxModelScorer and use it to score the loaded data.

var modelScorer = new OnnxModelScorer(imagesFolder, modelFilePath, mlContext);

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

Jetzt muss der Nachverarbeitungsschritt erfolgen.Now it's time for the post-processing step. Erstellen Sie eine Instanz von YoloOutputParser, und verwenden Sie diese, um die Modellausgabe zu verarbeiten.Create an instance of YoloOutputParser and use it to process the model output.

YoloOutputParser parser = new YoloOutputParser();

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

Nachdem die Modellausgabe verarbeitet wurde, können die Begrenzungsrahmen auf den Bildern gezeichnet werden.Once the model output has been processed, it's time to draw the bounding boxes on the images.

Visualisieren von VorhersagenVisualize predictions

Nachdem das Modell die Bilder bewertet hat und die Ausgaben verarbeitet wurden, müssen die Begrenzungsrahmen auf das Bild gezeichnet werden.After the model has scored the images and the outputs have been processed, the bounding boxes have to be drawn on the image. Fügen Sie zu diesem Zweck eine Methode namens DrawBoundingBox unter der Methode GetAbsolutePath der Datei Program.cs hinzu.To do so, add a method called DrawBoundingBox below the GetAbsolutePath method inside of Program.cs.

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

}

Laden Sie zuerst das Bild, und rufen Sie die Abmessungen für die Höhe und Breite in der DrawBoundingBox-Methode ab.First, load the image and get the height and width dimensions in the DrawBoundingBox method.

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

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

Erstellen Sie dann eine for-each-Schleife, um über jeden der vom Modell erfassten Begrenzungsrahmen zu iterieren.Then, create a for-each loop to iterate over each of the bounding boxes detected by the model.

foreach (var box in filteredBoundingBoxes)
{

}

In der for-each-Schleife rufen Sie die Abmessungen des Begrenzungsrahmens ab.Inside of the for-each loop, get the dimensions of the bounding box.

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

Da die Abmessungen des Begrenzungsrahmens der Modelleingabe von 416 x 416 entsprechen, skalieren Sie die Abmessungen des Begrenzungsrahmens so, dass sie der tatsächlichen Größe des Bilds entsprechen.Because the dimensions of the bounding box correspond to the model input of 416 x 416, scale the bounding box dimensions to match the actual size of the image.

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;

Definieren Sie dann eine Vorlage für Text, der oberhalb jedes Begrenzungsrahmens angezeigt wird.Then, define a template for text that will appear above each bounding box. Der Text enthält die Klasse des Objekts innerhalb des entsprechenden Begrenzungsrahmens sowie die Konfidenz.The text will contain the class of the object inside of the respective bounding box as well as the confidence.

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

Um auf dem Bild zu zeichnen, konvertieren Sie es in ein Graphics-Objekt.In order to draw on the image, convert it to a Graphics object.

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

}

Optimieren Sie innerhalb des using-Codeblocks die Graphics-Objekteinstellungen der Grafik.Inside the using code block, tune the graphic's Graphics object settings.

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

Legen Sie darunter die Schriftart- und Farboptionen für den Text und den Begrenzungsrahmen fest.Below that, set the font and color options for the text and bounding box.

// 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);

Erstellen und füllen Sie ein Rechteck oberhalb des Begrenzungsrahmens mithilfe der FillRectangle-Methode, damit es den Text enthält.Create and fill a rectangle above the bounding box to contain the text using the FillRectangle method. Dies hilft dabei, den Text vom Hintergrund abzuheben und die Lesbarkeit zu verbessern.This will help contrast the text and improve readability.

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

Zeichnen Sie dann den Text und den Begrenzungsrahmen auf dem Bild mithilfe der Methoden DrawString und DrawRectangle.Then, Draw the text and bounding box on the image using the DrawString and DrawRectangle methods.

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

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

Fügen Sie außerhalb der for-each-Schleife Code hinzu, um die Bilder im outputDirectory zu speichern.Outside of the for-each loop, add code to save the images in the outputDirectory.

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

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

Wenn Sie zusätzliches Feedback dazu erhalten möchten, ob die Anwendung Vorhersagen wie erwartet zur Laufzeit trifft, fügen Sie eine Methode namens LogDetectedObjects unterhalb der Methode DrawBoundingBox zu der Datei Program.cs hinzu, um die erkannten Objekte an die Konsole auszugeben.For additional feedback that the application is making predictions as expected at runtime, add a method called LogDetectedObjects below the DrawBoundingBox method in the Program.cs file to output the detected objects to the console.

private static 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("");
}

Nun, da Sie über Hilfsmethoden zum Erstellen von visuellem Feedback aus den Vorhersagen verfügen, sollten Sie eine For-Schleife hinzufügen, um die einzelnen bewerteten Bilder zu durchlaufen.Now that you have helper methods to create visual feedback from the predictions, add a for-loop to iterate over each of the scored images.

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

}

Rufen Sie in der for-Schleife den Namen der Bilddatei und die Begrenzungsrahmen ab, die ihr zugeordnet sind.Inside of the for-loop, get the name of the image file and the bounding boxes associated with it.

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

Verwenden Sie darunter die DrawBoundingBox-Methode, um die Begrenzungsrahmen auf dem Bild zu zeichnen.Below that, use the DrawBoundingBox method to draw the bounding boxes on the image.

DrawBoundingBox(imagesFolder, outputFolder, imageFileName, detectedObjects);

Verwenden Sie schließlich die Methode LogDetectedObjects, um Vorhersagen an die Konsole auszugeben.Lastly, use the LogDetectedObjects method to output predictions to the console.

LogDetectedObjects(imageFileName, detectedObjects);

Fügen Sie nach der try-catch-Anweisung zusätzliche Logik hinzu, um anzugeben, dass die Ausführung des Prozesses abgeschlossen wurde.After the try-catch statement, add additional logic to indicate the process is done running.

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

Das ist alles!That's it!

ErgebnisseResults

Nachdem Sie die vorherigen Schritte durchgeführt haben, führen Sie die Konsolen-App aus (STRG+F5).After following the previous steps, run your console app (Ctrl + F5). Ihre Ergebnisse sollten der folgenden Ausgabe ähneln.Your results should be similar to the following output. Möglicherweise werden Warnungen oder Verarbeitungsnachrichten angezeigt. Diese wurden jedoch aus Gründen der Übersichtlichkeit aus den folgenden Ergebnissen entfernt.You may see warnings or processing messages, but these messages have been removed from the following results for clarity.

=====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 ========

Navigieren Sie zum Verzeichnis assets/images/output/, um die Bilder mit Begrenzungsrahmen anzuzeigen.To see the images with bounding boxes, navigate to the assets/images/output/ directory. Unten sehen Sie ein Beispiel aus einem der verarbeiteten Bilder.Below is a sample from one of the processed images.

Beispiel für ein verarbeitetes Bild eines Esszimmers

Herzlichen Glückwunsch!Congratulations! Sie haben nun durch Wiederverwenden eines vortrainierten ONNX-Modells in ML.NET erfolgreich ein Machine Learning-Modell für die Objekterkennung erstellt.You've now successfully built a machine learning model for object detection by reusing a pre-trained ONNX model in ML.NET.

Sie finden den Quellcode für dieses Tutorial im Repository dotnet/machinelearning-samples.You can find the source code for this tutorial at the dotnet/machinelearning-samples repository.

In diesem Tutorial haben Sie gelernt, wie die folgenden Aufgaben ausgeführt werden:In this tutorial, you learned how to:

  • Das Problem verstehenUnderstand the problem
  • Erfahren Sie, was ONNX ist und wie ONNX mit ML.NET funktioniert.Learn what ONNX is and how it works with ML.NET
  • Verstehen des ModellsUnderstand the model
  • Wiederverwenden des vortrainierten ModellsReuse the pre-trained model
  • Erkennen von Objekten mit einem geladenen ModellDetect objects with a loaded model

Sehen Sie sich im GitHub-Repository für Machine Learning-Beispiele nach einem Beispiel für erweiterte Objekterkennung um, damit Sie es untersuchen können.Check out the Machine Learning samples GitHub repository to explore an expanded object detection sample.