Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Aprenda a usar ML.NET para detetar objetos em imagens usando um modelo ONNX treinado no serviço Microsoft Custom Vision.
O serviço Microsoft Custom Vision é um serviço de IA que treina um modelo baseado em imagens que carrega. Depois podes exportar o modelo para formato ONNX e usá-lo em ML.NET para fazer previsões.
Neste tutorial, aprenderás como:
- Use o serviço Custom Vision para criar um modelo ONNX
- Incorpore o modelo ONNX no fluxo de trabalho do ML.NET
- Treinar o modelo ML.NET
- Detetar sinais de stop em imagens de teste
Pré-requisitos
- Visual Studio 2022 ou posterior.
- Descarregue o conjunto de dados com 50 imagens de sinais de stop.
- Conta Azure. Se você não tiver uma, crie uma conta gratuita do Azure.
Criar o modelo
Criar o projeto Visão Personalizada
Inicie sessão no serviço Microsoft Custom Vision e selecione Novo Projeto.
No diálogo Novo Projeto , preencha os seguintes itens obrigatórios:
- Defina o nome do projeto Custom Vision como StopSignDetection.
- Selecione o recurso que vai usar. Este é um recurso Azure que será criado para o projeto Custom Vision. Se nenhum estiver listado, pode ser criado um selecionando a opção Criar novo link.
- Defina o tipo de Projeto como Deteção de Objetos.
- Defina os Tipos de Classificação como Multiclasse , pois haverá uma classe por imagem.
- Defina o domínio como geral (compacto) [S1]. O domínio compacto permite descarregar o modelo ONNX.
- Para capacidades de exportação, selecione plataformas Básicas para permitir a exportação do modelo ONNX.
Depois de preencherem os campos acima, selecione Criar projeto.
Adicionar imagens
- Com o projeto criado, escolha Adicionar imagens para começar a adicionar imagens para o modelo treinar. Selecione as imagens dos sinais de stop que descarregou.
- Seleciona a primeira imagem que é mostrada. Podes selecionar objetos na imagem que queres que o modelo detete. Selecione o sinal de stop na imagem. Um popup exibe e define a etiqueta como sinal de stop.
- Repete para todas as imagens restantes. Algumas imagens têm mais do que um sinal de stop, por isso certifique-se de marcar todos os que estão nas imagens.
Treinar o modelo
Com as imagens carregadas e etiquetadas, o modelo pode agora ser treinado. Selecione comboio.
Aparece um pop-up a perguntar que tipo de treino usar. Escolha Treino Rápido e depois selecione Treinar.
Descarregue o modelo ONNX
Quando o treino estiver concluído, clique no botão Exportar . Quando o pop-up aparecer, selecione ONNX para descarregar o modelo ONNX.
Inspecionar o modelo ONNX
Descompacta o ficheiro ONNX descarregado. A pasta contém vários ficheiros, mas os dois que vais usar neste tutorial são:
- labels.txt, que é um ficheiro de texto contendo os rótulos definidos no serviço Custom Vision.
- model.onnx, que é o modelo ONNX que vais usar para fazer previsões em ML.NET.
Para construir o pipeline de ML.NET, vais precisar dos nomes das colunas de entrada e saída. Para obter esta informação, use o Netron, uma aplicação web e desktop que pode analisar modelos ONNX e mostrar a sua arquitetura.
Ao usar a aplicação web ou desktop da Netron, abra o modelo ONNX na aplicação. Assim que abre, apresenta um gráfico. Este gráfico diz-lhe algumas coisas que vai precisar para construir o pipeline de ML.NET para previsões.
Nome da coluna de entrada - O nome da coluna de entrada necessário ao aplicar o modelo ONNX em ML.NET.
Nome da coluna de saída - O nome da coluna de saída necessário ao aplicar o modelo ONNX em ML.NET.
Tamanho da imagem - O tamanho necessário ao redimensionar imagens no pipeline de ML.NET.
Criar um projeto de consola C#
No Visual Studio, crie uma aplicação de consola C# chamada "StopSignDetection". Escolha o .NET 8 como framework de destino.
Instale os seguintes pacotes NuGet para o projeto:
- Microsoft.ML
- Microsoft.ML.ImageAnalytics
- Microsoft.Onnx.Transformer
Observação
Este exemplo utiliza a versão estável mais recente dos pacotes NuGet mencionados, salvo indicação em contrário.
Consulte o modelo ONNX
Encontre os dois ficheiros do modelo ONNX (labels.txt e model.onnx) no Visual Studio Solution Explorer. Clique com o botão direito neles e, na janela de Propriedades, defina Copiar para o diretório de saída para Copiar, se for mais recente.
Criar classes de entrada e previsão
Adiciona uma nova turma ao teu projeto e nomea-a
StopSignInput. Depois, adicione a seguinte estrutura à turma:public struct ImageSettings { public const int imageHeight = 320; public const int imageWidth = 320; }De seguida, adicione a seguinte propriedade à classe.
public class StopSignInput { [ImageType(ImageSettings.imageHeight, ImageSettings.imageWidth)] public Bitmap Image { get; set; } }A
Imagepropriedade contém o mapa de bits da imagem usada para a previsão. O atributoImageTypeindica ao ML.NET que a propriedade é uma imagem com dimensões de 320 por 320, determinadas usando Netron.Adiciona outra aula ao teu projeto e nomeia-a
StopSignPrediction. Depois, adicione as seguintes propriedades à 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; } }A
PredictedLabelspropriedade contém as previsões das etiquetas para cada objeto detetado. O tipo é um array flutuante, pelo que cada item no array é a previsão de cada rótulo. O atributoColumnNameindica ao ML.NET que esta coluna no modelo representa o nome fornecido, que édetected_classes.A
BoundingBoxespropriedade contém as caixas delimitadoras para cada objeto detetado. O tipo é um array flutuante e cada objeto detetado vem com quatro itens no array da caixa delimitadora. O atributoColumnNameindica à ML.NET que esta coluna no modelo corresponde ao nome fornecido, que édetected_boxes.A
Scorespropriedade contém as pontuações de confiança de cada objeto previsto e o seu rótulo. O tipo é um array flutuante, pelo que cada item no array é a pontuação de confiança de cada rótulo. O atributoColumnNameindica ao ML.NET que esta coluna no modelo é o nome dado, que édetected_scores.
Use o modelo para fazer previsões
Adicionar diretivas de utilização
No ficheiro Program.cs , adicione as seguintes using diretivas no topo do ficheiro.
using Microsoft.ML;
using Microsoft.ML.Transforms.Image;
using System.Drawing;
using WeatherRecognition;
Criar objetos
Cria o
MLContextobjeto.var context = new MLContext();Cria uma
IDataViewcom uma nova lista vaziaStopSignInput.var data = context.Data.LoadFromEnumerable(new List<StopSignInput>());Para consistência, guarde as imagens previstas no caminho de montagem.
var root = new FileInfo(typeof(Program).Assembly.Location); var assemblyFolderPath = root.Directory.FullName;
Construa o pipeline
Com o elemento vazio IDataView criado, o pipeline pode ser configurado para fazer as previsões de quaisquer novas imagens. O oleoduto consiste em vários passos:
Redimensione as imagens que chegam.
A imagem enviada ao modelo para previsão estará frequentemente numa proporção de aspeto diferente das imagens treinadas no modelo. Para manter a imagem consistente para previsões precisas, redimensione-a para 320x320. Para isso, use o
ResizeImagesmétodo e defina oimageColumnNamecomo nome daStopSignInput.Imagepropriedade.var pipeline = context.Transforms.ResizeImages(resizing: ImageResizingEstimator.ResizingKind.Fill, outputColumnName: "image_tensor", imageWidth: ImageSettings.imageWidth, imageHeight: ImageSettings.imageHeight, inputColumnName: nameof(StopSignInput.Image))Extrai os píxeis da imagem.
Depois de a imagem ter sido redimensionada, é necessário extrair os píxeis da imagem. Anexe o
ExtractPixelsmétodo ao seu pipeline e especifique o nome da coluna para onde os píxeis serão produzidos usando ooutputColumnNameparâmetro..Append(context.Transforms.ExtractPixels(outputColumnName: "image_tensor"))Aplique o modelo ONNX à imagem para fazer uma previsão. Isto requer alguns parâmetros:
- modelFile - O caminho para o ficheiro do modelo ONNX
- outputColumnNames - Um array de strings que contém os nomes de todos os nomes das colunas de saída, que pode ser encontrado ao analisar o modelo ONNX no Netron.
- inputColumnNames - Um array de strings que contém os nomes de todos os nomes das colunas de entrada, que também pode ser encontrado ao analisar o modelo ONNX no Netron.
.Append(context.Transforms.ApplyOnnxModel(outputColumnNames: new string[] { "detected_boxes", "detected_scores", "detected_classes" }, inputColumnNames: new string[] { "image_tensor" }, modelFile: "./Model/model.onnx"));
Ajustar o modelo
Agora que definiu um pipeline, pode usá-lo para construir o modelo ML.NET. Use o Fit método no pipeline e passe o vazio IDataView.
var model = pipeline.Fit(data);
De seguida, para fazer previsões, use o modelo para criar um motor de previsão. Este é um método genérico, por isso inclui as StopSignInput classes e StopSignPrediction que foram criadas anteriormente.
var predictionEngine = context.Model.CreatePredictionEngine<StopSignInput, StopSignPrediction>(model);
Extrair as etiquetas
Para mapear as saídas do modelo para as suas etiquetas, precisa de extrair as etiquetas fornecidas pelo Custom Vision. Estas etiquetas encontram-se no ficheirolabels.txt que estava incluído no ficheiro zip com o modelo ONNX.
Chame o ReadAllLines método para ler todas as etiquetas do ficheiro.
var labels = File.ReadAllLines("./model/labels.txt");
Prever numa imagem de teste
Agora pode usar o modelo para prever novas imagens. No projeto, existe uma pasta de teste que pode usar para fazer previsões. Esta pasta contém duas imagens aleatórias, cada uma com um sinal de paragem, de Unsplash. Uma imagem tem um sinal de stop enquanto a outra tem dois sinais de stop. Use o GetFiles método para ler os caminhos dos ficheiros das imagens no diretório.
var testFiles = Directory.GetFiles("./test");
Percorre os caminhos dos ficheiros para fazer uma previsão com o modelo e obter o resultado.
Crie um
foreachciclo para percorrer as imagens de teste.Bitmap testImage; foreach (var image in testFiles) { }No
foreachciclo, gera o nome da imagem prevista com base no nome da imagem de teste original.var predictedImage = $"{Path.GetFileName(image)}-predicted.jpg";Também no ciclo
foreach, cria umFileStreamda imagem e converte-o para umBitmap.using (var stream = new FileStream(image, FileMode.Open)) { testImage = (Bitmap)Image.FromStream(stream); }Também no
foreachciclo, chama oPredictmétodo no motor de previsão.var prediction = predictionEngine.Predict(new StopSignInput { Image = testImage });Com a previsão, podes obter as caixas delimitadoras. Use o Chunk método para determinar quantos objetos o modelo detetou. Faça isto ao tomar a contagem das caixas delimitadoras previstas e dividi-la pelo número de rótulos previstos. Por exemplo, se tivesse três objetos detetados numa imagem, haveria 12 itens no
BoundingBoxesarray e três etiquetas previstas. OChunkmétodo dar-lhe-á então três matrizes de quatro para representar as caixas delimitadoras de cada objeto.var boundingBoxes = prediction.BoundingBoxes.Chunk(prediction.BoundingBoxes.Count() / prediction.PredictedLabels.Count());De seguida, capture a largura e altura originais das imagens usadas para previsão.
var originalWidth = testImage.Width; var originalHeight = testImage.Height;Calcula onde na imagem deves desenhar as caixas. Para isso, cria um
forciclo baseado na contagem dos blocos de caixas delimitadoras.for (int i = 0; i < boundingBoxes.Count(); i++) { }Dentro do
forloop, calcula a posição das coordenadas x e y, bem como a largura e altura da caixa a desenhar na imagem. A primeira coisa que precisas de fazer é obter o conjunto de caixas delimitadoras usando oElementAtmétodo.var boundingBox = boundingBoxes.ElementAt(i);Com a caixa delimitadora atual, você pode agora calcular onde desenhar a caixa. Use a largura original da imagem para o primeiro e terceiro elementos da caixa delimitadora, e a altura original da imagem para o segundo e quarto elementos.
var left = boundingBox[0] * originalWidth; var top = boundingBox[1] * originalHeight; var right = boundingBox[2] * originalWidth; var bottom = boundingBox[3] * originalHeight;Calcule a largura e a altura da caixa para desenhar em torno do objeto detetado dentro da imagem. Os itens x e y são as
leftvariáveis etopdo cálculo anterior. Use oMath.Absmétodo para obter o valor absoluto dos cálculos de largura e altura caso seja negativo.var x = left; var y = top; var width = Math.Abs(right - left); var height = Math.Abs(top - bottom);De seguida, obtenha a etiqueta prevista a partir do conjunto de etiquetas.
var label = labels[prediction.PredictedLabels[i]];Crie um gráfico baseado na imagem de teste usando o
Graphics.FromImagemétodo.using var graphics = Graphics.FromImage(testImage);Desenhe a imagem usando a informação da caixa delimitadora. Primeiro, desenhe o retângulo em torno dos objetos detetados usando o
DrawRectanglemétodo que absorve umPenobjeto para determinar a cor e largura do retângulo, e passe asxvariáveis ,y,width, eheight.graphics.DrawRectangle(new Pen(Color.NavajoWhite, 8), x, y, width, height);Depois, mostre a etiqueta prevista dentro da caixa com o
DrawStringmétodo que requer a cadeia para imprimir e umFontobjeto para determinar como desenhar a cadeia e onde colocá-la.graphics.DrawString(label, new Font(FontFamily.Families[0], 18f), Brushes.NavajoWhite, x + 5, y + 5);Após o
forciclo, verifica se o ficheiro previsto já existe. Se acontecer, elimina-o. Depois, guarde-o no caminho de saída definido.if (File.Exists(predictedImage)) { File.Delete(predictedImage); } testImage.Save(Path.Combine(assemblyFolderPath, predictedImage));
Próximos passos
Experimenta um dos outros tutoriais de classificação de imagens: