זיהוי תנוחות באמצעות ML Kit ב-iOS

ערכת ML מספקת שתי ערכות SDK שעברו אופטימיזציה לזיהוי תנוחות.

שם ה-SDKזיהוי תנוחותזיהוי מדויק של Pose
יישוםנכסים של מזהה הבסיס מקושרים באופן סטטי לאפליקציה שלך בזמן היצירה.נכסים של מזהה מדויק מקושרים באופן סטטי לאפליקציה שלך בזמן היצירה.
גודל האפליקציהעד 29.6MBעד 33.2MB
ביצועיםiPhone X: כ-45FPSiPhone X: ~29FPS

לפני שמתחילים

  1. יש לכלול ב-Podfile את מוטות ה-ML Kit הבאים:

    # If you want to use the base implementation:
    pod 'GoogleMLKit/PoseDetection', '3.2.0'
    
    # If you want to use the accurate implementation:
    pod 'GoogleMLKit/PoseDetectionAccurate', '3.2.0'
    
  2. אחרי שמתקינים או מעדכנים את ה-pods של הפרויקט, פותחים את פרויקט Xcode באמצעות xcworkspace. ערכת ה-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. הכנת תמונת הקלט

כדי לזהות תנוחות, מבצעים את הפעולות הבאות לכל תמונה או מסגרת של סרטון. אם הפעלת את מצב הסטרימינג, עליך ליצור VisionImage אובייקטים מתוך CMSampleBuffer שנ'.

יצירת אובייקט 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. עיבוד התמונה

מעבירים את 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 אל ה-handler להשלמה או מחזיר את המערך, בהתאם לקריאה לשיטה האסינכרונית או הסינכרונית.

אם האדם לא נמצא בתוך התמונה, המודל מקצה את הקואורדינטות החסרות מחוץ למסגרת ומספק להן ערכי InFrameConTrustness נמוכים.

אם לא זוהה אדם, המערך ריק.

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 תזהה במדויק את התנוחות, האדם שמופיע בתמונה צריך להיות מיוצג על ידי מספיק נתוני פיקסלים. כדי להשיג את הביצועים הטובים ביותר, התמונה צריכה להיות בגודל של 256x256 פיקסלים לפחות.
  • אם מזהים תנוחה באפליקציה בזמן אמת, כדאי לשקול גם את המאפיינים הכוללים של תמונות הקלט. אפשר לעבד תמונות קטנות יותר מהר יותר, כדי לקצר את זמן האחזור ולצלם תמונות ברזולוציה נמוכה יותר. עם זאת, חשוב לזכור את דרישות הרזולוציה שצוינו למעלה ולוודא שהתמונה תופסת הכי הרבה בתמונה.
  • גם מיקוד בתמונה באיכות נמוכה עשוי להשפיע על הדיוק. אם לא מתקבלות תוצאות מקובלות, מבקשים מהמשתמש לצלם מחדש את התמונה.

אם רוצים להשתמש בזיהוי תנוחות בזמן אמת באפליקציה כלשהי, יש לפעול לפי ההנחיות הבאות כדי להשיג את קצב הפריימים הטוב ביותר:

  • שימוש ב-PoseDetection SDK הבסיסי ובמצב זיהוי stream.
  • מומלץ לצלם תמונות ברזולוציה נמוכה יותר. עם זאת, חשוב גם לזכור את הדרישות לגבי מידות התמונה של ה-API הזה.
  • לעיבוד מסגרות וידאו, יש להשתמש ב-API הסינכרוני של results(in:) עבור המזהה. אפשר לקרוא לשיטה הזו מהפונקציה AVCaptureVideoDataפלטSampleBufferDelegate's Captureפלט(_, Doפלט:מ:) כדי לקבל תוצאות באופן סינכרוני בפריים וידאו נתון. משאירים את הערך AVCaptureVideoDataOutput' תמיד תמיד מתבטל ל-TrueSl וידאו, כדי לווסת את הקריאות למזהה. אם מסגרת וידאו חדשה תהפוך לזמינה בזמן שהמזהה פועל, היא תוסר.
  • אם משתמשים בפלט של הגלאי כשכבת-על לתמונת הקלט, יש לקבל תחילה את התוצאה מ-ML Kit, ולאחר מכן לעבד את התמונה ואת שכבת-העל בפעולה אחת. על ידי כך אתם מעבדים את פני השטח לתצוגה פעם אחת בלבד עבור כל מסגרת קלט שעובדה. לדוגמה, אפשר לראות את הכיתות previewpreviewView ו-MLKDetectionLayerView באפליקציה לדוגמה של Showcase.

השלבים הבאים