Kurz: Kategorizace obrázku v ML.NET z modelu Custom Vision ONNX

Naučte se používat ML.NET k detekci objektů v obrázcích pomocí modelu ONNX natrénovaného ve službě Microsoft Custom Vision.

Služba Microsoft Custom Vision je služba AI, která trénuje model na základě obrázků, které nahrajete. Model pak můžete exportovat do formátu ONNX a použít ho v ML.NET k předpovědím.

V tomto návodu se naučíte, jak:

  • Vytvoření modelu ONNX pomocí služby Custom Vision
  • Integrovat model ONNX do kanálu ML.NET
  • Trénování modelu ML.NET
  • Detekce znaků zastavení na testovacích obrázcích

Požadavky

Vytvoření modelu

Vytvoření projektu Custom Vision

Přihlaste se ke službě Microsoft Custom Vision a vyberte Nový projekt.

V dialogovém okně Nový projekt vyplňte následující požadované položky:

  • Nastavte název projektu Custom Vision jako StopSignDetection.
  • Vyberte prostředek , který použijete. Jedná se o prostředek Azure, který se vytvoří pro projekt Custom Vision. Pokud žádný není uvedený, můžete ho vytvořit výběrem odkazu Vytvořit nový .
  • Nastavte typ projektu jako rozpoznávání objektů.
  • Nastavte typy klasifikace jako Více tříd, protože bude jedna třída na jeden obrázek.
  • Nastavte doménu jako obecné (kompaktní) [S1]. Kompaktní doména umožňuje stáhnout model ONNX.
  • U možností exportu vyberte základní platformy , které umožňují export modelu ONNX.

Po vyplnění výše uvedených polí vyberte Vytvořit projekt.

Přidání obrázků

  1. Po vytvoření projektu zvolte Přidat obrázky a začněte přidávat obrázky pro model, na který se má model trénovat. Vyberte obrázky stop-sign, které jste stáhli.
  2. Vyberte první obrázek, který se zobrazí. Na obrázku, který má model rozpoznat, můžete vybrat objekty. Vyberte značku stop v obrázku. Automaticky otevírané okno zobrazí a nastaví značku jako stop-sign.
  3. Opakujte pro všechny zbývající obrázky. Některé obrázky mají více než jedno znaménko zastavení, proto nezapomeňte označit všechny obrázky, které jsou na obrázcích.

Trénování modelu

Po nahrání a označení obrázků je teď možné model vytrénovat. Vyberte Trénovat.

Zobrazí se automaticky otevírané okno s dotazem, jaký typ trénování se má použít. Zvolte Rychlé trénování a pak vyberte Trénovat.

Stažení modelu ONNX

Po dokončení trénování klikněte na tlačítko Exportovat . Když se automaticky otevírané okno zobrazí, vyberte ONNX a stáhněte model ONNX.

Kontrola modelu ONNX

Rozbalte stažený soubor ONNX. Složka obsahuje několik souborů, ale dvě, které použijete v tomto kurzu, jsou:

  • labels.txt, což je textový soubor obsahující popisky definované ve službě Custom Vision.
  • model.onnx, což je model ONNX, který použijete k předpovědím v ML.NET.

K sestavení kanálu ML.NET budete potřebovat názvy vstupních a výstupních sloupců. K získání těchto informací použijte Netron, webovou a desktopovou aplikaci, která dokáže analyzovat modely ONNX a zobrazit jejich architekturu.

  1. Při použití webové nebo desktopové aplikace Netron otevřete v aplikaci model ONNX. Po otevření se zobrazí graf. Tento graf vám řekne několik věcí, které budete potřebovat k sestavení kanálu ML.NET pro předpovědi.

    • Název vstupního sloupce – název vstupního sloupce vyžadovaný při použití modelu ONNX v ML.NET.

      Vstupní sloupec Netron

    • Název výstupního sloupce – název výstupního sloupce vyžadovaný při použití modelu ONNX v ML.NET.

      Výstupní sloupec Netron

    • Velikost obrázku – velikost požadovaná při změně velikosti obrázků v kanálu ML.NET.

      Velikost obrázku Netron

Vytvoření projektu konzoly jazyka C#

  1. V sadě Visual Studio vytvořte konzolovou aplikaci jazyka C# s názvem StopSignDetection. Jako cílovou architekturu zvolte .NET 8.

  2. Nainstalujte pro projekt následující balíčky NuGet:

    • Microsoft.ML
    • Microsoft.ML.ImageAnalytics
    • Microsoft.Onnx.Transformer

    Poznámka:

    Tato ukázka používá nejnovější stabilní verzi uvedených balíčků NuGet, pokud není uvedeno jinak.

Odkazování na model ONNX

V Průzkumníku řešení sady Visual Studio najděte dva soubory z modelu ONNX (labels.txt a model.onnx). Klikněte na ně pravým tlačítkem myši a v okně Vlastnosti nastavte Kopírovat do výstupního adresáře na Kopírovat pokud je novější.

Vytvoření vstupních a prediktivních tříd

  1. Přidejte do projektu novou třídu a pojmenujte ji StopSignInput. Pak do třídy přidejte následující strukturu:

    public struct ImageSettings
    {
        public const int imageHeight = 320;
        public const int imageWidth = 320;
    }
    
  2. Dále do třídy přidejte následující vlastnost.

    public class StopSignInput
    {
        [ImageType(ImageSettings.imageHeight, ImageSettings.imageWidth)]
        public Bitmap Image { get; set; }
    }
    

    Vlastnost Image obsahuje rastrový obrázek použitý k predikci. Atribut ImageType udává ML.NET, že vlastnost je obrázek s rozměry 320x320, určený pomocí Netronu.

  3. Přidejte do projektu další třídu a pojmenujte ji StopSignPrediction. Pak do třídy přidejte následující vlastnosti.

    public class StopSignPrediction
    {
        [ColumnName("detected_classes")]
        public long[] PredictedLabels { get; set; }
    
        [ColumnName("detected_boxes")]
        public float[] BoundingBoxes { get; set; }
    
        [ColumnName("detected_scores")]
        public float[] Scores { get; set; }
    }
    

    Vlastnost PredictedLabels obsahuje předpovědi popisků pro každý zjištěný objekt. Typ je plovoucí matice, takže každá položka v poli je předpověď každého popisku. Atribut ColumnName ML.NET říká, že tento sloupec v modelu je zadaný název, což je detected_classes.

    Vlastnost BoundingBoxes obsahuje ohraničující rámečky pro každý zjištěný objekt. Typ je plovoucí matice a každý zjištěný objekt se dodává se čtyřmi položkami v poli pro ohraničující pole. Atribut ColumnName ML.NET říká, že tento sloupec v modelu je zadaný název, což je detected_boxes.

    Vlastnost Scores obsahuje skóre spolehlivosti každého předpovězeného objektu a jeho popisku. Typ je plovoucí matice, takže každá položka v poli je skóre spolehlivosti každého popisku. Atribut ColumnName ML.NET říká, že tento sloupec v modelu je zadaný název, což je detected_scores.

Použití modelu k předpovědím

Přidejte příkazy using

Do souboru Program.cs přidejte na začátek souboru následující using direktivy.

using Microsoft.ML;
using Microsoft.ML.Transforms.Image;
using System.Drawing;
using WeatherRecognition;

Vytváření objektů

  1. MLContext Vytvořte objekt.

    var context = new MLContext();
    
  2. Vytvořte nový IDataView prázdný StopSignInput seznam.

    var data = context.Data.LoadFromEnumerable(new List<StopSignInput>());
    
  3. Pro konzistenci uložte předpovězené obrázky do cesty sestavení.

    var root = new FileInfo(typeof(Program).Assembly.Location);
    var assemblyFolderPath = root.Directory.FullName;
    

Sestavení kanálu

Když je vytvořený prázdný IDataView kanál, můžete ho vytvořit tak, aby mohl provádět předpovědi všech nových obrázků. Pipelina se skládá z několika kroků:

  1. Změňte velikost příchozích obrázků.

    Obrázek odesílaný do modelu pro predikci bude často v jiném poměru stran k obrázkům, které byly natrénovány na modelu. Pokud chcete zachovat konzistentní obraz pro přesné předpovědi, změňte velikost obrázku na 320x320. Uděláte to tak, že použijete metodu ResizeImages a nastavíte imageColumnName ji jako název StopSignInput.Image vlastnosti.

    var pipeline = context.Transforms.ResizeImages(resizing: ImageResizingEstimator.ResizingKind.Fill, outputColumnName: "image_tensor", imageWidth: ImageSettings.imageWidth, imageHeight: ImageSettings.imageHeight, inputColumnName: nameof(StopSignInput.Image))
    
  2. Extrahujte pixely obrázku.

    Po změně velikosti obrázku je potřeba extrahovat pixely obrázku. Připojte metodu ExtractPixels do kanálu a zadejte název sloupce pro výstup pixelů pro použití parametru outputColumnName .

    .Append(context.Transforms.ExtractPixels(outputColumnName: "image_tensor"))
    
  3. Vytvořte predikci použitím modelu ONNX na obrázku. To má několik parametrů:

    • modelFile – cesta k souboru modelu ONNX
    • outputColumnNames – pole řetězců obsahující názvy všech názvů výstupních sloupců, které lze najít při analýze modelu ONNX v Netronu.
    • inputColumnNames – pole řetězců obsahující názvy všech názvů vstupních sloupců, které lze najít také při analýze modelu ONNX v Netronu.

    Důležité

    Používejte pouze modely z důvěryhodných zdrojů. Použití modelů z nedůvěryhodných zdrojů představuje bezpečnostní riziko.

    .Append(context.Transforms.ApplyOnnxModel(outputColumnNames: new string[] { "detected_boxes", "detected_scores", "detected_classes" }, inputColumnNames: new string[] { "image_tensor" }, modelFile: "./Model/model.onnx"));
    

Přizpůsobení modelu

Teď, když jste definovali kanál, můžete ho použít k sestavení modelu ML.NET. Použijte metodu Fit v kanálu a předejte prázdnou IDataView.

var model = pipeline.Fit(data);

V dalším kroku použijte model k vytvoření prediktivního modulu. Jedná se o obecnou metodu, takže přebírá třídy StopSignInputStopSignPrediction , které byly vytvořeny dříve.

var predictionEngine = context.Model.CreatePredictionEngine<StopSignInput, StopSignPrediction>(model);

Extrahování popisků

Pokud chcete přiřadit výstupy modelu k jejich štítkům, musíte extrahovat štítky poskytované službou Custom Vision. Tyto popisky jsou v souborulabels.txt , který byl součástí souboru ZIP s modelem ONNX.

ReadAllLines Voláním metody přečtete všechny popisky, které tvoří soubor.

var labels = File.ReadAllLines("./model/labels.txt");

Předpověď na testovacím obrázku

Teď můžete model použít k predikci na nových obrázcích. V projektu je k dispozici testovací složka, kterou můžete použít k předpovědím. Tato složka obsahuje dva náhodné obrázky s dopravní značkou stop z Unsplash. Jeden obrázek má jedno znaménko zastavení, zatímco druhé má dvě značky stop. Použijte metodu GetFiles ke čtení cest k souborům obrázků v adresáři.

var testFiles = Directory.GetFiles("./test");

Projděte cesty k souborům, vytvořte predikci pomocí modelu a zobrazte výsledek.

  1. Vytvořte smyčku foreach pro procházení testovacích imagí.

    Bitmap testImage;
    
    foreach (var image in testFiles)
    {
    
    }
    
  2. foreach Ve smyčce vygenerujte predikovaný název obrázku na základě názvu původního testovacího obrázku.

    var predictedImage = $"{Path.GetFileName(image)}-predicted.jpg";
    
  3. Také ve foreach smyčce vytvořte FileStream image a převeďte ji na .Bitmap

    using (var stream = new FileStream(image, FileMode.Open))
    {
        testImage = (Bitmap)Image.FromStream(stream);
    }
    
  4. foreach Ve smyčce také volejte metodu Predict na prediktivním modulu.

    var prediction = predictionEngine.Predict(new StopSignInput { Image = testImage });
    
  5. Pomocí předpovědi můžete získat ohraničující rámečky. Chunk Pomocí metody určete, kolik objektů model zjistil. Uděláte to tak, že vezmete počet předpovídaných ohraničujících rámečků a vydělíte ho počtem popisků, které byly předpovězeny. Pokud jste například na obrázku rozpoznali tři objekty, bylo by v BoundingBoxes poli zjištěno 12 položek a bylo by předpovězeno tři popisky. Metoda Chunk by pak poskytla tři pole obsahující čtyři prvky, která reprezentují hraniční rámečky pro každý objekt.

    var boundingBoxes = prediction.BoundingBoxes.Chunk(prediction.BoundingBoxes.Count() / prediction.PredictedLabels.Count());
    
  6. Dále zachyťte původní šířku a výšku obrázků použitých pro predikci.

    var originalWidth = testImage.Width;
    var originalHeight = testImage.Height;
    
  7. Výpočet místa na obrázku pro vykreslení polí Za tímto účelem vytvořte smyčku for založenou na počtu bloků ohraničujících rámečků.

    for (int i = 0; i < boundingBoxes.Count(); i++)
    {
    }
    
  8. for Ve smyčce vypočítejte pozici souřadnic x a y a šířku a výšku rámečku, které se mají na obrázku kreslit. První věcí, kterou musíte udělat, je získat sadu ohraničujících polí pomocí ElementAt metody.

    var boundingBox = boundingBoxes.ElementAt(i);
    
  9. Pomocí aktuálního ohraničujícího rámečku teď můžete vypočítat, kam chcete pole nakreslit. Použijte původní šířku obrázku pro první a třetí prvky ohraničujícího rámečku a původní výšku obrázku pro druhý a čtvrtý prvek.

    var left = boundingBox[0] * originalWidth;
    var top = boundingBox[1] * originalHeight;
    var right = boundingBox[2] * originalWidth;
    var bottom = boundingBox[3] * originalHeight;
    
  10. Vypočítejte šířku a výšku pole, které se má nakreslit kolem zjištěného objektu v obrázku. Položky x a y jsou left hodnoty a top proměnné z předchozího výpočtu. Math.Abs Pomocí metody získáte absolutní hodnotu z výpočtů šířky a výšky pro případ, že je záporná.

    var x = left;
    var y = top;
    var width = Math.Abs(right - left);
    var height = Math.Abs(top - bottom);
    
  11. Dále získáte predikovaný štítek z pole štítků.

    var label = labels[prediction.PredictedLabels[i]];
    
  12. Pomocí metody vytvořte grafiku založenou na testovacím obrázku Graphics.FromImage .

    using var graphics = Graphics.FromImage(testImage);
    
  13. Kreslete do obrázku pomocí informací o ohraničujícím rámečku. Nejprve nakreslete obdélník kolem detekovaných objektů pomocí DrawRectangle metody, která přebírá Pen objekt k určení barvy a šířky obdélníku, a předejte xmu , y, widtha height proměnné.

    graphics.DrawRectangle(new Pen(Color.NavajoWhite, 8), x, y, width, height);
    
  14. Pak zobrazte předpovězený popisek uvnitř pole metodou DrawString , která přebírá řetězec k vytištění, a Font objekt k určení, jak nakreslit řetězec a kam ho umístit.

    graphics.DrawString(label, new Font(FontFamily.Families[0], 18f), Brushes.NavajoWhite, x + 5, y + 5);
    
  15. Po smyčce for zkontrolujte, jestli předpovězený soubor již existuje. Pokud ano, odstraňte ho. Pak ho uložte do definované výstupní cesty.

    if (File.Exists(predictedImage))
    {
        File.Delete(predictedImage);
    }
    
    testImage.Save(Path.Combine(assemblyFolderPath, predictedImage));
    

Další kroky

Vyzkoušejte jeden z dalších kurzů klasifikace obrázků: