Обнаружение лиц на изображениях или в видео
В этом разделе показано, как использовать FaceDetector для обнаружения лиц на изображении. FaceTracker оптимизирован для отслеживания лиц с течением времени в последовательности видеокадров.
Альтернативный метод отслеживания лиц с помощью FaceDetectionEffect см. в разделе "Анализ сцены" для отслеживания мультимедиа.
Код, приведенный в этой статье, был адаптирован из примеров "Базовое обнаружение лиц" и "Базовое отслеживание лиц". Эти примеры можно скачать, чтобы просмотреть код, используемый в контексте, или использовать этот пример в качестве отправной точки для собственного приложения.
Обнаружение лиц на одном изображении
Класс FaceDetector позволяет обнаруживать один или несколько лиц в по-прежнему изображении.
В этом примере используются API из следующих пространств имен.
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;
Объявите переменную члена класса для объекта FaceDetector и список объектов DetectedFace , которые будут обнаружены на изображении.
FaceDetector faceDetector;
IList<DetectedFace> detectedFaces;
Обнаружение лиц работает с объектом SoftwareBitmap , который можно создать различными способами. В этом примере FileOpenPicker используется для выбора файла изображения, в котором будут обнаружены лица. Дополнительные сведения о работе с растровыми изображениями программного обеспечения см. в разделе "Образы".
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;
}
Используйте класс BitmapDecoder, чтобы декодировать файл изображения в SoftwareBitmap. Процесс обнаружения лиц быстрее с меньшим изображением и поэтому может потребоваться уменьшить размер исходного изображения до меньшего размера. Это можно сделать во время декодирования путем создания объекта BitmapTransform, задания свойств ScaledWidth и ScaledHeight и передачи его в вызов GetSoftwareBitmapAsync, который возвращает декодированный и масштабируемый SoftwareBitmapmap.
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);
В текущей версии класс FaceDetector поддерживает только изображения в Gray8 или Nv12. Класс SoftwareBitmap предоставляет метод Convert , который преобразует растровое изображение из одного формата в другой. В этом примере исходный образ преобразуется в формат пикселей Gray8, если он еще не в этом формате. Если вы хотите, можно использовать методы GetSupportedBitmapPixelFormats и IsBitmapPixelFormatSupported , чтобы определить, поддерживается ли формат пикселей во время выполнения, если поддерживается набор поддерживаемых форматов в будущих версиях.
// 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;
}
Создайте экземпляр объекта FaceDetector путем вызова CreateAsync и вызова DetectFacesAsync, передавая растровое изображение, масштабируемое до разумного размера и преобразованное в поддерживаемый формат пикселей. Этот метод возвращает список объектов DetectedFace . ShowDetectedFaces — это вспомогательный метод, показанный ниже, который рисует квадраты вокруг лиц на изображении.
if (faceDetector == null)
{
faceDetector = await FaceDetector.CreateAsync();
}
detectedFaces = await faceDetector.DetectFacesAsync(convertedBitmap);
ShowDetectedFaces(sourceBitmap, detectedFaces);
Не забудьте удалить объекты, созданные во время процесса обнаружения лиц.
sourceBitmap.Dispose();
fileStream.Dispose();
convertedBitmap.Dispose();
Чтобы отобразить изображение и нарисовать прямоугольники вокруг обнаруженных лиц, добавьте элемент Canvas на страницу XAML.
<Canvas x:Name="VisualizationCanvas" Visibility="Visible" Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
Определите некоторые переменные-члены для стиля квадратов, которые будут вырисовываться.
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);
В вспомогательном методе ShowDetectedFaces создается новый ImageBrush, а источник устанавливается на SoftwareBitmapSource, созданный из SoftwareBitmapmapMap, представляющего исходный образ. Фон элемента управления Canvas XAML устанавливается на кисть изображения.
Если список лиц, переданных в вспомогательный метод, не пуст, прокрутите по каждому лицу в списке и используйте свойство FaceBox класса DetectedFace для определения положения и размера прямоугольника в изображении, содержащего лицо. Так как элемент управления Canvas, скорее всего, отличается от размера исходного изображения, следует умножить координаты X и Y, а также ширину и высоту FaceBox на значение масштабирования, которое является соотношением размера исходного изображения к фактическому размеру элемента управления Canvas.
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);
}
}
}
Отслеживание лиц в последовательности кадров
Если вы хотите обнаружить лица в видео, более эффективно использовать класс FaceTracker, а не класс FaceDetector, хотя шаги реализации очень похожи. FaceTracker использует сведения о ранее обработанных кадрах для оптимизации процесса обнаружения.
using Windows.Media;
using System.Threading;
using Windows.System.Threading;
Объявите переменную класса для объекта FaceTracker . В этом примере используется ThreadPoolTimer для запуска отслеживания лиц в определенном интервале. SemaphoreSlim используется, чтобы убедиться, что одновременно выполняется только одна операция отслеживания лиц.
private FaceTracker faceTracker;
private ThreadPoolTimer frameProcessingTimer;
private SemaphoreSlim frameProcessingSemaphore = new SemaphoreSlim(1);
Чтобы инициализировать операцию отслеживания лиц, создайте новый объект FaceTracker, вызвав CreateAsync. Инициализируйте нужный интервал таймера и создайте таймер. Вспомогательный метод ProcessCurrentVideoFrame будет вызываться каждый раз, когда указанный интервал истекает.
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);
Вспомогательный метод ProcessCurrentVideoFrame вызывается асинхронно таймером, поэтому метод сначала вызывает метод wait семафора, чтобы узнать, продолжается ли операция отслеживания, и если метод возвращается, не пытаясь обнаружить лица. В конце этого метода вызывается метод выпуска семафора, который позволяет продолжить последующий вызов ProcessCurrentVideoFrame.
Класс FaceTracker работает с объектами VideoFrame. Существует несколько способов получения Кадра видео, включая запись предварительного кадра из запущенного объекта MediaCapture или реализации метода ProcessFrame IBasicVideoEffect. В этом примере используется неопределенный вспомогательный метод, возвращающий видеокадр, GetLatestFrame, в качестве заполнителя для этой операции. Сведения о получении видеокадров из потока предварительной версии работающего устройства захвата мультимедиа см. в разделе "Получение предварительного просмотра кадра".
Как и в Случае с FaceDetector, FaceTracker поддерживает ограниченный набор форматов пикселей. В этом примере отказывается от обнаружения лиц, если предоставленный кадр не находится в формате Nv12.
Вызовите ProcessNextFrameAsync, чтобы получить список объектов DetectedFace, представляющих лица в кадре. После получения списка лиц их можно отобразить таким же образом, как описано выше для обнаружения лиц. Обратите внимание, что, так как вспомогательный метод отслеживания лиц не вызывается в потоке пользовательского интерфейса, необходимо внести обновления пользовательского интерфейса в вызов CoreDispatcher.RunAsync.
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();
}