אפשר להשתמש ב-ML Kit כדי לזהות פנים בתמונות ובסרטונים.
אני רוצה לנסות
- שחקו עם האפליקציה לדוגמה כדי לראות דוגמה לשימוש ב-API הזה.
- אפשר לנסות את הקוד בעצמכם באמצעות Codelab.
לפני שמתחילים
- יש לכלול את רצפי ה-ML Kit ב-Podfile:
pod 'GoogleMLKit/FaceDetection', '3.2.0'
- אחרי שמתקינים או מעדכנים את ה-Pods של הפרויקט, צריך לפתוח את פרויקט ה-Xcode באמצעות
.xcworkspace
שלו. ערכת ML Kit נתמכת ב-Xcode מגרסה 12.4 ואילך.
הנחיות להזנת תמונה
עבור זיהוי פנים, עליך להשתמש בתמונה במידות של 480x360 פיקסלים לפחות. כדי ש-ML Kit יזהה באופן מדויק את הפנים, תמונות הקלט חייבות להכיל פנים שמיוצגות על ידי כמות מספיקה של נתוני פיקסלים. באופן כללי, כל פנים שרוצים לזהות בתמונה צריכות להיות בגודל של 100x100 פיקסלים לפחות. כדי לזהות את קווי המתאר של פנים, מערכת ML Kit מחייבת קלט ברזולוציה גבוהה יותר: כל פנים צריכות להיות בגודל של 200x200 פיקסלים לפחות.
אם מזהים פנים באפליקציה בזמן אמת, כדאי לשקול גם את הממדים הכוללים של התמונות שהוזנו. תמונות קטנות יותר יעובדו מהר יותר. כדי לקצר את זמן האחזור, כדאי לצלם תמונות ברזולוציה נמוכה יותר. עם זאת, חשוב לזכור את דרישות הדיוק שצוינו למעלה ולוודא שהפנים של המצולמים ממלאות כמה שיותר מהתמונה. בנוסף, כדאי לקרוא טיפים לשיפור הביצועים בזמן אמת.
גם מיקוד תמונה לא טוב יכול להשפיע על הדיוק. אם התוצאות לא מתקבלות מספיק, מבקשים מהמשתמש לצלם את התמונה מחדש.
גם הכיוון של הפנים ביחס למצלמה עשוי להשפיע על תכונות הפנים שמערכת ML Kit מזהה. למידע נוסף, ראו מושגים של זיהוי פנים.
1. הגדרה של מזהה הפנים
לפני שמחילים זיהוי פנים על תמונה, אם רוצים לשנות אחת מהגדרות ברירת המחדל של מזהה הפנים, צריך לציין את ההגדרות האלה באמצעות אובייקטFaceDetectorOptions
. תוכלו לשנות את ההגדרות הבאות:
הגדרות | |
---|---|
performanceMode |
fast (ברירת מחדל) | accurate
העדפה של מהירות או דיוק בזיהוי פנים. |
landmarkMode |
none (ברירת מחדל) | all
האם לנסות לזהות את סימני "הציונים" של הפנים – עיניים, אוזניים, אף, לחיים, פה – של כל הפנים שזוהו. |
contourMode |
none (ברירת מחדל) | all
האם לזהות את קווי המתאר של תווי פנים. המערכת מזהה קווי מתאר רק של הפנים הבולטות ביותר בתמונה. |
classificationMode |
none (ברירת מחדל) | all
אם לסווג פנים לקטגוריות כמו 'מחייך' ו'עיניים פקוחות'. |
minFaceSize |
CGFloat (ברירת המחדל: 0.1 )
הגדרה של גודל הפנים הרצוי הקטן ביותר, המבוטא כיחס בין רוחב הראש לרוחב התמונה. |
isTrackingEnabled |
false (ברירת מחדל) | true
האם להקצות או לא להקצות מזהה פנים, שיכול לשמש למעקב אחר פנים בתמונות שונות. לתשומת ליבך, כשזיהוי קווי המתאר מופעל, רק פנים אחדות מזוהות, ולכן מעקב אחר פנים לא מניב תוצאות מועילות. מסיבה זו, וכדי לשפר את מהירות הזיהוי, אין להפעיל גם את זיהוי קווי המתאר וגם את מעקב הפנים. |
לדוגמה, בנו אובייקט FaceDetectorOptions
כמו באחת מהדוגמאות הבאות:
Swift
// High-accuracy landmark detection and face classification let options = FaceDetectorOptions() options.performanceMode = .accurate options.landmarkMode = .all options.classificationMode = .all // Real-time contour detection of multiple faces // options.contourMode = .all
Objective-C
// High-accuracy landmark detection and face classification MLKFaceDetectorOptions *options = [[MLKFaceDetectorOptions alloc] init]; options.performanceMode = MLKFaceDetectorPerformanceModeAccurate; options.landmarkMode = MLKFaceDetectorLandmarkModeAll; options.classificationMode = MLKFaceDetectorClassificationModeAll; // Real-time contour detection of multiple faces // options.contourMode = MLKFaceDetectorContourModeAll;
2. הכנת התמונה לקלט
כדי לזהות פנים בתמונה, מעבירים את התמונה כ-UIImage
או כ-CMSampleBufferRef
ל-FaceDetector
בשיטה process(_:completion:)
או results(in:)
:
יוצרים אובייקט 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. הורדת מופע של FaceDetector
מקבלים מופע של FaceDetector
:
Swift
let faceDetector = FaceDetector.faceDetector(options: options)
Objective-C
MLKFaceDetector *faceDetector = [MLKFaceDetector faceDetectorWithOptions:options];
4. עיבוד התמונה
לאחר מכן, מעבירים את התמונה ל-methodprocess()
:
Swift
weak var weakSelf = self faceDetector.process(visionImage) { faces, error in guard let strongSelf = weakSelf else { print("Self is nil!") return } guard error == nil, let faces = faces, !faces.isEmpty else { // ... return } // Faces detected // ... }
Objective-C
[faceDetector processImage:image completion:^(NSArray<MLKFace *> *faces, NSError *error) { if (error != nil) { return; } if (faces.count > 0) { // Recognized faces } }];
5. קבלת מידע על פנים שזוהו
אם הפעולה של זיהוי הפנים תסתיים בהצלחה, המערכת תעביר מערך של אובייקטים מסוגFace
ל-handler ההשלמה. כל
אובייקט Face
מייצג פנים שזוהו בתמונה. לגבי כל פנים, אפשר לראות את הקואורדינטות התוחמות שלהן בתמונת הקלט, וגם כל מידע אחר שהגדרתם באמצעות התכונה 'זיהוי פנים'. לדוגמה:
Swift
for face in faces { let frame = face.frame if face.hasHeadEulerAngleX { let rotX = face.headEulerAngleX // Head is rotated to the uptoward rotX degrees } if face.hasHeadEulerAngleY { let rotY = face.headEulerAngleY // Head is rotated to the right rotY degrees } if face.hasHeadEulerAngleZ { let rotZ = face.headEulerAngleZ // Head is tilted sideways rotZ degrees } // If landmark detection was enabled (mouth, ears, eyes, cheeks, and // nose available): if let leftEye = face.landmark(ofType: .leftEye) { let leftEyePosition = leftEye.position } // If contour detection was enabled: if let leftEyeContour = face.contour(ofType: .leftEye) { let leftEyePoints = leftEyeContour.points } if let upperLipBottomContour = face.contour(ofType: .upperLipBottom) { let upperLipBottomPoints = upperLipBottomContour.points } // If classification was enabled: if face.hasSmilingProbability { let smileProb = face.smilingProbability } if face.hasRightEyeOpenProbability { let rightEyeOpenProb = face.rightEyeOpenProbability } // If face tracking was enabled: if face.hasTrackingID { let trackingId = face.trackingID } }
Objective-C
for (MLKFace *face in faces) { // Boundaries of face in image CGRect frame = face.frame; if (face.hasHeadEulerAngleX) { CGFloat rotX = face.headEulerAngleX; // Head is rotated to the upward rotX degrees } if (face.hasHeadEulerAngleY) { CGFloat rotY = face.headEulerAngleY; // Head is rotated to the right rotY degrees } if (face.hasHeadEulerAngleZ) { CGFloat rotZ = face.headEulerAngleZ; // Head is tilted sideways rotZ degrees } // If landmark detection was enabled (mouth, ears, eyes, cheeks, and // nose available): MLKFaceLandmark *leftEar = [face landmarkOfType:FIRFaceLandmarkTypeLeftEar]; if (leftEar != nil) { MLKVisionPoint *leftEarPosition = leftEar.position; } // If contour detection was enabled: MLKFaceContour *upperLipBottomContour = [face contourOfType:FIRFaceContourTypeUpperLipBottom]; if (upperLipBottomContour != nil) { NSArray<MLKVisionPoint *> *upperLipBottomPoints = upperLipBottomContour.points; if (upperLipBottomPoints.count > 0) { NSLog("Detected the bottom contour of the subject's upper lip.") } } // If classification was enabled: if (face.hasSmilingProbability) { CGFloat smileProb = face.smilingProbability; } if (face.hasRightEyeOpenProbability) { CGFloat rightEyeOpenProb = face.rightEyeOpenProbability; } // If face tracking was enabled: if (face.hasTrackingID) { NSInteger trackingID = face.trackingID; } }
דוגמה למתארי פנים
כשמפעילים זיהוי של קווי פנים, תוצג רשימה של נקודות לכל תכונת פנים שזוהתה. הנקודות האלה מייצגות את הצורה של התכונה. פרטים על האופן שבו קווי מתאר מיוצגים במאמר מושגים של זיהוי פנים
בתמונה הבאה אפשר לראות איך הנקודות האלה ממופות לפנים. אפשר ללחוץ על התמונה כדי להגדיל אותה:
זיהוי פנים בזמן אמת
אם אתם רוצים להשתמש בזיהוי פנים באפליקציה בזמן אמת, פעלו לפי ההנחיות הבאות כדי לשמור על קצב הפריימים הטוב ביותר:
תוכלו להגדיר את גלאי הפנים כך שישתמשו בזיהוי קווי פנים או בסיווג ובזיהוי של ציוני פנים, אבל לא בשניהם:
זיהוי קווי מתאר
זיהוי של ציוני דרך
סיווג
זיהוי וסיווג של ציוני דרך
זיהוי קווי מתאר וזיהוי ציוני דרך
זיהוי וסיווג קווי מתאר
זיהוי קווי מתאר, זיהוי ציוני דרך וסיווגהפעלת מצב
fast
(מופעל כברירת מחדל).כדאי לצלם תמונות ברזולוציה נמוכה יותר. עם זאת, חשוב לזכור את הדרישות לגבי מידות התמונה של ה-API הזה.
- לעיבוד פריימים של וידאו, יש להשתמש ב-API הסינכרוני
results(in:)
של המזהה. כדאי להפעיל את השיטה הזו מהפונקציהcaptureOutput(_, didOutput:from:)
שלAVCaptureVideoDataOutputSampleBufferDelegate
כדי לקבל באופן סינכרוני תוצאות מהפריים הנתון של הסרטון. יש לשמור אתalwaysDiscardsLateVideoFrames
שלAVCaptureVideoDataOutput
בתורtrue
כדי לווסת את הקריאות לחיישן. אם בזמן שהמזהה פועל פריים וידאו חדש, הוא יוסר. - אם משתמשים בפלט של המזהה כדי ליצור שכבת-על של גרפיקה בתמונת הקלט, קודם צריך לקבל את התוצאה מ-ML Kit ואז לעבד את התמונה ואת שכבת-העל בשלב אחד. באופן הזה, מתבצע רינדור על פני המסך פעם אחת בלבד לכל מסגרת קלט מעובדת. כדי לראות דוגמה, אפשר לעיין ב-updatePreviewOverlayViewWithLastFrame בדוגמה למתחילים של ערכת ML.