Detectar rostos com o Kit de ML no iOS

Você pode usar o Kit de ML para detectar rostos em imagens e vídeos.

Consulte a amostra do guia de início rápido do Kit de ML (em inglês) no GitHub para ver um exemplo desta API em uso.

Antes de começar

  1. Se você ainda não adicionou o Firebase ao seu app, siga as etapas no guia de primeiros passos.
  2. Inclua as bibliotecas do Kit de ML no seu Podfile:
    pod 'Firebase/MLVision'
    # If you want to detect face contours (landmark detection and classification
    # don't require this additional model):
    pod 'Firebase/MLVisionFaceModel'
    
    Depois de instalar ou atualizar os pods do seu projeto, abra o projeto do Xcode usando o .xcworkspace.
  3. Importe o Firebase para seu app:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

Diretrizes de imagem de entrada

Para que o Kit de ML detecte rostos com precisão, as imagens de entrada devem conter rostos representados por dados de pixel suficientes. Em geral, cada rosto que você quiser detectar em uma imagem deverá ter pelo menos 100x100 pixels. Se você quiser detectar os contornos dos rostos, o Kit de ML precisará de uma entrada de resolução mais alta: cada rosto deverá ter pelo menos 200x200 pixels.

Se você estiver fazendo a detecção facial em um aplicativo em tempo real, considere as dimensões gerais das imagens de entrada. Já que imagens menores podem ser processadas mais rapidamente, para reduzir a latência, capture imagens em resoluções mais baixas (tendo em mente os requisitos de precisão acima) e certifique-se de que o rosto da pessoa ocupe o máximo possível da imagem. Consulte também Dicas para melhorar o desempenho em tempo real.

Uma imagem com foco inadequado pode prejudicar a precisão. Se você não estiver conseguindo resultados aceitáveis, peça para o usuário recapturar a imagem.

A orientação de um rosto em relação à câmera também pode afetar os atributos faciais que o Kit de ML detecta. Veja Conceitos de detecção facial.

1. Configurar o detector facial

Antes de aplicar a detecção facial a uma imagem, se quiser alterar as configurações padrão do detector facial, especifique essas configurações com um objeto VisionFaceDetectorOptions. É possível alterar as seguintes configurações:

Configurações
performanceMode fast (padrão) | accurate

Favoreça a velocidade ou a precisão durante a detecção de faces.

landmarkMode none (padrão) | all

Para tentar detectar "pontos de referência" faciais de todos os rostos detectados: olhos, orelhas, nariz, bochechas ou boca.

contourMode none (padrão) | all

Para detectar os contornos das características faciais. São detectados apenas os contornos do rosto mais proeminente de uma imagem.

classificationMode none (padrão) | all

Se é necessário classificar faces em categorias como "sorrindo" e "olhos abertos".

minFaceSize CGFloat (padrão: 0.1)

O tamanho mínimo, relativo à imagem, de faces a serem detectadas.

isTrackingEnabled false (padrão) | true

Se é necessário atribuir um identificador às faces, que pode ser usado para rastrear rostos em imagens.

Quando a detecção de contorno está ativada, apenas um rosto é detectado, portanto, o rastreamento facial não produz resultados úteis. Por esse motivo, e para melhorar a velocidade de detecção, não ative a detecção de contorno simultaneamente com o rastreamento facial.

Por exemplo, crie um objeto VisionFaceDetectorOptions como um dos exemplos a seguir:

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. Executar o detector facial

Para detectar rostos em uma imagem, transfira-a como um UIImage ou CMSampleBufferRef ao método detect(in:) do VisionFaceDetector:

  1. Crie uma instância 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. Crie um objeto VisionImage usando um UIImage ou um CMSampleBufferRef.

    Para usar uma UIImage:

    1. Se necessário, gire a imagem para que a propriedade imageOrientation seja definida como .up.
    2. Crie um objeto VisionImage usando a UIImage girada corretamente. Não especifique nenhum metadado de rotação. É preciso usar o valor padrão .topLeft.

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

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

    Para usar uma CMSampleBufferRef:

    1. Crie um objeto VisionImageMetadata que especifique a orientação dos dados de imagem contidos no buffer CMSampleBufferRef.

      Para ver a orientação da imagem:

      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;
        }
      }

      Em seguida, crie o objeto de metadados:

      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. Crie um objeto VisionImage usando o CMSampleBufferRef e os metadados de rotação:

      Swift

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

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. Em seguida, transmita a imagem para o 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. Acessar informações sobre rostos detectados

Se a operação de detecção facial for bem-sucedida, o detector transmitirá uma matriz de objetos VisionFace para o gerenciador de conclusão. Cada objeto VisionFace representa uma face detectada na imagem. Para cada rosto, é possível receber as coordenadas delimitadoras na imagem de entrada, além de qualquer outra informação configurada no detector facial a ser encontrada. Por exemplo:

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;
  }
}

Exemplo de contornos faciais

Quando a detecção de contorno facial está ativada, você recebe uma lista de pontos para cada atributo facial que foi detectado. Esses pontos representam a forma do elemento. Consulte a Visão geral dos conceitos de detecção facial para ver detalhes sobre como os contornos são representados.

A imagem a seguir ilustra como esses pontos mapeiam um rosto. Clique para ampliá-la:

Detecção facial em tempo real

Se você quiser usar a detecção facial em um aplicativo em tempo real, siga estas diretrizes para conseguir as melhores taxas de frames.

  • Configure o detector facial para usar a detecção de contorno facial ou a classificação e a detecção de pontos de referência, mas não os dois:

    Detecção de contorno
    Detecção de pontos de referência
    Classificação
    Detecção e classificação de pontos de referência
    Detecção de contorno e detecção de pontos de referência
    Detecção e classificação de contornos
    Detecção de contorno, detecção e classificação de pontos de referência

  • Ative o modo fast (ativado por padrão).

  • Capture imagens em uma resolução menor. No entanto, lembre-se também dos requisitos de dimensão de imagem da API.

  • Limite as chamadas ao detector. Se um novo frame de vídeo for disponibilizado enquanto o detector estiver em execução, descarte o frame.
  • Se você estiver usando a saída do detector para sobrepor elementos gráficos na imagem de entrada, primeiro acesse o resultado do Kit de ML e, em seguida, renderize a imagem e a sobreposição em uma única etapa. Ao fazer isso, você renderiza a superfície de exibição apenas uma vez para cada frame de entrada. Consulte as classes previewOverlayView e FIRDetectionOverlayView no app de amostra da demonstração para ver um exemplo.

Próximas etapas

Consulte a amostra do guia de início rápido do Kit de ML (em inglês) no GitHub para ver um exemplo desta API em uso.