1. Übersicht
Im ersten Code-Lab laden Sie Bilder in einen Bucket hoch. Dadurch wird ein Dateierstellungsereignis generiert, das von einer Funktion verarbeitet wird. Die Funktion ruft die Vision API auf, um eine Bildanalyse durchzuführen und die Ergebnisse in einem Datenspeicher zu speichern.
Lerninhalte
- Cloud Storage
- Cloud Functions
- Cloud Vision API
- Cloud Firestore
2. Einrichtung und Anforderungen
Umgebung für das selbstbestimmte Lernen einrichten
- Melden Sie sich in der Google Cloud Console an und erstellen Sie ein neues Projekt oder verwenden Sie ein vorhandenes Projekt. Wenn Sie noch kein Gmail- oder Google Workspace-Konto haben, müssen Sie eines erstellen.
- Der Projektname ist der Anzeigename für die Projektteilnehmer. Es handelt sich um eine Zeichenfolge, die von Google APIs nicht verwendet wird. Sie können sie jederzeit aktualisieren.
- Die Projekt-ID muss für alle Google Cloud-Projekte eindeutig sein und ist unveränderlich. Sie kann nach dem Festlegen nicht mehr geändert werden. Die Cloud Console generiert automatisch einen eindeutigen String. ist Ihnen meist egal, was es ist. In den meisten Codelabs musst du auf die Projekt-ID verweisen, die üblicherweise als
PROJECT_ID
gekennzeichnet ist. Wenn Ihnen die generierte ID nicht gefällt, können Sie eine weitere zufällige ID generieren. Alternativ können Sie einen eigenen verwenden und nachsehen, ob er verfügbar ist. Sie kann nach diesem Schritt nicht mehr geändert werden und bleibt für die Dauer des Projekts bestehen. - Zur Information gibt es noch einen dritten Wert, die Projektnummer, die von manchen APIs verwendet wird. Weitere Informationen zu allen drei Werten finden Sie in der Dokumentation.
- Als Nächstes müssen Sie in der Cloud Console die Abrechnung aktivieren, um Cloud-Ressourcen/APIs verwenden zu können. Dieses Codelab sollte ohne großen Aufwand betrieben werden. Wenn Sie Ressourcen herunterfahren möchten, um über diese Anleitung hinaus keine Kosten zu verursachen, können Sie die von Ihnen erstellten Ressourcen oder das gesamte Projekt löschen. Neue Google Cloud-Nutzer haben Anspruch auf eine kostenlose Testversion von 300$.
Cloud Shell starten
Sie können Google Cloud zwar von Ihrem Laptop aus aus der Ferne bedienen, in diesem Codelab verwenden Sie jedoch Google Cloud Shell, eine Befehlszeilenumgebung, die in der Cloud ausgeführt wird.
Klicken Sie in der Google Cloud Console rechts oben in der Symbolleiste auf das Cloud Shell-Symbol:
Die Bereitstellung und Verbindung mit der Umgebung dauert nur einen Moment. Wenn er abgeschlossen ist, sollten Sie in etwa Folgendes sehen:
Diese virtuelle Maschine verfügt über sämtliche Entwicklertools, die Sie benötigen. Es bietet ein Basisverzeichnis mit 5 GB nichtflüchtigem Speicher und läuft auf Google Cloud, wodurch die Netzwerkleistung und Authentifizierung erheblich verbessert werden. Alle Arbeiten in diesem Codelab können in einem Browser erledigt werden. Sie müssen nichts installieren.
3. APIs aktivieren
Für dieses Lab verwenden Sie Cloud Functions und die Vision API. Die Funktionen müssen jedoch zuerst entweder in der Cloud Console oder mit gcloud
aktiviert werden.
Suchen Sie in der Suchleiste nach Cloud Vision API
, um die Vision API in der Cloud Console zu aktivieren:
Daraufhin gelangen Sie zur Seite mit der Cloud Vision API:
Klicken Sie auf ENABLE
.
Alternativ können Sie es auch in Cloud Shell mit dem gcloud-Befehlszeilentool aktivieren.
Führen Sie in Cloud Shell den folgenden Befehl aus:
gcloud services enable vision.googleapis.com
Der Vorgang sollte jetzt erfolgreich abgeschlossen werden:
Operation "operations/acf.12dba18b-106f-4fd2-942d-fea80ecc5c1c" finished successfully.
Aktivieren Sie auch Cloud Functions:
gcloud services enable cloudfunctions.googleapis.com
4. Bucket erstellen (Console)
Erstellen Sie einen Storage-Bucket für die Bilder. Sie können dies über die Google Cloud Platform Console ( console.cloud.google.com) oder mit dem gsutil-Befehlszeilentool in Cloud Shell oder in Ihrer lokalen Entwicklungsumgebung erledigen.
Zu „Speicherplatz“ gehen
Über das Feld „Hamburger“ (♢) öffnen Sie die Seite "Storage
".
Bucket benennen
Klicken Sie auf die Schaltfläche CREATE BUCKET
.
Klicken Sie auf CONTINUE
.
Standort auswählen
Erstellen Sie einen multiregionalen Bucket in der Region Ihrer Wahl (hier Europe
).
Klicken Sie auf CONTINUE
.
Standardspeicherklasse auswählen
Wählen Sie die Speicherklasse Standard
für Ihre Daten aus.
Klicken Sie auf CONTINUE
.
Zugriffssteuerung festlegen
Da Sie mit öffentlich zugänglichen Bildern arbeiten, möchten Sie, dass alle in diesem Bucket gespeicherten Bilder dieselbe einheitliche Zugriffssteuerung haben.
Wählen Sie die Zugriffssteuerungsoption Uniform
aus.
Klicken Sie auf CONTINUE
.
Schutz/Verschlüsselung festlegen
Behalten Sie die Standardeinstellung bei (Google-managed key)
, da Sie keine eigenen Verschlüsselungsschlüssel verwenden.
Klicken Sie auf CREATE
, um die Bucket-Erstellung abzuschließen.
allUsers als Storage Viewer hinzufügen
Rufen Sie den Tab Permissions
auf:
Fügen Sie dem Bucket ein allUsers
-Mitglied mit der Rolle Storage > Storage Object Viewer
hinzu:
Klicken Sie auf SAVE
.
5. Bucket erstellen (gsutil)
Sie können Buckets auch mit dem gsutil
-Befehlszeilentool in Cloud Shell erstellen.
Legen Sie in Cloud Shell eine Variable für den eindeutigen Bucket-Namen fest. In Cloud Shell ist GOOGLE_CLOUD_PROJECT
bereits auf Ihre eindeutige Projekt-ID festgelegt. Sie können dies an den Bucket-Namen anhängen.
Beispiel:
export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}
Erstellen Sie eine multiregionale Standardzone in Europa:
gsutil mb -l EU gs://${BUCKET_PICTURES}
Achten Sie auf einheitlichen Zugriff auf Bucket-Ebene:
gsutil uniformbucketlevelaccess set on gs://${BUCKET_PICTURES}
Veröffentlichen Sie den Bucket:
gsutil iam ch allUsers:objectViewer gs://${BUCKET_PICTURES}
Wenn Sie in der Console zum Abschnitt Cloud Storage
gehen, sollten Sie einen öffentlichen uploaded-pictures
-Bucket haben:
Testen Sie, ob Sie Bilder in den Bucket hochladen können und die hochgeladenen Bilder öffentlich zugänglich sind, wie im vorherigen Schritt erläutert.
6. Öffentlichen Zugriff auf den Bucket testen
Wenn Sie zum Storage-Browser zurückkehren, sehen Sie Ihren Bucket in der Liste mit „Öffentlich“ -Zugriff (einschließlich eines Warnsymbols, das Sie daran erinnert, dass jede Person Zugriff auf den Inhalt des Buckets hat).
Ihr Bucket ist jetzt bereit zum Empfangen von Bildern.
Wenn Sie auf den Bucket-Namen klicken, werden die Bucket-Details angezeigt.
Dort können Sie mit der Schaltfläche Upload files
testen, ob Sie dem Bucket ein Bild hinzufügen können. Sie werden in einem Pop-up-Fenster zur Dateiauswahl aufgefordert, eine Datei auszuwählen. Nach der Auswahl wird sie in Ihren Bucket hochgeladen und Sie sehen wieder den public
-Zugriff, der dieser neuen Datei automatisch zugeordnet wurde.
Neben dem Public
-Zugriffslabel siehst du auch ein kleines Linksymbol. Wenn Sie darauf klicken, ruft Ihr Browser die öffentliche URL dieses Bildes auf, die folgendes Format hat:
https://storage.googleapis.com/BUCKET_NAME/PICTURE_FILE.png
Dabei ist BUCKET_NAME
der global eindeutige Name, den Sie für Ihren Bucket ausgewählt haben, und dann der Dateiname Ihres Bildes.
Durch Klicken auf das Kästchen neben dem Bildnamen wird die Schaltfläche DELETE
aktiviert und Sie können dieses erste Bild löschen.
7. Funktion erstellen
In diesem Schritt erstellen Sie eine Funktion, die auf Bild-Upload-Ereignisse reagiert.
Rufen Sie in der Google Cloud Console den Bereich Cloud Functions
auf. Durch Aufrufen wird der Cloud Functions-Dienst automatisch aktiviert.
Klicken Sie auf Create function
.
Wählen Sie einen Namen aus (z. B. picture-uploaded
) und der Region (achten Sie darauf, dass dies der Regionsauswahl für den Bucket entspricht):
Es gibt zwei Arten von Funktionen:
- HTTP-Funktionen, die über eine URL aufgerufen werden können (d. h. eine Web-API),
- Hintergrundfunktionen, die durch ein bestimmtes Ereignis ausgelöst werden können.
Sie möchten eine Hintergrundfunktion erstellen, die ausgelöst wird, wenn eine neue Datei in den Cloud Storage
-Bucket hochgeladen wird:
Sie interessieren sich für den Ereignistyp Finalize/Create
. Das ist das Ereignis, das ausgelöst wird, wenn eine Datei im Bucket erstellt oder aktualisiert wird:
Wählen Sie den zuvor erstellten Bucket aus, damit Cloud Functions benachrichtigt werden soll, wenn eine Datei in diesem speziellen Bucket erstellt / aktualisiert wird:
Klicken Sie auf Select
, um den zuvor erstellten Bucket auszuwählen, und dann auf Save
Bevor Sie auf „Weiter“ klicken, können Sie die Standardeinstellungen (256 MB Arbeitsspeicher) unter Laufzeit, Build, Verbindungen und Sicherheitseinstellungen erweitern und auf 1 GB aktualisieren.
Nachdem Sie auf Next
geklickt haben, können Sie die Laufzeit, den Quellcode und den Einstiegspunkt optimieren.
Behalten Sie die Inline editor
für diese Funktion bei:
Wählen Sie eine der Java-Laufzeiten aus, z. B. Java 11:
Der Quellcode besteht aus einer Java
-Datei und einer pom.xml
-Maven-Datei, die verschiedene Metadaten und Abhängigkeiten bereitstellt.
Behalten Sie das Standard-Code-Snippet bei: Es protokolliert den Dateinamen des hochgeladenen Bildes:
Behalten Sie vorerst den Namen der Funktion, die in Example
ausgeführt werden soll, zu Testzwecken bei.
Klicken Sie auf Deploy
, um die Funktion zu erstellen und bereitzustellen. Sobald die Bereitstellung erfolgreich war, sollten Sie in der Liste der Funktionen ein grün eingekreistes Häkchen sehen:
8. Funktion testen
Testen Sie in diesem Schritt, ob die Funktion auf Speicherereignisse reagiert.
Über das Feld „Hamburger“ (♢) auf, navigiere zurück zur Seite "Storage
".
Klicken Sie auf den Bild-Bucket und dann auf Upload files
, um ein Bild hochzuladen.
Rufen Sie noch einmal in der Cloud Console die Seite Logging > Logs Explorer
auf.
Wählen Sie in der Log Fields
-Auswahl Cloud Function
aus, um die Logs aufzurufen, die Ihren Funktionen zugeordnet sind. Scrollen Sie nach unten durch die Logfelder und Sie können sogar eine bestimmte Funktion auswählen, um eine detailliertere Ansicht der funktionsbezogenen Logs zu erhalten. Wählen Sie die Funktion picture-uploaded
aus.
Sie sollten die Log-Elemente sehen, in denen die Erstellung der Funktion, die Start- und Endzeiten der Funktion und unsere eigentliche Log-Anweisung angegeben sind:
Unsere Log-Anweisung lautet: Processing file: pic-a-daily-architecture-events.png
. Das bedeutet, dass das Ereignis, das sich auf die Erstellung und Speicherung dieses Bildes bezieht, tatsächlich wie erwartet ausgelöst wurde.
9. Datenbank vorbereiten
Sie speichern Informationen über das Bild der Vision API in der Cloud Firestore-Datenbank, einer schnellen, vollständig verwalteten, serverlosen, cloudnativen NoSQL-Dokumentendatenbank. Bereiten Sie Ihre Datenbank vor. Rufen Sie dazu in der Cloud Console den Bereich Firestore
auf:
Es werden zwei Optionen angeboten: Native mode
oder Datastore mode
. Verwenden Sie den nativen Modus, der zusätzliche Funktionen wie Offlinesupport und Echtzeitsynchronisierung bietet.
Klicken Sie auf SELECT NATIVE MODE
.
Wählen Sie einen multiregionalen Standort aus (hier in Europa, idealerweise aber in derselben Region wie Ihre Funktion und Ihr Storage-Bucket).
Klicken Sie auf CREATE DATABASE
.
Nachdem die Datenbank erstellt wurde, sollten Sie Folgendes sehen:
Erstellen Sie eine neue Sammlung, indem Sie auf die Schaltfläche + START COLLECTION
klicken.
Sammlung „pictures
“ benennen.
Sie müssen kein Dokument erstellen. Sie fügen sie programmatisch hinzu, wenn neue Bilder in Cloud Storage gespeichert und von der Vision API analysiert werden.
Klicken Sie auf Save
.
Firestore erstellt ein erstes Standarddokument in der neu erstellten Sammlung. Sie können dieses Dokument problemlos löschen, da es keine nützlichen Informationen enthält:
Die Dokumente, die in unserer Sammlung programmatisch erstellt werden, enthalten vier Felder:
- name (String): der Dateiname des hochgeladenen Bildes, der auch der Schlüssel für das Dokument ist
- labels (Array von Strings): die Labels erkannter Elemente von der Vision API
- color (String): der hexadezimale Farbcode der dominanten Farbe (d. h. #ab12ef).
- created (Datum): der Zeitstempel, der angibt, wann die Metadaten dieses Images gespeichert wurden
- thumbnail (boolesch): ein optionales Feld, das angezeigt wird und „true“ ist, wenn eine Miniaturansicht für dieses Bild generiert wurde
Da wir in Firestore nach Bildern suchen, für die Miniaturansichten verfügbar sind, und nach dem Erstellungsdatum sortieren, müssen wir einen Suchindex erstellen.
Sie können den Index mit dem folgenden Befehl in Cloud Shell erstellen:
gcloud firestore indexes composite create \
--collection-group=pictures \
--field-config field-path=thumbnail,order=descending \
--field-config field-path=created,order=descending
Alternativ können Sie auch in der Cloud Console links in der Navigationsspalte auf Indexes
klicken und dann wie unten gezeigt einen zusammengesetzten Index erstellen:
Klicken Sie auf Create
. Die Indexerstellung kann einige Minuten dauern.
10. Funktion aktualisieren
Kehren Sie zur Seite Functions
zurück, um die Funktion zu aktualisieren, um die Vision API aufzurufen, um die Bilder zu analysieren und die Metadaten in Firestore zu speichern.
Über das Feld „Hamburger“ (Ђ) auf, navigieren Sie zum Abschnitt Cloud Functions
, klicken Sie auf den Funktionsnamen, wählen Sie den Tab Source
aus und klicken Sie dann auf die Schaltfläche EDIT
.
Bearbeiten Sie zuerst die Datei pom.xml
, in der die Abhängigkeiten der Java-Funktion aufgelistet sind. Aktualisieren Sie den Code, um die Maven-Abhängigkeit für die Cloud Vision API hinzuzufügen:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cloudfunctions</groupId>
<artifactId>gcs-function</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>26.1.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud.functions</groupId>
<artifactId>functions-framework-api</artifactId>
<version>1.0.4</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-firestore</artifactId>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-vision</artifactId>
</dependency>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-storage</artifactId>
</dependency>
</dependencies>
<!-- Required for Java 11 functions in the inline editor -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<excludes>
<exclude>.google/</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Jetzt, da die Abhängigkeiten auf dem neuesten Stand sind, arbeiten Sie am Code der Funktion. Aktualisieren Sie dazu die Datei Example.java
mit unserem benutzerdefinierten Code.
Bewegen Sie den Mauszeiger auf die Datei Example.java
und klicken Sie auf den Bleistift. Ersetzen Sie den Paketnamen und den Dateinamen durch src/main/java/fn/ImageAnalysis.java
.
Ersetzen Sie den Code in ImageAnalysis.java
durch den folgenden Code. Dies wird im nächsten Schritt erläutert.
package fn;
import com.google.cloud.functions.*;
import com.google.cloud.vision.v1.*;
import com.google.cloud.vision.v1.Feature.Type;
import com.google.cloud.firestore.*;
import com.google.api.core.ApiFuture;
import java.io.*;
import java.util.*;
import java.util.stream.*;
import java.util.concurrent.*;
import java.util.logging.Logger;
import fn.ImageAnalysis.GCSEvent;
public class ImageAnalysis implements BackgroundFunction<GCSEvent> {
private static final Logger logger = Logger.getLogger(ImageAnalysis.class.getName());
@Override
public void accept(GCSEvent event, Context context)
throws IOException, InterruptedException, ExecutionException {
String fileName = event.name;
String bucketName = event.bucket;
logger.info("New picture uploaded " + fileName);
try (ImageAnnotatorClient vision = ImageAnnotatorClient.create()) {
List<AnnotateImageRequest> requests = new ArrayList<>();
ImageSource imageSource = ImageSource.newBuilder()
.setGcsImageUri("gs://" + bucketName + "/" + fileName)
.build();
Image image = Image.newBuilder()
.setSource(imageSource)
.build();
Feature featureLabel = Feature.newBuilder()
.setType(Type.LABEL_DETECTION)
.build();
Feature featureImageProps = Feature.newBuilder()
.setType(Type.IMAGE_PROPERTIES)
.build();
Feature featureSafeSearch = Feature.newBuilder()
.setType(Type.SAFE_SEARCH_DETECTION)
.build();
AnnotateImageRequest request = AnnotateImageRequest.newBuilder()
.addFeatures(featureLabel)
.addFeatures(featureImageProps)
.addFeatures(featureSafeSearch)
.setImage(image)
.build();
requests.add(request);
logger.info("Calling the Vision API...");
BatchAnnotateImagesResponse result = vision.batchAnnotateImages(requests);
List<AnnotateImageResponse> responses = result.getResponsesList();
if (responses.size() == 0) {
logger.info("No response received from Vision API.");
return;
}
AnnotateImageResponse response = responses.get(0);
if (response.hasError()) {
logger.info("Error: " + response.getError().getMessage());
return;
}
List<String> labels = response.getLabelAnnotationsList().stream()
.map(annotation -> annotation.getDescription())
.collect(Collectors.toList());
logger.info("Annotations found:");
for (String label: labels) {
logger.info("- " + label);
}
String mainColor = "#FFFFFF";
ImageProperties imgProps = response.getImagePropertiesAnnotation();
if (imgProps.hasDominantColors()) {
DominantColorsAnnotation colorsAnn = imgProps.getDominantColors();
ColorInfo colorInfo = colorsAnn.getColors(0);
mainColor = rgbHex(
colorInfo.getColor().getRed(),
colorInfo.getColor().getGreen(),
colorInfo.getColor().getBlue());
logger.info("Color: " + mainColor);
}
boolean isSafe = false;
if (response.hasSafeSearchAnnotation()) {
SafeSearchAnnotation safeSearch = response.getSafeSearchAnnotation();
isSafe = Stream.of(
safeSearch.getAdult(), safeSearch.getMedical(), safeSearch.getRacy(),
safeSearch.getSpoof(), safeSearch.getViolence())
.allMatch( likelihood ->
likelihood != Likelihood.LIKELY && likelihood != Likelihood.VERY_LIKELY
);
logger.info("Safe? " + isSafe);
}
// Saving result to Firestore
if (isSafe) {
FirestoreOptions firestoreOptions = FirestoreOptions.getDefaultInstance();
Firestore pictureStore = firestoreOptions.getService();
DocumentReference doc = pictureStore.collection("pictures").document(fileName);
Map<String, Object> data = new HashMap<>();
data.put("labels", labels);
data.put("color", mainColor);
data.put("created", new Date());
ApiFuture<WriteResult> writeResult = doc.set(data, SetOptions.merge());
logger.info("Picture metadata saved in Firestore at " + writeResult.get().getUpdateTime());
}
}
}
private static String rgbHex(float red, float green, float blue) {
return String.format("#%02x%02x%02x", (int)red, (int)green, (int)blue);
}
public static class GCSEvent {
String bucket;
String name;
}
}
11. Funktion kennenlernen
Sehen wir uns die verschiedenen Aspekte genauer an.
Zuerst werden die spezifischen Abhängigkeiten in die Maven-Datei pom.xml
aufgenommen. Google Java-Clientbibliotheken veröffentlichen eine Bill-of-Materials(BOM)
, um Abhängigkeitskonflikte zu vermeiden. Wenn Sie ihn verwenden, müssen Sie keine Version für die einzelnen Google-Clientbibliotheken angeben.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>26.1.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Anschließend bereiten wir einen Client für die Vision API vor:
...
try (ImageAnnotatorClient vision = ImageAnnotatorClient.create()) {
...
Jetzt kommt die Struktur unserer Funktion. Wir erfassen aus dem eingehenden Ereignis die Felder, an denen wir interessiert sind, und ordnen sie der von uns definierten GCSEvent-Struktur zu:
...
public class ImageAnalysis implements BackgroundFunction<GCSEvent> {
@Override
public void accept(GCSEvent event, Context context)
throws IOException, InterruptedException,
ExecutionException {
...
public static class GCSEvent {
String bucket;
String name;
}
Beachten Sie die Signatur, aber auch, wie wir den Namen der Datei und des Buckets abrufen, die die Cloud Functions-Funktion ausgelöst haben.
So sieht die Ereignisnutzlast aus:
{
"bucket":"uploaded-pictures",
"contentType":"image/png",
"crc32c":"efhgyA==",
"etag":"CKqB956MmucCEAE=",
"generation":"1579795336773802",
"id":"uploaded-pictures/Screenshot.png/1579795336773802",
"kind":"storage#object",
"md5Hash":"PN8Hukfrt6C7IyhZ8d3gfQ==",
"mediaLink":"https://www.googleapis.com/download/storage/v1/b/uploaded-pictures/o/Screenshot.png?generation=1579795336773802&alt=media",
"metageneration":"1",
"name":"Screenshot.png",
"selfLink":"https://www.googleapis.com/storage/v1/b/uploaded-pictures/o/Screenshot.png",
"size":"173557",
"storageClass":"STANDARD",
"timeCreated":"2020-01-23T16:02:16.773Z",
"timeStorageClassUpdated":"2020-01-23T16:02:16.773Z",
"updated":"2020-01-23T16:02:16.773Z"
}
Wir bereiten eine Anfrage vor, die über den Vision-Client gesendet werden soll:
ImageSource imageSource = ImageSource.newBuilder()
.setGcsImageUri("gs://" + bucketName + "/" + fileName)
.build();
Image image = Image.newBuilder()
.setSource(imageSource)
.build();
Feature featureLabel = Feature.newBuilder()
.setType(Type.LABEL_DETECTION)
.build();
Feature featureImageProps = Feature.newBuilder()
.setType(Type.IMAGE_PROPERTIES)
.build();
Feature featureSafeSearch = Feature.newBuilder()
.setType(Type.SAFE_SEARCH_DETECTION)
.build();
AnnotateImageRequest request = AnnotateImageRequest.newBuilder()
.addFeatures(featureLabel)
.addFeatures(featureImageProps)
.addFeatures(featureSafeSearch)
.setImage(image)
.build();
Sie benötigen drei wichtige Funktionen der Vision API:
- Labelerkennung: Sie verstehen, was auf den Bildern zu sehen ist.
- Bildeigenschaften: Hiermit können Sie interessante Attribute des Bildes angeben, da wir uns für die Hauptfarbe des Bildes interessieren.
- SafeSearch: Herausfinden, ob das Bild sicher angezeigt werden kann (d. h., es darf keine Inhalte nur für Erwachsene / medizinische / nicht jugendfreie Inhalte / gewalttätige Inhalte enthalten)
Jetzt können Sie die Vision API aufrufen:
...
logger.info("Calling the Vision API...");
BatchAnnotateImagesResponse result =
vision.batchAnnotateImages(requests);
List<AnnotateImageResponse> responses = result.getResponsesList();
...
So sieht die Antwort der Vision API aus:
{
"faceAnnotations": [],
"landmarkAnnotations": [],
"logoAnnotations": [],
"labelAnnotations": [
{
"locations": [],
"properties": [],
"mid": "/m/01yrx",
"locale": "",
"description": "Cat",
"score": 0.9959855675697327,
"confidence": 0,
"topicality": 0.9959855675697327,
"boundingPoly": null
},
✄ - - - ✄
],
"textAnnotations": [],
"localizedObjectAnnotations": [],
"safeSearchAnnotation": {
"adult": "VERY_UNLIKELY",
"spoof": "UNLIKELY",
"medical": "VERY_UNLIKELY",
"violence": "VERY_UNLIKELY",
"racy": "VERY_UNLIKELY",
"adultConfidence": 0,
"spoofConfidence": 0,
"medicalConfidence": 0,
"violenceConfidence": 0,
"racyConfidence": 0,
"nsfwConfidence": 0
},
"imagePropertiesAnnotation": {
"dominantColors": {
"colors": [
{
"color": {
"red": 203,
"green": 201,
"blue": 201,
"alpha": null
},
"score": 0.4175916016101837,
"pixelFraction": 0.44456374645233154
},
✄ - - - ✄
]
}
},
"error": null,
"cropHintsAnnotation": {
"cropHints": [
{
"boundingPoly": {
"vertices": [
{ "x": 0, "y": 118 },
{ "x": 1177, "y": 118 },
{ "x": 1177, "y": 783 },
{ "x": 0, "y": 783 }
],
"normalizedVertices": []
},
"confidence": 0.41695669293403625,
"importanceFraction": 1
}
]
},
"fullTextAnnotation": null,
"webDetection": null,
"productSearchResults": null,
"context": null
}
Wenn kein Fehler zurückgegeben wird, können wir fortfahren. Daher haben wir diese Meldung, wenn sie blockieren:
AnnotateImageResponse response = responses.get(0);
if (response.hasError()) {
logger.info("Error: " + response.getError().getMessage());
return;
}
Wir bekommen die Beschriftungen der Dinge, Kategorien oder Themen, die auf dem Bild erkannt werden:
List<String> labels = response.getLabelAnnotationsList().stream()
.map(annotation -> annotation.getDescription())
.collect(Collectors.toList());
logger.info("Annotations found:");
for (String label: labels) {
logger.info("- " + label);
}
Wir möchten die vorherrschende Farbe des Bildes herausfinden:
String mainColor = "#FFFFFF";
ImageProperties imgProps = response.getImagePropertiesAnnotation();
if (imgProps.hasDominantColors()) {
DominantColorsAnnotation colorsAnn =
imgProps.getDominantColors();
ColorInfo colorInfo = colorsAnn.getColors(0);
mainColor = rgbHex(
colorInfo.getColor().getRed(),
colorInfo.getColor().getGreen(),
colorInfo.getColor().getBlue());
logger.info("Color: " + mainColor);
}
Außerdem verwenden wir eine Dienstfunktion, um die Werte für Rot, Grün und Blau in einen hexadezimalen Farbcode umzuwandeln, den wir in CSS-Stylesheets verwenden können.
So überprüfen Sie, ob das Bild sicher angezeigt wird:
boolean isSafe = false;
if (response.hasSafeSearchAnnotation()) {
SafeSearchAnnotation safeSearch =
response.getSafeSearchAnnotation();
isSafe = Stream.of(
safeSearch.getAdult(), safeSearch.getMedical(), safeSearch.getRacy(),
safeSearch.getSpoof(), safeSearch.getViolence())
.allMatch( likelihood ->
likelihood != Likelihood.LIKELY && likelihood != Likelihood.VERY_LIKELY
);
logger.info("Safe? " + isSafe);
}
Wir prüfen, ob die Attribute nicht wahrscheinlich oder sehr wahrscheinlich sind.
Wenn das Ergebnis der sicheren Suche in Ordnung ist, können wir Metadaten in Firestore speichern:
if (isSafe) {
FirestoreOptions firestoreOptions = FirestoreOptions.getDefaultInstance();
Firestore pictureStore = firestoreOptions.getService();
DocumentReference doc = pictureStore.collection("pictures").document(fileName);
Map<String, Object> data = new HashMap<>();
data.put("labels", labels);
data.put("color", mainColor);
data.put("created", new Date());
ApiFuture<WriteResult> writeResult = doc.set(data, SetOptions.merge());
logger.info("Picture metadata saved in Firestore at " + writeResult.get().getUpdateTime());
}
12. Funktion implementieren
Es ist an der Zeit, die Funktion bereitzustellen.
Klicken Sie auf die Schaltfläche DEPLOY
. Die neue Version wird bereitgestellt. Sie können den Fortschritt sehen:
13. Funktion noch einmal testen
Sobald die Funktion erfolgreich bereitgestellt wurde, posten Sie ein Bild in Cloud Storage. Prüfen Sie, ob die Funktion aufgerufen wird, was die Vision API zurückgibt und ob Metadaten in Firestore gespeichert sind.
Gehen Sie zurück zu Cloud Storage
und klicken Sie auf den Bucket, den wir zu Beginn des Labs erstellt haben:
Klicken Sie auf der Seite mit den Bucket-Details auf die Schaltfläche Upload files
, um ein Bild hochzuladen.
Über das Feld „Hamburger“ (♢) öffnen Sie den Explorer für Logging > Logs
.
Wählen Sie in der Log Fields
-Auswahl Cloud Function
aus, um die Logs aufzurufen, die Ihren Funktionen zugeordnet sind. Scrollen Sie nach unten durch die Logfelder und Sie können sogar eine bestimmte Funktion auswählen, um eine detailliertere Ansicht der funktionsbezogenen Logs zu erhalten. Wählen Sie die Funktion picture-uploaded
aus.
In der Liste der Logs sehe ich, dass die Funktion aufgerufen wurde:
Die Logs geben Beginn und Ende der Funktionsausführung an. Und dazwischen sehen wir die Logs, die wir in unsere Funktion mit den console.log()-Anweisungen eingefügt haben. Wir sehen:
- Die Details des Ereignisses, das unsere Funktion auslöst,
- Die Rohergebnisse des Vision API-Aufrufs
- Die Beschriftungen, die auf dem von uns hochgeladenen Bild gefunden wurden,
- Die Informationen zu den vorherrschenden Farben,
- Ob das Bild sicher gezeigt werden kann,
- Diese Metadaten zum Bild wurden schließlich in Firestore gespeichert.
Vom „Hamburger“ (∆) und zum Abschnitt Firestore
gehen. Im Unterabschnitt Data
(standardmäßig angezeigt) sollte die Sammlung pictures
mit einem neuen Dokument zu sehen sein, das dem gerade hochgeladenen Bild entspricht:
14. Bereinigen (optional)
Wenn Sie nicht vorhaben, mit den anderen Labs dieser Reihe fortzufahren, können Sie Ressourcen bereinigen, um Kosten zu sparen und insgesamt ein guter Cloud-Nutzer zu sein. Sie können Ressourcen wie folgt einzeln bereinigen.
Löschen Sie den Bucket:
gsutil rb gs://${BUCKET_PICTURES}
Löschen Sie die Funktion:
gcloud functions delete picture-uploaded --region europe-west1 -q
Löschen Sie die Firestore-Sammlung, indem Sie Sammlung löschen aus der Sammlung auswählen:
Alternativ können Sie das gesamte Projekt löschen:
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
15. Glückwunsch!
Glückwunsch! Sie haben den ersten Schlüsseldienst des Projekts erfolgreich implementiert!
Behandelte Themen
- Cloud Storage
- Cloud Functions
- Cloud Vision API
- Cloud Firestore