Save the date - Google I/O returns May 18-20. Register to get the most out of the digital experience: Build your schedule, reserve space, participate in Q&As, earn Google Developer profile badges, and more. Register now
本頁面由 Cloud Translation API 翻譯而成。
Switch to English

在Android上使用Firebase Auth和功能通過Cloud Vision安全地標記圖像

為了從您的應用程序調用Google Cloud API,您需要創建一個中間的REST API,該API處理授權並保護API密鑰之類的秘密值。然後,您需要在您的移動應用程序中編寫代碼以對該中間服務進行身份驗證並與之通信。

創建此REST API的一種方法是使用Firebase身份驗證和功能,它為您提供了一個可管理的,無服務器的Google Cloud API網關,該網關可處理身份驗證,並可通過您的移動應用使用內置的SDK進行調用。

本指南演示瞭如何使用此技術從您的應用程序調用Cloud Vision API。此方法將允許所有經過身份驗證的用戶通過您的Cloud項目訪問Cloud Vision計費服務,因此在繼續操作之前,請考慮此身份驗證機制是否足以滿足您的用例。

在你開始之前

配置您的項目

  1. 如果尚未將Firebase添加到您的Android項目中
  2. 如果您尚未為項目啟用基於雲的API,請立即執行以下操作:

    1. 打開Firebase控制台的Firebase ML API頁面
    2. 如果尚未將項目升級到Blaze定價計劃,請單擊“升級” 。 (僅當您的項目不在Blaze計劃中時,系統才會提示您升級。)

      只有Blaze級別的項目才能使用基於雲的API。

    3. 如果尚未啟用基於雲的API,請點擊啟用基於雲的API
  3. 配置您現有的Firebase API密鑰以禁止訪問Cloud Vision API:
    1. 打開雲控制台的“憑據”頁面。
    2. 對於列表中的每個API密鑰,打開編輯視圖,然後在“密鑰限制”部分中,將Cloud Vision API之外的所有可用API添加到列表中。

部署可調用函數

接下來,部署將用於橋接應用程序和Cloud Vision API的Cloud Function。 functions-samples存儲庫包含您可以使用的示例。

默認情況下,通過此功能訪問Cloud Vision API將僅允許您應用程序的經過身份驗證的用戶訪問Cloud Vision API。您可以針對不同需求修改功能。

部署功能:

  1. 克隆或下載功能樣本回購,然後轉到vision-annotate-image目錄:
    git clone https://github.com/firebase/functions-samples
    cd vision-annotate-image
    
  2. 安裝依賴項:
    cd functions
    npm install
    cd ..
    
  3. 如果您沒有Firebase CLI,請安裝它
  4. vision-annotate-image目錄中初始化Firebase項目。出現提示時,在列表中選擇您的項目。
    firebase init
  5. 部署功能:
    firebase deploy --only functions:annotateImage

將Firebase身份驗證添加到您的應用

上面部署的callable函數將拒絕您應用程序未經身份驗證的用戶的任何請求。如果您尚未這樣做,則需要將Firebase Auth添加到您的應用中。

向您的應用添加必要的依賴項

  • 將Firebase Functions和gson Android庫的依賴項添加到模塊(應用程序級)Gradle文件(通常為app / build.gradle):
    implementation 'com.google.firebase:firebase-functions:19.2.0'
    implementation 'com.google.code.gson:gson:2.8.6'
    
  • 現在您可以為圖像加標籤了。

    1.準備輸入圖像

    為了調用Cloud Vision,必須將圖像格式化為base64編碼的字符串。要從保存的文件URI處理圖像,請執行以下操作:
    1. 獲取圖像作為Bitmap對象:

      爪哇

      Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);

      Kotlin + KTX

      var bitmap: Bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
    2. (可選)按比例縮小圖像以節省帶寬。請參閱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)
    3. 將位圖對象轉換為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)
    4. Bitmap對象表示的圖像必須是直立的,不需要額外旋轉。

    2.調用可調用函數以標記圖像

    要標記圖像中的對象,請調用傳遞JSON Cloud Vision request的可調用函數。

    1. 首先,初始化一個Cloud Functions實例:

      爪哇

      private FirebaseFunctions mFunctions;
      // ...
      mFunctions = FirebaseFunctions.getInstance();
      

      Kotlin + KTX

      private lateinit var functions: FirebaseFunctions
      // ...
      functions = Firebase.functions
      
    2. 定義調用該函數的方法:

      爪哇

      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))
                  }
      }
      
    3. 創建類型設置為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)
      
    4. 最後,調用函數:

      爪哇

      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"]
    }