Mendeteksi wajah dalam gambar atau video

Topik ini menunjukkan cara menggunakan FaceDetector untuk mendeteksi wajah dalam gambar. FaceTracker dioptimalkan untuk melacak wajah dari waktu ke waktu dalam urutan bingkai video.

Untuk metode alternatif pelacakan wajah menggunakan FaceDetectionEffect, lihat Analisis adegan untuk pengambilan media.

Kode dalam artikel ini diadaptasi dari sampel Deteksi Wajah Dasar dan Pelacakan Wajah Dasar . Anda dapat mengunduh sampel ini untuk melihat kode yang digunakan dalam konteks atau menggunakan sampel sebagai titik awal untuk aplikasi Anda sendiri.

Mendeteksi wajah dalam satu gambar

Kelas FaceDetector memungkinkan Anda mendeteksi satu atau beberapa wajah dalam gambar diam.

Contoh ini menggunakan API dari ruang nama berikut.

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;

Mendeklarasikan variabel anggota kelas untuk objek FaceDetector dan untuk daftar objek DetectedFace yang akan terdeteksi dalam gambar.

FaceDetector faceDetector;
IList<DetectedFace> detectedFaces;

Deteksi wajah beroperasi pada objek SoftwareBitmap yang dapat dibuat dengan berbagai cara. Dalam contoh ini FileOpenPicker digunakan untuk memungkinkan pengguna memilih file gambar di mana wajah akan terdeteksi. Untuk informasi selengkapnya tentang bekerja dengan bitmap perangkat lunak, lihat Pencitraan.

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;
}

Gunakan kelas BitmapDecoder untuk memecahkan kode file gambar menjadi SoftwareBitmap. Proses deteksi wajah lebih cepat dengan gambar yang lebih kecil sehingga Anda mungkin ingin menskalakan gambar sumber ke ukuran yang lebih kecil. Ini dapat dilakukan selama decoding dengan membuat objek BitmapTransform , mengatur properti ScaledWidth dan ScaledHeight dan meneruskannya ke panggilan ke GetSoftwareBitmapAsync, yang mengembalikan SoftwareBitmap yang diterjemahkan dan diskalakan.

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);

Dalam versi saat ini, kelas FaceDetector hanya mendukung gambar di Gray8 atau Nv12. Kelas SoftwareBitmap menyediakan metode Konversi , yang mengonversi bitmap dari satu format ke format lainnya. Contoh ini mengubah gambar sumber menjadi format piksel Gray8 jika belum dalam format itu. Jika mau, Anda dapat menggunakan metode GetSupportedBitmapPixelFormats dan IsBitmapPixelFormatSupported untuk menentukan saat runtime jika format piksel didukung, jika kumpulan format yang didukung diperluas di versi mendatang.

// 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;
}

Instantiate objek FaceDetector dengan memanggil CreateAsync dan kemudian memanggil DetectFacesAsync, meneruskan bitmap yang telah diskalakan ke ukuran yang wajar dan dikonversi ke format piksel yang didukung. Metode ini mengembalikan daftar objek DetectedFace . ShowDetectedFaces adalah metode pembantu, yang ditunjukkan di bawah ini, yang menggambar kotak di sekitar wajah dalam gambar.

if (faceDetector == null)
{
    faceDetector = await FaceDetector.CreateAsync();
}

detectedFaces = await faceDetector.DetectFacesAsync(convertedBitmap);
ShowDetectedFaces(sourceBitmap, detectedFaces);

Pastikan untuk membuang benda-benda yang dibuat selama proses deteksi wajah.

sourceBitmap.Dispose();
fileStream.Dispose();
convertedBitmap.Dispose();

Untuk menampilkan gambar dan menggambar kotak di sekitar wajah yang terdeteksi, tambahkan elemen Kanvas ke halaman XAML Anda.

<Canvas x:Name="VisualizationCanvas" Visibility="Visible" Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>

Tentukan beberapa variabel anggota untuk menata kotak yang akan digambar.

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);

Dalam metode pembantu ShowDetectedFaces , ImageBrush baru dibuat dan sumber diatur ke SoftwareBitmapSource yang dibuat dari SoftwareBitmap yang mewakili gambar sumber. Latar belakang kontrol XAML Canvas diatur ke kuas gambar.

Jika daftar wajah yang diteruskan ke metode pembantu tidak kosong, putar setiap wajah dalam daftar dan gunakan properti FaceBox dari kelas DetectedFace untuk menentukan posisi dan ukuran persegi panjang dalam gambar yang berisi wajah. Karena kontrol Canvas sangat mungkin menjadi ukuran yang berbeda dari gambar sumber, Anda harus mengalikan koordinat X dan Y dan lebar dan tinggi FaceBox dengan nilai penskalaan yang merupakan rasio ukuran gambar sumber dengan ukuran sebenarnya dari kontrol 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);
        }
    }
}

Lacak wajah dalam urutan bingkai

Jika Anda ingin mendeteksi wajah dalam video, lebih efisien menggunakan kelas FaceTracker daripada kelas FaceDetector , meskipun langkah implementasinya sangat mirip. FaceTracker menggunakan informasi tentang frame yang diproses sebelumnya untuk mengoptimalkan proses deteksi.

using Windows.Media;
using System.Threading;
using Windows.System.Threading;

Mendeklarasikan variabel kelas untuk objek FaceTracker . Contoh ini menggunakan ThreadPoolTimer untuk memulai pelacakan wajah pada interval yang ditentukan. SemaphoreSlim digunakan untuk memastikan bahwa hanya satu operasi pelacakan wajah yang berjalan pada satu waktu.

private FaceTracker faceTracker;
private ThreadPoolTimer frameProcessingTimer;
private SemaphoreSlim frameProcessingSemaphore = new SemaphoreSlim(1);

Untuk menginisialisasi operasi pelacakan wajah, buat objek FaceTracker baru dengan memanggil CreateAsync. Menginisialisasi interval timer yang diinginkan dan kemudian buat timer. Metode pembantu ProcessCurrentVideoFrame akan dipanggil setiap kali interval yang ditentukan berlalu.

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);

Pembantu ProcessCurrentVideoFrame dipanggil secara asinkron oleh timer, sehingga metode ini pertama kali memanggil metode Tunggu semaphore untuk melihat apakah operasi pelacakan sedang berlangsung, dan apakah itu adalah metode yang kembali tanpa mencoba mendeteksi wajah. Pada akhir metode ini, metode Rilis semaphore disebut, yang memungkinkan panggilan berikutnya ke ProcessCurrentVideoFrame untuk melanjutkan.

Kelas FaceTracker beroperasi pada objek VideoFrame . Ada beberapa cara Anda bisa mendapatkan VideoFrame termasuk menangkap bingkai pratinjau dari objek MediaCapture yang sedang berjalan atau dengan menerapkan metode ProcessFrame dari IBasicVideoEffect. Contoh ini menggunakan metode pembantu yang tidak terdefinisi yang mengembalikan bingkai video, GetLatestFrame, sebagai placeholder untuk operasi ini. Untuk informasi tentang mendapatkan bingkai video dari aliran pratinjau perangkat pengambilan media yang sedang berjalan, lihat Mendapatkan bingkai pratinjau.

Seperti halnya FaceDetector, FaceTracker mendukung serangkaian format piksel terbatas. Contoh ini meninggalkan deteksi wajah jika bingkai yang disediakan tidak dalam format Nv12.

Panggil ProcessNextFrameAsync untuk mengambil daftar objek DetectedFace yang mewakili wajah dalam bingkai. Setelah Anda memiliki daftar wajah, Anda dapat menampilkannya dengan cara yang sama seperti yang dijelaskan di atas untuk deteksi wajah. Perhatikan bahwa, karena metode pembantu pelacakan wajah tidak dipanggil pada utas UI, Anda harus membuat pembaruan UI apa pun dalam panggilan 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();
}