Android'de AutoML tarafından eğitilmiş bir modelle görüntüleri etiketleme

AutoML Vision Edge'i kullanarak kendi modelinizi eğittikten sonra, bu modeli uygulamanızda kullanarak görüntüleri etiketleyebilirsiniz.

Başlamadan önce

  1. Henüz yapmadıysanız Firebase'i Android projenize ekleyin.
  2. ML Kit Android kitaplıklarının bağımlılıkları modülünüzün (uygulama düzeyinde) Gradle dosyasına (genellikle app/build.gradle) eklenmelidir:
    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. Modeli yükleme

ML Kit, AutoML tarafından oluşturulan modellerinizi cihazda çalıştırır. Ancak ML Kit'i, modelinizi Firebase'den, yerel depolama alanından veya her ikisinden de uzaktan yükleyecek şekilde yapılandırabilirsiniz.

Modeli Firebase'de barındırarak yeni bir uygulama sürümü yayınlamadan güncelleyebilir ve farklı kullanıcı gruplarına dinamik olarak farklı modeller sunmak için Remote Config ve A/B Testing'ü kullanabilirsiniz.

Modeli yalnızca Firebase ile barındırarak sağlamak ve uygulamanızla birlikte paketlemek istemiyorsanız uygulamanızın ilk indirme boyutunu azaltabilirsiniz. Ancak model uygulamanızla birlikte paketlenmemişse uygulamanız modeli ilk kez indirene kadar modelle ilgili işlevlerin kullanılamayacağını unutmayın.

Modelinizi uygulamanızla birlikte paketleyerek Firebase'da barındırılan model kullanılamadığında uygulamanızın ML özelliklerinin çalışmaya devam etmesini sağlayabilirsiniz.

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

Uzaktan barındırılan modeli kullanmak için modeli yayınlarken atadığınız adı belirterek bir FirebaseAutoMLRemoteModel nesnesi oluşturun:

JavaKotlin
// Specify the name you assigned in the Firebase console.
FirebaseAutoMLRemoteModel remoteModel =
    new FirebaseAutoMLRemoteModel.Builder("your_remote_model").build();
// Specify the name you assigned in the Firebase console.
val remoteModel = FirebaseAutoMLRemoteModel.Builder("your_remote_model").build()

Ardından, indirmeye izin vermek istediğiniz koşulları belirterek model indirme görevini başlatın. Model cihazda yoksa veya modelin daha yeni bir sürümü varsa görev, modeli Firebase'den eşzamansız olarak indirir:

JavaKotlin
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.
            }
        });
val conditions = FirebaseModelDownloadConditions.Builder()
    .requireWifi()
    .build()
FirebaseModelManager.getInstance().download(remoteModel, conditions)
    .addOnCompleteListener {
        // Success.
    }

Birçok uygulama, indirme görevini başlatma kodunda başlatır ancak modeli kullanmadan önce dilediğiniz zaman bu işlemi yapabilirsiniz.

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

Modeli uygulamanızla paket haline getirmek için:

  1. Modeli ve meta verilerini, Firebase konsolundan indirdiğiniz ZIP arşivinden çıkarın. Dosyaları, indirdiğiniz şekilde, herhangi bir değişiklik yapmadan (dosya adları dahil) kullanmanızı öneririz.
  2. Modelinizi ve meta veri dosyalarını uygulama paketinize ekleyin:

    1. Projenizde öğe klasörü yoksa app/ klasörünü sağ tıklayıp Yeni > Klasör > Öğe Klasörü'nü tıklayarak bir klasör oluşturun.
    2. Model dosyalarını içerecek şekilde öğeler klasörünün altında bir alt klasör oluşturun.
    3. model.tflite, dict.txt ve manifest.json dosyalarını alt klasöre kopyalayın (üç dosyanın da aynı klasörde olması gerekir).
  3. Gradle'ın uygulamayı derleyip model dosyasını sıkıştırmamasını sağlamak için uygulamanızın build.gradle dosyasına aşağıdakileri ekleyin:
    android {
        // ...
        aaptOptions {
            noCompress "tflite"
        }
    }
    
    Model dosyası uygulama paketine dahil edilir ve ML Kit tarafından ham öğe olarak kullanılabilir.
  4. Model manifest dosyasının yolunu belirten bir FirebaseAutoMLLocalModel nesnesi oluşturun:
    JavaKotlin
    FirebaseAutoMLLocalModel localModel = new FirebaseAutoMLLocalModel.Builder()
            .setAssetFilePath("manifest.json")
            .build();
    
    val localModel = FirebaseAutoMLLocalModel.Builder()
            .setAssetFilePath("manifest.json")
            .build()
    

Modelinizden resim etiketleyici oluşturma

Model kaynaklarınızı yapılandırdıktan sonra bunlardan birinde FirebaseVisionImageLabeler nesnesi oluşturun.

Yalnızca yerel olarak paketlenmiş bir modeliniz varsa FirebaseAutoMLLocalModel nesnenizle bir etiketleyici oluşturup zorunlu kılmak istediğiniz güven puanı eşiğini yapılandırmanız yeterlidir (Modelinizi değerlendirme bölümüne bakın):

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

Uzaktan barındırılan bir modeliniz varsa çalıştırmadan önce modelin indirildiğinden emin olmanız gerekir. Model yöneticisinin isModelDownloaded() yöntemini kullanarak model indirme görevinin durumunu kontrol edebilirsiniz.

Bunu yalnızca etiketleyiciyi çalıştırmadan önce onaylamanız gerekir. Ancak hem uzakta barındırılan hem de yerel olarak paketlenmiş bir modeliniz varsa görüntü etiketleyiciyi örneklendirirken bu kontrolü gerçekleştirmeniz yararlı olabilir: İndirilmişse uzak modelden, indirilmediyse yerel modelden bir etiketleyici oluşturun.

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

Yalnızca uzaktan barındırılan bir modeliniz varsa modelin indirildiğini onaylayana kadar modelle ilgili işlevleri (ör. kullanıcı arayüzünüzün bir bölümünü devre dışı bırakma veya gizleme) devre dışı bırakmanız gerekir. Bunu, model yöneticisinin download() yöntemine bir dinleyici ekleyerek yapabilirsiniz:

JavaKotlin
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.
            }
        });
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. Giriş resmini hazırlama

Ardından, etiketlemek istediğiniz her resim için bu bölümde açıklanan seçeneklerden birini kullanarak bir FirebaseVisionImage nesnesi oluşturun ve bunu bir FirebaseVisionImageLabeler örneğine (sonraki bölümde açıklanmaktadır) iletin.

Bir media.Image nesnesinden, cihazdaki bir dosyadan, bayt dizisinden veya Bitmap nesnesinden FirebaseVisionImage nesnesi oluşturabilirsiniz:

  • Bir media.Image nesnesinden FirebaseVisionImage nesnesi oluşturmak için (ör. bir cihazın kamerasından resim çekerken) media.Image nesnesini ve resmin dönme açısını FirebaseVisionImage.fromMediaImage()'ye iletin.

    CameraX kitaplığını kullanıyorsanız OnImageCapturedListener ve ImageAnalysis.Analyzer sınıfları rotasyon değerini sizin için hesaplar. Bu nedenle, FirebaseVisionImage.fromMediaImage() işlevini çağırmadan önce rotasyonu ML Kit'in ROTATION_ sabitlerinden birine dönüştürmeniz yeterlidir:

    JavaKotlin
    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
            // ...
        }
    }
    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
                // ...
            }
        }
    }

    Resmin dönme açısını gösteren bir kamera kitaplığı kullanmıyorsanız bunu cihazın dönme açısını ve cihazdaki kamera sensörünün yönünü kullanarak hesaplayabilirsiniz:

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

    Ardından, media.Image nesnesini ve dönüş değerini FirebaseVisionImage.fromMediaImage()'e gönderin:

    JavaKotlin
    FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
    val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
  • Dosya URI'sinden FirebaseVisionImage nesnesi oluşturmak için uygulama bağlamını ve dosya URI'sini FirebaseVisionImage.fromFilePath()'a iletin. Bu, kullanıcıdan galeri uygulamasından bir resim seçmesini istemek için ACTION_GET_CONTENT intent'i kullandığınızda kullanışlıdır.
    JavaKotlin
    FirebaseVisionImage image;
    try {
        image = FirebaseVisionImage.fromFilePath(context, uri);
    } catch (IOException e) {
        e.printStackTrace();
    }
    val image: FirebaseVisionImage
    try {
        image = FirebaseVisionImage.fromFilePath(context, uri)
    } catch (e: IOException) {
        e.printStackTrace()
    }
  • Bir ByteBuffer veya bayt dizisinden FirebaseVisionImage nesnesi oluşturmak için önce, media.Image girişi için yukarıda açıklandığı şekilde resim rotasyonunu hesaplayın.

    Ardından, resmin yüksekliğini, genişliğini, renk kodlama biçimini ve döndürülmüş durumunu içeren bir FirebaseVisionImageMetadata nesnesi oluşturun:

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

    FirebaseVisionImage nesnesi oluşturmak için arabellek veya diziyi ve meta veri nesnesini kullanın:

    JavaKotlin
    FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
    // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
    val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata)
    // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
  • Bitmap nesnesinden FirebaseVisionImage nesnesi oluşturmak için:
    JavaKotlin
    FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
    val image = FirebaseVisionImage.fromBitmap(bitmap)
    Bitmap nesnesi tarafından temsil edilen resim dik olmalıdır ve ek döndürme işlemi gerekmemelidir.

3. Resim etiketleyiciyi çalıştırma

Bir resimdeki nesneleri etiketlemek için FirebaseVisionImage nesnesini FirebaseVisionImageLabeler'un processImage() yöntemine iletin.

JavaKotlin
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
                // ...
            }
        });
labeler.processImage(image)
        .addOnSuccessListener { labels ->
            // Task completed successfully
            // ...
        }
        .addOnFailureListener { e ->
            // Task failed with an exception
            // ...
        }

Görüntü etiketleme başarılı olursa başarı dinleyicisine bir FirebaseVisionImageLabel nesnesi dizisi iletilir. Her nesneden, resimde tanınan bir özellik hakkında bilgi edinebilirsiniz.

Örneğin:

JavaKotlin
for (FirebaseVisionImageLabel label: labels) {
    String text = label.getText();
    float confidence = label.getConfidence();
}
for (label in labels) {
    val text = label.text
    val confidence = label.confidence
}

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

  • Dedektöre yapılan çağrıları azaltın. Algılayıcı çalışırken yeni bir video karesi kullanılabilir hale gelirse kareyi bırakın.
  • Giriş resmine grafik yerleştirmek için algılayıcının çıkışını kullanıyorsanız önce ML Kit'ten sonucu alın, ardından resmi ve yer paylaşımını tek bir adımda oluşturun. Böylece, her giriş çerçevesi için ekran yüzeyinde yalnızca bir kez oluşturma işlemi gerçekleştirirsiniz.
  • Camera2 API'yi kullanıyorsanız resimleri ImageFormat.YUV_420_888 biçiminde kaydedin.

    Eski Camera API'yi kullanıyorsanız resimleri ImageFormat.NV21 biçiminde çekin.