Google berkomitmen untuk mendorong terwujudnya keadilan ras bagi komunitas Kulit Hitam. Lihat caranya.

Memberi Label pada Gambar secara Aman dengan Cloud Vision menggunakan Firebase Auth dan Functions di Android

Untuk memanggil Google Cloud API dari aplikasi, Anda perlu membuat REST API perantara yang menangani otorisasi dan melindungi nilai rahasia seperti kunci API. Kemudian, Anda perlu menulis kode di aplikasi seluler untuk mengautentikasi dan berkomunikasi dengan layanan perantara ini.

Salah satu cara untuk membuat REST API ini adalah dengan menggunakan Firebase Authentication dan Functions, yang memberi Anda gateway tanpa server dan terkelola ke Google Cloud API yang menangani autentikasi dan dapat dipanggil dari aplikasi seluler Anda dengan SDK yang telah dibuat sebelumnya.

Panduan ini menunjukkan cara menggunakan teknik tersebut untuk memanggil Cloud Vision API dari aplikasi Anda. Metode ini akan mengizinkan semua pengguna terautentikasi untuk mengakses layanan yang ditagih Cloud Vision melalui project Cloud Anda. Jadi, pertimbangkan apakah mekanisme autentikasi ini sudah memadai untuk kasus penggunaan Anda sebelum melanjutkan.

Sebelum memulai

Mengonfigurasi project Anda

  1. Tambahkan Firebase ke project Android jika belum melakukannya.
  2. Jika belum mengaktifkan API berbasis Cloud untuk project Anda, lakukan sekarang:

    1. Buka halaman Firebase ML API di Firebase console.
    2. Jika Anda belum mengupgrade project ke paket harga Blaze, klik Upgrade untuk melakukannya. (Anda akan diminta untuk mengupgrade hanya jika project tidak menggunakan paket Blaze.)

      Hanya project tingkat Blaze yang dapat menggunakan API berbasis Cloud.

    3. Jika API berbasis Cloud belum diaktifkan, klik Aktifkan API berbasis Cloud.
  3. Konfigurasikan kunci API Firebase yang ada untuk melarang akses ke Cloud Vision API:
    1. Buka halaman Kredensial pada Cloud Console.
    2. Untuk setiap kunci API dalam daftar, buka tampilan edit, dan di bagian Pembatasan Kunci, tambahkan semua API yang tersedia ke dalam daftar, kecuali Cloud Vision API.

Men-deploy fungsi callable

Selanjutnya, deploy Cloud Function yang akan Anda gunakan untuk menghubungkan aplikasi dan Cloud Vision API. Repositori functions-samples berisi contoh yang dapat Anda gunakan.

Secara default, mengakses Cloud Vision API melalui fungsi ini akan memberikan akses ke Cloud Vision API hanya kepada pengguna aplikasi yang telah terautentikasi. Anda dapat mengubah fungsi untuk persyaratan yang berbeda.

Untuk men-deploy fungsi tersebut:

  1. Clone atau download repo functions-samples dan ubah ke direktori vision-annotate-image:
    git clone https://github.com/firebase/functions-samples
    cd vision-annotate-image
    
  2. Instal dependensi:
    cd functions
    npm install
    cd ..
    
  3. Jika Anda tidak memiliki Firebase CLI, instal sekarang.
  4. Lakukan inisialisasi project Firebase di direktori vision-annotate-image. Jika diminta, pilih project Anda dalam daftar.
    firebase init
  5. Deploy fungsinya:
    firebase deploy --only functions:annotateImage

Menambahkan Firebase Auth ke aplikasi Anda

Fungsi callable yang di-deploy di atas akan menolak permintaan apa pun dari pengguna aplikasi yang tidak terautentikasi. Jika belum melakukannya, Anda harus menambahkan Firebase Auth ke aplikasi Anda.

Menambahkan dependensi yang diperlukan ke aplikasi Anda

  • Tambahkan dependensi untuk library Firebase Functions dan Android gson ke file Gradle modul (level aplikasi) (biasanya app/build.gradle):
    implementation 'com.google.firebase:firebase-functions:20.0.0'
    implementation 'com.google.code.gson:gson:2.8.6'
    
  • Sekarang Anda siap memberi label pada gambar.

    1. Menyiapkan gambar input

    Untuk memanggil Cloud Vision, gambar harus diformat sebagai string berenkode base64. Untuk memproses gambar dari URI file tersimpan:
    1. Dapatkan gambar sebagai objek Bitmap:

      Java

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

      Kotlin+KTX

      var bitmap: Bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
    2. Jika ingin, perkecil skala gambar untuk menghemat bandwidth. Lihat ukuran gambar yang direkomendasikan Cloud Vision.

      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+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)
      }

      Java

      // Scale down bitmap size
      bitmap = scaleBitmapDown(bitmap, 640);

      Kotlin+KTX

      // Scale down bitmap size
      bitmap = scaleBitmapDown(bitmap, 640)
    3. Konversi objek bitmap ke string berenkode base64:

      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);

      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. Gambar yang diwakili oleh objek Bitmap harus berposisi tegak, tanpa perlu rotasi tambahan.

    2. Memanggil fungsi callable untuk memberi label pada gambar

    Untuk memberi label objek dalam gambar, panggil fungsi callable yang meneruskan permintaan JSON Cloud Vision.

    1. Pertama, lakukan inisialisasi instance Cloud Functions:

      Java

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

      Kotlin+KTX

      private lateinit var functions: FirebaseFunctions
      // ...
      functions = Firebase.functions
      
    2. Tentukan metode untuk memanggil fungsi:

      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()));
                      }
                  });
      }
      

      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. Buat permintaan JSON dengan Jenis yang ditetapkan ke LABEL_DETECTION:

      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+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. Terakhir, panggil fungsi tersebut:

      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
                          // ...
                      }
                  }
              });
      

      Kotlin+KTX

      annotateImage(request.toString())
              .addOnCompleteListener { task ->
                  if (!task.isSuccessful) {
                      // Task failed with an exception
                      // ...
                  } else {
                      // Task completed successfully
                      // ...
                  }
              }
      

    3. Mendapatkan informasi tentang objek berlabel

    Jika operasi pelabelan gambar berhasil, respons JSON BatchAnnotateImagesResponse akan ditampilkan di hasil tugas. Setiap objek dalam array labelAnnotations mewakili sesuatu yang diberi label dalam gambar. Untuk setiap label, Anda bisa mendapatkan deskripsi teks labelnya, ID entitas Grafik Pengetahuannya (jika ada), dan skor keyakinan kecocokannya. Contoh:

    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();
    }
    

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