| #import "FirebaseMlVisionPlugin.h" | 
 |  | 
 | @implementation FaceDetector | 
 | static FIRVisionFaceDetector *faceDetector; | 
 |  | 
 | + (void)handleDetection:(FIRVisionImage *)image | 
 |                 options:(NSDictionary *)options | 
 |                  result:(FlutterResult)result { | 
 |   FIRVision *vision = [FIRVision vision]; | 
 |   faceDetector = [vision faceDetectorWithOptions:[FaceDetector parseOptions:options]]; | 
 |  | 
 |   [faceDetector | 
 |       processImage:image | 
 |         completion:^(NSArray<FIRVisionFace *> *_Nullable faces, NSError *_Nullable error) { | 
 |           if (error) { | 
 |             [FLTFirebaseMlVisionPlugin handleError:error result:result]; | 
 |             return; | 
 |           } else if (!faces) { | 
 |             result(@[]); | 
 |             return; | 
 |           } | 
 |  | 
 |           NSMutableArray *faceData = [NSMutableArray array]; | 
 |           for (FIRVisionFace *face in faces) { | 
 |             id smileProb = face.hasSmilingProbability ? @(face.smilingProbability) : [NSNull null]; | 
 |             id leftProb = | 
 |                 face.hasLeftEyeOpenProbability ? @(face.leftEyeOpenProbability) : [NSNull null]; | 
 |             id rightProb = | 
 |                 face.hasRightEyeOpenProbability ? @(face.rightEyeOpenProbability) : [NSNull null]; | 
 |  | 
 |             NSDictionary *data = @{ | 
 |               @"left" : @(face.frame.origin.x), | 
 |               @"top" : @(face.frame.origin.y), | 
 |               @"width" : @(face.frame.size.width), | 
 |               @"height" : @(face.frame.size.height), | 
 |               @"headEulerAngleY" : face.hasHeadEulerAngleY ? @(face.headEulerAngleY) | 
 |                                                            : [NSNull null], | 
 |               @"headEulerAngleZ" : face.hasHeadEulerAngleZ ? @(face.headEulerAngleZ) | 
 |                                                            : [NSNull null], | 
 |               @"smilingProbability" : smileProb, | 
 |               @"leftEyeOpenProbability" : leftProb, | 
 |               @"rightEyeOpenProbability" : rightProb, | 
 |               @"trackingId" : face.hasTrackingID ? @(face.trackingID) : [NSNull null], | 
 |               @"landmarks" : @{ | 
 |                 @"bottomMouth" : [FaceDetector getLandmarkPosition:face | 
 |                                                           landmark:FIRFaceLandmarkTypeMouthBottom], | 
 |                 @"leftCheek" : [FaceDetector getLandmarkPosition:face | 
 |                                                         landmark:FIRFaceLandmarkTypeLeftCheek], | 
 |                 @"leftEar" : [FaceDetector getLandmarkPosition:face | 
 |                                                       landmark:FIRFaceLandmarkTypeLeftEar], | 
 |                 @"leftEye" : [FaceDetector getLandmarkPosition:face | 
 |                                                       landmark:FIRFaceLandmarkTypeLeftEye], | 
 |                 @"leftMouth" : [FaceDetector getLandmarkPosition:face | 
 |                                                         landmark:FIRFaceLandmarkTypeMouthLeft], | 
 |                 @"noseBase" : [FaceDetector getLandmarkPosition:face | 
 |                                                        landmark:FIRFaceLandmarkTypeNoseBase], | 
 |                 @"rightCheek" : [FaceDetector getLandmarkPosition:face | 
 |                                                          landmark:FIRFaceLandmarkTypeRightCheek], | 
 |                 @"rightEar" : [FaceDetector getLandmarkPosition:face | 
 |                                                        landmark:FIRFaceLandmarkTypeRightEar], | 
 |                 @"rightEye" : [FaceDetector getLandmarkPosition:face | 
 |                                                        landmark:FIRFaceLandmarkTypeRightEye], | 
 |                 @"rightMouth" : [FaceDetector getLandmarkPosition:face | 
 |                                                          landmark:FIRFaceLandmarkTypeMouthRight], | 
 |               }, | 
 |             }; | 
 |  | 
 |             [faceData addObject:data]; | 
 |           } | 
 |  | 
 |           result(faceData); | 
 |         }]; | 
 | } | 
 |  | 
 | + (id)getLandmarkPosition:(FIRVisionFace *)face landmark:(FIRFaceLandmarkType)landmarkType { | 
 |   FIRVisionFaceLandmark *landmark = [face landmarkOfType:landmarkType]; | 
 |   if (landmark) { | 
 |     return @[ landmark.position.x, landmark.position.y ]; | 
 |   } | 
 |  | 
 |   return [NSNull null]; | 
 | } | 
 |  | 
 | + (FIRVisionFaceDetectorOptions *)parseOptions:(NSDictionary *)optionsData { | 
 |   FIRVisionFaceDetectorOptions *options = [[FIRVisionFaceDetectorOptions alloc] init]; | 
 |  | 
 |   NSNumber *enableClassification = optionsData[@"enableClassification"]; | 
 |   if (enableClassification.boolValue) { | 
 |     options.classificationMode = FIRVisionFaceDetectorClassificationModeAll; | 
 |   } else { | 
 |     options.classificationMode = FIRVisionFaceDetectorClassificationModeNone; | 
 |   } | 
 |  | 
 |   NSNumber *enableLandmarks = optionsData[@"enableLandmarks"]; | 
 |   if (enableLandmarks.boolValue) { | 
 |     options.landmarkMode = FIRVisionFaceDetectorLandmarkModeAll; | 
 |   } else { | 
 |     options.landmarkMode = FIRVisionFaceDetectorLandmarkModeNone; | 
 |   } | 
 |  | 
 |   NSNumber *enableTracking = optionsData[@"enableTracking"]; | 
 |   options.trackingEnabled = enableTracking.boolValue; | 
 |  | 
 |   NSNumber *minFaceSize = optionsData[@"minFaceSize"]; | 
 |   options.minFaceSize = [minFaceSize doubleValue]; | 
 |  | 
 |   NSString *mode = optionsData[@"mode"]; | 
 |   if ([mode isEqualToString:@"accurate"]) { | 
 |     options.performanceMode = FIRVisionFaceDetectorPerformanceModeAccurate; | 
 |   } else if ([mode isEqualToString:@"fast"]) { | 
 |     options.performanceMode = FIRVisionFaceDetectorPerformanceModeFast; | 
 |   } | 
 |  | 
 |   return options; | 
 | } | 
 | @end |