1// 2// EXFaceDetector.m 3// Exponent 4// 5// Created by Stanisław Chmiela on 13.10.2017. 6// Copyright © 2017 650 Industries. All rights reserved. 7// 8 9#import <EXFaceDetector/EXFaceDetectorModule.h> 10#import <EXFaceDetector/EXFaceDetector.h> 11#import <ExpoModulesCore/EXFileSystemInterface.h> 12#import <EXFaceDetector/EXFaceDetectorUtils.h> 13#import <ExpoModulesCore/EXModuleRegistry.h> 14#import <EXFaceDetector/EXFaceEncoder.h> 15#import <EXFaceDetector/EXCSBufferOrientationCalculator.h> 16 17@interface EXFaceDetectorModule () 18 19@property (nonatomic, weak) EXModuleRegistry *moduleRegistry; 20 21@end 22 23@implementation EXFaceDetectorModule 24 25static NSFileManager *fileManager = nil; 26static NSDictionary *defaultDetectorOptions = nil; 27 28- (instancetype)initWithModuleRegistry:(EXModuleRegistry *)moduleRegistry 29{ 30 self = [super init]; 31 if (self) { 32 _moduleRegistry = moduleRegistry; 33 fileManager = [NSFileManager defaultManager]; 34 } 35 return self; 36} 37 38EX_EXPORT_MODULE(ExpoFaceDetector); 39 40- (NSDictionary *)constantsToExport 41{ 42 return [EXFaceDetectorUtils constantsToExport]; 43} 44 45- (void)setModuleRegistry:(EXModuleRegistry *)moduleRegistry 46{ 47 _moduleRegistry = moduleRegistry; 48} 49 50EX_EXPORT_METHOD_AS(detectFaces, detectFaces:(nonnull NSDictionary *)options resolver:(EXPromiseResolveBlock)resolve rejecter:(EXPromiseRejectBlock)reject) 51{ 52 NSString *uri = options[@"uri"]; 53 if (uri == nil) { 54 reject(@"E_FACE_DETECTION_FAILED", @"You must define a URI.", nil); 55 return; 56 } 57 58 NSURL *url = [NSURL URLWithString:uri]; 59 NSString *path = [url.path stringByStandardizingPath]; 60 61 NSException *exception; 62 id<EXFileSystemInterface> fileSystem = [_moduleRegistry getModuleImplementingProtocol:@protocol(EXFileSystemInterface)]; 63 if (!fileSystem || exception) { 64 reject(@"E_MODULE_UNAVAILABLE", @"No file system module", nil); 65 return; 66 } 67 68 if (!([fileSystem permissionsForURI:url] & EXFileSystemPermissionRead)) { 69 reject(@"E_FILESYSTEM_PERMISSIONS", [NSString stringWithFormat:@"File '%@' isn't readable.", uri], nil); 70 return; 71 } 72 73 @try { 74 UIImage *image = [[UIImage alloc] initWithContentsOfFile:path]; 75 CIImage *ciImage = image.CIImage; 76 if(!ciImage) { 77 ciImage = [CIImage imageWithCGImage:image.CGImage]; 78 } 79 ciImage = [ciImage imageByApplyingOrientation:[EXFaceDetectorUtils toCGImageOrientation:image.imageOrientation]]; 80 CIContext *context = [CIContext contextWithOptions:@{}]; 81 UIImage *temporaryImage = [UIImage imageWithCIImage:ciImage]; 82 CGRect tempImageRect = CGRectMake(0, 0, temporaryImage.size.width, temporaryImage.size.height); 83 CGImageRef cgImage = [context createCGImage:ciImage fromRect:tempImageRect]; 84 85 UIImage *finalImage = [UIImage imageWithCGImage:cgImage]; 86 EXFaceDetector* detector = [[EXFaceDetector alloc] initWithOptions: [EXFaceDetectorUtils mapOptions:options]]; 87 [detector detectFromImage:finalImage completionListener:^(NSArray<MLKFace *> * _Nullable faces, NSError * _Nullable error) { 88 NSMutableArray<NSDictionary*>* reportableFaces = [NSMutableArray new]; 89 90 if(faces.count > 0) { 91 EXFaceEncoder *encoder = [[EXFaceEncoder alloc] init]; 92 for(MLKFace* face in faces) 93 { 94 [reportableFaces addObject:[encoder encode:face]]; 95 } 96 } 97 98 CGImageRelease(cgImage); 99 if (error != nil) { 100 reject(@"E_FACE_DETECTION_FAILED", [exception description], nil); 101 } else { 102 resolve(@{ 103 @"faces" : reportableFaces, 104 @"image" : @{ 105 @"uri" : options[@"uri"], 106 @"width" : @(image.size.width), 107 @"height" : @(image.size.height), 108 @"orientation" : @([EXFaceDetectorModule exifOrientationFor:image.imageOrientation]) 109 } 110 }); 111 }}]; 112 } @catch (NSException *exception) { 113 reject(@"E_FACE_DETECTION_FAILED", [exception description], nil); 114 } 115} 116 117# pragma mark: - Utility methods 118 119// https://gist.github.com/steipete/4666527 120+ (int)exifOrientationFor:(UIImageOrientation)orientation 121{ 122 switch (orientation) { 123 case UIImageOrientationUp: 124 return 1; 125 case UIImageOrientationDown: 126 return 3; 127 case UIImageOrientationLeft: 128 return 8; 129 case UIImageOrientationRight: 130 return 6; 131 case UIImageOrientationUpMirrored: 132 return 2; 133 case UIImageOrientationDownMirrored: 134 return 4; 135 case UIImageOrientationLeftMirrored: 136 return 5; 137 case UIImageOrientationRightMirrored: 138 return 7; 139 } 140} 141 142@end 143