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 visar hur du använder OpenCV-biblioteket (open source Visuellt innehåll) med en MediaFrameReader för att bearbeta kameraramar i realtid. Den här tekniken är användbar för bildanalysuppgifter som gränsidentifiering, objektspårning och bakgrundsundertraktion i en WinUI 3-skrivbordsapp.
Förutsättningar
Innan du kan börja bör du kontrollera att du har:
- Ett WinUI 3-skrivbordsprojekt (paketerat eller uppackat).
- En kompatibel webbkamera som är ansluten till enheten.
- NuGet-paketen OpenCvSharp4 och OpenCvSharp4.runtime.win installerade i projektet.
- Kameraåtkomst deklarerad för din app enligt beskrivningen i Deklarera kameraåtkomst.
Så här installerar du OpenCV för .NET:
dotnet add package OpenCvSharp4
dotnet add package OpenCvSharp4.runtime.win
Deklarera kameraåtkomst
Innan appen kan använda MediaFrameReader för att läsa ramar från en webbkamera måste du deklarera att din app använder kameran. Hur du gör detta beror på om din app är paketerad.
Paketerade appar: Lägg till enhetsfunktionen i
webcamPackage.appxmanifest:<Capabilities> <DeviceCapability Name="webcam" /> </Capabilities>Uppackade appar: Uppackade appar har inget paketmanifest, så det finns ingen möjlighet att deklarera. Kameraåtkomst för uppackade appar styrs i stället av inställningen Låt skrivbordsappar komma åt kameran under Inställningar>Sekretess och säkerhetskamera> på användarens enhet.
I båda fallen kan användaren fortfarande neka kameraåtkomst på OS-nivå. Kontrollera åtkomsten innan du initierar kameran och hantera det nekade ärendet på ett smidigt sätt, enligt beskrivningen i Hantera sekretessinställningen för Windows kamera.
Konfigurera MediaFrameReader
Börja med att skapa en MediaCapture instans och hitta en färgvideoramkälla. Skapa sedan en MediaFrameReader för att ta emot ramar:
using System.Linq;
using Windows.Media.Capture;
using Windows.Media.Capture.Frames;
using Windows.Graphics.Imaging;
private MediaCapture _mediaCapture;
private MediaFrameReader _frameReader;
private async Task InitializeFrameReaderAsync()
{
// Find a color video source group
var sourceGroups =
await MediaFrameSourceGroup.FindAllAsync();
var selectedGroup = sourceGroups.FirstOrDefault(group =>
group.SourceInfos.Any(info =>
info.MediaStreamType == MediaStreamType.VideoPreview &&
info.SourceKind == MediaFrameSourceKind.Color));
if (selectedGroup == null)
{
// No suitable camera found
return;
}
_mediaCapture = new MediaCapture();
var settings = new MediaCaptureInitializationSettings
{
SourceGroup = selectedGroup,
MemoryPreference = MediaCaptureMemoryPreference.Cpu,
StreamingCaptureMode = StreamingCaptureMode.Video,
};
await _mediaCapture.InitializeAsync(settings);
// Find the color video source
var colorSource = _mediaCapture.FrameSources
.Values.FirstOrDefault(source =>
source.Info.SourceKind == MediaFrameSourceKind.Color);
if (colorSource == null)
{
return;
}
_frameReader =
await _mediaCapture.CreateFrameReaderAsync(colorSource);
_frameReader.FrameArrived += FrameReader_FrameArrived;
await _frameReader.StartAsync();
}
Important
Ange MemoryPreference till Cpu så att bildrutor tas emot som SoftwareBitmap-objekt som du kan komma åt direkt från hanterad kod.
Bearbeta ramar med OpenCV
FrameArrived I händelsehanteraren hämtar du SoftwareBitmap från ramen och konverterar den till en OpenCV Mat för bearbetning. Du behöver COM-gränssnittet IMemoryBufferByteAccess för att få åtkomst till rådata för pixeln. Lägg till den här definitionen i projektet:
using System.Runtime.InteropServices;
[ComImport]
[System.Runtime.InteropServices.Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe interface IMemoryBufferByteAccess
{
void GetBuffer(out byte* buffer, out uint capacity);
}
Bearbeta sedan ramarna:
using OpenCvSharp;
private void FrameReader_FrameArrived(
MediaFrameReader sender,
MediaFrameArrivedEventArgs args)
{
using var frameRef =
sender.TryAcquireLatestFrame();
if (frameRef == null) return;
var bitmap = frameRef.VideoMediaFrame?.SoftwareBitmap;
if (bitmap == null) return;
// Convert to Bgra8 format if needed
SoftwareBitmap convertedBitmap = null;
if (bitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8)
{
convertedBitmap = SoftwareBitmap.Convert(
bitmap, BitmapPixelFormat.Bgra8);
bitmap = convertedBitmap;
}
// Access the pixel buffer
using var buffer = bitmap.LockBuffer(
BitmapBufferAccessMode.Read);
using var reference =
buffer.CreateReference();
// Get the buffer as a byte array via
// IMemoryBufferByteAccess
unsafe
{
((IMemoryBufferByteAccess)reference).GetBuffer(
out byte* dataInBytes,
out uint capacity);
// Use stride from the buffer plane description
// to avoid corrupted images from padding
var desc = buffer.GetPlaneDescription(0);
using var mat = Mat.FromPixelData(
desc.Height, desc.Width,
MatType.CV_8UC4,
(IntPtr)dataInBytes,
desc.Stride);
// Process the frame with OpenCV
ProcessFrame(mat);
}
// Dispose the converted bitmap if we created one
convertedBitmap?.Dispose();
}
Note
Det osäkra kodblocket använder IMemoryBufferByteAccess för att hämta en direkt pekare till bitmapdata. Du måste aktivera osäker kod i projektinställningarna genom att lägga <AllowUnsafeBlocks>true</AllowUnsafeBlocks> till i .csproj filen.
Tillämpa OpenCV-åtgärder
Du kan tillämpa alla OpenCV-avbildningsbearbetningsåtgärder på Mat. Här är ett exempel som använder Canny-kantdetektering:
private SoftwareBitmap _processedBitmap;
private void ProcessFrame(Mat inputMat)
{
// Convert to grayscale for edge detection
using var grayMat = new Mat();
Cv2.CvtColor(inputMat, grayMat, ColorConversionCodes.BGRA2GRAY);
// Apply Canny edge detection
using var edges = new Mat();
Cv2.Canny(grayMat, edges, 50, 200);
// Convert back to BGRA for display
using var outputMat = new Mat();
Cv2.CvtColor(edges, outputMat, ColorConversionCodes.GRAY2BGRA);
// Create a SoftwareBitmap from the processed Mat
var processedBitmap = new SoftwareBitmap(
BitmapPixelFormat.Bgra8,
outputMat.Width,
outputMat.Height,
BitmapAlphaMode.Premultiplied);
using var destBuffer = processedBitmap.LockBuffer(
BitmapBufferAccessMode.Write);
using var destRef = destBuffer.CreateReference();
unsafe
{
((IMemoryBufferByteAccess)destRef).GetBuffer(
out byte* destBytes, out uint destCapacity);
// Copy the processed data to the SoftwareBitmap row by row.
// outputMat.Step() and desc.Stride can differ (for example,
// because of OpenCV's own row alignment), so a single bulk
// copy across the whole buffer can corrupt the image. Copying
// one row at a time, using each buffer's own stride, avoids
// that mismatch.
var desc = destBuffer.GetPlaneDescription(0);
int rowBytes = outputMat.Width * outputMat.ElemSize();
byte* sourceRow = (byte*)outputMat.Data.ToPointer();
byte* destRow = destBytes;
for (int row = 0; row < outputMat.Height; row++)
{
System.Buffer.MemoryCopy(
sourceRow,
destRow,
destCapacity - (uint)(row * desc.Stride),
(uint)rowBytes);
sourceRow += outputMat.Step();
destRow += desc.Stride;
}
}
// Update the UI with the processed frame
DispatcherQueue.TryEnqueue(async () =>
{
var source =
new Microsoft.UI.Xaml.Media.Imaging
.SoftwareBitmapSource();
await source.SetBitmapAsync(processedBitmap);
ProcessedImage.Source = source;
_processedBitmap?.Dispose();
_processedBitmap = processedBitmap;
});
}
Lägg till en Image kontroll i XAML för att visa de bearbetade ramarna:
<Image x:Name="ProcessedImage" Stretch="Uniform" />
Rensa resurser
Stoppa ramläsaren och frigör resurserna när du är klar:
private async Task CleanupAsync()
{
if (_frameReader != null)
{
_frameReader.FrameArrived -= FrameReader_FrameArrived;
await _frameReader.StopAsync();
_frameReader.Dispose();
}
_mediaCapture?.Dispose();
_processedBitmap?.Dispose();
}
Relaterat innehåll
Windows developer