2016 年 11 月

第 31 卷,第 11 期

此文章由机器翻译。

现代应用 - 将面部识别功能添加到你的应用

通过 Frank La La

Frank La Vigne在生成 2016,Microsoft 宣布推出认知服务 API 的版本。可用的众多 Api 中包括多个计算机构想服务。这些服务可以分析年龄和性别的输入图像中的平面。用于检测基于其面部表情的个人的情感甚至还有一个 API。为了突出显示该技术,出现了整个演示这一技术的各种用法的事件空间大量网亭。认知服务 API 利用了 Microsoft 的经验和机器学习的空间中的工作。通过神经网络被送入数千个标记的图像。最重要的是,您可以利用的机器学习或人工智能任何不知情的情况下这些服务。您只需从你的应用程序调用 Web 服务。您可以与一个参与项目的团队成员,若要了解有关在过程的详细监视面试 bit.ly/1TGi1QK。 

认知 Services Api,您可以添加基本面部检测到您的应用程序而不会调用任何 Api。Windows.Media.FaceAnalysis 命名空间包含用于检测中图像或视频的表面功能。功能集是基本的和缺少的认知服务丰富的数据集。事实上,它是非常类似于面部检测在许多数字照相机中找到。虽然基本功能,它们具有两个明显的优势︰ 脱机工作并不调用一个 API,因为将不会产生任何费用。作为优化策略,您的应用程序可以调用认知服务 API 之前,本地检测表面的状态。这种方式应用程序不会将没有表面图像发送到认知服务 API。可以为您和您的用户的减少的带宽使用率达到节省成本。本地检测面可以是到智能云服务,如认知服务有用补充。

设置项目

在 Visual Studio 2015 中,创建一个新的通用 Windows 平台 (UWP) 应用程序项目,选择空白模板,并将其命名 FaceDetection。因为应用程序将使用网络摄像头,必须将该功能添加到应用程序。在“解决方案资源管理器”中,双击 Package.appxmanifest 文件。在功能选项卡中,检查麦克风和网络摄像机,旁边的复选框中所示 图 1。保存文件。

将网络摄像头和麦克风功能添加到应用程序
图 1 将网络摄像头和麦克风功能添加到应用程序

现在,将以下 XAML 添加到要创建用户界面的 MainPage.xaml 文件中︰

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  <Grid.RowDefinitions>
    <RowDefinition Height="320*"/>
    <RowDefinition Height="389*"/>
  </Grid.RowDefinitions>
  <CaptureElement Name="cePreview" Stretch="Uniform" Grid.Row="0" />
  <Canvas x:Name="cvsFaceOverlay" Grid.Row="0" ></Canvas>
  <StackPanel Grid.Row="1" HorizontalAlignment="Center" Margin="5">
    <Button x:Name="btnCamera" Click="btnCamera_Click" >Turn on Camera</Button>
    <Button x:Name="btnDetectFaces" Click="btnDetectFaces_Click" >Detect
      Faces</Button>
  </StackPanel>
</Grid>

您可能不熟悉 CaptureElement 控件。CaptureElement 控件呈现来自连接的捕捉设备,通常的照相机或网络摄像头的流。在代码隐藏中,您将使用 MediaCapture API 将它连接到流中,从网络摄像头。

预览视频从摄像机

在 MainPage.xaml.cs 文件中添加以下命名空间︰

using Windows.Media.Capture;
using Windows.Media.Core;
using Windows.Media.FaceAnalysis;

接下来,将两个以下成员添加到 MainPage 类︰

private FaceDetectionEffect _faceDetectionEffect;
private MediaCapture _mediaCapture;
private IMediaEncodingProperties _previewProperties;

现在,添加以下事件处理程序启动照相机按钮︰

private async void btnCamera_Click(object sender, RoutedEventArgs e)
  {
    _mediaCapture = new MediaCapture();
    await _mediaCapture.InitializeAsync();
    cePreview.Source = _mediaCapture;
    await _mediaCapture.StartPreviewAsync();
  }

运行该项目,然后单击启动照相机按钮。现在应看到您的应用程序中的网络摄像机的输出。如果您没有网络摄像机连接到您的系统,将引发异常。

跟踪面

使用 CaptureElement 控件成功流式处理从网络摄像头的视频,现在就可以开始跟踪表面。跟踪面要求至少创建一个 FaceDetectionDefinition 对象,该对象上设置一些属性,然后将其连接到 steams CaptureElement 视频的 _mediaCapture 对象创建。

在事件处理程序检测 Faces 按钮中,添加以下代码︰

private async void btnDetectFaces_Click(object sender, RoutedEventArgs e)
{
  var faceDetectionDefinition = new FaceDetectionEffectDefinition();
  faceDetectionDefinition.DetectionMode = FaceDetectionMode.HighPerformance;
  faceDetectionDefinition.SynchronousDetectionEnabled = false;
  _faceDetectionEffect = (FaceDetectionEffect) await    
  _mediaCapture.AddVideoEffectAsync(faceDetectionDefinition,
    MediaStreamType.VideoPreview);
  _faceDetectionEffect.FaceDetected += FaceDetectionEffect_FaceDetected;
  _faceDetectionEffect.DesiredDetectionInterval = TimeSpan.FromMilliseconds(33);
  _faceDetectionEffect.Enabled = true;
}

此代码创建一个 FaceDetectionDefinition 对象,它针对性能进行了优化。可以在其中 DetectionMode 设置为高性能的代码行中出现此消息。FaceDetectionMode 枚举具有三个成员︰ 高性能需要速度优先准确性、 HighQuality 优先速度、 满足准确性和平衡查找准确性和速度的折衷。下的一行代码不会在运行检测算法的正面时延迟传入的视频帧。这会使预览视频平稳运行。

接下来,FaceDetectionDefinition 被添加到该 MediaCapture 对象,以及在流中指定的媒体类型的枚举。添加后,将返回一个 FaceDetectionEffect 对象。此对象具有当检测到一种外观,将引发一个 FaceDetected 事件、 设置人脸检测的频率的 DesiredDetectionInterval 属性和一个已启用属性,用于启用或禁用人脸检测。

绘图面周围的矩形

现在,已添加到 MediaCapture 对象并启用 FaceDetectionEffect,就可以将代码添加到 FaceDetection 事件处理程序︰

private async void FaceDetectionEffect_FaceDetected(
  FaceDetectionEffect sender, FaceDetectedEventArgs args)
{
  var detectedFaces = args.ResultFrame.DetectedFaces;
  await Dispatcher
    .RunAsync(CoreDispatcherPriority.Normal, 
      () => DrawFaceBoxes(detectedFaces));
}

在另一线程上运行时此事件,必须使用调度程序对 UI 线程进行更改。下的一行代码将循环访问的检测到的平面 IReadOnlyList。每个检测到的面有面临的已检测到的映像的位置的边界框。基于这些数据,创建一个新的矩形对象随后添加到表面覆盖画布上,如中所示 图 2

图 2 的表面添加矩形对象覆盖画布

private void DrawFaceBoxes(IReadOnlyList<DetectedFace> detectedFaces)
{
  cvsFaceOverlay.Children.Clear();
  for (int i = 0; i < detectedFaces.Count; i++)
  {
    var face = detectedFaces[i];
    var faceBounds = face.FaceBox;
    Rectangle faceHighlightRectangle= new Rectangle()
    {
     Height = faceBounds.Height,
     Width = faceBounds.Width
    };
    Canvas.SetLeft(faceHighlightRectangle, faceBounds.X);
    Canvas.SetTop(faceHighlightRectangle, faceBounds.Y);
    faceHighlightRectangle.StrokeThickness = 2;
    faceHighlightRectangle.Stroke = new SolidColorBrush(Colors.Red);
    cvsFaceOverlay.Children.Add(faceHighlightRectangle);
  }
}

现在运行解决方案,您会看到一些有关矩形,为"关闭"中所示 图 3

检测到表面,但在 UI 中的位置不是右
图 3 字体检测到,但在 UI 中的位置不是右

查找正确的偏移量

矩形下处于关闭状态的原因是栈顶开始面临检测算法像素网格左侧的媒体流并不表示它在 UI 中所示。您还必须考虑从摄像头的视频源的分辨率可能不同于在 UI 中解析。将该矩形放在适当的位置,则必须采取的位置和用户界面和视频流刻度差异考虑在内。若要实现此目的,您将添加将执行工作的两个函数︰ MapRectangleToDetectedFace 和 LocatePreviewStreamCoordinates 中。

第一步是检索预览流有关的信息。通过强制转换到 VideoEncodingProperties 对象类宽 _previewProperties 执行此操作。VideoEncodingProperties 描述视频流的格式。首先,您想要知道 stream 的高度和宽度。通过该信息,您可以确定纵横比的媒体流,以及是否有不同于 CaptureElement 控件。

LocatePreviewStreamCoordinates 方法比较媒体流高度和宽度设置为 CaptureElement 的控件。  具体取决于这两者之间的长宽比差异,这三种情况之一可能如下︰ 纵横比为相同,将有未进行调整。如果长宽比不同,则会添加建立信箱。如果 CaptureElement 的长宽比大于媒体流纵横比,然后建立信箱将添加到边。

如果将建立信箱添加到边,则必须调整表面矩形的 X 坐标。如果媒体流长宽比大于 CaptureElement,然后建立信箱添加的上方和下方视频。然后在这种情况下,必须调整表面矩形的 Y 坐标。

与考虑建立信箱放置,您现在必须确定缩放的媒体流与用户界面中的 CaptureElement 控件之间的差异。用矩形,有四个元素来设置︰ 顶端、 左侧、 宽度和高度。Top 和 left 是依赖项属性。依赖项属性的好好了解一下,阅读的文章 bit.ly/2bqvsVY

同样,运行该解决方案,如中所示 图 4, ,您应该看到要进行更为准确,如中所示的字体突出显示矩形的放置 图 5

图 4 代码,以计算正确的偏移量

private Rectangle MapRectangleToDetectedFace(BitmapBounds detectedfaceBoxCoordinates)
  {
    var faceRectangle = new Rectangle();
    var previewStreamPropterties =
      _previewProperties as VideoEncodingProperties;
    double mediaStreamWidth = previewStreamPropterties.Width;
    double mediaStreamHeight = previewStreamPropterties.Height;
    var faceHighlightRect = LocatePreviewStreamCoordinates(previewStreamPropterties,
      this.cePreview);
    faceRectangle.Width = (detectedfaceBoxCoordinates.Width / mediaStreamWidth) *
      faceHighlightRect.Width;
    faceRectangle.Height = (detectedfaceBoxCoordinates.Height / mediaStreamHeight) *
      faceHighlightRect.Height;
    var x = (detectedfaceBoxCoordinates.X / mediaStreamWidth) *
      faceHighlightRect.Width;
    var y = (detectedfaceBoxCoordinates.Y / mediaStreamHeight) *
      faceHighlightRect.Height;
    Canvas.SetLeft(faceRectangle, x);
    Canvas.SetTop(faceRectangle, y);
    return faceRectangle;
  }
  public Rect LocatePreviewStreamCoordinates(
    VideoEncodingProperties previewResolution,
    CaptureElement previewControl)
  {
    var uiRectangle = new Rect();
    var mediaStreamWidth = previewResolution.Width;
    var mediaStreamHeight = previewResolution.Height;
    uiRectangle.Width = previewControl.ActualWidth;
    uiRectangle.Height = previewControl.ActualHeight;
    var uiRatio = previewControl.ActualWidth / previewControl.ActualHeight;
    var mediaStreamRatio = mediaStreamWidth / mediaStreamHeight;
    if (uiRatio > mediaStreamRatio)
    {
     var scaleFactor = previewControl.ActualHeight / mediaStreamHeight;
     var scaledWidth = mediaStreamWidth * scaleFactor;
     uiRectangle.X = (previewControl.ActualWidth - scaledWidth) / 2.0;
     uiRectangle.Width = scaledWidth;
    }
    else
     {
      var scaleFactor = previewControl.ActualWidth / mediaStreamWidth;
      var scaledHeight = mediaStreamHeight * scaleFactor;
      uiRectangle.Y = (previewControl.ActualHeight - scaledHeight) / 2.0;
      uiRectangle.Height = scaledHeight;
     }
    return uiRectangle;
    }

更准确地放置面临突出显示矩形的
图 5 更准确地放置面临突出显示矩形的

正在停止人脸检测

人脸检测占用的处理能力,并在电池供电的移动设备,可能导致大大降低的电池寿命。一旦具有人脸检测后,您可能希望为用户提供选项来将其关闭。

幸运的是,关闭人脸检测其实非常简单。首先,将按钮添加到 StackPanel MainPage.xaml 文件中︰

<Button x:Name="btnStopDetection" Click="btnStopDetection_Click">Stop
  Detecting Faces</Button>

现在,将以下代码添加到的事件处理程序的停止检测面临着按钮︰

private async void btnStopDetection_Click(object sender, RoutedEventArgs e)
  {
    _faceDetectionEffect.Enabled = false;
    _faceDetectionEffect.FaceDetected -= FaceDetectionEffect_FaceDetected;
    await _mediaCapture.ClearEffectsAsync(MediaStreamType.VideoPreview);
    _faceDetectionEffect = null;
  }

该代码实质上是撤消在安装过程。禁用面临检测效果、 FaceDetected 事件是取消订阅事件处理程序,并且效果清除从媒体捕获对象。最后,_faceDetectionEffect 设置为 null 以释放内存。

现在运行该项目。单击启动照相机按钮,然后检测 Faces 和,停止检测朝上一次。您会注意到,即使应用程序不再检测表面,也是如此还有矩形表面检测到的最后一个位置中。这个问题修好了。

停止此应用程序并返回到 btnStopDetection_Click 事件处理程序并添加以下行代码即可清除 cvsFacesOverlay 画布的内容︰

this.cvsFaceOverlay.Children.Clear();

再次运行该解决方案并重复执行的所有步骤。现在,当关闭人脸检测时,是没有矩形突出显示。

总结

认知服务 Api 提供轻松访问功能强大的计算机构想算法。所有云服务一样,但是,需要 Internet 访问工作。对于某些需要脱机方案的使用情况,您仍然可以通过使用内置于 UWP 面临检测 Api 执行基本面部检测。 

脱机的用例可以包括将置于网络摄像机连接到远程位置中运行 Windows IoT Core Raspberry Pi 2。在检测到一个表面时应用程序然后将本地保存图像。收集设备中的数据时,无法然后将图像上载到进行高级分析的认知服务中。

执行本地人脸检测还通过允许开发人员,在它们的外观的图像仅上载优化带宽传输和云服务使用情况。简单地说,本地人脸检测可以增加 online 方案,并为提供强大支持脱机使用。

有关更深入的了解,请务必在 GitHub 上查看 UWP 示例应用程序中的 CameraFaceDetection 示例 (bit.ly/2b27gLk)。


Frank La Vigne 是 Microsoft 技术和市政参与团队的技术推广人员,他可以帮助用户利用技术来创建更好的社区。 他定期在 FranksWorld.com 上发布博客,且拥有一个称为“Frank’s World TV”(youtube.com/FranksWorldTV) 的 YouTube 频道。

衷心感谢以下技术专家对本文的审阅: Rachel Appel