使用 UWP 面部检测 API 在 WPF 中进行面部检测






4.50/5 (2投票s)
使用 UWP 面部检测 API 在 WPF 中检测图像中的面部
引言
通用 Windows 平台的Windows.Media.FaceAnalysis
命名空间包含可用于检测图像文件和视频帧中人脸的 API。人脸检测 API 是一个可用于桌面应用程序的 UWP API,这得益于Windows 10 WinRT API 包。本文将探讨如何在 WPF 应用程序中使用 UWP 人脸检测 API,特别是用于检测图像文件中的面部。
必备组件
要学习本文内容,需要对 MVVM 模式有一定的了解。要运行示例项目,您应该安装以下内容:
- .NET Core 3.1
- Visual Studio 2019
背景
示例应用程序是一个 .NET Core WPF 项目,它引用了Microsoft.Windows.SDK.Contracts
包(Windows 10 WinRT API 包),这使桌面应用程序能够访问某些 UWP API。用户可以选择一个图像文件,然后单击“**检测人脸**”按钮来检测所选图像中的人脸。
人脸检测
要使用 UWP 人脸检测 API,您必须导入Windows.Media.FaceAnalysis
命名空间。在示例项目中,这在包含DetectFaces()
方法的FaceDetectionService
类中完成,该方法执行人脸检测过程。
public async Task<IList<DetectedFace>> DetectFaces(Stream fileStream)
{
var stream = fileStream.AsRandomAccessStream();
var bitmapDecoder = await BitmapDecoder.CreateAsync(stream);
using SoftwareBitmap bitmap = await bitmapDecoder.GetSoftwareBitmapAsync();
var bmp = FaceDetector.IsBitmapPixelFormatSupported(bitmap.BitmapPixelFormat)
? bitmap : SoftwareBitmap.Convert(bitmap, BitmapPixelFormat.Gray8);
var faceDetector = await FaceDetector.CreateAsync();
var detectedFaces = await faceDetector.DetectFacesAsync(bmp);
return detectedFaces;
}
FaceDetector
仅适用于SoftwareBitmap
,因此目标图像使用BitmapDecoder
转换为SoftwareBitmap
。然后检查位图像素格式,如果它不是当前设备上FaceDetector
支持的格式之一,则进行转换。DetectFacesAsync()
检测SoftwareBitmap
中的人脸,并返回DetectedFace
对象的集合。
标记人脸
为了使用边界框标记检测到的人脸,我使用了System.Drawing
命名空间中的Graphics
类。
public Bitmap DetectedFacesBitmap(Stream fileStream, IList<DetectedFace> detectedFaces,
Color boxColor, int strokeThickness = 2)
{
var bitmap = new Bitmap(fileStream);
using (var graphics = Graphics.FromImage(bitmap))
{
using var stroke = new Pen(boxColor, strokeThickness);
foreach (var face in detectedFaces)
{
BitmapBounds faceBox = face.FaceBox;
graphics.DrawRectangle(stroke, (int)faceBox.X, (int)faceBox.Y,
(int)faceBox.Width, (int)faceBox.Height);
}
}
return bitmap;
}
DetectedFace
包含一个名为FaceBox
的属性,该属性提供检测到的人脸的边界。绘制包含检测到人脸的图像上的矩形时,将使用这些边界。
ViewModel
示例项目遵循 MVVM 模式,并且只有一个视图模型 – MainWindowViewModel
。此视图模型包含两个属性;一个类型为string
,指定所选图像的路径;另一个类型为Bitmap
,用于处理后的图像。视图模型还包含用于执行图像选择和人脸检测的命令。
using System.Drawing;
using System.IO;
using System.Threading.Tasks;
using FaceDetection.Commands;
using FaceDetection.Services.Interfaces;
namespace FaceDetection.ViewModels
{
public class MainWindowViewModel : ViewModelBase
{
private readonly IDialogService dialogService;
private readonly IFaceDetectionService faceDetectionService;
public MainWindowViewModel(IDialogService dialogSvc,
IFaceDetectionService faceDetectionSvc)
{
dialogService = dialogSvc;
faceDetectionService = faceDetectionSvc;
}
private string _selectedImage;
public string SelectedImage
{
get => _selectedImage;
set
{
_selectedImage = value;
OnPropertyChanged();
}
}
#region Select Image Command
private RelayCommand _selectImageCommand;
public RelayCommand SelectImageCommand =>
_selectImageCommand ??= new RelayCommand(_ => SelectImage());
private void SelectImage()
{
var image = dialogService.PickFile("Select Image",
"Image (*.jpg; *.jpeg; *.png)|*.jpg; *.jpeg; *.png");
if (string.IsNullOrWhiteSpace(image)) return;
SelectedImage = image;
}
#endregion
private Bitmap _facesBitmap;
public Bitmap FacesBitmap
{
get => _facesBitmap;
set
{
_facesBitmap = value;
OnPropertyChanged();
}
}
#region Detect faces Command
private RelayCommandAsync _detectFacesCommand;
public RelayCommandAsync DetectFacesCommand =>
_detectFacesCommand ??= new RelayCommandAsync
(DetectFaces, _ => CanDetectFaces());
private async Task DetectFaces()
{
await using FileStream fileStream = File.OpenRead(_selectedImage);
var faces = await faceDetectionService.DetectFaces(fileStream);
FacesBitmap = faceDetectionService.DetectedFacesBitmap
(fileStream, faces, Color.GreenYellow);
SelectedImage = null;
}
private bool CanDetectFaces() => !string.IsNullOrWhiteSpace(SelectedImage);
#endregion
}
}
视图
使用MainWindow.xaml中Image
控件的数据触发器在所选图像和处理后的图像之间进行切换。
<Image Margin="10">
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="{Binding SelectedImage, Mode=OneWay}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedImage}" Value="{x:Null}">
<Setter Property="Source"
Value="{Binding FacesBitmap,
Converter={StaticResource BitmapConverter}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
由于FacesBitmap
的类型为Bitmap
,因此必须将其转换为BitmapSource
。这是使用转换器完成的。
public class BitmapToBitmapSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value is null) return null;
using var bitmap = (Bitmap)value;
using var stream = new MemoryStream();
bitmap.Save(stream, ImageFormat.Bmp);
stream.Position = 0;
var bmpImg = new BitmapImage();
bmpImg.BeginInit();
bmpImg.CacheOption = BitmapCacheOption.OnLoad;
bmpImg.StreamSource = stream;
bmpImg.EndInit();
bmpImg.Freeze();
return bmpImg;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
结论
如您所见,使用 UWP 人脸检测 API 非常简单。需要注意的是,虽然该 API 非常擅长检测人脸,但它可能无法检测部分可见的人脸,因为像素可能不足以供人脸检测器使用。
就是这样!如果您需要查看示例项目的其余代码,请使用本文顶部的下载链接克隆或下载代码库。
历史
- 2020 年 10 月 7 日:初始发布