ML Kit fornisce due SDK ottimizzati per il rilevamento della postura.
| Nome SDK | pose-detection | pose-detection-accurate |
|---|---|---|
| Implementazione | Il codice e gli asset sono collegati staticamente alla tua app al momento della creazione. | Il codice e gli asset sono collegati staticamente alla tua app al momento della creazione. |
| Impatto sulle dimensioni dell'app (inclusi codice e asset) | ~10,1 MB | ~13,3 MB |
| Prestazioni | Pixel 3XL: ~30FPS | Pixel 3XL: ~23 f/s con CPU, ~30 f/s con GPU |
Prova
- Prova l'app di esempio per vedere un esempio di utilizzo di questa API.
Prima di iniziare
- Nel file
build.gradlea livello di progetto, assicurati di includere il repository Maven di Google nelle sezionibuildscripteallprojects. Aggiungi le dipendenze per le librerie Android di ML Kit al file gradle a livello di app del tuo modulo, che di solito è
app/build.gradle: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. Crea un'istanza di PoseDetector
PoseDetector opzioni
Per rilevare una posa in un'immagine, crea prima un'istanza di PoseDetector e, facoltativamente, specifica le impostazioni del rilevatore.
Modalità di rilevamento
PoseDetector opera in due modalità di rilevamento. Assicurati di scegliere quella che corrisponde al tuo caso d'uso.
STREAM_MODE(valore predefinito)- Il rilevatore di pose rileverà prima la persona più in evidenza nell'immagine, quindi eseguirà il rilevamento della posa. Nei frame successivi, il passaggio di rilevamento persone non verrà eseguito, a meno che la persona non venga oscurata o non venga più rilevata con un livello di confidenza elevato. Il rilevatore di pose tenterà di monitorare la persona più in evidenza e restituirà la sua posa in ogni inferenza. In questo modo si riduce la latenza e si semplifica il rilevamento. Utilizza questa modalità quando vuoi rilevare la posa in uno stream video.
SINGLE_IMAGE_MODE- Il rilevatore di posizioni rileva una persona e poi esegue il rilevamento della posizione. Il passaggio di rilevamento persone verrà eseguito per ogni immagine, quindi la latenza sarà maggiore e non è previsto il monitoraggio delle persone. Utilizza questa modalità quando usi il rilevamento della posa su immagini statiche o quando non vuoi il monitoraggio.
Configurazione hardware
PoseDetector supporta più configurazioni hardware per ottimizzare le prestazioni:
CPU: esegui il rilevatore utilizzando solo la CPUCPU_GPU: esegui il rilevatore utilizzando sia la CPU che la GPU
Quando crei le opzioni del rilevatore, puoi utilizzare l'API
setPreferredHardwareConfigs per controllare la selezione dell'hardware. Per impostazione predefinita, tutte le configurazioni hardware sono impostate come preferite.
ML Kit prenderà in considerazione la disponibilità, la stabilità, la correttezza e la latenza di ogni configurazione e sceglierà la migliore tra quelle preferite. Se nessuna delle configurazioni preferite è applicabile, verrà utilizzata automaticamente la configurazione CPU come riserva. ML Kit eseguirà questi controlli e la relativa preparazione in modo non bloccante prima di attivare l'accelerazione, quindi è molto probabile che la prima volta che l'utente esegue il rilevatore, utilizzerà CPU. Al termine di tutta la preparazione, la configurazione migliore verrà utilizzata nelle esecuzioni successive.
Esempi di utilizzo di setPreferredHardwareConfigs:
- Per consentire a ML Kit di scegliere la configurazione migliore, non chiamare questa API.
- Se non vuoi attivare alcuna accelerazione, passa solo
CPU. - Se vuoi utilizzare la GPU per l'offload della CPU anche se la GPU potrebbe essere più lenta, passa solo
CPU_GPU.
Specifica le opzioni del rilevatore di pose:
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();
Infine, crea un'istanza di PoseDetector. Passa le opzioni specificate:
Kotlin
val poseDetector = PoseDetection.getClient(options)
Java
PoseDetector poseDetector = PoseDetection.getClient(options);
2. Prepara l'immagine di input
Per rilevare le pose in un'immagine, crea un oggetto InputImage da un Bitmap, media.Image, ByteBuffer, un array di byte o un file sul dispositivo. Poi, passa l'oggetto InputImage a
PoseDetector.
Per il rilevamento della posa, devi utilizzare un'immagine con dimensioni di almeno 480 x 360 pixel. Se rilevi le pose in tempo reale, l'acquisizione dei frame con questa risoluzione minima può aiutare a ridurre la latenza.
Puoi creare un oggetto InputImage da diverse origini, ognuna delle quali è spiegata di seguito.
Utilizzo di un media.Image
Per creare un oggetto InputImage da un oggetto media.Image, ad esempio quando acquisisci un'immagine dalla fotocamera di un dispositivo, passa l'oggetto media.Image e la rotazione dell'immagine a InputImage.fromMediaImage().
Se utilizzi la libreria
CameraX, le classi OnImageCapturedListener e ImageAnalysis.Analyzer calcolano il valore di rotazione per te.
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 // ... } } }
Se non utilizzi una libreria della fotocamera che ti fornisce il grado di rotazione dell'immagine, puoi calcolarlo dal grado di rotazione del dispositivo e dall'orientamento del sensore della fotocamera nel dispositivo:
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; }
Poi, passa l'oggetto media.Image e il valore del grado di rotazione a InputImage.fromMediaImage():
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Utilizzo di un URI file
Per creare un oggetto InputImage da un URI file, passa il contesto dell'app e l'URI file a InputImage.fromFilePath(). Questo è utile quando utilizzi un intent ACTION_GET_CONTENT per chiedere all'utente di selezionare un'immagine dalla sua app di galleria.
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(); }
Utilizzo di un ByteBuffer o ByteArray
Per creare un oggetto InputImage da un ByteBuffer o un ByteArray, calcola prima il grado di rotazione dell'immagine come descritto in precedenza per l'input media.Image.
Poi, crea l'oggetto InputImage con il buffer o l'array, insieme all'altezza, alla larghezza, al formato di codifica dei colori e al grado di rotazione dell'immagine:
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 );
Utilizzo di un Bitmap
Per creare un oggetto InputImage da un oggetto Bitmap, effettua la seguente dichiarazione:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
L'immagine è rappresentata da un oggetto Bitmap insieme ai gradi di rotazione.
3. Elabora l'immagine
Passa l'oggetto InputImage preparato al metodo process di PoseDetector.
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. Ottieni informazioni sulla posa rilevata
Se nell'immagine viene rilevata una persona, l'API di rilevamento della posa restituisce un oggetto Pose con 33 PoseLandmark.
Se la persona non era completamente all'interno dell'immagine, il modello assegna le coordinate dei punti di riferimento mancanti all'esterno del frame e assegna loro valori InFrameConfidence bassi.
Se nel frame non è stata rilevata alcuna persona, l'oggetto Pose non contiene PoseLandmark.
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);
Suggerimenti per migliorare il rendimento
La qualità dei risultati dipende dalla qualità dell'immagine di input:
- Affinché ML Kit rilevi con precisione la posa, la persona nell'immagine deve essere rappresentata da dati di pixel sufficienti. Per un rendimento ottimale, il soggetto deve essere di almeno 256 x 256 pixel.
- Se rilevi la posa in un'applicazione in tempo reale, ti consigliamo di considerare anche le dimensioni complessive delle immagini di input. Le immagini più piccole possono essere elaborate più velocemente, quindi per ridurre la latenza, acquisisci immagini a risoluzioni inferiori, ma tieni presente i requisiti di risoluzione sopra indicati e assicurati che il soggetto occupi la maggior parte possibile dell'immagine.
- Anche una messa a fuoco scarsa dell'immagine può influire sull'accuratezza. Se non ottieni risultati accettabili, chiedi all'utente di acquisire nuovamente l'immagine.
Se vuoi utilizzare il rilevamento delle pose in un'applicazione in tempo reale, segui queste linee guida per ottenere i migliori frame rate:
- Utilizza l'SDK di base per il rilevamento della posa e
STREAM_MODE. - Valuta la possibilità di acquisire immagini a una risoluzione inferiore. Tuttavia, tieni presente anche i requisiti di dimensione delle immagini di questa API.
- Se utilizzi l'API
Cameraocamera2, limita le chiamate al rilevatore. Se un nuovo frame video diventa disponibile mentre il rilevatore è in esecuzione, elimina il frame. Per un esempio, consulta la classeVisionProcessorBasenell'app di esempio della guida rapida. - Se utilizzi l'API
CameraX, assicurati che la strategia di backpressure sia impostata sul valore predefinitoImageAnalysis.STRATEGY_KEEP_ONLY_LATEST. In questo modo, viene inviata per l'analisi una sola immagine alla volta. Se vengono prodotte più immagini quando l'analizzatore è occupato, queste verranno eliminate automaticamente e non verranno messe in coda per la consegna. Una volta chiusa l'immagine analizzata chiamando ImageProxy.close(), verrà visualizzata l'immagine successiva più recente. - Se utilizzi l'output del rilevatore per sovrapporre elementi grafici all'immagine di input, ottieni prima il risultato da ML Kit, poi esegui il rendering dell'immagine e della sovrapposizione in un unico passaggio. Il rendering sulla superficie di visualizzazione viene eseguito una sola volta per ogni frame di input. Per un esempio, consulta le classi
CameraSourcePrevieweGraphicOverlaynell'app di esempio della guida rapida. - Se utilizzi l'API Camera2, acquisisci le immagini in formato
ImageFormat.YUV_420_888. Se utilizzi l'API Camera precedente, acquisisci le immagini in formatoImageFormat.NV21.
Passaggi successivi
- Per scoprire come utilizzare i punti di riferimento delle pose per classificarle, consulta Suggerimenti per la classificazione delle pose.