Detecta rostros con el Kit de AA en iOS

Puedes usar el Kit de AA para detectar rostros en imágenes y videos.

Antes de comenzar

  1. Si aún no agregaste Firebase a tu app, sigue los pasos en la guía de introducción para hacerlo.
  2. Incluye las bibliotecas del ML Kit en tu Podfile:
    pod 'Firebase/MLVision', '6.25.0'
    # If you want to detect face contours (landmark detection and classification
    # don't require this additional model):
    pod 'Firebase/MLVisionFaceModel', '6.25.0'
    
    Después de instalar o actualizar los Pods de tu proyecto, asegúrate de abrir el proyecto de Xcode con su .xcworkspace.
  3. En tu app, importa Firebase:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

Lineamientos para imágenes de entrada

Para que el Kit de AA detecte rostros con precisión, las imágenes de entrada deben contener rostros representados con datos de píxeles suficientes. Por lo general, cada rostro que quieras detectar en una imagen debe tener al menos 100×100 píxeles. Si quieres detectar el contorno de los rostros, el Kit de AA requiere una entrada con mayor resolución: cada rostro debe ser de al menos 200x200 píxeles.

Si reconoces rostros en una aplicación en tiempo real, te recomendamos tener en cuenta las dimensiones generales de las imágenes de entrada. Las imágenes más pequeñas se pueden procesar más rápido. Así que, para reducir la latencia, captura imágenes con resoluciones más bajas (ten en cuenta los requisitos de exactitud mencionados anteriormente) y asegúrate de que el rostro de la persona ocupe la mayor parte de la imagen como sea posible. Consulta también las sugerencias para mejorar el rendimiento en tiempo real.

Un enfoque de imagen deficiente puede afectar la exactitud. Si no obtienes resultados aceptables, intenta pedirle al usuario que vuelva a capturar la imagen.

La orientación de un rostro en relación con la cámara también puede afectar las características que detecta el Kit de AA. Consulta Conceptos de detección de rostro.

1. Configura el detector de rostros

Antes de aplicar la detección de rostro a una imagen, si quieres modificar la configuración predeterminada del detector de rostros, especifica las opciones de configuración con un objeto VisionFaceDetectorOptions. Puedes cambiar las siguientes opciones de configuración:

Configuración
performanceMode fast (predeterminado) | accurate

Prefiere la velocidad o la precisión en la detección de rostros.

landmarkMode none (predeterminado) | all

Indica si se debe intentar detectar “puntos de referencia” faciales: ojos, orejas, nariz, mejillas y boca, de todos los rostros detectados.

contourMode none (predeterminado) | all

Indica si se deben detectar los contornos de rasgos faciales. Solo se detectarán los contornos de los rostros más destacados de la imagen.

classificationMode none (predeterminado) | all

Indica si se deben clasificar los rostros en categorías como “sonriente” y “ojos abiertos”.

minFaceSize CGFloat (predeterminado: 0.1)

El tamaño mínimo, en relación con la imagen, de los rostros que se detectarán.

isTrackingEnabled false (predeterminado) | true

Indica si se deben asignar ID a los rostros, que se pueden usar para seguir los rostros entre imágenes.

Ten en cuenta que cuando la detección de rostro está habilitada, solo se detectará un rostro, por lo que el seguimiento de rostro no brindará resultados útiles. Por eso, y para mejorar la velocidad de la detección, no habilites la detección de contorno y el seguimiento de rostro a la vez.

Por ejemplo, crea un objeto VisionFaceDetectorOptions como uno de los ejemplos siguientes:

Swift

// High-accuracy landmark detection and face classification
let options = VisionFaceDetectorOptions()
options.performanceMode = .accurate
options.landmarkMode = .all
options.classificationMode = .all

// Real-time contour detection of multiple faces
let options = VisionFaceDetectorOptions()
options.contourMode = .all

Objective-C

// High-accuracy landmark detection and face classification
FIRVisionFaceDetectorOptions *options = [[FIRVisionFaceDetectorOptions alloc] init];
options.performanceMode = FIRVisionFaceDetectorPerformanceModeAccurate;
options.landmarkMode = FIRVisionFaceDetectorLandmarkModeAll;
options.classificationMode = FIRVisionFaceDetectorClassificationModeAll;

// Real-time contour detection of multiple faces
FIRVisionFaceDetectorOptions *options = [[FIRVisionFaceDetectorOptions alloc] init];
options.contourMode = FIRVisionFaceDetectorContourModeAll;

2. Ejecuta el detector de rostros

Para detectar rostros en una imagen, pasa la imagen como una UIImage o una CMSampleBufferRef al método detect(in:) de VisionFaceDetector:

  1. Obtén una instancia de VisionFaceDetector:

    Swift

    lazy var vision = Vision.vision()
    
    let faceDetector = vision.faceDetector(options: options)
    

    Objective-C

    FIRVision *vision = [FIRVision vision];
    FIRVisionFaceDetector *faceDetector = [vision faceDetector];
    // Or, to change the default settings:
    // FIRVisionFaceDetector *faceDetector =
    //     [vision faceDetectorWithOptions:options];
    
  2. Crea un objeto VisionImage mediante una UIImage o CMSampleBufferRef.

    Para usar una UIImage, debes hacer lo siguiente:

    1. Si es necesario, rota la imagen para que la propiedad imageOrientation sea .up.
    2. Crea un objeto VisionImage mediante una UIImage que se haya rotado adecuadamente. No especifiques los metadatos de rotación. Se debe usar el valor predeterminado .topLeft.

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];

    Para usar una CMSampleBufferRef, debes hacer lo siguiente:

    1. Crea un objeto VisionImageMetadata que especifique la orientación de los datos de la imagen contenidos en el búfer CMSampleBufferRef.

      Para obtener la orientación de la imagen, haz lo siguiente:

      Swift

      func imageOrientation(
          deviceOrientation: UIDeviceOrientation,
          cameraPosition: AVCaptureDevice.Position
          ) -> VisionDetectorImageOrientation {
          switch deviceOrientation {
          case .portrait:
              return cameraPosition == .front ? .leftTop : .rightTop
          case .landscapeLeft:
              return cameraPosition == .front ? .bottomLeft : .topLeft
          case .portraitUpsideDown:
              return cameraPosition == .front ? .rightBottom : .leftBottom
          case .landscapeRight:
              return cameraPosition == .front ? .topRight : .bottomRight
          case .faceDown, .faceUp, .unknown:
              return .leftTop
          }
      }

      Objective-C

      - (FIRVisionDetectorImageOrientation)
          imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                                 cameraPosition:(AVCaptureDevicePosition)cameraPosition {
        switch (deviceOrientation) {
          case UIDeviceOrientationPortrait:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationLeftTop;
            } else {
              return FIRVisionDetectorImageOrientationRightTop;
            }
          case UIDeviceOrientationLandscapeLeft:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationBottomLeft;
            } else {
              return FIRVisionDetectorImageOrientationTopLeft;
            }
          case UIDeviceOrientationPortraitUpsideDown:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationRightBottom;
            } else {
              return FIRVisionDetectorImageOrientationLeftBottom;
            }
          case UIDeviceOrientationLandscapeRight:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationTopRight;
            } else {
              return FIRVisionDetectorImageOrientationBottomRight;
            }
          default:
            return FIRVisionDetectorImageOrientationTopLeft;
        }
      }

      Luego crea el objeto de metadatos de esta manera:

      Swift

      let cameraPosition = AVCaptureDevice.Position.back  // Set to the capture device you used.
      let metadata = VisionImageMetadata()
      metadata.orientation = imageOrientation(
          deviceOrientation: UIDevice.current.orientation,
          cameraPosition: cameraPosition
      )

      Objective-C

      FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
      AVCaptureDevicePosition cameraPosition =
          AVCaptureDevicePositionBack;  // Set to the capture device you used.
      metadata.orientation =
          [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                       cameraPosition:cameraPosition];
    2. Crea un objeto VisionImage a través del objeto CMSampleBufferRef y los metadatos de rotación:

      Swift

      let image = VisionImage(buffer: sampleBuffer)
      image.metadata = metadata

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. Por último, pasa la imagen al método detect(in:):

    Swift

    faceDetector.process(visionImage) { faces, error in
      guard error == nil, let faces = faces, !faces.isEmpty else {
        // ...
        return
      }
    
      // Faces detected
      // ...
    }
    

    Objective-C

    [faceDetector detectInImage:image
                     completion:^(NSArray<FIRVisionFace *> *faces,
                                  NSError *error) {
      if (error != nil) {
        return;
      } else if (faces != nil) {
        // Recognized faces
      }
    }];
    

3. Obtén información sobre los rostros detectados

Si la operación de detección de rostro se ejecuta correctamente, el detector de rostros pasará un arreglo de objetos VisionFace al controlador de finalización. Cada objeto VisionFace representa un rostro que se detectó en la imagen. Para cada rostro, puedes obtener las coordenadas de sus límites en la imagen de entrada, junto con cualquier otra información que encuentre el detector de rostros según la configuración que le asignaste. Por ejemplo:

Swift

for face in faces {
  let frame = face.frame
  if face.hasHeadEulerAngleY {
    let rotY = face.headEulerAngleY  // Head is rotated to the right rotY degrees
  }
  if face.hasHeadEulerAngleZ {
    let rotZ = face.headEulerAngleZ  // Head is rotated upward rotZ degrees
  }

  // If landmark detection was enabled (mouth, ears, eyes, cheeks, and
  // nose available):
  if let leftEye = face.landmark(ofType: .leftEye) {
    let leftEyePosition = leftEye.position
  }

  // If contour detection was enabled:
  if let leftEyeContour = face.contour(ofType: .leftEye) {
    let leftEyePoints = leftEyeContour.points
  }
  if let upperLipBottomContour = face.contour(ofType: .upperLipBottom) {
    let upperLipBottomPoints = upperLipBottomContour.points
  }

  // If classification was enabled:
  if face.hasSmilingProbability {
    let smileProb = face.smilingProbability
  }
  if face.hasRightEyeOpenProbability {
    let rightEyeOpenProb = face.rightEyeOpenProbability
  }

  // If face tracking was enabled:
  if face.hasTrackingID {
    let trackingId = face.trackingID
  }
}

Objective-C

for (FIRVisionFace *face in faces) {
  // Boundaries of face in image
  CGRect frame = face.frame;

  if (face.hasHeadEulerAngleY) {
    CGFloat rotY = face.headEulerAngleY;  // Head is rotated to the right rotY degrees
  }
  if (face.hasHeadEulerAngleZ) {
    CGFloat rotZ = face.headEulerAngleZ;  // Head is tilted sideways rotZ degrees
  }

  // If landmark detection was enabled (mouth, ears, eyes, cheeks, and
  // nose available):
  FIRVisionFaceLandmark *leftEar = [face landmarkOfType:FIRFaceLandmarkTypeLeftEar];
  if (leftEar != nil) {
    FIRVisionPoint *leftEarPosition = leftEar.position;
  }

  // If contour detection was enabled:
  FIRVisionFaceContour *upperLipBottomContour = [face contourOfType:FIRFaceContourTypeUpperLipBottom];
  if (upperLipBottomContour != nil) {
    NSArray<FIRVisionPoint *> *upperLipBottomPoints = upperLipBottomContour.points;
    if (upperLipBottomPoints.count > 0) {
      NSLog("Detected the bottom contour of the subject's upper lip.")
    }
  }

  // If classification was enabled:
  if (face.hasSmilingProbability) {
    CGFloat smileProb = face.smilingProbability;
  }
  if (face.hasRightEyeOpenProbability) {
    CGFloat rightEyeOpenProb = face.rightEyeOpenProbability;
  }

  // If face tracking was enabled:
  if (face.hasTrackingID) {
    NSInteger trackingID = face.trackingID;
  }
}

Ejemplo de contornos de rostro

Cuando la detección de contorno de rostro se encuentra habilitada, obtienes una lista de puntos por cada característica facial que se detectó. Estos puntos representan la forma de la característica. Consulta Descripción general de los conceptos de la detección de rostros para obtener más información sobre cómo se representan los contornos.

En la siguiente imagen, se describe cómo se asignan estos puntos a un rostro (haz clic en la imagen para ampliarla):

Detección de rostro en tiempo real

Si quieres usar la detección de rostro en una aplicación en tiempo real, sigue estos lineamientos para lograr los mejores fotogramas por segundo:

  • Configura el detector de rostros para usar la detección de contorno facial o la clasificación y detección de puntos de referencia, pero no ambos.

    Detección de contorno
    Detección de puntos de referencia
    Clasificación
    Detección y clasificación de puntos de referencia
    Detección de contorno y de puntos de referencia
    Detección y clasificación de contorno
    Detección de contorno y de puntos de referencia, y clasificación

  • Habilita el modo fast (habilitado de forma predeterminada).

  • Captura imágenes con una resolución más baja. De todas formas, también ten en cuenta los requisitos de dimensiones de imágenes de esta API.

  • Limita las llamadas al detector. Si hay un fotograma de video nuevo disponible mientras se ejecuta el detector, ignora ese fotograma.
  • Si estás usando la salida del detector para superponer gráficos en la imagen de entrada, primero obtén el resultado de la detección de ML Kit y, luego, procesa la imagen y la superposición en un solo paso. De esta manera, procesas la superficie de visualización solo una vez por cada fotograma de entrada. Consulta las clases previewOverlayView y FIRDetectionOverlayView en la app de muestra para ver un ejemplo.