Поделиться через


Обнаружение лиц на изображениях или в видео

В этом разделе показано, как использовать 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();
}