1. 概要
最初の Codelab では、バケットに画像をアップロードします。これにより、関数によって処理されるファイル作成イベントが生成されます。この関数は Vision API を呼び出して画像分析を行い、結果をデータストアに保存します。
学習内容
- Cloud Storage
- Cloud Functions
- Cloud Vision API
- Cloud Firestore
2. 設定と要件
セルフペース型の環境設定
- Google Cloud Console にログインして、プロジェクトを新規作成するか、既存のプロジェクトを再利用します。Gmail アカウントも Google Workspace アカウントもまだお持ちでない場合は、アカウントを作成してください。
- プロジェクト名は、このプロジェクトの参加者に表示される名称です。Google API では使用されない文字列です。この値はいつでも更新できます。
- プロジェクト ID は、すべての Google Cloud プロジェクトにおいて一意でなければならず、不変です(設定後は変更できません)。Cloud コンソールでは一意の文字列が自動生成されます。通常、それが何であるかは関係ありません。ほとんどの Codelab では、プロジェクト ID を参照する必要があります(通常は
PROJECT_ID
として識別されます)。生成された ID が気に入らない場合は、別のランダムな ID を生成できます。または、ご自身でお試しになることもできます。このステップを終えた後は変更できず、プロジェクト期間中は維持されます。 - なお、3 つ目の値は、一部の API で使用される [プロジェクト番号] です。これら 3 つの値について詳しくは、こちらのドキュメントをご覧ください。
- 次に、Cloud のリソースや API を使用するために、Cloud コンソールで課金を有効にする必要があります。この Codelab の操作をすべて行って、費用が生じたとしても、少額です。このチュートリアル以降課金が発生しないようにリソースをシャットダウンするには、作成したリソースを削除するか、プロジェクト全体を削除します。Google Cloud の新規ユーザーは、300 米ドル分の無料トライアル プログラムをご利用いただけます。
Cloud Shell の起動
Google Cloud はノートパソコンからリモートで操作できますが、この Codelab では、Google Cloud Shell(Cloud 上で動作するコマンドライン環境)を使用します。
Google Cloud Console で、右上のツールバーにある Cloud Shell アイコンをクリックします。
プロビジョニングと環境への接続にはそれほど時間はかかりません。完了すると、次のように表示されます。
この仮想マシンには、必要な開発ツールがすべて用意されています。永続的なホーム ディレクトリが 5 GB 用意されており、Google Cloud で稼働します。そのため、ネットワークのパフォーマンスと認証機能が大幅に向上しています。この Codelab での作業はすべて、ブラウザ内から実行できます。インストールは不要です。
3. API を有効にする
このラボでは Cloud Functions と Vision API を使用しますが、まず、これらを Cloud コンソールまたは gcloud
で有効にする必要があります。
Cloud コンソールで Vision API を有効にするには、検索バーで「Cloud Vision API
」を検索します。
Cloud Vision API のページが表示されます。
[ENABLE
] ボタンをクリックします。
または、gcloud コマンドライン ツールを使用して Cloud Shell で有効にすることもできます。
Cloud Shell で、次のコマンドを実行します。
gcloud services enable vision.googleapis.com
オペレーションが正常に完了することを確認できます。
Operation "operations/acf.12dba18b-106f-4fd2-942d-fea80ecc5c1c" finished successfully.
Cloud Functions も有効にします。
gcloud services enable cloudfunctions.googleapis.com
4. バケットを作成する(コンソール)
画像用の Storage バケットを作成します。これを行うには、Google Cloud Platform コンソール(console.cloud.google.com)を使用するか、Cloud Shell またはローカル開発環境の gsutil コマンドライン ツールを使用します。
Storage に移動
「ハンバーガー」から(✱)メニューから [Storage
] ページに移動します。
バケットに名前を付ける
[CREATE BUCKET
] ボタンをクリックします。
[CONTINUE
] をクリックします。
ロケーションを選択
任意のリージョン(ここでは Europe
)にマルチリージョン バケットを作成します。
[CONTINUE
] をクリックします。
デフォルトのストレージ クラスを選択する
データのストレージ クラスとして Standard
を選択します。
[CONTINUE
] をクリックします。
アクセス制御の設定
一般公開されているイメージを扱うため、このバケットに保存されているすべての写真に同じ均一なアクセス制御を適用する必要があります。
Uniform
アクセス制御オプションを選択します。
[CONTINUE
] をクリックします。
保護/暗号化の設定
デフォルトのままにします(独自の暗号鍵は使用しないため、Google-managed key)
。
CREATE
をクリックして、最終的にバケットの作成を完了します。
allUsers をストレージ閲覧者として追加する
[Permissions
] タブに移動します。
次のように、Storage > Storage Object Viewer
のロールを持つ allUsers
メンバーをバケットに追加します。
[SAVE
] をクリックします。
5. バケットを作成する(gsutil)
Cloud Shell の gsutil
コマンドライン ツールを使用してバケットを作成することもできます。
Cloud Shell で、一意のバケット名の変数を設定します。Cloud Shell には、GOOGLE_CLOUD_PROJECT
に一意のプロジェクト ID がすでに設定されています。これをバケット名に追加できます。
例:
export BUCKET_PICTURES=uploaded-pictures-${GOOGLE_CLOUD_PROJECT}
ヨーロッパに標準マルチリージョン ゾーンを作成します。
gsutil mb -l EU gs://${BUCKET_PICTURES}
均一なバケットレベルのアクセスを確保する:
gsutil uniformbucketlevelaccess set on gs://${BUCKET_PICTURES}
バケットを公開します。
gsutil iam ch allUsers:objectViewer gs://${BUCKET_PICTURES}
コンソールの Cloud Storage
セクションに移動すると、公開されている uploaded-pictures
バケットがあるはずです。
前のステップで説明したように、バケットに画像をアップロードできることと、アップロードされた画像が一般公開されることをテストします。
6. バケットへの公開アクセスをテストする
Storage ブラウザに戻ると、バケットに [Public] と表示されているバケットがリストに表示されています。アクセス(そのバケットのコンテンツには誰でもアクセスできることを示す警告マークを含む)を付与する必要があります。
これで、バケットで写真を受け取る準備が整いました。
バケット名をクリックすると、バケットの詳細が表示されます。
そこで Upload files
ボタンをクリックして、バケットに画像を追加できるかどうかテストできます。ファイル選択ツールのポップアップが表示され、ファイルの選択を求められます。選択すると、ファイルがバケットにアップロードされ、この新しいファイルに自動的に割り当てられた public
アクセス権が再び表示されます。
Public
アクセスラベルの横には、小さなリンクアイコンも表示されます。その画像をクリックすると、ブラウザはその画像の公開 URL(次の形式)に移動します。
https://storage.googleapis.com/BUCKET_NAME/PICTURE_FILE.png
BUCKET_NAME
はバケットに選択したグローバルに一意の名前と、画像のファイル名です。
画像名の横にあるチェックボックスをオンにすると、[DELETE
] ボタンが有効になり、この最初の画像を削除できます。
7. 関数を作成する
このステップでは、画像のアップロード イベントに反応する関数を作成します。
Google Cloud コンソールの Cloud Functions
セクションに移動します。このバケットにアクセスすると、Cloud Functions サービスが自動的に有効になります。
Create function
をクリックします。
名前を選択します(例:picture-uploaded
)と Region(バケットのリージョン選択との整合性を保つ必要があります):
関数には次の 2 種類があります。
- URL(ウェブ API)経由で呼び出せる HTTP 関数、
- イベントでトリガーできるバックグラウンド関数。
新しいファイルが Cloud Storage
バケットにアップロードされたときにトリガーされるバックグラウンド関数を作成します。
ここでは、Finalize/Create
イベントタイプに注目します。これは、バケット内でファイルが作成または更新されたときにトリガーされるイベントです。
以前に作成したバケットを選択して、この特定のバケットでファイルが作成または更新されたときに Cloud Functions に通知されるようにします。
[Select
] をクリックして、先ほど作成したバケットを選択してから、[Save
] をクリックします。
[次へ] をクリックする前に、[ランタイム、ビルド、接続、セキュリティの設定] のデフォルト(256 MB メモリ)を展開して変更し、1 GB に更新できます。
Next
をクリックした後、ランタイム、ソースコード、エントリ ポイントを調整できます。
この関数の Inline editor
はそのままにします。
Java ランタイムの 1 つ(Java 11 など)を選択します。
ソースコードは、Java
ファイルと、さまざまなメタデータと依存関係を提供する pom.xml
Maven ファイルで構成されます。
デフォルトのコード スニペットをそのまま残します。これにより、アップロードされた画像のファイル名がログに記録されます。
今回は、テスト用に実行する関数の名前を Example
にしておきます。
Deploy
をクリックし、関数を作成してデプロイします。デプロイが成功すると、関数のリストに緑色の丸いチェックマークが表示されます。
8. 関数をテストする
このステップでは、関数がストレージ イベントに応答することをテストします。
「ハンバーガー」から(💻?)メニューから [Storage
] ページに戻ります。
画像バケットをクリックし、Upload files
をクリックして画像をアップロードします。
Cloud コンソール内でもう一度移動して、[Logging > Logs Explorer
] ページに移動します。
Log Fields
セレクタで Cloud Function
を選択すると、関数専用のログが表示されます。ログのフィールドを下にスクロールし、特定の関数を選択して、関数関連のログを詳しく確認することもできます。picture-uploaded
関数を選択します。
関数の作成、関数の開始時刻と終了時刻、実際のログ ステートメントに関するログ項目が表示されます。
ログ ステートメントに「Processing file: pic-a-daily-architecture-events.png
」と記載されています。これは、この画像の作成と保存に関連するイベントが実際に想定どおりにトリガーされたことを意味します。
9. データベースを準備する
Vision API から提供された写真に関する情報を Cloud Firestore データベースに保存します。Cloud Firestore データベースは、高速、フルマネージド、サーバーレス、クラウドネイティブの NoSQL ドキュメント データベースです。Cloud コンソールの Firestore
セクションに移動して、データベースを準備します。
Native mode
または Datastore mode
の 2 つのオプションがあります。オフライン サポートやリアルタイム同期などの追加機能を備えたネイティブ モードを使用する。
SELECT NATIVE MODE
をクリックします。
マルチリージョンを選択します(ここではヨーロッパですが、関数とストレージ バケットと同じリージョンが理想的です)。
[CREATE DATABASE
] ボタンをクリックします。
データベースが作成されると、次のように表示されます。
[+ START COLLECTION
] ボタンをクリックして、新しいコレクションを作成します。
コレクション pictures
に名前を付けます。
ドキュメントを作成する必要はありません。新しい画像が Cloud Storage に保存され、Vision API によって分析されるときに、これらの画像をプログラムで追加します。
[Save
] をクリックします。
Firestore は、新しく作成されたコレクションに最初のデフォルト ドキュメントを作成します。このドキュメントには有用な情報が含まれていないため、安全に削除できます。
このコレクションでプログラムによって作成されるドキュメントには、次の 4 つのフィールドが含まれます。
- name(文字列): アップロードされた画像のファイル名。ドキュメントのキーでもあります。
- labels(文字列の配列): Vision API によって認識されたアイテムのラベル
- color(文字列): ドミナント カラーの 16 進数のカラーコード(#ab12ef)
- 作成日(日付): この画像のメタデータが保存されたときのタイムスタンプ
- thumbnail(ブール値): この画像のサムネイル画像が生成された場合に true になるオプション フィールド。
Firestore でサムネイルのある画像を検索し、作成日で並べ替えるため、検索インデックスを作成する必要があります。
インデックスを作成するには、Cloud Shell で次のコマンドを使用します。
gcloud firestore indexes composite create \
--collection-group=pictures \
--field-config field-path=thumbnail,order=descending \
--field-config field-path=created,order=descending
または、Cloud コンソールで左側のナビゲーション列で [Indexes
] をクリックし、以下のように複合インデックスを作成することでも作成できます。
[Create
] をクリックします。インデックスの作成には数分かかることがあります。
10. 関数を更新する
Functions
ページに戻り、Vision API を呼び出して画像を分析し、メタデータを Firestore に保存する関数を更新します。
「ハンバーガー」から(gcr)メニューから Cloud Functions
セクションに移動し、関数名をクリックして [Source
] タブを選択して [EDIT
] ボタンをクリックします。
まず、Java 関数の依存関係のリストを含む pom.xml
ファイルを編集します。コードを更新して、Cloud Vision API Maven 依存関係を追加します。
<?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>
依存関係が最新になったので、関数のコードを編集するために、カスタムコードで Example.java
ファイルを更新します。
Example.java
ファイルにカーソルを合わせ、鉛筆アイコンをクリックします。パッケージ名とファイル名を src/main/java/fn/ImageAnalysis.java
に置き換えます。
ImageAnalysis.java
のコードを次のコードに置き換えます。これについては次のステップで説明します。
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. 関数を詳しく見る
さまざまな興味深い要素を詳しく見てみましょう。
まず、Maven の pom.xml
ファイルに特定の依存関係を含めます。Google Java クライアント ライブラリは、依存関係の競合を解消するために Bill-of-Materials(BOM)
を公開しています。これを使用すると、個々の 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>
次に、Vision API のクライアントを準備します。
...
try (ImageAnnotatorClient vision = ImageAnnotatorClient.create()) {
...
次は関数の構造です。受信イベントから対象のフィールドを取得し、定義した GCSEvent 構造にマッピングします。
...
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;
}
署名だけでなく、Cloud Functions の関数をトリガーしたファイルとバケットの名前を取得する方法も注目してください。
参考までに、イベント ペイロードは次のようになります。
{
"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"
}
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();
Vision API には、
- ラベル検出: 画像の内容を把握
- 画像プロパティ: 画像の興味深い属性を指定します(画像の主な色を確認できます)。
- セーフサーチ: 画像が表示しても安全かどうかを知ることができます(アダルト / 医療 / 際どい / 暴力的なコンテンツを含む画像は使用できません)。
この時点で、Vision API を呼び出せます。
...
logger.info("Calling the Vision API...");
BatchAnnotateImagesResponse result =
vision.batchAnnotateImages(requests);
List<AnnotateImageResponse> responses = result.getResponsesList();
...
参考までに、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
}
エラーが返されない場合は先に進みます。これが if ブロックになっている理由です。
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);
}
また、ユーティリティ関数を使用して赤 / 緑 / 青の値を 16 進数のカラーコードに変換し、CSS スタイルシートで使用できるようにします。
画像が安全に見せることができるか確認しましょう。
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);
}
成人 / なりすまし / 医療 / 暴力 / 際どい属性をチェックし、それらの属性がその可能性や非常に低いかどうかを確認します。
セーフサーチの結果に問題がなければ、メタデータを 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. 関数をデプロイする
関数のデプロイにかかる時間。
[DEPLOY
] ボタンをクリックすると、新しいバージョンがデプロイされ、進行状況を確認できます。
13. 関数を再度テストする
関数が正常にデプロイされたら、画像を Cloud Storage に送信し、関数が呼び出されたかどうか、Vision API が返す結果、メタデータが Firestore に保存されているかどうかを確認します。
Cloud Storage
に戻り、ラボの始めに作成したバケットをクリックします。
バケットの詳細ページが表示されたら、Upload files
ボタンをクリックして画像をアップロードします。
「ハンバーガー」から(gcr)メニューで、Logging > Logs
Explorer に移動します。
Log Fields
セレクタで Cloud Function
を選択すると、関数専用のログが表示されます。ログのフィールドを下にスクロールし、特定の関数を選択して、関数関連のログを詳しく確認することもできます。picture-uploaded
関数を選択します。
ログのリストを見ると、関数が呼び出されたことがわかります。
ログには、関数の実行の開始と終了が示されます。その間に、console.log() ステートメントで関数に入力したログを確認できます。確認されたこと:
- 関数をトリガーするイベントの詳細
- Vision API 呼び出しの生の結果は、
- アップロードした写真に見つかったラベル
- ドミナントカラーの情報は
- 見せても問題ないか、
- そして最終的に、写真に関するこれらのメタデータが Firestore に保存されました。
ここも「ハンバーガー」から(Я)メニューで Firestore
セクションに移動します。Data
サブセクション(デフォルト)に、アップロードした画像に対応する新しいドキュメントが追加された pictures
コレクションが表示されます。
14. クリーンアップ(省略可)
シリーズの他のラボを継続する予定がない場合は、リソースをクリーンアップすることで費用を節約し、クラウド全般に精通するようにしてください。次のようにして、リソースを個別にクリーンアップできます。
バケットを削除します。
gsutil rb gs://${BUCKET_PICTURES}
関数を削除します。
gcloud functions delete picture-uploaded --region europe-west1 -q
[コレクションからコレクションを削除] を選択して、Firestore コレクションを削除します。
また、プロジェクト全体を削除することもできます。
gcloud projects delete ${GOOGLE_CLOUD_PROJECT}
15. 完了
これで、プロジェクトの最初の鍵サービスが正常に実装されました。
学習した内容
- Cloud Storage
- Cloud Functions
- Cloud Vision API
- Cloud Firestore