1. Ringkasan
Di lab kode pertama, Anda akan mengupload gambar dalam bucket. Tindakan ini akan menghasilkan peristiwa pembuatan file yang akan ditangani oleh fungsi. Fungsi ini akan melakukan panggilan ke Vision API untuk melakukan analisis gambar dan menyimpan hasilnya di datastore.

Yang akan Anda pelajari
- Cloud Storage
- Cloud Functions
- Cloud Vision API
- Cloud Firestore
2. Penyiapan dan Persyaratan
Penyiapan lingkungan mandiri
- Login ke Google Cloud Console dan buat project baru atau gunakan kembali project yang sudah ada. Jika belum memiliki akun Gmail atau Google Workspace, Anda harus membuatnya.



- Project name adalah nama tampilan untuk peserta project ini. String ini adalah string karakter yang tidak digunakan oleh Google API. Anda dapat memperbaruinya kapan saja.
- Project ID harus unik di semua project Google Cloud dan tidak dapat diubah (tidak dapat diubah setelah ditetapkan). Cloud Console otomatis membuat string unik; biasanya Anda tidak mementingkan kata-katanya. Di sebagian besar codelab, Anda harus merujuk Project ID-nya (biasanya diidentifikasi sebagai
PROJECT_ID). Jika tidak suka dengan ID yang dibuat, Anda dapat membuat ID acak lainnya. Atau, Anda dapat mencobanya sendiri dan melihat apakah ID tersebut tersedia. ID tidak dapat diubah setelah langkah ini dan akan tetap ada selama durasi project. - Sebagai informasi, ada nilai ketiga, Project Number yang digunakan oleh beberapa API. Pelajari lebih lanjut ketiga nilai ini di dokumentasi.
- Selanjutnya, Anda harus mengaktifkan penagihan di Konsol Cloud untuk menggunakan resource/API Cloud. Menjalankan operasi dalam codelab ini seharusnya tidak memerlukan banyak biaya, bahkan mungkin tidak sama sekali. Guna mematikan resource agar tidak menimbulkan penagihan di luar tutorial ini, Anda dapat menghapus resource yang dibuat atau menghapus seluruh project. Pengguna baru Google Cloud memenuhi syarat untuk mengikuti program Uji Coba Gratis senilai $300 USD.
Mulai Cloud Shell
Meskipun Google Cloud dapat dioperasikan dari jarak jauh menggunakan laptop Anda, dalam codelab ini, Anda akan menggunakan Google Cloud Shell, lingkungan command line yang berjalan di Cloud.
Dari Google Cloud Console, klik ikon Cloud Shell di toolbar kanan atas:

Hanya perlu waktu beberapa saat untuk penyediaan dan terhubung ke lingkungan. Jika sudah selesai, Anda akan melihat tampilan seperti ini:

Mesin virtual ini berisi semua alat pengembangan yang Anda perlukan. Layanan ini menawarkan direktori beranda tetap sebesar 5 GB dan beroperasi di Google Cloud, sehingga sangat meningkatkan performa dan autentikasi jaringan. Semua pekerjaan Anda dalam codelab ini dapat dilakukan di browser. Anda tidak perlu menginstal apa pun.
3. Mengaktifkan API
Untuk lab ini, Anda akan menggunakan Cloud Functions dan Vision API, tetapi keduanya harus diaktifkan terlebih dahulu di Cloud Console atau dengan gcloud.
Untuk mengaktifkan Vision API di Konsol Cloud, telusuri Cloud Vision API di kotak penelusuran:

Anda akan diarahkan ke halaman Cloud Vision API:

Klik tombol ENABLE.
Atau, Anda juga dapat mengaktifkannya di Cloud Shell menggunakan alat command line gcloud.
Di dalam Cloud Shell, jalankan perintah berikut:
gcloud services enable vision.googleapis.com
Anda akan melihat operasi selesai dengan berhasil:
Operation "operations/acf.12dba18b-106f-4fd2-942d-fea80ecc5c1c" finished successfully.
Aktifkan juga Cloud Functions:
gcloud services enable cloudfunctions.googleapis.com
4. Buat bucket (konsol)
Buat bucket penyimpanan untuk gambar. Anda dapat melakukannya dari konsol Google Cloud Platform ( console.cloud.google.com) atau dengan alat command line gsutil dari Cloud Shell atau lingkungan pengembangan lokal Anda.
Buka Penyimpanan
Dari menu "tiga garis" (☰), buka halaman Storage.

Beri nama bucket Anda
Klik tombol CREATE BUCKET.

Klik CONTINUE.
Pilih Lokasi

Buat bucket multi-regional di region pilihan Anda (di sini Europe).
Klik CONTINUE.
Pilih kelas penyimpanan default

Pilih kelas penyimpanan Standard untuk data Anda.
Klik CONTINUE.
Menetapkan Kontrol Akses

Karena Anda akan menggunakan gambar yang dapat diakses secara publik, Anda ingin semua gambar kita yang disimpan di bucket ini memiliki kontrol akses seragam yang sama.
Pilih opsi kontrol akses Uniform.
Klik CONTINUE.
Menetapkan Perlindungan/Enkripsi

Tetap menggunakan default (Google-managed key), karena Anda tidak akan menggunakan kunci enkripsi Anda sendiri.
Klik CREATE, untuk menyelesaikan pembuatan bucket.
Menambahkan allUsers sebagai pelihat penyimpanan
Buka tab Permissions:

Tambahkan anggota allUsers ke bucket, dengan peran Storage > Storage Object Viewer, sebagai berikut:

Klik SAVE.
5. Buat bucket (gsutil)
Anda juga dapat menggunakan alat command line gsutil di Cloud Shell untuk membuat bucket.
Di Cloud Shell, tetapkan variabel untuk nama bucket unik. Cloud Shell sudah menyetel GOOGLE_CLOUD_PROJECT ke project ID unik Anda. Anda dapat menambahkannya ke nama bucket.
Contoh:
export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}
Buat zona multi-region standar di Eropa:
gsutil mb -l EU gs://${BUCKET_PICTURES}
Pastikan akses level bucket yang seragam:
gsutil uniformbucketlevelaccess set on gs://${BUCKET_PICTURES}
Setel bucket untuk publik.
gsutil iam ch allUsers:objectViewer gs://${BUCKET_PICTURES}
Jika Anda membuka bagian Cloud Storage konsol, Anda akan memiliki bucket uploaded-pictures publik:

Uji apakah Anda dapat mengupload gambar ke bucket dan gambar yang diupload tersedia secara publik, seperti yang dijelaskan pada langkah sebelumnya.
6. Menguji akses publik ke bucket
Kembali ke browser penyimpanan, Anda akan melihat bucket Anda dalam daftar, dengan akses "Publik" (termasuk tanda peringatan yang mengingatkan Anda bahwa siapa pun memiliki akses ke konten bucket tersebut).

Bucket Anda sekarang siap menerima gambar.
Jika mengklik nama bucket, Anda akan melihat detail bucket.

Di sana, Anda dapat mencoba tombol Upload files untuk menguji apakah Anda dapat menambahkan gambar ke bucket. Pop-up pemilih file akan meminta Anda memilih file. Setelah dipilih, file akan diupload ke bucket Anda, dan Anda akan melihat kembali akses public yang telah otomatis diberikan ke file baru ini.

Di samping label akses Public, Anda juga akan melihat ikon link kecil. Saat mengkliknya, browser Anda akan membuka URL publik gambar tersebut, yang akan berbentuk:
https://storage.googleapis.com/BUCKET_NAME/PICTURE_FILE.png
Dengan BUCKET_NAME adalah nama unik secara global yang telah Anda pilih untuk bucket, lalu nama file gambar Anda.
Dengan mencentang kotak di samping nama gambar, tombol DELETE akan diaktifkan, dan Anda dapat menghapus gambar pertama ini.
7. Membuat fungsi
Pada langkah ini, Anda akan membuat fungsi yang bereaksi terhadap peristiwa upload gambar.
Buka bagian Cloud Functions di Konsol Google Cloud. Dengan membukanya, layanan Cloud Functions akan diaktifkan secara otomatis.

Klik Create function.
Pilih nama (mis. picture-uploaded) dan Region (ingatlah untuk konsisten dengan pilihan region untuk bucket):

Ada dua jenis fungsi:
- Fungsi HTTP yang dapat dipanggil melalui URL (yaitu, API web),
- Fungsi latar belakang yang dapat dipicu oleh beberapa peristiwa.
Anda ingin membuat fungsi latar belakang yang dipicu saat file baru diupload ke bucket Cloud Storage:

Anda tertarik dengan jenis peristiwa Finalize/Create, yaitu peristiwa yang dipicu saat file dibuat atau diperbarui di bucket:

Pilih bucket yang dibuat sebelumnya, untuk memberi tahu Cloud Functions agar diberi notifikasi saat file dibuat / diperbarui di bucket tertentu ini:

Klik Select untuk memilih bucket yang Anda buat sebelumnya, lalu Save

Sebelum mengklik Berikutnya, Anda dapat meluaskan dan mengubah setelan default (memori 256 MB) di bagian Runtime, build, connections and security settings dan memperbaruinya menjadi 1 GB.

Setelah mengklik Next, Anda dapat menyesuaikan Runtime, Source code, dan entry point.
Pertahankan Inline editor untuk fungsi ini:

Pilih salah satu runtime Java, misalnya Java 11:

Kode sumber terdiri dari file Java, dan file Maven pom.xml yang menyediakan berbagai metadata dan dependensi.
Biarkan cuplikan kode default: cuplikan ini mencatat nama file gambar yang diupload:

Untuk saat ini, tetap tetapkan nama fungsi yang akan dieksekusi ke Example, untuk tujuan pengujian.
Klik Deploy untuk membuat dan men-deploy fungsi. Setelah deployment berhasil, Anda akan melihat tanda centang dalam lingkaran hijau di daftar fungsi:

8. Menguji fungsi
Pada langkah ini, uji bahwa fungsi merespons peristiwa penyimpanan.
Dari menu "hamburger" (☰), kembali ke halaman Storage.
Klik bucket gambar, lalu klik Upload files untuk mengupload gambar.

Buka kembali konsol cloud untuk membuka halaman Logging > Logs Explorer.
Di pemilih Log Fields, pilih Cloud Function untuk melihat log khusus fungsi Anda. Scroll ke bawah melalui Kolom Log dan Anda bahkan dapat memilih fungsi tertentu untuk mendapatkan tampilan yang lebih terperinci dari log terkait fungsi. Pilih fungsi picture-uploaded.
Anda akan melihat item log yang menyebutkan pembuatan fungsi, waktu mulai dan berakhir fungsi, serta pernyataan log kita yang sebenarnya:

Pernyataan log kita berbunyi: Processing file: pic-a-daily-architecture-events.png, yang berarti bahwa peristiwa terkait pembuatan dan penyimpanan gambar ini memang telah dipicu seperti yang diharapkan.
9. Menyiapkan database
Anda akan menyimpan informasi tentang gambar yang diberikan oleh Vision API ke database Cloud Firestore, yaitu database dokumen NoSQL yang cepat, terkelola sepenuhnya, tanpa server, dan cloud-native. Siapkan database Anda dengan membuka bagian Firestore di Konsol Cloud:

Dua opsi ditawarkan: Native mode atau Datastore mode. Gunakan mode native, yang menawarkan fitur tambahan seperti dukungan offline dan sinkronisasi real-time.
Klik SELECT NATIVE MODE.

Pilih multi-region (di sini di Eropa, tetapi idealnya setidaknya region yang sama dengan fungsi dan bucket penyimpanan Anda).
Klik tombol CREATE DATABASE.
Setelah database dibuat, Anda akan melihat hal berikut:

Buat koleksi baru dengan mengklik tombol + START COLLECTION.
Beri nama koleksi pictures.

Anda tidak perlu membuat dokumen. Anda akan menambahkannya secara terprogram saat gambar baru disimpan di Cloud Storage dan dianalisis oleh Vision API.
Klik Save.
Firestore membuat dokumen default pertama dalam koleksi yang baru dibuat. Anda dapat menghapus dokumen tersebut dengan aman karena tidak berisi informasi yang berguna:

Dokumen yang akan dibuat secara terprogram dalam koleksi kita akan berisi 4 kolom:
- name (string): nama file gambar yang diupload, yang juga merupakan kunci dokumen
- labels (array string): label item yang dikenali oleh Vision API
- color (string): kode warna heksadesimal dari warna dominan (yaitu. #ab12ef)
- created (tanggal): stempel waktu saat metadata gambar ini disimpan
- thumbnail (boolean): kolom opsional yang akan ada dan bernilai benar jika gambar thumbnail telah dibuat untuk gambar ini
Karena kita akan menelusuri di Firestore untuk menemukan gambar yang memiliki thumbnail, dan mengurutkannya berdasarkan tanggal pembuatan, kita perlu membuat indeks penelusuran.
Anda dapat membuat indeks dengan perintah berikut di Cloud Shell:
gcloud firestore indexes composite create \
--collection-group=pictures \
--field-config field-path=thumbnail,order=descending \
--field-config field-path=created,order=descending
Atau, Anda juga dapat melakukannya dari Konsol Cloud, dengan mengklik Indexes di kolom navigasi di sebelah kiri, lalu membuat indeks gabungan seperti yang ditunjukkan di bawah:

Klik Create. Pembuatan indeks dapat memerlukan waktu beberapa menit.
10. Perbarui fungsi
Kembali ke halaman Functions, untuk mengupdate fungsi agar memanggil Vision API untuk menganalisis gambar kita, dan untuk menyimpan metadata di Firestore.
Dari menu "hamburger" (☰), buka bagian Cloud Functions, klik nama fungsi, pilih tab Source, lalu klik tombol EDIT.
Pertama, edit file pom.xml yang mencantumkan dependensi fungsi Java kita. Perbarui kode untuk menambahkan dependensi Maven Cloud Vision API:
<?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>
Setelah dependensi diupdate, Anda akan mengerjakan kode fungsi kita dengan memperbarui file Example.java menggunakan kode kustom kita.
Arahkan kursor mouse ke file Example.java, lalu klik pensil. Ganti nama paket dan nama file menjadi src/main/java/fn/ImageAnalysis.java.
Ganti kode di ImageAnalysis.java dengan kode di bawah ini. Hal ini akan dijelaskan pada langkah berikutnya.
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. Menjelajahi fungsi
Mari kita lihat lebih dekat berbagai bagian menariknya.
Pertama, kita menyertakan dependensi tertentu dalam file Maven pom.xml. Library Klien Java Google memublikasikan Bill-of-Materials(BOM), untuk menghilangkan konflik dependensi. Dengan menggunakannya, Anda tidak perlu menentukan versi apa pun untuk setiap Library Klien Google
<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>
Kemudian, kita menyiapkan klien untuk Vision API:
...
try (ImageAnnotatorClient vision = ImageAnnotatorClient.create()) {
...
Sekarang kita akan membahas struktur fungsi. Kita mengambil kolom yang kita minati dari peristiwa masuk dan memetakannya ke struktur GCSEvent yang kita tentukan:
...
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;
}
Perhatikan tanda tangan, tetapi juga cara kita mengambil nama file dan bucket yang memicu Cloud Function.
Sebagai referensi, berikut tampilan payload peristiwa:
{
"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"
}
Kita menyiapkan permintaan untuk dikirim melalui klien Vision:
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();
Kami meminta 3 kemampuan utama Vision API:
- Deteksi label: untuk memahami konten dalam gambar tersebut
- Properti gambar: untuk memberikan atribut menarik dari gambar (kita tertarik dengan warna dominan gambar)
- Penelusuran aman: untuk mengetahui apakah gambar aman untuk ditampilkan (tidak boleh berisi konten dewasa / medis / tidak pantas / kekerasan)
Pada tahap ini, kita dapat melakukan panggilan ke Vision API:
...
logger.info("Calling the Vision API...");
BatchAnnotateImagesResponse result =
vision.batchAnnotateImages(requests);
List<AnnotateImageResponse> responses = result.getResponsesList();
...
Sebagai referensi, berikut tampilan respons dari Vision API:
{
"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
}
Jika tidak ada error yang ditampilkan, kita dapat melanjutkan, oleh karena itu kita memiliki blok if ini:
AnnotateImageResponse response = responses.get(0);
if (response.hasError()) {
logger.info("Error: " + response.getError().getMessage());
return;
}
Kita akan mendapatkan label benda, kategori, atau tema yang dikenali dalam gambar:
List<String> labels = response.getLabelAnnotationsList().stream()
.map(annotation -> annotation.getDescription())
.collect(Collectors.toList());
logger.info("Annotations found:");
for (String label: labels) {
logger.info("- " + label);
}
Kami ingin mengetahui warna dominan gambar:
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);
}
Kita juga menggunakan fungsi utilitas untuk mengubah nilai merah / hijau / biru menjadi kode warna heksadesimal yang dapat kita gunakan dalam stylesheet CSS.
Mari kita periksa apakah gambar tersebut aman untuk ditampilkan:
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);
}
Kami memeriksa atribut dewasa / spoof / medis / kekerasan / tidak senonoh untuk melihat apakah atribut tersebut kemungkinan atau sangat mungkin tidak ada.
Jika hasil penelusuran aman tidak masalah, kita dapat menyimpan metadata di 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());
}
12. Men-deploy fungsi
Waktu untuk men-deploy fungsi.

Tekan tombol DEPLOY dan versi baru akan di-deploy, Anda dapat melihat progresnya:

13. Uji fungsi lagi
Setelah fungsi berhasil di-deploy, Anda akan memposting gambar ke Cloud Storage, melihat apakah fungsi kita dipanggil, apa yang ditampilkan Vision API, dan apakah metadata disimpan di Firestore.
Kembali ke Cloud Storage, lalu klik bucket yang kita buat di awal lab:

Setelah berada di halaman detail bucket, klik tombol Upload files untuk mengupload gambar.

Dari menu "tiga garis" (☰), buka Logging > Logs Explorer.
Di pemilih Log Fields, pilih Cloud Function untuk melihat log khusus fungsi Anda. Scroll ke bawah melalui Kolom Log dan Anda bahkan dapat memilih fungsi tertentu untuk mendapatkan tampilan yang lebih terperinci dari log terkait fungsi. Pilih fungsi picture-uploaded.

Dan memang, dalam daftar log, saya dapat melihat bahwa fungsi kita telah dipanggil:

Log menunjukkan awal dan akhir eksekusi fungsi. Di antaranya, kita dapat melihat log yang kita masukkan ke dalam fungsi dengan pernyataan console.log(). Kami melihat:
- Detail acara yang memicu fungsi kita,
- Hasil mentah dari panggilan Vision API,
- Label yang ditemukan dalam gambar yang kami upload,
- Informasi warna dominan,
- Apakah gambar aman untuk ditampilkan,
- Dan akhirnya metadata tentang gambar tersebut telah disimpan di Firestore.

Sekali lagi dari menu "tiga garis" (☰), buka bagian Firestore. Di subbagian Data (ditampilkan secara default), Anda akan melihat koleksi pictures dengan dokumen baru yang ditambahkan, sesuai dengan gambar yang baru saja Anda upload:

14. Membersihkan (Opsional)
Jika tidak berniat melanjutkan lab lainnya dalam seri ini, Anda dapat membersihkan resource untuk menghemat biaya dan menjadi pengguna cloud yang baik secara keseluruhan. Anda dapat membersihkan resource satu per satu sebagai berikut.
Hapus bucket:
gsutil rb gs://${BUCKET_PICTURES}
Hapus fungsi:
gcloud functions delete picture-uploaded --region europe-west1 -q
Hapus koleksi Firestore dengan memilih Hapus koleksi dari koleksi:

Atau, Anda dapat menghapus seluruh project:
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
15. Selamat!
Selamat! Anda telah berhasil menerapkan layanan utama pertama dari project ini.
Yang telah kita bahas
- Cloud Storage
- Cloud Functions
- Cloud Vision API
- Cloud Firestore