Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Saiba como usar ML.NET para detectar objetos em imagens usando um modelo ONNX treinado no serviço de Visão Personalizada da Microsoft.
O serviço de Visão Personalizada da Microsoft é um serviço de IA que treina um modelo com base em imagens que você carrega. Em seguida, você pode exportar o modelo para o formato ONNX e usá-lo em ML.NET para fazer previsões.
Neste tutorial, você aprenderá como:
- Usar o serviço de Visão Personalizada para criar um modelo ONNX
- Incorporar o modelo ONNX no pipeline de ML.NET
- Treinar o modelo de ML.NET
- Detectar sinais de parada em imagens de teste
Pré-requisitos
- Visual Studio 2022 ou posterior.
- Baixe o conjunto de dados de 50 imagens de sinal de parada.
- Uma conta do Azure. Se você não tiver uma, crie uma conta gratuita do Azure.
Criar o modelo
Criar o projeto de Visão Personalizada
Faça logon no serviço de Visão Personalizada da Microsoft e selecione Novo Projeto.
Na caixa de diálogo Novo Projeto , preencha os seguintes itens necessários:
- Defina o nome do projeto de Visão Personalizada como StopSignDetection.
- Selecione o recurso que você usará. Esse é um recurso do Azure que será criado para o projeto de Visão Personalizada. Se nenhum estiver listado, será possível criar um selecionando o link Criar novo .
- Defina o tipo de projeto como Detecção de Objeto.
- Defina os Tipos de Classificação como Multiclasses , pois haverá uma classe por imagem.
- Defina o Domínio como Geral (compacto) [S1]. O domínio compacto permite que você baixe o modelo ONNX.
- Para capacidades de exportação, selecione plataformas básicas para permitir a exportação do modelo ONNX.
Depois que os campos acima forem preenchidos, selecione Criar projeto.
Adicionar imagens
- Com o projeto criado, escolha Adicionar imagens para começar a adicionar imagens para o modelo a ser treinado. Selecione as imagens de sinal de interrupção que você baixou.
- Selecione a primeira imagem mostrada. Você pode selecionar objetos na imagem que deseja que o modelo detecte. Selecione o sinal de parada na imagem. Um pop-up aparece e define a tag como sinal de parada.
- Repita para todas as imagens restantes. Algumas imagens têm mais de um sinal de parada, portanto, certifique-se de marcar tudo o que está nas imagens.
Treinar o modelo
Com as imagens carregadas e marcadas, o modelo agora pode ser treinado. Selecione Treinar.
Um pop-up é exibido perguntando que tipo de treinamento usar. Escolha Treinamento rápido e selecione Treinar.
Baixar o modelo ONNX
Depois que o treinamento for concluído, clique no botão Exportar . Quando o pop-up for exibido, selecione ONNX para baixar o modelo ONNX.
Inspecionar o modelo ONNX
Descompacte o arquivo ONNX baixado. A pasta contém vários arquivos, mas os dois que você usará neste tutorial são:
- labels.txt, que é um arquivo de texto que contém os rótulos que foram definidos no serviço de Visão Personalizada.
- model.onnx, que é o modelo ONNX que você usará para fazer previsões no ML.NET.
Para criar o pipeline ML.NET, você precisará dos nomes das colunas de entrada e saída. Para obter essas informações, use o Netron, um aplicativo Web e desktop que pode analisar modelos ONNX e mostrar sua arquitetura.
Ao usar o aplicativo Web ou desktop do Netron, abra o modelo ONNX no aplicativo. Quando ele é aberto, ele exibe um grafo. Este grafo informa algumas coisas que você precisará para criar 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 ML.NET.
Criar um projeto de console em C#
No Visual Studio, crie um aplicativo de console C# chamado "StopSignDetection". Escolha o .NET 8 como a estrutura de destino.
Instale os seguintes pacotes NuGet para o projeto:
- Microsoft.ML
- Microsoft.ML.ImageAnalytics
- Microsoft.Onnx.Transformer
Observação
Este exemplo usa a versão estável mais recente dos pacotes NuGet mencionados, a menos que indicado de outra forma.
Referenciar o modelo ONNX
Localize os dois arquivos do modelo ONNX (labels.txt e model.onnx) no Gerenciador de Soluções do Visual Studio. Clique com o botão direito do mouse neles e, na janela Propriedades, defina Copiar para o diretório de saída como Copiar se for mais recente.
Criar classes de entrada e previsão
Adicione uma nova classe ao seu projeto e nomeie-a
StopSignInput. Em seguida, adicione o seguinte struct à classe:public struct ImageSettings { public const int imageHeight = 320; public const int imageWidth = 320; }Em seguida, adicione a propriedade a seguir à classe.
public class StopSignInput { [ImageType(ImageSettings.imageHeight, ImageSettings.imageWidth)] public Bitmap Image { get; set; } }A
Imagepropriedade contém o bitmap da imagem usada para previsão. OImageTypeatributo informa ML.NET que a propriedade é uma imagem com dimensões de 320 e 320, que foi determinada usando o Netron.Adicione outra classe ao seu projeto e nomeie-a
StopSignPrediction. Em seguida, 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 de rótulos para cada objeto detectado. O tipo é uma matriz flutuante, portanto, cada item na matriz é a previsão de cada rótulo. OColumnNameatributo informa ML.NET que essa coluna no modelo é o nome fornecido, que édetected_classes.A propriedade
BoundingBoxescontém as caixas delimitadoras de cada objeto detectado. O tipo é uma matriz flutuante e cada objeto detectado entra com quatro itens na matriz da caixa delimitadora. OColumnNameatributo informa ML.NET que essa coluna no modelo é o nome fornecido, que édetected_boxes.A
Scorespropriedade contém as pontuações de confiança de cada objeto previsto e seu rótulo. O tipo é uma matriz flutuante, portanto, cada item na matriz é a pontuação de confiança de cada rótulo. OColumnNameatributo informa ML.NET que essa coluna no modelo é o nome fornecido, que édetected_scores.
Usar o modelo para fazer previsões
Adicionar usando diretivas
No arquivo Program.cs , adicione as seguintes using diretivas à parte superior do arquivo.
using Microsoft.ML;
using Microsoft.ML.Transforms.Image;
using System.Drawing;
using WeatherRecognition;
Criar objetos
Crie o
MLContextobjeto.var context = new MLContext();Crie uma
IDataViewcom uma nova lista vaziaStopSignInput.var data = context.Data.LoadFromEnumerable(new List<StopSignInput>());Para garantir a consistência, salve as imagens previstas no caminho especificado do assembly.
var root = new FileInfo(typeof(Program).Assembly.Location); var assemblyFolderPath = root.Directory.FullName;
Criar o pipeline
Com o IDataView vazio criado, o pipeline pode ser configurado para realizar previsões de novas imagens. O pipeline consiste em várias etapas:
Redimensione as imagens de entrada.
A imagem que está sendo enviada para o modelo para previsão geralmente estará em uma proporção diferente daquelas que foram usadas para treino no modelo. Para manter a imagem consistente para previsões precisas, redimensione a imagem para 320 x 320. Para fazer isso, use o
ResizeImagesmétodo e defina como oimageColumnNamenome daStopSignInput.Imagepropriedade.var pipeline = context.Transforms.ResizeImages(resizing: ImageResizingEstimator.ResizingKind.Fill, outputColumnName: "image_tensor", imageWidth: ImageSettings.imageWidth, imageHeight: ImageSettings.imageHeight, inputColumnName: nameof(StopSignInput.Image))Extraia os pixels da imagem.
Depois que a imagem for redimensionada, você precisará extrair os pixels da imagem. Acrescente o
ExtractPixelsmétodo ao pipeline e especifique o nome da coluna para gerar os pixels para usar ooutputColumnNameparâmetro..Append(context.Transforms.ExtractPixels(outputColumnName: "image_tensor"))Aplique o modelo ONNX à imagem para fazer uma previsão. Isso usa alguns parâmetros:
- modelFile – O caminho para o arquivo de modelo ONNX
- outputColumnNames - Uma matriz de cadeia de caracteres que contém os nomes de todos os nomes de coluna de saída, que podem ser encontrados ao analisar o modelo ONNX no Netron.
- inputColumnNames - Uma matriz de cadeia de caracteres que contém os nomes de todo o nome da coluna de entrada, que também pode ser encontrada 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 você definiu um pipeline, pode usá-lo para criar o modelo de ML.NET. Use o Fit método no pipeline e passe o vazio IDataView.
var model = pipeline.Fit(data);
Em seguida, para fazer previsões, use o modelo para criar um mecanismo de previsão. Esse é um método genérico, portanto, ele usa as StopSignInput classes e StopSignPrediction que foram criadas anteriormente.
var predictionEngine = context.Model.CreatePredictionEngine<StopSignInput, StopSignPrediction>(model);
Extrair os rótulos
Para mapear as saídas do modelo para seus rótulos, você precisa extrair os rótulos fornecidos pelo Custom Vision. Esses rótulos estão no arquivo labels.txt incluído no arquivo zip com o modelo ONNX.
Chame o ReadAllLines método para ler todos os rótulos que formam o arquivo.
var labels = File.ReadAllLines("./model/labels.txt");
Prever em uma imagem de teste
Agora você pode usar o modelo para prever novas imagens. No projeto, há uma pasta de teste que você pode usar para fazer previsões. Esta pasta contém duas imagens aleatórias com um sinal de pare nelas, provenientes de Unsplash. Uma imagem tem um sinal de parada, enquanto a outra tem dois sinais de parada. Use o GetFiles método para ler os caminhos de arquivo das imagens no diretório.
var testFiles = Directory.GetFiles("./test");
Faça um loop pelos caminhos do arquivo para fazer uma previsão com o modelo e gerar o resultado.
Crie um
foreachloop para fazer loop pelas imagens de teste.Bitmap testImage; foreach (var image in testFiles) { }Dentro do
foreachloop, gere o nome da imagem prevista com base no nome da imagem de teste original.var predictedImage = $"{Path.GetFileName(image)}-predicted.jpg";Também no loop
foreach, crie umFileStreamda imagem e converta-o em umBitmap.using (var stream = new FileStream(image, FileMode.Open)) { testImage = (Bitmap)Image.FromStream(stream); }Também no
foreachloop, chame oPredictmétodo no mecanismo de previsão.var prediction = predictionEngine.Predict(new StopSignInput { Image = testImage });Com a previsão, você pode obter as caixas delimitadoras. Use o Chunk método para determinar quantos objetos o modelo detectou. Faça isso contando as caixas delimitadoras previstas e dividindo esse número pelo número de rótulos previstos. Por exemplo, se você tivesse três objetos detectados em uma imagem, haveria 12 itens na
BoundingBoxesmatriz e três rótulos previstos. EmChunkseguida, o método lhe daria três matrizes de quatro para representar as caixas delimitadoras de cada objeto.var boundingBoxes = prediction.BoundingBoxes.Chunk(prediction.BoundingBoxes.Count() / prediction.PredictedLabels.Count());Em seguida, capture a largura e a altura originais das imagens usadas para previsão.
var originalWidth = testImage.Width; var originalHeight = testImage.Height;Calcule onde desenhar as caixas na imagem. Para isso, crie um
forloop baseado na contagem do segmento das caixas delimitadoras.for (int i = 0; i < boundingBoxes.Count(); i++) { }Dentro do
forloop, calcule a posição das coordenadas x e y, bem como a largura e a altura da caixa a serem desenhadas na imagem. A primeira coisa que você precisa fazer é obter o conjunto de caixas delimitadoras usando o métodoElementAt.var boundingBox = boundingBoxes.ElementAt(i);Com a caixa delimitadora atual, agora você pode calcular onde desenhar a caixa. Use a largura da imagem original para o primeiro e o terceiro elementos da caixa delimitadora e a altura da imagem original para o segundo e o 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 detectado dentro da imagem. Os itens x e y são as variáveis
leftetopdo cálculo anterior. Use oMath.Absmétodo para obter o valor absoluto dos cálculos de largura e altura no caso de ser negativo.var x = left; var y = top; var width = Math.Abs(right - left); var height = Math.Abs(top - bottom);Em seguida, obtenha o rótulo previsto da matriz de rótulos.
var label = labels[prediction.PredictedLabels[i]];Crie um gráfico com base na imagem de teste usando o
Graphics.FromImagemétodo.using var graphics = Graphics.FromImage(testImage);Desenhe na imagem usando as informações da caixa delimitadora. Primeiro, desenhe um retângulo ao redor dos objetos detectados usando o método
DrawRectangleque recebe um objetoPenpara determinar a cor e a largura do retângulo, e passe as variáveisx,y,widtheheight.graphics.DrawRectangle(new Pen(Color.NavajoWhite, 8), x, y, width, height);Em seguida, exiba o rótulo previsto dentro da caixa com o
DrawStringmétodo que usa a cadeia de caracteres para imprimir e umFontobjeto para determinar como desenhar a cadeia de caracteres e onde colocá-la.graphics.DrawString(label, new Font(FontFamily.Families[0], 18f), Brushes.NavajoWhite, x + 5, y + 5);Após o
forloop, verifique se o arquivo previsto já existe. Se isso acontecer, exclua-o. Em seguida, salve-o no caminho de saída definido.if (File.Exists(predictedImage)) { File.Delete(predictedImage); } testImage.Save(Path.Combine(assemblyFolderPath, predictedImage));
Próximas etapas
Experimente um dos outros tutoriais de classificação de imagem: