ML Kit fornisce due SDK ottimizzati per il rilevamento delle posizioni.
Nome SDK | rilevamento della posa | rilevamento-posizione-preciso |
---|---|---|
Implementazione | Il codice e gli asset sono collegati in modo statico alla tua app al momento della creazione. | Il codice e gli asset sono collegati in modo statico alla tua app al momento della creazione. |
Impatto sulle dimensioni dell'app (inclusi codice e asset) | ~10,1 MB | ~13,3 MB |
Rendimento | Pixel 3 XL: ~30 f/s | Pixel 3 XL: circa 23 f/s con la CPU, circa 30 f/s con la GPU |
Provalo
- Prova l'app di esempio 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 in entrambe le sezionibuildscript
eallprojects
. Aggiungi le dipendenze per le librerie Android ML Kit al file gradle a livello di app del 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-beta4' // If you want to use the accurate sdk implementation 'com.google.mlkit:pose-detection-accurate:18.0.0-beta4' }
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
Il PoseDetector
funziona in due modalità di rilevamento. Assicurati di scegliere quello adatto
al tuo caso d'uso.
STREAM_MODE
(valore predefinito)- Il rilevatore della posa rileva prima la persona più in evidenza nell'immagine e poi esegue il rilevamento della posa. Nei frame successivi, la fase di rilevamento della persona non verrà eseguita, a meno che la persona non venga oscurata o non venga più rilevata con un grado di confidenza elevato. Il rilevatore della posa tenterà di tenere traccia della persona più in evidenza e riprenderà la sua posa in ogni inferenza. In questo modo si riduce la latenza e il rilevamento è più fluido. Utilizza questa modalità quando vuoi rilevare la posa in un video stream.
SINGLE_IMAGE_MODE
- Il rilevatore della posa rileva una persona e poi esegue il rilevamento della posa. Il passaggio di rilevamento persona viene eseguito per ogni immagine, quindi la latenza sarà più elevata e non è previsto il monitoraggio delle persone. Usa questa modalità quando usi il rilevamento della posa in immagini statiche o dove il tracciamento non è desiderato.
Configurazione hardware
PoseDetector
supporta più configurazioni hardware per ottimizzare le prestazioni:
CPU
: esegui il rilevatore utilizzando solo la CPUCPU_GPU
: esegui il rilevatore utilizzando CPU e 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 disponibilità, stabilità, correttezza e 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 riserva. ML Kit eseguirà questi controlli e la relativa preparazione in modo
senza blocchi prima di abilitare qualsiasi accelerazione, quindi è molto probabile che
la prima volta che l'utente esegua il rilevatore utilizzi CPU
. Al termine della preparazione, nelle esecuzioni seguenti verrà utilizzata la configurazione migliore.
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
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 sul
dispositivo. Quindi, passa l'oggetto InputImage
all'elemento
PoseDetector
.
Per il rilevamento della posa, utilizza un'immagine con dimensioni di almeno 480 x 360 pixel. Se rilevi le pose in tempo reale, acquisire fotogrammi a questa risoluzione minima può aiutare a ridurre la latenza.
Puoi creare un oggetto InputImage
da origini diverse, spiegate 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 della rotazione
automaticamente.
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 raccolta di videocamere che ti fornisce il grado di rotazione dell'immagine, puoi calcolarlo in base al grado di rotazione e all'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; }
Quindi, 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 del file
Per creare un oggetto InputImage
da un URI file, passa il contesto dell'app e l'URI del file a
InputImage.fromFilePath()
. Questo è utile quando
utilizzi un intent ACTION_GET_CONTENT
per richiedere 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 un oggetto InputImage
da un elemento ByteBuffer
o ByteArray
, devi prima calcolare 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 ad altezza, larghezza, formato di codifica del colore e 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
e da un grado 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. Ricevere informazioni sulla posa rilevata
Se viene rilevata una persona nell'immagine, l'API di rilevamento delle posizioni restituisce un oggetto Pose
con 33 PoseLandmark
.
Se la persona non è completamente all'interno dell'immagine, il modello assegna le coordinate dei punti di riferimento mancanti al di fuori dell'inquadratura e assegna loro valori InFrameConfidence bassi.
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 possa rilevare con precisione la posa, la persona nell'immagine deve essere rappresentata da un numero sufficiente di dati di pixel; per prestazioni ottimali, il soggetto deve essere di almeno 256 x 256 pixel.
- Se rilevi la posa in un'applicazione in tempo reale, potresti considerare anche le dimensioni complessive delle immagini di input. Le immagini più piccole possono essere elaborate più rapidamente, quindi per ridurre la latenza, acquisisci immagini a risoluzioni più basse, ma tieni presenti i requisiti di risoluzione di cui sopra e assicurati che il soggetto occupi il più possibile l'immagine.
- Anche una scarsa messa a fuoco dell'immagine può influire sulla precisione. Se non ottieni risultati accettabili, chiedi all'utente di recuperare l'immagine.
Se vuoi utilizzare il rilevamento della posa in un'applicazione in tempo reale, segui queste linee guida per ottenere le migliori frequenze di fotogrammi:
- 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 per le dimensioni immagine di questa API.
- Se usi l'API
Camera
ocamera2
, limita le chiamate al rilevatore. Se un nuovo fotogramma video diventa disponibile mentre il rilevatore è in esecuzione, eliminalo. Per un esempio, vedi la classeVisionProcessorBase
nell'app di esempio della guida rapida. - Se utilizzi l'API
CameraX
, assicurati che la strategia di contropressione sia impostata sul valore predefinitoImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
. Ciò garantisce che verrà pubblicata una sola immagine alla volta per l'analisi. Se vengono prodotte altre immagini quando l'analizzatore è occupato, queste verranno eliminate automaticamente e non verranno messe in coda per la distribuzione. Dopo aver chiuso l'immagine analizzata chiamando ImageProxy.close(), verrà pubblicata l'immagine più recente. - Se utilizzi l'output del rilevatore per sovrapporre elementi grafici
all'immagine di input, ottieni il risultato da ML Kit, quindi esegui il rendering dell'immagine
e della sovrapposizione in un unico passaggio. Il rendering viene eseguito sulla superficie di visualizzazione
una sola volta per ogni frame di input. Per un esempio, vedi le classi
CameraSourcePreview
eGraphicOverlay
nell'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 per le pose per classificare le pose, consulta i Suggerimenti per la classificazione delle posizioni.