Resim etiketlemeyi özel modellerle entegre etmenin iki yolu vardır: ardışık düzeni uygulamanızın bir parçası olarak paketleyerek veya Google Play Hizmetleri'ne bağlı, paketlenmemiş bir ardışık düzen kullanarak. Ayrılmış işlem hattını seçerseniz uygulamanız daha küçük olur. Ayrıntılar için aşağıdaki tabloya bakın.
| Gruplandırılanlar | Paketlenmemiş | |
|---|---|---|
| Kitaplık adı | com.google.mlkit:image-labeling-custom | com.google.android.gms:play-services-mlkit-image-labeling-custom |
Uygulama | Pipeline, derleme sırasında uygulamanıza statik olarak bağlanır. | Pipeline, Google Play Hizmetleri kullanılarak dinamik olarak indirilir. |
| Uygulama boyutu | Boyut yaklaşık 3,8 MB artar. | Boyut yaklaşık 200 KB artar. |
| İlk kullanıma hazırlama süresi | Pipeline hemen kullanılabilir. | İlk kullanımdan önce ardışık düzenin indirilmesini beklemeniz gerekebilir. |
| API yaşam döngüsü aşaması | Genel Kullanım | Beta |
Özel bir modeli entegre etmenin iki yolu vardır: Modeli uygulamanızın öğe klasörüne yerleştirerek paketleyin veya Firebase'den dinamik olarak indirin. Aşağıdaki tabloda bu iki seçenek karşılaştırılmaktadır.
| Paketlenmiş Model | Barındırılan Model |
|---|---|
| Model, uygulamanızın APK'sının bir parçasıdır ve bu da uygulamanızın boyutunu artırır. | Model, APK'nızın bir parçası değildir. Cloud Storage'a yüklenerek barındırılır. Firebase için Cloud Storage'ı kullanmanızı öneririz. |
| Model, Android cihaz çevrimdışı olsa bile hemen kullanılabilir. | Uygulamanız, modeli isteğe bağlı olarak indirmek için kod içermelidir. |
| Firebase projesi gerekmez | Firebase projesi gerektirir (Cloud Storage for Firebase kullanılıyorsa). |
| Modeli güncellemek için uygulamanızı yeniden yayınlamanız gerekir. | Uygulamanızı yeniden yayınlamadan model güncellemelerini gönderme |
| Yerleşik A/B testi yok | Firebase Remote Config ile A/B testi |
Deneyin
- Paketlenmiş modelin kullanımına ilişkin örnek için Vision hızlı başlangıç uygulamasına, barındırılan modelin kullanımına ilişkin örnek için AutoML hızlı başlangıç uygulamasına bakın.
Başlamadan önce
Proje düzeyindeki
build.gradle.ktsdosyanızda, Google'ın Maven deposunu hembuildscripthem deallprojectsbölümüne eklediğinizden emin olun.ML Kit Android kitaplıklarının bağımlılıklarını modülünüzün uygulama düzeyindeki Gradle dosyasına (genellikle
app/build.gradle.kts) ekleyin. İhtiyaçlarınıza göre aşağıdaki bağımlılıklardan birini seçin:İşlem hattını uygulamanızla birlikte paketlemek için:
dependencies { // ... // Use this dependency to bundle the pipeline with your app implementation("com.google.mlkit:image-labeling-custom:17.0.3") }Google Play Hizmetleri'nde işlem hattını kullanmak için:
dependencies { // ... // Use this dependency to use the dynamically downloaded pipeline in Google Play services implementation("com.google.android.gms:play-services-mlkit-image-labeling-custom:16.0.0-beta5") }Google Play Hizmetleri'nde ardışık düzeni kullanmayı tercih ederseniz, uygulamanız Play Store'dan yüklendikten sonra ardışık düzeni cihaza otomatik olarak indirecek şekilde yapılandırabilirsiniz. Bunu yapmak için uygulamanızın
AndroidManifest.xmldosyasına aşağıdaki beyanı ekleyin:<application ...> ... <meta-data android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="custom_ica" /> <!-- To use multiple downloads: android:value="custom_ica,download2,download3" --> </application>Ayrıca, Google Play Hizmetleri ModuleInstallClient API aracılığıyla ardışık düzenin kullanılabilirliğini açıkça kontrol edebilir ve indirme isteğinde bulunabilirsiniz.
Yükleme zamanı ardışık düzen indirmelerini etkinleştirmezseniz veya açıkça indirme isteğinde bulunmazsanız ardışık düzen, etiketleyiciyi ilk kez çalıştırdığınızda indirilir. İndirme tamamlanmadan önce yaptığınız istekler sonuç vermez.
Cloud Storage for Firebase kullanarak bir model indirmek istiyorsanız, henüz yapmadıysanız Firebase'i Android projenize eklediğinizden emin olun. Bu işlem, modeli paketlediğinizde gerekli değildir.
1. Modeli yükleme
Modeli yerel olarak paketlenmiş bir kaynaktan veya uzaktan barındırılan bir kaynaktan yükleyebilirsiniz.
Yerel model kaynağı yapılandırma
Modeli uygulamanızla paketlemek için:
Model dosyasını (genellikle
.tfliteveya.liteile biter) uygulamanızınassets/klasörüne kopyalayın. (Önceapp/klasörünü sağ tıklayıp Yeni > Klasör > Öğeler Klasörü'nü tıklayarak klasörü oluşturmanız gerekebilir.)Model dosyasının yolunu belirterek
LocalModelnesnesi oluşturun:Kotlin
val localModel = LocalModel.Builder() .setAssetFilePath("model.tflite") // or .setAbsoluteFilePath(absolute path to model file) // or .setUri(URI to model file) .build()
Java
LocalModel localModel = new LocalModel.Builder() .setAssetFilePath("model.tflite") // or .setAbsoluteFilePath(absolute path to model file) // or .setUri(URI to model file) .build();
Uzaktan barındırılan model kaynağını yapılandırma
Uzaktan barındırılan modeli kullanmak için model dosyasını kendi uygulama mantığınızı kullanarak cihazın yerel depolama alanına indirmeniz ve ardından yerel model olarak yüklemeniz gerekir. Bir modeli barındırmak için Firebase için Cloud Storage'ı kullanmanızı öneririz. Uygulama ayrıntıları için Firebase ML'den Cloud Storage'a taşıma kılavuzuna bakın.
Resim etiketleyiciyi yapılandırma
Model kaynaklarınızı yapılandırdıktan sonra bunlardan birinden ImageLabeler nesnesi oluşturun.
Aşağıdaki seçenekler kullanılabilir:
| Seçenekler | |
|---|---|
confidenceThreshold
|
Algılanan etiketlerin minimum güven puanı. Ayarlanmazsa modelin meta verileri tarafından belirtilen sınıflandırıcı eşiği kullanılır. Modelde herhangi bir meta veri yoksa veya meta verilerde bir sınıflandırıcı eşiği belirtilmemişse varsayılan eşik olarak 0, 0 kullanılır. |
maxResultCount
|
Döndürülecek maksimum etiket sayısı. Ayarlanmazsa varsayılan 10 değeri kullanılır. |
Yalnızca yerel olarak paketlenmiş bir modeliniz varsa LocalModel nesnenizden bir etiketleyici oluşturmanız yeterlidir:
Kotlin
val customImageLabelerOptions = CustomImageLabelerOptions.Builder(localModel) .setConfidenceThreshold(0.5f) .setMaxResultCount(5) .build() val labeler = ImageLabeling.getClient(customImageLabelerOptions)
Java
CustomImageLabelerOptions customImageLabelerOptions = new CustomImageLabelerOptions.Builder(localModel) .setConfidenceThreshold(0.5f) .setMaxResultCount(5) .build(); ImageLabeler labeler = ImageLabeling.getClient(customImageLabelerOptions);
Uzaktan barındırılan bir modeliniz varsa çalıştırmadan önce indirildiğinden emin olmanız gerekir.
Bu işlemi yalnızca etiketleyiciyi çalıştırmadan önce onaylamanız gerekse de hem uzaktan barındırılan hem de yerel olarak paketlenmiş bir modeliniz varsa görüntü etiketleyiciyi oluştururken bu kontrolü yapmanız mantıklı olabilir: İndirildiyse uzaktan modelden, aksi takdirde yerel modelden bir etiketleyici oluşturun.
Kotlin
val modelFile = File(context.cacheDir, "my_downloaded_model.tflite") val model = if (modelFile.exists()) { // Use the downloaded model if available LocalModel.Builder().setAbsoluteFilePath(modelFile.absolutePath).build() } else { // Fall back to the bundled model LocalModel.Builder().setAssetFilePath("model.tflite").build() } val options = CustomImageLabelerOptions.Builder(model) .setConfidenceThreshold(0.5f) .setMaxResultCount(5) .build() val labeler = ImageLabeling.getClient(options)
Java
File modelFile = new File(context.getCacheDir(), "my_downloaded_model.tflite"); LocalModel model; if (modelFile.exists()) { // Use the downloaded model if available model = new LocalModel.Builder().setAbsoluteFilePath(modelFile.getAbsolutePath()).build(); } else { // Fall back to the bundled model model = new LocalModel.Builder().setAssetFilePath("model.tflite").build(); } CustomImageLabelerOptions options = new CustomImageLabelerOptions.Builder(model) .setConfidenceThreshold(0.5f) .setMaxResultCount(5) .build(); ImageLabeler labeler = ImageLabeling.getClient(options);
Yalnızca uzaktan barındırılan bir modeliniz varsa modelin indirildiğini onaylayana kadar modelle ilgili işlevleri devre dışı bırakmanız (ör. kullanıcı arayüzünüzün bir bölümünü grileştirmeniz veya gizlemeniz) gerekir.
Kotlin
val localFile = File(context.cacheDir, "my_remote_model.tflite") if (localFile.exists()) { initializeLabeler(localFile) } else { showLoadingUI() val storage = Firebase.storage val modelRef = storage.getReferenceFromUrl("gs://YOUR_BUCKET/path/to/model.tflite") modelRef.getFile(localFile) .addOnSuccessListener { hideLoadingUI() initializeLabeler(localFile) } .addOnFailureListener { showErrorUI() } } private fun initializeLabeler(modelFile: File) { val localModel = LocalModel.Builder().setAbsoluteFilePath(modelFile.absolutePath).build() val options = CustomImageLabelerOptions.Builder(localModel).build() val labeler = ImageLabeling.getClient(options) enableMLFeatures(labeler) }
Java
File localFile = new File(context.getCacheDir(), "my_remote_model.tflite"); if (localFile.exists()) { initializeLabeler(localFile); } else { showLoadingUI(); FirebaseStorage storage = FirebaseStorage.getInstance(); StorageReference modelRef = storage.getReferenceFromUrl("gs://YOUR_BUCKET/path/to/model.tflite"); modelRef.getFile(localFile) .addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() { @Override public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) { hideLoadingUI(); initializeLabeler(localFile); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception exception) { showErrorUI(); } }); } private void initializeLabeler(File modelFile) { LocalModel localModel = new LocalModel.Builder().setAbsoluteFilePath(modelFile.getAbsolutePath()).build(); CustomImageLabelerOptions options = new CustomImageLabelerOptions.Builder(localModel).build(); ImageLabeler labeler = ImageLabeling.getClient(options); enableMLFeatures(labeler); }
2. Giriş görüntüsünü hazırlama
Ardından, etiketlemek istediğiniz her resim için resminizden birInputImage
nesne oluşturun. Görüntü etiketleyici, mümkün olduğunda önerilen Bitmap veya camera2 API'yi kullanıyorsanız YUV_420_888 media.Image kullanıldığında en hızlı şekilde çalışır.
Farklı kaynaklardan InputImage nesneler oluşturabilirsiniz. Her biri aşağıda açıklanmıştır.
media.Image kullanma
media.Image nesnesinden InputImage nesnesi oluşturmak için (ör. bir cihazın kamerasından resim yakaladığınızda) media.Image nesnesini ve resmin dönüşünü InputImage.fromMediaImage()'e iletin.
CameraX kitaplığını kullanıyorsanız OnImageCapturedListener ve
ImageAnalysis.Analyzer sınıfları, sizin için döndürme değerini hesaplar.
Kotlin
private class YourImageAnalyzer : ImageAnalysis.Analyzer { override fun analyze(imageProxy: ImageProxy) { val mediaImage = imageProxy.image if (mediaImage != null) { val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) // Pass image to an ML Kit Vision API // ... } } }
Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { @Override public void analyze(ImageProxy imageProxy) { Image mediaImage = imageProxy.getImage(); if (mediaImage != null) { InputImage image = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees()); // Pass image to an ML Kit Vision API // ... } } }
Resmin dönüş derecesini veren bir kamera kitaplığı kullanmıyorsanız bunu cihazın dönüş derecesinden ve cihazdaki kamera sensörünün yönünden hesaplayabilirsiniz:
Kotlin
private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 0) ORIENTATIONS.append(Surface.ROTATION_90, 90) ORIENTATIONS.append(Surface.ROTATION_180, 180) ORIENTATIONS.append(Surface.ROTATION_270, 270) } /** * 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, isFrontFacing: Boolean): 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) // Get the device's sensor orientation. val cameraManager = activity.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360 } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360 } return rotationCompensation }
Java
private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 0); ORIENTATIONS.append(Surface.ROTATION_90, 90); ORIENTATIONS.append(Surface.ROTATION_180, 180); ORIENTATIONS.append(Surface.ROTATION_270, 270); } /** * 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, boolean isFrontFacing) 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); // Get the device's sensor orientation. CameraManager cameraManager = (CameraManager) activity.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360; } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360; } return rotationCompensation; }
Ardından, media.Image nesnesini ve dönüş derecesi değerini InputImage.fromMediaImage()'e iletin:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Dosya URI'si kullanma
Dosya URI'sinden InputImage nesnesi oluşturmak için uygulama bağlamını ve dosya URI'sini InputImage.fromFilePath()'ye iletin. Bu, kullanıcıdan galeri uygulamasından bir resim seçmesini istemek için ACTION_GET_CONTENT amacını kullandığınızda yararlıdır.
Kotlin
val image: InputImage try { image = InputImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
Java
InputImage image; try { image = InputImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
ByteBuffer veya ByteArray kullanma
ByteBuffer veya ByteArray öğesinden InputImage
nesnesi oluşturmak için öncelikle media.Image girişi için daha önce açıklandığı gibi görüntü döndürme derecesini hesaplayın.
Ardından, arabellek veya diziyle birlikte resmin yüksekliği, genişliği, renk kodlama biçimi ve döndürme derecesiyle InputImage nesnesini oluşturun:
Kotlin
val image = InputImage.fromByteBuffer( byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ) // Or: val image = InputImage.fromByteArray( byteArray, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 )
Java
InputImage image = InputImage.fromByteBuffer(byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ); // Or: InputImage image = InputImage.fromByteArray( byteArray, /* image width */480, /* image height */360, rotation, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 );
Bitmap kullanma
Bitmap nesnesinden InputImage
nesnesi oluşturmak için aşağıdaki bildirimi yapın:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
Resim, döndürme dereceleriyle birlikte bir Bitmap nesnesiyle gösterilir.
3. Resim etiketleyicisini çalıştırma
Resimdeki nesneleri etiketlemek için image nesnesini ImageLabeler'nin process() yöntemine iletin.
Kotlin
labeler.process(image) .addOnSuccessListener { labels -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Java
labeler.process(image) .addOnSuccessListener(new OnSuccessListener<List<ImageLabel>>() { @Override public void onSuccess(List<ImageLabel> labels) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
4. Etiketlenmiş öğeler hakkında bilgi alma
Resim etiketleme işlemi başarılı olursaImageLabel
nesnelerin listesi başarı dinleyicisine iletilir. Her ImageLabel nesnesi, resimde etiketlenen bir şeyi temsil eder. Her etiketin metin açıklamasını (LiteRT model dosyasının meta verilerinde varsa), güven puanını ve dizinini alabilirsiniz. Örneğin:
Kotlin
for (label in labels) { val text = label.text val confidence = label.confidence val index = label.index }
Java
for (ImageLabel label : labels) { String text = label.getText(); float confidence = label.getConfidence(); int index = label.getIndex(); }
Anlık performansı artırmaya yönelik ipuçları
Resimleri gerçek zamanlı bir uygulamada etiketlemek istiyorsanız en iyi kare hızlarını elde etmek için aşağıdaki yönergeleri uygulayın:
Cameraveyacamera2API'yi kullanıyorsanız görüntü etiketleyiciye yapılan çağrıları kısıtlayın. Görüntü etiketleyici çalışırken yeni bir video karesi kullanılabilir hale gelirse kareyi bırakın. Bir örnek için hızlı başlangıç örnek uygulamasındakiVisionProcessorBasesınıfına bakın.CameraXAPI'sini kullanıyorsanız geri basınç stratejisinin varsayılan değerine ayarlandığından emin olunImageAnalysis.STRATEGY_KEEP_ONLY_LATEST. Bu, analiz için tek seferde yalnızca bir resmin gönderilmesini sağlar. Analizör meşgulken daha fazla resim üretilirse bunlar otomatik olarak bırakılır ve teslimat için sıraya alınmaz. Analiz edilen görüntü ImageProxy.close() çağrılarak kapatıldığında, en son görüntü teslim edilir.- Giriş resmine grafik yerleştirmek için resim etiketleyicinin çıkışını kullanıyorsanız önce ML Kit'ten sonucu alın, ardından resmi tek adımda oluşturup yerleştirin. Bu, her giriş karesi için yalnızca bir kez görüntüleme yüzeyinde oluşturulur. Örnek için hızlı başlangıç örnek uygulamasındaki
CameraSourcePreviewveGraphicOverlaysınıflarına bakın. - Camera2 API'yi kullanıyorsanız görüntüleri
ImageFormat.YUV_420_888biçiminde çekin. Eski Camera API'yi kullanıyorsanız görüntüleriImageFormat.NV21biçiminde çekin.