Tutorial: Mendeteksi objek menggunakan ONNX di ML.NET

Pelajari cara menggunakan model ONNX yang telah dilatih sebelumnya di ML.NET untuk mendeteksi objek dalam gambar.

Melatih model deteksi objek dari awal memerlukan pengaturan jutaan parameter, sejumlah besar data pelatihan berlabel, dan sejumlah besar sumber daya komputasi (ratusan jam GPU). Menggunakan model yang telah dilatih sebelumnya memungkinkan Anda untuk pintasan proses pelatihan.

Dalam tutorial ini, Anda akan mempelajari cara:

  • Memahami masalahnya
  • Pelajari apa itu ONNX dan cara kerjanya dengan ML.NET
  • Memahami model
  • Menggunakan kembali model yang telah dilatih sebelumnya
  • Mendeteksi objek dengan model yang dimuat

Prasyarat

Gambaran umum sampel deteksi objek ONNX

Sampel ini membuat aplikasi konsol inti .NET yang mendeteksi objek dalam gambar menggunakan model ONNX pembelajaran mendalam yang telah dilatih sebelumnya. Kode untuk sampel ini dapat ditemukan di repositori dotnet/machinelearning-samples di GitHub.

Apa itu deteksi objek?

Deteksi objek adalah masalah visi komputer. Meskipun terkait erat dengan klasifikasi gambar, deteksi objek melakukan klasifikasi gambar pada skala yang lebih terperinci. Deteksi objek menemukan dan mengategorikan entitas dalam gambar. Model deteksi objek umumnya dilatih menggunakan jaringan pembelajaran mendalam dan neural. Lihat Pembelajaran mendalam vs pembelajaran mesin untuk informasi selengkapnya.

Gunakan deteksi objek saat gambar berisi beberapa objek dari berbagai jenis.

Screenshots showing Image Classification versus Object Classification.

Beberapa kasus penggunaan untuk deteksi objek meliputi:

  • Mengendarai Mobil Mandiri
  • Robotika
  • Deteksi wajah
  • Brankas tempat kerja
  • Penghitungan Objek
  • Pengenalan Aktivitas

Pilih model pembelajaran mendalam

Deep learning adalah bagian dari pembelajaran mesin. Untuk melatih model pembelajaran mendalam, diperlukan data dalam jumlah besar. Pola dalam data diwakili oleh serangkaian lapisan. Hubungan dalam data dikodekan sebagai koneksi antara lapisan yang berisi bobot. Semakin tinggi berat badan, semakin kuat hubungannya. Secara kolektif, rangkaian lapisan dan koneksi ini dikenal sebagai jaringan neural buatan. Semakin banyak lapisan dalam jaringan, "lebih dalam", menjadikannya jaringan neural yang dalam.

Ada berbagai jenis jaringan saraf, yang paling umum adalah Multi-Layered Perceptron (MLP), Convolutional Neural Network (CNN) dan Recurrent Neural Network (RNN). Yang paling mendasar adalah MLP, yang memetakan sekumpulan input ke sekumpulan output. Jaringan neural ini baik ketika data tidak memiliki komponen spasial atau waktu. CNN menggunakan lapisan konvolusional untuk memproses informasi spasial yang terkandung dalam data. Kasus penggunaan yang baik untuk CN adalah pemrosesan gambar untuk mendeteksi keberadaan fitur di wilayah gambar (misalnya, apakah ada hidung di tengah gambar?). Terakhir, RNN memungkinkan kegigihan status atau memori untuk digunakan sebagai input. RNN digunakan untuk analisis rangkaian waktu, di mana urutan dan konteks peristiwa berurutan penting.

Memahami model

Deteksi objek adalah tugas pemrosesan gambar. Oleh karena itu, sebagian besar model pembelajaran mendalam yang dilatih untuk menyelesaikan masalah ini adalah CN. Model yang digunakan dalam tutorial ini adalah model TINY YOLOv2, versi yang lebih ringkas dari model YOLOv2 yang dijelaskan dalam makalah: "YOLO9000: Lebih Baik, Lebih Cepat, Lebih Kuat" oleh Redmon dan Farhadi. Tiny YOLOv2 dilatih pada himpunan data PASCAL VOC dan terdiri dari 15 lapisan yang dapat memprediksi 20 kelas objek yang berbeda. Karena Tiny YOLOv2 adalah versi ringkas dari model YOLOv2 asli, tradeoff dibuat antara kecepatan dan akurasi. Berbagai lapisan yang membentuk model dapat divisualisasikan menggunakan alat seperti Netron. Memeriksa model akan menghasilkan pemetaan koneksi antara semua lapisan yang membentuk jaringan neural, di mana setiap lapisan akan berisi nama lapisan bersama dengan dimensi input / output masing-masing. Struktur data yang digunakan untuk menjelaskan input dan output model dikenal sebagai tensor. Tensor dapat dianggap sebagai kontainer yang menyimpan data dalam N-dimensi. Dalam kasus Tiny YOLOv2, nama lapisan input adalah image dan mengharapkan tensor dimensi 3 x 416 x 416. Nama lapisan output adalah grid dan menghasilkan tensor output dimensi 125 x 13 x 13.

Input layer being split into hidden layers, then output layer

Model YOLO mengambil gambar 3(RGB) x 416px x 416px. Model mengambil input ini dan melewati lapisan yang berbeda untuk menghasilkan output. Output membagi gambar input menjadi 13 x 13 kisi, dengan setiap sel dalam kisi yang terdiri dari 125 nilai.

Apa itu model ONNX?

Open Neural Network Exchange (ONNX) adalah format sumber terbuka untuk model AI. ONNX mendukung interoperabilitas antarkerangka kerja. Ini berarti Anda dapat melatih model dalam salah satu dari banyak kerangka kerja pembelajaran mesin populer seperti PyTorch, mengonversinya menjadi format ONNX dan menggunakan model ONNX dalam kerangka kerja yang berbeda seperti ML.NET. Untuk mempelajari lebih lanjut, kunjungi situs web ONNX.

Diagram of ONNX supported formats being used.

Model Tiny YOLOv2 yang telah dilatih sebelumnya disimpan dalam format ONNX, representasi serial lapisan dan pola yang dipelajari dari lapisan tersebut. Dalam ML.NET, interoperabilitas dengan ONNX dicapai dengan ImageAnalytics paket dan OnnxTransformer NuGet. Paket ImageAnalytics berisi serangkaian transformasi yang mengambil gambar dan mengodekannya menjadi nilai numerik yang dapat digunakan sebagai input ke dalam alur prediksi atau pelatihan. Paket ini OnnxTransformer memanfaatkan ONNX Runtime untuk memuat model ONNX dan menggunakannya untuk membuat prediksi berdasarkan input yang disediakan.

Data flow of ONNX file into the ONNX Runtime.

Menyiapkan proyek Konsol .NET

Sekarang setelah Anda memiliki pemahaman umum tentang apa itu ONNX dan cara kerja Tiny YOLOv2, saatnya untuk membangun aplikasi.

Membuat aplikasi konsol lokal

  1. Buat Aplikasi Konsol C# yang disebut "ObjectDetection". Klik tombol Berikutnya.

  2. Pilih .NET 6 sebagai kerangka kerja yang akan digunakan. Klik tombol Buat.

  3. Instal Paket NuGet Microsoft.ML:

    Catatan

    Sampel ini menggunakan versi stabil terbaru dari paket NuGet yang disebutkan kecuali dinyatakan lain.

    • Di Penjelajah Solusi, klik kanan proyek Anda dan pilih Kelola Paket NuGet.
    • Pilih "nuget.org" sebagai sumber Paket, pilih tab Telusuri, cari Microsoft.ML.
    • Pilih tombol Instal.
    • Pilih tombol OK pada dialog Pratinjau Perubahan lalu pilih tombol Saya Terima pada dialog Penerimaan Lisensi jika Anda setuju dengan ketentuan lisensi untuk paket yang tercantum.
    • Ulangi langkah-langkah ini untuk Microsoft.Windows.Compatibility, Microsoft.ML.ImageAnalytics, Microsoft.ML.OnnxTransformer, dan Microsoft.ML.OnnxRuntime.

Menyiapkan data dan model yang telah dilatih sebelumnya

  1. Unduh file zip direktori aset proyek dan unzip.

  2. assets Salin direktori ke direktori proyek ObjectDetection Anda. Direktori ini dan subdirektorinya berisi file gambar (kecuali untuk model Tiny YOLOv2, yang akan Anda unduh dan tambahkan di langkah berikutnya) yang diperlukan untuk tutorial ini.

  3. Unduh model Tiny YOLOv2 dari Kebun Binatang Model ONNX.

  4. model.onnx Salin file ke direktori proyek assets\Model ObjectDetection Anda dan ganti namanya menjadi TinyYolo2_model.onnx. Direktori ini berisi model yang diperlukan untuk tutorial ini.

  5. Di Penjelajah Solusi, klik kanan setiap file di direktori aset dan subdirektori dan pilih Properti. Di bawah Tingkat Lanjut, ubah nilai Salin ke Direktori Output menjadi Salin jika lebih baru.

Membuat kelas dan menentukan jalur

Buka file Program.cs dan tambahkan pernyataan tambahan using berikut ke bagian atas file:

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

Selanjutnya, tentukan jalur berbagai aset.

  1. Pertama, buat GetAbsolutePath metode di bagian bawah 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. Kemudian, di bawah pernyataan penggunaan, buat bidang untuk menyimpan lokasi aset Anda.

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

Tambahkan direktori baru ke proyek Anda untuk menyimpan data input dan kelas prediksi Anda.

Di Penjelajah Solusi, klik kanan proyek, lalu pilih Tambahkan>Folder Baru. Saat folder baru muncul di Penjelajah Solusi, beri nama "DataStructures".

Buat kelas data input Anda di direktori DataStructures yang baru dibuat.

  1. Di Penjelajah Solusi, klik kanan direktori DataStructures, lalu pilih Tambahkan>Item Baru.

  2. Dalam kotak dialog Tambahkan Item Baru, pilih Kelas dan ubah bidang Nama menjadi ImageNetData.cs. Kemudian, pilih tombol Tambahkan .

    File ImageNetData.cs terbuka di editor kode. Tambahkan pernyataan berikut using ke bagian atas ImageNetData.cs:

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

    Hapus definisi kelas yang ada dan tambahkan kode berikut untuk ImageNetData kelas ke 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 adalah kelas data gambar input dan memiliki bidang berikut String :

    • ImagePath berisi jalur tempat gambar disimpan.
    • Label berisi nama file.

    Selain itu, ImageNetData berisi metode ReadFromFile yang memuat beberapa file gambar yang disimpan di imageFolder jalur yang ditentukan dan mengembalikannya sebagai kumpulan ImageNetData objek.

Buat kelas prediksi Anda di direktori DataStructures .

  1. Di Penjelajah Solusi, klik kanan direktori DataStructures, lalu pilih Tambahkan>Item Baru.

  2. Dalam kotak dialog Tambahkan Item Baru, pilih Kelas dan ubah bidang Nama menjadi ImageNetPrediction.cs. Kemudian, pilih tombol Tambahkan .

    File ImageNetPrediction.cs terbuka di editor kode. Tambahkan pernyataan berikut using ke bagian atas ImageNetPrediction.cs:

    using Microsoft.ML.Data;
    

    Hapus definisi kelas yang ada dan tambahkan kode berikut untuk ImageNetPrediction kelas ke file ImageNetPrediction.cs :

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

    ImageNetPrediction adalah kelas data prediksi dan memiliki bidang berikut float[] :

    • PredictedLabels berisi dimensi, skor objektif, dan probabilitas kelas untuk setiap kotak pembatas yang terdeteksi dalam gambar.

Menginisialisasi variabel

Kelas MLContext adalah titik awal untuk semua operasi ML.NET, dan menginisialisasi mlContext membuat lingkungan ML.NET baru yang dapat dibagikan di seluruh objek alur kerja pembuatan model. Ini mirip, secara konseptual, hingga DBContext di Entity Framework.

Inisialisasi mlContext variabel dengan instans MLContext baru dengan menambahkan baris berikut di outputFolder bawah bidang .

MLContext mlContext = new MLContext();

Membuat pengurai untuk output model pasca-proses

Model mengelompokan gambar ke dalam 13 x 13 kisi, di mana setiap sel kisi adalah 32px x 32px. Setiap sel kisi berisi 5 kotak pembatas objek potensial. Kotak pembatas memiliki 25 elemen:

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

  • x posisi x pusat kotak pembatas relatif terhadap sel kisi yang terkait dengannya.
  • y posisi y dari pusat kotak pembatas relatif terhadap sel kisi yang terkait dengannya.
  • w lebar kotak pembatas.
  • h tinggi kotak pembatas.
  • o nilai keyakinan bahwa objek ada dalam kotak pembatas, juga dikenal sebagai skor objek.
  • p1-p20 probabilitas kelas untuk masing-masing dari 20 kelas yang diprediksi oleh model.

Secara total, 25 elemen yang menjelaskan masing-masing dari 5 kotak pembatas membentuk 125 elemen yang terkandung dalam setiap sel kisi.

Output yang dihasilkan oleh model ONNX yang telah dilatih sebelumnya adalah array float panjang 21125, mewakili elemen tensor dengan dimensi 125 x 13 x 13. Untuk mengubah prediksi yang dihasilkan oleh model menjadi tensor, beberapa pekerjaan pasca-pemrosesan diperlukan. Untuk melakukannya, buat sekumpulan kelas untuk membantu mengurai output.

Tambahkan direktori baru ke proyek Anda untuk mengatur serangkaian kelas pengurai.

  1. Di Penjelajah Solusi, klik kanan proyek, lalu pilih Tambahkan>Folder Baru. Saat folder baru muncul di Penjelajah Solusi, beri nama "YoloParser".

Membuat kotak dan dimensi pembatas

Output data oleh model berisi koordinat dan dimensi kotak pembatas objek dalam gambar. Buat kelas dasar untuk dimensi.

  1. Di Penjelajah Solusi, klik kanan direktori YoloParser, lalu pilih Tambahkan>Item Baru.

  2. Dalam kotak dialog Tambahkan Item Baru, pilih Kelas dan ubah bidang Nama menjadi DimensionsBase.cs. Kemudian, pilih tombol Tambahkan .

    File DimensionsBase.cs terbuka di editor kode. Hapus semua using pernyataan dan definisi kelas yang ada.

    Tambahkan kode berikut untuk DimensionsBase kelas ke 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 memiliki properti berikut float :

    • X berisi posisi objek di sepanjang sumbu x.
    • Y berisi posisi objek di sepanjang sumbu y.
    • Height berisi tinggi objek.
    • Width berisi lebar objek.

Selanjutnya, buat kelas untuk kotak pembatas Anda.

  1. Di Penjelajah Solusi, klik kanan direktori YoloParser, lalu pilih Tambahkan>Item Baru.

  2. Dalam kotak dialog Tambahkan Item Baru, pilih Kelas dan ubah bidang Nama menjadi YoloBoundingBox.cs. Kemudian, pilih tombol Tambahkan .

    File YoloBoundingBox.cs terbuka di editor kode. Tambahkan pernyataan berikut using ke bagian atas YoloBoundingBox.cs:

    using System.Drawing;
    

    Tepat di atas definisi kelas yang ada, tambahkan definisi kelas baru yang disebut BoundingBoxDimensions yang mewarisi dari DimensionsBase kelas untuk berisi dimensi kotak pembatas masing-masing.

    public class BoundingBoxDimensions : DimensionsBase { }
    

    Hapus definisi kelas yang ada YoloBoundingBox dan tambahkan kode berikut untuk YoloBoundingBox kelas ke 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 memiliki properti berikut:

    • Dimensions berisi dimensi kotak pembatas.
    • Label berisi kelas objek yang terdeteksi dalam kotak pembatas.
    • Confidence berisi keyakinan kelas.
    • Rect berisi representasi persegi panjang dimensi kotak pembatas.
    • BoxColor berisi warna yang terkait dengan masing-masing kelas yang digunakan untuk menggambar pada gambar.

Membuat pengurai

Sekarang setelah kelas untuk dimensi dan kotak pembatas dibuat, saatnya untuk membuat pengurai.

  1. Di Penjelajah Solusi, klik kanan direktori YoloParser, lalu pilih Tambahkan>Item Baru.

  2. Dalam kotak dialog Tambahkan Item Baru, pilih Kelas dan ubah bidang Nama menjadi YoloOutputParser.cs. Kemudian, pilih tombol Tambahkan .

    File YoloOutputParser.cs terbuka di editor kode. Tambahkan pernyataan berikut using ke bagian atas YoloOutputParser.cs:

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

    Di dalam definisi kelas yang ada YoloOutputParser , tambahkan kelas berlapis yang berisi dimensi masing-masing sel dalam gambar. Tambahkan kode berikut untuk CellDimensions kelas yang mewarisi dari DimensionsBase kelas di bagian YoloOutputParser atas definisi kelas.

    class CellDimensions : DimensionsBase { }
    
  3. YoloOutputParser Di dalam definisi kelas, tambahkan konstanta dan bidang berikut.

    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 adalah jumlah baris dalam kisi yang dibagi menjadi gambar.
    • COL_COUNT adalah jumlah kolom dalam kisi yang dibagi menjadi gambar.
    • CHANNEL_COUNT adalah jumlah total nilai yang terkandung dalam satu sel kisi.
    • BOXES_PER_CELL adalah jumlah kotak pembatas dalam sel,
    • BOX_INFO_FEATURE_COUNT adalah jumlah fitur yang terkandung dalam kotak (x,y,tinggi, lebar, keyakinan).
    • CLASS_COUNT adalah jumlah prediksi kelas yang terkandung dalam setiap kotak pembatas.
    • CELL_WIDTH adalah lebar satu sel dalam kisi gambar.
    • CELL_HEIGHT adalah tinggi satu sel dalam kisi gambar.
    • channelStride adalah posisi awal sel saat ini dalam kisi.

    Ketika model membuat prediksi, juga dikenal sebagai penilaian, model membagi 416px x 416px gambar input menjadi kisi sel seukuran 13 x 13. Setiap sel berisi adalah 32px x 32px. Dalam setiap sel, ada 5 kotak pembatas yang masing-masing berisi 5 fitur (x, y, lebar, tinggi, keyakinan). Selain itu, setiap kotak pembatas berisi probabilitas masing-masing kelas, yang dalam hal ini adalah 20. Oleh karena itu, setiap sel berisi 125 informasi (5 fitur + 20 probabilitas kelas).

Buat daftar jangkar di bawah ini channelStride untuk 5 kotak pembatas:

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

Jangkar adalah rasio tinggi dan lebar kotak pembatas yang telah ditentukan sebelumnya. Sebagian besar objek atau kelas yang terdeteksi oleh model memiliki rasio yang sama. Ini berharga dalam hal membuat kotak pembatas. Alih-alih memprediksi kotak pembatas, offset dari dimensi yang telah ditentukan sebelumnya dihitung sehingga mengurangi komputasi yang diperlukan untuk memprediksi kotak pembatas. Biasanya rasio jangkar ini dihitung berdasarkan himpunan data yang digunakan. Dalam hal ini, karena himpunan data diketahui dan nilainya telah dikomputasi sebelumnya, jangkar dapat dikodekan secara permanen.

Selanjutnya, tentukan label atau kelas yang akan diprediksi model. Model ini memprediksi 20 kelas, yang merupakan subset dari jumlah total kelas yang diprediksi oleh model YOLOv2 asli.

Tambahkan daftar label Anda di anchorsbawah .

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

Ada warna yang terkait dengan masing-masing kelas. Tetapkan warna kelas Anda di bawah 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
};

Membuat fungsi pembantu

Ada serangkaian langkah yang terlibat dalam fase pasca-pemrosesan. Untuk membantu dengan itu, beberapa metode pembantu dapat digunakan.

Metode pembantu yang digunakan oleh pengurai adalah:

  • Sigmoid menerapkan fungsi sigmoid yang menghasilkan angka antara 0 dan 1.
  • Softmax menormalkan vektor input ke dalam distribusi probabilitas.
  • GetOffset memetakan elemen dalam output model satu dimensi ke posisi yang sesuai dalam 125 x 13 x 13 tensor.
  • ExtractBoundingBoxes mengekstrak dimensi kotak pembatas menggunakan GetOffset metode dari output model.
  • GetConfidence mengekstrak nilai keyakinan yang menyatakan seberapa yakin model telah mendeteksi objek dan menggunakan Sigmoid fungsi untuk mengubahnya menjadi persentase.
  • MapBoundingBoxToCell menggunakan dimensi kotak pembatas dan memetakannya ke sel masing-masing dalam gambar.
  • ExtractClasses mengekstrak prediksi kelas untuk kotak pembatas dari output model menggunakan GetOffset metode dan mengubahnya menjadi distribusi probabilitas menggunakan Softmax metode .
  • GetTopResult memilih kelas dari daftar kelas yang diprediksi dengan probabilitas tertinggi.
  • IntersectionOverUnion filter kotak pembatas yang tumpang tindih dengan probabilitas yang lebih rendah.

Tambahkan kode untuk semua metode pembantu di bawah daftar Anda.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);
}

Setelah Anda menentukan semua metode pembantu, saatnya untuk menggunakannya untuk memproses output model.

IntersectionOverUnion Di bawah metode , buat ParseOutputs metode untuk memproses output yang dihasilkan oleh model.

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

}

Buat daftar untuk menyimpan kotak pembatas Anda dan tentukan variabel di ParseOutputs dalam metode .

var boxes = new List<YoloBoundingBox>();

Setiap gambar dibagi menjadi kisi 13 x 13 sel. Setiap sel berisi lima kotak pembatas. boxes Di bawah variabel, tambahkan kode untuk memproses semua kotak di setiap sel.

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

        }
    }
}

Di dalam perulangan paling dalam, hitung posisi awal kotak saat ini dalam output model satu dimensi.

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

Tepat di bawahnya, gunakan ExtractBoundingBoxDimensions metode untuk mendapatkan dimensi kotak pembatas saat ini.

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

Kemudian, gunakan GetConfidence metode untuk mendapatkan keyakinan untuk kotak pembatas saat ini.

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

Setelah itu, gunakan MapBoundingBoxToCell metode untuk memetakan kotak batas saat ini ke sel saat ini yang sedang diproses.

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

Sebelum melakukan pemrosesan lebih lanjut, periksa apakah nilai keyakinan Anda lebih besar dari ambang batas yang disediakan. Jika tidak, proses kotak pembatas berikutnya.

if (confidence < threshold)
    continue;

Jika tidak, lanjutkan pemrosesan output. Langkah selanjutnya adalah mendapatkan distribusi probabilitas kelas yang diprediksi untuk kotak pembatas saat ini menggunakan ExtractClasses metode .

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

Kemudian, gunakan GetTopResult metode untuk mendapatkan nilai dan indeks kelas dengan probabilitas tertinggi untuk kotak saat ini dan menghitung skornya.

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

topScore Gunakan untuk sekali lagi hanya menyimpan kotak pembatas yang berada di atas ambang yang ditentukan.

if (topScore < threshold)
    continue;

Terakhir, jika kotak pembatas saat ini melebihi ambang batas, buat objek baru BoundingBox dan tambahkan ke boxes daftar.

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

Setelah semua sel dalam gambar diproses, kembalikan boxes daftar. Tambahkan pernyataan pengembalian berikut di bawah for-loop terluar dalam ParseOutputs metode .

return boxes;

Filter kotak yang tumpang tindih

Sekarang setelah semua kotak pembatas yang sangat percaya diri telah diekstraksi dari output model, pemfilteran tambahan perlu dilakukan untuk menghapus gambar yang tumpang tindih. Tambahkan metode yang disebut FilterBoundingBoxes di ParseOutputs bawah metode :

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

}

FilterBoundingBoxes Di dalam metode , mulailah dengan membuat array yang sama dengan ukuran kotak yang terdeteksi dan menandai semua slot sebagai aktif atau siap untuk diproses.

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

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

Kemudian, urutkan daftar yang berisi kotak pembatas Anda dalam urutan turun berdasarkan keyakinan.

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

Setelah itu, buat daftar untuk menyimpan hasil yang difilter.

var results = new List<YoloBoundingBox>();

Mulai memproses setiap kotak pembatas dengan melakukan iterasi di setiap kotak pembatas.

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

}

Di dalam for-loop ini, periksa apakah kotak pembatas saat ini dapat diproses.

if (isActiveBoxes[i])
{

}

Jika demikian, tambahkan kotak pembatas ke daftar hasil. Jika hasilnya melebihi batas kotak yang ditentukan untuk diekstrak, keluar dari perulangan. Tambahkan kode berikut di dalam pernyataan if.

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

if (results.Count >= limit)
    break;

Jika tidak, lihat kotak pembatas yang berdekatan. Tambahkan kode berikut di bawah kotak centang batas.

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

}

Seperti kotak pertama, jika kotak yang berdekatan aktif atau siap diproses, gunakan IntersectionOverUnion metode untuk mencentang apakah kotak pertama dan kotak kedua melebihi ambang yang ditentukan. Tambahkan kode berikut ke perulangan terda dalam Anda.

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

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

        if (activeCount <= 0)
            break;
    }
}

Di luar inner-most for-loop yang memeriksa kotak pembatas yang berdekatan, lihat apakah ada kotak pembatas yang tersisa untuk diproses. Jika tidak, keluar dari outer for-loop.

if (activeCount <= 0)
    break;

Akhirnya, di luar for-loop FilterBoundingBoxes awal metode , kembalikan hasilnya:

return results;

Bagus! Sekarang saatnya untuk menggunakan kode ini bersama dengan model untuk penilaian.

Menggunakan model untuk penilaian

Sama seperti dengan pasca-pemrosesan, ada beberapa langkah dalam langkah-langkah penilaian. Untuk membantu hal ini, tambahkan kelas yang akan berisi logika penilaian ke proyek Anda.

  1. Di Penjelajah Solusi, klik kanan proyek Anda dan pilih Tambahkan>Item baru.

  2. Dalam kotak dialog Tambahkan Item Baru, pilih Kelas dan ubah bidang Nama menjadi OnnxModelScorer.cs. Kemudian, pilih tombol Tambahkan .

    File OnnxModelScorer.cs terbuka di editor kode. Tambahkan pernyataan berikut using ke bagian atas OnnxModelScorer.cs:

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

    OnnxModelScorer Di dalam definisi kelas, tambahkan variabel berikut.

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

    Tepat di bawahnya, buat konstruktor untuk OnnxModelScorer kelas yang akan menginisialisasi variabel yang ditentukan sebelumnya.

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

    Setelah Anda membuat konstruktor, tentukan beberapa struktur yang berisi variabel yang terkait dengan pengaturan gambar dan model. Buat struktur yang dipanggil ImageNetSettings untuk berisi tinggi dan lebar yang diharapkan sebagai input untuk model.

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

    Setelah itu, buat struct lain yang disebut TinyYoloModelSettings yang berisi nama lapisan input dan output model. Untuk memvisualisasikan nama lapisan input dan output model, Anda dapat menggunakan alat seperti 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";
    }
    

    Selanjutnya, buat set metode pertama yang digunakan untuk penilaian. Buat metode di LoadModel dalam kelas Anda OnnxModelScorer .

    private ITransformer LoadModel(string modelLocation)
    {
    
    }
    

    LoadModel Di dalam metode , tambahkan kode berikut untuk pengelogan.

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

    ML.NET alur perlu mengetahui skema data yang akan dioperasikan saat Fit metode dipanggil. Dalam hal ini, proses yang mirip dengan pelatihan akan digunakan. Namun, karena tidak ada pelatihan aktual yang terjadi, dapat diterima untuk menggunakan kosong IDataView. Buat baru IDataView untuk alur dari daftar kosong.

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

    Di bawahnya, tentukan alur. Alur akan terdiri dari empat transformasi.

    • LoadImages memuat gambar sebagai Bitmap.
    • ResizeImages menskalakan ulang gambar ke ukuran yang ditentukan (dalam hal ini, 416 x 416).
    • ExtractPixels mengubah representasi piksel gambar dari Bitmap menjadi vektor numerik.
    • ApplyOnnxModel memuat model ONNX dan menggunakannya untuk menilai data yang disediakan.

    Tentukan alur Anda dalam metode di LoadModel bawah data variabel .

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

    Sekarang saatnya untuk membuat instans model untuk penilaian. Fit Panggil metode pada alur dan kembalikan untuk diproses lebih lanjut.

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

Setelah model dimuat, model kemudian dapat digunakan untuk membuat prediksi. Untuk memfasilitasi proses tersebut, buat metode yang disebut PredictDataUsingModel di LoadModel bawah metode .

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

}

PredictDataUsingModelDi dalam , tambahkan kode berikut untuk pengelogan.

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

Kemudian, gunakan Transform metode untuk menilai data.

IDataView scoredData = model.Transform(testData);

Ekstrak probabilitas yang diprediksi dan kembalikan untuk pemrosesan tambahan.

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

return probabilities;

Sekarang setelah kedua langkah disiapkan, gabungkan menjadi satu metode. PredictDataUsingModel Di bawah metode , tambahkan metode baru yang disebut Score.

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

    return PredictDataUsingModel(data, model);
}

Hampir selesai! Sekarang saatnya untuk menggunakan semuanya.

Mendeteksi objek

Sekarang setelah semua penyiapan selesai, saatnya untuk mendeteksi beberapa objek.

Menilai dan mengurai output model

Di bawah pembuatan mlContext variabel, tambahkan pernyataan try-catch.

try
{

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

try Di dalam blok, mulai terapkan logika deteksi objek. Pertama, muat data ke dalam IDataView.

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

Kemudian, buat instans OnnxModelScorer dan gunakan untuk menilai data yang dimuat.

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

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

Sekarang saatnya untuk langkah pasca-pemrosesan. Buat instans YoloOutputParser dan gunakan untuk memproses output model.

YoloOutputParser parser = new YoloOutputParser();

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

Setelah output model diproses, saatnya untuk menggambar kotak pembatas pada gambar.

Visualisasikan prediksi

Setelah model mencetak gambar dan output telah diproses, kotak pembatas harus digambar pada gambar. Untuk melakukannya, tambahkan metode yang disebut DrawBoundingBox di bawah GetAbsolutePath metode di dalam Program.cs.

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

}

Pertama, muat gambar dan dapatkan dimensi tinggi dan lebar dalam DrawBoundingBox metode .

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

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

Kemudian, buat perulangan for-each untuk melakukan iterasi pada setiap kotak pembatas yang terdeteksi oleh model.

foreach (var box in filteredBoundingBoxes)
{

}

Di dalam perulangan for-each, dapatkan dimensi kotak pembatas.

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

Karena dimensi kotak pembatas sesuai dengan input 416 x 416model , skalakan dimensi kotak pembatas agar sesuai dengan ukuran gambar yang sebenarnya.

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;

Kemudian, tentukan templat untuk teks yang akan muncul di atas setiap kotak pembatas. Teks akan berisi kelas objek di dalam kotak pembatas masing-masing serta keyakinan.

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

Untuk menggambar pada gambar, konversikan ke Graphics objek.

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

}

using Di dalam blok kode, sesuaikan pengaturan objek grafikGraphics.

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

Di bawahnya, atur opsi font dan warna untuk kotak teks dan pembatas.

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

Buat dan isi persegi panjang di atas kotak pembatas untuk berisi teks menggunakan FillRectangle metode . Ini akan membantu membedakan teks dan meningkatkan keterbacaan.

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

Kemudian, Gambar teks dan kotak pembatas pada gambar menggunakan DrawString metode dan DrawRectangle .

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

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

Di luar perulangan for-each, tambahkan kode untuk menyimpan gambar di outputFolder.

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

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

Untuk umpan balik tambahan bahwa aplikasi membuat prediksi seperti yang diharapkan pada run time, tambahkan metode yang disebut LogDetectedObjects di bawah DrawBoundingBox metode dalam file Program.cs untuk menghasilkan objek yang terdeteksi ke konsol.

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

Sekarang setelah Anda memiliki metode pembantu untuk membuat umpan balik visual dari prediksi, tambahkan perulangan untuk mengulangi setiap gambar yang dinilai.

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

}

Di dalam for-loop, dapatkan nama file gambar dan kotak pembatas yang terkait dengannya.

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

Di bawahnya, gunakan DrawBoundingBox metode untuk menggambar kotak pembatas pada gambar.

DrawBoundingBox(imagesFolder, outputFolder, imageFileName, detectedObjects);

Terakhir, gunakan LogDetectedObjects metode untuk menghasilkan prediksi ke konsol.

LogDetectedObjects(imageFileName, detectedObjects);

Setelah pernyataan try-catch, tambahkan logika tambahan untuk menunjukkan proses selesai berjalan.

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

Itu saja!

Hasil

Setelah mengikuti langkah-langkah sebelumnya, jalankan aplikasi konsol Anda (Ctrl + F5). Hasil Anda harus mirip dengan output berikut. Anda mungkin melihat peringatan atau pesan pemrosesan, tetapi pesan ini telah dihapus dari hasil berikut untuk kejelasan.

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

Untuk melihat gambar dengan kotak pembatas, navigasikan ke assets/images/output/ direktori. Di bawah ini adalah sampel dari salah satu gambar yang diproses.

Sample processed image of a dining room

Selamat! Anda sekarang telah berhasil membangun model pembelajaran mesin untuk deteksi objek dengan menggunakan kembali model yang telah dilatih ONNX sebelumnya di ML.NET.

Anda dapat menemukan kode sumber untuk tutorial ini di repositori dotnet/machinelearning-samples .

Dalam tutorial ini, Anda mempelajari cara:

  • Memahami masalahnya
  • Pelajari apa itu ONNX dan cara kerjanya dengan ML.NET
  • Memahami model
  • Menggunakan kembali model yang telah dilatih sebelumnya
  • Mendeteksi objek dengan model yang dimuat

Lihat repositori GitHub sampel Pembelajaran Mesin untuk menjelajahi sampel deteksi objek yang diperluas.