ML Kit fornisce due SDK ottimizzati per il rilevamento della posa.
Nome SDK | pose-detection | pose-detection-accurate |
---|---|---|
Implementazione | Il codice e gli asset vengono collegati in modo statico all'app in fase di compilazione. | Il codice e gli asset vengono collegati in modo statico all'app in fase di compilazione. |
Impatto sulle dimensioni dell'app (inclusi codice e asset) | ~10,1 MB | ~13,3 MB |
Prestazioni | Pixel 3 XL: ~30 f/s | Pixel 3 XL: circa 23 f/s con la CPU, circa 30 f/s con la GPU |
Prova
- Prova l'app di esempio per per vedere un esempio di utilizzo di questa API.
Prima di iniziare
- Nel file
build.gradle
a livello di progetto, assicurati di includere il repository Maven di Google sia nelle sezionibuildscript
cheallprojects
. Aggiungi le dipendenze per le librerie Android di ML Kit al file Gradle a livello di app del tuo modulo, che in genere è
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
o specificare le impostazioni del rilevatore.
Modalità di rilevamento
PoseDetector
opera in due modalità di rilevamento. Assicurati di scegliere quello che corrisponde
il tuo caso d'uso.
STREAM_MODE
(valore predefinito)- Il rilevatore di pose rileva prima la persona più prominente nell'immagine e poi esegue il rilevamento della posa. Nei frame successivi, il passaggio di rilevamento delle persone non verrà eseguito a meno che la persona non venga obscured o non venga più rilevata con un'elevata confidenza. Il rilevatore di pose tenterà di monitorare la persona più in evidenza e restituirà la sua posa in ogni deduzione. In questo modo si riduce la latenza e il rilevamento è più fluido. Usa questa modalità quando quando vuoi rilevare la posa in un video stream.
SINGLE_IMAGE_MODE
- Il rilevatore della posa rileva una persona e poi esegue la posa il rilevamento automatico. La fase di rilevamento della persona viene eseguita per ogni immagine, quindi la latenza più alta e non c'è il tracciamento delle persone. Utilizza questa modalità quando usi il rilevamento delle pose su immagini statiche o quando non è necessario il monitoraggio.
Configurazione hardware
PoseDetector
supporta più configurazioni hardware per l'ottimizzazione
rendimento:
CPU
: esegui il rilevamento utilizzando solo la CPUCPU_GPU
: esegui il rilevamento utilizzando sia la CPU che la GPU
Quando crei le opzioni del rilevatore, puoi usare l'API
setPreferredHardwareConfigs
per controllare la selezione 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 le configurazioni preferite. Se nessuna delle configurazioni preferite è applicabile, la configurazione CPU
verrà utilizzata automaticamente come alternativa. ML Kit eseguirà questi controlli e la relativa preparazione in una
modo non bloccante prima di abilitare qualsiasi accelerazione, quindi è molto probabile
La prima volta che l'utente esegue il rilevatore, utilizzerà CPU
. Al termine di tutte le operazioni di 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 nessuna accelerazione, trasmetti solo
CPU
. - Se vuoi utilizzare la GPU per non sovraccaricare la CPU anche se la GPU potrebbe essere più lenta, passa
solo in
CPU_GPU
.
Specifica le opzioni del rilevatore della posa:
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
. Trasmetti le opzioni che hai specificato:
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 array di byte Bitmap
, media.Image
, ByteBuffer
, o da un file
del dispositivo. Quindi, passa l'oggetto InputImage
a PoseDetector
.
Per il rilevamento della posa, devi utilizzare un'immagine con dimensioni di almeno 480 x 360 pixel. Se stai rilevando le pose in tempo reale, acquisire fotogrammi con questa risoluzione minima può contribuire a ridurre la latenza.
Puoi creare un oggetto InputImage
da diverse origini, ognuna delle quali è descritta 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 fornisca il grado di rotazione dell'immagine, puoi calcolarlo dal grado di rotazione del dispositivo e dall'orientamento del sensore della fotocamera al suo interno:
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()
. È utile quando
utilizza un intent ACTION_GET_CONTENT
per chiedere all'utente di selezionare
un'immagine dall'app 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(); }
Con ByteBuffer
o ByteArray
Per creare una InputImage
oggetto da un valore ByteBuffer
o ByteArray
, prima calcola l'immagine
grado di rotazione come descritto in precedenza per l'input media.Image
.
Quindi, crea l'oggetto InputImage
con il buffer o l'array, insieme al campo
altezza, larghezza, formato di codifica del colore e grado di rotazione:
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
, esegui 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. Visualizzare le informazioni sulla posa rilevata
Se nell'immagine viene rilevata una persona, l'API di rilevamento della posizione 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 al di fuori dell'inquadratura e li mostra Valori di InFrameConfidence.
Se non è stata rilevata alcuna persona nell'inquadratura, 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é il kit ML rilevi accuratamente la posa, la persona visibile nell'immagine deve essere rappresentati da una quantità sufficiente di dati di pixel; per ottenere le migliori prestazioni, l'oggetto essere di almeno 256 x 256 pixel.
- Se rilevi la posa in un'applicazione in tempo reale, valuta anche le dimensioni complessive delle immagini di input. Le immagini più piccole possono essere elaborate più velocemente. Per ridurre la latenza, acquisire immagini a risoluzioni più basse senza rinunciare tieni conto dei requisiti di risoluzione di cui sopra e assicurati che l'argomento rispetti i la maggior parte dell'immagine.
- Anche la scarsa messa a fuoco delle immagini può influire sull'accuratezza. Se non ottieni risultati accettabili, invita l'utente a acquisire di nuovo l'immagine.
Se vuoi utilizzare il rilevamento delle pose in un'applicazione in tempo reale, segui queste linee guida per ottenere le frequenze frame migliori:
- Utilizza l'SDK di base per il rilevamento della posizione e
STREAM_MODE
. - Prova ad acquisire immagini a una risoluzione inferiore. Tuttavia, tieni presente anche i requisiti relativi alle dimensioni delle immagini di questa API.
- Se utilizzi
Camera
oppure APIcamera2
, limitare le chiamate al rilevatore. Se viene pubblicato un nuovo video il frame diventa disponibile mentre il rilevatore è in esecuzione. Per un esempio, consulta la classeVisionProcessorBase
nell'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 garantito che venga inviata una sola immagine per l'analisi alla volta. Se vengono prodotte altre immagini quando l'analizzatore è occupato, queste verranno eliminate automaticamente e non verranno messe in coda per l'invio. Dopo aver chiuso l'immagine da analizzare richiamando ImageProxy.close(), verrà pubblicata l'immagine successiva più recente. - Se utilizzi l'output del rilevatore per sovrapporre elementi grafici
l'immagine di input, occorre prima ottenere il risultato da ML Kit, quindi eseguire il rendering dell'immagine
e la sovrapposizione in un solo passaggio. Viene visualizzato sulla superficie di visualizzazione
solo una volta per ogni frame di input. Per un esempio, consulta le classi
CameraSourcePreview
eGraphicOverlay
nell'app di esempio della guida di avvio rapido. - Se utilizzi l'API Camera2, acquisisci le immagini in formato
ImageFormat.YUV_420_888
. Se utilizzi la precedente API Camera, acquisisci le immagini in formatoImageFormat.NV21
.
Passaggi successivi
- Per scoprire come utilizzare i landmark delle pose per classificarle, consulta Suggerimenti per la classificazione delle pose.