Android'de resimleri özel bir modelle etiketleme

ML Kit'i kullanarak bir görüntüdeki varlıkları tanıyabilir ve etiketleyebilirsiniz. Bu API, çok çeşitli özel görüntü sınıflandırma modellerini destekler. Lütfen Makine Öğrenimi Kiti ile özel modeller başlıklı makaleyi inceleyin model uyumluluk gereksinimleri, önceden eğitilmiş modellerin ve kendi modellerinizi nasıl eğiteceğinizi öğreneceksiniz.

Görüntü etiketlemeyi özel modellerle entegre etmenin iki yolu vardır: gruplandırma veya ayrı bir ardışık düzen kullanarak Google Play Hizmetleri'nde kullanılabilir. Gruplandırılmamış ardışık düzeni seçerseniz uygulamanız daha küçüktür. Ayrıntılar için aşağıdaki tabloya bakın.

GruplandırılanlarGruplandırılmamış
Kitaplık adıcom.google.mlkit:image-labeling-customcom.google.android.gms:play-services-mlkit-image-labeling-custom
UygulamaArdışık düzen, derleme sırasında statik olarak uygulamanıza bağlıdır.Ardışık düzen, Google Play Hizmetleri aracılığıyla dinamik olarak indirilir.
Uygulama boyutuYaklaşık 3,8 MB boyut artışı.Yaklaşık 200 KB boyut artışı.
Başlatma süresiArdışık düzen 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 (GK)Beta

Özel bir modeli entegre etmenin iki yolu vardır: modeli şu ölçüte göre gruplandırma: öğe klasörünün içine yerleştirerek veya dinamik olarak indirerek Firebase'den geliyor. Aşağıdaki tabloda bu iki seçenek karşılaştırılmıştır.

Gruplandırılmış Model Barındırılan Model
Model, uygulamanızın APK'sının bir parçasıdır ve bu modelin boyutunu artırır. Model, APK'nızın parçası değil. Şu konuma yükleyerek barındırılır: Firebase Makine Öğrenimi.
Model, Android cihaz çevrimdışı olsa bile hemen kullanılabilir Model istek üzerine indirilir
Firebase projesine gerek yoktur Firebase projesi gerekir
Modeli güncellemek için uygulamanızı yeniden yayınlamanız gerekiyor Uygulamanızı yeniden yayınlamadan model güncellemelerini aktarma
Yerleşik A/B testi yok Firebase Remote Config ile kolay A/B testi

Deneyin

Başlamadan önce

  1. Proje düzeyindeki build.gradle dosyanıza Google'ın Maven deposu hem buildscript hem de allprojects bölüm.

  2. ML Kit Android kitaplıklarının bağımlılıklarını modülünüze uygulama düzeyinde gradle dosyasıdır. Bu dosya genellikle app/build.gradle boyutundadır. Aşağıdakilerden birini seçin: bağımlılıkları belirleyebilirsiniz:

    Ardışık düzeni uygulamanızla 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 ardışık düzeni 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'
    }
    
  3. Google Play Hizmetleri'nde ardışık düzeni kullanmayı seçerseniz ardışık düzeni cihaza indirdikten sonra, uygulamanızı Uygulamanızın Play Store'dan yüklendiğinden emin olun. Bunu yapmak için aşağıdakini ekleyin: beyanını uygulamanızın AndroidManifest.xml dosyasına 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 ardışık düzen kullanılabilirliğini açıkça kontrol edebilir ve Google Play Hizmetleri ModuleLoadClient API'si

    Yükleme sırasında ardışık düzen indirmelerini etkinleştirmez veya açık indirme isteğinde bulunmazsanız Ardışık düzen, etiketleyiciyi ilk çalıştırdığınızda indirilir. Yaptığınız istekler hiçbir sonuç döndürmez.

  4. BirlinkFirebase modeliniz:

    Firebase'den dinamik olarak bir model indirmek için linkFirebase bağımlılık:

    dependencies {
      // ...
      // Image labeling feature with model downloaded from Firebase
      implementation 'com.google.mlkit:image-labeling-custom:17.0.3'
      // Or use the dynamically downloaded pipeline in Google Play Services
      // implementation 'com.google.android.gms:play-services-mlkit-image-labeling-custom:16.0.0-beta5'
      implementation 'com.google.mlkit:linkfirebase:17.0.0'
    }
    
  5. Bir model indirmek istiyorsanız şunu yaptığınızdan emin olun: Firebase'i Android projenize ekleyin, (onaylamadıysanız). Modeli paket haline getirirken bu işlem gerekli değildir.

1. Modeli yükleme

Yerel model kaynağını yapılandırma

Modeli uygulamanızla paket haline getirmek için:

  1. Model dosyasını (genellikle .tflite veya .lite ile biter) uygulamanızın assets/ klasör. (İlk olarak klasörü app/ klasörünü sağ tıklayıp Yeni > Klasör > Öğeler Klasörü'nü tıklayın.)

  2. Ardından, uygulamanızın build.gradle dosyasına şunu ekleyin: Gradle, uygulamayı oluştururken model dosyasını sıkıştırmaz:

    android {
        // ...
        aaptOptions {
            noCompress "tflite"
            // or noCompress "lite"
        }
    }
    

    Model dosyası uygulama paketine dahil edilir ve ML Kit tarafından kullanılabilir işlenmemiş bir öğe olarak görebiliriz.

  3. Model dosyasının yolunu belirterek LocalModel nesnesi oluşturun:

    Kotlin

    val localModel = LocalModel.Builder()
            .setAssetFilePath("model.tflite")
            // or .setAbsoluteFilePath(absolute file path to model file)
            // or .setUri(URI to model file)
            .build()

    Java

    LocalModel localModel =
        new LocalModel.Builder()
            .setAssetFilePath("model.tflite")
            // or .setAbsoluteFilePath(absolute file path to model file)
            // or .setUri(URI to model file)
            .build();

Firebase tarafından barındırılan bir model kaynağını yapılandırma

Uzaktan barındırılan modeli kullanmak için şu şekilde bir RemoteModel nesnesi oluşturun: FirebaseModelSource, dosyayı açarken modeli atadığınızda yayınladı:

Kotlin

// Specify the name you assigned in the Firebase console.
val remoteModel =
    CustomRemoteModel
        .Builder(FirebaseModelSource.Builder("your_model_name").build())
        .build()

Java

// Specify the name you assigned in the Firebase console.
CustomRemoteModel remoteModel =
    new CustomRemoteModel
        .Builder(new FirebaseModelSource.Builder("your_model_name").build())
        .build();

Ardından, model indirme görevini başlatmak için model indirme işleminde indirmeye izin vermek istiyorsunuz. Model cihazda yoksa veya sürümü kullanılabiliyorsa görev, yeni bir sürümün yüklü olduğu modeliniz:

Kotlin

val downloadConditions = DownloadConditions.Builder()
    .requireWifi()
    .build()
RemoteModelManager.getInstance().download(remoteModel, downloadConditions)
    .addOnSuccessListener {
        // Success.
    }

Java

DownloadConditions downloadConditions = new DownloadConditions.Builder()
        .requireWifi()
        .build();
RemoteModelManager.getInstance().download(remoteModel, downloadConditions)
        .addOnSuccessListener(new OnSuccessListener() {
            @Override
            public void onSuccess(@NonNull Task task) {
                // Success.
            }
        });

Birçok uygulama, indirme görevini başlatma kodunda başlatır, ancak bunu, modeli kullanmaya başlamadan önce istediğiniz zaman yapabilirsiniz.

Görüntü etiketleyiciyi yapılandırma

Model kaynaklarınızı yapılandırdıktan sonra şuradan bir ImageLabeler nesnesi oluşturun: bunlardan biri olabilir.

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. Model herhangi bir meta veri içermiyorsa veya meta veriler bir sınıflandırıcı eşiği belirtirse 0, 0 varsayılan eşiği kullanılır.

maxResultCount

Döndürülecek maksimum etiket sayısı. Politika ayarlanmazsa 10 kullanılır.

Yalnızca yerel olarak paketlenmiş bir modeliniz varsa LocalModel nesne:

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 bu modelin indiremezsiniz. Model indirme işleminin durumunu kontrol edebilirsiniz. model yöneticisinin isModelDownloaded() yöntemini kullanarak görevi tamamlayın.

Etiketleyiciyi çalıştırmadan önce bunu onaylamanız yeterli olsa da hem uzaktan barındırılan hem de yerel olarak paketlenen örneklendirilirken şu kontrolü gerçekleştirmek mantıklıdır: etiketleyici indirilmişse uzak modelden ve yerel modelden modelini kullanmanız gerekir.

Kotlin

RemoteModelManager.getInstance().isModelDownloaded(remoteModel)
    .addOnSuccessListener { isDownloaded ->
    val optionsBuilder =
        if (isDownloaded) {
            CustomImageLabelerOptions.Builder(remoteModel)
        } else {
            CustomImageLabelerOptions.Builder(localModel)
        }
    val options = optionsBuilder
                  .setConfidenceThreshold(0.5f)
                  .setMaxResultCount(5)
                  .build()
    val labeler = ImageLabeling.getClient(options)
}

Java

RemoteModelManager.getInstance().isModelDownloaded(remoteModel)
        .addOnSuccessListener(new OnSuccessListener() {
            @Override
            public void onSuccess(Boolean isDownloaded) {
                CustomImageLabelerOptions.Builder optionsBuilder;
                if (isDownloaded) {
                    optionsBuilder = new CustomImageLabelerOptions.Builder(remoteModel);
                } else {
                    optionsBuilder = new CustomImageLabelerOptions.Builder(localModel);
                }
                CustomImageLabelerOptions options = optionsBuilder
                    .setConfidenceThreshold(0.5f)
                    .setMaxResultCount(5)
                    .build();
                ImageLabeler labeler = ImageLabeling.getClient(options);
            }
        });

Yalnızca uzaktan barındırılan bir modeliniz varsa modelle ilgili ayarını devre dışı bırakmanız gerekir. (örneğin, kullanıcı arayüzünüzün bir kısmını devre dışı bırakan veya gizleyen) modelin indirildiğini onaylayın. Bunu bir dinleyici ekleyerek model yöneticisinin download() yöntemine:

Kotlin

RemoteModelManager.getInstance().download(remoteModel, conditions)
    .addOnSuccessListener {
        // Download complete. Depending on your app, you could enable the ML
        // feature, or switch from the local model to the remote model, etc.
    }

Java

RemoteModelManager.getInstance().download(remoteModel, conditions)
        .addOnSuccessListener(new OnSuccessListener() {
            @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.
            }
        });

2. Giriş resmini hazırlama

Ardından, etiketlemek istediğiniz her görüntü için bir InputImage oluşturun nesnedir. Bitmap kullandığınızda görüntü etiketleyici en hızlı şekilde çalışır veya Camera2 API'sini kullanıyorsanız, YUV_420_888 media.Image. önerilir.

InputImage oluşturabilirsiniz her biri aşağıda açıklanmıştır.

media.Image kullanarak

InputImage oluşturmak için media.Image nesnesinden bir nesneden (örneğin, cihazın kamerasını, media.Image nesnesini ve resmin döndürme değeri InputImage.fromMediaImage() değerine ayarlanır.

URL'yi CameraX kitaplığı, OnImageCapturedListener ve ImageAnalysis.Analyzer sınıfları rotasyon değerini hesaplar sizin için.

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önme derecesini sağlayan bir kamera kitaplığı kullanmıyorsanız cihazın dönüş derecesinden ve kameranın yönünden hesaplayabilir cihazdaki sensör:

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öndürme derecesi değerini InputImage.fromMediaImage() değerine ayarlayın:

Kotlin

val image = InputImage.fromMediaImage(mediaImage, rotation)

Java

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

Dosya URI'si kullanarak

InputImage oluşturmak için uygulama bağlamını ve dosya URI'sini InputImage.fromFilePath(). Bu özellik, kullanıcıdan seçim yapmasını istemek için bir ACTION_GET_CONTENT niyeti kullanın galeri uygulamasından bir resim.

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 kullanarak

InputImage oluşturmak için bir ByteBuffer veya ByteArray nesnesinden alıp almayacaksanız önce resmi hesaplayın media.Image girişi için daha önce açıklandığı gibi dönme derecesi. Ardından, arabellek veya diziyle InputImage nesnesini, bu resmin yükseklik, genişlik, renk kodlama biçimi ve döndürme derecesi:

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 kullanarak

InputImage oluşturmak için Bitmap nesnesindeki şu 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 temsil edilir.

3. Görüntü etiketleyiciyi çalıştırma

Bir görüntüdeki nesneleri etiketlemek için image nesnesini ImageLabeler öğesinin process() yöntemi.

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. Etiketli varlıklar hakkında bilgi edinme

Görüntü etiketleme işlemi başarılı olursa ImageLabel listesi nesneleri başarılı işleyiciye aktarılır. Her ImageLabel nesnesi, resimde etiketlenen bir şeyi temsil eder. Her etiketin metnini alabilirsiniz açıklama (TensorFlow Lite model dosyasının meta verilerinde varsa), güven puanı ve dizin. Ö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();
}

Gerçek zamanlı performansı iyileştirmeye yönelik ipuçları

Görüntüleri gerçek zamanlı bir uygulamada etiketlemek isterseniz şu aşağıdaki talimatları uygulayın:

  • URL'yi Camera veya camera2 API, çağrıları sınırlamanız gerekir. Yeni bir video çerçeve, resim etiketleyici çalışırken kullanılabilir hale gelirse çerçeveyi bırakın. Bkz. Örnek için hızlı başlangıç örnek uygulamasındaki VisionProcessorBase sınıfı.
  • CameraX API'yi kullanıyorsanız karşı baskı stratejisinin varsayılan değerine ayarlandığından emin olun ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) Bu, aynı anda yalnızca bir resmin analiz için gönderilmesini garanti eder. Daha fazla resim üretilirse analiz aracı meşgulken üretilirse otomatik olarak bırakılır ve teslimat. Analiz edilen resim, çağırarak kapatıldıktan sonra ImageProxy.close(), bir sonraki en son resim yayınlanır.
  • Grafikleri üzerine bindirmek için görüntü etiketleyicinin çıkışını kullanırsanız giriş görüntüsünü kullanın, önce ML Kit'ten sonucu alın ve ardından görüntüyü oluşturun tek bir adımda yapabilirsiniz. Bu, görüntü yüzeyine oluşturulur her giriş karesi için yalnızca bir kez. Bkz. CameraSourcePreview ve Hızlı başlangıç örnek uygulamasındaki GraphicOverlay sınıflarına göz atın.
  • Camera2 API'sini kullanıyorsanız görüntüleri şurada yakalayın: ImageFormat.YUV_420_888 biçimindedir. Eski Kamera API'sini kullanıyorsanız görüntüleri şurada yakalayın: ImageFormat.NV21 biçimindedir.