Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Den här artikeln beskriver hur du skapar en WinUI-app som använder en ONNX-modell för att klassificera objekt i en bild och visa förtroende för varje klassificering. Mer information om hur du använder AI- och maskininlärningsmodeller i din Windows-app finns i Komma igång med AI på Windows. Den här genomgången använder Windows ML för att automatiskt hantera exekveringsprovidrar för hårdvaruaccelererad inferens.
När du använder AI-funktioner rekommenderar vi att du läser: Utveckla ansvarsfulla generativa AI-program och funktioner i Windows.
Vad är ONNX-körmiljön?
ONNX Runtime är en plattformsoberoende maskininlärningsmodellaccelerator med ett flexibelt gränssnitt för att integrera maskinvaruspecifika bibliotek. ONNX Runtime kan användas med modeller från PyTorch, Tensorflow/Keras, TFLite, scikit-learnoch andra ramverk. Mer information finns på ONNX Runtime webbplats på https://onnxruntime.ai/docs/.
Det här exemplet använder Windows ML som tillhandahåller en delad ONNX Runtime och dynamiskt laddar ned de bästa körningsprovidrar för användarens maskinvara. Windows ML abstraherar bort valet av maskinvara – din app drar automatiskt nytta av GPU-, NPU- eller CPU-acceleration utan att inkludera specifika körningsleverantörer.
Förutsättningar
- Enheten måste ha aktiverat utvecklarläge. Mer information finns i Aktivera enheten för utveckling.
- Visual Studio 2022 eller senare med .NET-arbetsbelastning för skrivbordsutveckling.
Skapa en ny C#WinUI-app
Skapa ett nytt projekt i Visual Studio. I dialogrutan Skapa ett nytt projekt anger du språkfiltret till "C#" och projekttypfiltret till "winui" och väljer sedan mallen Tom app, Paketerad (WinUI3 i Desktop). Ge det nya projektet namnet "ONNXWinUIExample".
Lägga till referenser till Nuget-paket
Högerklicka på Beroenden i Prieskumník riešení och välj Hantera NuGet-paket.... I NuGet-pakethanteraren väljer du fliken Bläddra. Sök efter följande paket och välj den senaste stabila versionen i listrutan Version och klicka sedan på Installera.
| Paket | Beskrivning |
|---|---|
| Microsoft.WindowsAppSDK.ML | Tillhandahåller Windows ML-körning med och ONNX Runtime automatisk körningsproviderhantering. |
| SixLabors.ImageSharp | Tillhandahåller bildverktyg för bearbetning av bilder för modellindata. |
Lägg till följande using-direktiv högst upp i MainWindows.xaml.cs för att få åtkomst till API:erna från dessa bibliotek.
// MainWindow.xaml.cs
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
using Microsoft.Windows.AI.MachineLearning;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
Lägg till modellen i projektet
Högerklicka på projektet i Prieskumník riešení och välj Lägg till> ny mapp. Ge den nya mappen namnet "model". I det här exemplet använder vi modellen resnet50-v2-7.onnx från https://github.com/onnx/models. Gå till lagringsplatsvy för modellen på https://github.com/onnx/models/blob/main/validated/vision/classification/resnet/model/resnet50-v2-7.onnx. Klicka på knappen *Ladda ned råfil . Kopiera den här filen till katalogen "modell" som du nyss skapade.
I Prieskumník riešení klickar du på modellfilen och anger Kopiera till Utdatakatalog till "Kopiera om nyare".
Skapa ett enkelt användargränssnitt
I det här exemplet skapar vi ett enkelt användargränssnitt som innehåller en knapp så att användaren kan välja en bild som ska utvärderas med modellen, en bildkontroll för att visa den valda bilden och en TextBlock för att visa de objekt som modellen identifierade i bilden och konfidensen för varje objektklassificering.
MainWindow.xaml I filen ersätter du standardelementet StackPanel med följande XAML-kod.
<!--MainWindow.xaml-->
<Grid Padding="25" >
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="myButton" Click="myButton_Click" Grid.Column="0" VerticalAlignment="Top">Select photo</Button>
<Image x:Name="myImage" MaxWidth="300" Grid.Column="1" VerticalAlignment="Top"/>
<TextBlock x:Name="featuresTextBlock" Grid.Column="2" VerticalAlignment="Top"/>
</Grid>
Initiera modellen
MainWindow.xaml.cs I filen i klassen MainWindow skapar du en hjälpmetod med namnet InitModel som initierar modellen. Den här metoden använder Windows ML för att automatiskt ladda ned och registrera de bästa tillgängliga exekveringsprovidrarna för användarens maskinvara.
ExecutionProviderCatalog hanterar maskinvaruidentifiering och EP-val – du behöver inte välja ett GPU-kort manuellt. Slutligen initieras en ny InferenceSession som skickar sökvägen till modellfilen.
// MainWindow.xaml.cs
private InferenceSession _inferenceSession;
private string modelDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "model");
private async Task InitModelAsync()
{
if (_inferenceSession != null)
{
return;
}
// Use Windows ML to download and register the best execution providers
var catalog = ExecutionProviderCatalog.GetDefault();
if (catalog is not null)
{
await catalog.EnsureAndRegisterCertifiedAsync();
}
// Create the inference session — uses registered EPs automatically
_inferenceSession = new InferenceSession($@"{modelDir}\resnet50-v2-7.onnx");
}
Läsa in och analysera en bild
För enkelhetens skull placeras alla steg för att läsa in och formatera bilden, anropa modellen och visa resultatet i knappklickshanteraren. Observera att vi lägger till nyckelordet async i knappklickshanteraren som ingår i standardmallen så att vi kan köra asynkrona åtgärder i hanteraren.
// MainWindow.xaml.cs
private async void myButton_Click(object sender, RoutedEventArgs e)
{
...
}
Använd en FileOpenPicker för att låta användaren välja en bild från sin dator för att analysera och visa den i användargränssnittet.
FileOpenPicker fileOpenPicker = new()
{
ViewMode = PickerViewMode.Thumbnail,
FileTypeFilter = { ".jpg", ".jpeg", ".png", ".gif" },
};
InitializeWithWindow.Initialize(fileOpenPicker, WinRT.Interop.WindowNative.GetWindowHandle(this));
StorageFile file = await fileOpenPicker.PickSingleFileAsync();
if (file == null)
{
return;
}
// Display the image in the UI
var bitmap = new BitmapImage();
bitmap.SetSource(await file.OpenAsync(Windows.Storage.FileAccessMode.Read));
myImage.Source = bitmap;
Därefter måste vi bearbeta indata för att få in dem i ett format som stöds av modellen. Biblioteket SixLabors.ImageSharp används för att läsa in bilden i 24-bitars RGB-format och ändra storlek på bilden till 224 x 224 bildpunkter. Sedan normaliseras pixelvärdena med medelvärdet 255*[0,485, 0,456, 0,406] och standardavvikelsen 255*[0,229, 0,224, 0,225]. Information om det format som modellen förväntar sig finns på github-sidan resnet-modellen.
using var fileStream = await file.OpenStreamForReadAsync();
IImageFormat format = SixLabors.ImageSharp.Image.DetectFormat(fileStream);
using Image<Rgb24> image = SixLabors.ImageSharp.Image.Load<Rgb24>(fileStream);
// Resize image
using Stream imageStream = new MemoryStream();
image.Mutate(x =>
{
x.Resize(new ResizeOptions
{
Size = new SixLabors.ImageSharp.Size(224, 224),
Mode = ResizeMode.Crop
});
});
image.Save(imageStream, format);
// Preprocess image
// We use DenseTensor for multi-dimensional access to populate the image data
var mean = new[] { 0.485f, 0.456f, 0.406f };
var stddev = new[] { 0.229f, 0.224f, 0.225f };
DenseTensor<float> processedImage = new(new[] { 1, 3, 224, 224 });
image.ProcessPixelRows(accessor =>
{
for (int y = 0; y < accessor.Height; y++)
{
Span<Rgb24> pixelSpan = accessor.GetRowSpan(y);
for (int x = 0; x < accessor.Width; x++)
{
processedImage[0, 0, y, x] = ((pixelSpan[x].R / 255f) - mean[0]) / stddev[0];
processedImage[0, 1, y, x] = ((pixelSpan[x].G / 255f) - mean[1]) / stddev[1];
processedImage[0, 2, y, x] = ((pixelSpan[x].B / 255f) - mean[2]) / stddev[2];
}
}
});
Därefter konfigurerar vi indata genom att skapa en OrtValue av Tensor-typ ovanpå datamatrisen för hanterade avbildningar.
// Setup inputs
// Pin tensor buffer and create a OrtValue with native tensor that makes use of
// DenseTensor buffer directly. This avoids extra data copy within OnnxRuntime.
// It will be unpinned on ortValue disposal
using var inputOrtValue = OrtValue.CreateTensorValueFromMemory(OrtMemoryInfo.DefaultInstance,
processedImage.Buffer, new long[] { 1, 3, 224, 224 });
var inputs = new Dictionary<string, OrtValue>
{
{ "data", inputOrtValue }
};
Om slutsatsdragningssessionen inte har initierats ännu anropar du sedan hjälpmetoden InitModelAsync . Anropa sedan kör-metoden för att köra modellen och hämta resultatet.
// Run inference
if (_inferenceSession == null)
{
await InitModelAsync();
}
using var runOptions = new RunOptions();
using IDisposableReadOnlyCollection<OrtValue> results = _inferenceSession.Run(runOptions, inputs, _inferenceSession.OutputNames);
Modellen matar ut resultatet som en intern tensorbuffert. Följande kod konverterar utdata till en matris med flyttal. En softmax-funktion tillämpas så att värdena ligger i intervallet [0,1] och summan till 1.
// Postprocess output
// We copy results to array only to apply algorithms, otherwise data can be accessed directly
// from the native buffer via ReadOnlySpan<T> or Span<T>
var output = results[0].GetTensorDataAsSpan<float>().ToArray();
float sum = output.Sum(x => (float)Math.Exp(x));
IEnumerable<float> softmax = output.Select(x => (float)Math.Exp(x) / sum);
Indexet för varje värde i utdatamatrisen mappar till en etikett som modellen tränats på, och värdet vid det indexet är modellens förtroende för att etiketten representerar ett objekt som identifierats i indatabilden. Vi väljer de 10 resultaten med det högsta konfidensvärdet. Den här koden använder några hjälpobjekt som vi kommer att definiera i nästa steg.
// Extract top 10
IEnumerable<Prediction> top10 = softmax.Select((x, i) => new Prediction { Label = LabelMap.Labels[i], Confidence = x })
.OrderByDescending(x => x.Confidence)
.Take(10);
// Print results
featuresTextBlock.Text = "Top 10 predictions for ResNet50 v2...\n";
featuresTextBlock.Text += "-------------------------------------\n";
foreach (var t in top10)
{
featuresTextBlock.Text += $"Label: {t.Label}, Confidence: {t.Confidence}\n";
}
} // End of myButton_Click
Deklarera hjälpobjekt
Klassen Prediction är bara ett enkelt sätt att associera en objektetikett med ett konfidensvärde. I MainPage.xaml.cslägger du till den här klassen i namnområdesblocket ONNXWinUIExample , men utanför klassdefinitionen MainWindow .
internal class Prediction
{
public object Label { get; set; }
public float Confidence { get; set; }
}
Lägg sedan till hjälpklassen LabelMap som visar alla objektetiketter som modellen tränats på, i en specifik ordning så att etiketterna mappas till indexen för de resultat som returneras av modellen. Listan med etiketter är för lång för att kunna visas i sin helhet här. Du kan kopiera den fullständiga
public class LabelMap
{
public static readonly string[] Labels = new[] {
"tench",
"goldfish",
"great white shark",
...
"hen-of-the-woods",
"bolete",
"ear",
"toilet paper"};
Kör exemplet
Skapa och kör projektet. Klicka på knappen Välj foto och välj en bildfil som ska analyseras. Du kan titta på klassdefinitionen för LabelMap-hjälpen för att se vad modellen kan känna igen och välja en bild som kan ha intressanta resultat. När modellen har initierats första gången den körs och när modellens bearbetning är klar bör du se en lista över objekt som har identifierats i bilden och konfidensvärdet för varje förutsägelse.
Top 10 predictions for ResNet50 v2...
-------------------------------------
Label: lakeshore, Confidence: 0.91674984
Label: seashore, Confidence: 0.033412453
Label: promontory, Confidence: 0.008877817
Label: shoal, Confidence: 0.0046836217
Label: container ship, Confidence: 0.001940886
Label: Lakeland Terrier, Confidence: 0.0016400366
Label: maze, Confidence: 0.0012478716
Label: breakwater, Confidence: 0.0012336193
Label: ocean liner, Confidence: 0.0011933135
Label: pier, Confidence: 0.0011284945