// // RNFaceEncoder.m // RCTCamera // // Created by Joao Guilherme Daros Fidelis on 21/01/18. // #import "RNFaceEncoder.h" #if __has_include() #define cDefaultFloatComparisonEpsilon 0.0001 #define cModEqualFloatsWithEpsilon(dividend, divisor, modulo, epsilon) \ fabs( fmod(dividend, divisor) - modulo ) < epsilon #define cModEqualFloats(dividend, divisor, modulo) \ cModEqualFloatsWithEpsilon(dividend, divisor, modulo, cDefaultFloatComparisonEpsilon) @interface RNFaceEncoder() @property (assign, nonatomic) BOOL swapWidthAndHeight; @property (assign, nonatomic) CGAffineTransform transform; @property (assign, nonatomic) CGFloat rollAngleDegreesFromTransform; @end @implementation RNFaceEncoder - (instancetype)init { return [self initWithTransform:CGAffineTransformIdentity]; } - (instancetype)initWithTransform:(CGAffineTransform)transform { self = [super init]; if (self) { _transform = transform; _rollAngleDegreesFromTransform = [self radianAngleToDegrees:[self rollAngleFromTransform:_transform]]; _swapWidthAndHeight = cModEqualFloats(_rollAngleDegreesFromTransform + 360, 180, 90); } return self; } - (NSDictionary *)encode:(GMVFaceFeature *)face { CGRect bounds = CGRectApplyAffineTransform(face.bounds, _transform); NSDictionary *initialDictionary = @{ @"bounds" : @{ @"size" : @{ @"width" : @(_swapWidthAndHeight ? bounds.size.height : bounds.size.width), @"height" : @(_swapWidthAndHeight ? bounds.size.width : bounds.size.height) }, @"origin" : @{ @"x" : @(bounds.origin.x), @"y" : @(bounds.origin.y) } } }; NSMutableDictionary *encodedFace = [[NSMutableDictionary alloc] initWithDictionary:initialDictionary]; [self putAFloat:face.smilingProbability forKey:@"smilingProbability" toDictionary:encodedFace ifValueIsValid:face.hasSmilingProbability]; [self putAnInteger:face.trackingID forKey:@"faceID" toDictionary:encodedFace ifValueIsValid:face.hasTrackingID]; [self putAPoint:face.leftEarPosition forKey:@"leftEarPosition" toDictionary:encodedFace ifValueIsValid:face.hasLeftEarPosition]; [self putAPoint:face.rightEarPosition forKey:@"rightEarPosition" toDictionary:encodedFace ifValueIsValid:face.hasRightEarPosition]; [self putAPoint:face.leftEyePosition forKey:@"leftEyePosition" toDictionary:encodedFace ifValueIsValid:face.hasLeftEyePosition]; [self putAFloat:face.leftEyeOpenProbability forKey:@"leftEyeOpenProbability" toDictionary:encodedFace ifValueIsValid:face.hasLeftEyeOpenProbability]; [self putAPoint:face.rightEyePosition forKey:@"rightEyePosition" toDictionary:encodedFace ifValueIsValid:face.hasRightEyePosition]; [self putAFloat:face.rightEyeOpenProbability forKey:@"rightEyeOpenProbability" toDictionary:encodedFace ifValueIsValid:face.hasRightEyeOpenProbability]; [self putAPoint:face.leftCheekPosition forKey:@"leftCheekPosition" toDictionary:encodedFace ifValueIsValid:face.hasLeftCheekPosition]; [self putAPoint:face.rightCheekPosition forKey:@"rightCheekPosition" toDictionary:encodedFace ifValueIsValid:face.hasRightCheekPosition]; [self putAPoint:face.leftMouthPosition forKey:@"leftMouthPosition" toDictionary:encodedFace ifValueIsValid:face.hasLeftMouthPosition]; [self putAPoint:face.mouthPosition forKey:@"mouthPosition" toDictionary:encodedFace ifValueIsValid:face.hasMouthPosition]; [self putAPoint:face.rightMouthPosition forKey:@"rightMouthPosition" toDictionary:encodedFace ifValueIsValid:face.hasRightMouthPosition]; [self putAPoint:face.bottomMouthPosition forKey:@"bottomMouthPosition" toDictionary:encodedFace ifValueIsValid:face.hasBottomMouthPosition]; [self putAPoint:face.noseBasePosition forKey:@"noseBasePosition" toDictionary:encodedFace ifValueIsValid:face.hasNoseBasePosition]; [self putAFloat:face.headEulerAngleY forKey:@"yawAngle" toDictionary:encodedFace ifValueIsValid:face.hasHeadEulerAngleY]; [self putAFloat:-(face.headEulerAngleZ - _rollAngleDegreesFromTransform) forKey:@"rollAngle" toDictionary:encodedFace ifValueIsValid:face.hasHeadEulerAngleZ]; return encodedFace; } - (void)putAPoint:(CGPoint)point forKey:(NSString *)key toDictionary:(NSMutableDictionary *)dictionary ifValueIsValid:(BOOL)pointIsValid { if (pointIsValid) { CGPoint transformedPoint = CGPointApplyAffineTransform(point, _transform); [dictionary setObject:@{ @"x" : @(transformedPoint.x), @"y" : @(transformedPoint.y) } forKey:key]; } } - (void)putAFloat:(CGFloat)value forKey:(NSString *)key toDictionary:(NSMutableDictionary *)dictionary ifValueIsValid:(BOOL)floatIsValid { if (floatIsValid) { [dictionary setObject:@(value) forKey:key]; } } - (void)putAnInteger:(NSUInteger)value forKey:(NSString *)key toDictionary:(NSMutableDictionary *)dictionary ifValueIsValid:(BOOL)integerIsValid { if (integerIsValid) { [dictionary setObject:@(value) forKey:key]; } } - (CGFloat)rollAngleFromTransform:(CGAffineTransform)transform { return atan2f(transform.b, transform.a); } - (CGFloat)radianAngleToDegrees:(CGFloat)angle { return angle * (180 / M_PI); } @end #endif