Partager via


Tutoriel : Catégoriser une image dans ML.NET à partir du modèle ONNX Custom Vision

Découvrez comment utiliser ML.NET pour détecter des objets dans des images à l’aide d’un modèle ONNX entraîné dans le service Microsoft Custom Vision.

Le service Microsoft Custom Vision est un service IA qui entraîne un modèle en fonction des images que vous chargez. Vous pouvez ensuite exporter le modèle au format ONNX et l’utiliser dans ML.NET pour effectuer des prédictions.

Dans ce tutoriel, vous allez apprendre à :

  • Utiliser le service Custom Vision pour créer un modèle ONNX
  • Incorporer le modèle ONNX dans le pipeline ML.NET
  • Entraîner le modèle ML.NET
  • Détecter les signes d’arrêt dans les images de test

Conditions préalables

Créer le modèle

Créer le projet Custom Vision

Connectez-vous au service Microsoft Custom Vision , puis sélectionnez nouveau projet.

Dans la boîte de dialogue Nouveau projet , renseignez les éléments requis suivants :

  • Définissez le nom du projet Custom Vision comme StopSignDetection.
  • Sélectionnez la ressource que vous utiliserez. Il s’agit d’une ressource Azure qui sera créée pour le projet Custom Vision. Si aucun n’est répertorié, vous pouvez en créer un en sélectionnant l'Créer un lien.
  • Définissez le type de projet comme Détection d'objets.
  • Définissez les types de classification comme Multiclass puisqu'il y aura une classe par image.
  • Définissez le domaine comme Général (compact) [S1]. Le domaine compact vous permet de télécharger le modèle ONNX.
  • Pour les capacités d'exportation, sélectionnez Plateformes de base pour permettre l'exportation du modèle ONNX.

Une fois les champs ci-dessus renseignés, sélectionnez Créer un projet.

Ajouter des images

  1. Une fois le projet créé, choisissez Ajouter des images pour commencer à ajouter des images sur lesquelles le modèle s'entraînera. Sélectionnez les images de signe d’arrêt que vous avez téléchargées.
  2. Sélectionnez la première image affichée. Vous pouvez sélectionner des objets dans l’image que vous souhaitez que le modèle détecte. Sélectionnez le panneau stop sur l'image. Une fenêtre contextuelle s'affiche et définit la balise comme panneau d'arrêt.
  3. Répétez pour toutes les images restantes. Certaines images ont plusieurs signes d’arrêt. Veillez donc à marquer tout ce qui se trouve dans les images.

Entraîner le modèle

Avec les images chargées et étiquetées, le modèle peut maintenant être entraîné. Sélectionnez Entraîner.

Une fenêtre contextuelle affiche le type d’entraînement à utiliser. Choisissez Formation rapide, puis sélectionnez Former.

Télécharger le modèle ONNX

Une fois la formation terminée, cliquez sur le bouton Export. Lorsque la fenêtre contextuelle s’affiche, sélectionnez ONNX pour télécharger le modèle ONNX.

Inspecter le modèle ONNX

Décompressez le fichier ONNX téléchargé. Le dossier contient plusieurs fichiers, mais les deux que vous allez utiliser dans ce tutoriel sont les suivants :

  • labels.txt, qui est un fichier texte contenant les étiquettes définies dans le service Custom Vision.
  • model.onnx, qui est le modèle ONNX que vous utiliserez pour effectuer des prédictions dans ML.NET.

Pour générer le pipeline ML.NET, vous aurez besoin des noms des colonnes d’entrée et de sortie. Pour obtenir ces informations, utilisez Netron, un web et application de bureau qui peut analyser les modèles ONNX et afficher leur architecture.

  1. Lorsque vous utilisez l’application web ou de bureau de Netron, ouvrez le modèle ONNX dans l’application. Une fois qu’il s’ouvre, il affiche un graphique. Ce graphique vous indique quelques éléments dont vous aurez besoin pour construire le pipeline ML.NET pour les prédictions.

    • nom de colonne d’entrée : nom de colonne d’entrée requis lors de l’application du modèle ONNX dans ML.NET.

      colonne d’entrée Netron

    • nom de colonne de sortie : nom de colonne de sortie requis lors de l’application du modèle ONNX dans ML.NET.

      colonne de sortie Netron

    • taille d’image : taille requise lors du redimensionnement des images dans le pipeline ML.NET.

      Taille de l’image Netron

Créer un projet de console C#

  1. Dans Visual Studio, créez une application console C# appelée « StopSignDetection ». Choisissez .NET 8 comme framework cible.

  2. Installez les packages NuGet suivants pour le projet :

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

    Remarque

    Cet exemple utilise la dernière version stable des packages NuGet mentionnés, sauf indication contraire.

Référencer le modèle ONNX

Recherchez les deux fichiers à partir du modèle ONNX (labels.txt et model.onnx) dans l’Explorateur de solutions visual Studio . Cliquez avec le bouton droit de la souris sur ces images et, dans la fenêtre Propriétés, définissez Copier dans le répertoire de sortie sur Copier si plus récent.

Créer des classes d’entrée et de prédiction

  1. Ajoutez une nouvelle classe à votre projet et nommez-la StopSignInput. Ensuite, ajoutez le struct suivant à la classe :

    public struct ImageSettings
    {
        public const int imageHeight = 320;
        public const int imageWidth = 320;
    }
    
  2. Ensuite, ajoutez la propriété suivante à la classe.

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

    La propriété Image contient la bitmap de l’image utilisée pour la prédiction. L’attribut ImageType indique ML.NET que la propriété est une image avec des dimensions de 320 et 320, qui a été déterminée à l’aide de Netron.

  3. Ajoutez une autre classe à votre projet et nommez-la StopSignPrediction. Ensuite, ajoutez les propriétés suivantes à la classe.

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

    La propriété PredictedLabels contient les prédictions d’étiquettes pour chaque objet détecté. Le type est un tableau float. Chaque élément du tableau est donc la prédiction de chaque étiquette. L’attribut ColumnName indique ML.NET que cette colonne du modèle est le nom donné, qui est detected_classes.

    La propriété BoundingBoxes contient les boîtes de délimitation de chaque objet détecté. Le type est un tableau de flottants et chaque objet détecté contient quatre éléments dans le tableau pour la boîte d'encombrement. L’attribut ColumnName indique ML.NET que cette colonne du modèle est le nom donné, qui est detected_boxes.

    La propriété Scores contient les scores de confiance de chaque objet prédit et de son étiquette. Le type est un tableau float. Chaque élément du tableau est donc le score de confiance de chaque étiquette. L’attribut ColumnName indique ML.NET que cette colonne du modèle est le nom donné, qui est detected_scores.

Utiliser le modèle pour effectuer des prédictions

Ajouter des directives d’utilisation

Dans le fichier Program.cs, ajoutez les directives using suivantes en haut du fichier.

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

Créer des objets

  1. Créez l’objet MLContext.

    var context = new MLContext();
    
  2. Créez un IDataView avec une nouvelle liste StopSignInput vide.

    var data = context.Data.LoadFromEnumerable(new List<StopSignInput>());
    
  3. Pour des raisons de cohérence, enregistrez les images prédites dans le chemin d'assemblage.

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

Construire le pipeline

Avec le IDataView vide créé, le pipeline peut être construit pour effectuer les prédictions de toutes les nouvelles images. Le pipeline se compose de plusieurs étapes :

  1. Redimensionnez les images entrantes.

    L’image envoyée au modèle pour la prédiction sera souvent dans un rapport d’aspect différent que les images qui ont servi à entraîner le modèle. Pour maintenir la cohérence de l’image pour des prédictions précises, redimensionnez l’image sur 320x320. Pour ce faire, utilisez la méthode ResizeImages et définissez l'imageColumnName comme nom de la propriété StopSignInput.Image.

    var pipeline = context.Transforms.ResizeImages(resizing: ImageResizingEstimator.ResizingKind.Fill, outputColumnName: "image_tensor", imageWidth: ImageSettings.imageWidth, imageHeight: ImageSettings.imageHeight, inputColumnName: nameof(StopSignInput.Image))
    
  2. Extrayez les pixels de l’image.

    Une fois l’image redimensionnée, vous devez extraire les pixels de l’image. Ajoutez la méthode ExtractPixels à votre pipeline et spécifiez le nom de la colonne pour générer les pixels à l’aide du paramètre outputColumnName.

    .Append(context.Transforms.ExtractPixels(outputColumnName: "image_tensor"))
    
  3. Appliquez le modèle ONNX à l’image pour effectuer une prédiction. Cela prend quelques paramètres :

    • modelFile - Chemin d’accès au fichier de modèle ONNX
    • outputColumnNames : tableau de chaînes contenant les noms de tous les noms de colonnes de sortie, qui peuvent être trouvés lors de l’analyse du modèle ONNX dans Netron.
    • inputColumnNames : tableau de chaînes contenant les noms de tous les noms de la colonne d’entrée, qui peuvent également être trouvés lors de l’analyse du modèle ONNX dans Netron.
    .Append(context.Transforms.ApplyOnnxModel(outputColumnNames: new string[] { "detected_boxes", "detected_scores", "detected_classes" }, inputColumnNames: new string[] { "image_tensor" }, modelFile: "./Model/model.onnx"));
    

Ajuster le modèle

Maintenant que vous avez défini un pipeline, vous pouvez l’utiliser pour générer le modèle ML.NET. Utilisez la méthode Fit sur le pipeline et transmettez le IDataView vide.

var model = pipeline.Fit(data);

Ensuite, pour effectuer des prédictions, utilisez le modèle pour créer un moteur de prédiction. Il s’agit d’une méthode générique. Il prend donc en charge les classes StopSignInput et StopSignPrediction créées précédemment.

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

Extraire les étiquettes

Pour mapper les sorties du modèle à ses étiquettes, vous devez extraire les étiquettes fournies par Custom Vision. Ces étiquettes se trouvent dans le fichier labels.txt qui a été inclus dans le fichier zip avec le modèle ONNX.

Appelez la méthode ReadAllLines pour lire toutes les étiquettes du fichier.

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

Prédire sur une image de test

Vous pouvez maintenant utiliser le modèle pour prédire sur de nouvelles images. Dans le projet, il existe un dossier de test que vous pouvez utiliser pour effectuer des prédictions. Ce dossier contient deux images aléatoires avec un panneau stop dedans provenant de Unsplash. Une image a un signe d’arrêt tandis que l’autre a deux signes d’arrêt. Utilisez la méthode GetFiles pour lire les chemins d’accès des fichiers des images dans le répertoire.

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

Parcourez les chemins d’accès au fichier pour effectuer une prédiction avec le modèle et générer le résultat.

  1. Créez une boucle foreach pour parcourir les images de test.

    Bitmap testImage;
    
    foreach (var image in testFiles)
    {
    
    }
    
  2. Dans la boucle foreach, générez le nom de l’image prédite en fonction du nom de l’image de test d’origine.

    var predictedImage = $"{Path.GetFileName(image)}-predicted.jpg";
    
  3. Toujours dans la boucle foreach, créez un FileStream de l'image et convertissez-le en Bitmap.

    using (var stream = new FileStream(image, FileMode.Open))
    {
        testImage = (Bitmap)Image.FromStream(stream);
    }
    
  4. Dans la boucle foreach également, appelez la méthode Predict sur le moteur de prédiction.

    var prediction = predictionEngine.Predict(new StopSignInput { Image = testImage });
    
  5. Avec la prédiction, vous pouvez obtenir les boîtes de délimitation. Utilisez la méthode Chunk pour déterminer le nombre d’objets détectés par le modèle. Pour ce faire, prenez le nombre de boîtes de délimitation prédites et divisez-le par le nombre d'étiquettes prédites. Par exemple, si vous aviez trois objets détectés dans une image, il y aurait 12 éléments dans le tableau BoundingBoxes et trois étiquettes prédites. La méthode Chunk vous permet d'obtenir trois tableaux de quatre pour représenter les boîtes de délimitation de chaque objet.

    var boundingBoxes = prediction.BoundingBoxes.Chunk(prediction.BoundingBoxes.Count() / prediction.PredictedLabels.Count());
    
  6. Ensuite, capturez la largeur et la hauteur d’origine des images utilisées pour la prédiction.

    var originalWidth = testImage.Width;
    var originalHeight = testImage.Height;
    
  7. Calculez l'endroit de l'image où vous devez dessiner les boîtes. Pour cela, créez une boucle for basée sur le nombre de segments de boîtes englobantes.

    for (int i = 0; i < boundingBoxes.Count(); i++)
    {
    }
    
  8. Dans la boucle for, calculez la position des coordonnées x et y, ainsi que la largeur et la hauteur de la zone à dessiner sur l’image. La première chose à faire est d'obtenir l'ensemble des boîtes de délimitation à l'aide de la méthode ElementAt.

    var boundingBox = boundingBoxes.ElementAt(i);
    
  9. Avec la boîte englobante actuelle, vous pouvez maintenant calculer où dessiner la boîte. Utilisez la largeur de l'image originale pour les premier et troisième éléments de la boîte englobante, et la hauteur de l'image originale pour les deuxième et quatrième éléments.

    var left = boundingBox[0] * originalWidth;
    var top = boundingBox[1] * originalHeight;
    var right = boundingBox[2] * originalWidth;
    var bottom = boundingBox[3] * originalHeight;
    
  10. Calculez la largeur et la hauteur de la zone à dessiner autour de l’objet détecté dans l’image. Les éléments x et y sont les variables left et top du calcul précédent. Utilisez la méthode Math.Abs pour obtenir la valeur absolue à partir des calculs de largeur et de hauteur au cas où elle était négative.

    var x = left;
    var y = top;
    var width = Math.Abs(right - left);
    var height = Math.Abs(top - bottom);
    
  11. Ensuite, obtenez l’étiquette prédite à partir du tableau d’étiquettes.

    var label = labels[prediction.PredictedLabels[i]];
    
  12. Créez un graphique basé sur l’image de test à l’aide de la méthode Graphics.FromImage.

    using var graphics = Graphics.FromImage(testImage);
    
  13. Dessinez sur l'image en utilisant les informations de la boîte englobante. Tout d’abord, dessinez le rectangle autour des objets détectés à l’aide de la méthode DrawRectangle qui prend un objet Pen pour déterminer la couleur et la largeur du rectangle, puis passez les variables x, y, widthet height.

    graphics.DrawRectangle(new Pen(Color.NavajoWhite, 8), x, y, width, height);
    
  14. Ensuite, affichez l'étiquette prédite à l'intérieur de la boîte avec la méthode DrawString qui prend en charge la chaîne à imprimer et un objet Font pour déterminer comment dessiner la chaîne et où la placer.

    graphics.DrawString(label, new Font(FontFamily.Families[0], 18f), Brushes.NavajoWhite, x + 5, y + 5);
    
  15. Après la boucle for, vérifiez si le fichier prédit existe déjà. Si c’est le cas, supprimez-le. Ensuite, enregistrez-le dans le chemin de sortie défini.

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

Étapes suivantes

Essayez l’un des autres didacticiels de classification d’images :