Распознавание лиц с помощью ML Kit на iOS

Вы можете использовать ML Kit для обнаружения лиц на изображениях и видео.

Попробовать

Подготовка

  1. Добавьте в Podfile следующие поды ML Kit:
    pod 'GoogleMLKit/FaceDetection', '8.0.0'
    
  2. После установки или обновления Pods проекта откройте проект Xcode, используя файл .xcworkspace. ML Kit поддерживается в Xcode версии 12.4 и более поздних.

Требования к исходным изображениям

Для распознавания лиц следует использовать изображения с размером не менее 480 x 360 пикселей. Чтобы ML Kit мог точно распознавать лица, на входных изображениях они должны быть представлены достаточным количеством пикселей. В целом, каждое лицо, которое вы хотите обнаружить на изображении, должно быть не менее 100x100 пикселей. Если вы хотите обнаружить контуры лиц, ML Kit требует более высокого разрешения входных данных: каждое лицо должно быть не менее 200 x 200 пикселей.

Если вы обнаруживаете лица в приложении, работающем в реальном времени, вам также следует учитывать общие размеры входных изображений. Небольшие изображения обрабатываются быстрее, поэтому, чтобы уменьшить задержку, снимайте с более низким разрешением. Однако учитывайте требования к точности, указанные выше, и убедитесь, что лицо занимает как можно большую часть изображения. Также ознакомьтесь с советами по повышению эффективности в реальном времени.

Точность также может снизиться из-за плохого фокуса. Если результаты не соответствуют требованиям, попросите пользователя сделать снимок ещё раз.

Ориентация лица относительно камеры также может влиять на то, какие черты лица обнаруживает ML Kit. Подробнее о распознавании лиц…

1. Как настроить детектор лиц

Если вы хотите изменить настройки детектора лиц по умолчанию, укажите их в объекте FaceDetectorOptions до того, как применить к изображению функцию распознавания лиц. Вы можете изменить следующие настройки:

Настройки
performanceMode fast (по умолчанию) | accurate

При распознавании лиц отдавать предпочтение скорости или точности.

landmarkMode none (по умолчанию) | all

Попытаться ли обнаружить "ориентиры" лица – глаза, уши, нос, щеки, рот – на всех обнаруженных лицах.

contourMode none (по умолчанию) | all

Обнаруживать ли контуры черт лица. Контуры определяются только для самого заметного лица на изображении.

classificationMode none (по умолчанию) | all

Определяет, нужно ли классифицировать лица по категориям, например "улыбается" и "глаза открыты".

minFaceSize CGFloat (по умолчанию: 0.1)

Задает наименьший желаемый размер лица, выраженный как отношение ширины головы к ширине изображения.

isTrackingEnabled false (по умолчанию) | true

Назначать ли лицам идентификатор, который можно использовать для отслеживания лиц на изображениях.

Обратите внимание, что при включенном распознавании контуров обнаруживается только одно лицо, поэтому отслеживание лиц не дает полезных результатов. По этой причине, а также для повышения скорости обнаружения не включайте одновременно обнаружение контуров и отслеживание лиц.

Например, создайте объект FaceDetectorOptions, как показано в одном из следующих примеров:

Swift

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

// Real-time contour detection of multiple faces
// options.contourMode = .all

Objective-C

// High-accuracy landmark detection and face classification
MLKFaceDetectorOptions *options = [[MLKFaceDetectorOptions alloc] init];
options.performanceMode = MLKFaceDetectorPerformanceModeAccurate;
options.landmarkMode = MLKFaceDetectorLandmarkModeAll;
options.classificationMode = MLKFaceDetectorClassificationModeAll;

// Real-time contour detection of multiple faces
// options.contourMode = MLKFaceDetectorContourModeAll;

2. Как подготовить входное изображение

Чтобы обнаружить лица на изображении, передайте изображение в виде объекта UIImage или CMSampleBufferRef в FaceDetector, используя метод process(_:completion:) или results(in:):

Создайте объект VisionImage, используя UIImage или CMSampleBuffer.

Если вы используете UIImage, выполните следующие действия:

  • Создайте объект VisionImage с UIImage. Убедитесь, что вы указали правильный .orientation.

    Swift

    let image = VisionImage(image: UIImage)
    visionImage.orientation = image.imageOrientation

    Objective-C

    MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image];
    visionImage.orientation = image.imageOrientation;

Если вы используете CMSampleBuffer, выполните следующие действия:

  • Укажите ориентацию данных изображения, содержащихся в элементе CMSampleBuffer.

    Чтобы получить ориентацию изображения:

    Swift

    func imageOrientation(
      deviceOrientation: UIDeviceOrientation,
      cameraPosition: AVCaptureDevice.Position
    ) -> UIImage.Orientation {
      switch deviceOrientation {
      case .portrait:
        return cameraPosition == .front ? .leftMirrored : .right
      case .landscapeLeft:
        return cameraPosition == .front ? .downMirrored : .up
      case .portraitUpsideDown:
        return cameraPosition == .front ? .rightMirrored : .left
      case .landscapeRight:
        return cameraPosition == .front ? .upMirrored : .down
      case .faceDown, .faceUp, .unknown:
        return .up
      }
    }
          

    Objective-C

    - (UIImageOrientation)
      imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                             cameraPosition:(AVCaptureDevicePosition)cameraPosition {
      switch (deviceOrientation) {
        case UIDeviceOrientationPortrait:
          return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationLeftMirrored
                                                                : UIImageOrientationRight;
    
        case UIDeviceOrientationLandscapeLeft:
          return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationDownMirrored
                                                                : UIImageOrientationUp;
        case UIDeviceOrientationPortraitUpsideDown:
          return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationRightMirrored
                                                                : UIImageOrientationLeft;
        case UIDeviceOrientationLandscapeRight:
          return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationUpMirrored
                                                                : UIImageOrientationDown;
        case UIDeviceOrientationUnknown:
        case UIDeviceOrientationFaceUp:
        case UIDeviceOrientationFaceDown:
          return UIImageOrientationUp;
      }
    }
          
  • Создайте объект VisionImage, используя объект CMSampleBuffer и ориентацию:

    Swift

    let image = VisionImage(buffer: sampleBuffer)
    image.orientation = imageOrientation(
      deviceOrientation: UIDevice.current.orientation,
      cameraPosition: cameraPosition)

    Objective-C

     MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer];
     image.orientation =
       [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                    cameraPosition:cameraPosition];

3. Получение экземпляра FaceDetector

Получите экземпляр FaceDetector:

Swift

let faceDetector = FaceDetector.faceDetector(options: options)

Objective-C

MLKFaceDetector *faceDetector = [MLKFaceDetector faceDetectorWithOptions:options];
      

4. Обработка изображения

Затем передайте изображение методу process():

Swift

weak var weakSelf = self
faceDetector.process(visionImage) { faces, error in
  guard let strongSelf = weakSelf else {
    print("Self is nil!")
    return
  }
  guard error == nil, let faces = faces, !faces.isEmpty else {
    // ...
    return
  }

  // Faces detected
  // ...
}

Objective-C

[faceDetector processImage:image
                completion:^(NSArray<MLKFace *> *faces,
                             NSError *error) {
  if (error != nil) {
    return;
  }
  if (faces.count > 0) {
    // Recognized faces
  }
}];

5. Как получить информацию о распознанных лицах

Если операция распознавания лица выполнена успешно, детектор лиц передает массив объектов Face в обработчик завершения. Каждый объект Face представляет лицо, обнаруженное на изображении. Для каждого лица можно получить его ограничивающие координаты на входном изображении, а также любую другую информацию, которую вы настроили для поиска детектором лиц. Пример:

Swift

for face in faces {
  let frame = face.frame
  if face.hasHeadEulerAngleX {
    let rotX = face.headEulerAngleX  // Head is rotated to the uptoward rotX degrees
  }
  if face.hasHeadEulerAngleY {
    let rotY = face.headEulerAngleY  // Head is rotated to the right rotY degrees
  }
  if face.hasHeadEulerAngleZ {
    let rotZ = face.headEulerAngleZ  // Head is tilted sideways 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 (MLKFace *face in faces) {
  // Boundaries of face in image
  CGRect frame = face.frame;
  if (face.hasHeadEulerAngleX) {
    CGFloat rotX = face.headEulerAngleX;  // Head is rotated to the upward rotX degrees
  }
  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):
  MLKFaceLandmark *leftEar = [face landmarkOfType:FIRFaceLandmarkTypeLeftEar];
  if (leftEar != nil) {
    MLKVisionPoint *leftEarPosition = leftEar.position;
  }

  // If contour detection was enabled:
  MLKFaceContour *upperLipBottomContour = [face contourOfType:FIRFaceContourTypeUpperLipBottom];
  if (upperLipBottomContour != nil) {
    NSArray<MLKVisionPoint *> *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;
  }
}

Пример контуров лица

Если включено распознавание контуров лица, вы получите список точек для каждой распознанной черты лица. Эти точки представляют форму объекта. Подробнее о том, как представляются контуры, рассказывается в разделе Концепции обнаружения лиц.

На изображении ниже показано, как эти точки сопоставляются с лицом. Нажмите на изображение, чтобы увеличить его.

Пример обнаруженной сетки контура лица

Распознавание лиц в реальном времени

Если вы хотите использовать распознавание лиц в приложении, работающем в реальном времени, следуйте этим рекомендациям, чтобы достичь оптимальной частоты кадров:

  • Настройте детектор лиц так, чтобы он использовал либо обнаружение контуров лица, либо классификацию и обнаружение ориентиров, но не оба варианта одновременно:

    Обнаружение контуров
    Обнаружение ориентиров
    Классификация
    Обнаружение и классификация ориентиров
    Обнаружение контуров и ориентиров
    Обнаружение контуров и классификация
    Обнаружение контуров и ориентиров, а также классификация

  • Включите режим fast (включен по умолчанию).

  • Попробуйте снимать с более низким разрешением. Однако помните о требованиях к размеру изображений, которые предъявляются в этом API.

  • Для обработки кадров видео используйте синхронный API детектора results(in:). Вызовите этот метод из функции captureOutput(_, didOutput:from:) объекта AVCaptureVideoDataOutputSampleBufferDelegate, чтобы синхронно получить результаты из заданного кадра видео. Оставьте для параметра alwaysDiscardsLateVideoFrames в AVCaptureVideoDataOutput значение true, чтобы ограничить количество вызовов детектора. Если во время работы детектора станет доступен новый кадр видео, он будет пропущен.
  • Если вы используете результаты работы детектора для наложения графики на входное изображение, сначала получите результат от ML Kit, а затем отрисуйте изображение и наложите графику за один шаг. В этом случае отрисовка на поверхности экрана выполняется только один раз для каждого обработанного входного кадра. Пример можно найти в функции updatePreviewOverlayViewWithLastFrame в образце быстрого запуска ML Kit.