如要從應用程式呼叫 Google Cloud API,您必須建立中繼 REST API,用於處理授權及保護 API 金鑰等密鑰值。接著,您必須在行動應用程式中編寫程式碼,以便向這項中介服務進行驗證及通訊。
建立這類 REST API 的其中一種方法是使用 Firebase 驗證和函式,這可讓您透過代管的無伺服器閘道存取 Google Cloud API,該閘道可處理驗證,並可透過預先建構的 SDK 從行動應用程式呼叫。
本指南說明如何使用這項技術,從應用程式呼叫 Cloud Vision API。這個方法可讓所有通過驗證的使用者透過您的雲端專案存取 Cloud Vision 計費服務,因此請先考量這項驗證機制是否足以因應您的用途,再繼續操作。
事前準備
設定專案
- 如果您尚未將 Firebase 新增至 Android 專案,請先新增。
-
如果尚未為專案啟用雲端式 API,請立即啟用:
- 在 Firebase 控制台中,開啟「Firebase ML API」頁面。
-
如果尚未將專案升級至即付即用 Blaze 定價方案,請按一下「升級」進行升級 (只有在專案未採用 Blaze 定價方案時,系統才會提示您升級)。
只有採用 Blaze 定價方案的專案,才能使用雲端式 API。
- 如果尚未啟用雲端式 API,請按一下「Enable Cloud-based APIs」(啟用雲端式 API)。
- 設定現有的 Firebase API 金鑰,禁止存取 Cloud Vision API:
部署可呼叫函式
接下來,請部署 Cloud 函式,用於連結應用程式和 Cloud Vision API。functions-samples 存放區包含可使用的範例。
根據預設,透過這個函式存取 Cloud Vision API 時,只有通過驗證的應用程式使用者才能存取 Cloud Vision API。您可以根據不同需求修改函式。
如何部署函式:
- 複製或下載 functions-samples 存放區,並切換至
Node-1st-gen/vision-annotate-image目錄:git clone https://github.com/firebase/functions-samplescd Node-1st-gen/vision-annotate-image - 安裝依附元件:
cd functionsnpm installcd .. - 如果沒有 Firebase CLI,請安裝。
- 在
vision-annotate-image目錄中初始化 Firebase 專案。系統提示時,請在清單中選取專案。firebase init
- 部署函式:
firebase deploy --only functions:annotateImage
在應用程式中加入 Firebase Auth
上述部署的可呼叫函式會拒絕應用程式中未經驗證使用者的任何要求。如果您尚未新增 Firebase Auth,請將其新增至應用程式。
在應用程式中新增必要的依附元件
<project>/<app-module>/build.gradle.kts 或 <project>/<app-module>/build.gradle):implementation("com.google.firebase:firebase-functions:23.0.0") implementation("com.google.code.gson:gson:2.8.6")
現在可以開始為圖片加上標籤。
1. 準備輸入圖片
如要呼叫 Cloud Vision,圖片必須採用 Base64 編碼字串格式。如要處理已儲存檔案 URI 中的圖片,請按照下列步驟操作:- 以
Bitmap物件的形式取得圖片:Kotlin
var bitmap: Bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
Java
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
- 您也可以縮小圖片,節省頻寬。請參閱
Cloud Vision 建議的圖片大小。
Kotlin
private fun scaleBitmapDown(bitmap: Bitmap, maxDimension: Int): Bitmap { val originalWidth = bitmap.width val originalHeight = bitmap.height var resizedWidth = maxDimension var resizedHeight = maxDimension if (originalHeight > originalWidth) { resizedHeight = maxDimension resizedWidth = (resizedHeight * originalWidth.toFloat() / originalHeight.toFloat()).toInt() } else if (originalWidth > originalHeight) { resizedWidth = maxDimension resizedHeight = (resizedWidth * originalHeight.toFloat() / originalWidth.toFloat()).toInt() } else if (originalHeight == originalWidth) { resizedHeight = maxDimension resizedWidth = maxDimension } return Bitmap.createScaledBitmap(bitmap, resizedWidth, resizedHeight, false) }
Java
private Bitmap scaleBitmapDown(Bitmap bitmap, int maxDimension) { int originalWidth = bitmap.getWidth(); int originalHeight = bitmap.getHeight(); int resizedWidth = maxDimension; int resizedHeight = maxDimension; if (originalHeight > originalWidth) { resizedHeight = maxDimension; resizedWidth = (int) (resizedHeight * (float) originalWidth / (float) originalHeight); } else if (originalWidth > originalHeight) { resizedWidth = maxDimension; resizedHeight = (int) (resizedWidth * (float) originalHeight / (float) originalWidth); } else if (originalHeight == originalWidth) { resizedHeight = maxDimension; resizedWidth = maxDimension; } return Bitmap.createScaledBitmap(bitmap, resizedWidth, resizedHeight, false); }
Kotlin
// Scale down bitmap size bitmap = scaleBitmapDown(bitmap, 640)
Java
// Scale down bitmap size bitmap = scaleBitmapDown(bitmap, 640);
- 將點陣圖物件轉換為 Base64 編碼字串:
Kotlin
// Convert bitmap to base64 encoded string val byteArrayOutputStream = ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) val imageBytes: ByteArray = byteArrayOutputStream.toByteArray() val base64encoded = Base64.encodeToString(imageBytes, Base64.NO_WRAP)
Java
// Convert bitmap to base64 encoded string ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream); byte[] imageBytes = byteArrayOutputStream.toByteArray(); String base64encoded = Base64.encodeToString(imageBytes, Base64.NO_WRAP);
Bitmap 物件代表的圖片必須是直立的,不需要額外旋轉。2. 叫用可呼叫函式來標記圖片
如要為圖片中的物件加上標籤,請叫用可呼叫函式,並傳遞 JSON Cloud Vision 要求。首先,初始化 Cloud Functions 執行個體:
Kotlin
private lateinit var functions: FirebaseFunctions // ... functions = Firebase.functionsJava
private FirebaseFunctions mFunctions; // ... mFunctions = FirebaseFunctions.getInstance();定義叫用函式的方法:
Kotlin
private fun annotateImage(requestJson: String): Task<JsonElement> { return functions .getHttpsCallable("annotateImage") .call(requestJson) .continueWith { task -> // This continuation runs on either success or failure, but if the task // has failed then result will throw an Exception which will be // propagated down. val result = task.result?.data JsonParser.parseString(Gson().toJson(result)) } }Java
private Task<JsonElement> annotateImage(String requestJson) { return mFunctions .getHttpsCallable("annotateImage") .call(requestJson) .continueWith(new Continuation<HttpsCallableResult, JsonElement>() { @Override public JsonElement then(@NonNull Task<HttpsCallableResult> task) { // This continuation runs on either success or failure, but if the task // has failed then getResult() will throw an Exception which will be // propagated down. return JsonParser.parseString(new Gson().toJson(task.getResult().getData())); } }); }建立 JSON 要求,並將「Type」設為
LABEL_DETECTION:Kotlin
// Create json request to cloud vision val request = JsonObject() // Add image to request val image = JsonObject() image.add("content", JsonPrimitive(base64encoded)) request.add("image", image) // Add features to the request val feature = JsonObject() feature.add("maxResults", JsonPrimitive(5)) feature.add("type", JsonPrimitive("LABEL_DETECTION")) val features = JsonArray() features.add(feature) request.add("features", features)Java
// Create json request to cloud vision JsonObject request = new JsonObject(); // Add image to request JsonObject image = new JsonObject(); image.add("content", new JsonPrimitive(base64encoded)); request.add("image", image); //Add features to the request JsonObject feature = new JsonObject(); feature.add("maxResults", new JsonPrimitive(5)); feature.add("type", new JsonPrimitive("LABEL_DETECTION")); JsonArray features = new JsonArray(); features.add(feature); request.add("features", features);最後,叫用函式:
Kotlin
annotateImage(request.toString()) .addOnCompleteListener { task -> if (!task.isSuccessful) { // Task failed with an exception // ... } else { // Task completed successfully // ... } }Java
annotateImage(request.toString()) .addOnCompleteListener(new OnCompleteListener<JsonElement>() { @Override public void onComplete(@NonNull Task<JsonElement> task) { if (!task.isSuccessful()) { // Task failed with an exception // ... } else { // Task completed successfully // ... } } });
3. 取得已加上標籤的物件資訊
如果圖片標籤作業成功,系統會在工作結果中傳回 BatchAnnotateImagesResponse 的 JSON 回應。labelAnnotations 陣列中的每個物件都代表圖片中加上標籤的內容。您可以取得每個標籤的文字說明、知識圖譜實體 ID (如有) 和相符項目的可信度分數。例如:
Kotlin
for (label in task.result!!.asJsonArray[0].asJsonObject["labelAnnotations"].asJsonArray) {
val labelObj = label.asJsonObject
val text = labelObj["description"]
val entityId = labelObj["mid"]
val confidence = labelObj["score"]
}
Java
for (JsonElement label : task.getResult().getAsJsonArray().get(0).getAsJsonObject().get("labelAnnotations").getAsJsonArray()) {
JsonObject labelObj = label.getAsJsonObject();
String text = labelObj.get("description").getAsString();
String entityId = labelObj.get("mid").getAsString();
float score = labelObj.get("score").getAsFloat();
}