Oktober 2016

Band 31, Nummer 10

Dieser Artikel wurde maschinell übersetzt.

Cognitive Services: Gesichts- und Emotionenerkennung in Xamarin.Forms mit Microsoft Cognitive Services

Von Alessandro Del Del

Auf der Entwicklerkonferenz Build 2016 hat Microsoft eine erste Preview von Cognitive Services  (microsoft.com/cognitive-services) angekündigt, einer reichhaltigen Sammlung von plattformübergreifenden RESTful-APIs, die Sie zum Erstellen der nächsten Generation von Apps basierend auf natürlicher Benutzerinteraktion für jede beliebige Plattform auf jedem beliebigen Gerät verwenden können. Cognitive Services (auch als „Project Oxford“ bezeichnet) basieren auf Machine Learning und passen perfekt in die Conversation-as-a-Platform-Philosophie, die Microsoft in das Apps-Ökosystem einbringen möchte. Auf höherer Ebene stehen die Cognitive Services-APIs durch RESTful-Dienste zur Verfügung und bieten zurzeit die folgenden Kategorien von APIs:

  • Gesichtserkennung: Die Erkennungsdienste bieten APIs, mit denen Sie Bilder und Videos analysieren können, um Gesichter und Emotionen zu erkennen sowie handlungsrelevante Informationen zu ermitteln. Diese Kategorie enthält die Maschinelles Sehen-, Gesichtserkennungs-, Emotionen- und Video-APIs.
  • Spracherkennung: Die Spracherkennungsdienste bieten APIs, die die Implementierung von Text-zu-Sprache vereinfachen, die Erkennung natürlicher Sprache unterstützen und sogar durch den Sprechererkennungsdienst das Erkennen eines Sprechers ermöglichen. Sie umfassen die Bing-Spracheingabe-, Custom Recognition Intelligent Service- und Speaker Recognition-APIs.
  • Sprache: Die Sprachdienste beziehen sich auf das Verstehen natürlicher Sprache. Dies bedeutet das Erkennen und Beheben von Rechtschreibfehlern, das Verstehen von Sprachbefehlen sowie das Analysieren komplexer Texte einschließlich Stimmungen und Schlüsselbegriffen. Sie umfassen APIs für die Rechtschreibprüfung von Bing, für Language Understanding Intelligent Service, für linguistische Analyse, für Textanalyse und für das Web Language Model.
  • Wissen: Durch die Wissensdienste können Anwendungen das Wissen von Kunden erweitern, indem nach personalisierten Produktempfehlungen, Ereignissen, Orten oder wissenschaftlichen Arbeiten oder Magazinen gesucht wird. Sie umfassen Academic Knowledge-, Entity Linking Intelligence Service-, Knowledge Exploration Service- und Empfehlungs-APIs.
  • Suche: Die Suchdienste basieren auf Bing und ermöglichen das Implementieren leistungsfähiger Suchtools in ihren Apps. Die Namen der enthaltenen Dienste sind mehr als selbsterklärend: Bing-Vorschlagssuche-, Bing-Bildersuche-, Bing News-Suche-, Bing-Videosuche- und Bing-Websuche-API.

In diesem Artikel erläutere ich, wie die Gesichtserkennungs- und Emotionen-APIs kombiniert werden, um Gesichtsdetails und Emotionen aus Bildern abzurufen, die mit einer Kamera aufgenommen werden oder aus einem Album auf dem Datenträger stammen. Ich verwende zu diesem Zweck eine mit C# und Visual Studio 2015 erstellte Xamarin.Forms-App, die unter Android, iOS oder Windows 10 ausgeführt werden kann. Abbildung 1 zeigt das Ergebnis des Tutorials in diesem Artikel. Wichtig zu erwähnen: Für diesen Artikel wurde Xamarin.Forms verwendet. Das gleiche Ergebnis kann jedoch auch mit traditionellen Xamarin-Apps oder einer beliebigen anderen Plattform erzielt werden, die REST unterstützt. Ich gehe davon aus, dass Sie über Grundkenntnisse beim Erstellen von Xamarin.Forms-Apps verfügen und die Konzepte zu Code Sharing kennen. Wenn dies nicht der Fall sein sollte, lesen Sie meine vorherigen Artikel: „Entwickeln einer plattformübergreifenden Benutzererfahrung mit Xamarin.Forms“ (msdn.com/magazine/mt595754) und „Gemeinsames Verwenden von Benutzeroberflächencode auf mobilen Plattformen mit Xamarin.Forms“ (msdn.com/magazine/dn904669).

Gesichts- und Emotionenerkennung für eine plattformübergreifende App mit Xamarin.Forms
Abbildung 1: Gesichts- und Emotionenerkennung für eine plattformübergreifende App mit Xamarin.Forms (Android-Gerät links, Windows 10 Desktop rechts)

Abonnieren von Cognitive Services-APIs

Wenn Sie Apps erstellen möchten, die Cognitive Services nutzen, müssen Sie den Dienst abonnieren, an dem Sie interessiert sind. Zurzeit bietet Microsoft kostenlose Testversionen an, die Sie auf der Abonnementseite (bit.ly/2b2rKDO) aktivieren können. Die aktuellen Pläne können jedoch in Zukunft ggf. geändert werden. Registrieren Sie sich auf der Seite mit einem Microsoft-Konto, und klicken Sie dann auf „Neue Testversionen anfordern“. Nun wird eine Liste der verfügbaren Dienste angezeigt. Stellen Sie sicher, dass Sie kostenlose Previews der Gesichtserkennungs- und Emotionen-APIs auswählen. An diesem Punkt zeigt Ihre Abonnementseite die Liste der aktiven Dienste an. Die Abonnements für die Gesichtserkennungs- und Emotionen-APIs sollten also angezeigt werden. Abbildung 2 zeigt ein Beispiel, das auf meinen Abonnements basiert. Beachten Sie, dass für jeden aktiven Dienst zwei geheime Schlüssel vorhanden sind. Einer dieser Schlüssel ist erforderlich, um die APIs aufzurufen. Blenden Sie sie zum jetzigen Zeitpunkt jedoch aus. Sie blenden den Schlüssel erneut ein, wenn Sie die Xamarin.Forms-App erstellen.

Aktivieren von Abonnements für Gesichtserkennungs- und Emotionen-APIs
Abbildung 2: Aktivieren von Abonnements für Gesichtserkennungs- und Emotionen-APIs

Allgemein gesagt, stellen Cognitive Services RESTful APIs bereit. Dies bedeutet, dass Sie mit diesen Diensten über HTTP-Anforderungen auf beliebigen Plattformen und mit beliebigem sprachunterstützendem REST interagieren können. Die folgende HTTP POST-Anforderung zeigt z. B., wie ein Bild an den Emotionenerkennungsdienst für die Erkennung von Emotionen gesendet wird:

POST https://api.projectoxford.ai/emotion/v1.0/recognize HTTP/1.1
Content-Type: application/json
Host: api.projectoxford.ai
Content-Length: 107
Ocp-Apim-Subscription-Key: YOUR-KEY-GOES-HERE
{ "url": "http://www.samplewebsite.com/sampleimage.jpg" }

Natürlich müssen Sie „Ocp-Apim-Subscription-Key“ durch einen Ihrer eigenen Schlüssel und die imaginäre Bild-URL durch eine echte Bildadresse ersetzen. Der Emotionenerkennungsdienst sendet dann das Ergebnis der Erkennung als JSON-Antwort zurück. Abbildung 3 zeigt diesen Vorgang.

Abbildung 3: Die Erkennungsantwort des Emotionenerkennungsdiensts

[
  {
    "faceRectangle": {
      "height": 70,
      "left": 26,
      "top": 35,
      "width": 70
    },
    "scores": {
      "anger": 2.012591E-11,
      "contempt": 1.95578984E-10,
      "disgust": 1.02281912E-10,
      "fear": 1.16242682E-13,
      "happiness": 1.0,
      "neutral": 9.79047E-09,
      "sadness": 2.91102975E-10,
      "surprise": 1.71011272E-09
    }
  }
]

Die Beispielantwort in Abbildung 3 zeigt, wie der Emotionendienst das Rechteck zurückgibt, in dem das Gesicht erkannt wurde, sowie ein Array namens „scores“, das eine Liste der Emotionen und einen Wert zwischen 0 und 1 enthält, der die Wahrscheinlichkeit angibt, dass die Emotion richtig erkannt wurde. Im Allgemeinen ist das Senden von HTTP-Anforderungen an RESTful-Dienste und das Erwarten einer JSON-Antwort eine häufig verwendete Vorgehensweise für alle Cognitive Services. Für .NET-Entwickler, die mit C# arbeiten, stellt Microsoft jedoch auch portable Clientbibliotheken zur Verfügung, die Sie von NuGet herunterladen können. Diese vereinfachen die Interaktion mit Diensten in verwaltetem Code und bieten vollständige Objektorientierung. Dies trifft auch auf die Gesichtserkennungs- und Emotionen- APIs zu, wie Sie bald sehen werden. Sie sollten auch die offizielle Dokumentation zu Rate ziehen, die Beispiele enthält, die auf dem REST-Ansatz und auf Clientbibliotheken (wenn verfügbar) basieren (bit.ly/2b2KJrB). Da Sie sich nun für beide Dienste registriert haben und über Ihre Schlüssel verfügen, können Sie damit beginnen, eine plattformübergreifende App mit Xamarin.Forms und Microsoft Visual Studio 2015 zu erstellen.

Erstellen einer Xamarin.Forms-Anwendung

Wie Sie bereits wissen, können Sie eine plattformübergreifende App mit Xamarin.Forms erstellen, indem Sie die Projektvorlage für portable oder freigegebene Apps auswählen. Da ich erläutern werde, wie die Clientbibliotheken für die Cognitive Services-APIs genutzt werden können, basiert die Beispielanwendung auf dem PCL-Modell (Portable Class Library, portable Klassenbibliothek). Wählen Sie in Visual Studio 2015 „Datei“ > „Projekt“ aus. Wenn Sie die aktuellen Updates von Xamarin (xamarin.com/download) installiert haben, finden Sie eine neue Projektvorlage namens „Leere XAML-App (Xamarin.Forms Portable)“ unter dem plattformübergreifenden Visual C#-Knoten des Dialogfelds „Neues Projekt“. Interessant an dieser Vorlage ist, dass sie eine leere XAML-Seite bereitstellt, die nicht mehr manuell erstellt werden muss. Abbildung 4 zeigt die neue Vorlage.

Geben Sie der Projektmappe den Namen „FaceEmotionRecognition“, und klicken Sie dann auf „OK“. Während der Generierung der Projektmappe werden Sie aufgefordert, die Mindestzielversion für das UWP-Projekt (universelle Windows-Plattform) anzugeben. Die Auswahl bleibt Ihnen überlassen. Ich empfehle jedoch , die höchste verfügbare Version anzugeben.

Erstellen einer neuen Xamarin.Forms-Anwendung
Abbildung 4: Erstellen einer neuen Xamarin.Forms-Anwendung

Plug-Ins für Xamarin

Die Beispielanwendung verwendet die Cognitive Services-APIs zum Erkennen von Gesichtsdetails und Emotionen in Bildern. Dabei werden vorhandene Bilder vom Gerät verwendet oder neue Bilder mit der Kamera aufgenommen. Dies bedeutet, dass die App Zugriff auf das Internet benötigt, um eine Verbindung mit den Diensten herzustellen. Außerdem muss sie eine Funktion zum Aufnehmen und Auswählen von Bildern besitzen. Für die App ist es einfach, eine Verbindung mit einem Netzwerk herzustellen. Es liegt jedoch in Ihrer Verantwortung als Entwickler, die Netzwerkverfügbarkeit zu überprüfen. Features zum Überprüfen der Netzwerkverfügbarkeit und Aufnehmen von Bildern würden das Schreiben von spezifischem Code in den Android-, iOS- und Windows-Projekten erfordern. Glücklicherweise unterstützt Xamarin jedoch Plug-Ins, die Sie in Xamarin.Forms verwenden und im PCL-Projekt installieren können, damit sie diese Aufgaben für Sie übernehmen. Ein Plug-In ist eine aus NuGet installierte Bibliothek, die als Wrapper für die nativen APIs dient und eine allgemeine Codeimplementierung bereitstellt. Das Plug-In wird im PCL-Projekt aufgerufen. Es sind zahlreiche Plug-Ins verfügbar. Einige davon wurden von Xamarin entwickelt, andere von der Entwicklercommunity veröffentlicht. Alle Plug-Ins sind Open Source. Eine Liste finden Sie auf GitHub unter bit.ly/29XZ3VM. In diesem Artikel zeige ich, wie die Konnektivitäts- und Medien-Plug-Ins verwendet werden.

Installieren von NuGet-Paketen

Wenn die Projektmappe bereit ist, müssen Sie als ersten Schritt die folgenden NuGet-Pakete installieren:

  • Microsoft.ProjectOxford.Face: Installiert die Clientbibliothek für die Gesichtserkennungs-APIs und darf nur im PCL-Projekt installiert werden.
  • Microsoft.ProjectOxford.Emotion: Installiert die Clientbibliothek für die Emotionen-APIs und darf (wie die Gesichtserkennungs-API) nur im PCL-Projekt installiert werden.
  • Xam.Plugin.Connectivity: Enthält das Konnektivitäts-Plug-In für Xamarin.Forms und muss in allen Projekten in der Projektmappe installiert werden.
  • Xam.Plugin.Media: Enthält das Medien-Plug-In für Xamarin.Forms und muss (wie die Konnektivitäts-API) in allen Projekten in der Projektmappe installiert werden.

Nachdem Sie die erforderlichen NuGet-Pakete installiert haben, stellen Sie sicher, dass Sie die Projektmappe erstellen, bevor Sie Code schreiben, damit alle Verweise aktualisiert werden.

Entwerfen der Benutzeroberfläche

Die Benutzeroberfläche der Beispielanwendung besteht aus einer einzelnen Seite. Aus Gründen der Einfachheit verwende ich die automatisch generierte Datei „MainPage.xaml“. Diese Seite definiert Folgendes: zwei Schaltflächen (eine Schaltfläche zum Übernehmen von mit der Kamera aufgenommenen Bildern und eine Schaltfläche zum Hochladen eines vorhandenen Bilds), ein ActivityIndicator-Steuerelement, das beim Warten auf eine Antwort vom Dienst den Status „Beschäftigt“ anzeigt, ein Image-Steuerelement, das das ausgewählte Bild anzeigt, und mehrere Bezeichnungen (in StackLayout-Bereichen), die an eine benutzerdefinierte Klasse datengebunden sind, die das Ergebnis der Erkennungen für das ausgewählte Bild enthält. Abbildung 5 zeigt den vollständigen XAML-Code für die Seite.

Abbildung 5: Die Benutzeroberfläche für die Hauptseite

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
  xmlns:x="https://schemas.microsoft.com/winfx/2009/xaml"
  xmlns:local="clr-namespace:FaceEmotionRecognition"
  xmlns:conv="clr-namespace:FaceEmotionRecognition. 
    Converters;assembly=FaceEmotionRecognition"
    x:Class="FaceEmotionRecognition.MainPage">
  <StackLayout Orientation="Vertical">
    <Button x:Name="TakePictureButton" Clicked="TakePictureButton_Clicked"
      Text="Take from camera"/>
    <Button x:Name="UploadPictureButton" Clicked="UploadPictureButton_Clicked"
      Text="Pick a photo"/>
    <ActivityIndicator x:Name="Indicator1" IsVisible="False" IsRunning="False" />
    <Image x:Name="Image1" HeightRequest="240" />
    <StackLayout Orientation="Horizontal" Padding="3">
      <Label Text="Gender: "/>
      <Label x:Name="GenderLabel" Text="{Binding Path=Gender}" />
    </StackLayout>
    <StackLayout Orientation="Horizontal" Padding="3">
      <Label Text="Age: "/>
      <Label x:Name="AgeLabel" Text="{Binding Path=Age}"/>
    </StackLayout>
    <StackLayout Orientation="Horizontal" Padding="3">
      <Label Text="Emotion: "/>
      <Label x:Name="EmotionLabel" Text="{Binding Path=Emotion}"/>
    </StackLayout>
    <StackLayout Orientation="Horizontal" Padding="3">
      <Label Text="Smile: "/>
      <Label x:Name="SmileLabel"
        Text="{Binding Path=Smile}"/>
    </StackLayout>
    <StackLayout Orientation="Horizontal" Padding="3">
      <Label Text="Glasses: "/>
      <Label x:Name="GlassesLabel" Text="{Binding Path=Glasses}"/>
    </StackLayout>
    <StackLayout Orientation="Horizontal" Padding="3">
      <Label Text="Beard: "/>
      <Label x:Name="BeardLabel"
        Text="{Binding Path=Beard}"/>
    </StackLayout>
    <StackLayout Orientation="Horizontal" Padding="3">
      <Label Text="Moustache: "/>
      <Label x:Name="MoustacheLabel"
        Text="{Binding Path=Moustache}"/>
    </StackLayout>
  </StackLayout>
</ContentPage>

Der nächste Schritt besteht im Vorbereiten eines Speicherorts zum Speichern des Ergebnisses der Gesichts- und Emotionenerkennung.

Speichern der Erkennungsergebnisse mit einer Klasse

Anstatt Bezeichnungen in der Benutzeroberfläche manuell mit den Ergebnisdaten der Gesichts- und Emotionenerkennung aufzufüllen, empfiehlt sich die bewährte Methode, eine benutzerdefinierte Klasse zu erstellen. Dies ist nicht nur ein objektorientierterer Ansatz, sondern ermöglicht auch die Datenbindung der Instanz der Klasse an die Benutzeroberfläche. Erstellen wir also eine neue Klasse namens „FaceEmotionDetection“:

public class FaceEmotionDetection
{
  public string Emotion { get; set; }
  public double Smile { get; set; }
  public string Glasses { get; set; }
  public string Gender { get; set; }
  public double Age { get; set; }
  public double Beard { get; set; }
  public double Moustache { get; set; }
}

Jede Eigenschaft weist einen selbsterklärenden Namen auf und speichert Informationen, die aus der Kombination der Gesichtserkennungs- und Emotionen-APIs stammen.

Deklarieren der Dienstclients

Bevor Sie anderen Code schreiben, empfiehlt es sich, die folgenden using-Direktiven hinzuzufügen:

using Microsoft.ProjectOxford.Emotion;
using Microsoft.ProjectOxford.Emotion.Contract;
using Microsoft.ProjectOxford.Face;
using Microsoft.ProjectOxford.Face.Contract;
using Plugin.Connectivity;
using Plugin.Media;

Diese vereinfachen den Aufruf von Objektnamen für die Cognitive Services-APIs und die Plug-Ins. Die Gesichtserkennungs-APIs und die Emotionen-APIs stellen die Klassen „Microsoft.ProjectOxford.Face.Face­ServiceClient“ und „Microsoft.ProjectOxford.Emotion.Emotion­ServiceClient“ bereit, die eine Verbindung mit Cognitive Services herstellen und dann Informationen zu den Gesichts- und Emotionendetails zurückgeben. Zuerst müssen Sie eine Instanz beider Klassen deklarieren und Ihren geheimen Schlüssen wie hier gezeigt an den Konstruktor übergeben:

private readonly IFaceServiceClient faceServiceClient;
private readonly EmotionServiceClient emotionServiceClient;
public MainPage()
{
  InitializeComponent();
  // Provides access to the Face APIs
  this.faceServiceClient = new FaceServiceClient("YOUR-KEY-GOES-HERE");
  // Provides access to the Emotion APIs
  this.emotionServiceClient = new EmotionServiceClient("YOUR-KEY-GOES-HERE");
}

Beachten Sie, dass Sie Ihre eigenen geheimen Schlüssel angeben müssen. Sie finden die geheimen Schlüssel der Gesichtserkennungs- und Emotionen-API auf der Abonnementseite im Microsoft Cognitive Services-Portal (bit.ly/2b2rKDO). Abbildung 2 zeigt dies.

Erfassen und Laden von Bildern

In Xamarin.Forms würde der Zugriff auf die Kamera und das Dateisystem das Schreiben plattformspezifischen Codes erfordern. Einfacher ist jedoch das Verwenden des Medien-Plug-Ins für Xamarin.Forms. Mit diesem können Sie Bilder und Videos vom Datenträger auswählen und Bilder und Videos mit der Kamera aus dem PCL-Projekt aufnehmen. Dafür sind nur wenige Codezeilen erforderlich. Dieses Plug-In stellt eine Klasse namens „CrossMedia“ mit den folgenden Membern bereit:

  • „Current“: Gibt eine Singletoninstanz der Klasse „CrossMedia“ zurück.
  • „IsPickPhotoSupported“ und „IsPickVideoSupported“: Boolesche Eigenschaften, die TRUE zurückgeben, wenn das aktuelle Gerät das Auswählen von Bildern und Videos vom Datenträger unterstützt.
  • „PickPhotoAsync“ und „PickVideoAsync“: Methoden, die die plattformspezifische Benutzeroberfläche zum Auswählen eines lokalen Bilds bzw. Videos aufrufen und ein Objekt vom Typ „MediaFile“ zurückgeben.
  • „IsCameraAvailable“: Eine boolesche Eigenschaft, die TRUE zurückgibt, wenn das Gerät über eine integrierte Kamera verfügt.
  • „IsTakePhotoSupported“ und „IsTakeVideoSupported“: Boolesche Eigenschaften, die TRUE zurückgeben, wenn das aktuelle Gerät das Aufnehmen von Bildern und Videos mit der Kamera unterstützt.
  • „TakePhotoAsync“ und „TakeVideoAsync“: Methoden, die die integrierte Kamera starten, um ein Bild bzw. Video aufzunehmen, und ein Objekt vom Typ „MediaFile“ zurückgeben.

Vergessen Sie nicht, die entsprechenden Berechtigungen für den Zugriff auf die Kamera im Manifest der App festzulegen. In einem UWP-Projekt benötigen Sie z. B. die Berechtigungen „Webcam“ und „Pictures Library“, unter Android die Berechtigungen „CAMERA“, „READ_EXTERNAL_STORAGE“ und „WRITE_EXTERNAL_STORAGE“. Wenn Sie vergessen, die erforderlichen Berechtigungen festzulegen, führt dies zu Laufzeitausnahmen. Schreiben wir nun den Clicked-Ereignishandler für „UploadPictureButton“. Abbildung 6 zeigt dies.

Abbildung 6: Auswählen eines Bilds vom Datenträger

private async void UploadPictureButton_Clicked(object sender, EventArgs e)
{
  if (!CrossMedia.Current.IsPickPhotoSupported)
  {
    await DisplayAlert("No upload", "Picking a photo is not supported.", "OK");
    return;
  }
  var file = await CrossMedia.Current.PickPhotoAsync();
  if (file == null)
    return;
  this.Indicator1.IsVisible = true;
  this.Indicator1.IsRunning = true;
  Image1.Source = ImageSource.FromStream(() => file.GetStream());
  this.Indicator1.IsRunning = false;
  this.Indicator1.IsVisible = false;
}

Der Code überprüft zuerst, ob das Auswählen von Bildern unterstützt wird. Wenn „IsPickPhotoSupported“ FALSE zurückgibt, wird eine Fehlermeldung angezeigt. „PickPhoto­Async“ (sowie „PickVideoAsync“) gibt ein Objekt vom Typ „Media­File“ zurück. Dies ist eine Klasse, die im Namespace „Plugin.Media“ definiert ist und die ausgewählte Datei darstellt. Sie müssen deren GetStream-Methode aufrufen, um einen Datenstrom zurückzugeben, der als Quelle für das Image-Steuerelement über ihre FromStream-Methode verwendet werden kann. Das Aufnehmen eines Bilds mit der Kamera ist ebenfalls ganz einfach. Abbildung 7 zeigt dies.

Abbildung 7: Aufnehmen eines Bilds mit der Kamera

private async void TakePictureButton_Clicked(object sender, EventArgs e)
{
  await CrossMedia.Current.Initialize();
  if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.
    IsTakePhotoSupported)
  {
    await DisplayAlert("No Camera", "No camera available.", "OK");
    return;
  }
  var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions
  {
    SaveToAlbum = true,
    Name = "test.jpg"
  });
  if (file == null)
    return;
  this.Indicator1.IsVisible = true;
  this.Indicator1.IsRunning = true;
  Image1.Source = ImageSource.FromStream(() => file.GetStream());
  this.Indicator1.IsRunning = false;
  this.Indicator1.IsVisible = false;
}

Interessant ist hier, dass „TakePhotoAsync“ einen Parameter vom Typ „StoreCameraMediaOptions“ annimmt, ein Objekt, mit dem Sie angeben können, wo und wie ein Bild gespeichert wird. Sie können die Eigenschaft „SaveToAlbum“ auf TRUE festlegen, wenn das Bild auf der lokalen Camera Roll gespeichert werden soll, oder Sie können die Eigenschaft „Directory“ festlegen, wenn es in einem anderen Ordner gespeichert werden soll. Wie Sie sehen können, kann Ihre App mit minimalem Aufwand und wenigen Codezeilen eine wichtige Funktion aller unterstützten Plattformen nutzen.

Erkennen von Emotionen und Implementieren der Gesichtserkennung

Jetzt ist es an der Zeit, die Gesichts- und Emotionenerkennung zu implementieren. Da es sich hier um einen einführenden Artikel handelt, lege ich den Schwerpunkt auf Einfachheit. Ich zeige, wie die Erkennung eines einzelnen Gesichts in einem Bild implementiert wird und beschreibe die wichtigsten Objekte und Member in den APIs. Außerdem unterbreite ich Ihnen Vorschläge zur Implementierung detailreicherer Erkennungen, wenn dies erforderlich sein sollte. Beginnen wir also basierend auf diesen Annahmen damit, eine asynchrone Methode zu schreiben, die Erkennungen ausführt. Der erste Codeausschnitt befasst sich mit der Emotionenerkennung und sieht wie folgt aus:

private async Task<FaceEmotionDetection> DetectFaceAndEmotionsAsync(MediaFile inputFile)
{
  try
  {
    // Get emotions from the specified stream
    Emotion[] emotionResult = await
      emotionServiceClient.RecognizeAsync(inputFile.GetStream());
    // Assuming the picture has one face, retrieve emotions for the
    // first item in the returned array
    var faceEmotion = emotionResult[0]?.Scores.ToRankedList();

Die Methode empfängt das MediaFile-Objekt, das durch Auswählen oder Aufnehmen eines Bilds generiert wird. Das Erkennen von Emotionen in Gesichtern in einem Bild ist unkompliziert, weil Sie einfach die RecognizeAsync-Methode aus der Instanz der Klasse „EmotionServiceClient“ aufrufen. Diese Methode kann einen Datenstrom oder eine URL als Argument empfangen. In diesem Fall ruft sie einen Datenstrom aus dem MediaFile-Objekt ab. „RecognizeAsync“ gibt ein Array von Emotion-Objekten zurück. Jedes Emotion-Objekt im Array speichert Emotionen, die in einem Gesicht in einem Bild erkannt wurden. Angenommen, auf dem ausgewählten Bild ist nur ein Gesicht zu sehen. Der Code ruft dann das erste Element im Array ab. Der Typ „Emotion“ stellt eine Eigenschaft namens „Scores“ bereit, die eine Liste mit acht Emotionsnamen sowie deren ungefähren Wert enthält. Genauer gesagt, wird ein „IEnumerable<string, float>“ generiert. Durch Aufrufen der Methode „ToRankedList“ können Sie eine sortierte Liste der erkannten Emotionen abrufen. Die APIs können eine einzelne Emotion nicht genau erkennen. Stattdessen erkennen sie mehrere mögliche Emotionen. Der höchste zurückgegebene Wert ist wahrscheinlich die tatsächliche Emotion, die das Gesicht zeigt. Es sind aber noch andere Werte vorhanden, die überprüft werden können.  Der höchste Wert in dieser Liste stellt die Emotion mit der höchstmöglichen wahrscheinlichen Ähnlichkeit dar, die möglicherweise die tatsächliche Emotion im Gesicht ist. Um dies besser zu verstehen, sehen Sie sich die folgende Liste mit Emotionen mit einer Rangfolge an, die mithilfe der Datentipps des Debuggers abgerufen wurde. Sie basiert auf dem in Abbildung 1 gezeigten Beispielbild:

[0] = {[Happiness, 1]}
[1] = {[Neutral, 1.089301E-09]}
[2] = {[Surprise, 7.085784E-10]}
[3] = {[Sadness, 9.352855E-11]}
[4] = {[Disgust, 4.52789E-11]}
[5] = {[Contempt, 1.431213E-11]}
[6] = {[Anger, 1.25112E-11]}
[7] = {[Fear, 5.629648E-14]}

Wie Sie sehen können, besitzt „Happiness“(Glück) den Wert 1. Dies ist der höchste Wert in der Liste und die geschätzte Ähnlichkeit mit der tatsächlichen Emotion. Der nächste Schritt besteht im Erkennen von Gesichtsattributen. Die Klasse „FaceServiceClient“ stellt die DetectAsync-Methode bereit, die extrem leistungsfähig ist. Sie kann nicht nur Gesichtsattribute wie Geschlecht, Alter und Lächeln abrufen, sondern auch Personen erkennen und das Gesichtsrechteck (den Bereich im Bild, in dem das Gesicht erkannt wurde) und 27 Orientierungspunkte des Gesichts zurückgeben, anhand derer eine App Informationen wie die Position von Nase, Mund, Ohren und Augen im Bild identifizieren kann. „DetectAsync“ weist die folgende Signatur auf:

Task<Contract.Face[]> DetectAsync(Stream imageStream,
  bool returnFaceId = true, bool returnFaceLandmarks = false,
  IEnumerable<FaceAttributeType> returnFaceAttributes = null);

Für ihren einfachsten Aufruf erfordert „DetectAsync“ einen Datenstrom, der auf ein Bild oder eine URL zeigt und gibt das Gesichtsrechteck zurück, während Sie mit den optionalen Parametern „returnFaceId“ bzw. „returnFaceLandmarks“ eine Person identifizieren und Orientierungspunkte des Gesichts zurückgeben können. Mit den Gesichtserkennungs-APIs können Sie Gruppen von Personen erstellen und jeder Person eine ID zuweisen, damit die Erkennung auf einfache Weise ausgeführt werden kann. Orientierungspunkte des Gesichts hingegen sind zum Identifizieren der Merkmale eines Gesichts hilfreich. Sie stehen über die Eigenschaft „FaceLandmarks“ des Face-Objekts zur Verfügung. Identifikation und Orientierungspunkte kann ich in diesem Artikel nicht ausführlich behandeln. Sie finden jedoch weitere Informationen zu diesen Themen unter bit.ly/2adPvoP und bit.ly/2ai9WjV. Ich kann Ihnen auch nicht zeigen, wie Orientierungspunkte des Gesichts verwendet werden. Nur so viel dazu: Sie werden in der FaceLandmarks-Eigenschaft des Face-Objekts gespeichert. Das Ziel des aktuellen Beispielszenarios besteht im Abrufen von Gesichtsattributen. Zu diesem Zweck benötigen Sie ein Array der FaceAttributeType-Enumeration, die die Liste der Attribute definiert, die Sie abrufen möchten:

// Create a list of face attributes that the
// app will need to retrieve
var requiredFaceAttributes = new FaceAttributeType[] {
  FaceAttributeType.Age,
  FaceAttributeType.Gender,
  FaceAttributeType.Smile,
  FaceAttributeType.FacialHair,
  FaceAttributeType.HeadPose,
  FaceAttributeType.Glasses
  };

Im nächsten Schritt rufen Sie „DetectAsync“ auf und übergeben den Bilddatenstrom sowie die Liste mit den Gesichtsattributen. Die Argumente „returnFaceId“ und „returnFaceLandmarks“ sind FALSE, weil die zugehörigen Informationen zurzeit nicht erforderlich sind. Der Methodenaufruf sieht folgendermaßen aus:

// Get a list of faces in a picture
var faces = await faceServiceClient.DetectAsync(inputFile.GetStream(),
  false, false, requiredFaceAttributes);
// Assuming there is only one face, store its attributes
var faceAttributes = faces[0]?.FaceAttributes;

„DetectAsync“ gibt ein Array von Face-Objekten zurück. Dabei stellt jedes Objekt ein Gesicht im Bild dar. Der Code verarbeitet das erste Element im Array, das ein einzelnes Gesicht darstellt, und ruft seine Gesichtsattribute ab. Beachten Sie, wie in der letzten Zeile der mit C# 6 eingeführte bedingte Nulloperator (?) verwendet wird, der null zurückgibt, wenn das erste Element im Array ebenfalls null ist, anstatt eine „NullReferenceException“ auszulösen. Weitere Informationen zu diesem Operator finden Sie unter bit.ly/2bc8VZ3. Da Ihnen nun die Gesichts- und Emotioneninformationen vorliegen, können Sie eine Instanz der FaceEmotionDetection-Klasse erstellen und ihre Eigenschaften mit Daten auffüllen. Der folgende Code zeigt dies:

FaceEmotionDetection faceEmotionDetection = new FaceEmotionDetection();
faceEmotionDetection.Age = faceAttributes.Age;
faceEmotionDetection.Emotion = faceEmotion.FirstOrDefault().Key;
faceEmotionDetection.Glasses = faceAttributes.Glasses.ToString();
faceEmotionDetection.Smile = faceAttributes.Smile;
faceEmotionDetection.Gender = faceAttributes.Gender;
faceEmotionDetection.Moustache = faceAttributes.FacialHair.Moustache;
faceEmotionDetection.Beard = faceAttributes.FacialHair.Beard;

Dazu einige Anmerkungen:

  • Der höchste Wert in der Emotionenliste wird durch Aufrufen von „FirstOrDefault“ für das Ergebnis des Aufrufs der Scores.ToRankedList-Methode ermittelt, der ein IEnumerable<string, float>-Objekt zurückgibt.
  • Der von „FirstOrDefault“ zurückgegebene Wert ist hier ein Objekt vom Typ „KeyValuePair<string, float>, und der Schlüssel vom Typ „string“ speichert den Namen der Emotion als Klartext, der in der Benutzeroberfläche angezeigt wird.
  • „Glasses“ ist eine Enumeration, die angibt, ob das erkannte Gesicht eine Brille trägt und welche Art von Brille vorliegt. Der Code ruft „ToString“ aus Gründen der Einfachheit auf. Sie könnten aber z. B. einen Konverter für eine andere Zeichenfolgenformatierung implementieren.

Der abschließende Block im Methodentext gibt die Instanz der FaceEmotionDetection-Klasse zurück und implementiert Ausnahmeverarbeitung:

return faceEmotionDetection;
  }
  catch (Exception ex)
  {
    await DisplayAlert("Error", ex.Message, "OK");
    return null;
  }
}

Der letzte Schritt, der ausgeführt werden muss, ist der Aufruf der benutzerdefinierten DetectFaceAndEmotionAsync-Methode. Dies kann in beiden Clicked-Ereignishandlern erfolgen, und zwar unmittelbar vor dem Festlegen der Eigenschaften „IsRunning“ und „IsVisible“ des ActivityIndicator-Steuerelements auf FALSE:

FaceEmotionDetection theData = await DetectFaceAndEmotionsAsync(file);
this.BindingContext = theData;
this.Indicator1.IsRunning = false;
this.Indicator1.IsVisible = false;

Die Eigenschaft „BindingContext“ der Seite empfängt eine Instanz der FaceEmotionDetection-Klasse Datenquelle, und datengebundene untergeordnete Steuerelemente zeigen die zugehörigen Informationen automatisch an. Mit Mustern wie Model-View-ViewModel können Sie eine ViewModel-Klasse als Wrapper für das Ergebnis verwenden. Nachdem alle diese Arbeiten nun abgeschlossen wurden, kann die Anwendung getestet werden.

Testen der Anwendung

Wählen Sie die gewünschte Plattform aus, und drücken Sie F5. Wenn Sie die Microsoft-Emulatoren verwenden, können Sie die Emulatortools zum Auswählen einer physischen Webcam zum Aufnehmen von Bildern verwenden, und Sie können eine SD-Karte zum Hochladen von Dateien simulieren. Abbildung 1 zeigt die Ergebnisse der Erkennung für ein Bild von mir auf einem Android-Gerät unter Windows 10 (Ausführung im Desktopmodus).

Die Gesichtserkennungs- und Emotionen-APIs haben ihre Aufgabe großartig erledigt, weil die zurückgegebenen Werte der Wahrheit sehr nahe kommen, obwohl sie noch immer Näherungswerte sind. Es sollte noch erwähnt werden, dass die FaceEmotionDetection-Klasse einige Eigenschaften von Typ „double“ besitzt, z. B. „Smile“ (Lächeln), „Beard“ (Bart) und „Moustache“ (Schnauzbart). Diese geben numerische Werte zurück, die möglicherweise für den Endbenutzer in einer echten App nicht viel Sinn ergeben. Sollten Sie also diese numerischen Werte in Klartextzeichenfolgen konvertieren wollen, können Sie ggf. Wertkonverter und die IValueConverter-Schnittstelle (bit.ly/2bZn01J) implementieren.

Implementieren der Überprüfung der Netzwerkkonnektivität

Eine durchdachte App, die Zugriff auf Ressourcen im Internet benötigt, sollte immer zuerst die Verfügbarkeit der Verbindung überprüfen. Wie für den Zugriff auf die Kamera und das Dateisystem sollte auch für das Überprüfen der Verfügbarkeit der Verbindung in Xamarin.Forms plattformspezifischer Code erforderlich sein. Glücklicherweise bietet hier das Konnektivitäts-Plug-In Unterstützung durch Bereitstellen einer freigegebenen Möglichkeit zum direkten Ausführen dieser Überprüfung aus dem PCL-Projekt. Das Plug-In stellt eine Klasse namens „CrossConnectivity“ mit einer Eigenschaft „Current“ bereit, die eine Singletoninstanz der Klasse darstellt. Eine boolesche Eigenschaft namens „IsConnected“ gibt einfach TRUE zurück, wenn eine Verbindung verfügbar ist. Fügen Sie zum Überprüfen der Netzwerkverfügbarkeit in der Beispielanwendung einfach den folgenden Code nach der Deklaration der DetectFaceAndEmotionAsync-Methode ein:

private async Task<FaceEmotionDetection>
  DetectFaceAndEmotionsAsync(MediaFile inputFile)
{
  if(!CrossConnectivity.Current.IsConnected)
  {
    await DisplayAlert("Network error",
      "Please check your network connection and retry.", "OK");
    return null;
  }

Die Klasse stellt außerdem die folgenden interessanten Member bereit:

  • „ConnectivityChanged“: Ein Ereignis, das ausgelöst wird, wenn sich der Verbindungszustand ändert. Sie können dieses Ereignis abonnieren und Informationen zum Verbindungsstatus über ein Objekt vom Typ „ConnectivityChangedEventArgs“ abrufen.
  • „BandWidths“: Eine Eigenschaft, die eine Liste der verfügbaren Bandbreiten für die aktuelle Plattform zurückgibt.

Weitere Informationen zum Konnektivitäts-Plug-In finden Sie unter bit.ly/2bbU7wu.

Zusammenfassung

Microsoft Cognitive Services stellen RESTful-Dienste und reichhaltige APIs basierend auf Machine Learning bereit, mit denen Sie die nächste Generation von Apps erstellen können. Durch Kombinieren der Leistungsfähigkeit dieser Dienste mit Xamarin können Sie natürliche Benutzerinteraktion in Ihre plattformübergreifenden Apps für Android, iOS und Windows integrieren und Kunden eine fantastische Benutzeroberfläche bieten.


Alessandro Del Soleist seit 2008 ein Microsoft MVP. Er wurde bereits fünf Mal als MVP des Jahres ausgezeichnet und ist der Autor zahlreicher Bücher, eBooks, Videoanleitungen und Artikel zur .NET-Entwicklung mit Visual Studio. Del Sole arbeitet als Experte für die Lösungsentwicklung für Brain-Sys (brain-sys.it) mit dem Schwerpunkt .NET-Entwicklung, Training und Consulting. Sie können ihm auf Twitter folgen: @progalex..

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: James McCaffrey und James Montemagno