Puoi utilizzare ML Kit per rilevare i volti nelle immagini e nei video.
Prima di iniziare
- Se non l'hai già fatto, aggiungi Firebase al tuo progetto Android .
- Aggiungi le dipendenze per le librerie ML Kit Android al tuo modulo (a livello di app) File Gradle (di solito
app/build.gradle
):apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' dependencies { // ... implementation 'com.google.firebase:firebase-ml-vision:24.0.3' // If you want to detect face contours (landmark detection and classification // don't require this additional model): implementation 'com.google.firebase:firebase-ml-vision-face-model:20.0.1' }
- Facoltativo ma consigliato : configura la tua app per scaricare automaticamente il modello ML sul dispositivo dopo che l'app è stata installata dal Play Store.
A tale scopo, aggiungi la seguente dichiarazione al file
AndroidManifest.xml
della tua app:<application ...> ... <meta-data android:name="com.google.firebase.ml.vision.DEPENDENCIES" android:value="face" /> <!-- To use multiple models: android:value="face,model2,model3" --> </application>
Se non si abilitano i download del modello in fase di installazione, il modello verrà scaricato la prima volta che si esegue il rilevatore. Le richieste effettuate prima del completamento del download non produrranno risultati.
Linee guida per l'immagine di input
Affinché ML Kit rilevi accuratamente i volti, le immagini di input devono contenere volti rappresentati da dati pixel sufficienti. In generale, ogni volto che desideri rilevare in un'immagine deve essere di almeno 100x100 pixel. Se si desidera rilevare i contorni dei volti, ML Kit richiede un input di risoluzione più elevato: ogni volto deve essere di almeno 200x200 pixel.
Se stai rilevando i volti in un'applicazione in tempo reale, potresti anche voler considerare le dimensioni complessive delle immagini di input. Le immagini più piccole possono essere elaborate più velocemente, quindi per ridurre la latenza, acquisire immagini a risoluzioni inferiori (tenendo presente i requisiti di precisione di cui sopra) e assicurarsi che il viso del soggetto occupi la maggior parte dell'immagine possibile. Vedi anche Suggerimenti per migliorare le prestazioni in tempo reale .
Una scarsa messa a fuoco dell'immagine può compromettere la precisione. Se non ottieni risultati accettabili, prova a chiedere all'utente di acquisire nuovamente l'immagine.
L'orientamento di un viso rispetto alla fotocamera può anche influenzare le caratteristiche del viso rilevate da ML Kit. Vedere Concetti di rilevamento dei volti .
1. Configurare il rilevatore facciale
Prima di applicare il rilevamento del volto a un'immagine, se si desidera modificare una delle impostazioni predefinite del rilevatore di volti, specificare tali impostazioni con un oggettoFirebaseVisionFaceDetectorOptions
. È possibile modificare le seguenti impostazioni:Impostazioni | |
---|---|
Modalità performante | FAST (predefinito) | ACCURATE Favorisci velocità o precisione quando rilevi i volti. |
Rileva i punti di riferimento | NO_LANDMARKS (predefinito) | ALL_LANDMARKS Se tentare di identificare i "punti di riferimento" facciali: occhi, orecchie, naso, guance, bocca e così via. |
Rileva i contorni | NO_CONTOURS (predefinito) | ALL_CONTOURS Se rilevare i contorni dei lineamenti del viso. I contorni vengono rilevati solo per il viso più prominente in un'immagine. |
Classifica i volti | NO_CLASSIFICATIONS (predefinito) | ALL_CLASSIFICATIONS Se classificare o meno i volti in categorie come "sorridenti" e "occhi aperti". |
Dimensione minima del viso | float (predefinito: 0.1f )La dimensione minima, relativa all'immagine, dei volti da rilevare. |
Abilita il rilevamento del volto | false (predefinito) | true Se assegnare o meno ai volti un ID, che può essere utilizzato per tracciare i volti attraverso le immagini. Si noti che quando il rilevamento del contorno è abilitato, viene rilevata solo una faccia, quindi il rilevamento della faccia non produce risultati utili. Per questo motivo, e per migliorare la velocità di rilevamento, non abilitare sia il rilevamento del contorno che il rilevamento del volto. |
Per esempio:
Java
// High-accuracy landmark detection and face classification FirebaseVisionFaceDetectorOptions highAccuracyOpts = new FirebaseVisionFaceDetectorOptions.Builder() .setPerformanceMode(FirebaseVisionFaceDetectorOptions.ACCURATE) .setLandmarkMode(FirebaseVisionFaceDetectorOptions.ALL_LANDMARKS) .setClassificationMode(FirebaseVisionFaceDetectorOptions.ALL_CLASSIFICATIONS) .build(); // Real-time contour detection of multiple faces FirebaseVisionFaceDetectorOptions realTimeOpts = new FirebaseVisionFaceDetectorOptions.Builder() .setContourMode(FirebaseVisionFaceDetectorOptions.ALL_CONTOURS) .build();
Kotlin+KTX
// High-accuracy landmark detection and face classification val highAccuracyOpts = FirebaseVisionFaceDetectorOptions.Builder() .setPerformanceMode(FirebaseVisionFaceDetectorOptions.ACCURATE) .setLandmarkMode(FirebaseVisionFaceDetectorOptions.ALL_LANDMARKS) .setClassificationMode(FirebaseVisionFaceDetectorOptions.ALL_CLASSIFICATIONS) .build() // Real-time contour detection of multiple faces val realTimeOpts = FirebaseVisionFaceDetectorOptions.Builder() .setContourMode(FirebaseVisionFaceDetectorOptions.ALL_CONTOURS) .build()
2. Avviare il rilevatore facciale
Per rilevare i volti in un'immagine, crea un oggettoFirebaseVisionImage
da Bitmap
, media.Image
, ByteBuffer
, array di byte o un file sul dispositivo. Quindi, passa l'oggetto FirebaseVisionImage
al metodo detectInImage
di FirebaseVisionFaceDetector
.Per il riconoscimento facciale, dovresti utilizzare un'immagine con dimensioni di almeno 480x360 pixel. Se stai riconoscendo i volti in tempo reale, l'acquisizione di fotogrammi a questa risoluzione minima può aiutare a ridurre la latenza.
Crea un oggetto
FirebaseVisionImage
dalla tua immagine.Per creare un oggetto
FirebaseVisionImage
da un oggettomedia.Image
, ad esempio quando si acquisisce un'immagine dalla fotocamera di un dispositivo, passare l'oggettomedia.Image
e la rotazione dell'immagine aFirebaseVisionImage.fromMediaImage()
.Se utilizzi la libreria CameraX , le classi
OnImageCapturedListener
eImageAnalysis.Analyzer
calcolano il valore di rotazione per te, quindi devi solo convertire la rotazione in una delle costantiROTATION_
di ML Kit prima di chiamareFirebaseVisionImage.fromMediaImage()
:Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { private int degreesToFirebaseRotation(int degrees) { switch (degrees) { case 0: return FirebaseVisionImageMetadata.ROTATION_0; case 90: return FirebaseVisionImageMetadata.ROTATION_90; case 180: return FirebaseVisionImageMetadata.ROTATION_180; case 270: return FirebaseVisionImageMetadata.ROTATION_270; default: throw new IllegalArgumentException( "Rotation must be 0, 90, 180, or 270."); } } @Override public void analyze(ImageProxy imageProxy, int degrees) { if (imageProxy == null || imageProxy.getImage() == null) { return; } Image mediaImage = imageProxy.getImage(); int rotation = degreesToFirebaseRotation(degrees); FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation); // Pass image to an ML Kit Vision API // ... } }
Kotlin+KTX
private class YourImageAnalyzer : ImageAnalysis.Analyzer { private fun degreesToFirebaseRotation(degrees: Int): Int = when(degrees) { 0 -> FirebaseVisionImageMetadata.ROTATION_0 90 -> FirebaseVisionImageMetadata.ROTATION_90 180 -> FirebaseVisionImageMetadata.ROTATION_180 270 -> FirebaseVisionImageMetadata.ROTATION_270 else -> throw Exception("Rotation must be 0, 90, 180, or 270.") } override fun analyze(imageProxy: ImageProxy?, degrees: Int) { val mediaImage = imageProxy?.image val imageRotation = degreesToFirebaseRotation(degrees) if (mediaImage != null) { val image = FirebaseVisionImage.fromMediaImage(mediaImage, imageRotation) // Pass image to an ML Kit Vision API // ... } } }
Se non utilizzi una libreria della fotocamera che ti fornisce la rotazione dell'immagine, puoi calcolarla dalla rotazione del dispositivo e dall'orientamento del sensore della fotocamera nel dispositivo:
Java
private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } /** * 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, Context context) 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); // On most devices, the sensor orientation is 90 degrees, but for some // devices it is 270 degrees. For devices with a sensor orientation of // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees. CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360; // Return the corresponding FirebaseVisionImageMetadata rotation value. int result; switch (rotationCompensation) { case 0: result = FirebaseVisionImageMetadata.ROTATION_0; break; case 90: result = FirebaseVisionImageMetadata.ROTATION_90; break; case 180: result = FirebaseVisionImageMetadata.ROTATION_180; break; case 270: result = FirebaseVisionImageMetadata.ROTATION_270; break; default: result = FirebaseVisionImageMetadata.ROTATION_0; Log.e(TAG, "Bad rotation value: " + rotationCompensation); } return result; }
Kotlin+KTX
private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 90) ORIENTATIONS.append(Surface.ROTATION_90, 0) ORIENTATIONS.append(Surface.ROTATION_180, 270) ORIENTATIONS.append(Surface.ROTATION_270, 180) } /** * 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, context: Context): 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) // On most devices, the sensor orientation is 90 degrees, but for some // devices it is 270 degrees. For devices with a sensor orientation of // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees. val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360 // Return the corresponding FirebaseVisionImageMetadata rotation value. val result: Int when (rotationCompensation) { 0 -> result = FirebaseVisionImageMetadata.ROTATION_0 90 -> result = FirebaseVisionImageMetadata.ROTATION_90 180 -> result = FirebaseVisionImageMetadata.ROTATION_180 270 -> result = FirebaseVisionImageMetadata.ROTATION_270 else -> { result = FirebaseVisionImageMetadata.ROTATION_0 Log.e(TAG, "Bad rotation value: $rotationCompensation") } } return result }
Quindi, passa l'oggetto
media.Image
e il valore di rotazione aFirebaseVisionImage.fromMediaImage()
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin+KTX
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- Per creare un oggetto
FirebaseVisionImage
da un URI di file, passa il contesto dell'app e l'URI del file aFirebaseVisionImage.fromFilePath()
. Ciò è utile quando utilizzi un intentoACTION_GET_CONTENT
per richiedere all'utente di selezionare un'immagine dalla sua app galleria.Java
FirebaseVisionImage image; try { image = FirebaseVisionImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Kotlin+KTX
val image: FirebaseVisionImage try { image = FirebaseVisionImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
- Per creare un oggetto
FirebaseVisionImage
da unByteBuffer
o da un array di byte, calcola innanzitutto la rotazione dell'immagine come descritto sopra per l'inputmedia.Image
.Quindi, crea un oggetto
FirebaseVisionImageMetadata
che contenga l'altezza, la larghezza, il formato di codifica del colore e la rotazione dell'immagine:Java
FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build();
Kotlin+KTX
val metadata = FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build()
Utilizzare il buffer o l'array e l'oggetto metadati per creare un oggetto
FirebaseVisionImage
:Java
FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata); // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
Kotlin+KTX
val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata) // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
- Per creare un oggetto
FirebaseVisionImage
da un oggettoBitmap
:L'immagine rappresentata dall'oggettoJava
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin+KTX
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmap
deve essere verticale, senza necessità di rotazione aggiuntiva.
Ottieni un'istanza di
FirebaseVisionFaceDetector
:Java
FirebaseVisionFaceDetector detector = FirebaseVision.getInstance() .getVisionFaceDetector(options);
Kotlin+KTX
val detector = FirebaseVision.getInstance() .getVisionFaceDetector(options)
Infine, passa l'immagine al metodo
detectInImage
:Java
Task<List<FirebaseVisionFace>> result = detector.detectInImage(image) .addOnSuccessListener( new OnSuccessListener<List<FirebaseVisionFace>>() { @Override public void onSuccess(List<FirebaseVisionFace> faces) { // Task completed successfully // ... } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
Kotlin+KTX
val result = detector.detectInImage(image) .addOnSuccessListener { faces -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
3. Ottieni informazioni sui volti rilevati
Se l'operazione di riconoscimento facciale ha esito positivo, un elenco di oggettiFirebaseVisionFace
verrà passato al listener di successo. Ogni oggetto FirebaseVisionFace
rappresenta un volto che è stato rilevato nell'immagine. Per ogni volto, puoi ottenere le coordinate di delimitazione nell'immagine di input, così come qualsiasi altra informazione che hai configurato per trovare il rilevatore di volti. Per esempio: Java
for (FirebaseVisionFace face : faces) { Rect bounds = face.getBoundingBox(); float rotY = face.getHeadEulerAngleY(); // Head is rotated to the right rotY degrees float rotZ = face.getHeadEulerAngleZ(); // Head is tilted sideways rotZ degrees // If landmark detection was enabled (mouth, ears, eyes, cheeks, and // nose available): FirebaseVisionFaceLandmark leftEar = face.getLandmark(FirebaseVisionFaceLandmark.LEFT_EAR); if (leftEar != null) { FirebaseVisionPoint leftEarPos = leftEar.getPosition(); } // If contour detection was enabled: List<FirebaseVisionPoint> leftEyeContour = face.getContour(FirebaseVisionFaceContour.LEFT_EYE).getPoints(); List<FirebaseVisionPoint> upperLipBottomContour = face.getContour(FirebaseVisionFaceContour.UPPER_LIP_BOTTOM).getPoints(); // If classification was enabled: if (face.getSmilingProbability() != FirebaseVisionFace.UNCOMPUTED_PROBABILITY) { float smileProb = face.getSmilingProbability(); } if (face.getRightEyeOpenProbability() != FirebaseVisionFace.UNCOMPUTED_PROBABILITY) { float rightEyeOpenProb = face.getRightEyeOpenProbability(); } // If face tracking was enabled: if (face.getTrackingId() != FirebaseVisionFace.INVALID_ID) { int id = face.getTrackingId(); } }
Kotlin+KTX
for (face in faces) { val bounds = face.boundingBox val rotY = face.headEulerAngleY // Head is rotated to the right rotY degrees val rotZ = face.headEulerAngleZ // Head is tilted sideways rotZ degrees // If landmark detection was enabled (mouth, ears, eyes, cheeks, and // nose available): val leftEar = face.getLandmark(FirebaseVisionFaceLandmark.LEFT_EAR) leftEar?.let { val leftEarPos = leftEar.position } // If contour detection was enabled: val leftEyeContour = face.getContour(FirebaseVisionFaceContour.LEFT_EYE).points val upperLipBottomContour = face.getContour(FirebaseVisionFaceContour.UPPER_LIP_BOTTOM).points // If classification was enabled: if (face.smilingProbability != FirebaseVisionFace.UNCOMPUTED_PROBABILITY) { val smileProb = face.smilingProbability } if (face.rightEyeOpenProbability != FirebaseVisionFace.UNCOMPUTED_PROBABILITY) { val rightEyeOpenProb = face.rightEyeOpenProbability } // If face tracking was enabled: if (face.trackingId != FirebaseVisionFace.INVALID_ID) { val id = face.trackingId } }
Esempio di contorni del viso
Quando hai abilitato il rilevamento del contorno del viso, ottieni un elenco di punti per ogni caratteristica del viso che è stata rilevata. Questi punti rappresentano la forma dell'elemento. Consulta la Panoramica sui concetti di rilevamento dei volti per i dettagli su come vengono rappresentati i contorni.
L'immagine seguente illustra come questi punti si associano a una faccia (fare clic sull'immagine per ingrandirla):
Rilevamento dei volti in tempo reale
Se desideri utilizzare il rilevamento dei volti in un'applicazione in tempo reale, segui queste linee guida per ottenere i framerate migliori:
Configurare il rilevatore facciale per utilizzare il rilevamento del contorno del viso o la classificazione e il rilevamento del punto di riferimento, ma non entrambi:
Rilevamento del contorno
Rilevamento di punti di riferimento
Classificazione
Rilevamento e classificazione dei punti di riferimento
Rilevamento del contorno e rilevamento del punto di riferimento
Rilevamento e classificazione dei contorni
Rilevamento del contorno, rilevamento del punto di riferimento e classificazioneAbilita la modalità
FAST
(abilitata per impostazione predefinita).Prendi in considerazione l'acquisizione di immagini a una risoluzione inferiore. Tuttavia, tieni anche a mente i requisiti della dimensione dell'immagine di questa API.
- Accelera le chiamate al rilevatore. Se un nuovo fotogramma video diventa disponibile mentre il rilevatore è in funzione, rilasciare il fotogramma.
- Se si utilizza l'output del rilevatore per sovrapporre la grafica all'immagine di input, ottenere prima il risultato da ML Kit, quindi eseguire il rendering dell'immagine e sovrapporre in un unico passaggio. In questo modo, si esegue il rendering sulla superficie di visualizzazione solo una volta per ciascun frame di input.
Se utilizzi l'API Camera2, acquisisci immagini nel formato
ImageFormat.YUV_420_888
.Se utilizzi l'API della fotocamera precedente, acquisisci le immagini nel formato
ImageFormat.NV21
.