1// 2// EXFaceEncoder.m 3// Exponent 4// 5// Created by Stanisław Chmiela on 23.10.2017. 6// Copyright © 2017 650 Industries. All rights reserved. 7// 8 9#import <EXFaceDetector/EXFaceEncoder.h> 10#import <EXFaceDetector/EXFaceDetectorUtils.h> 11#import <GoogleMLKit/MLKit.h> 12 13#define cDefaultFloatComparisonEpsilon 0.0001 14#define cModEqualFloatsWithEpsilon(dividend, divisor, modulo, epsilon) \ 15fabs( fmod(dividend, divisor) - modulo ) < epsilon 16#define cModEqualFloats(dividend, divisor, modulo) \ 17cModEqualFloatsWithEpsilon(dividend, divisor, modulo, cDefaultFloatComparisonEpsilon) 18 19@interface EXFaceEncoder() 20 21@property (assign, nonatomic) BOOL swapWidthAndHeight; 22@property CGAffineTransform transform; 23@property EXFaceDetectionAngleTransformBlock angleTransformer; 24 25@end 26 27@implementation EXFaceEncoder 28 29- (instancetype)init 30{ 31 return [self initWithTransform:CGAffineTransformIdentity]; 32} 33 34- (instancetype)initWithTransform:(CGAffineTransform)pointTransformer 35{ 36 EXFaceDetectionAngleTransformBlock transformer = ^(float angle) {return angle;}; 37 return [self initWithTransform:pointTransformer withRotationTransform:transformer]; 38} 39 40- (instancetype)initWithRotationTransform:(EXFaceDetectionAngleTransformBlock)rotationTransform 41{ 42 return [self initWithTransform:CGAffineTransformIdentity withRotationTransform:rotationTransform]; 43} 44 45- (instancetype)initWithTransform:(CGAffineTransform)transform withRotationTransform:(EXFaceDetectionAngleTransformBlock)rotationTransform 46{ 47 self = [super init]; 48 if (self) { 49 _transform = transform; 50 _angleTransformer = rotationTransform; 51 } 52 return self; 53} 54 55 56- (NSDictionary *)encode:(MLKFace *)face 57{ 58 CGRect bounds = CGRectApplyAffineTransform(face.frame, _transform); 59 NSDictionary *initialDictionary = @{ 60 @"bounds" : @{ 61 @"size" : @{ 62 @"width" : @(bounds.size.width), 63 @"height" : @(bounds.size.height) 64 }, 65 @"origin" : @{ 66 @"x" : @(bounds.origin.x), 67 @"y" : @(bounds.origin.y) 68 } 69 } 70 }; 71 NSMutableDictionary *encodedFace = [[NSMutableDictionary alloc] initWithDictionary:initialDictionary]; 72 [self putAFloat:face.smilingProbability forKey:@"smilingProbability" toDictionary:encodedFace ifValueIsValid:face.hasSmilingProbability]; 73 [self putAnInteger:face.trackingID forKey:@"faceID" toDictionary:encodedFace ifValueIsValid:face.hasTrackingID]; 74 75 MLKFaceLandmark *leftEar = [face landmarkOfType:MLKFaceLandmarkTypeLeftEar]; 76 if(leftEar != nil) { 77 [self putAPoint:leftEar.position forKey:@"leftEarPosition" toDictionary:encodedFace]; 78 } 79 MLKFaceLandmark *rightEar = [face landmarkOfType:MLKFaceLandmarkTypeRightEar]; 80 if(rightEar != nil) { 81 [self putAPoint:rightEar.position forKey:@"rightEarPosition" toDictionary:encodedFace]; 82 } 83 84 MLKFaceLandmark *leftEye = [face landmarkOfType:MLKFaceLandmarkTypeLeftEye]; 85 if (leftEye != nil) { 86 [self putAPoint:leftEye.position forKey:@"leftEyePosition" toDictionary:encodedFace]; 87 } 88 MLKFaceLandmark *rightEye = [face landmarkOfType:MLKFaceLandmarkTypeRightEye]; 89 if(rightEye) { 90 [self putAPoint:rightEye.position forKey:@"rightEyePosition" toDictionary:encodedFace]; 91 } 92 93 [self putAFloat:face.leftEyeOpenProbability forKey:@"leftEyeOpenProbability" toDictionary:encodedFace ifValueIsValid:face.hasLeftEyeOpenProbability]; 94 [self putAFloat:face.rightEyeOpenProbability forKey:@"rightEyeOpenProbability" toDictionary:encodedFace ifValueIsValid:face.hasRightEyeOpenProbability]; 95 96 MLKFaceLandmark *leftCheek = [face landmarkOfType:MLKFaceLandmarkTypeLeftCheek]; 97 if(leftCheek) { 98 [self putAPoint:leftCheek.position forKey:@"leftCheekPosition" toDictionary:encodedFace]; 99 } 100 MLKFaceLandmark *rightCheek = [face landmarkOfType:MLKFaceLandmarkTypeRightCheek]; 101 if(rightCheek) { 102 [self putAPoint:rightCheek.position forKey:@"rightCheekPosition" toDictionary:encodedFace]; 103 } 104 105 MLKFaceLandmark *leftMouth = [face landmarkOfType:MLKFaceLandmarkTypeMouthLeft]; 106 if(leftMouth) { 107 [self putAPoint:leftMouth.position forKey:@"leftMouthPosition" toDictionary:encodedFace]; 108 } 109 MLKFaceLandmark *rightMouth = [face landmarkOfType:MLKFaceLandmarkTypeMouthRight]; 110 if(rightMouth) { 111 [self putAPoint:rightMouth.position forKey:@"rightMouthPosition" toDictionary:encodedFace]; 112 } 113 MLKFaceLandmark *bottomMouth = [face landmarkOfType:MLKFaceLandmarkTypeMouthBottom]; 114 if(bottomMouth) { 115 [self putAPoint:bottomMouth.position forKey:@"bottomMouthPosition" toDictionary:encodedFace]; 116 } 117 118 MLKFaceLandmark *noseBase = [face landmarkOfType:MLKFaceLandmarkTypeNoseBase]; 119 if(noseBase) { 120 [self putAPoint:noseBase.position forKey:@"noseBasePosition" toDictionary:encodedFace]; 121 } 122 123 [self putAFloat:face.headEulerAngleY forKey:@"yawAngle" toDictionary:encodedFace ifValueIsValid:face.hasHeadEulerAngleY]; 124 125 [self putAFloat:self.angleTransformer(face.headEulerAngleZ) forKey:@"rollAngle" toDictionary:encodedFace ifValueIsValid:face.hasHeadEulerAngleZ]; 126 127 return encodedFace; 128} 129 130- (void)putAPoint:(MLKVisionPoint *)point 131 forKey:(NSString *)key 132 toDictionary:(NSMutableDictionary *)dictionary 133{ 134 CGPoint transformedPoint = CGPointApplyAffineTransform([self toPoint:point], _transform); 135 [dictionary setObject:@{ @"x" : @(transformedPoint.x), @"y" : @(transformedPoint.y) } forKey:key]; 136} 137 138- (void)putAFloat:(CGFloat)value forKey:(NSString *)key toDictionary:(NSMutableDictionary *)dictionary ifValueIsValid:(BOOL)floatIsValid 139{ 140 if (floatIsValid) { 141 [dictionary setObject:@(value) forKey:key]; 142 } 143} 144 145- (void)putAnInteger:(NSUInteger)value forKey:(NSString *)key toDictionary:(NSMutableDictionary *)dictionary ifValueIsValid:(BOOL)integerIsValid 146{ 147 if (integerIsValid) { 148 [dictionary setObject:@(value) forKey:key]; 149 } 150} 151 152- (CGFloat)rollAngleFromTransform:(CGAffineTransform)transform 153{ 154 return atan2f(transform.b, transform.a); 155} 156 157- (CGFloat)radianAngleToDegrees:(CGFloat)angle 158{ 159 return angle * (180 / M_PI); 160} 161 162- (CGPoint)toPoint:(MLKVisionPoint *)visionPoint 163{ 164 return CGPointMake([visionPoint x], [visionPoint y]); 165} 166 167@end 168