ML Kiti, poz algılama için optimize edilmiş iki SDK sunar.
| SDK adı | pose-detection | pose-detection-accurate |
|---|---|---|
| Uygulama | Kod ve öğeler, derleme sırasında uygulamanıza statik olarak bağlanır. | Kod ve öğeler, derleme sırasında uygulamanıza statik olarak bağlanır. |
| Uygulama boyutuna etkisi (kod ve öğeler dahil) | ~10,1 MB | ~13,3 MB |
| Performans | Pixel 3XL: ~30FPS | Pixel 3XL: CPU ile yaklaşık 23 FPS, GPU ile yaklaşık 30 FPS |
Deneyin
- Bu API'nin örnek kullanımını görmek için örnek uygulamayı deneyin.
Başlamadan önce
- Proje düzeyindeki
build.gradledosyanızda, Google'ın Maven deposunu hembuildscripthem deallprojectsbölümlerinize eklediğinizden emin olun. ML Kit Android kitaplıklarına ait bağımlılıkları, modülünüzün uygulama düzeyindeki Gradle dosyasına (genellikle
app/build.gradle) ekleyin:dependencies { // If you want to use the base sdk implementation 'com.google.mlkit:pose-detection:18.0.0-beta5' // If you want to use the accurate sdk implementation 'com.google.mlkit:pose-detection-accurate:18.0.0-beta5' }
1. PoseDetector örneği oluşturma
PoseDetector seçenek
Bir resimdeki pozu algılamak için önce PoseDetector örneği oluşturun ve isteğe bağlı olarak algılayıcı ayarlarını belirtin.
Algılama modu
PoseDetector, iki algılama modunda çalışır. Kullanım alanınıza uygun olanı seçtiğinizden emin olun.
STREAM_MODE(varsayılan)- Duruş algılayıcı, önce resimdeki en belirgin kişiyi algılar, ardından duruş algılama işlemini çalıştırır. Sonraki karelerde, kişi gizlenmediği veya yüksek güvenle algılanmaya devam ettiği sürece kişi algılama adımı gerçekleştirilmez. Duruş algılayıcı, en belirgin kişiyi takip etmeye ve her çıkarımda bu kişinin duruşunu döndürmeye çalışır. Bu sayede gecikme süresi azalır ve algılama daha sorunsuz hale gelir. Bu modu, bir video akışında duruş algılamak istediğinizde kullanın.
SINGLE_IMAGE_MODE- Duruş algılayıcı, bir kişiyi algılar ve ardından duruş algılama işlemini çalıştırır. Kişi algılama adımı her görüntü için çalıştırılacağından gecikme daha yüksek olur ve kişi takibi yapılmaz. Statik görüntülerde duruş algılama özelliğini kullanırken veya izlemenin istenmediği durumlarda bu modu kullanın.
Donanım yapılandırması
PoseDetector, performansı optimize etmek için birden fazla donanım yapılandırmasını destekler:
CPU: Algılayıcıyı yalnızca CPU kullanarak çalıştırın.CPU_GPU: Algılayıcıyı hem CPU hem de GPU kullanarak çalıştırın.
Algılayıcı seçeneklerini oluştururken donanım seçimini kontrol etmek için API'yi kullanabilirsiniz.
setPreferredHardwareConfigs Varsayılan olarak tüm donanım yapılandırmaları tercih edilen olarak ayarlanır.
ML Kiti, her yapılandırmanın kullanılabilirliğini, kararlılığını, doğruluğunu ve gecikmesini dikkate alır ve tercih edilen yapılandırmalar arasından en iyisini seçer. Tercih edilen yapılandırmalardan hiçbiri geçerli değilse yedek olarak otomatik şekilde CPU yapılandırması kullanılır. ML Kiti, herhangi bir hızlandırmayı etkinleştirmeden önce bu kontrolleri ve ilgili hazırlıkları engelleme yapmadan gerçekleştirir. Bu nedenle, kullanıcınız algılayıcıyı ilk kez çalıştırdığında büyük olasılıkla CPU kullanılır. Tüm hazırlıklar tamamlandıktan sonra, sonraki çalıştırmalarda en iyi yapılandırma kullanılır.
setPreferredHardwareConfigs için örnek kullanımlar:
- ML Kit'in en iyi yapılandırmayı seçmesi için bu API'yi çağırmayın.
- Herhangi bir hızlandırmayı etkinleştirmek istemiyorsanız yalnızca
CPUdeğerini iletin. - GPU daha yavaş olsa bile CPU'nun yükünü azaltmak için GPU kullanmak istiyorsanız yalnızca
CPU_GPUdeğerini iletin.
Poz algılayıcı seçeneklerini belirtin:
Kotlin
// Base pose detector with streaming frames, when depending on the pose-detection sdk val options = PoseDetectorOptions.Builder() .setDetectorMode(PoseDetectorOptions.STREAM_MODE) .build() // Accurate pose detector on static images, when depending on the pose-detection-accurate sdk val options = AccuratePoseDetectorOptions.Builder() .setDetectorMode(AccuratePoseDetectorOptions.SINGLE_IMAGE_MODE) .build()
Java
// Base pose detector with streaming frames, when depending on the pose-detection sdk PoseDetectorOptions options = new PoseDetectorOptions.Builder() .setDetectorMode(PoseDetectorOptions.STREAM_MODE) .build(); // Accurate pose detector on static images, when depending on the pose-detection-accurate sdk AccuratePoseDetectorOptions options = new AccuratePoseDetectorOptions.Builder() .setDetectorMode(AccuratePoseDetectorOptions.SINGLE_IMAGE_MODE) .build();
Son olarak, PoseDetector örneği oluşturun. Belirttiğiniz seçenekleri iletin:
Kotlin
val poseDetector = PoseDetection.getClient(options)
Java
PoseDetector poseDetector = PoseDetection.getClient(options);
2. Giriş görüntüsünü hazırlama
Bir resimdeki pozları algılamak için Bitmap, media.Image, ByteBuffer, bayt dizisi veya cihazdaki bir dosyadan InputImage nesnesi oluşturun. Ardından InputImage nesnesini PoseDetector'e geçirin.
Duruş algılama için en az 480x360 piksel boyutunda bir resim kullanmanız gerekir. Pozları gerçek zamanlı olarak algılıyorsanız kareleri bu minimum çözünürlükte yakalamak gecikmeyi azaltmaya yardımcı olabilir.
Farklı kaynaklardan InputImage nesnesi oluşturabilirsiniz. Bu kaynakların 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 görüntü yakaladığınızda) media.Image nesnesini ve görüntünün dönüşünü InputImage.fromMediaImage() değerine iletin.
CameraX kitaplığını kullanıyorsanız OnImageCapturedListener ve ImageAnalysis.Analyzer sınıfları, rotasyon değerini sizin için 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 // ... } } }
Görüntünün dönüş derecesini veren bir kamera kitaplığı kullanmıyorsanız bu değeri 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
Bir dosya URI'sinden InputImage nesnesi oluşturmak için uygulama bağlamını ve dosya URI'sini InputImage.fromFilePath() yöntemine iletin. Bu, kullanıcının galeri uygulamasından bir resim seçmesini istemek için ACTION_GET_CONTENT intent'ini kullandığınızda yararlı olur.
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 nesnesinden InputImage nesnesi oluşturmak için önce media.Image girişi için daha önce açıklandığı şekilde resim 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önüş derecesini içeren 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
Bir 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önüş dereceleriyle birlikte bir Bitmap nesnesiyle temsil edilir.
3. Resmi işleme
Hazırlanan InputImage nesnesini PoseDetector öğesinin process yöntemine iletin.
Kotlin
Task<Pose> result = poseDetector.process(image)
.addOnSuccessListener { results ->
// Task completed successfully
// ...
}
.addOnFailureListener { e ->
// Task failed with an exception
// ...
}Java
Task<Pose> result = poseDetector.process(image) .addOnSuccessListener( new OnSuccessListener<Pose>() { @Override public void onSuccess(Pose pose) { // Task completed successfully // ... } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
4. Algılanan poz hakkında bilgi edinme
Resimde bir kişi algılanırsa poz algılama API'si, 33 PoseLandmark içeren bir Pose nesnesi döndürür.
Kişi tamamen resmin içinde değilse model, eksik referans noktası koordinatlarını çerçevenin dışına atar ve bunlara düşük InFrameConfidence değerleri verir.
Karede kişi algılanmadıysa Pose nesnesi PoseLandmark içermez.
Kotlin
// Get all PoseLandmarks. If no person was detected, the list will be empty val allPoseLandmarks = pose.getAllPoseLandmarks() // Or get specific PoseLandmarks individually. These will all be null if no person // was detected val leftShoulder = pose.getPoseLandmark(PoseLandmark.LEFT_SHOULDER) val rightShoulder = pose.getPoseLandmark(PoseLandmark.RIGHT_SHOULDER) val leftElbow = pose.getPoseLandmark(PoseLandmark.LEFT_ELBOW) val rightElbow = pose.getPoseLandmark(PoseLandmark.RIGHT_ELBOW) val leftWrist = pose.getPoseLandmark(PoseLandmark.LEFT_WRIST) val rightWrist = pose.getPoseLandmark(PoseLandmark.RIGHT_WRIST) val leftHip = pose.getPoseLandmark(PoseLandmark.LEFT_HIP) val rightHip = pose.getPoseLandmark(PoseLandmark.RIGHT_HIP) val leftKnee = pose.getPoseLandmark(PoseLandmark.LEFT_KNEE) val rightKnee = pose.getPoseLandmark(PoseLandmark.RIGHT_KNEE) val leftAnkle = pose.getPoseLandmark(PoseLandmark.LEFT_ANKLE) val rightAnkle = pose.getPoseLandmark(PoseLandmark.RIGHT_ANKLE) val leftPinky = pose.getPoseLandmark(PoseLandmark.LEFT_PINKY) val rightPinky = pose.getPoseLandmark(PoseLandmark.RIGHT_PINKY) val leftIndex = pose.getPoseLandmark(PoseLandmark.LEFT_INDEX) val rightIndex = pose.getPoseLandmark(PoseLandmark.RIGHT_INDEX) val leftThumb = pose.getPoseLandmark(PoseLandmark.LEFT_THUMB) val rightThumb = pose.getPoseLandmark(PoseLandmark.RIGHT_THUMB) val leftHeel = pose.getPoseLandmark(PoseLandmark.LEFT_HEEL) val rightHeel = pose.getPoseLandmark(PoseLandmark.RIGHT_HEEL) val leftFootIndex = pose.getPoseLandmark(PoseLandmark.LEFT_FOOT_INDEX) val rightFootIndex = pose.getPoseLandmark(PoseLandmark.RIGHT_FOOT_INDEX) val nose = pose.getPoseLandmark(PoseLandmark.NOSE) val leftEyeInner = pose.getPoseLandmark(PoseLandmark.LEFT_EYE_INNER) val leftEye = pose.getPoseLandmark(PoseLandmark.LEFT_EYE) val leftEyeOuter = pose.getPoseLandmark(PoseLandmark.LEFT_EYE_OUTER) val rightEyeInner = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE_INNER) val rightEye = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE) val rightEyeOuter = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE_OUTER) val leftEar = pose.getPoseLandmark(PoseLandmark.LEFT_EAR) val rightEar = pose.getPoseLandmark(PoseLandmark.RIGHT_EAR) val leftMouth = pose.getPoseLandmark(PoseLandmark.LEFT_MOUTH) val rightMouth = pose.getPoseLandmark(PoseLandmark.RIGHT_MOUTH)
Java
// Get all PoseLandmarks. If no person was detected, the list will be empty List<PoseLandmark> allPoseLandmarks = pose.getAllPoseLandmarks(); // Or get specific PoseLandmarks individually. These will all be null if no person // was detected PoseLandmark leftShoulder = pose.getPoseLandmark(PoseLandmark.LEFT_SHOULDER); PoseLandmark rightShoulder = pose.getPoseLandmark(PoseLandmark.RIGHT_SHOULDER); PoseLandmark leftElbow = pose.getPoseLandmark(PoseLandmark.LEFT_ELBOW); PoseLandmark rightElbow = pose.getPoseLandmark(PoseLandmark.RIGHT_ELBOW); PoseLandmark leftWrist = pose.getPoseLandmark(PoseLandmark.LEFT_WRIST); PoseLandmark rightWrist = pose.getPoseLandmark(PoseLandmark.RIGHT_WRIST); PoseLandmark leftHip = pose.getPoseLandmark(PoseLandmark.LEFT_HIP); PoseLandmark rightHip = pose.getPoseLandmark(PoseLandmark.RIGHT_HIP); PoseLandmark leftKnee = pose.getPoseLandmark(PoseLandmark.LEFT_KNEE); PoseLandmark rightKnee = pose.getPoseLandmark(PoseLandmark.RIGHT_KNEE); PoseLandmark leftAnkle = pose.getPoseLandmark(PoseLandmark.LEFT_ANKLE); PoseLandmark rightAnkle = pose.getPoseLandmark(PoseLandmark.RIGHT_ANKLE); PoseLandmark leftPinky = pose.getPoseLandmark(PoseLandmark.LEFT_PINKY); PoseLandmark rightPinky = pose.getPoseLandmark(PoseLandmark.RIGHT_PINKY); PoseLandmark leftIndex = pose.getPoseLandmark(PoseLandmark.LEFT_INDEX); PoseLandmark rightIndex = pose.getPoseLandmark(PoseLandmark.RIGHT_INDEX); PoseLandmark leftThumb = pose.getPoseLandmark(PoseLandmark.LEFT_THUMB); PoseLandmark rightThumb = pose.getPoseLandmark(PoseLandmark.RIGHT_THUMB); PoseLandmark leftHeel = pose.getPoseLandmark(PoseLandmark.LEFT_HEEL); PoseLandmark rightHeel = pose.getPoseLandmark(PoseLandmark.RIGHT_HEEL); PoseLandmark leftFootIndex = pose.getPoseLandmark(PoseLandmark.LEFT_FOOT_INDEX); PoseLandmark rightFootIndex = pose.getPoseLandmark(PoseLandmark.RIGHT_FOOT_INDEX); PoseLandmark nose = pose.getPoseLandmark(PoseLandmark.NOSE); PoseLandmark leftEyeInner = pose.getPoseLandmark(PoseLandmark.LEFT_EYE_INNER); PoseLandmark leftEye = pose.getPoseLandmark(PoseLandmark.LEFT_EYE); PoseLandmark leftEyeOuter = pose.getPoseLandmark(PoseLandmark.LEFT_EYE_OUTER); PoseLandmark rightEyeInner = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE_INNER); PoseLandmark rightEye = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE); PoseLandmark rightEyeOuter = pose.getPoseLandmark(PoseLandmark.RIGHT_EYE_OUTER); PoseLandmark leftEar = pose.getPoseLandmark(PoseLandmark.LEFT_EAR); PoseLandmark rightEar = pose.getPoseLandmark(PoseLandmark.RIGHT_EAR); PoseLandmark leftMouth = pose.getPoseLandmark(PoseLandmark.LEFT_MOUTH); PoseLandmark rightMouth = pose.getPoseLandmark(PoseLandmark.RIGHT_MOUTH);
Performansı artırmaya yönelik ipuçları
Sonuçlarınızın kalitesi, giriş resminin kalitesine bağlıdır:
- ML Kit'in pozu doğru şekilde algılaması için resimdeki kişi yeterli piksel verisiyle temsil edilmelidir. En iyi performans için özne en az 256x256 piksel olmalıdır.
- Gerçek zamanlı bir uygulamada poz algılıyorsanız giriş resimlerinin genel boyutlarını da göz önünde bulundurmanız gerekebilir. Daha küçük resimler daha hızlı işlenebilir. Bu nedenle, gecikmeyi azaltmak için resimleri daha düşük çözünürlüklerde çekin ancak yukarıdaki çözünürlük şartlarını göz önünde bulundurun ve öznenin resmin mümkün olduğunca büyük bir kısmını kapladığından emin olun.
- Resim odağının kötü olması da doğruluğu etkileyebilir. Kabul edilebilir sonuçlar alamazsanız kullanıcıdan görüntüyü yeniden yakalamasını isteyin.
Duruş algılama özelliğini 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:
- Temel pose-detection SDK'sını ve
STREAM_MODEkullanın. - Görüntüleri daha düşük çözünürlükte yakalamayı deneyin. Ancak bu API'nin resim boyutu koşullarını da göz önünde bulundurun.
Cameraveyacamera2API'sini kullanıyorsanız algılayıcıya yapılan çağrıları kısıtlayın. Algılayıcı çalışırken yeni bir video karesi kullanılabilir hale gelirse kareyi bırakın. Ö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ğeri olanImageAnalysis.STRATEGY_KEEP_ONLY_LATESTdeğerine ayarlandığından emin olun. Bu sayede, analiz için aynı anda yalnızca bir resim gönderilir. Analizör meşgulken daha fazla görüntü üretilirse bunlar otomatik olarak silinir ve yayınlanmak üzere sıraya alınmaz. Analiz edilen görüntü ImageProxy.close() çağrılarak kapatıldıktan sonra bir sonraki en yeni görüntü yayınlanır.- Algılayıcının çıkışını giriş resminin üzerine grafik yerleştirmek için 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üleme yüzeyine 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 yakalayın. Eski Camera API'yi kullanıyorsanız görüntüleriImageFormat.NV21biçiminde yakalayın.
Sonraki adımlar
- Duruşları sınıflandırmak için duruş işaretlerini nasıl kullanacağınızı öğrenmek istiyorsanız Duruş Sınıflandırma İpuçları başlıklı makaleyi inceleyin.