话说现在检测人脸的技术有很多有在线 AI 服务, 比如 Megvii Face++,Microsoft Cognitive Services,Tencent AI 等等还有本地的库实现的, 比如 OpenCV
但是这些这篇文章都不讨论, 微软在 .NETCore 里面也提供了一种本地检测人脸的 API, 那就是 Windows.Media.FaceAnalysis
.NetCore 在你新建通用 UWP 应用的时候, Nuget 自动添加了
那么接下来, 我们在设计 Xaml 代码的时候, 加两个按钮, 一个是选择图片, 一个是检测人脸
再建一个 Canvas 控件, 用来显示图片
之所以用 Canvas 画布, 不用 Image, 是因为我们还需要在图片上画出一个矩形框, 框出识别的人脸位置和大小呢
- <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
- <Grid.RowDefinitions>
- <RowDefinition Height="30" />
- <RowDefinition/>
- </Grid.RowDefinitions>
- <Grid.ColumnDefinitions>
- <ColumnDefinition/>
- <ColumnDefinition/>
- </Grid.ColumnDefinitions>
- <Button Content="Choose Picture" Click="ChoosePicture" />
- <Button Grid.Column="1" Content="Detect Face" Click="DetectFace" />
- <Canvas x:Name="canvasDetected" Grid.ColumnSpan="2" Grid.Row="1" VerticalAlignment="Stretch"
- HorizontalAlignment="Stretch" />
- </Grid>
然后开始写代码, 选择图片的逻辑很简单, 只需要选择一个图片, 显示到 Canvas 中即可
- private async void ChoosePicture(object sender, RoutedEventArgs e)
- {
- FileOpenPicker openPicker = new FileOpenPicker();
- openPicker.ViewMode = PickerViewMode.Thumbnail;
- openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
- openPicker.FileTypeFilter.Add(".bmp");
- openPicker.FileTypeFilter.Add(".png");
- openPicker.FileTypeFilter.Add(".jpeg");
- openPicker.FileTypeFilter.Add(".jpg");
- StorageFile file = await openPicker.PickSingleFileAsync();
- if (file != null)
- {
- using (IRandomAccessStream strm = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
- {
- BitmapDecoder decoder = await BitmapDecoder.CreateAsync(strm);
- BitmapTransform transform = new BitmapTransform();
- source = await decoder.GetSoftwareBitmapAsync();
- WriteableBitmap displaySource = new WriteableBitmap(source.PixelWidth, source.PixelHeight);
- source.CopyToBuffer(displaySource.PixelBuffer);
- ImageBrush brush = new ImageBrush();
- brush.ImageSource = displaySource;
- brush.Stretch = Stretch.Uniform;
- canvasDetected.Background = brush;
- canvasDetected.Children.Clear();
- }
- }
- }
遇到红色波浪线提示的, 用 VS 自动修复功能, 自动添加引用即可
还有一个 source 没有定义, 不慌, 反正下一步就要检测人脸了, 我们来看一看 FaceDetector 的定义,
- namespace Windows.Media.FaceAnalysis
- {
/$/ 摘要:
- // 在 SoftwareBitmap 中检测人脸
- [ContractVersion(typeof(UniversalApiContract), 65536)]
- [MarshalingBehavior(MarshalingType.Agile)]
- [Static(typeof(IFaceDetectorStatics), 65536, "Windows.Foundation.UniversalApiContract")]
- [Threading(ThreadingModel.Both)]
- public sealed class FaceDetector : IFaceDetector
- {
/$/ 摘要:
// 异步检测提供的 SoftwareBitmap 中的人脸
/$/ 参数:
- // image:
- // 要进行人脸检测处理的图像数据
/$/ 返回结果:
- // 一个异步操作, 在成功完成时返回 DetectedFace 对象的列表
- [Overload("DetectFacesAsync")]
- [RemoteAsync]
- public IAsyncOperation<IList<DetectedFace>> DetectFacesAsync(SoftwareBitmap image);
- }
- }
看到没, 使用 FaceDetector 需要一个 SoftwareBitmap, 那么好了, 我们定义一个私有变量 SoftwareBitmap source 即可
然后写检测的代码,
- private async void DetectFace(object sender, RoutedEventArgs e)
- {
- const BitmapPixelFormat faceDetectionPixelFormat = BitmapPixelFormat.Gray8;
- SoftwareBitmap converted;
- if (source.BitmapPixelFormat != faceDetectionPixelFormat)
- {
- converted = SoftwareBitmap.Convert(source, faceDetectionPixelFormat);
- }
- else
- {
- converted = source;
- }
- FaceDetector faceDetector = await FaceDetector.CreateAsync();
- IList<DetectedFace> detectedFaces = await faceDetector.DetectFacesAsync(converted);
- DrawBoxes(detectedFaces);// 这个功能在实际场景中使用不多, 在这可以写你的实际业务场景
- }
画人脸矩形:
- // 这个功能在实际场景中使用不多
- private void DrawBoxes(IList<DetectedFace> detectedFaces)
- {
- if (detectedFaces != null)
- {
- //get the scaling factor
- double scaleWidth = source.PixelWidth / this.canvasDetected.ActualWidth;
- double scaleHeight = source.PixelHeight / this.canvasDetected.ActualHeight;
- double scalingFactor = scaleHeight> scaleWidth ? scaleHeight : scaleWidth;
- //get the display width of the image.
- double displayWidth = source.PixelWidth / scalingFactor;
- double displayHeight = source.PixelHeight / scalingFactor;
- //get the delta width/height between canvas actual width and the image display width
- double deltaWidth = this.canvasDetected.ActualWidth - displayWidth;
- double deltaHeight = this.canvasDetected.ActualHeight - displayHeight;
- SolidColorBrush lineBrush = new SolidColorBrush(Windows.UI.Colors.White);
- double lineThickness = 2.0;
- SolidColorBrush fillBrush = new SolidColorBrush(Windows.UI.Colors.Transparent);
- foreach (DetectedFace face in detectedFaces)
- {
- Rectangle box = new Rectangle();
- box.Tag = face.FaceBox;
- //scale the box with the scaling factor
- box.Width = face.FaceBox.Width / scalingFactor;
- box.Height = face.FaceBox.Height / scalingFactor;
- box.Fill = fillBrush;
- box.Stroke = lineBrush;
- box.StrokeThickness = lineThickness;
- //set coordinate of the box in the canvas
- box.Margin = new Thickness((uint)(face.FaceBox.X / scalingFactor + deltaWidth / 2), (uint)(face.FaceBox.Y / scalingFactor + deltaHeight / 2), 0, 0);
- this.canvasDetected.Children.Add(box);
- }
- }
- }
其实, 像上面的 DrawBoxes 注释那样, 一般用的还不算多
我的项目都是判断如果 detectedFaces 不是 null 的话, 接下来就可以调用云 API 来实现人脸搜索了, 毕竟这个本地微软的 api 还做不到
下面看一下效果
总结
微软提供的 FaceDetector 还是挺实用的, 毕竟可以节约我们一遍一遍像服务器发送请求检测人脸的开支了, 虽然云 API 检测人脸并不贵, face++ 的 10000 次才一块钱毕竟你上传图片, 还不要带宽资源吧万一碰到个网络不好, 那不是还要再请求一次哈哈, 折腾点
不过这个也随便了, 看自己喜好吧
来源: https://www.cnblogs.com/hupo376787/p/8694724.html