Android'de ML Kit ile nesneleri algılama ve izleme

ML Kit'i kullanarak art arda gelen video karelerindeki nesneleri algılayıp izleyebilirsiniz.

Bir resmi ML Kit'e ilettiğinizde, resimdeki beşe kadar nesneyi algılar konumla birlikte resimdeki her bir nesnenin konumunu da içerir. Şu yolda nesneleri algılarken: her nesnenin, nesneyi izlemek için kullanabileceğiniz benzersiz bir kimliği vardır her kareden kareye geçelim. İsterseniz genel nesneyi de etkinleştirebilirsiniz. geniş kategori tanımlarına sahip nesneleri etiketleyen bir modeldir.

Deneyin

Başlamadan önce

  1. Proje düzeyindeki build.gradle dosyanızda, Google'ın Maven deposunu hem buildscript hem de allprojects bölümünüze eklediğinizden emin olun.
  2. ML Kit Android kitaplıklarının bağımlılıklarını modülünüze uygulama düzeyinde gradle dosyası vardır. Bu genellikle app/build.gradle olan:
    dependencies {
      // ...
    
      implementation 'com.google.mlkit:object-detection:17.0.2'
    
    }
    

1. Nesne algılayıcıyı yapılandırma

Nesneleri algılamak ve izlemek için önce bir ObjectDetector örneği oluşturun ve isteğe bağlı olarak varsayılan ayarlardan değiştirmek istediğiniz tüm dedektör ayarlarını belirtin.

  1. Nesne algılayıcıyı kullanım alanınız için bir ObjectDetectorOptions nesnesi ile yapılandırın. Şunları değiştirebilirsiniz: Ayarlar:

    Nesne Algılayıcı Ayarları
    Algılama modu STREAM_MODE (varsayılan) | SINGLE_IMAGE_MODE

    STREAM_MODE ürününde (varsayılan) nesne algılayıcısı çalışır düşük gecikme süresi vardır, ancak eksik sonuçlar oluşturabilir (ör. (belirtilmemiş sınırlayıcı kutular veya kategori etiketleri) yer alır. çağrılarına karşılık gelir. Ayrıca STREAM_MODE içinde, algılayıcı, nesnelere izleme kimlikleri atar. Bu kimlikleri çerçevelerde nesneleri izler. YouTube TV'yi izlemek için veya veri işleme sırasında olduğu gibi düşük gecikmenin gerçek zamanlı video akışı sağlar.

    SINGLE_IMAGE_MODE işlevinde, nesne algılayıcısı nesnenin sınırlayıcı kutusu belirlendikten sonra sonuç elde edilir. Sınıflandırmayı da etkinleştirirseniz sonuç, sınırlayıcı kutu ve kategori etiketi mevcut olduktan sonra döndürülür. Bunun sonucunda, algılama gecikmesi muhtemelen daha yüksektir. Ayrıca SINGLE_IMAGE_MODE'te izleme kimlikleri atanmaz. Tekliflerinizi otomatikleştirmek ve optimize etmek için bu modu kullanırsanız ve gecikmeyle uğraşmak istemezseniz kısmi sonuçlar sağlar.

    Birden çok nesneyi algılama ve izleme false (varsayılan) | true

    En fazla beş nesnenin mi yoksa yalnızca en belirgin nesnenin mi algılanıp izleneceğini belirler (varsayılan).

    Nesneleri sınıflandırma false (varsayılan) | true

    Algılanan nesnelerin kaba kategorilere sınıflandırılıp sınıflandırılmayacağı. Nesne algılayıcı etkinleştirildiğinde nesneleri aşağıdaki kategorilere sınıflandırır: moda ürünleri, gıda, ev ürünleri, yerler ve bitkiler.

    Nesne algılama ve izleme API'si, aşağıdaki iki temel kullanım alanı için optimize edilmiştir:

    • Kamera vizöründeki en belirgin nesnenin canlı olarak algılanması ve izlenmesi.
    • Statik görüntüden birden fazla nesnenin algılanması.

    API'yi bu kullanım alanları için yapılandırmak üzere:

    Kotlin

    // Live detection and tracking
    val options = ObjectDetectorOptions.Builder()
            .setDetectorMode(ObjectDetectorOptions.STREAM_MODE)
            .enableClassification()  // Optional
            .build()
    
    // Multiple object detection in static images
    val options = ObjectDetectorOptions.Builder()
            .setDetectorMode(ObjectDetectorOptions.SINGLE_IMAGE_MODE)
            .enableMultipleObjects()
            .enableClassification()  // Optional
            .build()

    Java

    // Live detection and tracking
    ObjectDetectorOptions options =
            new ObjectDetectorOptions.Builder()
                    .setDetectorMode(ObjectDetectorOptions.STREAM_MODE)
                    .enableClassification()  // Optional
                    .build();
    
    // Multiple object detection in static images
    ObjectDetectorOptions options =
            new ObjectDetectorOptions.Builder()
                    .setDetectorMode(ObjectDetectorOptions.SINGLE_IMAGE_MODE)
                    .enableMultipleObjects()
                    .enableClassification()  // Optional
                    .build();
  2. ObjectDetector örneği alın:

    Kotlin

    val objectDetector = ObjectDetection.getClient(options)

    Java

    ObjectDetector objectDetector = ObjectDetection.getClient(options);

2. Giriş resmini hazırlama

Nesneleri algılamak ve izlemek için resimleri ObjectDetector cihazına iletin örneğin process() yöntemini kullanabilirsiniz.

Nesne algılayıcı, doğrudan bir Bitmap, NV21 ByteBuffer veya YUV_420_888 media.Image. Bunlardan birine doğrudan erişiminiz varsa bu kaynaklardan InputImage oluşturmanız önerilir. Diğer kaynaklardan bir InputImage oluşturursanız dönüşümü sizin için dahili olarak biz yönetiriz ve bu işlem daha az verimli olabilir.

Bir dizideki her bir video veya resim karesi için aşağıdakileri yapın:

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 gösteren bir kamera kitaplığı kullanmıyorsanız bunu cihazın dönme derecesinden ve cihazdaki kamera sensörünün yöneliminden 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ö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, 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.

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 kullanma

Bir Bitmap nesnesinden InputImage nesnesi oluşturmak için aşağıdaki beyanı yapın:

Kotlin

val image = InputImage.fromBitmap(bitmap, 0)

Java

InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);

Resim, döndürme dereceleriyle birlikte bir Bitmap nesnesi ile temsil edilir.

3. Resmi işleyin

Resmi process() yöntemine geçirin:

Kotlin

objectDetector.process(image)
    .addOnSuccessListener { detectedObjects ->
        // Task completed successfully
        // ...
    }
    .addOnFailureListener { e ->
        // Task failed with an exception
        // ...
    }

Java

objectDetector.process(image)
    .addOnSuccessListener(
        new OnSuccessListener<List<DetectedObject>>() {
            @Override
            public void onSuccess(List<DetectedObject> detectedObjects) {
                // Task completed successfully
                // ...
            }
        })
    .addOnFailureListener(
        new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // Task failed with an exception
                // ...
            }
        });

4. Algılanan nesneler hakkında bilgi alma

process() çağrısı başarılı olursa başarı dinleyicisine bir DetectedObject listesi iletilir.

Her DetectedObject aşağıdaki özellikleri içerir:

Sınırlayıcı kutu NesneninRect görüntüsüdür.
İzleme Kimliği Nesneyi resimler arasında tanımlayan bir tam sayı. Boş değer: SINGLE_IMAGE_MODE.
Etiketler
Etiket açıklaması Etiketin metin açıklaması. Bu, sabit değerler PredefinedCategory içinde tanımlanır.
Etiket dizini Desteklenen tüm etiketler arasından etiketin dizini sınıflandırıcıyı kullanabilirsiniz. Belirlenen tam sayı sabitlerinden biri olmalıdır. PredefinedCategory içinde.
Etiket güvenilirliği Nesne sınıflandırmasının güven değeri.

Kotlin

for (detectedObject in detectedObjects) {
    val boundingBox = detectedObject.boundingBox
    val trackingId = detectedObject.trackingId
    for (label in detectedObject.labels) {
        val text = label.text
        if (PredefinedCategory.FOOD == text) {
            ...
        }
        val index = label.index
        if (PredefinedCategory.FOOD_INDEX == index) {
            ...
        }
        val confidence = label.confidence
    }
}

Java

// The list of detected objects contains one item if multiple
// object detection wasn't enabled.
for (DetectedObject detectedObject : detectedObjects) {
    Rect boundingBox = detectedObject.getBoundingBox();
    Integer trackingId = detectedObject.getTrackingId();
    for (Label label : detectedObject.getLabels()) {
        String text = label.getText();
        if (PredefinedCategory.FOOD.equals(text)) {
            ...
        }
        int index = label.getIndex();
        if (PredefinedCategory.FOOD_INDEX == index) {
            ...
        }
        float confidence = label.getConfidence();
    }
}

Mükemmel bir kullanıcı deneyimi sunma

En iyi kullanıcı deneyimi için uygulamanızda aşağıdaki yönergeleri uygulayın:

  • Nesne algılamanın başarılı olması, nesnenin görsel karmaşıklığına bağlıdır. Az sayıda görsel özelliğe sahip nesnelerin algılanması için görüntünün daha büyük bir kısmını kaplaması gerekebilir. Kullanıcılara şu konularda yol göstermeniz gerekir: algılamak istediğiniz tür nesnelerde iyi çalışan bir giriş yakalamanızı sağlar.
  • Sınıflandırmayı kullanırken desteklenen kategorilere tam olarak uymayan nesneleri algılamak istiyorsanız bilinmeyen nesneler için özel işlem uygulayın.

Ayrıca ML Kit Material Design vitrin uygulamasına ve Material Design Makine öğrenimi destekli özellikler için kalıplar koleksiyonuna göz atın.

Performansı artırma

Nesne algılamayı gerçek zamanlı bir uygulamada kullanmak istiyorsanız en iyi kare hızlarını elde etmek için aşağıdaki yönergeleri uygulayın:

  • Gerçek zamanlı bir uygulamada akış modunu kullandığınızda çoğu cihaz yeterli kare hızları üretemeyeceğinden birden fazla nesne algılama özelliğini kullanmayın.

  • İhtiyacınız yoksa sınıflandırmayı devre dışı bırakın.

  • Camera veya camera2 API'sini kullanıyorsanız algılayıcıya yapılan çağrıları sınırlayın. Yeni bir video çerçeve, algılayıcı ç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 sayede, aynı anda analiz için yalnızca bir resim gönderilir. Analizör meşgulken daha fazla görüntü oluşturulursa bu görüntüler otomatik olarak bırakılır ve yayınlama için sıraya alınmaz. Analiz edilen resim, çağırarak kapatıldıktan sonra ImageProxy.close(), bir sonraki en son resim yayınlanır.
  • 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. Bu, her giriş karesi için yalnızca bir kez görüntü yüzeyinde oluşturulur. Örnek olarak, hızlı başlangıç kılavuzundaki örnek uygulamadaki CameraSourcePreview ve GraphicOverlay sınıflarına bakı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.