Esercitazione: Rilevare gli oggetti usando ONNX in ML.NET

Informazioni su come usare un modello ONNX già sottoposto a training in ML.NET per rilevare gli oggetti nelle immagini.

Il training di un modello di rilevamento degli oggetti da zero richiede l'impostazione di milioni di parametri, numerosi dati di training con etichetta e una notevole quantità di risorse di calcolo (centinaia di ore di GPU). L'uso di un modello già sottoposto a training consente di abbreviare il processo di training.

In questa esercitazione apprenderai a:

  • Informazioni sul problema
  • Acquisire familiarità con ONNX e comprenderne il funzionamento con ML.NET
  • Acquisire familiarità con il modello
  • Riutilizzare il modello già sottoposto a training
  • Rilevare oggetti con un modello caricato

Prerequisiti

Panoramica dell'esempio di rilevamento degli oggetti ONNX

Questo esempio crea un'applicazione console .NET Core che rileva gli oggetti all'interno di un'immagine usando un modello ONNX di Deep Learning già sottoposto a training. Il codice per questo esempio è disponibile nel repository dotnet/machinelearning-samples in GitHub.

Che cos'è il rilevamento degli oggetti?

Il rilevamento degli oggetti è una questione correlata alla visione artificiale. Sebbene sia un concetto strettamente correlato alla classificazione delle immagini, il rilevamento degli oggetti esegue l'operazione di classificazione delle immagini su scala più granulare. Il rilevamento degli oggetti individua e classifica le entità all'interno delle immagini. I modelli di rilevamento oggetti vengono comunemente sottoposti a training usando deep learning e reti neurali. Per altre informazioni, vedere Deep Learning e Machine Learning .

Usare il rilevamento degli oggetti quando le immagini contengono più oggetti di tipi diversi.

Screenshots showing Image Classification versus Object Classification.

Ecco alcuni casi d'uso per il rilevamento degli oggetti:

  • Auto senza guidatore
  • Robotica
  • Rilevamento viso
  • Sicurezza nell'ambiente di lavoro
  • Conteggio di oggetti
  • Riconoscimento di attività

Selezionare un modello di Deep Learning

Deep Learning è un subset di Machine Learning. Per eseguire il training di modelli di Deep Learning, sono necessarie grandi quantità di dati. I modelli nei dati sono rappresentati da una serie di livelli. Le relazioni nei dati sono codificate come connessioni tra i livelli contenenti pesi. Maggiore è il peso, più forte è la relazione. Collettivamente, questa serie di livelli e connessioni è nota come rete neurale artificiale. Maggiore è il numero di livelli in una rete, più "profonda" è la rete, che diventa una rete neurale profonda.

Ci sono diversi tipi di reti neurali, tra cui i più comuni sono percettrone multistrato (MLP, Multi-Layered Perceptron), rete neurale convoluzionale (CNN, Convolutional Neural Network) e rete neurale ricorrente (RNN, Recurrent Neural Network). Il tipo più semplice è MLP, che esegue il mapping di un set di input a un set di output. Questa rete neurale è efficace quando i dati non hanno una componente spaziale o temporale. La rete CNN usa i livelli convoluzionali per elaborare le informazioni spaziali contenute nei dati. Un buon caso d'uso per le reti CNN è l'elaborazione di immagini per rilevare la presenza di una caratteristica in un'area di un'immagine (ad esempio, è presente un naso al centro di un'immagine?). Infine, le reti RNN consentono di usare come input la persistenza dello stato o della memoria. Le reti RNN vengono usate per l'analisi delle serie temporali, in cui l'ordinamento sequenziale e il contesto degli eventi sono importanti.

Acquisire familiarità con il modello

Il rilevamento degli oggetti è un'attività di elaborazione delle immagini. Per questo motivo, i modelli di Deep Learning sottoposti a training per risolvere questo problema sono prevalentemente di tipo CNN. Il modello usato in questa esercitazione è il modello Tiny YOLOv2, una versione più compatta del modello YOLOv2 descritta nel documento: "YOLO9000: Better, Faster, Stronger" di Redmon e Farhadi. Il training di Tiny YOLOv2 viene eseguito sul set di dati Pascal VOC ed è costituito da 15 livelli in grado di eseguire stime per 20 diverse classi di oggetti. Poiché il modello Tiny YOLOv2 è una versione ridotta del modello YOLOv2 originale, rappresenta un compromesso tra velocità e accuratezza. I diversi livelli che compongono il modello possono essere visualizzati usando strumenti come Netron. L'esame del modello restituirebbe un mapping delle connessioni tra tutti i livelli che compongono la rete neurale, in cui ogni livello contiene il nome del livello insieme alle dimensioni del rispettivo input/output. Le strutture di dati usate per descrivere gli input e gli output del modello sono note come tensori. I tensori possono essere considerati contenitori che archiviano i dati in N dimensioni. Nel caso di Tiny YOLOv2, il nome del livello di input è image e prevede un tensore con dimensioni 3 x 416 x 416. Il nome del livello di output è grid e genera un tensore di output con dimensioni 125 x 13 x 13.

Input layer being split into hidden layers, then output layer

Il modello YOLO accetta un'immagine 3(RGB) x 416px x 416px. Il modello accetta questo input e lo passa attraverso i diversi livelli per produrre un output. L'output divide l'immagine di input in una griglia 13 x 13, con ogni cella della griglia costituita da 125 valori.

Che cos'è un modello ONNX?

Open Neural Network Exchange (ONNX) è un formato open source per i modelli di intelligenza artificiale. ONNX supporta l'interoperabilità tra framework. Ciò significa che è possibile eseguire il training di un modello in uno dei numerosi framework di apprendimento automatico diffusi, ad esempio PyTorch, eseguire la conversione in formato ONNX e utilizzare il modello ONNX in un framework diverso, come ML.NET. Per altre informazioni, vedere il sito Web ONNX.

Diagram of ONNX supported formats being used.

Il modello Tiny YOLOv2 già sottoposto a training è archiviato in formato ONNX, una rappresentazione serializzata dei livelli e dei modelli appresi di tali livelli. In ML.NET, l'interoperabilità con ONNX viene raggiunta con i pacchetti NuGet ImageAnalytics e OnnxTransformer. Il pacchetto ImageAnalytics contiene una serie di trasformazioni che accettano un'immagine e la codificano in valori numerici che possono essere usati come input in una pipeline di stima o di training. Il pacchetto OnnxTransformer sfrutta il runtime ONNX per caricare un modello ONNX e usarlo per eseguire stime basate sull'input fornito.

Data flow of ONNX file into the ONNX Runtime.

Configurare il progetto console .NET

Ora che sono state apprese le nozioni generali su ONNX e sul funzionamento di Tiny YOLOv2, è possibile creare l'applicazione.

Creare un'applicazione console

  1. Creare un'applicazione console C# denominata "ObjectDetection". Fare clic sul pulsante Avanti.

  2. Scegliere .NET 6 come framework da usare. Fare clic sul pulsante Crea.

  3. Installare il pacchetto NuGet Microsoft.ML:

    Nota

    In questo esempio viene usata la versione stabile più recente dei pacchetti NuGet menzionati, a meno che non diversamente specificato.

    • In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e selezionare Gestisci pacchetti NuGet.
    • Scegliere "nuget.org" come origine del pacchetto, selezionare la scheda Sfoglia e cercare Microsoft.ML.
    • Selezionare il pulsante Installa.
    • Selezionare il pulsante OK nella finestra di dialogo Anteprima modifiche e quindi selezionare il pulsante Accetto nella finestra di dialogo Accettazione della licenza se si accettano le condizioni di licenza per i pacchetti elencati.
    • Ripetere questi passaggi per Microsoft.Windows.Compatibility, Microsoft.ML.ImageAnalytics, Microsoft.ML.OnnxTransformer e Microsoft.ML.OnnxRuntime.

Preparare i dati e il modello già sottoposto a training

  1. Scaricare il file ZIP della directory assets del progetto e decomprimerlo.

  2. Copiare la directory assets nella directory del progetto ObjectDetection. Questa directory e le relative sottodirectory contengono i file di immagine (ad eccezione del modello Tiny YOLOv2, che verrà scaricato e aggiunto nel passaggio successivo) richiesti per questa esercitazione.

  3. Scaricare il modello Tiny YOLOv2 dallo zoo del modello ONNX.

  4. Copiare il file nella directory del model.onnx progetto assets\Model ObjectDetection e rinominarlo in TinyYolo2_model.onnx. Questa directory contiene il modello necessario per questa esercitazione.

  5. In Esplora soluzioni fare clic con il pulsante destro del mouse su ognuno dei file nella directory assets e nelle relative sottodirectory e selezionare Proprietà. In Avanzate impostare il valore di Copia nella directory di output su Copia se più recente.

Creare le classi e definire i percorsi

Aprire il file Program.cs e aggiungere le istruzioni using aggiuntive seguenti all'inizio del file:

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

Definire quindi i percorsi dei diversi asset.

  1. Creare prima di tutto il GetAbsolutePath metodo nella parte inferiore del file 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. Quindi, sotto le istruzioni using, creare campi per archiviare la posizione degli asset.

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

Aggiungere una nuova directory al progetto per archiviare i dati di input e le classi di stima.

In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Aggiungi>Nuova cartella. Quando la nuova cartella viene visualizzata in Esplora soluzioni, assegnarle il nome "DataStructures".

Creare la classe di dati di input nella directory DataStructures appena creata.

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sulla directory DataStructures e quindi scegliere Aggiungi>Nuovo elemento.

  2. Nella finestra di dialogo Aggiungi nuovo elemento selezionare Classe e modificare il campo Nome in ImageNetData.cs. Selezionare quindi il pulsante Aggiungi.

    Il file ImageNetData.cs viene aperto nell'editor del codice. Aggiungere l'istruzione using seguente all'inizio di ImageNetData.cs:

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

    Rimuovere la definizione di classe esistente e aggiungere il codice seguente per la classe ImageNetData al file 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 è la classe dei dati di immagine di input e ha i campi String seguenti:

    • ImagePath contiene il percorso in cui è archiviata l'immagine.
    • Label contiene il nome del file.

    Contiene inoltre ImageNetData un metodo ReadFromFile che carica più file di immagine archiviati nel imageFolder percorso specificato e li restituisce come raccolta di ImageNetData oggetti.

Creare la classe di stima nella directory DataStructures.

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sulla directory DataStructures e quindi scegliere Aggiungi>Nuovo elemento.

  2. Nella finestra di dialogo Aggiungi nuovo elemento selezionare Classe e modificare il campo Nome in ImageNetPrediction.cs. Selezionare quindi il pulsante Aggiungi.

    Il file ImageNetPrediction.cs viene aperto nell'editor del codice. Aggiungere l'istruzione using seguente all'inizio di ImageNetPrediction.cs:

    using Microsoft.ML.Data;
    

    Rimuovere la definizione di classe esistente e aggiungere il codice seguente per la classe ImageNetPrediction al file ImageNetPrediction.cs:

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

    ImageNetPrediction è la classe di dati di stima e ha il campo float[] seguente:

    • PredictedLabels contiene le dimensioni, il punteggio di oggetti e le probabilità di classe per ognuna delle caselle di delimitazione rilevate in un'immagine.

Inizializzare le variabili

La classe MLContext è un punto di partenza per tutte le operazioni ML.NET e l'inizializzazione di mlContext crea un nuovo ambiente ML.NET che può essere condiviso tra gli oggetti del flusso di lavoro della creazione del modello. Dal punto di vista concettuale è simile a DBContext in Entity Framework.

Inizializzare la mlContext variabile con una nuova istanza di MLContext aggiungendo la riga seguente sotto il outputFolder campo .

MLContext mlContext = new MLContext();

Creare un parser per la post-elaborazione degli output del modello

Il modello segmenta un'immagine in una griglia 13 x 13, in cui ogni cella è 32px x 32px. Ogni cella della griglia contiene 5 rettangoli di selezione di oggetti potenziali. Un rettangolo di selezione contiene 25 elementi:

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

  • x la posizione x del centro del rettangolo di selezione rispetto alla cella della griglia a cui è associato.
  • y la posizione y del centro del rettangolo di selezione rispetto alla cella della griglia a cui è associato.
  • w la larghezza del rettangolo di selezione.
  • h l'altezza del rettangolo di selezione.
  • o il valore di confidenza che un oggetto esiste nel rettangolo di selezione, noto anche come punteggio di riconoscimento degli oggetti.
  • p1-p20 le probabilità delle classi per ognuna delle 20 classi stimate dal modello.

In totale, i 25 elementi che descrivono ognuno dei 5 rettangoli di selezione costituiscono i 125 elementi contenuti in ogni cella della griglia.

L'output generato dal modello ONNX già sottoposto a training è una matrice mobile di lunghezza 21125 che rappresenta gli elementi di un tensore con dimensioni 125 x 13 x 13. Per trasformare le stime generate dal modello in un tensore, è necessario eseguire alcune operazioni di post-elaborazione. A tale scopo, creare un set di classi per l'analisi dell'output.

Aggiungere una nuova directory al progetto per organizzare il set di classi parser.

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e scegliere Aggiungi>Nuova cartella. Quando la nuova cartella viene visualizzata in Esplora soluzioni, assegnarle il nome "YoloParser".

Creare rettangoli di selezione e dimensioni

I dati restituiti dal modello contengono le coordinate e le dimensioni dei rettangoli di selezione degli oggetti all'interno dell'immagine. Creare una classe di base per le dimensioni.

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sulla directory YoloParser e quindi scegliere Aggiungi>Nuovo elemento.

  2. Nella finestra di dialogo Aggiungi nuovo elemento selezionare Classe e modificare il campo Nome in DimensionsBase.cs. Selezionare quindi il pulsante Aggiungi.

    Il file DimensionsBase.cs viene aperto nell'editor del codice. Rimuovere tutte le istruzioni using e la definizione di classe esistente.

    Aggiungere il codice seguente per la classe DimensionsBase al file 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 ha le proprietà seguenti float :

    • X contiene la posizione dell'oggetto lungo l'asse x.
    • Y contiene la posizione dell'oggetto lungo l'asse y.
    • Height contiene l'altezza dell'oggetto.
    • Width contiene la larghezza dell'oggetto.

Creare quindi una classe per i rettangoli di selezione.

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sulla directory YoloParser e quindi scegliere Aggiungi>Nuovo elemento.

  2. Nella finestra di dialogo Aggiungi nuovo elemento selezionare Classe e modificare il campo Nome in YoloBoundingBox.cs. Selezionare quindi il pulsante Aggiungi.

    Il file YoloBoundingBox.cs viene aperto nell'editor del codice. Aggiungere l'istruzione using seguente all'inizio di YoloBoundingBox.cs:

    using System.Drawing;
    

    Appena sopra la definizione di classe esistente, aggiungere una nuova definizione di classe denominata BoundingBoxDimensions che eredita dalla DimensionsBase classe per contenere le dimensioni del rispettivo rettangolo di selezione.

    public class BoundingBoxDimensions : DimensionsBase { }
    

    Rimuovere la definizione di classe YoloBoundingBox esistente e aggiungere il codice seguente per la classe YoloBoundingBox al file 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 ha le proprietà seguenti:

    • Dimensions contiene le dimensioni del rettangolo di selezione.
    • Label contiene la classe dell'oggetto rilevato nel rettangolo di selezione.
    • Confidence contiene la confidenza della classe.
    • Rect contiene la rappresentazione del rettangolo delle dimensioni del rettangolo di selezione.
    • BoxColor contiene il colore associato alla rispettiva classe usata per disegnare l'immagine.

Creare il parser

Dopo aver creato le classi per le dimensioni e i rettangoli di selezione, è possibile creare il parser.

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sulla directory YoloParser e quindi scegliere Aggiungi>Nuovo elemento.

  2. Nella finestra di dialogo Aggiungi nuovo elemento selezionare Classe e modificare il campo Nome in YoloOutputParser.cs. Selezionare quindi il pulsante Aggiungi.

    Il file YoloOutputParser.cs viene aperto nell'editor del codice. Aggiungere le istruzioni seguenti using all'inizio di YoloOutputParser.cs:

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

    All'interno della definizione di classe YoloOutputParser esistente aggiungere una classe annidata che contiene le dimensioni di ciascuna cella nell'immagine. Aggiungere il codice seguente per la CellDimensions classe che eredita dalla DimensionsBase classe all'inizio della definizione della YoloOutputParser classe.

    class CellDimensions : DimensionsBase { }
    
  3. All'interno della definizione della YoloOutputParser classe aggiungere le costanti e il campo seguenti.

    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: numero di righe nella griglia in cui è divisa l'immagine.
    • COL_COUNT: numero di colonne nella griglia in cui è divisa l'immagine.
    • CHANNEL_COUNT: numero totale di valori contenuti in una cella della griglia.
    • BOXES_PER_CELL: numero di rettangoli di selezione in una cella.
    • BOX_INFO_FEATURE_COUNT: numero di funzionalità contenute all'interno di un rettangolo di selezione (x, y, altezza, larghezza, confidenza).
    • CLASS_COUNT numero di stime delle classi contenute in ogni rettangolo di selezione.
    • CELL_WIDTH: larghezza di una cella nella griglia dell'immagine.
    • CELL_HEIGHT: altezza di una cella nella griglia dell'immagine.
    • channelStride: posizione iniziale della cella corrente nella griglia.

    Quando il modello esegue una stima, operazione nota anche come assegnazione di punteggi, divide l'immagine di input 416px x 416px in una griglia di celle delle dimensioni di 13 x 13. Ogni cella contenuta è 32px x 32px. All'interno di ogni cella sono presenti 5 rettangoli di selezione, ognuno contenente 5 funzionalità (x, y, larghezza, altezza, confidenza). Inoltre, ogni rettangolo di selezione contiene la probabilità di ognuna delle classi, che in questo caso è 20. Di conseguenza, ogni cella contiene 125 informazioni (5 funzionalità + 20 probabilità delle classi).

Creare un elenco di ancoraggi sotto channelStride per tutti i 5 rettangoli di selezione:

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

Gli ancoraggi sono rapporti di altezza e larghezza predefiniti per i rettangoli di selezione. La maggior parte degli oggetti o delle classi rilevate da un modello ha proporzioni simili. Questo è importante quando si tratta di creare rettangoli di selezione. Anziché stimare i rettangoli di selezione, viene calcolato l'offset dalle dimensioni predefinite, riducendo di conseguenza il calcolo necessario per stimare il rettangolo di selezione. In genere i rapporti di ancoraggio vengono calcolati in base al set di dati usato. In questo caso, poiché il set di dati è noto e i valori sono stati pre-calcolati, gli ancoraggi possono essere hardcoded.

Definire quindi le etichette o le classi che devono essere stimate dal modello. Questo modello stima 20 classi, ovvero un subset del numero totale di classi stimate dal modello YOLOv2 originale.

Aggiungere l'elenco di etichette sotto 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"
};

A ognuna delle classi sono associati colori specifici. Assegnare i colori delle classi sotto 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
};

Creare funzioni di supporto

La fase di post-elaborazione prevede una serie di passaggi. Per semplificare questi passaggi, è possibile usare diversi metodi di supporto.

I metodi di supporto usati dal parser sono:

  • Sigmoid: applica la funzione sigma che restituisce un numero compreso tra 0 e 1.
  • Softmax: normalizza un vettore di input in una distribuzione di probabilità.
  • GetOffset: esegue il mapping degli elementi nell'output di un modello unidimensionale alla posizione corrispondente in un tensore 125 x 13 x 13.
  • ExtractBoundingBoxes: estrae le dimensioni dei rettangoli di selezione usando il metodo GetOffset dall'output del modello.
  • GetConfidence estrae il valore di attendibilità che indica in che modo il modello è che ha rilevato un oggetto e usa la Sigmoid funzione per trasformarla in una percentuale.
  • MapBoundingBoxToCell: usa le dimensioni del rettangolo di selezione e ne esegue il mapping alla rispettiva cella all'interno dell'immagine.
  • ExtractClasses: estrae le stime delle classi per il rettangolo di selezione dall'output del modello usando il metodo GetOffset e le converte in una distribuzione di probabilità tramite il metodo Softmax.
  • GetTopResult: seleziona la classe dall'elenco delle classi stimate con la probabilità maggiore.
  • IntersectionOverUnion: filtra i rettangoli di selezione sovrapposti con probabilità inferiori.

Aggiungere il codice per tutti i metodi di supporto sotto l'elenco 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);
}

Dopo aver definito tutti i metodi di supporto, è possibile usarli per elaborare l'output del modello.

Sotto il metodo IntersectionOverUnion creare il metodo ParseOutputs per elaborare l'output generato dal modello.

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

}

Creare un elenco per archiviare i rettangoli di selezione e definire variabili all'interno del metodo ParseOutputs.

var boxes = new List<YoloBoundingBox>();

Ogni immagine è divisa in una griglia di celle 13 x 13. Ogni cella contiene cinque rettangoli di selezione. Sotto la variabile boxes aggiungere il codice per elaborare tutti i rettangoli in ognuna delle celle.

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

        }
    }
}

All'interno del ciclo più interno calcolare la posizione iniziale del rettangolo corrente all'interno dell'output del modello unidimensionale.

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

Direttamente sotto usare il metodo ExtractBoundingBoxDimensions per ottenere le dimensioni del rettangolo di selezione corrente.

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

Usare quindi il metodo GetConfidence per ottenere la confidenza per il rettangolo di selezione corrente.

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

Usare ora il metodo MapBoundingBoxToCell per eseguire il mapping del rettangolo di selezione corrente alla cella corrente in fase di elaborazione.

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

Prima di eseguire altre operazioni di elaborazione, controllare se il valore di confidenza è maggiore della soglia specificata. In caso contrario, elaborare il rettangolo di selezione successivo.

if (confidence < threshold)
    continue;

Altrimenti, proseguire con l'elaborazione dell'output. Il passaggio successivo consiste nell'ottenere la distribuzione di probabilità delle classi stimate per il rettangolo di selezione corrente usando il metodo ExtractClasses.

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

Usare quindi il metodo GetTopResult per ottenere il valore e l'indice della classe con la probabilità maggiore per il rettangolo corrente e calcolarne il punteggio.

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

Usare topScore ancora una volta per mantenere solo i rettangoli di selezione che superano la soglia specificata.

if (topScore < threshold)
    continue;

Infine, se il rettangolo di selezione corrente supera la soglia, creare un nuovo oggetto BoundingBox e aggiungerlo all'elenco boxes.

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

Dopo aver elaborato tutte le celle nell'immagine, restituire l'elenco boxes. Aggiungere l'istruzione return seguente sotto il ciclo for più esterno nel metodo ParseOutputs.

return boxes;

Filtrare i rettangoli sovrapposti

Ora che tutti i rettangoli di selezione con confidenza elevata sono stati estratti dall'output del modello, è necessario filtrarli ulteriormente per rimuovere le immagini sovrapposte. Aggiungere un metodo denominato FilterBoundingBoxes sotto il metodo ParseOutputs:

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

}

All'interno del metodo FilterBoundingBoxes iniziare creando una matrice uguale alle dimensioni dei rettangoli rilevati e contrassegnando tutti gli slot come attivi o pronti per l'elaborazione.

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

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

Ordinare quindi l'elenco contenente i rettangoli di selezione in ordine decrescente in base alla confidenza.

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

Infine, creare un elenco che conterrà i risultati filtrati.

var results = new List<YoloBoundingBox>();

Iniziare a elaborare ogni rettangolo di selezione tramite l'iterazione.

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

}

All'interno di questo ciclo for controllare se il rettangolo di selezione corrente può essere elaborato.

if (isActiveBoxes[i])
{

}

Se sì, aggiungere il rettangolo di selezione all'elenco dei risultati. Se i risultati superano il limite specificato di caselle da estrarre, interrompere il ciclo. Aggiungere il codice seguente all'interno dell'istruzione if.

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

if (results.Count >= limit)
    break;

In caso contrario, controllare i rettangoli di selezione adiacenti. Aggiungere il codice seguente sotto il controllo del limite di rettangoli.

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

}

Come per il primo rettangolo, se il rettangolo adiacente è attivo o pronto per l'elaborazione, usare il metodo IntersectionOverUnion per verificare se il primo e il secondo rettangolo superano la soglia specificata. Aggiungere il codice seguente al ciclo for più interno.

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

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

        if (activeCount <= 0)
            break;
    }
}

All'esterno del ciclo for più interno che controlla i rettangoli di selezione adiacenti, verificare se sono presenti altri rettangoli di selezione da elaborare. In caso contrario, interrompere il ciclo for esterno.

if (activeCount <= 0)
    break;

Infine, all'esterno del ciclo for iniziale del metodo FilterBoundingBoxes restituire i risultati:

return results;

Ottimo. È ora possibile usare il codice insieme al modello per l'assegnazione dei punteggi.

Usare il modello per l'assegnazione dei punteggi

Analogamente alla post-elaborazione, la fase di assegnazione dei punteggi prevede alcuni passaggi. Per semplificare questi passaggi, aggiungere una classe che conterrà la logica di assegnazione dei punteggi al progetto.

  1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto e quindi selezionare Aggiungi>Nuovo elemento.

  2. Nella finestra di dialogo Aggiungi nuovo elemento selezionare Classe e modificare il campo Nome in OnnxModelScorer.cs. Selezionare quindi il pulsante Aggiungi.

    Il file OnnxModelScorer.cs viene aperto nell'editor del codice. Aggiungere le istruzioni seguenti using all'inizio di OnnxModelScorer.cs:

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

    Aggiungere le variabili seguenti all'interno della definizione di classe OnnxModelScorer.

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

    Direttamente sotto creare un costruttore per la classe OnnxModelScorer che Inizializzerà le variabili definite in precedenza.

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

    Dopo aver creato il costruttore, definire un paio di struct che contengono variabili correlate alle impostazioni dell'immagine e del modello. Creare uno struct denominato ImageNetSettings che conterrà l'altezza e la larghezza previste come input per il modello.

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

    Successivamente, creare un altro struct denominato TinyYoloModelSettings che contiene i nomi dei livelli di input e output del modello. Per visualizzare il nome dei livelli di input e output del modello, è possibile usare uno strumento come 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";
    }
    

    Creare quindi il primo set di metodi da usare per l'assegnazione dei punteggi. Creare il metodo LoadModel all'interno della classe OnnxModelScorer.

    private ITransformer LoadModel(string modelLocation)
    {
    
    }
    

    All'interno del metodo LoadModel aggiungere il codice seguente per la registrazione.

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

    Le pipeline ML.NET devono conoscere lo schema dei dati su cui agire quando viene chiamato il metodo Fit. In questo caso, verrà usato un processo simile al training. Tuttavia, poiché di fatto non viene eseguito alcun training, è accettabile usare un'interfaccia IDataView vuota. Creare una nuova interfaccia IDataView per la pipeline da un elenco vuoto.

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

    Sotto questo codice definire la pipeline. La pipeline sarà costituita da quattro trasformazioni.

    • LoadImages: carica l'immagine come bitmap.
    • ResizeImages: ridimensiona l'immagine in base alla dimensione specificata (in questo caso 416 x 416).
    • ExtractPixels: modifica la rappresentazione in pixel dell'immagine da bitmap a vettore numerico.
    • ApplyOnnxModel: carica il modello ONNX e lo usa per assegnare un punteggio ai dati forniti.

    Definire la pipeline nel metodo LoadModel sotto la variabile data.

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

    È ora possibile creare un'istanza del modello per l'assegnazione dei punteggi. Chiamare il metodo Fit nella pipeline e restituirlo per un'ulteriore elaborazione.

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

Una volta caricato, il modello può essere usato per eseguire stime. Per semplificare il processo, creare un metodo denominato PredictDataUsingModel sotto il metodo LoadModel.

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

}

All'interno di PredictDataUsingModel aggiungere il codice seguente per la registrazione.

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

Usare quindi il metodo Transform per assegnare punteggi ai dati.

IDataView scoredData = model.Transform(testData);

Estrarre le probabilità stimate e restituirle per l'ulteriore elaborazione.

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

return probabilities;

Una volta configurati entrambi i passaggi, combinarli in un unico metodo. Sotto il metodo PredictDataUsingModel aggiungere un nuovo metodo denominato Score.

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

    return PredictDataUsingModel(data, model);
}

La configurazione è stata completata. È ora possibile usarla per lo scopo di questa esercitazione.

Rilevare oggetti

Dopo aver completato la configurazione, è possibile iniziare a rilevare alcuni oggetti.

Assegnare punteggi agli output del modello e analizzarli

Sotto la creazione della mlContext variabile aggiungere un'istruzione try-catch.

try
{

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

All'interno del blocco try iniziare a implementare la logica di rilevamento degli oggetti. Prima di tutto, caricare i dati in un'interfaccia IDataView.

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

Creare quindi un'istanza di OnnxModelScorer e usarla per assegnare punteggi ai dati caricati.

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

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

È ora possibile passare alla fase di post-elaborazione. Creare un'istanza di YoloOutputParser e usarla per elaborare l'output del modello.

YoloOutputParser parser = new YoloOutputParser();

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

Dopo aver elaborato l'output del modello, è possibile tracciare i rettangoli di selezione sulle immagini.

Visualizzare stime

Dopo che il modello ha assegnato un punteggio alle immagini e gli output sono stati elaborati, è necessario disegnare i rettangoli di selezione sull'immagine. A tale scopo, aggiungere un metodo denominato DrawBoundingBox sotto il metodo GetAbsolutePath all'interno di Program.cs.

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

}

Caricare prima di tutto l'immagine e ottenere le dimensioni di altezza e larghezza nel metodo DrawBoundingBox.

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

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

Creare quindi un ciclo for-each per eseguire l'iterazione di ogni rettangolo di selezione rilevato dal modello.

foreach (var box in filteredBoundingBoxes)
{

}

All'interno del ciclo for-each, ottenere le dimensioni del rettangolo di selezione.

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

Poiché le dimensioni del rettangolo di selezione corrispondono all'input del modello di 416 x 416, ridimensionare il rettangolo di selezione in modo che le sue dimensioni corrispondano a quelle effettive dell'immagine.

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;

Definire quindi un modello per il testo che verrà visualizzato sopra ogni rettangolo di selezione. Il testo conterrà la classe dell'oggetto all'interno del rispettivo rettangolo di selezione e la confidenza.

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

Per disegnare sull'immagine, convertirla in un oggetto Graphics.

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

}

All'interno del blocco di codice using ottimizzare le impostazioni dell'oggetto Graphics del grafico.

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

Al di sotto, impostare le opzioni relative al tipo di carattere e al colore per il testo e il rettangolo di selezione.

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

Creare e riempire un rettangolo sopra il rettangolo di selezione per contenere il testo usando il metodo FillRectangle. Ciò consentirà di creare un contrasto per il testo e migliorare la leggibilità.

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

Disegnare quindi il testo e il rettangolo di selezione nell'immagine usando i metodi DrawString e DrawRectangle.

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

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

Al di fuori del ciclo for-each aggiungere il codice per salvare le immagini in outputFolder.

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

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

Per ulteriori commenti e suggerimenti che l'applicazione sta effettuando stime come previsto in fase di esecuzione, aggiungere un metodo denominato LogDetectedObjects sotto il DrawBoundingBox metodo nel file Program.cs per restituire gli oggetti rilevati nella console.

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

Ora che sono disponibili metodi helper per creare un riscontro visivo dalle stime, aggiungere un ciclo for per scorrere ognuna delle immagini con punteggio.

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

}

All'interno del ciclo for ottenere il nome del file di immagine e dei rettangoli di selezione associati.

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

Sotto questo codice usare il metodo DrawBoundingBox per tracciare i rettangoli di selezione sull'immagine.

DrawBoundingBox(imagesFolder, outputFolder, imageFileName, detectedObjects);

Infine, usare il metodo LogDetectedObjects per restituire le stime alla console.

LogDetectedObjects(imageFileName, detectedObjects);

Dopo l'istruzione try-catch aggiungere logica aggiuntiva per indicare che l'esecuzione del processo è stata completata.

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

Ecco fatto!

Risultati

Dopo aver completato i passaggi precedenti, eseguire l'app console (CTRL+F5). I risultati saranno simili all'output seguente. È possibile che vengano visualizzati avvisi o messaggi di elaborazione che tuttavia, per chiarezza, sono stati rimossi dai risultati riportati di seguito.

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

Per visualizzare le immagini con i rettangoli di selezione, passare alla directory assets/images/output/. Di seguito viene fornito un esempio da una delle immagini elaborate.

Sample processed image of a dining room

Complimenti. È stato creato un modello di Machine Learning per il rilevamento di oggetti riutilizzando un modello ONNX già sottoposto a training in ML.NET.

È possibile trovare il codice sorgente per questa esercitazione nel repository dotnet/machinelearning-samples .

Questa esercitazione ha descritto come:

  • Informazioni sul problema
  • Acquisire familiarità con ONNX e comprenderne il funzionamento con ML.NET
  • Acquisire familiarità con il modello
  • Riutilizzare il modello già sottoposto a training
  • Rilevare oggetti con un modello caricato

Consultare il repository GitHub degli esempi di Machine Learning per esaminare un esempio di rilevamento di oggetti esteso.