Aracılığıyla paylaş


Öğretici: ML.NET'da ONNX kullanarak nesneleri algılama

görüntülerdeki nesneleri algılamak için ML.NET'da önceden eğitilmiş bir ONNX modelini kullanmayı öğrenin.

Sıfırdan nesne algılama modelini eğitme, milyonlarca parametre, büyük miktarda etiketlenmiş eğitim verisi ve çok miktarda işlem kaynağı (yüzlerce GPU saati) ayarlamayı gerektirir. Önceden eğitilmiş bir model kullanmak, eğitim sürecine kısayol oluşturmanıza olanak tanır.

Bu eğitimde şunları öğreniyorsunuz:

  • Sorunu anlama
  • ONNX'in ne olduğunu ve ML.NET ile nasıl çalıştığını öğrenin
  • Modeli anlama
  • Önceden eğitilmiş modeli yeniden kullanma
  • Yüklü bir modelle nesneleri algılama

Önkoşullar

ONNX nesne algılama örneğine genel bakış

Bu örnek, önceden eğitilmiş bir derin öğrenme ONNX modeli kullanarak görüntüdeki nesneleri algılayan bir .NET core konsol uygulaması oluşturur. Bu örneğin kodu GitHub'daki dotnet/machinelearning-samples deposunda bulunabilir.

Nesne algılama nedir?

Nesne algılama bir görüntü işleme sorunudur. Nesne algılama, görüntü sınıflandırmasıyla yakından ilişkili olsa da görüntü sınıflandırmasını daha ayrıntılı bir ölçekte gerçekleştirir. Nesne algılama, görüntüler içindeki varlıkları hem bulur hem de kategorilere ayırır. Nesne algılama modelleri genellikle derin öğrenme ve sinir ağları kullanılarak eğitilir. Daha fazla bilgi için bkz. Derin öğrenme ve makine öğrenmesi .

Görüntüler farklı türde birden çok nesne içerdiğinde nesne algılamayı kullanın.

Görüntü Sınıflandırma ve Nesne Sınıflandırması'nın gösterildiği ekran görüntüleri.

Nesne algılama için bazı kullanım örnekleri şunlardır:

  • Otonom Arabalar
  • Robotik
  • Yüz Algılama
  • İş Yeri Güvenliği
  • Nesne Sayma
  • Aktivite Tanıma

Derin öğrenme modeli seçme

Derin öğrenme, makine öğrenmesinin bir alt kümesidir. Derin öğrenme modellerini eğitmek için büyük miktarda veri gerekir. Verilerdeki desenler bir dizi katmanla temsil edilir. Verilerdeki ilişkiler, ağırlık içeren katmanlar arasındaki bağlantılar olarak kodlanır. Ağırlık ne kadar yüksekse ilişki o kadar güçlü olur. Bu katman ve bağlantı serisi, yapay sinir ağları olarak bilinir. Bir ağda ne kadar çok katman olursa o kadar "derin" olur ve bu da onu derin bir sinir ağı haline getirir.

En yaygın olanı Çok Katmanlı Algılama (MLP), Kıvrımlı Sinir Ağı (CNN) ve Yinelenen Sinir Ağı (RNN) olan farklı sinir ağı türleri vardır. En temel olan, bir dizi girişi bir çıkış kümesiyle eşleyen MLP'dir. Bu sinir ağı, verilerin uzamsal veya zaman bileşeni olmadığında iyidir. CNN, verilerde yer alan uzamsal bilgileri işlemek için kıvrımlı katmanları kullanır. CNN'ler için iyi bir kullanım örneği, görüntünün bir bölgesinde bir özelliğin varlığını algılamak için görüntü işlemedir (örneğin, görüntünün ortasında bir burun var mı?). Son olarak, RNN'ler durum veya belleğin kalıcılığının giriş olarak kullanılmasına olanak sağlar. RNN'ler, olayların sıralı sıralamasının ve bağlamının önemli olduğu zaman serisi analizi için kullanılır.

Modeli anlama

Nesne algılama bir görüntü işleme görevidir. Bu nedenle, bu sorunu çözmek için eğitilen derin öğrenme modellerinin çoğu CNN'lerdir. Bu öğreticide kullanılan model, MAKALEDE açıklanan YOLOv2 modelinin daha kompakt bir sürümü olan Tiny YOLOv2 modelidir: Redmon ve Farhadi tarafından "YOLO9000: Daha İyi, Daha Hızlı, Daha Güçlü". Tiny YOLOv2, Pascal VOC veri kümesi üzerinde eğitilir ve 20 farklı nesne sınıfını tahmin edebilen 15 katmandan oluşur. Tiny YOLOv2 orijinal YOLOv2 modelinin sıkıştırılmış bir sürümü olduğundan, hız ve doğruluk arasında bir denge sağlanır. Modeli oluşturan farklı katmanlar Netron gibi araçlar kullanılarak görselleştirilebilir. Modelin incelenmesi sinir ağını oluşturan tüm katmanlar arasındaki bağlantıların eşlenmesine neden olur ve burada her katman, ilgili giriş/çıkışın boyutlarıyla birlikte katmanın adını içerir. Modelin girişlerini ve çıkışlarını açıklamak için kullanılan veri yapıları, tensor olarak bilinir. Tensor'lar, verileri N boyutlu olarak depolayan kapsayıcılar olarak düşünülebilir. Tiny YOLOv2 söz konusu olduğunda, giriş katmanının adı image olup, 3 x 416 x 416 boyutlarında bir tensör bekler. Çıkış katmanının adıdır grid ve boyutların 125 x 13 x 13çıkış tensorunu oluşturur.

Gizli katmanlara bölünen giriş katmanı, ardından çıkış katmanı

YOLO modeli bir görüntü 3(RGB) x 416px x 416pxalır. Model bu girişi alır ve çıkış oluşturmak için farklı katmanlardan geçirir. Giriş görüntüsünü bir 13 x 13 kılavuza bölen çıkış, kılavuzdaki her hücrenin 125 değerlerden oluşmasını sağlar.

ONNX modeli nedir?

Açık Sinir Ağı Değişimi (ONNX), yapay zeka modelleri için açık kaynak bir biçimdir. ONNX, çerçeveler arasında birlikte çalışabilirliği destekler. Bu, PyTorch gibi birçok popüler makine öğrenmesi çerçevelerinden birinde modeli eğitebileceğiniz, ONNX biçimine dönüştürebileceğiniz ve ONNX modelini ML.NET gibi farklı bir çerçevede kullanabileceğiniz anlamına gelir. Daha fazla bilgi edinmek için ONNX web sitesini ziyaret edin.

Kullanılan ONNX desteklenen biçimlerin diyagramı.

Önceden eğitilmiş Tiny YOLOv2 modeli, katmanların ve bu katmanların öğrenilen desenlerinin serileştirilmiş bir gösterimi olarak ONNX biçiminde depolanır. ML.NET'te, ImageAnalytics ve OnnxTransformer NuGet paketleriyle ONNX ile birlikte çalışabilirlik sağlanır. Paket, ImageAnalytics bir görüntü alan ve bunu bir tahmin veya eğitim işlem hattına giriş olarak kullanılabilecek sayısal değerlere kodlayan bir dizi dönüşüm içerir. Paket, OnnxTransformer ONNX modeli yüklemek için ONNX Çalışma Zamanı'nı kullanır ve sağlanan girişe göre tahminlerde bulunmak için bu modeli kullanır.

ONNX dosyasının ONNX Çalışma Zamanına veri akışı.

.NET Konsol projesini ayarlama

ARTıK ONNX'in ne olduğunu ve Tiny YOLOv2'nin nasıl çalıştığını genel olarak anladığınıza göre, uygulamayı oluşturmanın zamanı geldi.

Konsol uygulaması oluşturma

  1. "ObjectDetection" adlı bir C# Konsol Uygulaması oluşturun. İleri düğmesine tıklayın.

  2. Kullanılacak çerçeve olarak .NET 8'i seçin. Oluştur düğmesine tıklayın.

  3. Microsoft.ML NuGet Paketini yükleyin:

    Uyarı

    Bu örnek, aksi belirtilmedikçe bahsedilen NuGet paketlerinin en son kararlı sürümünü kullanır.

    • Çözüm Gezgini'nde projenize sağ tıklayın ve NuGet Paketlerini Yönet'i seçin.
    • Paket kaynağı olarak "nuget.org" seçeneğini belirleyin, Gözat sekmesini seçin, Microsoft.ML arayın.
    • Yükle düğmesini seçin.
    • Listelenen paketlerin lisans koşullarını kabul ediyorsanız, Değişiklikleri Önizle iletişim kutusunda Tamam düğmesini ve ardından Lisans Kabulü iletişim kutusunda Kabul Ediyorum düğmesini seçin.
    • Microsoft.Windows.Compatibility, Microsoft.ML.ImageAnalytics, Microsoft.ML.OnnxTransformer ve Microsoft.ML.OnnxRuntime için bu adımları yineleyin.

Verilerinizi ve önceden eğitilmiş modelinizi hazırlama

  1. Proje varlıkları dizini zip dosyasını indirin ve sıkıştırmayı açın.

  2. assets Dizini ObjectDetection proje dizininize kopyalayın. Bu dizin ve alt dizinleri, bu öğretici için gereken görüntü dosyalarını (bir sonraki adımda indirip ekleyeceğiniz Tiny YOLOv2 modeli hariç) içerir.

  3. ONNX Model Hayvanat Bahçesi'nden Tiny YOLOv2 modelini indirin.

  4. model.onnx Dosyayı ObjectDetection proje assets\Model dizininize kopyalayın ve olarak yeniden adlandırınTinyYolo2_model.onnx. Bu dizin, bu öğretici için gereken modeli içerir.

  5. Çözüm Gezgini'nde varlık dizinindeki ve alt dizinlerdeki dosyaların her birine sağ tıklayın ve Özellikler'i seçin. Gelişmiş altında, Çıkış Dizinine Kopyala değerini Daha yeniyse kopyala olarak değiştirin.

Sınıf oluşturma ve yolları tanımlama

Program.cs dosyasını açın ve dosyanın en üstüne aşağıdaki ek using yönergeleri ekleyin:

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

Ardından, çeşitli varlıkların yollarını tanımlayın.

  1. İlk olarak, GetAbsolutePath dosyasının en altında yöntemini oluşturun.

    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. Ardından, yönergelerin using altında varlıklarınızın konumunu depolamak için alanlar oluşturun.

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

Giriş verilerinizi ve tahmin sınıflarınızı depolamak için projenize yeni bir dizin ekleyin.

Çözüm Gezgini'nde projeye sağ tıklayın veardından Yeni Klasör Ekle'yi> seçin. Yeni klasör Çözüm Gezgini'nde göründüğünde, bunu "DataStructures" olarak adlandırın.

Giriş veri sınıfınızı yeni oluşturulan DataStructures dizininde oluşturun.

  1. Çözüm Gezgini'ndeDataStructures dizinine sağ tıklayın veardından Yeni Öğe Ekle'yi> seçin.

  2. Yeni Öğe Ekle iletişim kutusunda Sınıf'ı seçin ve Ad alanını ImageNetData.cs olarak değiştirin. Ardından Ekle'yi seçin.

    ImageNetData.cs dosyası kod düzenleyicisinde açılır. aşağıdaki using yönergeyi ImageNetData.cs en üstüne ekleyin:

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

    Mevcut sınıf tanımını kaldırın ve sınıfı için ImageNetData aşağıdaki kodu ImageNetData.cs dosyasına ekleyin:

    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 giriş görüntüsü veri sınıfıdır ve aşağıdaki String alanlara sahiptir:

    • ImagePath görüntünün depolandığı yolu içerir.
    • Label dosyanın adını içerir.

    Ayrıca, ImageNetData belirtilen yolda depolanan ReadFromFile birden çok görüntü dosyasını yükleyen ve bunları bir nesne koleksiyonu imageFolder olarak döndüren bir yöntem ImageNetData içerir.

DataStructures dizininde tahmin sınıfınızı oluşturun.

  1. Çözüm Gezgini'ndeDataStructures dizinine sağ tıklayın veardından Yeni Öğe Ekle'yi> seçin.

  2. Yeni Öğe Ekle iletişim kutusunda Sınıf'ı seçin ve Ad alanını ImageNetPrediction.cs olarak değiştirin. Ardından Ekle'yi seçin.

    ImageNetPrediction.cs dosyası kod düzenleyicisinde açılır. aşağıdaki using yönergeyi ImageNetPrediction.cs en üstüne ekleyin:

    using Microsoft.ML.Data;
    

    Mevcut sınıf tanımını kaldırın ve sınıfı için ImageNetPrediction aşağıdaki kodu ImageNetPrediction.cs dosyasına ekleyin:

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

    ImageNetPrediction tahmin veri sınıfıdır ve aşağıdaki float[] alana sahiptir:

    • PredictedLabels bir görüntüde algılanan sınırlayıcı kutuların her biri için boyutları, nesnelik puanını ve sınıf olasılıklarını içerir.

Değişkenleri başlatma

MLContext sınıfı tüm ML.NET işlemleri için bir başlangıç noktasıdır ve başlatma mlContext işlemi, model oluşturma iş akışı nesneleri arasında paylaşılabilen yeni bir ML.NET ortamı oluşturur. Entity Framework'tekine DBContext benzer, kavramsal olarak.

mlContext Alanının altına aşağıdaki satırı ekleyerek değişkenini MLContext yeni bir örneğiyle outputFolder başlatın.

MLContext mlContext = new MLContext();

İşlem sonrası model çıkışları için ayrıştırıcı oluşturma

Model, her kılavuz hücresinin 13 x 13olduğu bir görüntüyü bir 32px x 32px kılavuzda segmentlere ayırır. Her kılavuz hücresi 5 olası nesne sınırlayıcı kutu içerir. Sınırlayıcı kutuda 25 öğe vardır:

Soldaki Izgara örneği ve sağda Sınırlayıcı Kutu örneği

  • x sınırlayıcı kutu merkezinin ilişkili olduğu kılavuz hücresine göre x konumu.
  • y sınırlayıcı kutu merkezinin, ilişkili olduğu ızgara hücresine göre y koordinatı.
  • w sınırlayıcı kutunun genişliği.
  • h sınırlayıcı kutunun yüksekliği.
  • o nesnenin sınırlayıcı kutu içinde var olduğu güvenilirlik değeri( nesnelik puanı olarak da bilinir).
  • p1-p20 model tarafından tahmin edilen 20 sınıfın her biri için sınıf olasılıkları.

Toplamda, 5 sınırlayıcı kutunun her birini açıklayan 25 öğe, her kılavuz hücresinde bulunan 125 öğeyi oluşturur.

Önceden eğitilmiş ONNX modelinin ürettiği çıkış, boyutları 125 x 13 x 13 olan bir tensörün öğelerini temsil eden, uzunluğu 21125 olan bir float dizisidir. Model tarafından oluşturulan tahminleri bir tensora dönüştürmek için bazı işlem sonrası işler gereklidir. Bunu yapmak için çıkışı ayrıştırmaya yardımcı olacak bir sınıf kümesi oluşturun.

Ayrıştırıcı sınıfları kümesini düzenlemek için projenize yeni bir dizin ekleyin.

  1. Çözüm Gezgini'nde projeye sağ tıklayın veardından Yeni Klasör Ekle'yi> seçin. Yeni klasör Çözüm Gezgini'nde göründüğünde, bunu "YoloParser" olarak adlandırın.

Sınırlayıcı kutular ve boyutlar oluşturma

Modelin veri çıkışı, görüntüdeki sınırlayıcı nesne kutularının koordinatlarını ve boyutlarını içerir. Boyutlar için bir temel sınıf oluşturun.

  1. Çözüm Gezgini'ndeYoloParser dizinine sağ tıklayın veardından Yeni Öğe Ekle'yi> seçin.

  2. Yeni Öğe Ekle iletişim kutusunda Sınıf'ı seçin ve Ad alanını DimensionsBase.cs olarak değiştirin. Ardından Ekle'yi seçin.

    DimensionsBase.cs dosyası kod düzenleyicisinde açılır. Tüm using yönergeleri ve mevcut sınıf tanımını kaldırın.

    sınıfı için DimensionsBase aşağıdaki kodu DimensionsBase.cs dosyasına ekleyin:

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

    DimensionsBase aşağıdaki float özelliklere sahiptir:

    • X nesnenin x ekseni boyunca konumunu içerir.
    • Y nesnenin y ekseni boyunca konumunu içerir.
    • Height nesnesinin yüksekliğini içerir.
    • Width nesnenin genişliğini içerir.

Ardından bounding box'larınız için bir sınıf oluşturun.

  1. Çözüm Gezgini'ndeYoloParser dizinine sağ tıklayın veardından Yeni Öğe Ekle'yi> seçin.

  2. Yeni Öğe Ekle iletişim kutusunda Sınıf'ı seçin ve Ad alanını YoloBoundingBox.cs olarak değiştirin. Ardından Ekle'yi seçin.

    YoloBoundingBox.cs dosyası kod düzenleyicisinde açılır. aşağıdaki using yönergeyi YoloBoundingBox.cs en üstüne ekleyin:

    using System.Drawing;
    

    Mevcut sınıf tanımının hemen üstüne, BoundingBoxDimensions adıyla, DimensionsBase sınıfından devralan, ilgili sınırlayıcı kutunun boyutlarını içerecek yeni bir sınıf tanımı ekleyin.

    public class BoundingBoxDimensions : DimensionsBase { }
    

    Mevcut YoloBoundingBox sınıf tanımını kaldırın ve sınıf için YoloBoundingBox aşağıdaki kodu YoloBoundingBox.cs dosyasına ekleyin:

    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 aşağıdaki özelliklere sahiptir:

    • Dimensions sınırlayıcı kutunun boyutlarını içerir.
    • Label sınırlayıcı kutu içinde algılanan nesnenin sınıfını içerir.
    • Confidence sınıfının güvenilirliğini içerir.
    • Rect sınırlayıcı kutunun boyutlarının dikdörtgen gösterimini içerir.
    • BoxColor , görüntü üzerinde çizim yapmak için kullanılan ilgili sınıfla ilişkili rengi içerir.

Ayrıştırıcıyı oluşturma

Boyutlar ve sınırlayıcı kutuları için sınıflar oluşturulduğuna göre, ayrıştırıcıyı oluşturmanın zamanı geldi.

  1. Çözüm Gezgini'ndeYoloParser dizinine sağ tıklayın veardından Yeni Öğe Ekle'yi> seçin.

  2. Yeni Öğe Ekle iletişim kutusunda Sınıf'ı seçin ve Ad alanını YoloOutputParser.cs olarak değiştirin. Ardından Ekle'yi seçin.

    YoloOutputParser.cs dosyası kod düzenleyicisinde açılır. aşağıdaki using yönergeleri YoloOutputParser.cs en üstüne ekleyin:

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

    Mevcut YoloOutputParser sınıf tanımının içine, görüntüdeki her bir hücrenin boyutlarını içeren iç içe geçmiş bir sınıf ekleyin. CellDimensions sınıfından devralan DimensionsBase sınıfı için YoloOutputParser sınıfı tanımının başına aşağıdaki kodu ekleyin.

    class CellDimensions : DimensionsBase { }
    
  3. Sınıf tanımının YoloOutputParser içine aşağıdaki sabitleri ve alanı ekleyin.

    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 , görüntünün bölündüğü kılavuzdaki satır sayısıdır.
    • COL_COUNT , görüntünün bölündüğü kılavuzdaki sütunların sayısıdır.
    • CHANNEL_COUNT , kılavuzun bir hücresinde yer alan toplam değer sayısıdır.
    • BOXES_PER_CELL hücredeki sınırlayıcı kutuların sayısıdır,
    • BOX_INFO_FEATURE_COUNT bir kutunun içindeki özelliklerin sayısıdır (x,y,yükseklik,genişlik,güvenilirlik).
    • CLASS_COUNT her bir sınırlayıcı kutuda yer alan sınıf tahminlerinin sayısıdır.
    • CELL_WIDTH , görüntü kılavuzundaki bir hücrenin genişliğidir.
    • CELL_HEIGHT , görüntü kılavuzundaki bir hücrenin yüksekliğidir.
    • channelStride kılavuzdaki geçerli hücrenin başlangıç konumudur.

    Model, puanlama olarak da bilinen bir tahmin yaptığında, giriş görüntüsünü boyutunda 416px x 416pxbir hücre kılavuzuna böler13 x 13. Her hücre içerir 32px x 32px. Her hücre içinde, her biri 5 özellik (x, y, genişlik, yükseklik, güven) içeren 5 sınırlayıcı kutu bulunmaktadır. Buna ek olarak, her sınırlayıcı kutu sınıfların her birinin olasılığını içerir ve bu durumda 20'dir. Bu nedenle, her hücre 125 bilgi parçası içerir (5 özellik + 20 sınıf olasılığı).

5 sınırlayıcı kutunun tümü için aşağıda channelStride bağlantıların listesini oluşturun.

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

Tutturucular, sınırlayıcı kutuların önceden tanımlanmış yükseklik ve genişlik oranlarıdır. Bir model tarafından algılanan nesne veya sınıfların çoğu benzer oranlara sahiptir. Sınırlayıcı kutular oluşturma söz konusu olduğunda bu değerlidir. Sınırlayıcı kutuları tahmin etmek yerine, önceden tanımlanmış boyutlardan uzaklık hesaplanır, bu nedenle sınırlayıcı kutuyu tahmin etmek için gereken hesaplama azaltılır. Bu yer işareti oranları genellikle kullanılan veri kümesine göre hesaplanır. Bu durumda, veri kümesi bilindiğinden ve değerler önceden derlendiğinden, tutturucular sabit kodlanabilir.

Ardından, modelin tahmin edeceği etiketleri veya sınıfları tanımlayın. Bu model, özgün YOLOv2 modeli tarafından tahmin edilen toplam sınıf sayısının bir alt kümesi olan 20 sınıfı tahmin eder.

Etiket listenizi altına anchorsekleyin.

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

Sınıfların her biriyle ilişkilendirilmiş renkler vardır. sınıf renklerinizi aşağıdakine atayın 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
};

Yardımcı işlevler oluşturma

İşlem sonrası aşamasında bir dizi adım vardır. Buna yardımcı olmak için çeşitli yardımcı yöntemler kullanılabilir.

Ayrıştırıcı tarafından kullanılan yardımcı yöntemler şunlardır:

  • Sigmoid 0 ile 1 arasında bir sayı veren sigmoid işlevini uygular.
  • Softmax giriş vektörlerini bir olasılık dağılımına normalleştirir.
  • GetOffset tek boyutlu model çıkışındaki öğeleri bir 125 x 13 x 13 tensordaki ilgili konuma eşler.
  • ExtractBoundingBoxes model çıktısından yöntemini kullanarak GetOffset sınırlayıcı kutu boyutlarını ayıklar.
  • GetConfidence modelin bir nesneyi algıladığından ne kadar emin olduğunu belirten güvenilirlik değerini ayıklar ve sonucu yüzdeye dönüştürmek için Sigmoid işlevini kullanır.
  • MapBoundingBoxToCell sınırlayıcı kutu boyutlarını kullanır ve bunları görüntüdeki ilgili hücreye eşler.
  • ExtractClasses yöntemini kullanarak model çıkışından sınırlayıcı kutu için sınıf tahminlerini ayıklar ve bunları Softmax yöntemini kullanarak bir olasılık dağılımına dönüştürür.
  • GetTopResult en yüksek olasılıkla tahmin edilen sınıflar listesinden sınıfı seçer.
  • IntersectionOverUnion daha düşük olasılıklarla örtüşen sınırlayıcı kutuları filtreler.

Listenizin altına tüm yardımcı yöntemlerin classColorskodunu ekleyin.

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

Yardımcı yöntemlerin tümünü tanımladıktan sonra, bunları kullanarak model çıkışını işlemenin zamanı geldi.

yönteminin IntersectionOverUnion altında, model tarafından oluşturulan çıkışı işlemek için yöntemini oluşturun ParseOutputs .

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

}

Sınırlayıcı kutularınızı depolamak ve yönteminin ParseOutputs içinde değişkenler tanımlamak için bir liste oluşturun.

var boxes = new List<YoloBoundingBox>();

Her resim bir hücre kılavuzuna 13 x 13 ayrılır. Her hücre beş sınırlayıcı kutu içerir. değişkeninin boxes altına, her hücredeki tüm kutuları işlemek için kod ekleyin.

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

        }
    }
}

En iç döngünün içinde, geçerli kutunun tek boyutlu model çıkışındaki başlangıç konumunu hesaplayın.

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

Bunun hemen altında, geçerli sınırlayıcı kutunun boyutlarını almak için yöntemini kullanın ExtractBoundingBoxDimensions .

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

Ardından geçerli sınırlayıcı kutunun güvenilirliğini elde etmek için yöntemini kullanın GetConfidence .

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

Bundan sonra, geçerli sınırlayıcı kutuyu işlenen geçerli hücreyle eşlemek için yöntemini kullanın MapBoundingBoxToCell .

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

Daha fazla işlem yapmadan önce güvenilirlik değerinizin sağlanan eşikten büyük olup olmadığını denetleyin. Aksi takdirde, sonraki sınırlayıcı kutuyu işleyin.

if (confidence < threshold)
    continue;

Aksi takdirde, çıkışı işlemeye devam edin. Sonraki adım, yöntemini kullanarak geçerli sınırlayıcı kutu için tahmin edilen sınıfların olasılık dağılımını almaktır ExtractClasses .

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

Ardından geçerli kutu için en yüksek olasılıkla sınıfın değerini ve dizinini almak ve puanını hesaplamak için yöntemini kullanın GetTopResult .

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

için değerini topScore bir kez daha yalnızca belirtilen eşiğin üzerinde olan sınırlayıcı kutuları koruyun.

if (topScore < threshold)
    continue;

Son olarak, geçerli sınırlayıcı kutu eşiği aşarsa, yeni bir BoundingBox nesne oluşturun ve onu boxes listesine ekleyin.

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

Görüntüdeki tüm hücreler işlendikten sonra boxes listesini döndürün. yöntemindeki en dış for-loop öğesinin altına aşağıdaki return deyimini ParseOutputs ekleyin.

return boxes;

Örtüşen kutuları filtrele

Artık model çıkışından son derece güvenli sınırlayıcı kutuların tümü ayıklandığına göre, çakışan görüntüleri kaldırmak için ek filtreleme yapılması gerekir. yönteminin FilterBoundingBoxes altına adlı ParseOutputs bir yöntem ekleyin:

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

}

yönteminin FilterBoundingBoxes içinde, algılanan kutuların boyutuna eşit bir dizi oluşturarak ve tüm yuvaları etkin veya işlemeye hazır olarak işaretleyerek başlayın.

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

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

Ardından sınırlayıcı kutularınızı içeren listeyi güvene göre azalan düzende sıralayın.

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

Bundan sonra, filtrelenen sonuçları tutmak için bir liste oluşturun.

var results = new List<YoloBoundingBox>();

Sınırlayıcı kutuların her biri üzerinde yineleme yaparak her sınırlayıcı kutuyu işlemeye başlayın.

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

}

Bu döngü (for-loop) içinde, mevcut sınırlayıcı kutunun işlenip işlenemeyeceğini kontrol edin.

if (isActiveBoxes[i])
{

}

Öyleyse, bounding box'u sonuç listesine ekleyin. Sonuçlar ayıklanması gereken kutu sayısının belirtilen sınırını aşarsa döngüden çıkın. Aşağıdaki kodu if-deyiminin içine ekleyin.

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

if (results.Count >= limit)
    break;

Aksi takdirde, bitişik sınırlayıcı kutularına bakın. Kutu sınırı denetiminin altına aşağıdaki kodu ekleyin.

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

}

İlk kutu gibiyse, bitişik kutu etkinse veya işlenmeye hazırsa, ilk kutunun ve ikinci kutunun belirtilen eşiği aşıp aşmadığını denetlemek için IntersectionOverUnion yöntemini kullanın. Aşağıdaki kodu en iç döngünüze ekleyin.

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

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

        if (activeCount <= 0)
            break;
    }
}

En içteki bitişik sınırlayıcı kutuları denetleyen for-loop'un dışında, işlenmeyi bekleyen başka sınırlayıcı kutular olup olmadığını kontrol edin. Aksi takdirde, dış for-loop'un dışına çıkın.

if (activeCount <= 0)
    break;

Son olarak, FilterBoundingBoxes yönteminin başlangıçtaki for döngüsünün dışında sonuçları döndürün:

return results;

Harika! Şimdi puanlama için modelle birlikte bu kodu kullanma zamanı geldi.

Puanlama için modeli kullanma

İşlem sonrası işlemlerde olduğu gibi puanlama adımlarında da birkaç adım vardır. Bu konuda yardımcı olmak için projenize puanlama mantığını içerecek bir sınıf ekleyin.

  1. Çözüm Gezgini'nde projeye sağ tıklayın veardından Yeni Öğe Ekle'yi> seçin.

  2. Yeni Öğe Ekle iletişim kutusunda Sınıf'ı seçin ve Ad alanını OnnxModelScorer.cs olarak değiştirin. Ardından Ekle'yi seçin.

    OnnxModelScorer.cs dosyası kod düzenleyicisinde açılır. aşağıdaki using yönergeleri OnnxModelScorer.cs en üstüne ekleyin:

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

    Sınıf tanımının OnnxModelScorer içine aşağıdaki değişkenleri ekleyin.

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

    Bunun hemen altında, önceden tanımlanmış değişkenleri başlatacak sınıf için OnnxModelScorer bir oluşturucu oluşturun.

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

    Oluşturucuyu oluşturduktan sonra, görüntü ve model ayarlarıyla ilgili değişkenleri içeren birkaç yapı tanımlayın. Modelin girişi olarak beklenen yükseklik ve genişliği içerecek şekilde adlı ImageNetSettings bir yapı oluşturun.

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

    Bundan sonra, modelin giriş ve çıkış katmanlarının adlarını içeren adlı TinyYoloModelSettings başka bir yapı oluşturun. Modelin giriş ve çıkış katmanlarının adını görselleştirmek için Netron gibi bir araç kullanabilirsiniz.

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

    Ardından, puanlama için kullanılan ilk yöntem kümesini oluşturun. LoadModel sınıfınızın OnnxModelScorer içinde yöntemini oluşturun.

    private ITransformer LoadModel(string modelLocation)
    {
    
    }
    

    LoadModel yöntemine aşağıdaki kodu loglama için ekleyin.

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

    ML.NET işlem hatlarının, yöntem çağrıldığında üzerinde çalışacak veri şemasını Fit bilmesi gerekir. Bu durumda eğitime benzer bir süreç kullanılır. Ancak, gerçek bir eğitim olmadığından, boş IDataViewbir kullanmak kabul edilebilir. boş bir listeden işlem hattı için yeni IDataView bir oluşturun.

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

    Bunun altında işlem hattını tanımlayın. İşlem hattı dört dönüşümden oluşur.

    • LoadImages görüntüyü Bit Eşlem olarak yükler.
    • ResizeImages görüntüyü belirtilen boyuta (bu örnekte) 416 x 416yeniden ölçekler.
    • ExtractPixels görüntünün piksel gösterimini Bit Eşlem'den sayısal vektöre değiştirir.
    • ApplyOnnxModel ONNX modelini yükler ve sağlanan verilere puan vermek için bu modeli kullanır.

    değişkeninin altındaki LoadModel yönteminde data işlem hattınızı tanımlayın.

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

    Şimdi puanlama için modelin örneğini oluşturma zamanı geldi. Fit İşlem hattında yöntemini çağırın ve daha fazla işlem için döndürebilirsiniz.

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

Model yüklendikten sonra tahminlerde bulunmak için kullanılabilir. Bu işlemi kolaylaştırmak için yönteminin PredictDataUsingModel altında adlı LoadModel bir yöntem oluşturun.

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

}

içinde PredictDataUsingModelgünlüğe kaydetmek için aşağıdaki kodu ekleyin.

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

Ardından, verileri puan etmek için yöntemini kullanın Transform .

IDataView scoredData = model.Transform(testData);

Tahmin edilen olasılıkları ayıklayın ve ek işlem için bunları döndürin.

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

return probabilities;

Her iki adım da ayarlandıklarına göre, bunları tek bir yöntemde birleştirin. yönteminin PredictDataUsingModel altına adlı Scoreyeni bir yöntem ekleyin.

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

    return PredictDataUsingModel(data, model);
}

Neredeyse oradayız! Şimdi hepsini kullanma zamanı.

Nesneleri algılama

Tüm kurulum tamamlandıktan sonra bazı nesneleri algılamanın zamanı geldi.

Model çıkışlarını puanlayıp ayrıştırma

değişkeninin oluşturulmasının mlContext altına bir try-catch deyimi ekleyin.

try
{

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

Bloğun try içinde nesne algılama mantığını uygulamaya başlayın. İlk olarak, verileri içine IDataViewyükleyin.

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

Ardından bir örneği OnnxModelScorer oluşturun ve bunu kullanarak yüklenen verileri puanlayın.

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

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

Şimdi işlem sonrası adımının zamanı geldi. örneğini YoloOutputParser oluşturun ve model çıkışını işlemek için kullanın.

YoloOutputParser parser = new YoloOutputParser();

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

Model çıktısı işlendikten sonra, resimlere sınırlayıcı kutuları çizmenin zamanı geldi.

Tahminleri görselleştirme

Model, görüntüleri puanlayıp çıkışları işledikten sonra, sınırlayıcı kutular görüntüye çizilmelidir. Bunu yapmak için Program.cs içinde GetAbsolutePath yönteminin altına adı DrawBoundingBox olan bir yöntem ekleyin.

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

}

İlk olarak, görüntüyü yükleyin ve yöntemindeki yükseklik ve genişlik boyutlarını DrawBoundingBox alın.

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

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

Ardından, model tarafından algılanan sınırlayıcı kutuların her biri üzerinde yineleme yapmak için her biri için bir döngü oluşturun.

foreach (var box in filteredBoundingBoxes)
{

}

For-each döngüsünün içinde sınırlayıcı kutunun boyutlarını elde edin.

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

Sınırlayıcı kutunun boyutları model girişine 416 x 416karşılık geldiği için sınırlayıcı kutu boyutlarını resmin gerçek boyutuyla eşleşecek şekilde ölçeklendirin.

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;

Ardından, her sınırlayıcı kutunun üzerinde görünecek metin için bir şablon tanımlayın. Metin, ilgili sınırlayıcı kutunun içindeki nesnenin sınıfını ve güvenilirliğini içerir.

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

Resmin üzerine çizim yapmak için resmi bir Graphics nesneye dönüştürün.

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

}

Kod bloğunun using içinde grafiğin Graphics nesne ayarlarını yapın.

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

Bunun altında, metin ve sınırlayıcı kutu için yazı tipi ve renk seçeneklerini ayarlayın.

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

FillRectangle yöntemini kullanarak metni içerecek şekilde sınırlayıcı kutunun üzerine bir dikdörtgen oluşturun ve doldurun. Bu, metnin karşıtlığını artırmaya ve okunabilirliği artırmaya yardımcı olur.

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

Ardından, DrawString ve DrawRectangle yöntemlerini kullanarak resimdeki metni ve sınırlayıcı kutuyu çizin.

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

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

For-each döngüsünün dışında, görüntüleri kaydetmek için outputFolder içerisine kod ekleyin.

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

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

Ek geri bildirim sağlamak ve uygulamanın çalışma zamanında beklenildiği gibi tahminlerde bulunduğunu teyit etmek için, DrawBoundingBox yönteminin altına LogDetectedObjects adlı bir yöntem ekleyerek algılanan nesneleri konsola çıkartmak üzere Program.cs dosyasına bir yöntem ekleyin.

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

Artık tahminlerden görsel geri bildirim oluşturmak için yardımcı yöntemleriniz olduğuna göre, puanlanan görüntülerin her birini yinelemek için bir for-loop ekleyin.

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

}

for-döngüsü içinde, görüntü dosyasının adını ve onunla ilişkili sınırlayıcı kutuları bulun.

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

Bunun altında, görüntüdeki sınırlayıcı kutuları çizmek için yöntemini kullanın DrawBoundingBox .

DrawBoundingBox(imagesFolder, outputFolder, imageFileName, detectedObjects);

Son olarak, konsola LogDetectedObjects tahminleri çıkarmak için yöntemini kullanın.

LogDetectedObjects(imageFileName, detectedObjects);

try-catch deyiminden sonra, işlemin çalışmasının tamam olduğunu belirtmek için ek mantık ekleyin.

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

Hepsi bu kadar!

Results

Önceki adımları izledikten sonra konsol uygulamanızı (Ctrl + F5) çalıştırın. Sonuçlarınız aşağıdaki çıkışa benzer olmalıdır. Uyarılar veya işleme iletileri görebilirsiniz, ancak bu iletiler netlik için aşağıdaki sonuçlardan kaldırılmıştır.

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

Sınırlayıcı kutular içeren görüntüleri görmek için assets/images/output/ dizinine gidin. aşağıda, işlenen görüntülerden birinden bir örnek verilmiştir.

Yemek odasının işlenmiş örnek görüntüsü

Tebrikler! Artık ML.NET'da önceden eğitilmiş ONNX bir modeli yeniden kullanarak nesne algılama için bir makine öğrenmesi modelini başarıyla oluşturdunuz.

Bu öğreticinin kaynak kodunu dotnet/machinelearning-samples deposunda bulabilirsiniz.

Bu öğreticide şunların nasıl yapılacağını öğrendiniz:

  • Sorunu anlama
  • ONNX'in ne olduğunu ve ML.NET ile nasıl çalıştığını öğrenin
  • Modeli anlama
  • Önceden eğitilmiş modeli yeniden kullanma
  • Yüklü bir modelle nesneleri algılama

Genişletilmiş nesne algılama örneğini keşfetmek için Machine Learning örnekleri GitHub deposuna göz atın.