Ardışık video karelerindeki nesneleri algılamak ve izlemek için ML Kit'i kullanabilirsiniz.
ML Kit'e bir resim ilettiğinizde, resimdeki her nesnenin konumuyla birlikte resimde en fazla beş nesne algılanır. Video akışlarındaki nesneler algılanırken her nesnenin, nesneyi kareden kareye izlemek için kullanabileceğiniz benzersiz bir kimliği olur. İsteğe bağlı olarak, nesneleri geniş kategori açıklamalarıyla etiketleyen kaba nesne sınıflandırmasını da etkinleştirebilirsiniz.
Deneyin
- Bu API'nin kullanımına dair bir örnek görmek için örnek uygulamayı inceleyin.
- Bu API'nin uçtan uca uygulanması için Material Design Showcase uygulamasını inceleyin.
Başlamadan önce
- Proje düzeyindeki
build.gradle
dosyanızda, Google'ın Maven deposunu hembuildscript
hem deallprojects
bölümlerine 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
) ekleyin: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 ObjectDetector
örneği oluşturun ve isteğe bağlı olarak varsayılandan değiştirmek istediğiniz algılayıcı ayarlarını belirtin.
Nesne algılayıcıyı,
ObjectDetectorOptions
nesnesiyle kullanım alanınıza göre yapılandırın. Aşağıdaki ayarları değiştirebilirsiniz:Nesne Algılayıcı Ayarları Algılama modu STREAM_MODE
(varsayılan) |SINGLE_IMAGE_MODE
STREAM_MODE
(varsayılan) modunda, nesne algılayıcı düşük gecikmeyle çalışır ancak algılayıcının ilk birkaç çağrısında eksik sonuçlar (ör. belirtilmemiş sınırlayıcı kutular veya kategori etiketleri) üretebilir. Ayrıca,STREAM_MODE
içinde dedektör, nesnelere izleme kimlikleri atar. Bu kimlikleri, nesneleri kareler arasında izlemek için kullanabilirsiniz. Nesneleri izlemek istediğinizde veya düşük gecikmenin önemli olduğu durumlarda (ör. video akışlarını gerçek zamanlı olarak işlerken) bu modu kullanın.SINGLE_IMAGE_MODE
içinde, nesne algılayıcı, nesnenin sınırlayıcı kutusu belirlendikten sonra sonucu döndürür. Sınıflandırmayı da etkinleştirirseniz hem sınırlayıcı kutu hem de kategori etiketi kullanılabilir olduğunda sonuç döndürülür. Sonuç olarak, algılama gecikmesi daha yüksek olabilir. Ayrıca,SINGLE_IMAGE_MODE
içinde izleme kimlikleri atanmaz. Gecikme kritik değilse ve kısmi sonuçlarla uğraşmak istemiyorsanız bu modu kullanın.Birden fazla nesneyi algılama ve izleme false
(varsayılan) |true
Beş nesneye kadar algılama ve izleme veya yalnızca en belirgin nesneyi algılama ve izleme (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ğı. Etkinleştirildiğinde nesne dedektörü, nesneleri şu kategorilere ayırır: moda ürünleri, yiyecek, 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 bir 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();
ObjectDetector
örneğini alma:Kotlin
val objectDetector = ObjectDetection.getClient(options)
Java
ObjectDetector objectDetector = ObjectDetection.getClient(options);
2. Giriş resmini hazırlama
Nesneleri algılamak ve izlemek için görüntüleriObjectDetector
örneğinin process()
yöntemine iletin.
Nesne dedektörü doğrudan Bitmap
, NV21 ByteBuffer
veya YUV_420_888 media.Image
'den çalışır. Bu kaynaklardan birine doğrudan erişiminiz varsa bu kaynaklardan InputImage
oluşturmanız önerilir. Diğer kaynaklardan InputImage
oluşturursanız dönüşümü sizin için dahili olarak gerçekleştiririz ve bu işlem daha az verimli olabilir.
Bir dizideki her video veya resim karesi için aşağıdakileri yapın:
Farklı kaynaklardan InputImage
nesnesi oluşturabilirsiniz. Her biri aşağıda açıklanmıştır.
media.Image
kullanma
Bir 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, cihazın dönüş derecesi ve cihazdaki kamera sensörünün yönlendirmesinden yararlanarak dönüş derecesini 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()
'ye 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. Resmi işleme
Resmiprocess()
yöntemine iletin:
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 DetectedObject
listesi başarı dinleyicisine iletilir.
Her DetectedObject
aşağıdaki özellikleri içerir:
Sınırlayıcı kutu | Nesnenin resimdeki konumunu belirten bir Rect . |
||||||
İzleme Kimliği | Nesneyi resimler arasında tanımlayan bir tam sayı. Null in SINGLE_IMAGE_MODE. | ||||||
Etiketler |
|
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 sağlama
En iyi kullanıcı deneyimi için uygulamanızda aşağıdaki kurallara uyun:
- Başarılı nesne algılama, nesnenin görsel karmaşıklığına bağlıdır. Algılanabilmesi için az sayıda görsel özelliğe sahip nesnelerin görüntünün daha büyük bir bölümünü kaplaması gerekebilir. Kullanıcılara, algılamak istediğiniz nesne türleriyle iyi çalışan girişler yakalama konusunda rehberlik etmelisiniz.
- Sınıflandırma kullanırken desteklenen kategorilere net bir şekilde girmeyen nesneleri algılamak istiyorsanız bilinmeyen nesneler için özel işlem uygulayın.
Ayrıca, ML Kit Material Design tanıtım uygulamasına ve Material Design Makine öğrenimi destekli özellikler için kalıplar koleksiyonuna da 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:
Anlık bir uygulamada akış modunu kullanırken çoğu cihaz yeterli kare hızını sağlayamayacağından birden fazla nesne algılama kullanmayın.
İhtiyacınız yoksa sınıflandırmayı devre dışı bırakın.
Camera
veyacamera2
API'sini kullanıyorsanız dedektöre yapılan çağrıları sınırlayın. Dedektör çalışırken yeni bir video karesi kullanılabilir hale gelirse kareyi bırakın. Örnek için hızlı başlangıç örnek uygulamasındakiVisionProcessorBase
sınıfına bakın.CameraX
API'sini kullanıyorsanız geri basınç stratejisinin varsayılan değerine ayarlandığından emin olunImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
. Bu, analiz için aynı anda 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 algılayıcının çıkışını kullanıyorsanız önce ML Kit'ten sonucu alın, ardından resmi tek adımda oluşturun ve 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
CameraSourcePreview
veGraphicOverlay
sınıflarına bakın. - Camera2 API'yi kullanıyorsanız
ImageFormat.YUV_420_888
biçiminde resim çekin. Eski Camera API'yi kullanıyorsanız görüntüleriImageFormat.NV21
biçiminde çekin.