
Teilen Sie:
Michael ist der kahlköpfige, bärtige Baumeister. Mit seiner 20-jährigen Erfahrung in der Software-Entwicklung und DevOps verbringt er seine Tage damit, anderen zum Erfolg zu verhelfen.
Stimmungsanalyse mit Azure Face API und Vonage
Lesedauer: 6 Minuten
Sie kennen diese Person. Es könnte Ihr Lebensgefährte sein, ein Kind, ein Kollege oder ein Freund. Die Person, die etwas sagt, aber an deren Gesicht Sie erkennen können, dass sie etwas ganz anderes meint. Wahrscheinlich haben Sie sich diese Person gerade in Ihrem Kopf vorgestellt. Vielleicht erinnern Sie sich noch genau an das Gespräch. Vielleicht lief es so ab:
Sie: Okay?
Sie: Gut.
Spoiler-Alarm: Es war nicht in Ordnung.
Wäre es nicht großartig, wenn Sie die Stimmung hinter dem, was sie sagen, kennen würden? Mit Vonage Video API und Azure's Face API können Sie das!
In diesem Tutorial werden wir eine Videokonferenz mit mehreren Teilnehmern aufbauen, die es uns ermöglicht, die Stimmung jedes Teilnehmers anhand seines Gesichtsausdrucks zu analysieren. Dann zeigen wir diese Stimmung als Emoji über dem Video an.
Voraussetzungen
Bevor Sie beginnen, benötigen Sie einige Dinge:
Ein Vonage Video-API-Konto - kostenlos einrichten wenn Sie es noch nicht haben
Ein kostenloses Azure-Konto mit Gesicht API Cognitive Service
Fakultativ: Ngrok für den Testeinsatz
Möchten Sie zum Ende springen? Sie finden den gesamten Quellcode für dieses Tutorial auf GitHub.
Erste Schritte
Wir werden JavaScript verwenden, um die schwere Arbeit zu erledigen, also lassen Sie uns HTML und CSS aus dem Weg räumen.
mkdir video-sentiment
cd video-sentimentErstellen Sie im Stammverzeichnis des Ordners video-sentiment eine index.html Datei und kopieren Sie das Folgende hinein.
<!DOCTYPE html>
<html>
<head>
<title>Vonage Video API Sentiment Analysis</title>
<link href="https://emoji-css.afeld.me/emoji.css" rel="stylesheet" type="text/css" />
<link href="css/app.css" rel="stylesheet" type="text/css" />
<a href="https://static.opentok.com/v2/js/opentok.min.js">https://static.opentok.com/v2/js/opentok.min.js</a>
<!-- Polyfill for fetch API so that we can fetch the sessionId and token in IE11 -->
<a href="https://cdn.jsdelivr.net/npm/promise-polyfill@7/dist/polyfill.min.js">https://cdn.jsdelivr.net/npm/promise-polyfill@7/dist/polyfill.min.js</a>
<a href="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.min.js">https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.min.js</a>
</head>
<body>
<div id="videos">
<div id="subscriber"></div>
<div id="publisher"></div>
</div>
<!-- Footer will go here -->
<a href="http://js/config.js">http://js/config.js</a>
<a href="http://js/app.js">http://js/app.js</a>
</body>
</html>
Als nächstes erstellen Sie ein css Verzeichnis und fügen eine app.css Datei hinzu. Kopieren Sie das unten stehende CSS in diese Datei.
body,
html {
height: 100%;
background-color: black;
margin: 0;
padding: 0;
font-family: Arial, Helvetica, sans-serif;
}
#videos {
width: 100%;
height: 50%;
margin-left: auto;
margin-right: auto;
}
#subscriber {
width: 100%;
height: 100%;
}
#publisher {
position: absolute;
bottom: 50px;
right: 0px;
z-index: 100;
}
.OT_subscriber {
width: 300px !important;
height: 200px !important;
float: left;
margin: 5px !important;
}
.OT_widget-container {
padding: 6px 0 0 6px !important;
background-color: #70B7FD !important;
}
#publisher .OT_widget-container {
padding: 6px 0 0 6px !important;
background-color: hotpink !important;
}
.sentiment {
position: absolute;
z-index: 9000;
height: 100px;
width: 100px;
font-size: 48px;
}
footer {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 30px;
padding: 10px;
background-color: gray;
}
button {
font-size: 16px;
padding: 5px 10px;
display: inline;
}
ul {
float: right;
display: inline;
list-style: none;
padding: 5px 10px;
margin: 0;
}
li {
display: inline;
background-color: lightgrey;
padding: 10px;
display: none;
}
li.used {
display: inline;
} Konfigurieren wir uns
Großartig! Jetzt können wir das hübsche HTML und CSS in die Tat umsetzen. Erstellen Sie einen js Ordner und fügen Sie eine config.js Datei hinzu.
Die Datei config.js enthält Konfigurationsparameter, die wir von unserer Vonage Video-API und unseren Azure-Konten erhalten werden. Kopieren Sie das Folgende in die Datei config.js Datei.
// Replace these values with those generated in your Vonage Video API and Azure Accounts
const OPENTOK_API_KEY = '';
const OPENTOK_SESSION_ID = '';
const OPENTOK_TOKEN = '';
const AZURE_FACE_API_SUBSCRIPTION_KEY = '';
const AZURE_FACE_API_ENDPOINT = ''; OpenTok Einstellungen
Wir bekommen die OPENTOK_API_KEY, OPENTOK_SESSION_ID und OPENTOK_TOKEN Variablen von unserem Vonage Video API-Konto.
Klicken Sie in Ihrem Vonage Video-API-Konto auf das Menü "Projekte" und "Neues Projekt erstellen" und dann auf die Schaltfläche "Benutzerdefiniertes Projekt erstellen". Geben Sie Ihrem neuen Projekt einen Namen und klicken Sie auf die Schaltfläche "Erstellen". Sie können den bevorzugten Codec auf "VP8" setzen.
(/content/blog/sentiment-analysis-with-azure-face-api-and-vonage/tb-project-created.png "Screenshot des Dialogs "Projekt erstellt" innerhalb eines Vonage Video API-Kontos.")
Sie können dann Ihren API-Schlüssel kopieren und ihn als Wert für die OPENTOK_API_KEY Einstellung ein.
Klicken Sie dann auf "Projekt anzeigen". Unten auf der Projektdetailseite finden Sie die Projektwerkzeuge, mit denen Sie eine Sitzungs-ID und ein Token erstellen können. Wählen Sie für den Medienmodus Ihrer Sitzung "Routed" und klicken Sie auf die Schaltfläche "Sitzungs-ID erstellen". Kopieren Sie dann die erstellte Sitzungs-ID und fügen Sie sie als Wert der OPENTOK_SESSION_ID Einstellung ein.
Fügen Sie schließlich die generierte Sitzungs-ID in das Feld Sitzungs-ID des Formulars "Token generieren" ein und klicken Sie auf die Schaltfläche "Token generieren". Kopieren Sie das generierte Token als Wert der OPENTOK_TOKEN Einstellung.
Project Tools area of a specific project in a Vonage Video API account.
Azure Face API-Einstellungen
Melden Sie sich bei Ihrem Azure-Konto an und erstellen Sie einen neuen Face API Cognitive Service. Klicken Sie nach der Erstellung auf den Dienst und gehen Sie zum Blatt "Schnellstart". Dort finden Sie Ihre Key und Endpoint. Kopieren Sie diese beiden Werte in die Felder AZURE_FACE_API_SUBSCRIPTION_KEY und AZURE_FACE_API_ENDPOINT bzw. in die Einstellungen.
Screenshot of the Quick start blade within Azure for the Face API service.
Ich fühle mich gesehen
Nachdem unsere Konfiguration fertig ist, fügen wir nun JavaScript hinzu, um eine Verbindung zu einer OpenTok-Sitzung herzustellen. Füge eine app.js Datei in den js-Ordner und kopieren Sie das Folgende hinein.
var opentok_api_key;
var opentok_session_id;
var opentok_token;
var azure_face_api_subscription_key;
var azure_face_api_endpoint;
// See the config.js file.
if (OPENTOK_API_KEY &&
OPENTOK_SESSION_ID &&
OPENTOK_TOKEN &&
AZURE_FACE_API_SUBSCRIPTION_KEY &&
AZURE_FACE_API_ENDPOINT) {
opentok_api_key = OPENTOK_API_KEY;
opentok_session_id = OPENTOK_SESSION_ID;
opentok_token = OPENTOK_TOKEN;
azure_face_api_subscription_key = AZURE_FACE_API_SUBSCRIPTION_KEY;
azure_face_api_endpoint = AZURE_FACE_API_ENDPOINT;
initializeSession();
} else {
alert('Failed to get configuration variables. Make sure you have updated the config.js file.');
}
// Handling all of our errors here by logging them to the console
function handleError(error) {
if (error) {
console.log(error.message);
}
}
function dataURItoBlob(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(",")[0].indexOf("base64") >= 0)
byteString = atob(dataURI.split(",")[1]);
else byteString = unescape(dataURI.split(",")[1]);
// separate out the mime component
var mimeString = dataURI
.split(",")[0]
.split(":")[1]
.split(";")[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], { type: mimeString });
}
var streams = [];
var emotions = [];
Hier sind vier Dinge im Gange:
Wir laden die Variablen, die wir in der Datei
config.jsDateiWir erstellen eine
handleErrorMethode, die wir durchgehend verwenden, wenn ein Fehler auftrittWir fügen eine
dataURItoBlobMethode hinzu, mit der wir ein base64/URLEncodiertes Bild in einen Blob konvertieren, der an die Azure Face API gesendet wirdWir haben zwei Arrays mit den Namen
streamsundemotions
Das Array streams Array enthält alle aktiven Teilnehmerströme, so dass wir auf sie zugreifen können, um Bilder zu erfassen und an die Azure Face API zu senden.
Das Array emotions Array enthält Zeichenketten, die alle von der Azure Face API zurückgegebenen Emotionen darstellen. Dies wird verwendet, um dem Benutzer dynamisch eine Legende von Emojis anzuzeigen.
Initialisierung der OpenTok-Sitzung
Fügen Sie die initializeSession Methode unten am Ende der app.js Datei hinzu.
function initializeSession() {
var session = OT.initSession(opentok_api_key, opentok_session_id);
// Subscribe to a newly created streams and add
// them to our collection of active streams.
session.on("streamCreated", function (event) {
streams.push(event.stream);
session.subscribe(
event.stream,
"subscriber",
{
insertMode: "append"
},
handleError
);
});
// Remove streams from our array when they are destroyed.
session.on("streamDestroyed", function (event) {
streams = streams.filter(f => f.id !== event.stream.id);
});
// Create a publisher
var publisher = OT.initPublisher(
"publisher",
{
insertMode: "append"
},
handleError
);
// Connect to the session
session.connect(opentok_token, function (error) {
// If the connection is successful, initialize a publisher and publish to the session
if (error) {
handleError(error);
} else {
session.publish(publisher, handleError);
}
});
}
Die Methode initializeSession Methode initialisiert unseren Vonage Video API-Client mit der Sitzung, die wir mit der Sitzungs-ID angegeben haben. Sie fügt dann Ereignishandler für die streamCreated und streamDestroyed Ereignisse hinzu, um das Hinzufügen und Entfernen von Streams aus unserem streams Array zu verwalten. Schließlich wird eine Verbindung zur Sitzung mit dem Token hergestellt, das wir in unserer config.js Datei festgelegt haben.
Sie können nun die index.html in Chrome oder Firefox öffnen. Wenn Sie die Seite laden, müssen Sie dem Browser möglicherweise erlauben, auf Ihre Webcam und Ihr Mikrofon zuzugreifen. Danach sollten Sie einen Videostream von sich selbst (oder was auch immer Ihre Webcam anschaut) auf der Seite sehen.
Wenn das funktioniert hat, schalten Sie den Ton stumm, öffnen Sie eine weitere Registerkarte (wobei die ursprüngliche geöffnet bleibt) und laden Sie die gleiche Datei. Sie sollten nun ein zweites Video sehen können.
Tipp zur Fehlerbehebung: Wenn auf der Seite kein Video angezeigt wird, öffnen Sie die Registerkarte "Konsole" in Ihren Browser-Tools (Befehl+Option+i auf Mac, STRG+i auf Windows) und suchen Sie nach Fehlern. Das wahrscheinlichste Problem ist, dass Ihr Vonage Video-API-Schlüssel, Ihre Sitzungs-ID oder Ihr Token nicht richtig eingerichtet ist. Da Sie Ihre Anmeldeinformationen hartcodiert haben, ist es auch möglich, dass Ihr Token abgelaufen ist.
Ich kenne diesen Blick
Jetzt können wir die Teilnehmer sehen und hören, aber was sagt uns ihr Gesicht, was ihr Mund nicht sagt? Fügen wir eine Schaltfläche hinzu, mit der wir jeden Teilnehmer analysieren können.
In der Datei index.html Datei ersetzen Sie den Kommentar, der lautet <!-- Footer will go here --> durch den folgenden:
<footer>
<button id="analyze" type="button" onclick="processImages();">Analyze</button>
<ul>
<li name="em-angry"><i class="em em-angry"></i> Angry</li>
<li name="em-frowning"><i class="em em-frowning"></i> Contempt</li>
<li name="em-face_vomiting"><i class="em em-face_vomiting"></i> Disgust</li>
<li name="em-fearful"><i class="em em-fearful"></i> Fear</li>
<li name="em-grin"><i class="em em-grin"></i> Happiness</li>
<li name="em-neutral_face"><i class="em em-neutral_face"></i> Neutral</li>
<li name="em-cry"><i class="em em-cry"></i> Sadness</li>
<li name="em-astonished"><i class="em em-astonished"></i> Surprise</li>
</ul>
</footer>Dadurch wird unten auf der Seite eine Fußzeile mit einer Schaltfläche "Analysieren" und einer ungeordneten Liste hinzugefügt, die wir als Legende zwischen Emojis und Gefühlen verwenden werden.
Fügen wir nun das JavaScript hinzu, um unsere Stimmungsanalyse durchzuführen. Fügen Sie das Folgende am Ende der Datei app.js Datei hinzu.
function assignEmoji(emojiClass, index) {
var widgets = document.getElementsByClassName('OT_widget-container');
emotions.push(emojiClass);
var sentimentDiv = document.createElement("div");
sentimentDiv.classList.add("sentiment");
sentimentDiv.classList.add("em");
sentimentDiv.classList.add(emojiClass);
widgets[index].appendChild(sentimentDiv);
const legendEl = document.getElementsByName(emojiClass);
legendEl[0].classList.add('used');
}
function processEmotion(faces, index) {
// for each face identified in the result
for (i = 0; i
memo[1] > value ? memo : [key, value]
);
let emojiClass = 'em-neutral_face';
switch (maxEmotion[0]) {
case 'angry':
emojiClass = 'em-angry';
break;
case 'contempt':
emojiClass = 'em-frowning';
break;
case 'disgust':
emojiClass = 'em-face_vomiting';
break;
case 'fear':
emojiClass = 'em-fearful';
break;
case 'happiness':
emojiClass = 'em-grin';
break;
case 'sadness':
emojiClass = 'em-cry';
break;
case 'surprise':
emojiClass = 'em-astonished';
break;
default:
break;
}
assignEmoji(emojiClass, index);
}
}
// Gets a <video> element and draws it to a new
// canvas object. Then creates a jpeg blob from that
// canvas and sends to Azure Face API to get emotion
// data.
function sendToAzure(video, index) {
// Get the stream object associated with this
// <video> element.
var stream = streams[index];
var canvas = document.createElement("canvas");
canvas.height = stream.videoDimensions.height;
canvas.width = stream.videoDimensions.width;
var ctx = canvas.getContext("2d");
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
var dataURL = canvas.toDataURL("image/jpeg", 0.8);
var blob = dataURItoBlob(dataURL);
var fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
// Perform the REST API call.
var uriBase = `${azure_face_api_endpoint}/face/v1.0/detect`;
// Request parameters.
var params = 'returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=emotion';
const xhr = new XMLHttpRequest();
xhr.open('POST', `${uriBase}?${params}`);
xhr.responseType = 'json';
xhr.setRequestHeader('Content-Type', 'application/octet-stream');
xhr.setRequestHeader("Ocp-Apim-Subscription-Key", azure_face_api_subscription_key);
xhr.send(blob);
xhr.onload = () => {
if (xhr.status == 200) {
processEmotion(xhr.response, index);
} else {
var errorString = `(${xhr.status}) ${xhr.statusText}`;
alert(errorString);
}
}
}
// Reset emojis and loop through all <video> elements and call
// sendToAzure
function processImages() {
emotions = [];
var sentiments = document.getElementsByClassName('sentiment');
var usedListItems = document.getElementsByClassName('used');
var videos = document.getElementsByTagName('video');
// Remove any existing sentiment & emotion objects
if (sentiments.length > 0) {
for (s = 0; s 0) {
for (l = 0; l < usedListItems.length; l++) {
usedListItems[l].classList.remove('used');
}
}
for (v = 0; v < (videos.length - 1); v++) {
sendToAzure(videos[v], v);
}
}Schauen wir uns an, was dieser Code bewirkt.
Die Methode assignEmoji Methode nimmt eine CSS-Klasse auf, die mit der Emotion für einen bestimmten Videostream und dem Index dieses Streams in unserer Benutzeroberfläche verbunden ist. Sie tut das Folgende:
Fügt die angegebene Klasse zu unserem
emotionsArrayFügt ein Div über dem entsprechenden Videopanel mit der Klasse für das anzuzeigende Emoji hinzu
Fügt eine
usedKlasse zu demliin unserer Fußzeile für dieses Emoji, damit es in der Legende angezeigt wird
Die Methode processEmotion Methode empfängt die Gesichtsdaten von der Azure Face API und identifiziert die Emotion mit dem höchsten Ranking. Sie ruft dann assignEmoji mit der entsprechenden CSS-Klasse für diese Emotion und dem Index des zu verarbeitenden Videos auf.
Die Methode sendToAzure Methode erhält ein HTML-Videoelement und den Index dieses Videoobjekts auf unserer Seite. Sie ruft den mit dem Videoelement verbundenen Stream ab und erstellt dann eine HTML-Leinwand mit denselben Abmessungen wie der Stream. Anschließend wird eine Aufnahme des Streams auf die neue Leinwand gezeichnet und eine XMLHttpRequest mit dem erstellten Bild an die Azure Face API gesendet. Die Azure Face API gibt ein JSON-Objekt zurück, das wir dann an die processEmotion Methode senden.
Zum Schluss löscht die processImages Methode alle vorhandenen Emojis aus der Benutzeroberfläche und holt alle HTML-Video-Tags aus dem DOM und sendet sie an die sendToAzure Methode zur Verarbeitung. Diese Methode wird von unserer Schaltfläche "Analysieren" in der Fußzeile aufgerufen.
Was denkst du wirklich?
Wenn wir nun die index.html Seite in unseren Browsern öffnen, können wir auf die Schaltfläche "Analysieren" klicken, um zu sehen, welche Emotion die Gesichts-API von Azure erkannt hat. Im Moment gibt es noch einige Einschränkungen. Wenn die Azure Face API zum Beispiel zwei Gesichter im Frame erkennt, werden Daten für beide zurückgegeben, aber unser Code fügt derzeit nur ein Emoji für das erste hinzu.
Ich bin mir auch nicht sicher, aber vielleicht funktioniert es bei Teenagern nicht. Ich habe meine Tochter im Teenageralter Dutzende Male testen lassen, aber sie bekam nur "Ekel" und "Verachtung" als Emotionen zurück. Vielleicht war das keine so gute Idee. Vielleicht ist es besser, nicht zu wissen, was sie wirklich denken. 😂
Four video frames of Michael showing different sentiments returned from Azure's Face API
Weitere Lektüre
Möchten Sie mehr über die Nutzung der Stimmungsanalyse mit Nexmo erfahren? Sehen Sie sich die folgenden Blogbeiträge an: