Este ejemplo es una aplicación Web con Blazor que consume un modelo de clasificación de imágenes utilizando ONNX Runtime y C#
GitHub
El código del proyecto puede descargarse del siguiente repositorio de GitHub: demo-ortblazor
Prerequisitos
Setup
Crear un proyecto nuevo
Crear la aplicación Web Blazor (servidor) con el siguiente comando
dotnet new blazorserver -o demo-ortblazor --no-https
Añadir paquetes
Agregar los siguientes paquetes al proyecto
dotnet add package Microsoft.ML.OnnxRuntime
dotnet add package SixLabors.ImageSharp
Código
Procesamiento de la imagen
Lo más complicado para consumir un modelo ONNX con ONNX Runtime es el procesamiento previo de los datos, en este caso vamos a consumir un modelo de clasificación de imágenes llamado resnet50v2.onnx
Para consumir este modelo se necesita previamente procesar la imagen que queremos clasificar para que la imagen esté en el formato requerido por el modelo
Este procesamiento se realiza con las siguientes clases en la carpeta Util

La función principal es: GetImageTensorFromString, esta función recibe una imagen en Base64 y la transforma en un tensor con el formato que el modelo resnet50v2.onnx requiere para procesarlo y dar una respuesta, a continuación el código de la función
public static Tensor<float> GetImageTensorFromString(string strBase64, int imgWidth = 224, int imgHeight=224)
{
// Read image
using Image<Rgb24> image = Image.Load<Rgb24>(Convert.FromBase64String(strBase64));
// Resize image
image.Mutate(x =>
{
x.Resize(new ResizeOptions
{
Size = new Size(imgWidth, imgHeight),
Mode = ResizeMode.Crop
});
});
// Preprocess image
Tensor<float> input = new DenseTensor<float>(new[] { 1, 3, 224, 224 });
var mean = new[] { 0.485f, 0.456f, 0.406f };
var stddev = new[] { 0.229f, 0.224f, 0.225f };
for (int y = 0; y < image.Height; y++)
{
image.ProcessPixelRows(accesor => {
Span<Rgb24> pixelSpan = accesor.GetRowSpan(y);
for (int x = 0; x < image.Width; x++)
{
input[0, 0, y, x] = ((pixelSpan[x].R / 255f) - mean[0]) / stddev[0];
input[0, 1, y, x] = ((pixelSpan[x].G / 255f) - mean[1]) / stddev[1];
input[0, 2, y, x] = ((pixelSpan[x].B / 255f) - mean[2]) / stddev[2];
}
});
}
return input;
}
Consumo del modelo desde la aplicación web
El objetivo de la aplicación es que se pueda seleccionar una imagen y enviarla como parámetro de entrada al modelo de clasificación de imágenes (resnet50v2.onnx) y mostrar la respuesta del modelo en la misma página

Lo primero que debemos hacer es agregar el modelo al proyecto, este está ubicado en la carpeta:
demo-ortblazor/Model/resnet50v2.onnx
En el archivo demo-ortblazor.csproj debemos agregar un ItemGroup para que el modelo sea parte del compilado del proyecto
<ItemGroup>
<Content Include="Model\*" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
Solo nos queda agregar una página Blazor en la carpeta Pages: Pages/OrtBlazor.razor
El código de esta página tiene dos secciones. La primera sección se encarga de cargar la imagen seleccionada en base64 y mostrarla un un componente <img>
@code {
public List<ImageFile> filesBase64 = new List<ImageFile>();
string message = "InputFile";
bool isDisabled = false;
async Task OnChange(InputFileChangeEventArgs e)
{
var files = e.GetMultipleFiles(); // get the files selected by the users
foreach(var file in files)
{
var resizedFile = await file.RequestImageFileAsync(file.ContentType, 640, 480); // resize the image file
var buf = new byte[resizedFile.Size]; // allocate a buffer to fill with the file's data
using (var stream = resizedFile.OpenReadStream())
{
await stream.ReadAsync(buf); // copy the stream to the buffer
}
filesBase64.Add(new ImageFile { base64data = Convert.ToBase64String(buf), contentType = file.ContentType, fileName = file.Name }); // convert to a base64 string!!
}
message = "Click to continue ->";
}
}
La segunda sección de código se encarga de enviar la imagen en base64 al modelo de clasificación ONNX -pasando por el procesamiento previo de la clase ImageHelper- y recibir la respuesta del modelo y mostrarlo en una tabla
@code {
private List<Prediction> top10 = new List<Prediction> { new Prediction { Label = "Label Ejemplo", Confidence = 0.0f }};
private void Predict()
{
string modelFilePath = @"Model/resnet50v2.onnx";
//string imageFilePath = @"/Users/diegomera/repos/demo-ortcsharp/data/dog.jpeg";
//Microsoft.ML.OnnxRuntime.Tensors.Tensor<float> input = ImageHelper.GetImageTensorFromPath(imageFilePath);
Microsoft.ML.OnnxRuntime.Tensors.Tensor<float> input = ImageHelper.GetImageTensorFromString(filesBase64.First().base64data);
top10 = ModelHelper.GetPredictions(input, modelFilePath);
// Print results to console
foreach (var t in top10)
{
Console.WriteLine($"Label: {t.Label}, Confidence: {t.Confidence}");
}
}
}
Ejecutar el proyecto
Para ejecutar el proyecto localmente usamos el comando
dotnet run
En la aplicación web abra la pestaña del menú «Ort Blazor» Seleccione la imagen de un perro ubicado en el mismo proyecto demo-ortblazor/SampleImg/dog.jpeg
Si el proyecto se ejecuta correctamente, debería mostrar el siguiente resultado:
Golden Retriever 0.7550826
Kuvasz 0.1418474
Clumber Spaniel 0.02673398
Otterhound 0.011237004
Sussex Spaniel 0.008394765
Pyrenean Mountain Dog 0.007158577
Labrador Retriever 0.004976533
Saluki 0.0046295193
Tibetan Terrier 0.004320859
English Setter 0.003663472