Setelah melatih model sendiri menggunakan AutoML Vision Edge, Anda dapat menggunakannya di aplikasi untuk memberi label pada gambar.
Sebelum memulai
- Tambahkan Firebase ke project Android jika Anda 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-automl:18.0.5' }
1. Memuat model
ML Kit menjalankan model yang dihasilkan AutoML di perangkat. Namun, Anda dapat mengonfigurasi ML Kit untuk memuat model Anda dari jarak jauh dari Firebase, dari penyimpanan lokal, atau keduanya.
Dengan menghosting model di Firebase, Anda dapat mengupdate model tanpa merilis versi baru aplikasi. Anda juga dapat menggunakan Remote Config dan Pengujian A/B untuk menerapkan berbagai model secara dinamis ke sekelompok pengguna yang berbeda.
Jika memilih untuk hanya menyediakan model dengan menghostingnya di Firebase, dan tidak memaketkannya dengan aplikasi, Anda dapat mengurangi ukuran download awal aplikasi. Namun, ingat bahwa jika model tidak dipaketkan dengan aplikasi, fungsi yang terkait dengan model tidak akan tersedia sebelum aplikasi mendownload model untuk pertama kalinya.
Dengan memaketkan model dengan aplikasi, Anda dapat memastikan bahwa fitur ML pada aplikasi tetap berfungsi jika model yang dihosting Firebase tidak tersedia.
Mengonfigurasi sumber model yang dihosting Firebase
Untuk menggunakan model yang dihosting dari jarak jauh, buat objek FirebaseAutoMLRemoteModel
dengan menyebutkan nama yang ditetapkan pada model ketika Anda memublikasikannya:
Java
// Specify the name you assigned in the Firebase console.
FirebaseAutoMLRemoteModel remoteModel =
new FirebaseAutoMLRemoteModel.Builder("your_remote_model").build();
Kotlin+KTX
// Specify the name you assigned in the Firebase console.
val remoteModel = FirebaseAutoMLRemoteModel.Builder("your_remote_model").build()
Kemudian, mulai tugas download model dengan menentukan kondisi yang Anda inginkan untuk mengizinkan download. Jika model tidak ada di perangkat, atau jika versi model yang lebih baru tersedia, tugas akan mendownload model dari Firebase secara asinkron:
Java
FirebaseModelDownloadConditions conditions = new FirebaseModelDownloadConditions.Builder()
.requireWifi()
.build();
FirebaseModelManager.getInstance().download(remoteModel, conditions)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
// Success.
}
});
Kotlin+KTX
val conditions = FirebaseModelDownloadConditions.Builder()
.requireWifi()
.build()
FirebaseModelManager.getInstance().download(remoteModel, conditions)
.addOnCompleteListener {
// Success.
}
Banyak aplikasi memulai tugas download dalam kode inisialisasinya, tetapi Anda dapat melakukannya kapan saja sebelum menggunakan model.
Mengonfigurasi sumber model lokal
Untuk memaketkan model dengan aplikasi:
- Ekstrak model dan metadata-nya dari arsip zip yang Anda download dari Firebase console. Sebaiknya gunakan file hasil download apa adanya, tanpa melakukan perubahan (termasuk nama file).
-
Sertakan model Anda dan file metadata-nya dalam paket aplikasi:
- Jika belum memiliki folder aset dalam project, Anda dapat membuatnya dengan
mengklik kanan folder
app/
, lalu mengklik Baru > Folder > Folder Aset. - Buat subfolder pada bagian folder aset untuk memuat file model.
- Salin file
model.tflite
,dict.txt
, danmanifest.json
ke subfolder (ketiga file harus berada di folder yang sama).
- Jika belum memiliki folder aset dalam project, Anda dapat membuatnya dengan
mengklik kanan folder
- Tambahkan hal berikut ke file
build.gradle
aplikasi untuk memastikan Gradle tidak mengompresi file model saat membangun aplikasi:android { // ... aaptOptions { noCompress "tflite" } }
File model akan disertakan dalam paket aplikasi dan tersedia untuk ML Kit sebagai aset mentah. - Buat objek
FirebaseAutoMLLocalModel
dengan menentukan jalur ke file manifes model:Java
FirebaseAutoMLLocalModel localModel = new FirebaseAutoMLLocalModel.Builder() .setAssetFilePath("manifest.json") .build();
Kotlin+KTX
val localModel = FirebaseAutoMLLocalModel.Builder() .setAssetFilePath("manifest.json") .build()
Membuat pemberi label gambar dari model
Setelah mengonfigurasi sumber model, buat objek FirebaseVisionImageLabeler
dari salah satu sumber tersebut.
Jika hanya memiliki model yang dipaketkan secara lokal, cukup buat pemberi label dari objek FirebaseAutoMLLocalModel
dan konfigurasi nilai minimum skor keyakinan yang ingin Anda wajibkan (lihat Mengevaluasi model):
Java
FirebaseVisionImageLabeler labeler;
try {
FirebaseVisionOnDeviceAutoMLImageLabelerOptions options =
new FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(localModel)
.setConfidenceThreshold(0.0f) // Evaluate your model in the Firebase console
// to determine an appropriate value.
.build();
labeler = FirebaseVision.getInstance().getOnDeviceAutoMLImageLabeler(options);
} catch (FirebaseMLException e) {
// ...
}
Kotlin+KTX
val options = FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(localModel)
.setConfidenceThreshold(0) // Evaluate your model in the Firebase console
// to determine an appropriate value.
.build()
val labeler = FirebaseVision.getInstance().getOnDeviceAutoMLImageLabeler(options)
Jika Anda memiliki model yang dihosting dari jarak jauh, Anda harus memeriksa apakah model tersebut sudah
didownload sebelum menjalankannya. Anda dapat memeriksa status tugas
download model menggunakan metode isModelDownloaded()
pengelola model.
Meskipun Anda hanya perlu memastikan hal ini sebelum menjalankan pemberi label, jika Anda memiliki model yang dihosting dari jarak jauh dan model yang dipaketkan secara lokal, mungkin ada baiknya untuk melakukan pemeriksaan ini saat membuat instance pemberi label gambar: buat pemberi label dari model jarak jauh jika model tersebut telah didownload, dan dari model lokal jika belum didownload.
Java
FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
.addOnSuccessListener(new OnSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean isDownloaded) {
FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder optionsBuilder;
if (isDownloaded) {
optionsBuilder = new FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(remoteModel);
} else {
optionsBuilder = new FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(localModel);
}
FirebaseVisionOnDeviceAutoMLImageLabelerOptions options = optionsBuilder
.setConfidenceThreshold(0.0f) // Evaluate your model in the Firebase console
// to determine an appropriate threshold.
.build();
FirebaseVisionImageLabeler labeler;
try {
labeler = FirebaseVision.getInstance().getOnDeviceAutoMLImageLabeler(options);
} catch (FirebaseMLException e) {
// Error.
}
}
});
Kotlin+KTX
FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
.addOnSuccessListener { isDownloaded ->
val optionsBuilder =
if (isDownloaded) {
FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(remoteModel)
} else {
FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(localModel)
}
// Evaluate your model in the Firebase console to determine an appropriate threshold.
val options = optionsBuilder.setConfidenceThreshold(0.0f).build()
val labeler = FirebaseVision.getInstance().getOnDeviceAutoMLImageLabeler(options)
}
Jika Anda hanya memiliki model yang dihosting dari jarak jauh, Anda harus menonaktifkan fungsionalitas terkait model—misalnya, menyamarkan atau menyembunyikan sebagian UI—hingga Anda mengonfirmasi model tersebut telah didownload. Anda dapat melakukannya dengan menambahkan pemroses
ke metode download()
pengelola model:
Java
FirebaseModelManager.getInstance().download(remoteModel, conditions)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void v) {
// Download complete. Depending on your app, you could enable
// the ML feature, or switch from the local model to the remote
// model, etc.
}
});
Kotlin+KTX
FirebaseModelManager.getInstance().download(remoteModel, conditions)
.addOnCompleteListener {
// Download complete. Depending on your app, you could enable the ML
// feature, or switch from the local model to the remote model, etc.
}
2. Menyiapkan gambar input
Kemudian, untuk setiap gambar yang ingin diberi label, buat objek FirebaseVisionImage
menggunakan salah satu opsi yang dijelaskan di bagian ini dan teruskan ke instance
FirebaseVisionImageLabeler
(dijelaskan di bagian berikutnya).
Anda dapat membuat objek FirebaseVisionImage
dari objek media.Image
,
file di perangkat, array byte, atau objek Bitmap
:
-
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+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 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+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 }
Lalu, teruskan objek
media.Image
dan nilai rotasi keFirebaseVisionImage.fromMediaImage()
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin+KTX
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+KTX
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+KTX
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+KTX
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+KTX
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
harus berposisi tegak, tanpa perlu rotasi tambahan.
3. Menjalankan pemberi label gambar
Untuk memberi label pada objek dalam gambar, teruskan objek FirebaseVisionImage
ke metode processImage()
FirebaseVisionImageLabeler
.
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+KTX
labeler.processImage(image)
.addOnSuccessListener { labels ->
// Task completed successfully
// ...
}
.addOnFailureListener { e ->
// Task failed with an exception
// ...
}
Jika pemberian label pada gambar berhasil, array objek FirebaseVisionImageLabel
akan diteruskan ke pemroses peristiwa sukses. Dari setiap objek, Anda bisa mendapatkan
informasi tentang fitur yang dikenali dalam gambar.
Contoh:
Java
for (FirebaseVisionImageLabel label: labels) {
String text = label.getText();
float confidence = label.getConfidence();
}
Kotlin+KTX
for (label in labels) {
val text = label.text
val confidence = label.confidence
}
Tips untuk meningkatkan performa real-time
- 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
.