Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Tento článek ukazuje, jak pomocí knihovny OpenCV (Open Source Počítačové zpracování obrazu) s MediaFrameReader zpracovávat snímky fotoaparátu v reálném čase. Tato technika je užitečná pro úlohy analýzy obrázků, jako je detekce okrajů, sledování objektů a odečítání na pozadí v desktopové aplikaci WinUI 3.
Předpoklady
Než začnete, ujistěte se, že máte:
- Desktopový projekt WinUI 3 (zabalený nebo rozbalený)
- Kompatibilní webová kamera připojená k vašemu zařízení.
- Balíčky NuGet OpenCvSharp4 a OpenCvSharp4.runtime.win nainstalované v projektu.
- Přístup k fotoaparátu deklarovaný pro vaši aplikaci, jak je popsáno v části Deklarování přístupu k fotoaparátu.
Instalace OpenCV pro .NET:
dotnet add package OpenCvSharp4
dotnet add package OpenCvSharp4.runtime.win
Deklarace přístupu k fotoaparátu
Než bude vaše aplikace moct číst MediaFrameReader snímky z webové kamery, musíte deklarovat, že vaše aplikace používá fotoaparát. Způsob, jakým to uděláte, závisí na tom, jestli je vaše aplikace zabalená.
Zabalené aplikace: Přidejte
webcamfunkci zařízení doPackage.appxmanifest:<Capabilities> <DeviceCapability Name="webcam" /> </Capabilities>Rozbalené aplikace: Nevybalené aplikace nemají manifest balíčku, takže není k dispozici žádná možnost deklarovat. Přístup k fotoaparátu pro nezabalené aplikace se místo toho řídí nastavením Povolit desktopovým aplikacím přístup k fotoaparátu v části Nastavení>soukromí a zabezpečení>Fotoaparátu na zařízení uživatele.
V obou případech může uživatel dál odepřít přístup k fotoaparátu na úrovni operačního systému. Než inicializujete kameru, zkontrolujte přístup a řádně zpracujte zamítnutý případ, jak je popsáno v nastavení ochrany osobních údajů Windows fotoaparátu.
Nastavte MediaFrameReader
Nejprve vytvořte MediaCapture instanci a vyhledejte zdroj barevného rámečku videa. Pak vytvořte rámec MediaFrameReader pro příjem rámců:
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
Nastavte MemoryPreference na Cpu, aby snímky přicházely jako objekty SoftwareBitmap, ke kterým můžete přistupovat přímo ze spravovaného kódu.
Zpracování snímků pomocí OpenCV
V obslužné rutině události FrameArrived získejte SoftwareBitmap z rámce a převeďte jej na OpenCV Mat pro zpracování. K přístupu k nezpracovaným pixelovým datům potřebujete rozhraní COM IMemoryBufferByteAccess. Přidejte tuto definici do projektu:
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);
}
Pak zpracujte rámce:
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
Blok nebezpečného kódu používá IMemoryBufferByteAccess k získání přímého ukazatele na rastrová data. V nastavení projektu je potřeba povolit nebezpečný kód tak, že ho přidáte <AllowUnsafeBlocks>true</AllowUnsafeBlocks>.csproj do souboru.
Použití operací OpenCV
Můžete použít jakoukoli operaci zpracování obrazu OpenCV na Mat. Tady je příklad, který používá detekci Canny Edge:
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;
});
}
Image Přidejte do XAML ovládací prvek pro zobrazení zpracovaných snímků:
<Image x:Name="ProcessedImage" Stretch="Uniform" />
Vyčistěte zdroje
Až budete hotovi, zastavte čtečku snímků a uvolněte prostředky:
private async Task CleanupAsync()
{
if (_frameReader != null)
{
_frameReader.FrameArrived -= FrameReader_FrameArrived;
await _frameReader.StopAsync();
_frameReader.Dispose();
}
_mediaCapture?.Dispose();
_processedBitmap?.Dispose();
}
Související obsah
Windows developer