Anda dapat menggunakan ML Kit untuk mendeteksi dan melacak objek di seluruh bingkai video.
Ketika Anda meneruskan gambar ke ML Kit, ML Kit akan menampilkan daftar hingga 5 objek yang terdeteksi untuk setiap gambar dan posisinya dalam gambar tersebut. Saat mendeteksi objek dalam streaming video, setiap objek memiliki ID yang dapat Anda gunakan untuk melacak objek di seluruh gambar. Anda juga dapat mengaktifkan klasifikasi objek mentah secara opsional, yang menandai objek dengan deskripsi umum kategori.
Sebelum memulai
- Tambahkan Firebase ke project Android jika belum melakukannya.
- Tambahkan dependensi untuk library Android ML Kit ke file Gradle modul (level aplikasi), biasanya
app/build.gradle
:apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' dependencies { // ... implementation 'com.google.firebase:firebase-ml-vision:24.0.3' implementation 'com.google.firebase:firebase-ml-vision-object-detection-model:19.0.6' }
1. Mengonfigurasi detektor objek
Untuk mulai mendeteksi dan melacak objek, pertama-tama buat instance
FirebaseVisionObjectDetector
, yang secara opsional menentukan setelan detektor yang
ingin diubah dari setelan default.
Konfigurasi detektor objek untuk kasus penggunaan Anda dengan objek
FirebaseVisionObjectDetectorOptions
. Anda dapat mengubah setelan berikut:Setelan Detektor Objek Mode deteksi STREAM_MODE
(default) |SINGLE_IMAGE_MODE
Pada
STREAM_MODE
(default), detektor objek berjalan dengan latensi yang rendah, tetapi dapat membuat hasil yang tidak lengkap (seperti kotak pembatas atau label kategori yang belum ditetapkan) pada beberapa pemanggilan pertama detektor. Selain itu, padaSTREAM_MODE
, detektor menetapkan ID pelacakan ke objek, yang dapat Anda gunakan untuk melacak objek di rangkaian frame. Gunakan mode ini saat Anda ingin melacak objek, atau ketika latensi rendah lebih diutamakan, seperti saat memproses streaming video secara real time.Pada
SINGLE_IMAGE_MODE
, detektor objek menunggu hingga kotak pembatas objek yang terdeteksi dan label kategori (jika Anda mengaktifkan klasifikasi) tersedia sebelum menampilkan hasil. Akibatnya, latensi deteksi berpotensi lebih tinggi. Selain itu, padaSINGLE_IMAGE_MODE
, ID pelacakan tidak ditetapkan. Gunakan mode ini jika latensi tidak diutamakan dan Anda tidak ingin mendapatkan hasil parsial.Mendeteksi dan melacak beberapa objek false
(default) |true
Mendeteksi dan melacak hingga 5 objek atau hanya objek yang paling tampil beda (default).
Mengklasifikasikan objek false
(default) |true
Mengklasifikasikan objek yang terdeteksi ke dalam kategori mentah atau tidak. Jika diaktifkan, detektor objek akan mengklasifikasikan objek ke dalam kategori berikut: benda mode, makanan, peralatan rumah tangga, tempat, tanaman, dan hal yang tidak diketahui.
API deteksi dan pelacakan objek dioptimalkan untuk dua kasus penggunaan inti berikut ini:
- Deteksi langsung dan pelacakan objek paling tampil beda di jendela bidik kamera
- Deteksi banyak objek dalam gambar statis
Untuk mengonfigurasi API bagi kasus penggunaan ini:
Java
// Live detection and tracking FirebaseVisionObjectDetectorOptions options = new FirebaseVisionObjectDetectorOptions.Builder() .setDetectorMode(FirebaseVisionObjectDetectorOptions.STREAM_MODE) .enableClassification() // Optional .build(); // Multiple object detection in static images FirebaseVisionObjectDetectorOptions options = new FirebaseVisionObjectDetectorOptions.Builder() .setDetectorMode(FirebaseVisionObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableMultipleObjects() .enableClassification() // Optional .build();
Kotlin
// Live detection and tracking val options = FirebaseVisionObjectDetectorOptions.Builder() .setDetectorMode(FirebaseVisionObjectDetectorOptions.STREAM_MODE) .enableClassification() // Optional .build() // Multiple object detection in static images val options = FirebaseVisionObjectDetectorOptions.Builder() .setDetectorMode(FirebaseVisionObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableMultipleObjects() .enableClassification() // Optional .build()
Dapatkan instance
FirebaseVisionObjectDetector
:Java
FirebaseVisionObjectDetector objectDetector = FirebaseVision.getInstance().getOnDeviceObjectDetector(); // Or, to change the default settings: FirebaseVisionObjectDetector objectDetector = FirebaseVision.getInstance().getOnDeviceObjectDetector(options);
Kotlin
val objectDetector = FirebaseVision.getInstance().getOnDeviceObjectDetector() // Or, to change the default settings: val objectDetector = FirebaseVision.getInstance().getOnDeviceObjectDetector(options)
2. Menjalankan detektor objek
Untuk mendeteksi dan melacak objek, teruskan gambar ke metode processImage()
instance FirebaseVisionObjectDetector
.
Untuk setiap frame video atau gambar dalam sebuah rangkaian, lakukan hal berikut:
Buat objek
FirebaseVisionImage
dari gambar Anda.-
Untuk membuat objek
FirebaseVisionImage
dari objekmedia.Image
, seperti saat mengambil gambar dari kamera perangkat, teruskan objekmedia.Image
dan nilai rotasi gambar keFirebaseVisionImage.fromMediaImage()
.Jika Anda menggunakan library CameraX, class
OnImageCapturedListener
danImageAnalysis.Analyzer
menghitung nilai rotasi, sehingga Anda hanya perlu mengonversi rotasi ke salah satu konstantaROTATION_
ML Kit sebelum memanggilFirebaseVisionImage.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 tidak menggunakan library kamera yang memberikan nilai 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 }
Lalu, teruskan objek
media.Image
dan nilai rotasi keFirebaseVisionImage.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 keFirebaseVisionImage.fromFilePath()
. Hal ini berguna saat Anda menggunakan intentACTION_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
dariByteBuffer
atau array byte, pertama-tama hitung rotasi gambar seperti yang dijelaskan di atas untuk inputmedia.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 buffering 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 objekBitmap
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
harus berposisi tegak, tanpa perlu rotasi tambahan.
-
Teruskan gambar ke metode
processImage()
:Java
objectDetector.processImage(image) .addOnSuccessListener( new OnSuccessListener<List<FirebaseVisionObject>>() { @Override public void onSuccess(List<FirebaseVisionObject> detectedObjects) { // Task completed successfully // ... } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
Kotlin
objectDetector.processImage(image) .addOnSuccessListener { detectedObjects -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Jika panggilan ke
processImage()
berhasil, daftarFirebaseVisionObject
akan diteruskan ke pemroses peristiwa sukses.Setiap
FirebaseVisionObject
berisi properti berikut:Kotak pembatas Rect
yang menunjukkan posisi objek dalam gambar.ID Pelacakan Bilangan bulat yang mengidentifikasi objek lintas gambar. Null dalam SINGLE_IMAGE_MODE. Kategori Kategori mentah suatu objek. Jika detektor objek belum mengaktifkan klasifikasi, nilainya selalu FirebaseVisionObject.CATEGORY_UNKNOWN
.Keyakinan Tingkat keyakinan klasifikasi objek. Jika detektor objek belum mengaktifkan klasifikasi, atau objek diklasifikasikan sebagai tidak diketahui, nilainya adalah null
.Java
// The list of detected objects contains one item if multiple object detection wasn't enabled. for (FirebaseVisionObject obj : detectedObjects) { Integer id = obj.getTrackingId(); Rect bounds = obj.getBoundingBox(); // If classification was enabled: int category = obj.getClassificationCategory(); Float confidence = obj.getClassificationConfidence(); }
Kotlin
// The list of detected objects contains one item if multiple object detection wasn't enabled. for (obj in detectedObjects) { val id = obj.trackingId // A number that identifies the object across images val bounds = obj.boundingBox // The object's position in the image // If classification was enabled: val category = obj.classificationCategory val confidence = obj.classificationConfidence }
Meningkatkan kegunaan dan performa
Untuk mendapatkan pengalaman pengguna terbaik, ikuti pedoman ini dalam aplikasi:
- Keberhasilan deteksi objek bergantung pada kompleksitas visual objek. Objek yang memiliki sedikit fitur visual mungkin perlu menempati bagian yang lebih besar pada gambar agar terdeteksi. Anda sebaiknya memberikan panduan kepada pengguna tentang pengambilan input yang tepat bagi jenis objek yang ingin Anda deteksi.
- Saat menggunakan klasifikasi, jika ingin mendeteksi objek yang tidak secara jelas termasuk dalam kategori yang didukung, terapkan penanganan khusus untuk objek yang tidak diketahui.
Selain itu, lihat [aplikasi contoh Desain Material ML Kit][showcase-link]{: .external} dan koleksi Pola Desain Material untuk fitur yang didukung machine learning.
Saat menggunakan mode streaming dalam aplikasi real-time, ikuti pedoman ini untuk mencapai kecepatan frame terbaik:
Jangan gunakan deteksi banyak objek dalam mode streaming, karena sebagian besar perangkat tidak akan dapat menghasilkan frekuensi frame yang memadai.
Nonaktifkan klasifikasi jika tidak diperlukan.
- Batasi panggilan ke detektor. Jika frame video baru tersedia saat detektor sedang berjalan, hapus frame tersebut.
- Jika Anda menggunakan output detektor untuk menempatkan grafis pada gambar input, pertama-tama dapatkan hasilnya 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.
-
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
.