在 iOS 上使用 ML Kit 偵測姿勢

ML Kit 提供兩個經過最佳化的姿勢偵測 SDK。

SDK 名稱PoseDetectionPoseDetectionAccurate
導入作業基本偵測器的素材資源會在建構期間與應用程式建立靜態連結。精確偵測器的素材資源會在建構期間與應用程式建立靜態連結。
應用程式大小上限為 29.6 MB最多 33.2 MB
成效iPhone X:約 45 FPSiPhone X:約 29 FPS

立即試用

事前準備

  1. 在 Podfile 中加入下列 ML Kit Pod:

    # If you want to use the base implementation:
    pod 'GoogleMLKit/PoseDetection', '8.0.0'
    
    # If you want to use the accurate implementation:
    pod 'GoogleMLKit/PoseDetectionAccurate', '8.0.0'
    
  2. 安裝或更新專案的 Pod 後,請使用 xcworkspace 開啟 Xcode 專案。Xcode 13.2.1 以上版本支援 ML Kit。

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 物件。

使用 UIImageCMSampleBuffer 建立 VisionImage 物件。

如果您使用 UIImage,請按照下列步驟操作:

  • 使用 UIImage 建立 VisionImage 物件。請務必指定正確的 .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 準確偵測姿勢,圖片中的人物應包含足夠的像素資料;為求最佳效能,主體至少應為 256 x 256 像素。
  • 如果您要在即時應用程式中偵測姿勢,也許也要考慮輸入圖片的整體尺寸。較小的圖片可加快處理速度,因此為了減少延遲時間,請以較低解析度拍攝圖片,但請注意上述解析度規定,並確保拍攝主體盡可能佔據圖片的大部分空間。
  • 圖片對焦不佳也會影響準確度。如果您無法取得可接受的結果,請要求使用者重新拍攝圖片。

如果您想在即時應用程式中使用姿勢偵測功能,請按照下列指南取得最佳影格速率:

  • 使用基本 PoseDetection SDK 和 stream 偵測模式。
  • 建議您以較低解析度拍攝相片。不過,請注意這個 API 的圖片尺寸規定。
  • 如要處理影片影格,請使用偵測器的 results(in:) 同步 API。從 AVCaptureVideoDataOutputSampleBufferDelegatecaptureOutput(_, didOutput:from:) 函式呼叫這個方法,即可同步取得指定影片影格中的結果。請將 AVCaptureVideoDataOutputalwaysDiscardsLateVideoFrames 設為 true,以便限制對偵測器的呼叫。如果偵測器在執行期間收到新的影片影格,系統會捨棄該影格。
  • 如果您使用偵測器的輸出內容,在輸入圖片上疊加圖形,請先從 ML Kit 取得結果,然後在單一步驟中算繪圖片和疊加圖形。這樣一來,您只需為每個已處理的輸入影格轉譯一次顯示介面。如需範例,請參閱展示範例應用程式中的 previewOverlayViewMLKDetectionOverlayView 類別。

後續步驟