Compartir vía


Tutorial: Categorización de una imagen en ML.NET del modelo ONNX de Custom Vision

Aprenda a usar ML.NET para detectar objetos en imágenes mediante un modelo ONNX entrenado en el servicio Microsoft Custom Vision.

El servicio Microsoft Custom Vision es un servicio de IA que entrena un modelo basado en imágenes que se cargan. A continuación, puede exportar el modelo al formato ONNX y usarlo en ML.NET para realizar predicciones.

En este tutorial, aprenderá a:

  • Uso del servicio Custom Vision para crear un modelo ONNX
  • Incorporación del modelo ONNX a la canalización de ML.NET
  • Entrenamiento del modelo de ML.NET
  • Detectar señales de stop en imágenes de prueba

Prerrequisitos

Creación del modelo

Creación del proyecto de Custom Vision

Inicie sesión en el servicio Microsoft Custom Vision y seleccione Nuevo proyecto.

En el cuadro de diálogo Nuevo proyecto , rellene los siguientes elementos necesarios:

  • Establezca el nombre del proyecto de Custom Vision como StopSignDetection.
  • Seleccione el recurso que usará. Se trata de un recurso de Azure que se creará para el proyecto de Custom Vision. Si no aparece ninguno, se puede crear seleccionando el vínculo Crear nuevo .
  • Establezca el tipo de proyecto como Detección de objetos.
  • Establezca los tipos de clasificación como multiclase , ya que habrá una clase por imagen.
  • Establezca el dominio como General (compacto) [S1]. El dominio compacto le permite descargar el modelo ONNX.
  • En Funcionalidades de exportación, seleccione Plataformas básicas para permitir la exportación del modelo ONNX.

Una vez rellenados los campos anteriores, seleccione Crear proyecto.

Agregar imágenes

  1. Con el proyecto creado, elija Agregar imágenes para empezar a agregar imágenes para que el modelo se entrene. Seleccione las imágenes de señal de stop que descargó.
  2. Seleccione la primera imagen que se muestra. Puede seleccionar objetos en la imagen que desea que el modelo detecte. Seleccione la señal de alto en la imagen. Una ventana emergente se muestra y establece la etiqueta como stop-sign.
  3. Repita esta operación para todas las imágenes restantes. Algunas imágenes tienen más de una señal de stop, por lo tanto, asegúrese de marcar todas las señales que están en las imágenes.

Entrenamiento del modelo

Con las imágenes cargadas y etiquetadas, el modelo ahora se puede entrenar. Seleccione Entrenar.

Se muestra una ventana emergente preguntando qué tipo de entrenamiento usar. Elija Entrenamiento rápido y, a continuación, seleccione Entrenar.

Descarga del modelo ONNX

Una vez completado el entrenamiento, haga clic en el botón Exportar . Cuando se muestre el elemento emergente, seleccione ONNX para descargar el modelo ONNX.

Inspección del modelo ONNX

Descomprima el archivo ONNX descargado. La carpeta contiene varios archivos, pero los dos que usará en este tutorial son:

  • labels.txt, que es un archivo de texto que contiene las etiquetas definidas en el servicio Custom Vision.
  • model.onnx, que es el modelo de ONNX que usará para hacer predicciones en ML.NET.

Para compilar la canalización de ML.NET, necesitará los nombres de las columnas de entrada y salida. Para obtener esta información, use Netron, una aplicación web y de escritorio que pueda analizar modelos ONNX y mostrar su arquitectura.

  1. Al usar la aplicación web o de escritorio de Netron, abra el modelo ONNX en la aplicación. Una vez que se abre, muestra un gráfico. Este gráfico le informa sobre algunas cosas que necesitará para construir la canalización de ML.NET para las predicciones.

    • Nombre de columna de entrada: el nombre de columna de entrada necesario al aplicar el modelo ONNX en ML.NET.

      Columna de entrada de Netron

    • Nombre de columna de salida: el nombre de columna de salida necesario al aplicar el modelo ONNX en ML.NET.

      Columna de salida de Netron

    • Tamaño de imagen - El tamaño que se requiere al redimensionar las imágenes en la canalización de ML.NET.

      Tamaño de imagen de Netron

Creación de un proyecto de consola de C#

  1. En Visual Studio, cree una aplicación de consola de C# denominada "StopSignDetection". Elija .NET 8 como marco de destino.

  2. Instale los siguientes paquetes NuGet para el proyecto:

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

    Nota:

    En este ejemplo se usa la versión estable más reciente de los paquetes NuGet mencionados a menos que se indique lo contrario.

Referencia al modelo ONNX

Busque los dos archivos del modelo ONNX (labels.txt y model.onnx) en el Explorador de soluciones de Visual Studio. Haga clic con el botón derecho en ellos y, en la ventana Propiedades, establezca Copiar en el directorio de salida en Copiar si es más reciente.

Creación de clases de entrada y predicción

  1. Agregue una nueva clase al proyecto y asígnele StopSignInputel nombre . A continuación, agregue la siguiente estructura a la clase :

    public struct ImageSettings
    {
        public const int imageHeight = 320;
        public const int imageWidth = 320;
    }
    
  2. A continuación, agregue la siguiente propiedad a la clase .

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

    La Image propiedad contiene el mapa de bits de la imagen utilizada para la predicción. El ImageType atributo indica ML.NET que la propiedad es una imagen con dimensiones de 320 y 320, que se determinó mediante Netron.

  3. Agregue otra clase al proyecto y asígnele StopSignPredictionel nombre . A continuación, agregue las siguientes propiedades a la clase .

    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 PredictedLabels propiedad contiene las predicciones de etiquetas para cada objeto detectado. El tipo es un arreglo de números flotantes, por lo que cada elemento del arreglo predice cada etiqueta. El ColumnName atributo indica ML.NET que esta columna del modelo es el nombre especificado, que es detected_classes.

    La BoundingBoxes propiedad contiene los cuadros de límite para cada objeto detectado. El tipo es un arreglo de flotantes y cada objeto detectado viene con cuatro elementos en el arreglo para la caja delimitadora. El ColumnName atributo indica ML.NET que esta columna del modelo es el nombre especificado, que es detected_boxes.

    La Scores propiedad contiene las puntuaciones de confianza de cada objeto previsto y su etiqueta. El tipo es una matriz float, por lo que cada elemento de la matriz es la puntuación de confianza de cada etiqueta. El ColumnName atributo indica ML.NET que esta columna del modelo es el nombre especificado, que es detected_scores.

Uso del modelo para realizar predicciones

Adición de directivas using

En el archivo Program.cs , agregue las siguientes using directivas a la parte superior del archivo.

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

Crear objetos

  1. Cree el MLContext objeto .

    var context = new MLContext();
    
  2. Cree un IDataView con una nueva lista vacía StopSignInput .

    var data = context.Data.LoadFromEnumerable(new List<StopSignInput>());
    
  3. Para mantener la coherencia, guarde las imágenes predichas en la ruta de acceso del ensamblado.

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

Creación de la canalización

Con el objeto IDataView vacío creado, la canalización se puede construir para realizar las predicciones de cualquier imagen nueva. La canalización consta de varios pasos:

  1. Cambie el tamaño de las imágenes entrantes.

    La imagen que se envía al modelo para la predicción suele tener una relación de aspecto diferente a las imágenes con las que se entrenó el modelo. Para mantener la imagen coherente con las predicciones precisas, cambie el tamaño de la imagen a 320x320. Para ello, use el ResizeImages método y establezca como imageColumnName nombre de la StopSignInput.Image propiedad .

    var pipeline = context.Transforms.ResizeImages(resizing: ImageResizingEstimator.ResizingKind.Fill, outputColumnName: "image_tensor", imageWidth: ImageSettings.imageWidth, imageHeight: ImageSettings.imageHeight, inputColumnName: nameof(StopSignInput.Image))
    
  2. Extraiga los píxeles de la imagen.

    Una vez que se ha cambiado el tamaño de la imagen, debe extraer los píxeles de la imagen. Aneja el método ExtractPixels a la canalización y especifica el nombre de la columna para direccionar los píxeles utilizando el parámetro outputColumnName.

    .Append(context.Transforms.ExtractPixels(outputColumnName: "image_tensor"))
    
  3. Aplique el modelo ONNX a la imagen para realizar una predicción. Esto toma algunos parámetros:

    • modelFile : ruta de acceso al archivo de modelo ONNX
    • outputColumnNames : matriz de cadenas que contiene los nombres de todos los nombres de columna de salida, que se pueden encontrar al analizar el modelo ONNX en Netron.
    • inputColumnNames : matriz de cadenas que contiene los nombres de todos los nombres de la columna de entrada, que también se pueden encontrar al analizar el modelo ONNX en Netron.
    .Append(context.Transforms.ApplyOnnxModel(outputColumnNames: new string[] { "detected_boxes", "detected_scores", "detected_classes" }, inputColumnNames: new string[] { "image_tensor" }, modelFile: "./Model/model.onnx"));
    

Ajustar el modelo

Ahora que ha definido una canalización, puede usarla para compilar el modelo de ML.NET. Use el Fit método en la canalización y pase el vacío IDataView.

var model = pipeline.Fit(data);

A continuación, para realizar predicciones, use el modelo para crear un motor de predicción. Se trata de un método genérico, por lo que toma las StopSignInput clases y StopSignPrediction que se crearon anteriormente.

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

Extracción de las etiquetas

Para asignar las salidas del modelo a sus etiquetas, debe extraer las etiquetas proporcionadas por Custom Vision. Estas etiquetas se encuentran en el archivo labels.txt que se incluyó en el archivo ZIP con el modelo ONNX.

Llame al ReadAllLines método para leer todas las etiquetas del archivo.

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

Predicción en una imagen de prueba

Ahora puede usar el modelo para predecir imágenes nuevas. En el proyecto, hay una carpeta de prueba que puede usar para realizar predicciones. Esta carpeta contiene dos imágenes aleatorias con una señal de stop desde Unsplash. Una imagen tiene una señal de stop, mientras que la otra tiene dos señales de stop. Use el GetFiles método para leer las rutas de acceso de archivo de las imágenes del directorio.

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

Recorra las rutas de acceso de archivo para realizar una predicción utilizando el modelo y obtenga el resultado.

  1. Cree un foreach bucle para recorrer las imágenes de prueba.

    Bitmap testImage;
    
    foreach (var image in testFiles)
    {
    
    }
    
  2. En el foreach bucle, genere el nombre de la imagen prevista en función del nombre de la imagen de prueba original.

    var predictedImage = $"{Path.GetFileName(image)}-predicted.jpg";
    
  3. También en el foreach bucle, cree una FileStream de la imagen y conviértela en un Bitmap.

    using (var stream = new FileStream(image, FileMode.Open))
    {
        testImage = (Bitmap)Image.FromStream(stream);
    }
    
  4. También en el bucle foreach, llame al método Predict del motor de predicción.

    var prediction = predictionEngine.Predict(new StopSignInput { Image = testImage });
    
  5. Con la predicción, puede obtener las cajas delimitadoras. Use el Chunk método para determinar cuántos objetos ha detectado el modelo. Para ello, tome el recuento de las cajas delimitadoras previstas y divida eso por el número de etiquetas que se predijeron. Por ejemplo, si tuviera tres objetos detectados en una imagen, habría 12 elementos en la BoundingBoxes matriz y tres etiquetas predichas. A continuación, el Chunk método le daría tres matrices de cuatro para representar los cuadros de límite para cada objeto.

    var boundingBoxes = prediction.BoundingBoxes.Chunk(prediction.BoundingBoxes.Count() / prediction.PredictedLabels.Count());
    
  6. A continuación, capture el ancho original y el alto de las imágenes usadas para la predicción.

    var originalWidth = testImage.Width;
    var originalHeight = testImage.Height;
    
  7. Calcule dónde en la imagen dibujar las cajas. Para ello, cree un for bucle basado en el recuento del fragmento de cajas delimitadoras.

    for (int i = 0; i < boundingBoxes.Count(); i++)
    {
    }
    
  8. Dentro del for bucle, calcule la posición de las coordenadas x e y, así como el ancho y alto del cuadro que se va a dibujar en la imagen. Lo primero que debe hacer es obtener el conjunto de cuadros de límite mediante el ElementAt método .

    var boundingBox = boundingBoxes.ElementAt(i);
    
  9. Con la caja delimitadora actual, ahora puede calcular dónde dibujar la caja. Utilice el ancho de la imagen original para los elementos primer y tercer del cuadro delimitador, y el alto de la imagen original para los elementos segundo y cuarto.

    var left = boundingBox[0] * originalWidth;
    var top = boundingBox[1] * originalHeight;
    var right = boundingBox[2] * originalWidth;
    var bottom = boundingBox[3] * originalHeight;
    
  10. Calcule el ancho y el alto del cuadro para dibujar alrededor del objeto detectado dentro de la imagen. Los elementos x e y son las variables left y top del cálculo anterior. Use el Math.Abs método para obtener el valor absoluto de los cálculos de ancho y alto en caso de que sea negativo.

    var x = left;
    var y = top;
    var width = Math.Abs(right - left);
    var height = Math.Abs(top - bottom);
    
  11. A continuación, obtenga la etiqueta predicha de la matriz de etiquetas.

    var label = labels[prediction.PredictedLabels[i]];
    
  12. Cree un gráfico basado en la imagen de prueba mediante el Graphics.FromImage método .

    using var graphics = Graphics.FromImage(testImage);
    
  13. Dibuja sobre la imagen utilizando la información del cuadro delimitador. En primer lugar, dibuje el rectángulo alrededor de los objetos detectados utilizando el método DrawRectangle, que emplea un objeto Pen para determinar el color y el ancho del rectángulo, y pase las variables x, y, width y height.

    graphics.DrawRectangle(new Pen(Color.NavajoWhite, 8), x, y, width, height);
    
  14. A continuación, muestre la etiqueta predicha dentro del cuadro con el DrawString método que toma la cadena para imprimir y un Font objeto para determinar cómo dibujar la cadena y dónde colocarla.

    graphics.DrawString(label, new Font(FontFamily.Families[0], 18f), Brushes.NavajoWhite, x + 5, y + 5);
    
  15. Después del for bucle, compruebe si el archivo previsto ya existe. Si lo hace, elimínelo. A continuación, guárdelo en la ruta de acceso definida para la salida.

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

Pasos siguientes

Pruebe uno de los otros tutoriales de clasificación de imágenes: