Erkennen von Prominenten mit der Twilio-API für WhatsApp, AWS Rekognition und Ruby

April 02, 2019
Autor:in:
Phil Nash
Twilion

Erkennen von Prominenten mit der Twilio-API für WhatsApp, AWS Rekognition und Ruby


Hallo und Danke fürs Lesen! Dieser Blogpost ist eine Übersetzung von Go celebrity spotting with the Twilio API for WhatsApp, AWS Rekognition and Ruby. Während wir unsere Übersetzungsprozesse verbessern, würden wir uns über Dein Feedback an help@twilio.com freuen, solltest Du etwas bemerken, was falsch übersetzt wurde. Wir bedanken uns für hilfreiche Beiträge mit Twilio Swag :)

Wusstest du schon, dass du Medien über die Twilio-API für WhatsApp senden und empfangen kannst? Als ich davon erfahren habe, dachte ich mir: Warum nicht etwas Spaß damit haben und die API mit AWS Rekognition kombinieren, um herauszufinden, ob ich einem Prominenten ähnlich sehe.

In diesem Blog zeige ich, wie wir eine App erstellen, mit der wir ein Bild an eine WhatsApp-Nummer senden, das Bild herunterladen und mit der AWS Rekognition-API analysieren und anschließend eine Antwort senden, die angibt, ob sich Prominenten auf dem Bild befinden.

Das brauchen wir

Zum Erstellen dieser App benötigen wir ein paar Dinge:

Steht alles bereit? Dann können wir loslegen.

Anwendungsgrundlagen

Wenn Twilio eine WhatsApp-Nachricht empfängt, sendet es eine HTTP-Anfrage (einen WebHook) an eine von uns bereitgestellte URL. Wir müssen eine Anwendung erstellen, die diese WebHooks empfangen, das Bild mit dem AWS Rekognition-Dienst verarbeiten und dann eine Nachricht als Antwort an Twilio zurücksenden kann.

Wir erstellen ein Verzeichnis, in dem wir die Anwendung erzeugen möchten, und initialisieren eine neue Gemfile mit Bundler:

mkdir celebrity-spotting
cd celebrity-spotting
bundle init

Wir öffnen die Gemfile und fügen die Gems hinzu, die wir für diese Anwendung verwenden möchten:

# frozen_string_literal: true

source "https://rubygems.org"

gem "sinatra", require: "sinatra/base"
gem "aws-sdk"
gem "envyable"
gem "down"
gem "twilio-ruby"

Wir nutzen Sinatra als Web-Framework, um die eingehenden WebHooks von Twilio zu empfangen. Wir benötigen außerdem das AWS-SDK zur Kommunikation mit dem Rekognition-Dienst. Envyable dient dazu, unsere Anmeldeinformationen in Umgebungsvariablen bei der Entwicklung zu speichernDown ist ein Gem, mit dem das Herunterladen von Dateien ein Kinderspiel ist. Und mit dem twilio-ruby-Gem erzeugen wir die TwiML, damit wir eine Antwort an Twilio zurücksenden können.

Wir führen bundle install aus, um die Gems zu installieren, und erstellen dann die anderen Dateien, die wir für diese App benötigen: app.rbconfig.ru und config/env.yml. Damit ist die Vorbereitung abgeschlossen. Jetzt können wir mit dem Erstellen der Anwendung beginnen.

Erstellen der App

Mit config.ru laden wir die Anwendung und führen sie aus. Wir fügen config.ru den folgenden Code hinzu:

require "bundler"
Bundler.require

Envyable.load("./config/env.yml") unless ENV["RACK_ENV"] == "production"

require "./app.rb"

run CelebritySpotting

Dadurch müssen alle Abhängigkeiten in der Gemfile definiert werden. Außerdem wird unsere Konfiguration über Envyable in die Umgebung geladen und schließlich die Anwendung selbst geladen und ausgeführt. Als Nächstes erstellen wir die CelebritySpotting-App.

Wir öffnen app.rb und erstellen eine neue Klasse:

class CelebritySpotting < Sinatra::Base

end

Wir benötigen einen Pfad zu einem Endpunkt, den wir als unsere WebHook-URL bereitstellen können. Twilio stellt standardmäßig eine POST-Anfrage, deshalb antwortet unser Endpunkt auf POST-Anfragen:

class CelebritySpotting < Sinatra::Base
  post "/messages" do

  end
end

Unsere Antwort wird als TwiML zurückgegeben, deshalb erstellen wir eine neue Twilio::TwiML::MessagingResponse und legen den Content-Type-Header auf application/xml fest:

class CelebritySpotting < Sinatra::Base
  post "/messages" do
    content_type "application/xml"
    twiml = Twilio::TwiML::MessagingResponse.new
  end
end

Um sicherzugehen, ob alles soweit funktioniert, fügen wir eine Nachricht hinzu, geben die TwiML als XML zurück und testen die Anwendung:

class CelebritySpotting < Sinatra::Base
  post "/messages" do
    content_type "application/xml"
    twiml = Twilio::TwiML::MessagingResponse.new
    twiml.message body: "Hello! Just testing here."
    twiml.to_xml
  end
end

Wir rufen die Anwendung über die Befehlszeile folgendermaßen auf:

bundle exec rackup

Die Anwendung wird unter http://localhost:9292 gestartet. Es gibt keine Benutzeroberfläche, deshalb führen wir den Test mithilfe von curl durch, um zu sehen, ob sich die Anwendung richtig verhält.

$ curl -d "" http://localhost:9292/messages
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Message>Hello! Just testing here.</Message>
</Response>

Wir sehen, dass die Nachricht in der TwiML zurückgegeben wird. Jetzt können wir die Anwendung mit der Twilio-API für WhatsApp verbinden.

Herstellen einer Verbindung zur Twilio-API für WhatsApp

Twilio stellt eine Sandbox bereit, um WhatsApp-Integrationen zu testen, ohne dass wir darauf warten müssen, bis WhatsApp eine Twilio-Nummer genehmigt. Wir melden uns an der Twilio-Konsole an und folgen den Anweisungen zum Einrichten der WhatsApp-Sandbox.

Nachdem wir die Sandbox eingerichtet haben, müssen wir eine WebHook-URL festlegen, damit wir die WhatsApp-Sandboxnummer konfigurieren können.

Unsere App wird aktuell auf unserem Computer ausgeführt. Wir müssen also einen Tunnel zum öffentlichen Internet erstellen. Hier kommt ngrok ins Spiel. Zum Starten von ngrok führen wir den folgenden Befehl aus:

ngrok http 9292

Wenn wir diesen Befehl ausführen, erhalten wir eine öffentliche URL, die ungefähr so aussieht: https://RANDOM_STRING.ngrok.io. Wir kopieren diese ngrok-URL, fügen den /messages-Pfad hinzu und geben sie in unsere WhatsApp-Sandboxeinstellungen als URL ein, die aufgerufen werden soll, wenn eine Nachricht von WhatsApp eingeht.

Wir geben unsere ngrok-URL in das Feld „When a message comes in“ in der Twilio-Sandbox für WhatsApp ein

Wir speichern die Einstellungen für die WhatsApp-Sandbox und senden eine Nachricht an die Sandboxnummer. Wir sollten unsere Testnachricht zurückerhalten.

Wenn wir eine Nachricht an unsere WhatsApp-Sandbox-Nummer senden, erhalten wir die Antwort „Hello! Just testing here.“ (Hallo! Dies ist nur ein Test.)

Wir haben jetzt erfolgreich zu WhatsApp eine Verbindung hergestellt und können Nachrichten hin- und hersenden. Das bildet die Grundlage für die Arbeit mit den enthaltenen Bildern und ihrer Analyse mit AWS Rekognition.

Empfangen und Herunterladen von Bildern

Zu Beginn hatten wir das Down-Gem in die Anwendung eingefügt. Mit diesem Gem laden wir die Bilder herunter, die an unsere WhatsApp-Nummer gesendet werden.

Wir rufen noch einmal die Datei app.rb auf und prüfen, ob die eingehende Nachricht Bilder enthält. Wenn ja, laden wir das erste Bild herunter.

Twilio sendet alle erforderlichen Informationen im Text der WebHook-Anfrage. Wir suchen nach dem NumMedia-Parameter, der uns Aufschluss darüber gibt, ob Medien in der Nachricht enthalten sind. Falls dies der Fall ist, befindet sich die Bild-URL im MediaUrl0-Parameter.

Anhand dieses MediaUrl0-Parameters können wir mit Down das Bild herunterladen. Wenn wir mit Down ein Bild herunterladen, erhalten wir eine Tempfile. Wir können diese Datei oder ihre Eigenschaften lesen.

Wenn wir mit der Tempfile fertig sind, sollten wir sie mit der Methode close! schließen und die Verknüpfung aufheben, damit sie nicht unnötig Speicherplatz im Betriebssystem belegt. Wir müssen auch den Fall berücksichtigen, wenn kein Bild in der Nachricht enthalten ist. In einem solchen Fall können wir mit einer Nachricht antworten, in der wir um ein Bild bitten.

Wir löschen die Testnachricht und fügen den folgenden Code hinzu:

  post "/messages" do
    content_type = "text/xml"
    twiml = Twilio::TwiML::MessagingResponse.new
    if params["NumMedia"].to_i > 0
      tempfile = Down.download(params["MediaUrl0"])
      begin
        twiml.message body: "Thanks for the image! It's #{tempfile.size} bytes large."
      ensure
        tempfile.close!
      end
    else
      twiml.message body: "I can't look for celebrities if you don't send me a picture!"
    end
    twiml.to_xml
  end

Wir starten die Anwendung neu und senden einige weitere Testnachrichten mit und ohne Bildern, um sicherzustellen, dass die Ergebnisse auch unseren Erwartungen entsprechen.

Jetzt können wir endlich damit beginnen, nach Prominenten in den Bildern zu suchen. Zeit für AWS Rekognition.

AWS Rekognition

Bevor wir API-Aufrufe an AWS senden können, benötigen wir einen Zugriffsschlüssel und ein Geheimnis. In der AWS-Konsole erstellen wir einen Benutzer mit der AmazonRekognitionFullAccess-Richtlinie.

AWS bietet viele Möglichkeiten, um Benutzer zu erstellen und Berechtigungen zu erteilen. Anhand des folgenden Beispiels können wir einen API-Benutzer mit Zugriff auf den Rekognition-Dienst erstellen.

Wir beginnen auf der Startseite der AWS-Konsole. Dort wählen wir im Feld „Find Services“ die Option „IAM“ aus.

Wir suchen in der AWS Managementkonsole nach „IAM“ und wählen das Ergebnis aus der Dropdown-Liste aus.

Im Abschnitt „IAM“ klicken wir in der linken Navigationsleiste auf das Menü „Users“ und dann auf die Schaltfläche „Add user“.

Im IAM-Dashboard navigieren wir zu „Users“ und klicken dann auf „Add user“.

Wir geben dem Benutzer einen Namen, aktivieren das Kontrollkästchen „Programmatic Access“ und klicken dann auf „Next: Permissions“.

Wir fügen im Feld „User name“ einen eindeutigen Namen für unseren Benutzer hinzu. Wir aktivieren unter „Access type“ „Programmatic access“.

Wenn wir die Option „Attach existing policies directly“ auswählen, wird eine Tabelle mit Richtlinien angezeigt. Wir suchen nach den Richtlinien für „Rekognition“. Wir sehen drei Richtlinien. Unter den drei Richtlinien wählen wir die AmazonRekognitionFullAccess-Richtlinie mit der Beschreibung „Access to all Amazon Rekognition APIs“ aus.

Wir wählen beim Festlegen von Berechtigungen das direkte Anhängen von Richtlinien aus, suchen nach Rekognition und wählen die Richtlinie für den vollständigen Zugriff aus.

Dann klicken wir auf „Next“, bis wir die Erfolgsmeldung sehen.

Auf der Erfolgsseite finden wir unsere Access key ID und den Secret access key

Auf dieser Seite finden wir die „Access key ID“ und den „Secret access key“. Wir speichern beide in der config/env.yml zusammen mit einer AWS-Region, in der Rekognition verfügbar ist, z. B. „us-east-1“.  Weitere Informationen über dieses Verfahren findest du in der Dokumentation zur Authentifizierung und Zugriffssteuerung für Rekognition.

AWS_ACCESS_KEY_ID: YOUR_KEY_ID
AWS_SECRET_ACCESS_KEY: YOUR_SECRET_KEY
AWS_REGION: us-east-1

Um Prominente in Bildern zu erkennen, müssen wir jetzt einen Client erstellen, der die AWS-API verwendet und das Bild an den RecognizeCelebrities-Endpunkt sendet. Wir fügen innerhalb des begin-Blocks den folgenden Code hinzu:

      begin  
        client = Aws::Rekognition::Client.new
        response = client.recognize_celebrities image: { bytes: tempfile.read }
      ensure
        tempfile.close!
      end

Das Ruby-AWS-SDK ruft automatisch unsere Anmeldeinformationen aus der Umgebung ab. Wir lesen dann das heruntergeladene Bild und senden es als Bytes an die recognize_celebrities-Methode des Clients.

In der response sind alle Details zu den erkannten Gesichtern enthalten und dazu, ob es sich wahrscheinlich um Prominente handelt. Die Antwort können wir ganz nach unseren Wünschen erstellen. Ich habe mich dazu entschieden, dass in der Antwort enthalten sein soll, ob sich Prominente auf dem Bild befinden, oder falls dies nicht der Fall ist, wie viele Gesichter erkannt wurden:

        if response.celebrity_faces.any?
          if response.celebrity_faces.count == 1
            celebrity = response.celebrity_faces.first
            twiml.message body: "Ooh, I am #{celebrity.match_confidence}% confident this looks like #{celebrity.name}."
          else
            twiml.message body: "I found #{response.celebrity_faces.count} celebrities in this picture. Looks like #{to_sentence(response.celebrity_faces.map { |face| face.name }) } are in the picture."
          end
        else
          case response.unrecognized_faces.count
          when 0
            twiml.message body: "I couldn't find any faces in that picture. Maybe try another pic?"
          when 1
            twiml.message body: "I found 1 face in that picture, but it didn't look like any celebrity I'm afraid."
          else
            twiml.message body: "I found #{response.unrecognized_faces.count} faces in that picture, but none of them look like celebrities."
          end
        end

Außerdem habe ich eine kurze Hilfsfunktion hinzugefügt, die eine Liste mit Namen in einen lesbaren Satz umwandelt:

def to_sentence(array)
  return array.to_s if array.length <= 1
  "#{array[0..-2].join(", ")} and #{array[-1]}"
end

Wir starten die App noch einmal und senden ein Bild an die WhatsApp-Nummer. Wie sich herausgestellt hat, sehe ich keinem Prominenten wirklich ähnlich, um eine Übereinstimmung mit Rekognition zu erhalten, deshalb habe ich es mit einem Bild mit richtigen Prominenten versucht. Ich habe an mich ein paar Bilder mit Prominenten gesendet, wie dieses hier, um die Ergebnisse zu prüfen.

Beim Senden des Oscars-Fotos von Ellen DeGeneres voller Prominenter entdeckt Rekognition Bradley Cooper, Ellen DeGeneres und Kevin Spacey.

Auf diesem Bild sind noch weitere Prominente vertreten, die Rekognition nicht erkannt hat.

WhatsApp, Bilder, AWS und Prominente

In diesem Blog haben wir erfahren, wie wir Bilder empfangen, die über die Twilio-API für WhatsApp an eine WhatsApp-Nummer gesendet werden, wie diese Bilder mit Down heruntergeladen und dann mit AWS Rekognition nach Prominenten durchsucht werden. Der vollständige Code zu diesem Blog befindet sich in diesem GitHub-Repository.

Das ist aber erst der Anfang. Rekognition bietet eine Fülle an Tools für die Analyse von Bildern, einschließlich der Erkennung von Objekten und SzenenText und sogar Nacktszenen oder anzüglichen Inhalten.

Bei dieser Anwendung handelt es sich um eine kleine Sinatra-App, aber wir können das Ganze auch in Rails implementieren. Das Herunterladen von Bildern und die Verwendung der Rekognition-APIs dauert ziemlich lange. Deshalb ist es vielleicht besser, diese API-Aufrufe mit ActiveJob zu verzögern und stattdessen mit der REST-API zu antworten. Es lohnt sich, die Reaktionszeiten in Betracht zu ziehen, da Twilio Webhooks nur 15 Sekunden wartet, bevor es eine Zeitüberschreitung ausgibt.

Hast du schon etwas Interessantes mit der Bildanalyse erstellt? Dann würde ich gern davon erfahren. Hinterlasse einfach einen Kommentar oder kontaktiere mich auf Twitter unter @philnash.