זיהוי פנים עם ערכת ML ב-iOS

אתה יכול להשתמש בערכת ML כדי לזהות פרצופים בתמונות ובווידאו.

לפני שאתה מתחיל

  1. אם עדיין לא הוספת את Firebase לאפליקציה שלך, עשה זאת על ידי ביצוע השלבים במדריך לתחילת העבודה .
  2. כלול את ספריות ML Kit ב-Podfile שלך:
    pod 'Firebase/MLVision', '6.25.0'
    # If you want to detect face contours (landmark detection and classification
    # don't require this additional model):
    pod 'Firebase/MLVisionFaceModel', '6.25.0'
    
    לאחר שתתקין או תעדכן את ה-Pods של הפרויקט שלך, הקפד לפתוח את פרויקט Xcode שלך ​​באמצעות .xcworkspace שלו.
  3. באפליקציה שלך, ייבא את Firebase:

    מָהִיר

    import Firebase

    Objective-C

    @import Firebase;

הנחיות לקלט תמונה

כדי ש-ML Kit יזהה במדויק פרצופים, תמונות קלט חייבות להכיל פרצופים המיוצגים על ידי מספיק נתוני פיקסלים. באופן כללי, כל פנים שאתה רוצה לזהות בתמונה צריך להיות לפחות 100x100 פיקסלים. אם אתה רוצה לזהות את קווי המתאר של הפנים, ערכת ML דורשת קלט ברזולוציה גבוהה יותר: כל פנים צריך להיות לפחות 200x200 פיקסלים.

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

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

כיוון הפנים ביחס למצלמה יכול גם להשפיע על תווי הפנים שמזהה ML Kit. ראה מושגי זיהוי פנים .

1. הגדר את גלאי הפנים

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

הגדרות
performanceMode fast (ברירת מחדל) | accurate

העדיפו מהירות או דיוק בעת זיהוי פנים.

landmarkMode none (ברירת מחדל) | all

האם לנסות לזהות את "ציוני הדרך" של הפנים - עיניים, אוזניים, אף, לחיים, פה - של כל הפנים שזוהו.

contourMode none (ברירת מחדל) | all

האם לזהות את קווי המתאר של תווי הפנים. קווי מתאר מזוהים רק עבור הפנים הבולטות ביותר בתמונה.

classificationMode none (ברירת מחדל) | all

האם לסווג פרצופים לקטגוריות כמו "מחייכים" ו"עיניים פקוחות" או לא.

minFaceSize CGFloat (ברירת מחדל: 0.1 )

הגודל המינימלי, יחסית לתמונה, של פרצופים לזיהוי.

isTrackingEnabled false (ברירת מחדל) | true

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

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

לדוגמה, בנה אובייקט VisionFaceDetectorOptions כמו אחת מהדוגמאות הבאות:

מָהִיר

// High-accuracy landmark detection and face classification
let options = VisionFaceDetectorOptions()
options.performanceMode = .accurate
options.landmarkMode = .all
options.classificationMode = .all

// Real-time contour detection of multiple faces
let options = VisionFaceDetectorOptions()
options.contourMode = .all

Objective-C

// High-accuracy landmark detection and face classification
FIRVisionFaceDetectorOptions *options = [[FIRVisionFaceDetectorOptions alloc] init];
options.performanceMode = FIRVisionFaceDetectorPerformanceModeAccurate;
options.landmarkMode = FIRVisionFaceDetectorLandmarkModeAll;
options.classificationMode = FIRVisionFaceDetectorClassificationModeAll;

// Real-time contour detection of multiple faces
FIRVisionFaceDetectorOptions *options = [[FIRVisionFaceDetectorOptions alloc] init];
options.contourMode = FIRVisionFaceDetectorContourModeAll;

2. הפעל את גלאי הפנים

כדי לזהות פרצופים בתמונה, העבר את התמונה כ- UIImage או CMSampleBufferRef לשיטת detect(in:) של VisionFaceDetector :

  1. קבל מופע של VisionFaceDetector :

    מָהִיר

    lazy var vision = Vision.vision()
    
    let faceDetector = vision.faceDetector(options: options)
    

    Objective-C

    FIRVision *vision = [FIRVision vision];
    FIRVisionFaceDetector *faceDetector = [vision faceDetector];
    // Or, to change the default settings:
    // FIRVisionFaceDetector *faceDetector =
    //     [vision faceDetectorWithOptions:options];
    
  2. צור אובייקט VisionImage באמצעות UIImage או CMSampleBufferRef .

    כדי להשתמש UIImage :

    1. במידת הצורך, סובב את התמונה כך שהמאפיין imageOrientation שלה יהיה .up .
    2. צור אובייקט VisionImage באמצעות UIImage המסובב כהלכה. אל תציין מטא נתונים כלשהם של סיבוב - יש להשתמש בערך ברירת המחדל, .topLeft .

      מָהִיר

      let image = VisionImage(image: uiImage)

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];

    כדי להשתמש ב- CMSampleBufferRef :

    1. צור אובייקט VisionImageMetadata המציין את הכיוון של נתוני התמונה הכלולים במאגר CMSampleBufferRef .

      כדי לקבל את כיוון התמונה:

      מָהִיר

      func imageOrientation(
          deviceOrientation: UIDeviceOrientation,
          cameraPosition: AVCaptureDevice.Position
          ) -> VisionDetectorImageOrientation {
          switch deviceOrientation {
          case .portrait:
              return cameraPosition == .front ? .leftTop : .rightTop
          case .landscapeLeft:
              return cameraPosition == .front ? .bottomLeft : .topLeft
          case .portraitUpsideDown:
              return cameraPosition == .front ? .rightBottom : .leftBottom
          case .landscapeRight:
              return cameraPosition == .front ? .topRight : .bottomRight
          case .faceDown, .faceUp, .unknown:
              return .leftTop
          }
      }

      Objective-C

      - (FIRVisionDetectorImageOrientation)
          imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                                 cameraPosition:(AVCaptureDevicePosition)cameraPosition {
        switch (deviceOrientation) {
          case UIDeviceOrientationPortrait:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationLeftTop;
            } else {
              return FIRVisionDetectorImageOrientationRightTop;
            }
          case UIDeviceOrientationLandscapeLeft:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationBottomLeft;
            } else {
              return FIRVisionDetectorImageOrientationTopLeft;
            }
          case UIDeviceOrientationPortraitUpsideDown:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationRightBottom;
            } else {
              return FIRVisionDetectorImageOrientationLeftBottom;
            }
          case UIDeviceOrientationLandscapeRight:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationTopRight;
            } else {
              return FIRVisionDetectorImageOrientationBottomRight;
            }
          default:
            return FIRVisionDetectorImageOrientationTopLeft;
        }
      }

      לאחר מכן, צור את אובייקט המטא נתונים:

      מָהִיר

      let cameraPosition = AVCaptureDevice.Position.back  // Set to the capture device you used.
      let metadata = VisionImageMetadata()
      metadata.orientation = imageOrientation(
          deviceOrientation: UIDevice.current.orientation,
          cameraPosition: cameraPosition
      )

      Objective-C

      FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
      AVCaptureDevicePosition cameraPosition =
          AVCaptureDevicePositionBack;  // Set to the capture device you used.
      metadata.orientation =
          [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                       cameraPosition:cameraPosition];
    2. צור אובייקט VisionImage באמצעות האובייקט CMSampleBufferRef והמטא נתונים של הסיבוב:

      מָהִיר

      let image = VisionImage(buffer: sampleBuffer)
      image.metadata = metadata

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. לאחר מכן, העבירו את התמונה לשיטת detect(in:) :

    מָהִיר

    faceDetector.process(visionImage) { faces, error in
      guard error == nil, let faces = faces, !faces.isEmpty else {
        // ...
        return
      }
    
      // Faces detected
      // ...
    }
    

    Objective-C

    [faceDetector detectInImage:image
                     completion:^(NSArray<FIRVisionFace *> *faces,
                                  NSError *error) {
      if (error != nil) {
        return;
      } else if (faces != nil) {
        // Recognized faces
      }
    }];
    

3. קבל מידע על פרצופים שזוהו

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

מָהִיר

for face in faces {
  let frame = face.frame
  if face.hasHeadEulerAngleY {
    let rotY = face.headEulerAngleY  // Head is rotated to the right rotY degrees
  }
  if face.hasHeadEulerAngleZ {
    let rotZ = face.headEulerAngleZ  // Head is rotated upward 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 (FIRVisionFace *face in faces) {
  // Boundaries of face in image
  CGRect frame = face.frame;

  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):
  FIRVisionFaceLandmark *leftEar = [face landmarkOfType:FIRFaceLandmarkTypeLeftEar];
  if (leftEar != nil) {
    FIRVisionPoint *leftEarPosition = leftEar.position;
  }

  // If contour detection was enabled:
  FIRVisionFaceContour *upperLipBottomContour = [face contourOfType:FIRFaceContourTypeUpperLipBottom];
  if (upperLipBottomContour != nil) {
    NSArray<FIRVisionPoint *> *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 זה.

  • מצערת קוראת לגלאי. אם מסגרת וידאו חדשה הופכת לזמינה בזמן שהגלאי פועל, שחרר את המסגרת.
  • אם אתה משתמש בפלט של הגלאי כדי לשכב גרפיקה על תמונת הקלט, תחילה קבל את התוצאה מ-ML Kit, ולאחר מכן עבד את התמונה ואת שכבת העל בצעד אחד. על ידי כך, אתה מעבד למשטח התצוגה רק פעם אחת עבור כל מסגרת קלט. ראה את המחלקות previewOverlayView ו- FIRDetectionOverlayView באפליקציה לדוגמה לראווה לדוגמא.