ML Kit, poz algılama için optimize edilmiş iki SDK sunar.
SDK Adı | poz-tespit | poz-algılama-doğru |
---|---|---|
Uygulama | Kod ve öğeler, derleme sırasında statik olarak uygulamanıza bağlıdır. | Kod ve öğeler, derleme sırasında statik olarak uygulamanıza bağlıdır. |
Uygulama boyutuna etkisi (kod ve öğeler dahil) | ~10,1 MB | ~13,3 MB |
Performans | Pixel 3XL: ~30FPS | Pixel 3XL: CPU ile ~23FPS, GPU ile ~30FPS |
Deneyin
- Bu API'nin örnek kullanımını görmek için örnek uygulamayı inceleyin.
Başlamadan önce
- Proje düzeyindeki
build.gradle
dosyanıza, hembuildscript
hem deallprojects
bölümlerinize Google'ın Maven deposunu 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 ekleyin. Bu dosya genellikle
app/build.gradle
türündedir:dependencies { // If you want to use the base sdk implementation 'com.google.mlkit:pose-detection:18.0.0-beta4' // If you want to use the accurate sdk implementation 'com.google.mlkit:pose-detection-accurate:18.0.0-beta4' }
1. PoseDetector
örneği oluşturun
PoseDetector
seçenek
Bir görüntüdeki 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)- Poz algılayıcı, önce resimdeki en belirgin kişiyi algılar ve ardından poz algılamayı çalıştırır. Sonraki karelerde, kişi bulanıklaştırılmadığı veya artık yüksek güvenilirlikle algılanmadığı sürece kişi algılama adımı gerçekleştirilmez. Poz dedektörü, en belirgin kişiyi takip etmeye ve her çıkarımda kendi duruşunu döndürmeye çalışır. Bu, gecikmeyi azaltır ve algılamayı sorunsuz hale getirir. Video akışında pozu algılamak istediğinizde bu modu kullanın.
SINGLE_IMAGE_MODE
- Poz dedektörü bir kişiyi algılar ve ardından poz algılama özelliğini çalıştırır. Kişi algılama adımı her resim için çalışır. Bu nedenle gecikme daha yüksek olur ve kişi izleme yapılmaz. Statik resimlerde duruş algılamayı 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ır.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 setPreferredHardwareConfigs
API'sini kullanabilirsiniz. Varsayılan olarak, tüm donanım yapılandırmaları tercih edilen şekilde ayarlanır.
ML Kit her yapılandırmanın kullanılabilirliğini, kararlılığını, doğruluğunu ve gecikmesini dikkate alarak tercih edilen yapılandırmalar arasından en iyisini seçer. Tercih edilen yapılandırmalardan hiçbiri geçerli değilse CPU
yapılandırması otomatik olarak yedek olarak kullanılır. ML Kit, herhangi bir hızlandırmayı etkinleştirmeden önce bu kontrolleri ve ilgili hazırlıkları engellemeyen bir şekilde yapar. Bu nedenle, kullanıcınız büyük olasılıkla algılayıcıyı ilk kez çalıştırdığında CPU
kullanır. Tüm hazırlıklar tamamlandıktan sonra, sonraki çalıştırmalarda en iyi yapılandırma kullanılır.
setPreferredHardwareConfigs
kullanımıyla ilgili örnek kullanımlar:
- ML Kit'in en iyi yapılandırmayı seçmesini sağlamak için bu API'yi çağırmayın.
- Herhangi bir hızlandırmayı etkinleştirmek istemiyorsanız yalnızca
CPU
olarak geçin. - GPU daha yavaş olsa bile CPU'yu boşaltmak için GPU'yu kullanmak istiyorsanız yalnızca
CPU_GPU
'yi aktarın.
Poz algılayıcısı 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
öğesinin bir örneğini oluşturun. Belirttiğiniz seçenekleri iletin:
Kotlin
val poseDetector = PoseDetection.getClient(options)
Java
PoseDetector poseDetector = PoseDetection.getClient(options);
2. Giriş resmini hazırlama
Bir görüntüdeki duruşları algılamak için Bitmap
, media.Image
, ByteBuffer
, bayt dizisi veya cihazdaki bir dosyadan InputImage
nesnesi oluşturun. Ardından InputImage
nesnesini PoseDetector
öğesine iletin.
Poz algılama için en az 480x360 piksel boyutunda bir resim kullanmanız gerekir. Pozları gerçek zamanlı olarak algılıyorsanız karelerin bu minimum çözünürlükte yakalanması gecikmenin azaltılmasına yardımcı olabilir.
Farklı kaynaklardan InputImage
nesnesi oluşturabilirsiniz. Bu nesnelerin her biri aşağıda açıklanmıştır.
media.Image
kullanarak
media.Image
nesnesinden InputImage
nesnesi oluşturmak için (örneğin, cihaz kamerasından resim çekerken) media.Image
nesnesini ve görüntünün dönüşünü InputImage.fromMediaImage()
yönüne geçirin.
KameraX kitaplığını kullanırsanı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 // ... } } }
Resmin dönüş derecesini gösteren bir kamera kitaplığı kullanmıyorsanız 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öndürme derecesi değerini InputImage.fromMediaImage()
öğesine geçirin:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Dosya URI'si kullanarak
Dosya URI'sinden bir InputImage
nesnesi oluşturmak için uygulama bağlamını ve dosya URI'sini InputImage.fromFilePath()
öğesine iletin. Bu, kullanıcıdan galeri uygulamasından resim seçmesini istemek için ACTION_GET_CONTENT
niyeti 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
kullanarak
ByteBuffer
veya ByteArray
öğesinden InputImage
nesnesi oluşturmak için önce media.Image
girişi için daha önce açıklandığı gibi resim döndürme derecesini hesaplayın.
Ardından, resmin yüksekliği, genişliği, renk kodlama biçimi ve döndürme derecesiyle birlikte arabellek veya diziyle 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
kullanarak
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 temsil edilir.
3. Resmi işleyin
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 alma
Görüntüde 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 önemli nokta koordinatlarını çerçevenin dışına atar ve onlara düşük InFrameConfidence değerleri verir.
Çerçevede hiç kimse algılanmazsa 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:
- Makine Öğrenimi Kiti'nin durumu doğru bir şekilde algılayabilmesi için resimdeki kişi yeterli piksel verisiyle; en iyi performans için kişi en az 256x256 piksel boyutunda olmalıdır.
- Gerçek zamanlı bir uygulamada poz tespit ederseniz giriş görüntülerinin genel boyutlarını da göz önünde bulundurmak isteyebilirsiniz. Daha küçük resimler daha hızlı işlenebilir. Bu nedenle, gecikmeyi azaltmak için görüntüleri düşük çözünürlüklerde yakalayın. Ancak yukarıdaki çözünürlük gereksinimlerini göz önünde bulundurun ve objenin resmi mümkün olduğunca çok doldurduğundan emin olun.
- Kötü bir resim odağı, doğruluğu da etkileyebilir. Kabul edilebilir sonuçlar alamazsanız kullanıcıdan resmi yeniden çekmesini isteyin.
Poz algılamayı gerçek zamanlı bir uygulamada kullanmak istiyorsanız en iyi kare hızlarına ulaşmak için şu yönergeleri uygulayın:
- Temel poz algılama SDK'sını ve
STREAM_MODE
kullanın. - Görüntüleri daha düşük çözünürlükte çekmeyi düşünün. Ancak bu API'nin resim boyutu şartlarını da göz önünde bulundurun.
Camera
veyacamera2
API kullanıyorsanız algılayıcıya yapılan çağrıları azaltı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ındakiVisionProcessorBase
sınıfına göz atın.CameraX
API'yi kullanıyorsanız karşı basınç stratejisinin varsayılan değerine (ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
) ayarlandığından emin olun. Bu, aynı anda yalnızca bir resmin analiz için gönderilmesini garanti eder. Analiz aracı meşgulken daha fazla görüntü üretilirse bu görüntüler otomatik olarak bırakılır ve teslim edilmek üzere sıraya alınmaz. Analiz edilen resim ImageProxy.close() çağrısı yapılarak kapatıldıktan sonra, bir sonraki son resim yayınlanır.- Algılayıcının çıkışını, giriş görüntüsünün üzerine grafik yerleştirmek için kullanırsanız önce ML Kit'ten sonucu alın, ardından görüntüyü oluşturun ve tek bir adımda bindirme yapın. Bu, her bir giriş karesi için görüntü yüzeyinde yalnızca bir kez oluşturulur. Örnek için hızlı başlangıç örnek uygulamasındaki
CameraSourcePreview
veGraphicOverlay
sınıflarına göz atın. - Camera2 API'yi kullanıyorsanız görüntüleri
ImageFormat.YUV_420_888
biçiminde çekin. Eski Kamera API'sini kullanıyorsanız görüntüleriImageFormat.NV21
biçiminde çekin.
Sonraki adımlar
- Pozları sınıflandırmak için önemli pozların nasıl kullanılacağını öğrenmek üzere Poz Sınıflandırma İpuçları'nı inceleyin.