2016 年 11 月

第 31 卷,第 11 期

本文章是由機器翻譯。

新式應用程式 - 在應用程式新增臉部辨識功能

Frank La La

Frank La Vigne在組建 2016,Microsoft 已宣佈的認知服務 API。提供的許多 Api,其中包括數個電腦目標服務。年齡和性別輸入映像中的表面,就可以分析這些服務。可用來偵測根據其臉部表情的人才情感甚至還有 API。反白顯示這項技術,沒有在示範這項技術的各種用法事件空間內的許多 kiosk。認知服務 API 會運用 Microsoft 的經驗和學習的空間中的工作。類神經網路已送數千個標記的影像。最棒的是,您可以利用任何機器學習或人工智慧不知情的情況下,這些服務。您只要從您的應用程式呼叫 Web 服務。您可以觀看的訪談,參與專案的小組成員若要深入了解程序,在其中 bit.ly/1TGi1QK。 

與認知服務 Api,您可以加入基本臉部偵測到您的應用程式而不需要呼叫任何 Api。Windows.Media.FaceAnalysis 的命名空間包含用於偵測面影像或視訊功能。功能集是基本的且缺乏豐富資料集的認知的服務。事實上,它是非常類似於臉部偵測許多數位相機中找到。雖然基本功能,但是有兩個明顯的優點︰ 在離線,因為您不呼叫 API,您將不會產生任何費用。最佳化策略,為您的應用程式可以呼叫認知服務 API 之前,在本機偵測字樣的目前狀態。如此一來應用程式不會傳送映像而表面認知的服務應用程式開發介面。可以顯著的成本節省金額為您和您的使用者更低的頻寬使用量。在本機偵測面可以是智慧型的雲端服務與認知服務一樣實用增加。

設定專案

在 Visual Studio 2015 中,建立新的通用 Windows 平台 (UWP) 應用程式專案,選擇 [空白] 範本,並將它 FaceDetection。因為應用程式會使用網路攝影機,您必須在應用程式中新增這項功能。在 [方案總管] 中按兩下 Package.appxmanifest 檔案。在 [功能] 索引標籤中,檢查麥克風和攝影機,旁邊的核取方塊中所示 [圖 1。儲存檔案。

將網路攝影機和麥克風功能加入至應用程式
[圖 1 將網路攝影機和麥克風功能加入至應用程式

現在,將下列 XAML 加入至 MainPage.xaml 檔案,以建立 UI:

<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 物件建立。

在 [偵測面臨] 按鈕的事件處理常式,加入下列程式碼︰

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 且設為 HighPerformance 的程式碼行。FaceDetectionMode 列舉有三個成員︰ HighPerformance 排列速度而非、 HighQuality 排列精確度速度,以及平衡尋找速度和精確度之間取得折衷。下的一行程式碼不會執行偵測演算法的圖示時延遲連入的視訊畫面。這樣可以預覽視訊順利執行。

接下來,FaceDetectionDefinition 會加入至 MediaCapture 物件,並指定資料流中指定的媒體類型的列舉。一旦加入,就會傳回 FaceDetectionEffect 物件。此物件具有 FaceDetected 引發之事件的臉偵測到設定臉偵測頻率 DesiredDetectionInterval 屬性,啟用或停用臉偵測已啟用屬性。

表面周圍繪製矩形

既然 FaceDetectionEffect 已加入至 MediaCapture 物件啟用,而且是時機 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 中解析不同。若要將矩形放在適當的位置,您必須採取位置和 UI 和視訊資料流中的小數位數差異列入考量。若要達成此目的,您需要新增兩個函式會執行工作︰ MapRectangleToDetectedFace 和 LocatePreviewStreamCoordinates。

第一個步驟是擷取預覽資料流的相關資訊。您可以將類別寬 _previewProperties VideoEncodingProperties 物件轉型。VideoEncodingProperties 描述視訊資料流的格式。主要是您想要知道資料流的高度和寬度。憑著此資訊,您可以判斷在媒體資料流,以及是否不同於 CaptureElement 控制項的外觀比例。

LocatePreviewStreamCoordinates 方法比較媒體資料流的高度和寬度設為 CaptureElement 控制項。  根據長寬比的差異,其中三種情況下可能會︰ 長寬比相同,而且會有任何調整。如果不同的外觀比例,則會加入 letterboxes。如果大於媒體資料流的長寬比 CaptureElement 的長寬比,letterboxes 會加入側邊。

如果加入 letterboxes 側邊,必須調整朝矩形的 X 座標。如果大於 CaptureElement 媒體資料流的長寬比,則會加入 letterboxes,視訊上下。在此情況下,然後朝矩形的 Y 座標都必須隨著調整。

具有 letterboxes 列入考量的位置,您現在必須決定調整媒體資料流和 CaptureElement 在 UI 控制項之間的差異。以矩形,有四個設定項目︰ 頁首、 左邊、 寬度和高度。頂端和左邊是相依性屬性。針對相依性屬性的概觀,請參閱在 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 更精確的位置,朝反白顯示矩形的

正在停止臉偵測

朝偵測會消耗處理能力,並在電池供電的行動裝置,可能會造成大幅減少的電池壽命。一旦您在有正面的偵測,您可能想要讓使用者可以將它關閉。

幸運的是,關閉臉偵測就相當簡單。首先,將按鈕加入至 MainPage.xaml 檔案中的 StackPanel:

<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,以釋出記憶體。

現在執行專案。按一下 [啟動數位相機] 按鈕,然後偵測面臨,最後,停止偵測面臨。您應該注意到,即使應用程式不會再偵測到的表面,還有矩形臉偵測到的最後一個位置中。改一下錯字。

停止應用程式返回至 btnStopDetection_Click 事件處理常式,並新增下列程式碼來清除 cvsFacesOverlay 畫布上的內容︰

this.cvsFaceOverlay.Children.Clear();

再次執行此方案,並重複所有步驟。現在,當面臨偵測已關閉,就沒有矩形反白顯示。

總結

認知服務 Api 提供功能強大的電腦願景演算法讓您輕鬆存取。如同所有的雲端服務,不過它們需要網際網路存取的工作。針對某些需要離線案例的使用案例,您仍然可以使用內建於 UWP 臉偵測 Api 執行基本臉部偵測。 

將攝影機連接到遠端位置中執行 Windows IoT 核心 Raspberry Pi 2 時,可離線使用案例。應用程式會接著儲存映像在本機上時偵測表面。當收集到的裝置中的資料時,映像無法再上傳至認知服務以供進階分析。

執行本機臉偵測也會最佳化頻寬傳輸和雲端服務使用透過讓開發人員只能上傳的表面的影像。簡單地說,本機臉偵測可以擴大 online 案例,並能夠離線使用。

深入探索,請務必在 GitHub 上查看 CameraFaceDetection 中的範例 UWP 範例應用程式 (bit.ly/2b27gLk)。


Frank La Vigne 是 Microsoft 技術和民事參與小組的技術推廣者,可協助使用者利用技術,來建立更好的社群。 定期在他的部落格 FranksWorld.com 和已製作成 YouTube 頻道呼叫的 Frank 世界電視 (youtube.com/FranksWorldTV)。

由於閱本篇文章的下列技術專家︰ Rachel Appel