Buka konsol

Memberikan Label pada Gambar dengan ML Kit di Android

Anda dapat menggunakan ML Kit untuk melabeli objek yang dikenali dalam gambar, baik dengan menggunakan model di perangkat maupun model cloud. Baca ringkasan untuk mempelajari manfaat tiap-tiap pendekatan tersebut.

Lihat sampel panduan memulai ML Kit di GitHub untuk mengetahui contoh penggunaan API ini.

Sebelum memulai

  1. Tambahkan Firebase ke project Android jika Anda belum melakukannya.
  2. Pada file build.gradle level project, pastikan untuk menyertakan repositori Maven Google di bagian buildscript dan allprojects Anda.
  3. Tambahkan dependensi untuk library Android ML Kit ke file Gradle modul (tingkat aplikasi) (biasanya app/build.gradle):
    dependencies {
      // ...
    
      implementation 'com.google.firebase:firebase-ml-vision:23.0.0'
      implementation 'com.google.firebase:firebase-ml-vision-image-label-model:18.0.0'
    }
    apply plugin: 'com.google.gms.google-services'
    
  4. Opsional tetapi direkomendasikan: Jika Anda menggunakan API di perangkat, konfigurasikan aplikasi Anda untuk mendownload model ML secara otomatis ke perangkat setelah aplikasi diinstal dari Play Store.

    Untuk melakukannya, tambahkan deklarasi berikut ke file AndroidManifest.xml aplikasi Anda:

    <application ...>
      ...
      <meta-data
          android:name="com.google.firebase.ml.vision.DEPENDENCIES"
          android:value="label" />
      <!-- To use multiple models: android:value="label,model2,model3" -->
    </application>
    
    Jika Anda tidak mengaktifkan download model waktu-instal, model ini akan didownload saat pertama kali Anda menjalankan detektor pada perangkat. Permintaan yang Anda buat sebelum download selesai tidak akan menghasilkan apa pun.
  5. Jika Anda ingin menggunakan model berbasis Cloud, dan Anda belum mengaktifkan API berbasis Cloud untuk project Anda, lakukan sekarang:

    1. Buka halaman ML Kit API dari konsol Firebase.
    2. Jika Anda belum meng-upgrade project Anda ke paket Blaze, klik Upgrade untuk melakukannya. (Anda akan diminta untuk meng-upgrade hanya jika project Anda tidak dalam paket Blaze.)

      Hanya project tingkat Blaze yang dapat menggunakan API berbasis Cloud.

    3. Jika API berbasis Cloud belum diaktifkan, klik Aktifkan API berbasis Cloud.

    Jika hanya ingin menggunakan model pada perangkat, Anda dapat melewati langkah ini.

Kini Anda siap memberikan label pada gambar menggunakan model di perangkat atau model berbasis cloud.

1. Menyiapkan gambar input

Buat objek FirebaseVisionImage dari gambar Anda. Pemberi label pada gambar berfungsi secara optimal saat Anda menggunakan Bitmap atau jika Anda menggunakan camera2 API, media.Image berformat JPEG, yang direkomendasikan jika memungkinkan.

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

    Jika Anda menggunakan library CameraX, class OnImageCapturedListener dan ImageAnalysis.Analyzer akan menghitung nilai rotasi untuk Anda, jadi Anda hanya perlu mengubah rotasi ke salah satu konstanta ROTATION_ ML Kit sebelum memanggil FirebaseVisionImage.fromMediaImage():

    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 Kit Vision API
            // ...
        }
    }
    

    Kotlin

    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 Kit Vision API
                // ...
            }
        }
    }
    

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

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

    Kotlin

    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
    }

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

    Java

    FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);

    Kotlin

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

    Java

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

    Kotlin

    val image: FirebaseVisionImage
    try {
        image = FirebaseVisionImage.fromFilePath(context, uri)
    } catch (e: IOException) {
        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:

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

    Kotlin

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

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

    Java

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

    Kotlin

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

    Java

    FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

    Kotlin

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

2. Mengonfigurasikan dan mejalankan pemberi label pada gambar

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

  1. Pertama, dapatkan instance FirebaseVisionImageLabeler.

    Jika Anda ingin menggunakan pemberi label pada gambar di perangkat:

    Java

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

    Kotlin

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

    Jika Anda ingin menggunakan pemberi label pada gambar di cloud:

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

    Kotlin

    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)
    

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

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

    Kotlin

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

3. Mendapatkan informasi tentang objek berlabel

Jika operasi pelabelan gambar berhasil, daftar objek FirebaseVisionImageLabel akan diteruskan ke pemroses yang berhasil. Setiap objek FirebaseVisionImageLabel mewakili sesuatu yang berlabel dalam gambar. Untuk setiap label, Anda bisa mendapatkan deskripsi teks label tersebut, ID entitas Grafik Pengetahuan (jika tersedia), dan skor keyakinan dari kecocokan tersebut. Contoh:

Java

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

Kotlin

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

Tips untuk meningkatkan performa real-time

Jika Anda ingin memberikan label pada gambar dalam aplikasi real-time, ikuti panduan ini untuk mencapai frekuensi gambar terbaik:

  • Batasi panggilan ke pemberi label pada gambar. Jika frame video baru tersedia saat pemberi label pada gambar sedang berjalan, hapus frame tersebut. Lihat class VisionProcessorBase dalam aplikasi sampel panduan memulai untuk digunakan sebagai contoh.
  • Jika Anda menggunakan output pemberi label pada gambar untuk menempatkan grafis pada gambar input, pertama-tama dapatkan hasil dari ML Kit, lalu render gambar dan tempatkan grafis dalam satu langkah. Dengan demikian, Anda hanya merender ke permukaan tampilan sekali untuk setiap frame input. Lihat class CameraSourcePreview dan GraphicOverlay di aplikasi sampel panduan memulai untuk digunakan sebagai contoh.
  • Jika Anda menggunakan Camera2 API, ambil gambar dalam format ImageFormat.YUV_420_888.

    Jika Anda menggunakan Camera API versi lama, ambil gambar dalam format ImageFormat.NV21.

Langkah berikutnya