Share via


Zelfstudie: Objecten detecteren met BEHULP van ONNX in ML.NET

Meer informatie over het gebruik van een vooraf getraind ONNX-model in ML.NET om objecten in afbeeldingen te detecteren.

Voor het trainen van een volledig nieuw objectdetectiemodel moeten miljoenen parameters, een grote hoeveelheid gelabelde trainingsgegevens en een grote hoeveelheid rekenresources (honderden GPU-uren) worden ingesteld. Met behulp van een vooraf getraind model kunt u het trainingsproces snel doorlopen.

In deze zelfstudie leert u het volgende:

  • Het probleem begrijpen
  • Ontdek wat ONNX is en hoe het werkt met ML.NET
  • Inzicht in het model
  • Het vooraf getrainde model opnieuw gebruiken
  • Objecten detecteren met een geladen model

Vereisten

Overzicht van voorbeeld van ONNX-objectdetectie

In dit voorbeeld wordt een .NET Core-consoletoepassing gemaakt waarmee objecten in een afbeelding worden gedetecteerd met behulp van een vooraf getraind DEEP Learning ONNX-model. De code voor dit voorbeeld vindt u in de opslagplaats dotnet/machinelearning-samples op GitHub.

Wat is objectdetectie?

Objectdetectie is een computer vision-probleem. Hoewel objectdetectie nauw verwant is aan afbeeldingsclassificatie, wordt de classificatie van afbeeldingen op een gedetailleerdere schaal uitgevoerd. Objectdetectie zoekt en categoriseert entiteiten binnen afbeeldingen. Objectdetectiemodellen worden vaak getraind met behulp van deep learning en neurale netwerken. Zie Deep Learning versus machine learning voor meer informatie.

Objectdetectie gebruiken wanneer afbeeldingen meerdere objecten van verschillende typen bevatten.

Screenshots showing Image Classification versus Object Classification.

Enkele gebruiksvoorbeelden voor objectdetectie zijn:

  • Zelfrijdende auto's
  • Robotica
  • Gezichtsdetectie
  • Veiligheid op de werkplek
  • Object tellen
  • Activiteitsherkenning

Een Deep Learning-model selecteren

Deep learning is een subset van machine learning. Voor het trainen van Deep Learning-modellen zijn grote hoeveelheden gegevens vereist. Patronen in de gegevens worden vertegenwoordigd door een reeks lagen. De relaties in de gegevens worden gecodeerd als verbindingen tussen de lagen met gewichten. Hoe hoger het gewicht, hoe sterker de relatie. Gezamenlijk worden deze reeks lagen en verbindingen kunstmatige neurale netwerken genoemd. Hoe meer lagen in een netwerk, hoe dieper het is, waardoor het een diep neuraal netwerk wordt.

Er zijn verschillende soorten neurale netwerken, de meest voorkomende multi-layered Perceptron (MLP), Convolutional Neural Network (CNN) en terugkerende neurale netwerken (RNN). De meest eenvoudige is de MLP, waarmee een set invoer wordt toegewezen aan een set uitvoerwaarden. Dit neurale netwerk is goed wanneer de gegevens geen ruimtelijk of tijdonderdeel hebben. De CNN maakt gebruik van convolutionele lagen om ruimtelijke informatie in de gegevens te verwerken. Een goed gebruiksvoorbeeld voor CNN's is het verwerken van afbeeldingen om de aanwezigheid van een functie in een regio van een afbeelding te detecteren (is er bijvoorbeeld een neus in het midden van een afbeelding?). Ten slotte zorgen RNN's ervoor dat de persistentie van de status of het geheugen als invoer kan worden gebruikt. RNN's worden gebruikt voor tijdreeksanalyse, waarbij de volgorde en context van gebeurtenissen belangrijk zijn.

Inzicht in het model

Objectdetectie is een taak voor het verwerken van afbeeldingen. Daarom zijn de meeste deep learning-modellen die zijn getraind om dit probleem op te lossen CNN's. Het model dat in deze zelfstudie wordt gebruikt, is het Tiny YOLOv2-model, een compactere versie van het YOLOv2-model dat wordt beschreven in het document: "YOLO9000: Better, Faster, Stronger" door Redmon en Farhadi. Kleine YOLOv2 wordt getraind op de Pascal VOC-gegevensset en bestaat uit 15 lagen die 20 verschillende klassen objecten kunnen voorspellen. Omdat Tiny YOLOv2 een verkorte versie van het oorspronkelijke YOLOv2-model is, wordt er een compromis gemaakt tussen snelheid en nauwkeurigheid. De verschillende lagen waaruit het model bestaat, kunnen worden gevisualiseerd met behulp van hulpprogramma's zoals Netron. Het inspecteren van het model zou een toewijzing opleveren van de verbindingen tussen alle lagen waaruit het neurale netwerk bestaat, waarbij elke laag de naam van de laag samen met de dimensies van de respectieve invoer/uitvoer zou bevatten. De gegevensstructuren die worden gebruikt om de invoer en uitvoer van het model te beschrijven, worden tensors genoemd. Tensors kunnen worden beschouwd als containers die gegevens opslaan in N-dimensies. In het geval van Tiny YOLOv2 is de naam van de invoerlaag image en verwacht het een tensor van dimensies 3 x 416 x 416. De naam van de uitvoerlaag is grid en genereert een uitvoertenant van dimensies 125 x 13 x 13.

Input layer being split into hidden layers, then output layer

Het YOLO-model maakt een afbeelding 3(RGB) x 416px x 416px. Het model neemt deze invoer en geeft deze door de verschillende lagen om een uitvoer te produceren. De uitvoer verdeelt de invoerafbeelding in een 13 x 13 raster, waarbij elke cel in het raster bestaat uit 125 waarden.

Wat is een ONNX-model?

De Open Neural Network Exchange (ONNX) is een opensource-indeling voor AI-modellen. ONNX ondersteunt interoperabiliteit tussen frameworks. Dit betekent dat u een model kunt trainen in een van de vele populaire machine learning-frameworks zoals PyTorch, het kunt converteren naar ONNX-indeling en het ONNX-model in een ander framework kunt gebruiken, zoals ML.NET. Ga naar de ONNX-website voor meer informatie.

Diagram of ONNX supported formats being used.

Het vooraf getrainde Tiny YOLOv2-model wordt opgeslagen in ONNX-indeling, een geserialiseerde weergave van de lagen en geleerde patronen van deze lagen. In ML.NET wordt de interoperabiliteit met ONNX bereikt met de ImageAnalytics en OnnxTransformer NuGet-pakketten. Het ImageAnalytics pakket bevat een reeks transformaties die een afbeelding maken en coderen in numerieke waarden die kunnen worden gebruikt als invoer in een voorspellings- of trainingspijplijn. Het OnnxTransformer pakket maakt gebruik van de ONNX Runtime om een ONNX-model te laden en te gebruiken om voorspellingen te doen op basis van de verstrekte invoer.

Data flow of ONNX file into the ONNX Runtime.

Het .NET Console-project instellen

Nu u een algemeen begrip hebt van wat ONNX is en hoe Tiny YOLOv2 werkt, is het tijd om de toepassing te bouwen.

Een consoletoepassing maken

  1. Maak een C# -consoletoepassing met de naam ObjectDetection. Klik op de knop Volgende.

  2. Kies .NET 6 als het framework dat u wilt gebruiken. Klik op de knop Maken.

  3. Installeer het Microsoft.ML NuGet-pakket:

    Notitie

    In dit voorbeeld wordt de meest recente stabiele versie van de Vermelde NuGet-pakketten gebruikt, tenzij anders vermeld.

    • Klik in Solution Explorer met de rechtermuisknop op uw project en selecteer NuGet-pakketten beheren.
    • Kies 'nuget.org' als pakketbron, selecteer het tabblad Bladeren, zoek naar Microsoft.ML.
    • Selecteer de knop Installeren .
    • Selecteer de knop OK in het dialoogvenster Voorbeeld van wijzigingen en selecteer vervolgens de knop Accepteren in het dialoogvenster Acceptatie van licenties als u akkoord gaat met de licentievoorwaarden voor de vermelde pakketten.
    • Herhaal deze stappen voor Microsoft.Windows.Compatibility, Microsoft.ML.ImageAnalytics, Microsoft.ML.OnnxTransformer en Microsoft.ML.OnnxRuntime.

Uw gegevens en vooraf getraind model voorbereiden

  1. Download het zip-bestand van de map projectactiva en pak het uit.

  2. Kopieer de map naar de assetsprojectmap ObjectDetection . Deze map en de bijbehorende submappen bevatten de afbeeldingsbestanden (met uitzondering van het Tiny YOLOv2-model, dat u in de volgende stap gaat downloaden en toevoegen) die nodig zijn voor deze zelfstudie.

  3. Download het Tiny YOLOv2-model uit de ONNX Model Zoo.

  4. Kopieer het model.onnx bestand naar de projectmap assets\Model ObjectDetection en wijzig de naam ervan in TinyYolo2_model.onnx. Deze map bevat het model dat nodig is voor deze zelfstudie.

  5. Klik in Solution Explorer met de rechtermuisknop op elk van de bestanden in de assetmap en submappen en selecteer Eigenschappen. Wijzig onder Geavanceerd de waarde van Kopiëren naar Uitvoermap om te kopiëren als nieuwer.

Klassen maken en paden definiëren

Open het bestand Program.cs en voeg de volgende aanvullende using instructies toe aan het begin van het bestand:

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

Definieer vervolgens de paden van de verschillende assets.

  1. Maak eerst de GetAbsolutePath methode onderaan het bestand Program.cs .

    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. Maak vervolgens onder de using-instructies velden om de locatie van uw assets op te slaan.

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

Voeg een nieuwe map toe aan uw project om uw invoergegevens en voorspellingsklassen op te slaan.

Klik in Solution Explorer met de rechtermuisknop op het project en selecteer Nieuwe map toevoegen>. Wanneer de nieuwe map wordt weergegeven in Solution Explorer, geeft u deze de naam 'DataStructures'.

Maak uw invoergegevensklasse in de zojuist gemaakte DataStructures-map .

  1. Klik in Solution Explorer met de rechtermuisknop op de map DataStructures en selecteer Nieuw item toevoegen>.

  2. Selecteer klasse in het dialoogvenster Nieuw item toevoegen en wijzig het veld Naam in ImageNetData.cs. Selecteer vervolgens de knop Toevoegen .

    Het bestand ImageNetData.cs wordt geopend in de code-editor. Voeg de volgende using instructie toe aan het begin van ImageNetData.cs:

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

    Verwijder de bestaande klassedefinitie en voeg de volgende code voor de ImageNetData klasse toe aan het bestand ImageNetData.cs :

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

    ImageNetData is de klasse invoerafbeeldingsgegevens en heeft de volgende String velden:

    • ImagePath bevat het pad waarin de afbeelding is opgeslagen.
    • Label bevat de naam van het bestand.

    ImageNetData Daarnaast bevat een methode ReadFromFile waarmee meerdere afbeeldingsbestanden worden geladen die zijn opgeslagen in het imageFolder opgegeven pad en worden geretourneerd als een verzameling ImageNetData objecten.

Maak uw voorspellingsklasse in de map DataStructures .

  1. Klik in Solution Explorer met de rechtermuisknop op de map DataStructures en selecteer Nieuw item toevoegen>.

  2. Selecteer in het dialoogvenster Nieuw item toevoegen de optie Klasse en wijzig het veld Naam in ImageNetPrediction.cs. Selecteer vervolgens de knop Toevoegen .

    Het bestand ImageNetPrediction.cs wordt geopend in de code-editor. Voeg de volgende using instructie toe aan het begin van ImageNetPrediction.cs:

    using Microsoft.ML.Data;
    

    Verwijder de bestaande klassedefinitie en voeg de volgende code voor de ImageNetPrediction klasse toe aan het bestand ImageNetPrediction.cs :

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

    ImageNetPrediction is de voorspellingsgegevensklasse en heeft het volgende float[] veld:

    • PredictedLabels bevat de dimensies, objectnessscore en klassekansen voor elk van de begrenzingsvakken die in een afbeelding zijn gedetecteerd.

Variabelen initialiseren

De MLContext-klasse is een uitgangspunt voor alle ML.NET bewerkingen en het initialiseren mlContext van een nieuwe ML.NET-omgeving die kan worden gedeeld in de werkstroomobjecten voor het maken van modellen. Het is vergelijkbaar, conceptueel gezien, met DBContext in Entity Framework.

Initialiseer de mlContext variabele met een nieuw exemplaar van MLContext door de volgende regel onder het outputFolder veld toe te voegen.

MLContext mlContext = new MLContext();

Een parser maken voor uitvoer na het procesmodel

Het model segmenteert een afbeelding in een 13 x 13 raster, waarbij elke rastercel zich bevindt 32px x 32px. Elke rastercel bevat 5 potentiële objectbegrenzingsvakken. Een begrenzingsvak heeft 25 elementen:

Grid sample on the left, and Bounding Box sample on the right

  • x de x-positie van het begrenzingsvak centreren ten opzichte van de rastercel waaraan deze is gekoppeld.
  • y de y-positie van het begrenzingsvak centreren ten opzichte van de rastercel waaraan deze is gekoppeld.
  • w de breedte van het begrenzingsvak.
  • h de hoogte van het begrenzingsvak.
  • o de betrouwbaarheidswaarde die een object bevat in het begrenzingsvak, ook wel objectnessscore genoemd.
  • p1-p20 klassenkansen voor elk van de 20 klassen die door het model worden voorspeld.

In totaal vormen de 25 elementen die elk van de vijf begrenzingsvakken beschrijven de 125 elementen in elke rastercel.

De uitvoer die wordt gegenereerd door het vooraf getrainde ONNX-model is een floatmatrix van lengte 21125, die de elementen van een tensor met dimensies 125 x 13 x 13vertegenwoordigt. Om de voorspellingen die door het model worden gegenereerd, te transformeren in een tensor, is enige naverwerking vereist. Hiervoor maakt u een set klassen om de uitvoer te parseren.

Voeg een nieuwe map toe aan uw project om de set parserklassen te organiseren.

  1. Klik in Solution Explorer met de rechtermuisknop op het project en selecteer Nieuwe map toevoegen>. Wanneer de nieuwe map wordt weergegeven in Solution Explorer, geeft u deze de naam YoloParser.

Begrenzingsvakken en dimensies maken

De gegevensuitvoer van het model bevat coördinaten en dimensies van de begrenzingsvakken van objecten in de afbeelding. Maak een basisklasse voor dimensies.

  1. Klik in Solution Explorer met de rechtermuisknop op de map YoloParser en selecteer Nieuw item toevoegen>.

  2. Selecteer in het dialoogvenster Nieuw item toevoegen de optie Klasse en wijzig het veld Naam in DimensionsBase.cs. Selecteer vervolgens de knop Toevoegen .

    Het bestand DimensionsBase.cs wordt geopend in de code-editor. Verwijder alle using instructies en bestaande klassedefinities.

    Voeg de volgende code voor de DimensionsBase klasse toe aan het bestand DimensionsBase.cs :

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

    DimensionsBase heeft de volgende float eigenschappen:

    • X bevat de positie van het object langs de x-as.
    • Y bevat de positie van het object langs de y-as.
    • Height bevat de hoogte van het object.
    • Width bevat de breedte van het object.

Maak vervolgens een klasse voor uw begrenzingsvakken.

  1. Klik in Solution Explorer met de rechtermuisknop op de map YoloParser en selecteer Nieuw item toevoegen>.

  2. Selecteer in het dialoogvenster Nieuw item toevoegen de optie Klasse en wijzig het veld Naam in YoloBoundingBox.cs. Selecteer vervolgens de knop Toevoegen .

    Het bestand YoloBoundingBox.cs wordt geopend in de code-editor. Voeg de volgende using instructie toe aan het begin van YoloBoundingBox.cs:

    using System.Drawing;
    

    Voeg net boven de bestaande klassedefinitie een nieuwe klassedefinitie toe die wordt overgenomen BoundingBoxDimensions van de DimensionsBase klasse die de dimensies van het desbetreffende begrenzingsvak bevat.

    public class BoundingBoxDimensions : DimensionsBase { }
    

    Verwijder de bestaande YoloBoundingBox klassedefinitie en voeg de volgende code voor de YoloBoundingBox klasse toe aan het bestand YoloBoundingBox.cs :

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

    YoloBoundingBox heeft de volgende eigenschappen:

    • Dimensions bevat dimensies van het begrenzingsvak.
    • Label bevat de klasse van het object dat in het begrenzingsvak is gedetecteerd.
    • Confidence bevat het vertrouwen van de klasse.
    • Rect bevat de rechthoekweergave van de afmetingen van het begrenzingsvak.
    • BoxColor bevat de kleur die is gekoppeld aan de desbetreffende klasse die wordt gebruikt om op de afbeelding te tekenen.

De parser maken

Nu de klassen voor dimensies en begrenzingsvakken zijn gemaakt, is het tijd om de parser te maken.

  1. Klik in Solution Explorer met de rechtermuisknop op de map YoloParser en selecteer Nieuw item toevoegen>.

  2. Selecteer in het dialoogvenster Nieuw item toevoegen de optie Klasse en wijzig het veld Naam in YoloOutputParser.cs. Selecteer vervolgens de knop Toevoegen .

    Het bestand YoloOutputParser.cs wordt geopend in de code-editor. Voeg de volgende using instructies toe aan het begin van YoloOutputParser.cs:

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

    Voeg binnen de bestaande YoloOutputParser klassedefinitie een geneste klasse toe die de afmetingen van elk van de cellen in de afbeelding bevat. Voeg de volgende code toe voor de CellDimensions klasse die wordt overgenomen van de DimensionsBase klasse boven aan de YoloOutputParser klassedefinitie.

    class CellDimensions : DimensionsBase { }
    
  3. Voeg in de YoloOutputParser klassedefinitie de volgende constanten en velden toe.

    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 is het aantal rijen in het raster waarin de afbeelding is onderverdeeld.
    • COL_COUNT is het aantal kolommen in het raster waarin de afbeelding is onderverdeeld.
    • CHANNEL_COUNT is het totale aantal waarden in één cel van het raster.
    • BOXES_PER_CELL is het aantal begrenzingsvakken in een cel,
    • BOX_INFO_FEATURE_COUNT is het aantal functies in een vak (x,y,hoogte,breedte,betrouwbaarheid).
    • CLASS_COUNT is het aantal klassevoorspellingen in elk begrenzingsvak.
    • CELL_WIDTH is de breedte van één cel in het afbeeldingsraster.
    • CELL_HEIGHT is de hoogte van één cel in het afbeeldingsraster.
    • channelStride is de beginpositie van de huidige cel in het raster.

    Wanneer het model een voorspelling doet, ook wel scoren genoemd, wordt de 416px x 416px invoerafbeelding verdeeld in een raster van cellen met de grootte van 13 x 13. Elke cel bevat.32px x 32px Binnen elke cel zijn er 5 begrenzingsvakken met elk 5 functies (x, y, breedte, hoogte, betrouwbaarheid). Bovendien bevat elk begrenzingsvak de waarschijnlijkheid van elk van de klassen, in dit geval 20. Daarom bevat elke cel 125 stukjes informatie (5 kenmerken + 20 klassenkansen).

Maak een lijst met ankers hieronder channelStride voor alle vijf begrenzingsvakken:

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

Ankers zijn vooraf gedefinieerde hoogte- en breedteverhoudingen van begrenzingsvakken. De meeste objecten of klassen die door een model worden gedetecteerd, hebben vergelijkbare verhoudingen. Dit is waardevol als het gaat om het maken van begrenzingsvakken. In plaats van de begrenzingsvakken te voorspellen, wordt de verschuiving van de vooraf gedefinieerde dimensies berekend, waardoor de berekening die nodig is om het begrenzingsvak te voorspellen, wordt verminderd. Deze ankerverhoudingen worden doorgaans berekend op basis van de gebruikte gegevensset. Omdat de gegevensset bekend is en de waarden vooraf zijn berekend, kunnen de ankers in code worden vastgelegd.

Definieer vervolgens de labels of klassen die door het model worden voorspeld. Dit model voorspelt 20 klassen, een subset van het totale aantal klassen dat wordt voorspeld door het oorspronkelijke YOLOv2-model.

Voeg uw lijst met labels toe onder de 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"
};

Er zijn kleuren gekoppeld aan elk van de klassen. Wijs de klassenkleuren toe onder uw 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
};

Helperfuncties maken

Er zijn een reeks stappen betrokken bij de naverwerkingsfase. Hiervoor kunnen verschillende helpermethoden worden gebruikt.

De helpermethoden die door de parser worden gebruikt, zijn:

  • Sigmoid past de sigmoid-functie toe die een getal tussen 0 en 1 uitvoert.
  • Softmax normaliseert een invoervector in een waarschijnlijkheidsverdeling.
  • GetOffset wijst elementen in de eendimensionale modeluitvoer toe aan de bijbehorende positie in een 125 x 13 x 13 tensor.
  • ExtractBoundingBoxes extraheert de dimensies van het begrenzingsvak met behulp van de GetOffset methode uit de modeluitvoer.
  • GetConfidence extraheert de betrouwbaarheidswaarde die aangeeft hoe zeker het model is dat het een object heeft gedetecteerd en de Sigmoid functie gebruikt om het om te zetten in een percentage.
  • MapBoundingBoxToCell gebruikt de afmetingen van het begrenzingsvak en wijst deze toe aan de desbetreffende cel in de afbeelding.
  • ExtractClasses extraheert de klassevoorspellingen voor het begrenzingsvak uit de modeluitvoer met behulp van de GetOffset methode en verandert deze in een waarschijnlijkheidsverdeling met behulp van de Softmax methode.
  • GetTopResult selecteert de klasse in de lijst met voorspelde klassen met de hoogste waarschijnlijkheid.
  • IntersectionOverUnion filters overlappende begrenzingsvakken met lagere waarschijnlijkheden.

Voeg de code toe voor alle helpermethoden onder uw lijst met 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);
}

Nadat u alle helpermethoden hebt gedefinieerd, is het tijd om ze te gebruiken om de modeluitvoer te verwerken.

Maak onder de IntersectionOverUnion methode de ParseOutputs methode om de uitvoer te verwerken die door het model is gegenereerd.

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

}

Maak een lijst om uw begrenzingsvakken op te slaan en variabelen in de ParseOutputs methode te definiëren.

var boxes = new List<YoloBoundingBox>();

Elke afbeelding is onderverdeeld in een raster met 13 x 13 cellen. Elke cel bevat vijf begrenzingsvakken. Voeg onder de boxes variabele code toe om alle vakken in elk van de cellen te verwerken.

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

        }
    }
}

Bereken in de binnenste lus de beginpositie van het huidige vak in de eendimensionale modeluitvoer.

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

Direct daaronder gebruikt u de ExtractBoundingBoxDimensions methode om de afmetingen van het huidige begrenzingsvak op te halen.

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

Gebruik vervolgens de GetConfidence methode om het vertrouwen voor het huidige begrenzingsvak te verkrijgen.

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

Gebruik daarna de MapBoundingBoxToCell methode om het huidige begrenzingsvak toe te wijzen aan de huidige cel die wordt verwerkt.

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

Controleer voordat u verdere verwerking uitvoert of uw betrouwbaarheidswaarde groter is dan de opgegeven drempelwaarde. Zo niet, dan verwerkt u het volgende begrenzingsvak.

if (confidence < threshold)
    continue;

Anders kunt u doorgaan met het verwerken van de uitvoer. De volgende stap is het ophalen van de waarschijnlijkheidsverdeling van de voorspelde klassen voor het huidige begrenzingsvak met behulp van de ExtractClasses methode.

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

Gebruik vervolgens de GetTopResult methode om de waarde en index van de klasse op te halen met de hoogste waarschijnlijkheid voor het huidige vak en de bijbehorende score te berekenen.

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

Gebruik de topScore functie om alleen de begrenzingsvakken boven de opgegeven drempelwaarde te houden.

if (topScore < threshold)
    continue;

Als het huidige begrenzingsvak de drempelwaarde overschrijdt, maakt u een nieuw BoundingBox object en voegt u dit toe aan de boxes lijst.

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

Zodra alle cellen in de afbeelding zijn verwerkt, retourneert u de boxes lijst. Voeg de volgende retourinstructie toe onder de buitenste for-lus in de ParseOutputs methode.

return boxes;

Overlappende vakken filteren

Nu alle zeer zeker begrenzingsvakken zijn geëxtraheerd uit de modeluitvoer, moet u aanvullende filters uitvoeren om overlappende afbeeldingen te verwijderen. Voeg een methode toe die onder de ParseOutputs methode wordt aangeroepenFilterBoundingBoxes:

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

}

Begin in de FilterBoundingBoxes methode door een matrix te maken die gelijk is aan de grootte van gedetecteerde vakken en alle sleuven als actief of gereed voor verwerking te markeren.

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

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

Sorteer vervolgens de lijst met de begrenzingsvakken in aflopende volgorde op basis van betrouwbaarheid.

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

Maak daarna een lijst voor het opslaan van de gefilterde resultaten.

var results = new List<YoloBoundingBox>();

Begin met het verwerken van elk begrenzingsvak door elk van de begrenzingsvakken te herhalen.

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

}

Controleer in deze for-lus of het huidige begrenzingsvak kan worden verwerkt.

if (isActiveBoxes[i])
{

}

Zo ja, voeg het begrenzingsvak toe aan de lijst met resultaten. Als de resultaten de opgegeven limiet overschrijden voor vakken die moeten worden geëxtraheerd, breekt u de lus uit. Voeg de volgende code toe in de if-instructie.

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

if (results.Count >= limit)
    break;

Kijk anders naar de aangrenzende begrenzingsvakken. Voeg de volgende code toe onder de selectievakjeslimiet.

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

}

Als het aangrenzende vak actief is of klaar is om te worden verwerkt, gebruikt u de IntersectionOverUnion methode om te controleren of het eerste vak en het tweede vak de opgegeven drempelwaarde overschrijden. Voeg de volgende code toe aan uw binnenste 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;
    }
}

Buiten de binnenste for-lus die aangrenzende begrenzingsvakken controleert, controleert u of er nog begrenzingsvakken moeten worden verwerkt. Zo niet, dan kunt u de buitenste for-lus losmaken.

if (activeCount <= 0)
    break;

Retourneer ten slotte, buiten de eerste for-loop van de FilterBoundingBoxes methode, de resultaten:

return results;

Mooi! Nu is het tijd om deze code samen met het model te gebruiken voor scoren.

Het model gebruiken voor scoren

Net als bij naverwerking zijn er enkele stappen in de scorestappen. Als u dit wilt helpen, voegt u een klasse toe die de scorelogica aan uw project bevat.

  1. Klik in Solution Explorer met de rechtermuisknop op het project en selecteer Nieuw item toevoegen>.

  2. Selecteer in het dialoogvenster Nieuw item toevoegen de optie Klasse en wijzig het veld Naam in OnnxModelScorer.cs. Selecteer vervolgens de knop Toevoegen .

    Het bestand OnnxModelScorer.cs wordt geopend in de code-editor. Voeg de volgende using instructies toe aan het begin van OnnxModelScorer.cs:

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

    Voeg in de OnnxModelScorer klassedefinitie de volgende variabelen toe.

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

    Maak daaronder een constructor voor de OnnxModelScorer klasse waarmee de eerder gedefinieerde variabelen worden geïnitialiseerd.

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

    Zodra u de constructor hebt gemaakt, definieert u een aantal structs die variabelen bevatten die betrekking hebben op de afbeeldings- en modelinstellingen. Maak een struct die wordt aangeroepen ImageNetSettings om de verwachte hoogte en breedte te bevatten als invoer voor het model.

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

    Maak daarna een andere struct met de naam TinyYoloModelSettings van de invoer- en uitvoerlagen van het model. Als u de naam van de invoer- en uitvoerlagen van het model wilt visualiseren, kunt u een hulpprogramma zoals Netron gebruiken.

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

    Maak vervolgens de eerste set methoden die worden gebruikt voor scoren. Maak de LoadModel methode in uw OnnxModelScorer klas.

    private ITransformer LoadModel(string modelLocation)
    {
    
    }
    

    Voeg in de LoadModel methode de volgende code toe voor logboekregistratie.

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

    ML.NET pijplijnen moeten weten waar het gegevensschema op moet worden uitgevoerd wanneer de Fit methode wordt aangeroepen. In dit geval wordt een proces gebruikt dat vergelijkbaar is met training. Omdat er echter geen daadwerkelijke training plaatsvindt, is het acceptabel om een lege IDataViewte gebruiken. Maak een nieuwe IDataView voor de pijplijn op basis van een lege lijst.

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

    Definieer daaronder de pijplijn. De pijplijn bestaat uit vier transformaties.

    • LoadImages laadt de afbeelding als bitmap.
    • ResizeImages schaalt de afbeelding opnieuw naar de opgegeven grootte (in dit geval 416 x 416).
    • ExtractPixels wijzigt de pixelweergave van de afbeelding van een bitmap in een numerieke vector.
    • ApplyOnnxModel laadt het ONNX-model en gebruikt het om te scoren op de opgegeven gegevens.

    Definieer uw pijplijn in de LoadModel methode onder de data variabele.

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

    Nu is het tijd om het model te instantiëren voor scoren. Roep de Fit methode in de pijplijn aan en retourneer deze voor verdere verwerking.

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

Zodra het model is geladen, kan het worden gebruikt om voorspellingen te doen. Als u dit proces wilt vergemakkelijken, maakt u een methode die onder de LoadModel methode wordt aangeroepenPredictDataUsingModel.

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

}

Voeg in de PredictDataUsingModelcode de volgende code toe voor logboekregistratie.

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

Gebruik vervolgens de Transform methode om de gegevens te scoren.

IDataView scoredData = model.Transform(testData);

Extraheer de voorspelde waarschijnlijkheden en retourneer ze voor aanvullende verwerking.

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

return probabilities;

Nu beide stappen zijn ingesteld, combineert u deze in één methode. Voeg onder de methode een nieuwe methode toe met de PredictDataUsingModel naam Score.

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

    return PredictDataUsingModel(data, model);
}

Bijna klaar! Nu is het tijd om alles te gebruiken.

Objecten detecteren

Nu alle installatie is voltooid, is het tijd om enkele objecten te detecteren.

Modeluitvoer beoordelen en parseren

Voeg onder het maken van de mlContext variabele een try-catch-instructie toe.

try
{

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

Begin binnen het try blok met het implementeren van de objectdetectielogica. Laad eerst de gegevens in een IDataView.

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

Maak vervolgens een exemplaar van OnnxModelScorer en gebruik deze om de geladen gegevens te scoren.

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

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

Nu is het tijd voor de stap na verwerking. Maak een exemplaar van YoloOutputParser en gebruik dit om de modeluitvoer te verwerken.

YoloOutputParser parser = new YoloOutputParser();

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

Zodra de modeluitvoer is verwerkt, is het tijd om de begrenzingsvakken op de afbeeldingen te tekenen.

Voorspellingen visualiseren

Nadat het model de afbeeldingen heeft gescoord en de uitvoer is verwerkt, moeten de begrenzingsvakken op de afbeelding worden getekend. Voeg hiervoor een methode toe die wordt aangeroepen DrawBoundingBox onder de GetAbsolutePath methode in Program.cs.

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

}

Laad eerst de afbeelding en haal de afmetingen van de hoogte en breedte op in de DrawBoundingBox methode.

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

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

Maak vervolgens een for-each-lus om elk van de begrenzingsvakken te herhalen die door het model worden gedetecteerd.

foreach (var box in filteredBoundingBoxes)
{

}

Haal in de voor-elke lus de afmetingen van het begrenzingsvak op.

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

Omdat de afmetingen van het begrenzingsvak overeenkomen met de modelinvoer 416 x 416, kunt u de afmetingen van het begrenzingsvak aanpassen aan de werkelijke grootte van de afbeelding.

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;

Definieer vervolgens een sjabloon voor tekst die boven elk begrenzingsvak wordt weergegeven. De tekst bevat de klasse van het object in het desbetreffende begrenzingsvak en de betrouwbaarheid.

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

Als u op de afbeelding wilt tekenen, converteert u deze naar een Graphics object.

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

}

Stem in het using codeblok de objectinstellingen van Graphics de afbeelding af.

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

Stel daaronder het lettertype en de kleuropties voor het tekst- en begrenzingsvak in.

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

Maak en vul een rechthoek boven het begrenzingsvak om de tekst te bevatten met behulp van de FillRectangle methode. Hiermee kunt u de tekst contrasteren en de leesbaarheid verbeteren.

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

Teken vervolgens de tekst en het begrenzingsvak op de afbeelding met behulp van de DrawString en DrawRectangle methoden.

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

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

Voeg buiten de for-each-lus code toe om de afbeeldingen op te slaan in de outputFolder.

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

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

Voor aanvullende feedback dat de toepassing voorspellingen doet zoals verwacht tijdens de runtime, voegt u een methode toe die onder de DrawBoundingBox methode in het bestand Program.cs wordt aangeroepen LogDetectedObjects om de gedetecteerde objecten naar de console uit te voeren.

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

Nu u helpermethoden hebt om visuele feedback te maken op basis van de voorspellingen, voegt u een for-loop toe om elk van de gescoorde afbeeldingen te herhalen.

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

}

Haal in de for-lus de naam op van het afbeeldingsbestand en de begrenzingsvakken die eraan zijn gekoppeld.

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

Gebruik daaronder de DrawBoundingBox methode om de begrenzingsvakken op de afbeelding te tekenen.

DrawBoundingBox(imagesFolder, outputFolder, imageFileName, detectedObjects);

Ten slotte gebruikt u de LogDetectedObjects methode om voorspellingen naar de console uit te voeren.

LogDetectedObjects(imageFileName, detectedObjects);

Voeg na de try-catch-instructie aanvullende logica toe om aan te geven dat het proces is uitgevoerd.

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

Dat is het!

Resultaten

Nadat u de vorige stappen hebt uitgevoerd, voert u de console-app (Ctrl+F5) uit. Uw resultaten moeten vergelijkbaar zijn met de volgende uitvoer. U ziet mogelijk waarschuwingen of het verwerken van berichten, maar deze berichten zijn voor de duidelijkheid verwijderd uit de volgende resultaten.

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

Als u de afbeeldingen met begrenzingsvakken wilt zien, gaat u naar de assets/images/output/ map. Hieronder ziet u een voorbeeld van een van de verwerkte afbeeldingen.

Sample processed image of a dining room

Gefeliciteerd. U hebt nu een machine learning-model gebouwd voor objectdetectie door een vooraf getraind ONNX model opnieuw te gebruiken in ML.NET.

U vindt de broncode voor deze zelfstudie in de opslagplaats dotnet/machinelearning-samples .

In deze zelfstudie heeft u het volgende geleerd:

  • Het probleem begrijpen
  • Ontdek wat ONNX is en hoe het werkt met ML.NET
  • Inzicht in het model
  • Het vooraf getrainde model opnieuw gebruiken
  • Objecten detecteren met een geladen model

Bekijk de GitHub-opslagplaats met Machine Learning-voorbeelden om een uitgebreid voorbeeld van objectdetectie te verkennen.