#import #import #import "PGFaceProcessor.h" #include @interface PGRealtimeFaceTracker : NSObject - (NSDictionary *)resultForGeometry:(NSArray *)raw settings:(PGBeautySettings)s time:(CMTime)time extent:(CGRect)extent; @end @interface PGFaceProcessor (ProbeWarp) - (CIImage *)warpImage:(CIImage *)image geometry:(NSArray *)geometry settings:(PGBeautySettings)s extent:(CGRect)extent; - (NSArray *)observationsInImage:(CIImage *)image settings:(PGBeautySettings)s error:(NSError **)error; @end @interface ProbeFaceProcessor : PGFaceProcessor @property (nonatomic,copy) NSArray *authoredDetections; @end @implementation ProbeFaceProcessor - (NSArray *)observationsInImage:(CIImage *)image settings:(PGBeautySettings)s error:(NSError **)error {return self.authoredDetections?:@[];} @end static NSDictionary *Geometry(CGRect box, double localJitter, BOOL has76) { NSMutableDictionary *g=[@{@"box":@[@(box.origin.x),@(box.origin.y),@(box.size.width),@(box.size.height)]} mutableCopy]; for(NSString *name in @[@"leftEye",@"rightEye",@"leftBrow",@"rightBrow",@"nose",@"lips",@"mouth",@"contour"]){ double x=[name isEqual:@"rightEye"]?.7:.3; g[name]=@[@[@(box.origin.x+(x+localJitter)*box.size.width),@(box.origin.y+.65*box.size.height)]]; } if(has76){ NSMutableArray *p=[NSMutableArray new]; for(NSUInteger i=0;i<76;i++)[p addObject:@[@.5,@.5]]; p[6]=@[@.34,@.65];p[13]=@[@.66,@.65]; p[46]=@[@.5,@.68];p[47]=@[@.5,@.60];p[48]=@[@.5,@.52];p[49]=@[@.5,@.45];p[52]=@[@.5,@.42]; p[29]=@[@.5,@.39];p[36]=@[@.54,@.36];p[37]=@[@.5,@.35];p[38]=@[@.46,@.36]; p[26]=@[@.42,@.40];p[34]=@[@.58,@.40];p[35]=@[@.57,@.36];p[39]=@[@.43,@.36]; for(NSUInteger i=0;i<17;i++){double t=M_PI+M_PI*i/16;p[59+i]=@[@(.5+.29*cos(t)),@(.65+.43*sin(t))];} for(NSUInteger i=0;i<76;i++)p[i]=@[@(box.origin.x+([p[i][0] doubleValue]+localJitter)*box.size.width),@(box.origin.y+[p[i][1] doubleValue]*box.size.height)]; g[@"points76"]=p; } return g; } static NSDictionary *Live(PGRealtimeFaceTracker *t,NSArray *g,PGBeautySettings s,int64_t tick){return[t resultForGeometry:g settings:s time:CMTimeMake(tick,1000) extent:CGRectMake(0,0,256,256)];} static double PointX(NSDictionary *r){return[r[@"output"][0][@"points76"][69][0] doubleValue];} static NSData *Pixels(CIContext *context,CIImage *image){ CGRect e=image.extent;NSUInteger w=e.size.width,h=e.size.height;NSMutableData *d=[NSMutableData dataWithLength:w*h*4*sizeof(float)]; [context render:image toBitmap:d.mutableBytes rowBytes:w*4*sizeof(float) bounds:e format:kCIFormatRGBAf colorSpace:NULL];return d; } static NSDictionary *Difference(NSData *a,NSData *b){ const float *x=a.bytes,*y=b.bytes;NSUInteger n=a.length/sizeof(float),changed=0;double maximum=0,sum=0; for(NSUInteger i=0;i1e-6)changed++;} return@{@"maxChannelDifference":@(maximum),@"meanChannelDifference":@(sum/n),@"changedChannelsAbove1e_6":@(changed)}; } static double MeanRed(CIContext *context,CIImage *image){NSData *d=Pixels(context,image);const float *p=d.bytes;NSUInteger n=d.length/(4*sizeof(float));double sum=0;for(NSUInteger i=0;i