iOS에서 ML Kit를 사용하여 포즈 감지

ML Kit는 포즈 감지를 위해 최적화된 두 가지 SDK를 제공합니다.

SDK 이름PoseDetectionPoseDetectionAccurate
구현기본 감지기의 애셋은 빌드 시 앱에 정적으로 연결됩니다.정확한 감지기의 애셋은 빌드 시 앱에 정적으로 연결됩니다.
앱 크기최대 29.6MB최대 33.2MB
성능iPhone X: ~45FPSiPhone X: ~29FPS

사용해 보기

  • 샘플 앱을 사용해 이 API의 사용 예를 살펴보세요.

시작하기 전에

  1. Podfile에 다음 ML Kit 포드를 포함합니다.

    # If you want to use the base implementation:
    pod 'GoogleMLKit/PoseDetection', '7.0.0'
    
    # If you want to use the accurate implementation:
    pod 'GoogleMLKit/PoseDetectionAccurate', '7.0.0'
    
  2. 프로젝트의 pod를 설치하거나 업데이트한 후 xcworkspace를 사용하여 Xcode 프로젝트를 엽니다. ML Kit는 Xcode 13.2.1 이상 버전에서 지원됩니다.

1. PoseDetector 인스턴스 만들기

이미지에서 포즈를 감지하려면 먼저 PoseDetector의 인스턴스를 만들고 원하는 경우 감지기 설정을 지정합니다.

옵션 PoseDetector

감지 모드

PoseDetector는 두 가지 감지 모드로 작동합니다. 사용 사례에 맞는 항목을 선택해야 합니다.

stream(기본)
포즈 감지기는 먼저 이미지에서 가장 눈에 띄는 사람을 감지한 다음 포즈 감지를 실행합니다. 후속 프레임에서는 사람이 가려지거나 더 이상 높은 신뢰도로 감지되지 않는 한 사람 감지 단계가 실행되지 않습니다. 자세 감지기는 가장 눈에 띄는 사람을 추적하고 각 추론에서 자세를 반환하려고 시도합니다. 이렇게 하면 지연 시간이 줄고 감지가 원활해집니다. 동영상 스트림에서 포즈를 감지하려는 경우 이 모드를 사용하세요.
singleImage
포즈 감지기가 사람을 감지한 후 포즈 감지를 실행합니다. 사람 감지 단계는 모든 이미지에 대해 실행되므로 지연 시간이 더 길어지고 사람 추적은 이루어지지 않습니다. 정적 이미지에서 포즈 감지를 사용하거나 추적을 원하지 않는 경우에 이 모드를 사용합니다.

포즈 감지기 옵션을 지정합니다.

Swift

// Base pose detector with streaming, when depending on the PoseDetection SDK
let options = PoseDetectorOptions()
options.detectorMode = .stream

// Accurate pose detector on static images, when depending on the
// PoseDetectionAccurate SDK
let options = AccuratePoseDetectorOptions()
options.detectorMode = .singleImage

Objective-C

// Base pose detector with streaming, when depending on the PoseDetection SDK
MLKPoseDetectorOptions *options = [[MLKPoseDetectorOptions alloc] init];
options.detectorMode = MLKPoseDetectorModeStream;

// Accurate pose detector on static images, when depending on the
// PoseDetectionAccurate SDK
MLKAccuratePoseDetectorOptions *options =
    [[MLKAccuratePoseDetectorOptions alloc] init];
options.detectorMode = MLKPoseDetectorModeSingleImage;

마지막으로 PoseDetector의 인스턴스를 가져옵니다. 지정한 옵션을 전달합니다.

Swift

let poseDetector = PoseDetector.poseDetector(options: options)

Objective-C

MLKPoseDetector *poseDetector =
    [MLKPoseDetector poseDetectorWithOptions:options];

2. 입력 이미지 준비

포즈를 감지하려면 각 이미지 또는 동영상 프레임에 대해 다음을 실행합니다. 스트림 모드를 사용 설정한 경우 CMSampleBuffer에서 VisionImage 객체를 만들어야 합니다.

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. 이미지 처리

VisionImage를 포즈 감지기의 이미지 처리 메서드 중 하나에 전달합니다. 비동기 process(image:) 메서드 또는 동기 results() 메서드를 사용할 수 있습니다.

객체를 동기식으로 감지하는 방법은 다음과 같습니다.

Swift

var results: [Pose]
do {
  results = try poseDetector.results(in: image)
} catch let error {
  print("Failed to detect pose with error: \(error.localizedDescription).")
  return
}
guard let detectedPoses = results, !detectedPoses.isEmpty else {
  print("Pose detector returned no results.")
  return
}

// Success. Get pose landmarks here.

Objective-C

NSError *error;
NSArray *poses = [poseDetector resultsInImage:image error:&error];
if (error != nil) {
  // Error.
  return;
}
if (poses.count == 0) {
  // No pose detected.
  return;
}

// Success. Get pose landmarks here.

객체를 비동기식으로 감지하는 방법은 다음과 같습니다.

Swift

poseDetector.process(image) { detectedPoses, error in
  guard error == nil else {
    // Error.
    return
  }
  guard !detectedPoses.isEmpty else {
    // No pose detected.
    return
  }

  // Success. Get pose landmarks here.
}

Objective-C

[poseDetector processImage:image
                completion:^(NSArray * _Nullable poses,
                             NSError * _Nullable error) {
                    if (error != nil) {
                      // Error.
                      return;
                    }
                    if (poses.count == 0) {
                      // No pose detected.
                      return;
                    }

                    // Success. Get pose landmarks here.
                  }];

4. 감지된 포즈에 관한 정보 가져오기

이미지에서 사람이 감지되면 자세 감지 API는 비동기 메서드를 호출했는지 아니면 동기 메서드를 호출했는지에 따라 Pose 객체 배열을 완료 핸들러에 전달하거나 배열을 반환합니다.

사람이 이미지 내에 완전히 있지 않은 경우 모델은 프레임 외부에 누락된 랜드마크 좌표를 할당하고 낮은 InFrameConfidence 값을 부여합니다.

사람이 감지되지 않으면 배열이 비어 있습니다.

Swift

for pose in detectedPoses {
  let leftAnkleLandmark = pose.landmark(ofType: .leftAnkle)
  if leftAnkleLandmark.inFrameLikelihood > 0.5 {
    let position = leftAnkleLandmark.position
  }
}

Objective-C

for (MLKPose *pose in detectedPoses) {
  MLKPoseLandmark *leftAnkleLandmark =
      [pose landmarkOfType:MLKPoseLandmarkTypeLeftAnkle];
  if (leftAnkleLandmark.inFrameLikelihood > 0.5) {
    MLKVision3DPoint *position = leftAnkleLandmark.position;
  }
}

성능 개선을 위한 팁

결과 품질은 입력 이미지의 품질에 따라 다릅니다.

  • ML Kit가 포즈를 정확하게 감지하려면 이미지의 사람이 충분한 픽셀 데이터로 표시되어야 합니다. 최상의 성능을 얻으려면 피사체가 256x256픽셀 이상이어야 합니다.
  • 실시간 애플리케이션에서 포즈를 감지하는 경우 입력 이미지의 전체 크기를 고려해야 할 수도 있습니다. 이미지 크기가 작을수록 더 빠르게 처리될 수 있으므로 지연 시간을 줄이려면 위의 해상도 요구사항에 유의하여 낮은 해상도에서 이미지를 캡처하고 피사체가 이미지의 많은 부분을 차지하도록 합니다.
  • 이미지 초점이 잘 맞지 않으면 정확도에 영향을 미칠 수도 있습니다. 허용 가능한 수준의 결과를 얻지 못하는 경우 사용자에게 이미지를 다시 캡처하도록 요청합니다.

실시간 애플리케이션에서 자세 감지를 사용하려는 경우 최상의 프레임 속도를 얻으려면 다음 안내를 따르세요.

  • 기본 PoseDetection SDK 및 stream 감지 모드를 사용합니다.
  • 낮은 해상도에서 이미지를 캡처하는 것이 좋습니다. 단, 이 API의 이미지 크기 요구사항도 유의해야 합니다.
  • 동영상 프레임을 처리하려면 감지기의 results(in:) 동기식 API를 사용하세요. AVCaptureVideoDataOutputSampleBufferDelegatecaptureOutput(_, didOutput:from:) 함수에서 이 메서드를 호출하여 지정된 동영상 프레임에서 결과를 동기식으로 가져옵니다. 감지기 호출을 제한하려면 AVCaptureVideoDataOutputalwaysDiscardsLateVideoFrames를 true로 유지합니다. 감지기가 실행 중일 때 새 동영상 프레임이 제공되면 삭제됩니다.
  • 인식기 출력을 사용하여 입력 이미지에 그래픽을 오버레이하는 경우 먼저 ML Kit에서 결과를 가져온 후 이미지를 렌더링하고 단일 단계로 오버레이합니다. 이렇게 하면 처리된 입력 프레임별로 한 번만 디스플레이 노출 영역에 렌더링됩니다. 관련 예시는 쇼케이스 샘플 앱에서 previewOverlayViewMLKDetectionOverlayView 클래스를 참고하세요.

다음 단계