ML Kit מספק SDK מותאם לסגמנטציה של תמונות סלפי. הנכסים של פילוח הסלפי מקושרים באופן סטטי לאפליקציה בזמן ה-build. זה יגדיל את גודל האפליקציה ב-24MB. זמן האחזור של ה-API יכול לנוע בין 7 אלפיות השנייה ל-12 אלפיות שנייה, בהתאם לגודל של תמונת הקלט, כפי שנמדד ב-iPhone X.
רוצה לנסות?
- מומלץ לשחק עם האפליקציה לדוגמה כדי .
לפני שמתחילים
כוללים ב-Podfile את ספריות ML Kit הבאות:
pod 'GoogleMLKit/SegmentationSelfie', '3.2.0'
אחרי שמתקינים או מעדכנים את ה-Pods של הפרויקט, פותחים את פרויקט Xcode באמצעות קובץ ה-
xcworkspace
שלו. יש תמיכה ב-ML Kit ב-Xcode בגרסה 13.2.1 ואילך.
1. יוצרים מופע של כלי הפילוח
כדי לבצע סגמנטציה בתמונת סלפי, קודם יוצרים מכונה של Segmenter
עם SelfieSegmenterOptions
, ואפשר גם לציין את הגדרות הפילוח.
אפשרויות פילוח
מצב פילוח
Segmenter
פועל בשני מצבים. חשוב לבחור את השיטה שמתאימה לתרחיש לדוגמה שלכם.
STREAM_MODE (default)
המצב הזה מיועד לסטרימינג של פריימים מווידאו או מהמצלמה. במצב הזה, הפילוח ישתמש בתוצאות מפריימים קודמים כדי להחזיר תוצאות פילוח חלק יותר.
SINGLE_IMAGE_MODE (default)
המצב הזה מיועד לתמונות בודדות שאינן קשורות. במצב זה, כלי הפילוח יעבד כל תמונה בנפרד, ללא החלקה על פני פריימים.
הפעלת מסכת גודל גולמי
מבקשת מהפלח להחזיר את מסיכת הגודל הגולמית שתואמת לגודל הפלט של המודל.
גודל המסכה הגולמית (למשל 256x256) קטן בדרך כלל מגודל הקלט של תמונת הקלט.
אם לא מציינים את האפשרות הזו, הפילוח ישנה מחדש את המסכה הגולמית כך שתתאים לגודל הקלט של תמונת הקלט. כדאי להשתמש באפשרות הזו אם רוצים להחיל לוגיקה מותאמת אישית של שינוי גודל או התאמה לעומס (scaling) בתרחיש לדוגמה שלכם.
מציינים את אפשרויות הפילוח:
Swift
let options = SelfieSegmenterOptions() options.segmenterMode = .singleImage options.shouldEnableRawSizeMask = true
Objective-C
MLKSelfieSegmenterOptions *options = [[MLKSelfieSegmenterOptions alloc] init]; options.segmenterMode = MLKSegmenterModeSingleImage; options.shouldEnableRawSizeMask = YES;
לבסוף, מקבלים מופע של Segmenter
. מעבירים את האפשרויות שציינתם:
Swift
let segmenter = Segmenter.segmenter(options: options)
Objective-C
MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions: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
לאחת משיטות עיבוד התמונות של Segmenter
. אפשר להשתמש בשיטה process(image:)
האסינכרונית או בשיטה results(in:)
הסינכרונית.
כדי לבצע סגמנטציה של תמונת סלפי באופן סינכרוני:
Swift
var mask: [SegmentationMask] do { mask = try segmenter.results(in: image) } catch let error { print("Failed to perform segmentation with error: \(error.localizedDescription).") return } // Success. Get a segmentation mask here.
Objective-C
NSError *error; MLKSegmentationMask *mask = [segmenter resultsInImage:image error:&error]; if (error != nil) { // Error. return; } // Success. Get a segmentation mask here.
כדי לבצע סגמנטציה בתמונת סלפי באופן אסינכרוני:
Swift
segmenter.process(image) { mask, error in guard error == nil else { // Error. return } // Success. Get a segmentation mask here.
Objective-C
[segmenter processImage:image completion:^(MLKSegmentationMask * _Nullable mask, NSError * _Nullable error) { if (error != nil) { // Error. return; } // Success. Get a segmentation mask here. }];
4. הצגת מסיכת הפילוח
אפשר לקבל את תוצאת הפילוח כך:
Swift
let maskWidth = CVPixelBufferGetWidth(mask.buffer) let maskHeight = CVPixelBufferGetHeight(mask.buffer) CVPixelBufferLockBaseAddress(mask.buffer, CVPixelBufferLockFlags.readOnly) let maskBytesPerRow = CVPixelBufferGetBytesPerRow(mask.buffer) var maskAddress = CVPixelBufferGetBaseAddress(mask.buffer)!.bindMemory( to: Float32.self, capacity: maskBytesPerRow * maskHeight) for _ in 0...(maskHeight - 1) { for col in 0...(maskWidth - 1) { // Gets the confidence of the pixel in the mask being in the foreground. let foregroundConfidence: Float32 = maskAddress[col] } maskAddress += maskBytesPerRow / MemoryLayout<Float32>.size }
Objective-C
size_t width = CVPixelBufferGetWidth(mask.buffer); size_t height = CVPixelBufferGetHeight(mask.buffer); CVPixelBufferLockBaseAddress(mask.buffer, kCVPixelBufferLock_ReadOnly); size_t maskBytesPerRow = CVPixelBufferGetBytesPerRow(mask.buffer); float *maskAddress = (float *)CVPixelBufferGetBaseAddress(mask.buffer); for (int row = 0; row < height; ++row) { for (int col = 0; col < width; ++col) { // Gets the confidence of the pixel in the mask being in the foreground. float foregroundConfidence = maskAddress[col]; } maskAddress += maskBytesPerRow / sizeof(float); }
כדי לראות דוגמה מלאה לשימוש בתוצאות הפילוח, אפשר לעיין דוגמה במדריך למתחילים ל-ML Kit.
טיפים לשיפור הביצועים
איכות התוצאות תלויה באיכות של תמונת הקלט:
- כדי ש-ML Kit יקבל תוצאת פילוח מדויקת, התמונה צריכה להיות בגודל של לפחות 256x256 פיקסלים.
- אם אתם מבצעים סגמנטציה של סלפי באפליקציה בזמן אמת, כדאי גם לקחת בחשבון את המידות הכוללות של תמונות הקלט. העיבוד של תמונות קטנות יותר מתבצע מהר יותר. לכן, כדי לצמצם את זמן האחזור, כדאי לצלם תמונות ברזולוציות נמוכות יותר. עם זאת, חשוב לזכור את דרישות הרזולוציה שצוינו למעלה ולוודא שהנושא תופס כמה שיותר מהתמונה.
- גם מיקוד תמונה לא טוב יכול להשפיע על רמת הדיוק. אם לא מתקבלות תוצאות מקובלות, מבקשים מהמשתמש לצלם מחדש את התמונה.
אם אתם רוצים להשתמש בפילוח באפליקציה בזמן אמת, כדאי לפעול לפי ההנחיות הבאות כדי להשיג את קצב הפריימים הטוב ביותר:
- משתמשים במצב הפילוח
stream
. - כדאי לצלם תמונות ברזולוציה נמוכה יותר. עם זאת, חשוב לזכור גם את הדרישות בנוגע למידות תמונה ב-API הזה.
- כדי לעבד פריימים של וידאו, צריך להשתמש ב-API הסינכרוני
results(in:)
של כלי הפילוח. קוראים לשיטה הזו מהפונקציה captureOutput(_, didOutput:from:) בפונקציה AVCaptureVideoDataOutputSampleBufferDelegate כדי לקבל באופן סינכרוני תוצאות ממסגרת הווידאו הנתונה. יש לשמור על הערך alwaysDiscardsLateVideoFrames של AVCaptureVideoDataOutput כ-TRUE כדי לבצע ויסות נתונים (throttle) לקריאות לפלח. אם פריים חדש לסרטון יהפוך לזמין בזמן שהפילוח פועל, היא תוסר. - אם משתמשים בפלט של הפלח כדי ליצור שכבת-על של גרפיקה בתמונת הקלט, מקבלים קודם את התוצאה מ-ML Kit ואז מעבדים את התמונה ושכבת-העל בשלב אחד. כך תוכלו לעבד את משטח המסך פעם אחת בלבד עבור כל מסגרת קלט מעובדת. כדוגמה, אפשר לעיין במחלקות previewOverlayView ו-CameraViewController בדוגמה למתחילים של ML Kit.