אפשר להשתמש ב-ML Kit כדי לזהות פנים בתמונות ובסרטונים.
רוצה לנסות?
- מומלץ לשחק עם האפליקציה לדוגמה כדי .
- נסו את הקוד בעצמכם בעזרת Codelab.
לפני שמתחילים
- כוללים ב-Podfile את רצפי ה-ML Kit הבאים:
pod 'GoogleMLKit/FaceDetection', '15.5.0'
- אחרי שמתקינים או מעדכנים את קבוצות ה-Pod של הפרויקט, פותחים את פרויקט 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:)
של הגלאי. שיחת טלפון שיטה זו מAVCaptureVideoDataOutputSampleBufferDelegate
פונקציהcaptureOutput(_, didOutput:from:)
לקבלת תוצאות בסרטון הנתון באופן סינכרוני מסגרת. שמור את שלAVCaptureVideoDataOutput
alwaysDiscardsLateVideoFrames
בתורtrue
כדי לווסת שיחות למזהה. אם תג חדש פריים הווידאו יהפוך לזמין כשהגלאי פועל, הוא יוסר. - אם משתמשים בפלט של הגלאי כדי להציג גרפיקה בשכבת-על מקבלים קודם את התוצאה מ-ML Kit ואז מעבדים את התמונה וליצור שכבת-על בשלב אחד. כך תוכלו להציג את משטח המסך רק פעם אחת לכל מסגרת קלט שעברה עיבוד. אפשר לעיין בתצוגה updatePreviewOverlayViewWithLastFrame בדוגמת המדריך למתחילים ל-ML Kit.