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

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

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

אני רוצה לנסות

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

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

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

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

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

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 הזה.
  • לעיבוד מסגרות וידאו, יש להשתמש ב-API הסינכרוני של results(in:) של המזהה. קראו לשיטה הזו מתוך הפונקציה captureפלט(_, doפלט:מ:) של AVCaptureVideoDataפלטSampleBufferDelegate כדי לקבל תוצאות סינכרוניות ממסגרת הווידאו הנתונה. משאירים את תמיד מתבטלים ב-AVCaptureVideoDataפלט כוויסות נתונים (throttle) של קריאות אל המזהה. אם פריים חדש של סרטון הופך לזמין בזמן שהמזהה פועל, הוא יוסר.
  • אם משתמשים בפלט של המזהה כדי להוסיף גרפיקה כשכבת-על בתמונת הקלט, קודם צריך לקבל את התוצאה מ-ML Kit ולאחר מכן לעבד את התמונה ואת שכבת-העל בפעולה אחת. על ידי כך, אתם מבצעים רינדור למשטח התצוגה פעם אחת בלבד לכל מסגרת קלט מעובדת. לצפייה בדוגמה, תוכלו לראות את המחלקות PreviewLayerView ו-MLKDetectionLayerView.

השלבים הבאים