為了從您的應用程序調用Google Cloud API,您需要創建一個中間的REST API,該API處理授權並保護API密鑰等秘密值。然後,您需要在移動應用程序中編寫代碼,以對此中間服務進行身份驗證並與之通信。
創建此REST API的一種方法是使用Firebase身份驗證和功能,它為您提供了一個可管理的,無服務器的Google Cloud API網關,該網關可處理身份驗證,並且可以使用內置的SDK從移動應用中調用。
本指南演示瞭如何使用此技術從您的應用程序調用Cloud Vision API。此方法將允許所有經過身份驗證的用戶通過您的Cloud項目訪問Cloud Vision計費服務,因此在繼續操作之前,請考慮此身份驗證機制是否足以滿足您的用例。
在你開始之前
配置項目
- 如果尚未將Firebase添加到您的Android項目中。
如果您尚未為項目啟用基於雲的API,請立即執行以下操作:
- 打開Firebase控制台的Firebase ML API頁面。
如果尚未將項目升級到Blaze計劃,請單擊“升級” 。 (僅當您的項目不在Blaze計劃中時,系統才會提示您升級。)
只有Blaze級別的項目才能使用基於雲的API。
- 如果尚未啟用基於雲的API,請點擊啟用基於雲的API 。
- 配置您現有的Firebase API密鑰以禁止訪問Cloud Vision API:
- 打開雲控制台的“憑據”頁面。
- 對於列表中的每個API密鑰,打開編輯視圖,然後在“密鑰限制”部分中,將除Cloud Vision API之外的所有可用API添加到列表中。
部署可調用函數
接下來,部署將用於橋接應用程序和Cloud Vision API的Cloud Function。 functions-samples
存儲庫包含您可以使用的示例。
默認情況下,通過此功能訪問Cloud Vision API將僅允許您應用程序的經過身份驗證的用戶訪問Cloud Vision API。您可以針對不同需求修改功能。
部署功能:
- 克隆或下載功能樣本回購,然後轉到
vision-annotate-image
目錄:git clone https://github.com/firebase/functions-samples
cd vision-annotate-image
- 安裝依賴項:
cd functions
npm install
cd ..
- 如果您沒有Firebase CLI,請安裝它。
- 在
vision-annotate-image
目錄中初始化Firebase項目。出現提示時,在列表中選擇您的項目。firebase init
- 部署功能:
firebase deploy --only functions:annotateImage
將Firebase身份驗證添加到您的應用
上面部署的callable函數將拒絕您應用程序未經身份驗證的用戶的任何請求。如果您尚未這樣做,則需要將Firebase Auth添加到您的應用中。
向您的應用添加必要的依賴項
implementation 'com.google.firebase:firebase-functions:19.2.0' implementation 'com.google.code.gson:gson:2.8.6'
現在您可以為圖像加標籤了。
1.準備輸入圖像
為了調用Cloud Vision,必須將圖像格式化為base64編碼的字符串。要從保存的文件URI處理圖像:- 獲取圖像作為
Bitmap
對象:爪哇
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
Kotlin + KTX
var bitmap: Bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
- (可選)按比例縮小圖像以節省帶寬。請參閱Cloud Vision建議的圖像大小。
爪哇
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 + KTX
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) }
爪哇
// Scale down bitmap size bitmap = scaleBitmapDown(bitmap, 640);
Kotlin + KTX
// Scale down bitmap size bitmap = scaleBitmapDown(bitmap, 640)
- 將位圖對象轉換為base64編碼的字符串:
爪哇
// 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);
Kotlin + KTX
// 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)
由
Bitmap
對象表示的圖像必須是直立的,不需要額外旋轉。 2.調用可調用函數以標記圖像
要標記圖像中的對象,請調用傳遞JSON Cloud Vision request的可調用函數。首先,初始化一個Cloud Functions實例:
爪哇
private FirebaseFunctions mFunctions; // ... mFunctions = FirebaseFunctions.getInstance();
Kotlin + KTX
private lateinit var functions: FirebaseFunctions // ... functions = Firebase.functions
定義調用函數的方法:
爪哇
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())); } }); }
Kotlin + KTX
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)) } }
創建類型設置為
LABEL_DETECTION
的JSON請求:爪哇
// 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 + KTX
// 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)
最後,調用函數:
爪哇
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 // ... } } });
Kotlin + KTX
annotateImage(request.toString()) .addOnCompleteListener { task -> if (!task.isSuccessful) { // Task failed with an exception // ... } else { // Task completed successfully // ... } }
3.獲取有關標籤對象的信息
如果圖像標記操作成功,則任務結果中將返回BatchAnnotateImagesResponse的JSON響應。labelAnnotations
數組中的每個對labelAnnotations
代表圖像中標記的對象。對於每個標籤,您可以獲得標籤的文本描述,其知識圖實體ID (如果有)以及匹配項的置信度得分。例如: 爪哇
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();
}
Kotlin + KTX
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"]
}