Kurz: zjištění objektů pomocí ONNX v ML.NETTutorial: Detect objects using ONNX in ML.NET

Naučte se používat předem vyškolený model ONNX v ML.NET ke zjišťování objektů v obrázcích.Learn how to use a pre-trained ONNX model in ML.NET to detect objects in images.

Školení modelu detekce objektu od začátku vyžaduje nastavení milionů parametrů, velké množství označených školicích dat a obrovské množství výpočetních prostředků (stovky hodin GPU).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). Použití předem připraveného modelu vám umožní zástupce školicího procesu.Using a pre-trained model allows you to shortcut the training process.

V tomto kurzu se naučíte:In this tutorial, you learn how to:

  • Pochopení problémuUnderstand the problem
  • Zjistěte, co je ONNX a jak funguje s ML.NETLearn what ONNX is and how it works with ML.NET
  • Pochopení modeluUnderstand the model
  • Opakované použití předem připraveného modeluReuse the pre-trained model
  • Detekovat objekty s načteným modelemDetect objects with a loaded model

PředpokladyPre-requisites

Přehled ukázky rozpoznávání objektů ONNXONNX object detection sample overview

Tato ukázka vytvoří konzolovou aplikaci .NET Core, která detekuje objekty v rámci Image pomocí předem připraveného modelu ONNX hloubkového učení.This sample creates a .NET core console application that detects objects within an image using a pre-trained deep learning ONNX model. Kód pro tuto ukázku najdete v úložišti dotnet/machinelearning-Samples na GitHubu.The code for this sample can be found on the dotnet/machinelearning-samples repository on GitHub.

Co je rozpoznávání objektů?What is object detection?

Detekce objektu je problém počítačové vize.Object detection is a computer vision problem. V úzce souvisejícím s klasifikací imagí provádí detekce objektů klasifikaci obrázků v podrobnějším měřítku.While closely related to image classification, object detection performs image classification at a more granular scale. Detekce objektů vyhledává a kategorizuje entity v rámci imagí.Object detection both locates and categorizes entities within images. Použijte detekci objektů, pokud obrázky obsahují více objektů různých typů.Use object detection when images contain multiple objects of different types.

Snímky obrazovky zobrazující klasifikaci obrázku versus klasifikaci objektů

Mezi případy použití při detekci objektu patří:Some use cases for object detection include:

  • Osobní hnací automobilySelf-Driving Cars
  • RobotikaRobotics
  • Rozpoznávání tvářeFace Detection
  • Bezpečnost na pracovištiWorkplace Safety
  • Počítání objektůObject Counting
  • Rozpoznávání aktivitActivity Recognition

Vyberte model hloubkového učení.Select a deep learning model

Obsáhlý Learning je podmnožinou strojového učení.Deep learning is a subset of machine learning. Aby bylo možné naučit modely hloubkového učení, jsou potřeba velké množství dat.To train deep learning models, large quantities of data are required. Vzory v datech jsou reprezentovány řadou vrstev.Patterns in the data are represented by a series of layers. Vztahy v datech jsou kódovány jako připojení mezi vrstvami, které obsahují váhy.The relationships in the data are encoded as connections between the layers containing weights. Čím vyšší je váha, tím silnější je vztah.The higher the weight, the stronger the relationship. Souhrnně je tato série vrstev a připojení známá jako umělá neuronové síť.Collectively, this series of layers and connections are known as artificial neural networks. Více vrstev v síti, "hlubší", je díky tomu rozsáhlá neuronové síť.The more layers in a network, the "deeper" it is, making it a deep neural network.

Existují různé typy sítí neuronové, nejběžnější jsou vícevrstvé Perceptron (MLP), konvoluční neuronové Network (CNN) a znovu aktuální neuronové síť (RNN).There are different types of neural networks, the most common being Multi-Layered Perceptron (MLP), Convolutional Neural Network (CNN) and Recurrent Neural Network (RNN). Nejzákladnější je MLP, který mapuje sadu vstupů na sadu výstupů.The most basic is the MLP, which maps a set of inputs to a set of outputs. Tato neuronové síť je dobrá, pokud data neobsahují prostorová nebo časová komponenta.This neural network is good when the data does not have a spatial or time component. CNN využívá vrstvy konvoluční ke zpracování prostorových informací obsažených v datech.The CNN makes use of convolutional layers to process spatial information contained in the data. Dobrým případem použití pro DNN je zpracování obrazu k detekci přítomnosti funkce v oblasti obrázku (například je tam uprostřed obrázku nějaký nos?).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?). Nakonec RNN umožní, aby se jako vstup použila trvalá stavová nebo paměť.Finally, RNNs allow for the persistence of state or memory to be used as input. RNN se používají pro analýzu časových řad, kde je důležité sekvenční řazení a kontext událostí.RNNs are used for time-series analysis, where the sequential ordering and context of events is important.

Pochopení modeluUnderstand the model

Detekce objektu je úloha zpracování obrázku.Object detection is an image processing task. Proto se většina modelů hloubkového učení, které jsou vyškolené k vyřešení tohoto problému, DNN.Therefore, most deep learning models trained to solve this problem are CNNs. Model použitý v tomto kurzu je malý model YOLOv2, což je kompaktnější verze modelu YOLOv2 popsané v dokumentu: "YOLO9000: lepší, rychlejší, silnější" podle Redmon a 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. Drobný YOLOv2 je vyškolená pro datovou sadu Pascal a skládá se z 15 vrstev, které mohou odhadnout 20 různých tříd objektů.Tiny YOLOv2 is trained on the Pascal VOC dataset and is made up of 15 layers that can predict 20 different classes of objects. Vzhledem k tomu, že malá YOLOv2 je Zhuštěná verze původního modelu YOLOv2, je mezi rychlostí a přesností provedeno kompromis.Because Tiny YOLOv2 is a condensed version of the original YOLOv2 model, a tradeoff is made between speed and accuracy. Různé vrstvy, které tvoří model, lze vizuálně vymezit pomocí nástrojů, jako je Netron.The different layers that make up the model can be visualized using tools like Netron. Kontrola modelu by způsobila mapování propojení mezi všemi vrstvami tvořící neuronové síť, kde každá z vrstev obsahuje název vrstvy spolu s rozměry příslušného vstupu/výstupu.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. Datové struktury používané k popisu vstupů a výstupů modelu jsou známé jako modely.The data structures used to describe the inputs and outputs of the model are known as tensors. Křížové procesory si můžete představit jako kontejnery, které ukládají data v N-dimenzích.Tensors can be thought of as containers that store data in N-dimensions. V případě malých YOLOv2 je název vstupní vrstvy image a očekává tensor dimenzí 3 x 416 x 416.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. Název výstupní vrstvy je grid a generuje výstupní tensor dimenzí 125 x 13 x 13.The name of the output layer is grid and generates an output tensor of dimensions 125 x 13 x 13.

Vstupní vrstva je rozdělená na skryté vrstvy a pak na výstupní vrstvu.

YOLO model přebírá image 3(RGB) x 416px x 416px.The YOLO model takes an image 3(RGB) x 416px x 416px. Model provede tento vstup a předá jej prostřednictvím různých vrstev a vytvoří výstup.The model takes this input and passes it through the different layers to produce an output. Výstup rozdělí vstupní obrázek do mřížky 13 x 13, přičemž každou buňku v mřížce tvoří hodnoty 125.The output divides the input image into a 13 x 13 grid, with each cell in the grid consisting of 125 values.

Co je model ONNX?What is an ONNX model?

Open neuronové Network Exchange (ONNX) je open source formát pro modely AI.The Open Neural Network Exchange (ONNX) is an open source format for AI models. ONNX podporuje interoperabilitu mezi platformami.ONNX supports interoperability between frameworks. To znamená, že můžete model vytvořit v jedné z mnoha oblíbených rozhraní pro strojové učení, jako je PyTorch, převést ho do formátu ONNX a spotřebovat model ONNX v jiném rozhraní jako ML.NET.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. Další informace najdete na webu ONNX.To learn more, visit the ONNX website.

Diagram používaných formátů podporuje ONNX.

Předem vyškolený neYOLOv2 model je uložený ve formátu ONNX, serializovaná reprezentace vrstev a zjištěné vzory těchto vrstev.The pre-trained Tiny YOLOv2 model is stored in ONNX format, a serialized representation of the layers and learned patterns of those layers. V ML.NET se spolupráce s ONNX dosahuje pomocí balíčků NuGet ImageAnalytics a OnnxTransformer .In ML.NET, interoperability with ONNX is achieved with the ImageAnalytics and OnnxTransformer NuGet packages. Balíček ImageAnalytics obsahuje řadu transformací, které přijímají obrázek a zakódují je do numerických hodnot, které lze použít jako vstup do předpovědi nebo školicího kanálu.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. Balíček OnnxTransformer využívá modul runtime ONNX k načtení modelu ONNX a používá ho k tomu, aby předpovědi na základě poskytnutého vstupu.The OnnxTransformer package leverages the ONNX Runtime to load an ONNX model and use it to make predictions based on input provided.

Tok dat souboru ONNX do modulu runtime ONNX.

Nastavení projektu .NET CoreSet up the .NET Core project

Teď, když máte obecné informace o tom, co ONNX je a jak malý YOLOv2 funguje, je čas sestavit aplikaci.Now that you have a general understanding of what ONNX is and how Tiny YOLOv2 works, it's time to build the application.

Vytvoření konzolové aplikaceCreate a console application

  1. Vytvořte konzolovou aplikaci .NET Core nazvanou "ObjectDetection".Create a .NET Core Console Application called "ObjectDetection".

  2. Nainstalujte balíček NuGet Microsoft.ml:Install the Microsoft.ML NuGet Package:

    • V Průzkumník řešení klikněte pravým tlačítkem na projekt a vyberte Spravovat balíčky NuGet.In Solution Explorer, right-click on your project and select Manage NuGet Packages.
    • Jako zdroj balíčku zvolte "nuget.org", vyberte kartu Procházet a vyhledejte Microsoft.ml.Choose "nuget.org" as the Package source, select the Browse tab, search for Microsoft.ML.
    • Vyberte tlačítko instalovat .Select the Install button.
    • Pokud souhlasíte s licenčními podmínkami pro uvedené balíčky, klikněte na tlačítko OK v dialogovém okně Náhled změn a potom v dialogovém okně pro přijetí licence vyberte tlačítko přijmout .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.
    • Opakujte tyto kroky pro Microsoft. ml. ImageAnalytics a Microsoft. ml. OnnxTransformer.Repeat these steps for Microsoft.ML.ImageAnalytics and Microsoft.ML.OnnxTransformer.

Příprava dat a předem vyškoleného modeluPrepare your data and pre-trained model

  1. Stáhněte si soubor zip adresáře prostředků projektu a rozbalte ho.Download The project assets directory zip file and unzip.

  2. Zkopírujte adresář assets do adresáře projektu ObjectDetection .Copy the assets directory into your ObjectDetection project directory. Tento adresář a jeho podadresáře obsahují soubory obrázků (kromě nemalého modelu YOLOv2, který si stáhnete a přidáte v dalším kroku) potřebného pro tento kurz.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. Stáhněte si malý YOLOv2 model z modelu ONNX pro zoologickéiia rozbalte ho.Download the Tiny YOLOv2 model from the ONNX Model Zoo, and unzip.

    Otevřete příkazový řádek a zadejte následující příkaz:Open the command prompt and enter the following command:

    tar -xvzf tiny_yolov2.tar.gz
    
  4. Zkopírujte extrahovaný model.onnx soubor z adresáře, který se do projektu ObjectDetection assets\Model, a přejmenujte ho na TinyYolo2_model.onnx.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. Tento adresář obsahuje model potřebný pro tento kurz.This directory contains the model needed for this tutorial.

  5. V Průzkumník řešení klikněte pravým tlačítkem na každý ze souborů v adresáři assetů a podadresářích a vyberte vlastnosti.In Solution Explorer, right-click each of the files in the asset directory and subdirectories and select Properties. V části Upřesnitzměňte hodnotu Kopírovat do výstupního adresáře na Kopírovat, pokud je novější.Under Advanced, change the value of Copy to Output Directory to Copy if newer.

Vytváření tříd a definování cestCreate classes and define paths

Otevřete soubor program.cs a na začátek souboru přidejte následující dodatečné příkazy using: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;

Dále definujte cesty různých prostředků.Next, define the paths of the various assets.

  1. Nejprve do třídy Program přidejte metodu GetAbsolutePath pod metodu Main.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. Pak uvnitř metody Main vytvořte pole pro uložení umístění vašich assetů.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");
    

Přidejte do projektu nový adresář, do kterého se budou ukládat vstupní data a třídy předpovědi.Add a new directory to your project to store your input data and prediction classes.

V Průzkumník řešeníklikněte pravým tlačítkem myši na projekt a vyberte přidat novou složku > .In Solution Explorer, right-click the project, and then select Add > New Folder. Když se nová složka zobrazí v Průzkumník řešení, pojmenujte ji "datastructures".When the new folder appears in the Solution Explorer, name it "DataStructures".

Vytvořte vstupní datovou třídu v nově vytvořených adresářích Datastrukturas .Create your input data class in the newly created DataStructures directory.

  1. V Průzkumník řešeníklikněte pravým tlačítkem na adresář datastrukturas a pak vyberte Přidat novou položku > .In Solution Explorer, right-click the DataStructures directory, and then select Add > New Item.

  2. V dialogovém okně Přidat novou položku vyberte třída a změňte pole název na ImageNetData.cs.In the Add New Item dialog box, select Class and change the Name field to ImageNetData.cs. Pak vyberte tlačítko Přidat .Then, select the Add button.

    V editoru kódu se otevře soubor ImageNetData.cs .The ImageNetData.cs file opens in the code editor. Přidejte následující příkaz using do horní části ImageNetData.cs: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;
    

    Odeberte existující definici třídy a přidejte následující kód pro třídu ImageNetData do souboru ImageNetData.cs :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 je vstupní třída dat obrázku a má následující pole String:ImageNetData is the input image data class and has the following String fields:

    • ImagePath obsahuje cestu, kde je obrázek uložený.ImagePath contains the path where the image is stored.
    • Label obsahuje název souboru.Label contains the name of the file.

    Kromě toho ImageNetData obsahuje metodu ReadFromFile, která načte více souborů obrázků uložených v zadané cestě imageFolder a vrátí je jako kolekci objektů ImageNetData.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.

Vytvořte třídu předpovědi v adresáři datastruktuře .Create your prediction class in the DataStructures directory.

  1. V Průzkumník řešeníklikněte pravým tlačítkem na adresář datastrukturas a pak vyberte Přidat novou položku > .In Solution Explorer, right-click the DataStructures directory, and then select Add > New Item.

  2. V dialogovém okně Přidat novou položku vyberte třída a změňte pole název na ImageNetPrediction.cs.In the Add New Item dialog box, select Class and change the Name field to ImageNetPrediction.cs. Pak vyberte tlačítko Přidat .Then, select the Add button.

    V editoru kódu se otevře soubor ImageNetPrediction.cs .The ImageNetPrediction.cs file opens in the code editor. Přidejte následující příkaz using do horní části ImageNetPrediction.cs:Add the following using statement to the top of ImageNetPrediction.cs:

    using Microsoft.ML.Data;
    

    Odeberte existující definici třídy a přidejte následující kód pro třídu ImageNetPrediction do souboru ImageNetPrediction.cs :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 je třída dat předpovědi a má následující float[] pole:ImageNetPrediction is the prediction data class and has the following float[] field:

    • PredictedLabel obsahuje dimenze, skóre objektů a pravděpodobnosti třídy pro každé z ohraničujících polí zjištěných v obrázku.PredictedLabel contains the dimensions, objectness score and class probabilities for each of the bounding boxes detected in an image.

Inicializovat proměnné v MainInitialize variables in Main

Třída MLContext je výchozím bodem pro všechny operace ml.NET a inicializuje se mlContext vytvoří nové prostředí ml.NET, které se dá sdílet napříč objekty pracovního postupu pro vytváření modelů.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. Je podobné, koncepčně, DBContext v Entity Framework.It's similar, conceptually, to DBContext in Entity Framework.

Inicializujte proměnnou mlContext novou instancí MLContext přidáním následujícího řádku do metody Main program.cs pod polem outputFolder.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();

Vytvoření analyzátoru pro výstupy modelu po zpracováníCreate a parser to post-process model outputs

Model segmentuje obrázek do mřížky 13 x 13, kde je každá buňka mřížky 32px x 32px.The model segments an image into a 13 x 13 grid, where each grid cell is 32px x 32px. Každá buňka mřížky obsahuje 5 potenciálních ohraničovacích rámečků objektů.Each grid cell contains 5 potential object bounding boxes. Ohraničující rámeček má 25 prvků:A bounding box has 25 elements:

Ukázka mřížky vlevo a vzorek ohraničovacího rámečku na pravé straně

  • x pozici x středu ohraničovacího boxu vzhledem k buňce mřížky, ke které je přidružena.x the x position of the bounding box center relative to the grid cell it's associated with.
  • y pozice y ohraničovacího boxu vzhledem k buňce mřížky, ke které je přidružena.y the y position of the bounding box center relative to the grid cell it's associated with.
  • w šířka ohraničovacího rámečku.w the width of the bounding box.
  • h výška ohraničovacího rámečku.h the height of the bounding box.
  • o hodnota spolehlivosti, kterou objekt existuje v ohraničujícím poli, označovaný také jako skóre objektu.o the confidence value that an object exists within the bounding box, also known as objectness score.
  • p1-p20 pravděpodobnosti třídy pro každou 20 tříd předpovídat modelem.p1-p20 class probabilities for each of the 20 classes predicted by the model.

Celkem 25 prvků popisujících každé 5 ohraničujících polí tvoří prvky 125 obsažené v každé buňce mřížky.In total, the 25 elements describing each of the 5 bounding boxes make up the 125 elements contained in each grid cell.

Výstup vygenerovaný předučeným ONNX modelem je float Array délky 21125, který představuje prvky tensor s dimenzemi 125 x 13 x 13.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. Aby bylo možné transformovat předpovědi generované modelem na tensor, je nutné provést některé práce po zpracování.In order to transform the predictions generated by the model into a tensor, some post-processing work is required. Provedete to tak, že vytvoříte sadu tříd, které vám pomůžou analyzovat výstup.To do so, create a set of classes to help parse the output.

Přidáním nového adresáře do projektu můžete uspořádat sadu tříd analyzátoru.Add a new directory to your project to organize the set of parser classes.

  1. V Průzkumník řešeníklikněte pravým tlačítkem myši na projekt a vyberte přidat novou složku > .In Solution Explorer, right-click the project, and then select Add > New Folder. Když se nová složka zobrazí v Průzkumník řešení, pojmenujte ji "YoloParser".When the new folder appears in the Solution Explorer, name it "YoloParser".

Vytváření ohraničovacích rámečků a dimenzíCreate bounding boxes and dimensions

Výstup dat modelu obsahuje souřadnice a rozměry ohraničujících polí objektů v rámci obrázku.The data output by the model contains coordinates and dimensions of the bounding boxes of objects within the image. Vytvořte základní třídu pro dimenze.Create a base class for dimensions.

  1. V Průzkumník řešeníklikněte pravým tlačítkem na adresář YoloParser a pak vyberte Přidat novou položku > .In Solution Explorer, right-click the YoloParser directory, and then select Add > New Item.

  2. V dialogovém okně Přidat novou položku vyberte třída a změňte pole název na DimensionsBase.cs.In the Add New Item dialog box, select Class and change the Name field to DimensionsBase.cs. Pak vyberte tlačítko Přidat .Then, select the Add button.

    V editoru kódu se otevře soubor DimensionsBase.cs .The DimensionsBase.cs file opens in the code editor. Odeberte všechny příkazy using a definici existující třídy.Remove all using statements and existing class definition.

    Do souboru DimensionsBase.cs přidejte následující kód pro třídu DimensionsBase: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 má následující pole float:DimensionsBase has the following float fields:

    • X obsahuje pozici objektu podél osy x.X contains the position of the object along the x-axis.
    • Y obsahuje pozici objektu podél osy y.Y contains the position of the object along the y-axis.
    • Height obsahuje výšku objektu.Height contains the height of the object.
    • Width obsahuje šířku objektu.Width contains the width of the object.

Dále vytvořte třídu pro vaše ohraničovací rámečky.Next, create a class for your bounding boxes.

  1. V Průzkumník řešeníklikněte pravým tlačítkem na adresář YoloParser a pak vyberte Přidat novou položku > .In Solution Explorer, right-click the YoloParser directory, and then select Add > New Item.

  2. V dialogovém okně Přidat novou položku vyberte třída a změňte pole název na YoloBoundingBox.cs.In the Add New Item dialog box, select Class and change the Name field to YoloBoundingBox.cs. Pak vyberte tlačítko Přidat .Then, select the Add button.

    V editoru kódu se otevře soubor YoloBoundingBox.cs .The YoloBoundingBox.cs file opens in the code editor. Přidejte následující příkaz using do horní části YoloBoundingBox.cs:Add the following using statement to the top of YoloBoundingBox.cs:

    using System.Drawing;
    

    Hned nad existující definicí třídy přidejte novou definici třídy s názvem BoundingBoxDimensions, která dědí z třídy DimensionsBase, aby obsahovala rozměry příslušného ohraničovacího rámečku.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 { }
    

    Odeberte existující definici třídy YoloBoundingBox a přidejte následující kód pro třídu YoloBoundingBox do souboru YoloBoundingBox.cs :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 má následující pole:YoloBoundingBox has the following fields:

    • Dimensions obsahuje rozměry ohraničovacího rámečku.Dimensions contains dimensions of the bounding box.
    • Label obsahuje třídu objektu zjištěnou v ohraničujícím poli.Label contains the class of object detected within the bounding box.
    • Confidence obsahuje jistotu třídy.Confidence contains the confidence of the class.
    • Rect obsahuje obdélníkové znázornění rozměrů ohraničovacího rámečku.Rect contains the rectangle representation of the bounding box's dimensions.
    • BoxColor obsahuje barvu spojenou s příslušnou třídou použitou k vykreslení na obrázku.BoxColor contains the color associated with the respective class used to draw on the image.

Vytvoření analyzátoruCreate the parser

Nyní, když jsou vytvořeny třídy dimenzí a ohraničujících polí, je čas vytvořit analyzátor.Now that the classes for dimensions and bounding boxes are created, it's time to create the parser.

  1. V Průzkumník řešeníklikněte pravým tlačítkem na adresář YoloParser a pak vyberte Přidat novou položku > .In Solution Explorer, right-click the YoloParser directory, and then select Add > New Item.

  2. V dialogovém okně Přidat novou položku vyberte třída a změňte pole název na YoloOutputParser.cs.In the Add New Item dialog box, select Class and change the Name field to YoloOutputParser.cs. Pak vyberte tlačítko Přidat .Then, select the Add button.

    V editoru kódu se otevře soubor YoloOutputParser.cs .The YoloOutputParser.cs file opens in the code editor. Přidejte následující příkaz using do horní části YoloOutputParser.cs:Add the following using statement to the top of YoloOutputParser.cs:

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

    Uvnitř existující definice třídy YoloOutputParser přidejte vnořenou třídu obsahující rozměry každé buňky v obrázku.Inside the existing YoloOutputParser class definition, add a nested class that contains the dimensions of each of the cells in the image. Přidejte následující kód pro třídu CellDimensions, která dědí z třídy DimensionsBase v horní části definice třídy YoloOutputParser.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. Uvnitř definice třídy YoloOutputParser přidejte následující konstantu a pole.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 je počet řádků v mřížce, na který je obrázek rozdělen.ROW_COUNT is the number of rows in the grid the image is divided into.
    • COL_COUNT je počet sloupců v mřížce, na který je obrázek rozdělen.COL_COUNT is the number of columns in the grid the image is divided into.
    • CHANNEL_COUNT je celkový počet hodnot obsažených v jedné buňce mřížky.CHANNEL_COUNT is the total number of values contained in one cell of the grid.
    • BOXES_PER_CELL je počet ohraničujících polí v buňce,BOXES_PER_CELL is the number of bounding boxes in a cell,
    • BOX_INFO_FEATURE_COUNT je počet funkcí obsažených v poli (x, y, Height, Width, důvěrné).BOX_INFO_FEATURE_COUNT is the number of features contained within a box (x,y,height,width,confidence).
    • CLASS_COUNT je počet tříd předpovědi obsažených v každém ohraničujícím poli.CLASS_COUNT is the number of class predictions contained in each bounding box.
    • CELL_WIDTH je šířka jedné buňky v mřížce obrázku.CELL_WIDTH is the width of one cell in the image grid.
    • CELL_HEIGHT je výška jedné buňky v mřížce obrázku.CELL_HEIGHT is the height of one cell in the image grid.
    • channelStride je počáteční pozice aktuální buňky v mřížce.channelStride is the starting position of the current cell in the grid.

    Když model vytvoří předpověď, označovanou také jako bodování, rozdělí vstupní obrázek 416px x 416px do mřížky buněk o velikost 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. Každá buňka obsahuje hodnotu 32px x 32px.Each cell contains is 32px x 32px. V každé buňce jsou 5 ohraničujících polí, z nichž každý obsahuje 5 funkcí (x, y, Šířka, Výška, spolehlivost).Within each cell, there are 5 bounding boxes each containing 5 features (x, y, width, height, confidence). Kromě toho každý ohraničovací rámeček obsahuje pravděpodobnost každé třídy, která v tomto případě je 20.In addition, each bounding box contains the probability of each of the classes which in this case is 20. Každá buňka proto obsahuje 125 informací (5 funkcí + 20 pravděpodobností třídy).Therefore, each cell contains 125 pieces of information (5 features + 20 class probabilities).

Vytvořte seznam ukotvení níže channelStride pro všechna 5 ohraničená pole: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
};

Kotvy jsou předem definované poměry výšky a šířky ohraničujících polí.Anchors are pre-defined height and width ratios of bounding boxes. Většina objektů nebo tříd zjištěných modelem má podobné poměry.Most object or classes detected by a model have similar ratios. To je výhodné při vytváření ohraničujících rámečků.This is valuable when it comes to creating bounding boxes. Místo předpovědi ohraničujících polí se počítá posun z předdefinovaných dimenzí a proto se zkrátí výpočet vyžadovaný pro předpověď ohraničovacího rámečku.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. Tyto poměry kotvy jsou obvykle počítány na základě použité datové sady.Typically these anchor ratios are calculated based on the dataset used. V tomto případě, protože je známá datová sada a hodnoty byly předem vypočítány, kotvy mohou být pevně kódované.In this case because the dataset is known and the values have been pre-computed, the anchors can be hard-coded.

Dále definujte popisky nebo třídy, které bude model předpovědět.Next, define the labels or classes that the model will predict. Tento model předpovídá 20 tříd, které jsou podmnožinou celkového počtu tříd předpokládaných původním YOLOv2m modelem.This model predicts 20 classes which is a subset of the total number of classes predicted by the original YOLOv2 model.

Přidejte seznam popisků pod anchors.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"
};

Ke každé z těchto tříd jsou přiřazeny barvy.There are colors associated with each of the classes. Přiřaďte barvy vaší třídy pod labels: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
};

Vytváření pomocných funkcíCreate helper functions

Ve fázi následného zpracování se účastní řada kroků.There are a series of steps involved in the post-processing phase. V takovém případě je možné využít několik pomocných metod.To help with that, several helper methods can be employed.

Pomocné metody používané v analyzátoru jsou:The helper methods used in by the parser are:

  • Sigmoid použije funkci sigmoid, která vypíše číslo mezi 0 a 1.Sigmoid applies the sigmoid function that outputs a number between 0 and 1.
  • Softmax normalizuje vstupní vektor na rozdělení pravděpodobnosti.Softmax normalizes an input vector into a probability distribution.
  • GetOffset mapuje prvky ve výstupu jednorozměrného modelu na odpovídající pozici v rámci 125 x 13 x 13 tensor.GetOffset maps elements in the one-dimensional model output to the corresponding position in a 125 x 13 x 13 tensor.
  • ExtractBoundingBoxes extrahuje dimenze ohraničovacího rámečku pomocí metody GetOffset z výstupu modelu.ExtractBoundingBoxes extracts the bounding box dimensions using the GetOffset method from the model output.
  • GetConfidence extrahuje hodnotu spolehlivosti, která určuje, jak se v modelu zjistilo, že se objekt detekoval, a pomocí funkce Sigmoid ho převeďte na procento.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 používá rozměry ohraničovacího boxu a mapuje je na příslušnou buňku v rámci obrázku.MapBoundingBoxToCell uses the bounding box dimensions and maps them onto its respective cell within the image.
  • ExtractClasses extrahuje třídu předpovědi pro ohraničující rámeček z výstupu modelu pomocí metody GetOffset a převede je na rozdělení pravděpodobnosti pomocí metody Softmax.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 vybere třídu ze seznamu předpokládaných tříd s nejvyšší pravděpodobností.GetTopResult selects the class from the list of predicted classes with the highest probability.
  • filtry IntersectionOverUnion překrývají ohraničovací rámečky s nižší pravděpodobností.IntersectionOverUnion filters overlapping bounding boxes with lower probabilities.

Přidejte kód pro všechny pomocné metody pod seznam classColors.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);
}

Po definování všech pomocných metod je čas je použít ke zpracování výstupu modelu.Once you have defined all of the helper methods, it's time to use them to process the model output.

Pod metodou IntersectionOverUnion vytvořte metodu ParseOutputs pro zpracování výstupu generovaného modelem.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)
{

}

Vytvořte seznam pro uložení vašich ohraničovacích polí a definujte proměnné uvnitř metody ParseOutputs.Create a list to store your bounding boxes and define variables inside the ParseOutputs method.

var boxes = new List<YoloBoundingBox>();

Každý obrázek je rozdělen do mřížky 13 x 13 buněk.Each image is divided into a grid of 13 x 13 cells. Každá buňka obsahuje pět ohraničujících rámečků.Each cell contains five bounding boxes. Pod proměnnou boxes přidejte kód pro zpracování všech polí v každé z buněk.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++)
        {

        }
    }
}

Uvnitř cyklické smyčky Vypočítejte počáteční pozici aktuálního pole ve výstupu jednorozměrného modelu.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));

Přímo pod ním použijte metodu ExtractBoundingBoxDimensions pro získání rozměrů aktuálního ohraničovacího boxu.Directly below that, use the ExtractBoundingBoxDimensions method to get the dimensions of the current bounding box.

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

Pak použijte metodu GetConfidence, abyste získali jistotu pro aktuální ohraničovací rámeček.Then, use the GetConfidence method to get the confidence for the current bounding box.

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

Potom použijte metodu MapBoundingBoxToCell k namapování aktuálního ohraničovacího boxu na aktuálně zpracovávanou buňku.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);

Před provedením dalšího zpracování ověřte, zda je hodnota spolehlivosti větší, než je zadaná prahová hodnota.Before doing any further processing, check whether your confidence value is greater than the threshold provided. Pokud ne, zpracujte další ohraničovací rámeček.If not, process the next bounding box.

if (confidence < threshold)
    continue;

V opačném případě pokračujte ve zpracování výstupu.Otherwise, continue processing the output. Dalším krokem je získání pravděpodobnosti rozdělení předpokládaných tříd pro aktuální ohraničovací rámeček pomocí metody ExtractClasses.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);

Pak použijte metodu GetTopResult pro získání hodnoty a indexu třídy s největší pravděpodobností pro aktuální pole a výpočet jeho skóre.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;

Pomocí topScore můžete znovu zachovat pouze ty ohraničovací schránky, které jsou nad určenou prahovou hodnotou.Use the topScore to once again keep only those bounding boxes that are above the specified threshold.

if (topScore < threshold)
    continue;

Nakonec, pokud aktuální ohraničující rámeček překračuje prahovou hodnotu, vytvořte nový objekt BoundingBox a přidejte ho do seznamu boxes.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]
});

Po zpracování všech buněk v imagi vrátí seznam boxes.Once all cells in the image have been processed, return the boxes list. Přidejte následující návratový příkaz pod vnější smyčkou for-Loop v metodě ParseOutputs.Add the following return statement below the outer-most for-loop in the ParseOutputs method.

return boxes;

Filtrovat překrývající se rámečkyFilter overlapping boxes

Teď, když se všechna vysoce důvěrně ohraničená pole extrahují z výstupu modelu, je potřeba provést další filtrování, aby se překrývající image odebraly.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. Přidejte metodu s názvem FilterBoundingBoxes pod metodou ParseOutputs:Add a method called FilterBoundingBoxes below the ParseOutputs method:

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

}

Uvnitř metody FilterBoundingBoxes můžete začít tím, že vytvoříte pole rovnající se velikosti zjištěných polí a označíte všechny sloty jako aktivní nebo připravené ke zpracování.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;

Pak seřaďte seznam obsahující vaše ohraničující pole v sestupném pořadí na základě jistoty.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();

Potom vytvořte seznam pro ukládání filtrovaných výsledků.After that, create a list to hold the filtered results.

var results = new List<YoloBoundingBox>();

Zahajte zpracování každého ohraničovacího rámečku tak, že do každého ohraničovacího pole zahájíte iteraci.Begin processing each bounding box by iterating over each of the bounding boxes.

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

}

Uvnitř tohoto cyklu for-Loop ověřte, zda je možné zpracovat aktuální ohraničovací rámeček.Inside of this for-loop, check whether the current bounding box can be processed.

if (isActiveBoxes[i])
{

}

Pokud ano, přidejte ohraničovací rámeček do seznamu výsledků.If so, add the bounding box to the list of results. Pokud výsledky překročí zadaný limit pro extrakci, zrušte rozdělení smyčky.If the results exceeds the specified limit of boxes to be extracted, break out of the loop. Do příkazu if-přidejte následující kód.Add the following code inside the if-statement.

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

if (results.Count >= limit)
    break;

V opačném případě se podívejte na sousedící ohraničovací rámečky.Otherwise, look at the adjacent bounding boxes. Pod kontrolu limitu pole přidejte následující kód.Add the following code below the box limit check.

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

}

Podobně jako v prvním poli, pokud je sousední pole aktivní nebo připravené ke zpracování, použijte metodu IntersectionOverUnion a ověřte, zda první pole a druhé pole překročí zadanou prahovou hodnotu.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. Přidejte následující kód do vnitřní smyčky for Loop.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;
    }
}

Mimo vnitřní smyčku for-Loop, která kontroluje sousední ohraničená pole, se podívejte, zda existují zbývající ohraničovací rámečky, které mají být zpracovány.Outside of the inner-most for-loop that checks adjacent bounding boxes, see whether there are any remaining bounding boxes to be processed. Pokud ne, přerušte vnější smyčka for-Loop.If not, break out of the outer for-loop.

if (activeCount <= 0)
    break;

Nakonec mimo počáteční smyčku metody FilterBoundingBoxes vrátí výsledky:Finally, outside of the initial for-loop of the FilterBoundingBoxes method, return the results:

return results;

Skvělé!Great! Nyní je čas použít tento kód spolu s modelem pro bodování.Now it's time to use this code along with the model for scoring.

Použití modelu pro bodováníUse the model for scoring

Stejně jako u následného zpracování je několik kroků v postupu hodnocení.Just like with post-processing, there are a few steps in the scoring steps. K tomu je potřeba přidat třídu, která bude obsahovat logiku bodování pro váš projekt.To help with this, add a class that will contain the scoring logic to your project.

  1. V Průzkumník řešeníklikněte pravým tlačítkem myši na projekt a vyberte možnost přidat novou položku > .In Solution Explorer, right-click the project, and then select Add > New Item.

  2. V dialogovém okně Přidat novou položku vyberte třída a změňte pole název na OnnxModelScorer.cs.In the Add New Item dialog box, select Class and change the Name field to OnnxModelScorer.cs. Pak vyberte tlačítko Přidat .Then, select the Add button.

    V editoru kódu se otevře soubor OnnxModelScorer.cs .The OnnxModelScorer.cs file opens in the code editor. Přidejte následující příkaz using do horní části OnnxModelScorer.cs: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;
    

    Do definice třídy OnnxModelScorer přidejte následující proměnné.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>();
    

    Přímo pod tím vytvořte konstruktor pro třídu OnnxModelScorer, která inicializuje dříve definované proměnné.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;
    }
    

    Po vytvoření konstruktoru definujte několik struktur, které obsahují proměnné související s nastavením obrázku a modelu.Once you have created the constructor, define a couple of structs that contain variables related to the image and model settings. Vytvořte strukturu s názvem ImageNetSettings, která bude obsahovat jako vstup pro model očekávanou výšku a šířku.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;
    }
    

    Potom vytvořte další strukturu s názvem TinyYoloModelSettings, která obsahuje názvy vstupní a výstupní vrstvy modelu.After that, create another struct called TinyYoloModelSettings which contains the names of the input and output layers of the model. Chcete-li vizualizovat název vstupní a výstupní vrstvy modelu, můžete použít nástroj, jako je Netron.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";
    }
    

    Dále vytvořte první sadu metod, které se použijí pro bodování.Next, create the first set of methods use for scoring. Vytvořte metodu LoadModel uvnitř vaší třídy OnnxModelScorer.Create the LoadModel method inside of your OnnxModelScorer class.

    private ITransformer LoadModel(string modelLocation)
    {
    
    }
    

    Uvnitř metody LoadModel přidejte následující kód pro protokolování.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})");
    

    Kanály ML.NET musí znát schéma dat, aby fungovalo při volání metody Fit .ML.NET pipelines need to know the data schema to operate on when the Fit method is called. V takovém případě se použije proces podobný školení.In this case, a process similar to training will be used. Vzhledem k tomu, že nedojde k žádnému skutečnému školení, je přijatelné použít prázdné IDataView.However, because no actual training is happening, it is acceptable to use an empty IDataView. Vytvoří nový IDataView pro kanál z prázdného seznamu.Create a new IDataView for the pipeline from an empty list.

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

    Níže definujte kanál.Below that, define the pipeline. Kanál se skládá ze čtyř transformací.The pipeline will consist of four transforms.

    • LoadImages načte obrázek v podobě rastrového obrázku.LoadImages loads the image as a Bitmap.
    • ResizeImages změní měřítko obrázku na zadanou velikost (v tomto případě 416 x 416).ResizeImages rescales the image to the size specified (in this case, 416 x 416).
    • ExtractPixels změní reprezentace obrázku z rastrového obrázku na číselný vektor.ExtractPixels changes the pixel representation of the image from a Bitmap to a numerical vector.
    • ApplyOnnxModel NAČTE model ONNX a použije ho ke stanovení skóre z poskytnutých dat.ApplyOnnxModel loads the ONNX model and uses it to score on the data provided.

    V metodě LoadModel pod proměnnou data definujte svůj kanál.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 }));
    

    Nyní je čas vytvořit instanci modelu pro bodování.Now it's time to instantiate the model for scoring. Zavolejte metodu Fit na kanálu a vraťte ji k dalšímu zpracování.Call the Fit method on the pipeline and return it for further processing.

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

Po načtení modelu je možné ho použít k vytvoření předpovědi.Once the model is loaded, it can then be used to make predictions. Pro usnadnění tohoto procesu vytvořte metodu s názvem PredictDataUsingModel pod metodou LoadModel.To facilitate that process, create a method called PredictDataUsingModel below the LoadModel method.

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

}

Uvnitř PredictDataUsingModel přidejte následující kód pro protokolování.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("");

Pak použijte metodu Transform pro vyhodnocení dat.Then, use the Transform method to score the data.

IDataView scoredData = model.Transform(testData);

Extrakce předpokládaných pravděpodobností a jejich vrácení pro další zpracování.Extract the predicted probabilities and return them for additional processing.

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

return probabilities;

Teď, když jsou oba kroky nastavené, je zkombinovat do jediné metody.Now that both steps are set up, combine them into a single method. Pod metodou PredictDataUsingModel přidejte novou metodu nazvanou Score.Below the PredictDataUsingModel method, add a new method called Score.

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

    return PredictDataUsingModel(data, model);
}

Už to skoro je!Almost there! Teď je čas na to, aby se všechno používalo.Now it's time to put it all to use.

Detekovat objektyDetect objects

Po dokončení celého nastavení je čas detekovat některé objekty.Now that all of the setup is complete, it's time to detect some objects. Začněte tím, že přidáte odkazy na skore a analyzátor ve vaší třídě program.cs .Start off by adding references to the scorer and parser in your Program.cs class.

using ObjectDetection.YoloParser;
using ObjectDetection.DataStructures;

Skóre a analýza výstupů modeluScore and parse model outputs

Uvnitř metody Main třídy program.cs přidejte příkaz try-catch.Inside the Main method of your Program.cs class, add a try-catch statement.

try
{

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

Uvnitř bloku try spusťte implementaci logiky detekce objektů.Inside of the try block, start implementing the object detection logic. Nejdřív načtěte data do IDataView.First, load the data into an IDataView.

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

Pak vytvořte instanci OnnxModelScorer a použijte ji k vyhodnocení načtených dat.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);

Nyní je čas pro krok po zpracování.Now it's time for the post-processing step. Vytvořte instanci YoloOutputParser a použijte ji ke zpracování výstupu modelu.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));

Až se výstup modelu zpracuje, je čas vykreslit ohraničovací rámečky na obrázcích.Once the model output has been processed, it's time to draw the bounding boxes on the images.

Vizualizovat předpovědiVisualize predictions

Poté, co model vyhodnotí obrázky a byly zpracovány výstupy, musí být ohraničovací pole vykreslena na obrázku.After the model has scored the images and the outputs have been processed, the bounding boxes have to be drawn on the image. K tomu přidejte metodu s názvem DrawBoundingBox pod metodou GetAbsolutePath v rámci program.cs.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)
{

}

Nejprve načtěte obrázek a získejte rozměry výšky a šířky v metodě DrawBoundingBox.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;

Pak vytvořte smyčku For-Each k iterování každého ohraničovacího pole zjištěného modelem.Then, create a for-each loop to iterate over each of the bounding boxes detected by the model.

foreach (var box in filteredBoundingBoxes)
{

}

Uvnitř smyčky for-each Získejte rozměry ohraničovacího rámečku.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);

Vzhledem k tomu, že rozměry ohraničovacího rámečku odpovídají vstupnímu typu 416 x 416, Škálujte rozměry ohraničovacího pole tak, aby odpovídaly skutečné velikosti obrázku.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;

Pak definujte šablonu pro text, který se zobrazí nad každým ohraničujícím polem.Then, define a template for text that will appear above each bounding box. Text bude obsahovat třídu objektu uvnitř příslušného ohraničovacího rámečku a také jistotu.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")}%)";

Chcete-li kreslit na obrázek, převeďte jej na objekt Graphics .In order to draw on the image, convert it to a Graphics object.

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

}

V bloku kódu using vyladění nastavení objektu Graphics grafiky.Inside the using code block, tune the graphic's Graphics object settings.

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

Níže můžete nastavit možnosti písma a barvy pro text a ohraničovací rámeček.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);

Vytvořte a vyplňte obdélník nad ohraničujícím polem tak, aby obsahoval text pomocí metody FillRectangle .Create and fill a rectangle above the bounding box to contain the text using the FillRectangle method. To vám pomůže kontrastovat text a zlepšit čitelnost.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);

Pak nakreslete text a ohraničovací rámeček na obrázku pomocí metod DrawString a 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);

Mimo smyčku For-Each přidejte kód pro uložení obrázků v outputDirectory.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));

Pro další zpětnou vazbu, že aplikace provádí předpovědi podle očekávání za běhu, přidejte metodu s názvem LogDetectedObjects pod metodou DrawBoundingBox v souboru program.cs pro výstup zjištěných objektů do konzoly.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("");
}

Teď, když máte pomocné metody k vytvoření vizuální zpětné vazby z předpovědi, přidejte smyčku for-Loop k iterování všech imagí s hodnocením.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++)
{

}

Uvnitř smyčky for-Loop Získá název souboru obrázku a ohraničená pole, která jsou k němu přidružená.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);

Níže použijte metodu DrawBoundingBox pro vykreslení ohraničujících polí na obrázku.Below that, use the DrawBoundingBox method to draw the bounding boxes on the image.

DrawBoundingBox(imagesFolder, outputFolder, imageFileName, detectedObjects);

Nakonec použijte metodu LogDetectedObjects pro výstup předpovědi do konzoly.Lastly, use the LogDetectedObjects method to output predictions to the console.

LogDetectedObjects(imageFileName, detectedObjects);

Po příkazu try-catch přidejte další logiku, která indikuje, že proces je spuštěný.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();

A je to!That's it!

VýsledkyResults

Po provedení předchozích kroků spusťte konzolovou aplikaci (CTRL + F5).After following the previous steps, run your console app (Ctrl + F5). Výsledky by měly být podobné následujícímu výstupu.Your results should be similar to the following output. Můžou se zobrazovat upozornění nebo zprávy o zpracování, ale tyto zprávy se z následujících výsledků odebraly z důvodu srozumitelnosti.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 ========

Chcete-li zobrazit obrázky s ohraničujícími poli, přejděte do adresáře assets/images/output/.To see the images with bounding boxes, navigate to the assets/images/output/ directory. Níže je ukázka z jedné ze zpracovaných imagí.Below is a sample from one of the processed images.

Ukázka zpracovaného obrázku dinning místnosti

Blahopřejeme!Congratulations! Nyní jste úspěšně vytvořili model strojového učení pro detekci objektů opětovným použitím předem připraveného modelu ONNX v ML.NET.You've now successfully built a machine learning model for object detection by reusing a pre-trained ONNX model in ML.NET.

Zdrojový kód pro tento kurz najdete v úložišti dotnet/machinelearning-Samples .You can find the source code for this tutorial at the dotnet/machinelearning-samples repository.

V tomto kurzu jste zjistili, jak:In this tutorial, you learned how to:

  • Pochopení problémuUnderstand the problem
  • Zjistěte, co je ONNX a jak funguje s ML.NETLearn what ONNX is and how it works with ML.NET
  • Pochopení modeluUnderstand the model
  • Opakované použití předem připraveného modeluReuse the pre-trained model
  • Detekovat objekty s načteným modelemDetect objects with a loaded model

Podívejte se na úložiště GitHub Samples Machine Learning a prozkoumejte ukázku detekce rozbaleného objektu.Check out the Machine Learning samples GitHub repository to explore an expanded object detection sample.