Erkennen von Gesichtern in Bildern oder Videos
In diesem Thema wird gezeigt, wie Sie mit dem FaceDetector Gesichter in einem Bild erkennen. Der FaceTracker ist für das Nachverfolgen von Gesichtern im Laufe der Zeit in einer Sequenz von Videoframes optimiert.
Eine alternative Methode zum Nachverfolgen von Gesichtern mithilfe von FaceDetectionEffect finden Sie unter Szenenanalyse für die Medienaufnahme.
Der Code in diesem Artikel wurde aus den Beispielen "Basic Face Detection " und "Basic Face Tracking" angepasst. Sie können diese Beispiele herunterladen, um den im Kontext verwendeten Code anzuzeigen oder das Beispiel als Ausgangspunkt für Ihre eigene App zu verwenden.
Erkennen von Gesichtern in einem einzelnen Bild
Mit der FaceDetector-Klasse können Sie ein oder mehrere Gesichter in einem Stillbild erkennen.
In diesem Beispiel werden APIs aus den folgenden Namespaces verwendet.
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using Windows.Graphics.Imaging;
using Windows.Media.FaceAnalysis;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Shapes;
Deklarieren Sie eine Klassenmemembvariable für das FaceDetector-Objekt und für die Liste der DetectedFace-Objekte, die im Bild erkannt werden.
FaceDetector faceDetector;
IList<DetectedFace> detectedFaces;
Die Gesichtserkennung funktioniert auf einem SoftwareBitmap-Objekt , das auf vielfältige Weise erstellt werden kann. In diesem Beispiel wird ein FileOpenPicker verwendet, um dem Benutzer die Auswahl einer Bilddatei zu ermöglichen, in der Gesichter erkannt werden. Weitere Informationen zum Arbeiten mit Softwarebitmaps finden Sie unter "Imaging".
FileOpenPicker photoPicker = new FileOpenPicker();
photoPicker.ViewMode = PickerViewMode.Thumbnail;
photoPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
photoPicker.FileTypeFilter.Add(".jpg");
photoPicker.FileTypeFilter.Add(".jpeg");
photoPicker.FileTypeFilter.Add(".png");
photoPicker.FileTypeFilter.Add(".bmp");
StorageFile photoFile = await photoPicker.PickSingleFileAsync();
if (photoFile == null)
{
return;
}
Verwenden Sie die BitmapDecoder-Klasse, um die Bilddatei in eine SoftwareBitmap zu decodieren. Der Gesichtserkennungsprozess ist schneller mit einem kleineren Bild, sodass Sie das Quellbild möglicherweise auf eine kleinere Größe skalieren möchten. Dies kann während der Decodierung ausgeführt werden, indem ein BitmapTransform-Objekt erstellt wird, die ScaledWidth- und ScaledHeight-Eigenschaften festgelegt und an den Aufruf von GetSoftwareBitmapAsync übergeben werden, der die decodierte und skalierte SoftwareBitmap zurückgibt.
IRandomAccessStream fileStream = await photoFile.OpenAsync(FileAccessMode.Read);
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
BitmapTransform transform = new BitmapTransform();
const float sourceImageHeightLimit = 1280;
if (decoder.PixelHeight > sourceImageHeightLimit)
{
float scalingFactor = (float)sourceImageHeightLimit / (float)decoder.PixelHeight;
transform.ScaledWidth = (uint)Math.Floor(decoder.PixelWidth * scalingFactor);
transform.ScaledHeight = (uint)Math.Floor(decoder.PixelHeight * scalingFactor);
}
SoftwareBitmap sourceBitmap = await decoder.GetSoftwareBitmapAsync(decoder.BitmapPixelFormat, BitmapAlphaMode.Premultiplied, transform, ExifOrientationMode.IgnoreExifOrientation, ColorManagementMode.DoNotColorManage);
In der aktuellen Version unterstützt die FaceDetector-Klasse nur Bilder in Gray8 oder Nv12. Die SoftwareBitmap-Klasse stellt die Convert-Methode bereit, die eine Bitmap von einem Format in ein anderes konvertiert. In diesem Beispiel wird das Quellbild in das Pixelformat Gray8 konvertiert, wenn es sich nicht bereits in diesem Format befindet. Wenn Sie möchten, können Sie die Methoden GetSupportedBitmapPixelFormats und IsBitmapPixelFormatSupported verwenden, um zur Laufzeit zu ermitteln, ob ein Pixelformat unterstützt wird, falls der Satz unterstützter Formate in zukünftigen Versionen erweitert wird.
// Use FaceDetector.GetSupportedBitmapPixelFormats and IsBitmapPixelFormatSupported to dynamically
// determine supported formats
const BitmapPixelFormat faceDetectionPixelFormat = BitmapPixelFormat.Gray8;
SoftwareBitmap convertedBitmap;
if (sourceBitmap.BitmapPixelFormat != faceDetectionPixelFormat)
{
convertedBitmap = SoftwareBitmap.Convert(sourceBitmap, faceDetectionPixelFormat);
}
else
{
convertedBitmap = sourceBitmap;
}
Instanziieren Sie das FaceDetector-Objekt, indem Sie CreateAsync aufrufen und dann DetectFacesAsync aufrufen und die Bitmap übergeben, die auf eine angemessene Größe skaliert und in ein unterstütztes Pixelformat konvertiert wurde. Diese Methode gibt eine Liste von DetectedFace-Objekten zurück. ShowDetectedFaces ist eine Hilfsmethode, die unten gezeigt wird, die Quadrate um die Gesichter im Bild zeichnet.
if (faceDetector == null)
{
faceDetector = await FaceDetector.CreateAsync();
}
detectedFaces = await faceDetector.DetectFacesAsync(convertedBitmap);
ShowDetectedFaces(sourceBitmap, detectedFaces);
Stellen Sie sicher, dass Sie die Objekte löschen, die während des Gesichtserkennungsprozesses erstellt wurden.
sourceBitmap.Dispose();
fileStream.Dispose();
convertedBitmap.Dispose();
Um das Bild anzuzeigen und Felder um die erkannten Gesichter zu zeichnen, fügen Sie der XAML-Seite ein Canvas-Element hinzu.
<Canvas x:Name="VisualizationCanvas" Visibility="Visible" Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
Definieren Sie einige Membervariablen, um die gezeichneten Quadrate zu formatieren.
private readonly SolidColorBrush lineBrush = new SolidColorBrush(Windows.UI.Colors.Yellow);
private readonly double lineThickness = 2.0;
private readonly SolidColorBrush fillBrush = new SolidColorBrush(Windows.UI.Colors.Transparent);
In der ShowDetectedFaces-Hilfsmethode wird ein neuer ImageBrush erstellt, und die Quelle wird auf eine SoftwareBitmapSource festgelegt, die aus der SoftwareBitmap erstellt wird, die das Quellbild darstellt. Der Hintergrund des XAML-Canvas-Steuerelements wird auf den Bildpinsel festgelegt.
Wenn die an die Hilfsmethode übergebene Liste der Gesichter nicht leer ist, durchlaufen Sie die einzelnen Gesichter in der Liste, und verwenden Sie die FaceBox-Eigenschaft der DetectedFace-Klasse , um die Position und Größe des Rechtecks innerhalb des Bilds zu bestimmen, das das Gesicht enthält. Da das Canvas-Steuerelement sehr wahrscheinlich eine andere Größe als das Quellbild aufweist, sollten Sie sowohl die X- als auch die Y-Koordinaten und die Breite und Höhe des FaceBox-Steuerelements mit einem Skalierungswert multiplizieren, der das Verhältnis der Quellbildgröße auf die tatsächliche Größe des Canvas-Steuerelements darstellt.
private async void ShowDetectedFaces(SoftwareBitmap sourceBitmap, IList<DetectedFace> faces)
{
ImageBrush brush = new ImageBrush();
SoftwareBitmapSource bitmapSource = new SoftwareBitmapSource();
await bitmapSource.SetBitmapAsync(sourceBitmap);
brush.ImageSource = bitmapSource;
brush.Stretch = Stretch.Fill;
this.VisualizationCanvas.Background = brush;
if (detectedFaces != null)
{
double widthScale = sourceBitmap.PixelWidth / this.VisualizationCanvas.ActualWidth;
double heightScale = sourceBitmap.PixelHeight / this.VisualizationCanvas.ActualHeight;
foreach (DetectedFace face in detectedFaces)
{
// Create a rectangle element for displaying the face box but since we're using a Canvas
// we must scale the rectangles according to the image’s actual size.
// The original FaceBox values are saved in the Rectangle's Tag field so we can update the
// boxes when the Canvas is resized.
Rectangle box = new Rectangle();
box.Tag = face.FaceBox;
box.Width = (uint)(face.FaceBox.Width / widthScale);
box.Height = (uint)(face.FaceBox.Height / heightScale);
box.Fill = this.fillBrush;
box.Stroke = this.lineBrush;
box.StrokeThickness = this.lineThickness;
box.Margin = new Thickness((uint)(face.FaceBox.X / widthScale), (uint)(face.FaceBox.Y / heightScale), 0, 0);
this.VisualizationCanvas.Children.Add(box);
}
}
}
Nachverfolgen von Gesichtern in einer Abfolge von Frames
Wenn Sie Gesichter im Video erkennen möchten, ist es effizienter, die FaceTracker-Klasse anstelle der FaceDetector-Klasse zu verwenden, obwohl die Implementierungsschritte sehr ähnlich sind. Der FaceTracker verwendet Informationen zu zuvor verarbeiteten Frames, um den Erkennungsprozess zu optimieren.
using Windows.Media;
using System.Threading;
using Windows.System.Threading;
Deklarieren Sie eine Klassenvariable für das FaceTracker-Objekt . In diesem Beispiel wird ein ThreadPoolTimer verwendet, um die Gesichtsverfolgung in einem definierten Intervall zu initiieren. Ein SemaphoreSlim wird verwendet, um sicherzustellen, dass jeweils nur ein Gesichtsverfolgungsvorgang ausgeführt wird.
private FaceTracker faceTracker;
private ThreadPoolTimer frameProcessingTimer;
private SemaphoreSlim frameProcessingSemaphore = new SemaphoreSlim(1);
Um den Gesichtsverfolgungsvorgang zu initialisieren, erstellen Sie ein neues FaceTracker-Objekt, indem Sie CreateAsync aufrufen. Initialisieren Sie das gewünschte Zeitgeberintervall, und erstellen Sie dann den Timer. Die ProcessCurrentVideoFrame-Hilfsmethode wird jedes Mal aufgerufen, wenn das angegebene Intervall verstrichen ist.
this.faceTracker = await FaceTracker.CreateAsync();
TimeSpan timerInterval = TimeSpan.FromMilliseconds(66); // 15 fps
this.frameProcessingTimer = Windows.System.Threading.ThreadPoolTimer.CreatePeriodicTimer(new Windows.System.Threading.TimerElapsedHandler(ProcessCurrentVideoFrame), timerInterval);
Die ProcessCurrentVideoFrame-Hilfsprogramm wird asynchron vom Timer aufgerufen. Daher ruft die Methode zuerst die Wait-Methode des Semaphors auf, um festzustellen, ob ein Nachverfolgungsvorgang ausgeführt wird, und wenn die Methode zurückgegeben wird, ohne zu versuchen, Gesichter zu erkennen. Am Ende dieser Methode wird die Release-Methode des Semaphors aufgerufen, wodurch der nachfolgende Aufruf von ProcessCurrentVideoFrame fortgesetzt werden kann.
Die FaceTracker-Klasse wird auf VideoFrame-Objekten ausgeführt. Es gibt mehrere Möglichkeiten, einen VideoFrame abzurufen, einschließlich der Aufnahme eines Vorschauframes aus einem ausgeführten MediaCapture-Objekt oder durch Implementieren der ProcessFrame-Methode des IBasicVideoEffect. In diesem Beispiel wird eine nicht definierte Hilfsmethode verwendet, die einen Videoframe, GetLatestFrame, als Platzhalter für diesen Vorgang zurückgibt. Informationen zum Abrufen von Videoframes aus dem Vorschaudatenstrom eines ausgeführten Medienaufnahmegeräts finden Sie unter Abrufen eines Vorschauframes.
Wie bei FaceDetector unterstützt faceTracker einen begrenzten Satz von Pixelformaten. In diesem Beispiel wird die Gesichtserkennung abgebrochen, wenn sich der angegebene Frame nicht im Nv12-Format befindet.
Rufen Sie ProcessNextFrameAsync auf, um eine Liste der DetectedFace-Objekte abzurufen, die die Gesichter im Frame darstellen. Nachdem Sie über die Liste der Gesichter verfügen, können Sie sie auf die gleiche Weise wie oben beschrieben für die Gesichtserkennung anzeigen. Beachten Sie, dass sie in einem Aufruf von CoreDispatcher.RunAsync alle UI-Aktualisierungen vornehmen müssen, da die Face Tracking-Hilfsmethode nicht im UI-Thread aufgerufen wird.
public async void ProcessCurrentVideoFrame(ThreadPoolTimer timer)
{
if (!frameProcessingSemaphore.Wait(0))
{
return;
}
VideoFrame currentFrame = await GetLatestFrame();
// Use FaceDetector.GetSupportedBitmapPixelFormats and IsBitmapPixelFormatSupported to dynamically
// determine supported formats
const BitmapPixelFormat faceDetectionPixelFormat = BitmapPixelFormat.Nv12;
if (currentFrame.SoftwareBitmap.BitmapPixelFormat != faceDetectionPixelFormat)
{
return;
}
try
{
IList<DetectedFace> detectedFaces = await faceTracker.ProcessNextFrameAsync(currentFrame);
var previewFrameSize = new Windows.Foundation.Size(currentFrame.SoftwareBitmap.PixelWidth, currentFrame.SoftwareBitmap.PixelHeight);
var ignored = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
this.SetupVisualization(previewFrameSize, detectedFaces);
});
}
catch (Exception e)
{
// Face tracking failed
}
finally
{
frameProcessingSemaphore.Release();
}
currentFrame.Dispose();
}