Memberi Label pada Gambar dengan Firebase ML di Android

Anda dapat menggunakan Firebase ML untuk memberi label pada objek yang dikenali dalam gambar. Baca ringkasan untuk mengetahui informasi tentang fitur-fitur pada API ini.

Sebelum memulai

  1. Tambahkan Firebase ke project Android jika Anda belum melakukannya.
  2. Dalam file Gradle modul (level aplikasi) (biasanya <project>/<app-module>/build.gradle.kts atau <project>/<app-module>/build.gradle), tambahkan dependensi untuk library Firebase ML Vision untuk Android. Sebaiknya gunakan Firebase Android BoM untuk mengontrol pembuatan versi library.
    dependencies {
        // Import the BoM for the Firebase platform
        implementation(platform("com.google.firebase:firebase-bom:33.6.0"))
    
        // Add the dependency for the Firebase ML Vision library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-ml-vision'
    }

    Dengan menggunakan Firebase Android BoM, aplikasi Anda akan selalu menggunakan versi library Android Firebase yang kompatibel.

    (Alternatif)  Tambahkan dependensi library Firebase tanpa menggunakan BoM

    Jika memilih untuk tidak menggunakan Firebase BoM, Anda harus menentukan setiap versi library Firebase di baris dependensinya.

    Perlu diperhatikan bahwa jika Anda menggunakan beberapa library Firebase di aplikasi, sebaiknya gunakan BoM untuk mengelola versi library, yang memastikan bahwa semua versi kompatibel.

    dependencies {
        // Add the dependency for the Firebase ML Vision library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-ml-vision:24.1.0'
    }
    Mencari modul library khusus Kotlin? Mulai Oktober 2023 (Firebase BoM 32.5.0), developer Kotlin dan Java dapat bergantung pada modul library utama (untuk mengetahui detailnya, lihat FAQ tentang inisiatif ini).
  3. Jika belum mengaktifkan API berbasis Cloud untuk project, lakukan sekarang:

    1. Buka halaman API di bagian Firebase ML 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 tersebut tidak menggunakan paket Blaze.)

      Hanya project tingkat Blaze yang dapat menggunakan API berbasis Cloud.

    3. Jika API berbasis Cloud belum diaktifkan, klik Enable Cloud-based APIs.

Sekarang Anda siap memberi label pada gambar.

1. Menyiapkan gambar input

Buat objek FirebaseVisionImage dari gambar Anda. Pemberi label pada gambar berfungsi optimal jika Anda menggunakan Bitmap, atau media.Image berformat JPEG jika Anda menggunakan camera2 API. Sebaiknya gunakan salah satunya jika memungkinkan.

  • Untuk membuat objek FirebaseVisionImage dari objek media.Image, seperti saat mengambil gambar dari kamera perangkat, teruskan objek media.Image dan nilai rotasi gambar ke FirebaseVisionImage.fromMediaImage().

    Jika Anda menggunakan library CameraX, class OnImageCapturedListener dan ImageAnalysis.Analyzer akan menghitung nilai rotasinya, sehingga Anda hanya perlu mengonversi rotasi tersebut ke salah satu konstanta ROTATION_ Firebase ML sebelum memanggil FirebaseVisionImage.fromMediaImage():

    Kotlin+KTX

    private class YourImageAnalyzer : ImageAnalysis.Analyzer {
        private fun degreesToFirebaseRotation(degrees: Int): Int = when(degrees) {
            0 -> FirebaseVisionImageMetadata.ROTATION_0
            90 -> FirebaseVisionImageMetadata.ROTATION_90
            180 -> FirebaseVisionImageMetadata.ROTATION_180
            270 -> FirebaseVisionImageMetadata.ROTATION_270
            else -> throw Exception("Rotation must be 0, 90, 180, or 270.")
        }
    
        override fun analyze(imageProxy: ImageProxy?, degrees: Int) {
            val mediaImage = imageProxy?.image
            val imageRotation = degreesToFirebaseRotation(degrees)
            if (mediaImage != null) {
                val image = FirebaseVisionImage.fromMediaImage(mediaImage, imageRotation)
                // Pass image to an ML Vision API
                // ...
            }
        }
    }

    Java

    private class YourAnalyzer implements ImageAnalysis.Analyzer {
    
        private int degreesToFirebaseRotation(int degrees) {
            switch (degrees) {
                case 0:
                    return FirebaseVisionImageMetadata.ROTATION_0;
                case 90:
                    return FirebaseVisionImageMetadata.ROTATION_90;
                case 180:
                    return FirebaseVisionImageMetadata.ROTATION_180;
                case 270:
                    return FirebaseVisionImageMetadata.ROTATION_270;
                default:
                    throw new IllegalArgumentException(
                            "Rotation must be 0, 90, 180, or 270.");
            }
        }
    
        @Override
        public void analyze(ImageProxy imageProxy, int degrees) {
            if (imageProxy == null || imageProxy.getImage() == null) {
                return;
            }
            Image mediaImage = imageProxy.getImage();
            int rotation = degreesToFirebaseRotation(degrees);
            FirebaseVisionImage image =
                    FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
            // Pass image to an ML Vision API
            // ...
        }
    }

    Jika tidak menggunakan library kamera yang memberikan nilai rotasi gambar, Anda dapat menghitungnya dari rotasi perangkat dan orientasi sensor kamera pada perangkat:

    Kotlin+KTX

    private val ORIENTATIONS = SparseIntArray()
    
    init {
        ORIENTATIONS.append(Surface.ROTATION_0, 90)
        ORIENTATIONS.append(Surface.ROTATION_90, 0)
        ORIENTATIONS.append(Surface.ROTATION_180, 270)
        ORIENTATIONS.append(Surface.ROTATION_270, 180)
    }
    /**
     * Get the angle by which an image must be rotated given the device's current
     * orientation.
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Throws(CameraAccessException::class)
    private fun getRotationCompensation(cameraId: String, activity: Activity, context: Context): Int {
        // Get the device's current rotation relative to its "native" orientation.
        // Then, from the ORIENTATIONS table, look up the angle the image must be
        // rotated to compensate for the device's rotation.
        val deviceRotation = activity.windowManager.defaultDisplay.rotation
        var rotationCompensation = ORIENTATIONS.get(deviceRotation)
    
        // On most devices, the sensor orientation is 90 degrees, but for some
        // devices it is 270 degrees. For devices with a sensor orientation of
        // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
        val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager
        val sensorOrientation = cameraManager
            .getCameraCharacteristics(cameraId)
            .get(CameraCharacteristics.SENSOR_ORIENTATION)!!
        rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360
    
        // Return the corresponding FirebaseVisionImageMetadata rotation value.
        val result: Int
        when (rotationCompensation) {
            0 -> result = FirebaseVisionImageMetadata.ROTATION_0
            90 -> result = FirebaseVisionImageMetadata.ROTATION_90
            180 -> result = FirebaseVisionImageMetadata.ROTATION_180
            270 -> result = FirebaseVisionImageMetadata.ROTATION_270
            else -> {
                result = FirebaseVisionImageMetadata.ROTATION_0
                Log.e(TAG, "Bad rotation value: $rotationCompensation")
            }
        }
        return result
    }

    Java

    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
    static {
        ORIENTATIONS.append(Surface.ROTATION_0, 90);
        ORIENTATIONS.append(Surface.ROTATION_90, 0);
        ORIENTATIONS.append(Surface.ROTATION_180, 270);
        ORIENTATIONS.append(Surface.ROTATION_270, 180);
    }
    
    /**
     * Get the angle by which an image must be rotated given the device's current
     * orientation.
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private int getRotationCompensation(String cameraId, Activity activity, Context context)
            throws CameraAccessException {
        // Get the device's current rotation relative to its "native" orientation.
        // Then, from the ORIENTATIONS table, look up the angle the image must be
        // rotated to compensate for the device's rotation.
        int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        int rotationCompensation = ORIENTATIONS.get(deviceRotation);
    
        // On most devices, the sensor orientation is 90 degrees, but for some
        // devices it is 270 degrees. For devices with a sensor orientation of
        // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
        CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE);
        int sensorOrientation = cameraManager
                .getCameraCharacteristics(cameraId)
                .get(CameraCharacteristics.SENSOR_ORIENTATION);
        rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360;
    
        // Return the corresponding FirebaseVisionImageMetadata rotation value.
        int result;
        switch (rotationCompensation) {
            case 0:
                result = FirebaseVisionImageMetadata.ROTATION_0;
                break;
            case 90:
                result = FirebaseVisionImageMetadata.ROTATION_90;
                break;
            case 180:
                result = FirebaseVisionImageMetadata.ROTATION_180;
                break;
            case 270:
                result = FirebaseVisionImageMetadata.ROTATION_270;
                break;
            default:
                result = FirebaseVisionImageMetadata.ROTATION_0;
                Log.e(TAG, "Bad rotation value: " + rotationCompensation);
        }
        return result;
    }

    Lalu, teruskan objek media.Image dan nilai rotasi ke FirebaseVisionImage.fromMediaImage():

    Kotlin+KTX

    val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)

    Java

    FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
  • Untuk membuat objek FirebaseVisionImage dari URI file, teruskan konteks aplikasi dan URI file ke FirebaseVisionImage.fromFilePath(). Hal ini berguna saat Anda menggunakan intent ACTION_GET_CONTENT untuk meminta pengguna memilih gambar dari aplikasi galeri mereka.

    Kotlin+KTX

    val image: FirebaseVisionImage
    try {
        image = FirebaseVisionImage.fromFilePath(context, uri)
    } catch (e: IOException) {
        e.printStackTrace()
    }

    Java

    FirebaseVisionImage image;
    try {
        image = FirebaseVisionImage.fromFilePath(context, uri);
    } catch (IOException e) {
        e.printStackTrace();
    }
  • Untuk membuat objek FirebaseVisionImage dari ByteBuffer atau array byte, pertama-tama hitung rotasi gambar seperti yang dijelaskan di atas untuk input media.Image.

    Lalu, buat objek FirebaseVisionImageMetadata yang berisi tinggi, lebar, format encoding warna, dan rotasi gambar:

    Kotlin+KTX

    val metadata = FirebaseVisionImageMetadata.Builder()
        .setWidth(480) // 480x360 is typically sufficient for
        .setHeight(360) // image recognition
        .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
        .setRotation(rotation)
        .build()

    Java

    FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
            .setWidth(480)   // 480x360 is typically sufficient for
            .setHeight(360)  // image recognition
            .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
            .setRotation(rotation)
            .build();

    Gunakan buffering atau array, dan objek metadata, untuk membuat objek FirebaseVisionImage:

    Kotlin+KTX

    val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata)
    // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)

    Java

    FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
    // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
  • Untuk membuat objek FirebaseVisionImage dari objek Bitmap:

    Kotlin+KTX

    val image = FirebaseVisionImage.fromBitmap(bitmap)

    Java

    FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
    Gambar yang diwakili oleh objek Bitmap harus berposisi tegak, tanpa perlu rotasi tambahan.

2. Mengonfigurasi dan menjalankan pemberi label pada gambar

Untuk memberi label pada objek dalam gambar, teruskan objek FirebaseVisionImage ke metode processImage FirebaseVisionImageLabeler.

  1. Pertama, dapatkan instance FirebaseVisionImageLabeler.

    Kotlin+KTX

    val labeler = FirebaseVision.getInstance().getCloudImageLabeler()
    
    // Or, to set the minimum confidence required:
    // val options = FirebaseVisionCloudImageLabelerOptions.Builder()
    //     .setConfidenceThreshold(0.7f)
    //     .build()
    // val labeler = FirebaseVision.getInstance().getCloudImageLabeler(options)
    

    Java

    FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance()
        .getCloudImageLabeler();
    
    // Or, to set the minimum confidence required:
    // FirebaseVisionCloudImageLabelerOptions options =
    //     new FirebaseVisionCloudImageLabelerOptions.Builder()
    //         .setConfidenceThreshold(0.7f)
    //         .build();
    // FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance()
    //     .getCloudImageLabeler(options);
    

  2. Lalu, teruskan gambar ke metode processImage():

    Kotlin+KTX

    labeler.processImage(image)
        .addOnSuccessListener { labels ->
          // Task completed successfully
          // ...
        }
        .addOnFailureListener { e ->
          // Task failed with an exception
          // ...
        }
    

    Java

    labeler.processImage(image)
        .addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionImageLabel>>() {
          @Override
          public void onSuccess(List<FirebaseVisionImageLabel> labels) {
            // Task completed successfully
            // ...
          }
        })
        .addOnFailureListener(new OnFailureListener() {
          @Override
          public void onFailure(@NonNull Exception e) {
            // Task failed with an exception
            // ...
          }
        });
    

3. Mendapatkan informasi tentang objek berlabel

Jika operasi pelabelan pada gambar berhasil, daftar objek FirebaseVisionImageLabel akan diteruskan ke pemroses peristiwa sukses. Setiap objek FirebaseVisionImageLabel mewakili sesuatu yang dilabeli dalam gambar. Untuk setiap label, Anda bisa mendapatkan deskripsi teks, ID entitas Pustaka Pengetahuan (jika ada), dan skor keyakinan kecocokannya. Contoh:

Kotlin+KTX

for (label in labels) {
  val text = label.text
  val entityId = label.entityId
  val confidence = label.confidence
}

Java

for (FirebaseVisionImageLabel label: labels) {
  String text = label.getText();
  String entityId = label.getEntityId();
  float confidence = label.getConfidence();
}

Langkah berikutnya