iOS에서 ML Kit를 사용하여 얼굴 인식

ML Kit를 사용하면 이미지 및 동영상 속 얼굴을 감지할 수 있습니다.

사용해 보기

  • 샘플 앱을 살펴보고 이 API의 사용 예를 확인하세요.
  • Codelab을 사용하여 직접 코드를 사용해 보세요.

시작하기 전에

  1. Podfile에 다음 ML Kit 포드를 추가합니다.
    pod 'GoogleMLKit/FaceDetection', '3.2.0'
    
  2. 프로젝트의 포드를 설치하거나 업데이트한 후 .xcworkspace를 사용하여 Xcode 프로젝트를 엽니다. ML Kit는 Xcode 버전 12.4 이상에서 지원됩니다.

입력 이미지 가이드라인

얼굴 인식의 경우 크기가 480x360픽셀 이상인 이미지를 사용해야 합니다. ML Kit가 얼굴을 정확하게 인식하려면 입력 이미지에 충분한 픽셀 데이터로 표시된 얼굴이 있어야 합니다. 일반적으로 이미지에서 인식하려는 얼굴은 각각 100x100픽셀 이상이어야 합니다. 얼굴의 윤곽을 인식하려면 ML Kit에 더 높은 해상도의 입력이 필요합니다. 얼굴이 200x200픽셀 이상이어야 합니다.

실시간 애플리케이션에서 얼굴을 인식하는 경우 입력 이미지의 전체 크기를 고려해야 할 수도 있습니다. 이미지 크기가 작을수록 더 빠르게 처리될 수 있으므로 지연 시간을 줄이려면 더 낮은 해상도에서 이미지를 캡처하세요. 하지만 위의 정확도 요구사항을 염두에 두고 피사체의 얼굴이 가능한 한 많은 이미지를 차지하도록 해야 합니다. 실시간 성능 개선을 위한 팁도 참고하세요.

이미지 초점이 잘 맞지 않으면 정확도에 영향을 줄 수 있습니다. 허용 가능한 수준의 결과를 얻지 못하면 사용자에게 이미지를 다시 캡처하도록 요청합니다.

카메라를 기준으로 한 얼굴의 방향은 ML Kit가 감지하는 얼굴 특징에 영향을 미칠 수 있습니다. 얼굴 인식 개념을 참조하세요.

1. 얼굴 인식기 구성

이미지에 얼굴 인식 기능을 적용하기 전에 얼굴 인식기의 기본 설정을 변경하려면 FaceDetectorOptions 객체를 사용하여 설정을 지정합니다. 다음 설정을 변경할 수 있습니다.

설정
performanceMode fast (기본값) | accurate

얼굴을 감지할 때 속도 또는 정확도를 우선시합니다.

landmarkMode none (기본값) | all

인식된 모든 얼굴에서 눈, 귀, 코, 뺨, 입과 같은 얼굴의 '표지점'을 감지할지 여부입니다.

contourMode none (기본값) | all

얼굴 특징의 윤곽을 감지할지 여부입니다. 윤곽은 이미지에서 가장 뚜렷한 얼굴에 대해서만 인식됩니다.

classificationMode none (기본값) | all

얼굴을 '웃고 있음' 및 '눈을 뜨고 있음'과 같은 카테고리로 분류할지 여부입니다.

minFaceSize CGFloat(기본값: 0.1)

이미지 너비에 대한 머리 너비 비율로 표현하여 원하는 가장 작은 얼굴 크기를 설정합니다.

isTrackingEnabled false (기본값) | true

얼굴에 ID를 할당할지 여부이며, 여러 이미지에서 얼굴을 추적하는 데 사용할 수 있습니다.

윤곽 감지가 사용 설정된 경우 얼굴이 하나만 인식되므로 얼굴 추적에서 유용한 결과를 얻지 못합니다. 따라서 감지 속도를 개선하려면 윤곽 인식과 얼굴 추적은 모두 사용 설정하지 마세요.

다음 예와 같이 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. 입력 이미지 준비

이미지 속 얼굴을 인식하려면 process(_:completion:) 또는 results(in:) 메서드를 사용하여 이미지를 UIImage 또는 CMSampleBufferRefFaceDetector에 전달합니다.

UIImage 또는 CMSampleBuffer를 사용하여 VisionImage 객체를 만듭니다.

UIImage를 사용하는 경우 다음 단계를 따르세요.

  • UIImageVisionImage 객체를 만듭니다. 올바른 .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;
      }
    }
          
  • CMSampleBuffer 객체와 방향을 사용하여 VisionImage 객체를 만듭니다.

    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의 이미지 크기 요구사항도 유의해야 합니다.

  • 동영상 프레임을 처리하려면 감지기의 results(in:) 동기 API를 사용합니다. AVCaptureVideoDataOutputSampleBufferDelegate captureOutput(_, didOutput:from:) 함수에서 이 메서드를 호출하여 주어진 동영상 프레임에서 결과를 동기식으로 가져옵니다. AVCaptureVideoDataOutput alwaysDiscardsLateVideoFramestrue로 유지하여 감지기 호출을 제한합니다. 감지기가 실행 중일 때 새 동영상 프레임이 사용 가능해지면 삭제됩니다.
  • 인식기 출력을 사용하여 입력 이미지에서 그래픽을 오버레이하는 경우 먼저 ML Kit에서 결과를 가져온 후 이미지를 렌더링하고 단일 단계로 오버레이합니다. 이렇게 하면 처리된 입력 프레임마다 한 번만 디스플레이 표면에 렌더링됩니다. 예시는 ML Kit 빠른 시작 샘플의 updatePreviewOverlayViewWithLastFrame을 참조하세요.