Puoi utilizzare ML Kit per rilevare i volti nelle immagini e nei video in stile selfie.
API Face Mesh Detection | |
---|---|
Nome SDK | face-mesh-detection |
Implementazione | Il codice e gli asset sono collegati in modo statico alla tua app al momento della creazione. |
Impatto sulle dimensioni dell'app | ~6,4 MB |
Prestazioni | In tempo reale sulla maggior parte dei dispositivi. |
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 la proprietà Repository Maven nelle sezioni buildscript e allprojects.Aggiungi la dipendenza per la libreria di rilevamento del mesh di volti di ML Kit al tuo file gradle a livello di app del modulo, che in genere è
app/build.gradle
:dependencies { // ... implementation 'com.google.mlkit:face-mesh-detection:16.0.0-beta1' }
Linee guida per l'immagine di input
Le immagini devono essere scattate a una distanza massima di 2 metri dalla fotocamera del dispositivo, quindi in modo che i volti siano sufficientemente grandi da consentire un riconoscimento mesh dei volti ottimale. Nella In generale, più è grande il volto, migliore sarà il riconoscimento del mesh di volti.
Il volto deve essere rivolto verso la videocamera con almeno metà del volto visibile. Qualsiasi oggetto di grandi dimensioni tra il volto e la videocamera potrebbe abbassare la precisione.
Se desideri rilevare i volti in un'applicazione in tempo reale, devi considera le dimensioni complessive dell'immagine di input. Le immagini più piccole possono essere elaborati più velocemente, quindi acquisire immagini a risoluzioni più basse riduce la latenza. Tuttavia, tieni presente i requisiti di accuratezza illustrati sopra e assicurati che il volto del soggetto occupa la maggior quantità possibile dell'immagine.
Configura il rilevatore di mesh per il volto
Se vuoi modificare una delle impostazioni predefinite del rilevatore di mesh di volti, specifica queste impostazioni con FaceMeshDetectorOptions . Puoi modificare le seguenti impostazioni:
setUseCase
BOUNDING_BOX_ONLY
: fornisce un riquadro di delimitazione solo per il mesh di volti rilevati. È il rilevatore di volti più veloce, ma ha una portata limitata(volti) deve essere a una distanza massima di 2 metri dalla videocamera).FACE_MESH
(opzione predefinita): fornisce un riquadro di delimitazione e un volto aggiuntivo. informazioni mesh (468 punti 3D e informazioni triangolari). Rispetto al caso d'uso diBOUNDING_BOX_ONLY
, la latenza aumenta di circa il 15%, misurata Pixel 3,
Ad esempio:
Kotlin
val defaultDetector = FaceMeshDetection.getClient( FaceMeshDetectorOptions.DEFAULT_OPTIONS) val boundingBoxDetector = FaceMeshDetection.getClient( FaceMeshDetectorOptions.Builder() .setUseCase(UseCase.BOUNDING_BOX_ONLY) .build() )
Java
FaceMeshDetector defaultDetector = FaceMeshDetection.getClient( FaceMeshDetectorOptions.DEFAULT_OPTIONS); FaceMeshDetector boundingBoxDetector = FaceMeshDetection.getClient( new FaceMeshDetectorOptions.Builder() .setUseCase(UseCase.BOUNDING_BOX_ONLY) .build() );
Prepara l'immagine di input
Per rilevare i volti in un'immagine, crea un oggetto InputImage
da un
Bitmap
, media.Image
, ByteBuffer
, array di byte o un file sul dispositivo.
Quindi, passa l'oggetto InputImage
al metodo process
di FaceDetector
.
Per il rilevamento della mesh di volti, devi utilizzare un'immagine con dimensioni di almeno 480 x 360 pixel. Se rilevi i volti in tempo reale, acquisisci i fotogrammi a questa risoluzione minima può aiutare a ridurre la latenza.
Puoi creare una InputImage
da diverse origini, ciascuna è spiegata di seguito.
Utilizzo di un media.Image
Per creare una InputImage
da un oggetto media.Image
, ad esempio quando acquisisci un'immagine da un
fotocamera del dispositivo, passa l'oggetto media.Image
e la
rotazione in InputImage.fromMediaImage()
.
Se utilizzi
nella libreria di CameraX, OnImageCapturedListener
e
ImageAnalysis.Analyzer
classi 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 raccolta di videocamere che fornisce il grado di rotazione dell'immagine, può calcolarlo in base al grado di rotazione e all'orientamento 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
valore del grado di rotazione su InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Utilizzo di un URI del file
Per creare una InputImage
da un URI file, passa il contesto dell'app e l'URI del 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 una InputImage
oggetto 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.
Elabora l'immagine
Trasferisci l'immagine al metodo process
:
Kotlin
val result = detector.process(image) .addOnSuccessListener { result -> // Task completed successfully // … } .addOnFailureListener { e -> // Task failed with an exception // … }
Java
Task<List<FaceMesh>> result = detector.process(image) .addOnSuccessListener( new OnSuccessListener<List<FaceMesh>>() { @Override public void onSuccess(List<FaceMesh> result) { // Task completed successfully // … } }) .addOnFailureListener( new OnFailureListener() { @Override Public void onFailure(Exception e) { // Task failed with an exception // … } });
Ricevere informazioni sul mesh di volti rilevato
Se viene rilevato un volto nell'immagine, viene passato un elenco di FaceMesh
oggetti a
chi ascolta il successo. Ogni FaceMesh
rappresenta un volto rilevato nell'
dell'immagine. Per ogni mesh di volti, puoi ottenere le coordinate di delimitazione nell'input
immagine, nonché qualsiasi altra informazione configurata per il mesh del volto
per la ricerca.
Kotlin
for (faceMesh in faceMeshs) { val bounds: Rect = faceMesh.boundingBox() // Gets all points val faceMeshpoints = faceMesh.allPoints for (faceMeshpoint in faceMeshpoints) { val index: Int = faceMeshpoints.index() val position = faceMeshpoint.position } // Gets triangle info val triangles: List<Triangle<FaceMeshPoint>> = faceMesh.allTriangles for (triangle in triangles) { // 3 Points connecting to each other and representing a triangle area. val connectedPoints = triangle.allPoints() } }
Java
for (FaceMesh faceMesh : faceMeshs) { Rect bounds = faceMesh.getBoundingBox(); // Gets all points List<FaceMeshPoint> faceMeshpoints = faceMesh.getAllPoints(); for (FaceMeshPoint faceMeshpoint : faceMeshpoints) { int index = faceMeshpoints.getIndex(); PointF3D position = faceMeshpoint.getPosition(); } // Gets triangle info List<Triangle<FaceMeshPoint>> triangles = faceMesh.getAllTriangles(); for (Triangle<FaceMeshPoint> triangle : triangles) { // 3 Points connecting to each other and representing a triangle area. List<FaceMeshPoint> connectedPoints = triangle.getAllPoints(); } }