Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
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
- Visual Studio 2022.
- Télécharger le jeu de données de 50 images de panneaux d'arrêt.
- Compte Azure. Si vous n’en avez pas, créer un compte Azure gratuit.
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
- 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
- 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.
- 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.
- 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.
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.
nom de colonne de sortie : nom de colonne de sortie requis lors de l’application du modèle ONNX dans ML.NET.
taille d’image : taille requise lors du redimensionnement des images dans le pipeline ML.NET.
Créer un projet de console C#
Dans Visual Studio, créez une application console C# appelée « StopSignDetection ». Choisissez .NET 8 comme framework cible.
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
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; }
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’attributImageType
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.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’attributColumnName
indique ML.NET que cette colonne du modèle est le nom donné, qui estdetected_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’attributColumnName
indique ML.NET que cette colonne du modèle est le nom donné, qui estdetected_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’attributColumnName
indique ML.NET que cette colonne du modèle est le nom donné, qui estdetected_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
Créez l’objet
MLContext
.var context = new MLContext();
Créez un
IDataView
avec une nouvelle listeStopSignInput
vide.var data = context.Data.LoadFromEnumerable(new List<StopSignInput>());
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 :
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))
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ètreoutputColumnName
..Append(context.Transforms.ExtractPixels(outputColumnName: "image_tensor"))
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.
Créez une boucle
foreach
pour parcourir les images de test.Bitmap testImage; foreach (var image in testFiles) { }
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";
Toujours dans la boucle
foreach
, créez unFileStream
de l'image et convertissez-le enBitmap
.using (var stream = new FileStream(image, FileMode.Open)) { testImage = (Bitmap)Image.FromStream(stream); }
Dans la boucle
foreach
également, appelez la méthodePredict
sur le moteur de prédiction.var prediction = predictionEngine.Predict(new StopSignInput { Image = testImage });
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éthodeChunk
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());
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;
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++) { }
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éthodeElementAt
.var boundingBox = boundingBoxes.ElementAt(i);
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;
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
ettop
du calcul précédent. Utilisez la méthodeMath.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);
Ensuite, obtenez l’étiquette prédite à partir du tableau d’étiquettes.
var label = labels[prediction.PredictedLabels[i]];
Créez un graphique basé sur l’image de test à l’aide de la méthode
Graphics.FromImage
.using var graphics = Graphics.FromImage(testImage);
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 objetPen
pour déterminer la couleur et la largeur du rectangle, puis passez les variablesx
,y
,width
etheight
.graphics.DrawRectangle(new Pen(Color.NavajoWhite, 8), x, y, width, height);
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 objetFont
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);
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 :