Puoi utilizzare ML Kit per rilevare i volti in immagini e video simili a selfie.
| API Face Mesh Detection | |
|---|---|
| Nome SDK | face-mesh-detection |
| Implementazione | Il codice e gli asset sono collegati staticamente alla tua app in fase di compilazione. |
| Impatto sulle dimensioni dell'app | ~6,4 MB |
| Rendimento | In tempo reale sulla maggior parte dei dispositivi. |
Prova
- Prova a giocare con l'app di esempio per vedere un utilizzo di esempio di questa API.
Prima di iniziare
Nel file
build.gradlea livello di progetto, assicurati di includere il repository Maven di Google nelle sezioni `buildscript` e `allprojects`.Aggiungi la dipendenza per la libreria di rilevamento di mesh facciali di ML Kit al 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 le immagini di input
Le immagini devono essere scattate a una distanza di circa 2 metri dalla fotocamera del dispositivo, in modo che i volti siano sufficientemente grandi per un riconoscimento ottimale della mesh facciale. In generale, più grande è il volto, migliore è il riconoscimento della mesh facciale.
Il volto deve essere rivolto verso la fotocamera con almeno metà del volto visibile. Qualsiasi oggetto di grandi dimensioni tra il volto e la fotocamera può comportare una minore precisione.
Se vuoi rilevare i volti in un'applicazione in tempo reale, devi anche considerare le dimensioni complessive dell'immagine di input. Le immagini più piccole possono essere elaborate più velocemente, quindi l'acquisizione di immagini a risoluzioni inferiori riduce la latenza. Tuttavia, tieni presente i requisiti di precisione sopra indicati e assicurati che il volto del soggetto occupi la maggior parte possibile dell'immagine.
Configurare il rilevatore di mesh facciali
Se vuoi modificare una delle impostazioni predefinite del rilevatore di mesh facciali, specifica queste impostazioni con un FaceMeshDetectorOptions FaceMeshDetectorOptions. Puoi modificare le seguenti impostazioni:
setUseCaseBOUNDING_BOX_ONLY: fornisce solo un riquadro di delimitazione per una mesh facciale rilevata. Questo è il rilevatore di volti più veloce, ma ha una limitazione di intervallo(i volti devono trovarsi a circa 2 metri dalla fotocamera).FACE_MESH(opzione predefinita): fornisce un riquadro di delimitazione e informazioni aggiuntive sulla mesh facciale (468 punti 3D e informazioni sui triangoli). Rispetto al caso d'usoBOUNDING_BOX_ONLY, la latenza aumenta di circa il 15%, come misurato su 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() );
Preparare 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.
Poi, passa l'oggetto InputImage al metodo process di FaceDetector.
Per il rilevamento della mesh facciale, devi utilizzare un'immagine con dimensioni di almeno 480 x 360 pixel. Se rilevi i volti in tempo reale, l'acquisizione di frame a questa risoluzione minima può contribuire a ridurre la latenza.
Puoi creare un InputImage
oggetto da diverse origini, ognuna delle quali è descritta di seguito.
Utilizzare 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
CameraX library, 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 di fotocamere 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);
Utilizzare un URI del file
Per creare un InputImage
oggetto da un URI del file, passa il contesto dell'app e l'URI del file a
InputImage.fromFilePath(). Questa opzione è 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(); }
Utilizzare un ByteBuffer o ByteArray
Per creare un InputImage
oggetto 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 );
Utilizzare 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.
Elaborare l'immagine
Passa 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 // … } });
Ottenere informazioni sulla mesh facciale rilevata
Se viene rilevato un volto nell'immagine, all'ascoltatore di successo viene passata una lista di oggetti FaceMesh. Ogni FaceMesh rappresenta un volto rilevato nell'immagine. Per ogni mesh facciale, puoi ottenere le coordinate di delimitazione nell'immagine di input, nonché qualsiasi altra informazione che hai configurato per trovare il rilevatore di mesh facciali.
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(); } }