// Contract tests for the 2.4.0 pin-style reconstruction. The source is imported // once so the quality gate and actual tracker can be exercised without a host, // real portrait, private Vision construction, or reference binary execution. #import "../FCP-BeautyGirl/FCP-BeautyGirl/Plugin/PGFaceProcessor.m" #import "tracking_fixture.h" static NSUInteger checks=0,failures=0; static void Check(BOOL ok,NSString *message){checks++;if(!ok){failures++;fprintf(stderr,"FAIL: %s\n",message.UTF8String);}} static BOOL EqualPoints(NSArray *a,NSArray *b,double tolerance){ if(a.count!=76||b.count!=76)return NO; for(NSUInteger i=0;i<76;i++)for(NSUInteger j=0;j<2;j++)if(fabs([a[i][j] doubleValue]-[b[i][j] doubleValue])>tolerance)return NO; return YES; } static NSDictionary *First(NSDictionary *result){return [result[@"output"] firstObject];} 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 NSArray *MappedPoints(NSDictionary *g,CGRect box){ CGRect old=PGArrayRect(g[@"box"]);NSMutableArray *a=[NSMutableArray new]; for(NSArray *point in g[@"points76"]){CGPoint p=PGRemapPoint(point,old,box);[a addObject:@[@(p.x),@(p.y)]];}return a; } static NSDictionary *Without76(NSDictionary *g){NSMutableDictionary *bad=[g mutableCopy];[bad removeObjectForKey:@"points76"];return bad;} static NSDictionary *WithPoints(NSDictionary *g,void (^change)(NSMutableArray *)){ NSMutableDictionary *bad=[g mutableCopy];NSMutableArray *p=[g[@"points76"] mutableCopy];change(p);bad[@"points76"]=p;return bad; } @interface PGScriptedTracker : PGRealtimeFaceTracker @property CGRect proposedBox; @property NSUInteger followCalls; @property NSUInteger seedCalls; @end @implementation PGScriptedTracker - (instancetype)init{if((self=[super init]))_proposedBox=CGRectNull;return self;} - (CGRect)trackedBoxForImage:(CIImage *)image time:(CMTime)time seedBox:(CGRect)seed { if(PGValidRect(seed)){self.seedCalls++;return seed;} self.followCalls++;return self.proposedBox; } @end @interface PGCountingDetector : PGFaceProcessor @property (nonatomic,copy) NSArray *suppliedFaces; @property NSUInteger calls; @property BOOL fail; @end @implementation PGCountingDetector - (NSArray *)observationsInImage:(CIImage *)image settings:(PGBeautySettings)s error:(NSError **)error { self.calls++;if(self.fail){if(error)*error=[NSError errorWithDomain:@"synthetic-detection" code:1 userInfo:nil];return nil;}return self.suppliedFaces?:@[]; } @end @interface PGScriptedOutputTracker : PGRealtimeFaceTracker @property (nonatomic,copy) NSDictionary *scriptedOutput; @end @implementation PGScriptedOutputTracker - (NSDictionary *)resultForGeometry:(NSArray *)raw settings:(PGBeautySettings)s time:(CMTime)time extent:(CGRect)extent image:(CIImage *)image {return self.scriptedOutput;} @end // Objective-C message-compatible test doubles, never supplied to Vision. They // expose only the public read-only observation/region selectors used by the // production geometry converter, backed entirely by our authored fixture. @interface PGAuthoredRegion : NSObject @property (nonatomic,strong) NSData *storage; @property NSUInteger pointCount; - (const CGPoint *)normalizedPoints; @end @implementation PGAuthoredRegion - (const CGPoint *)normalizedPoints{return self.storage.bytes;} @end @interface PGAuthoredLandmarks : NSObject @property (nonatomic,strong) PGAuthoredRegion *allPoints,*leftEye,*rightEye,*leftEyebrow,*rightEyebrow,*nose,*outerLips,*innerLips,*faceContour; @property VNConfidence confidence; @end @implementation PGAuthoredLandmarks @end @interface PGAuthoredObservation : NSObject @property CGRect boundingBox; @property (nonatomic,strong) PGAuthoredLandmarks *landmarks; @property VNConfidence confidence; @end @implementation PGAuthoredObservation @end static PGAuthoredRegion *AuthoredRegion(NSArray *points,CGRect box){ NSMutableData *data=[NSMutableData dataWithLength:points.count*sizeof(CGPoint)];CGPoint *p=data.mutableBytes; for(NSUInteger i=0;i0,[NSString stringWithFormat:@"quality rejects malformed case %lu",(unsigned long)i]); PGRealtimeFaceTracker *t=[PGRealtimeFaceTracker new];Live(t,@[good],s,0);NSDictionary *r=Live(t,@[bad],s,40); Check([r[@"held"] boolValue] && EqualPoints(First(r)[@"points76"],good[@"points76"],1e-12),[NSString stringWithFormat:@"malformed case %lu holds the last accepted complete shape",(unsigned long)i]); PGRealtimeFaceTracker *cold=[PGRealtimeFaceTracker new];NSDictionary *initial=Live(cold,@[bad],s,0); Check([First(initial)[@"points76"] count]==0,[NSString stringWithFormat:@"cold malformed case %lu never invents topology",(unsigned long)i]); } for(NSNumber *fps in @[@25,@30,@60,@120]){ PGRealtimeFaceTracker *t=[PGRealtimeFaceTracker new];Live(t,@[good],s,0);NSDictionary *next=PGTrackingTestGeometry(box,.002); NSDictionary *r=[t resultForGeometry:@[next] settings:s time:CMTimeMake(1,fps.intValue) extent:CGRectMake(0,0,256,256)]; Check(![r[@"held"] boolValue] && EqualPoints(First(r)[@"points76"],next[@"points76"],0),[NSString stringWithFormat:@"accepted %ldfps points are copied exactly without legacy temporal blending",(long)fps.integerValue]); } PGRealtimeFaceTracker *t=[PGRealtimeFaceTracker new];Live(t,@[good],s,0);NSDictionary *middle=Live(t,@[Without76(good)],s,40),*back=Live(t,@[good],s,80); Check([middle[@"held"] boolValue] && [First(middle)[@"points76"] count]==76 && EqualPoints(First(back)[@"points76"],good[@"points76"],0),@"76 to missing76 to76 never disables the geometric field"); NSDictionary *jumpy=PGTrackingTestGeometry(box,.34); Check(PGGeometryQualityReason(jumpy).length==0,@"large coherent local jump remains structurally valid before temporal compatibility checks"); PGRealtimeFaceTracker *jump=[PGRealtimeFaceTracker new];Live(jump,@[good],s,0);NSDictionary *rejected=Live(jump,@[jumpy],s,40); Check([rejected[@"held"] boolValue] && EqualPoints(First(rejected)[@"points76"],good[@"points76"],1e-12),@"a large finite median jump cannot poison accepted shape memory"); NSDictionary *axisReverse=WithPoints(good,^(NSMutableArray *p){for(NSUInteger i=66;i<=68;i++)p[i]=@[p[i][0],@(2*CGRectGetMidY(box)-[p[i][1] doubleValue])];}); Check(PGGeometryAxisFlip(axisReverse,good),@"weighted lower-contour to bbox-center axis detects a vertical sign reversal"); NSMutableDictionary *eyeReverse=[good mutableCopy];eyeReverse[@"leftEye"]=good[@"rightEye"];eyeReverse[@"rightEye"]=good[@"leftEye"]; Check(!PGGeometryAxisFlip(eyeReverse,good),@"an isolated eye-order change is not the recovered lower-contour axis test"); NSDictionary *fewOutliers=WithPoints(good,^(NSMutableArray *p){for(NSUInteger i=0;i<6;i++)p[i]=@[@([p[i][0] doubleValue]+.55),p[i][1]];}); NSDictionary *manyOutliers=WithPoints(good,^(NSMutableArray *p){for(NSUInteger i=0;i<12;i++)p[i]=@[@([p[i][0] doubleValue]+.55),p[i][1]];}); Check(PGGeometryTransitionReason(fewOutliers,good).length==0,@"the temporal quantile gate does not treat a few outliers as a large median or P90 jump"); Check(PGGeometryTransitionReason(manyOutliers,good).length>0,@"P90 rejects a substantial outlier group even when the median remains zero"); } static void SequenceAndCache(void){ PGBeautySettings s=PGBeautyDefaults();s.faceThin=1;CGRect box=CGRectMake(.15,.12,.70,.78); NSDictionary *good=PGTrackingTestGeometry(box,0),*changed=PGTrackingTestGeometry(box,.003);CIImage *image=[[CIImage imageWithColor:[CIColor colorWithRed:.5 green:.5 blue:.5]] imageByCroppingToRect:CGRectMake(0,0,256,256)]; PGScriptedTracker *t=[PGScriptedTracker new];NSDictionary *first=[t resultForGeometry:@[good] settings:s time:kCMTimeZero extent:image.extent image:image]; Check(t.seedCalls>0,@"first accepted face seeds the continuous object-box tracker"); CGRect tracked=CGRectMake(.17,.13,.66,.74);t.proposedBox=tracked; NSDictionary *hold=[t resultForGeometry:@[Without76(good)] settings:s time:CMTimeMake(1,25) extent:image.extent image:image]; Check(t.followCalls>0 && [hold[@"held"] boolValue],@"a partial detection asks the continuous sequence tracker for a box"); Check(CGRectEqualToRect(PGArrayRect(First(hold)[@"box"]),tracked) && EqualPoints(First(hold)[@"points76"],MappedPoints(good,tracked),1e-12),@"held complete points follow the tracked box with exact affine remapping"); NSUInteger calls=t.followCalls+t.seedCalls; NSDictionary *repeat=[t resultForGeometry:@[changed] settings:s time:CMTimeMake(2,50) extent:image.extent image:image]; Check([repeat isEqual:hold] && t.followCalls+t.seedCalls==calls,@"same rational frame freezes its output and does not advance Vision again when raw changes"); t.proposedBox=CGRectNull;NSDictionary *reacquired=[t resultForGeometry:@[changed] settings:s time:CMTimeMake(2,25) extent:image.extent image:image]; Check(![reacquired[@"held"] boolValue] && EqualPoints(First(reacquired)[@"points76"],changed[@"points76"],0),@"a good reacquired detection replaces held shape exactly"); NSDictionary *miss=Live(t,@[],s,120);Check([miss[@"held"] boolValue] && [First(miss)[@"points76"] count]==76,@"nearby complete detection loss preserves complete accepted geometry"); PGRealtimeFaceTracker *cold=[PGRealtimeFaceTracker new];NSDictionary *empty=Live(cold,@[],s,40);Live(cold,@[good],s,0); Check([empty isEqual:Live(cold,@[changed],s,40)],@"same-frame freeze is stable when an earlier request arrives later"); PGRealtimeFaceTracker *past=[PGRealtimeFaceTracker new];Live(past,@[good],s,1000);NSDictionary *rewind=Live(past,@[],s,960); Check([rewind[@"output"] count]==0,@"uncached reverse request cannot inherit a future accepted face"); PGRealtimeFaceTracker *scope=[PGRealtimeFaceTracker new];Live(scope,@[good],s,0); Check([[scope resultForGeometry:@[] settings:s time:CMTimeMakeWithEpoch(40,1000,9) extent:image.extent][@"output"] count]==0,@"CMTime epoch changes isolate accepted geometry"); Check([[scope resultForGeometry:@[] settings:s time:CMTimeMake(40,1000) extent:CGRectMake(10,0,256,256)][@"output"] count]==0,@"image origin changes isolate accepted geometry"); PGBeautySettings multi=s;multi.fastMultiFace=1;NSDictionary *many=Live(scope,@[good,Without76(changed)],multi,40); Check([many[@"output"] count]==2,@"fast-multi retains its independent legacy raw-geometry contract"); Check([Live(scope,@[],multi,80)[@"output"] count]==0,@"fast-multi cannot inherit the default primary-face hold"); NSDictionary *defaultAgain=Live(scope,@[],s,80);Check([First(defaultAgain)[@"points76"] count]==76,@"fast-multi requests do not overwrite default accepted memory"); [scope reset];Check([Live(scope,@[],s,120)[@"output"] count]==0,@"reset removes both output history and accepted shape state"); Check(EqualPoints(First(first)[@"points76"],good[@"points76"],0),@"the first sequence frame preserves every authored point"); } // Exercise the real VNSequenceRequestHandler/VNTrackObjectRequest on a textured // synthetic rectangle. This verifies API wiring; it is not portrait accuracy or // target parity. No face detector or media file is required. static CIImage *ObjectFrame(NSUInteger frame){ NSMutableData *d=[NSMutableData dataWithLength:256*256*4];uint8_t *p=d.mutableBytes;NSInteger dx=(NSInteger)frame*2; for(NSInteger y=0;y<256;y++)for(NSInteger x=0;x<256;x++){ NSUInteger i=4*(y*256+x);NSInteger u=x-76-dx,v=y-51;BOOL inside=u>=0&&u<90&&v>=0&&v<128; uint8_t texture=inside?(uint8_t)(35+(u*37+v*53+((u/7+v/9)%2)*83)%210):8; p[i]=texture;p[i+1]=inside?(uint8_t)(50+(u*17+v*13)%180):12;p[i+2]=inside?(uint8_t)(40+(u*11+v*29)%200):16;p[i+3]=255; } return[CIImage imageWithBitmapData:d bytesPerRow:1024 size:CGSizeMake(256,256) format:kCIFormatRGBA8 colorSpace:NULL]; } static double MaskPeak(CIContext *context,CIImage *image){ CGRect extent=image.extent;NSUInteger width=extent.size.width,height=extent.size.height; NSMutableData *d=[NSMutableData dataWithLength:width*height*4*sizeof(float)]; [context render:image toBitmap:d.mutableBytes rowBytes:width*4*sizeof(float) bounds:extent format:kCIFormatRGBAf colorSpace:NULL]; const float *p=d.bytes;double peak=0;for(NSUInteger i=0;i.5 && skinPixel[0]>.25,@"default held geometry preserves skin and color support through complete detection loss"); Check(MaskPeak(context,frame.teethMask)>.5 && MaskPeak(context,frame.eyeMask)>.5,@"held default geometry retains nonzero teeth and eye support after the shared warp"); PGCountingDetector *errorProcessor=[PGCountingDetector new];PGScriptedTracker *objectTracker=[PGScriptedTracker new]; [errorProcessor setValue:objectTracker forKey:@"realtime"];errorProcessor.suppliedFaces=(NSArray *)@[AuthoredObservation(g)]; PGFaceFrame *valid=[errorProcessor processImage:image settings:s time:kCMTimeZero error:nil]; Check(valid.deformationGeometry.count==1 && [valid.deformationGeometry[0][@"points76"] count]==76,@"public-selector authored observations seed actual processImage accepted geometry"); errorProcessor.fail=YES;objectTracker.proposedBox=PGArrayRect(g[@"box"]);NSError *stageError=nil; PGFaceFrame *failureHold=[errorProcessor processImage:image settings:s time:CMTimeMake(1,25) error:&stageError]; Check(failureHold!=nil && failureHold.deformationGeometry.count==1 && [failureHold.deformationGeometry[0][@"points76"] count]==76,@"a realtime detector API error returns a frame with the last accepted complete shape"); Check(failureHold!=nil && MaskPeak(context,failureHold.skinMask)>.5 && MaskPeak(context,failureHold.colorMask)>.5 && MaskPeak(context,failureHold.teethMask)>.5 && MaskPeak(context,failureHold.eyeMask)>.5,@"realtime detector errors keep all held beauty masks instead of flashing them off"); NSDictionary *recovered=PGTrackingTestGeometry(PGArrayRect(g[@"box"]),.01); errorProcessor.fail=NO;errorProcessor.suppliedFaces=(NSArray *)@[AuthoredObservation(recovered)]; PGFaceFrame *sameTime=[errorProcessor processImage:image settings:s time:CMTimeMake(1,25) error:nil]; Check(EqualPoints(sameTime.deformationGeometry.firstObject[@"points76"],recovered[@"points76"],1e-12),@"same-time recovery replaces a transient error hold instead of replaying its frozen geometry"); PGCountingDetector *coldError=[PGCountingDetector new];coldError.fail=YES; [coldError processImage:image settings:s time:kCMTimeZero error:nil];coldError.fail=NO; coldError.suppliedFaces=(NSArray *)@[AuthoredObservation(g)]; PGFaceFrame *acquired=[coldError processImage:image settings:s time:kCMTimeZero error:nil]; Check(acquired.deformationGeometry.count==1&&coldError.calls==2,@"a cold API failure cannot freeze the exact frame without geometry after a successful retry"); errorProcessor.fail=YES; [[errorProcessor valueForKey:@"observationCache"] removeAllObjects]; PGFaceFrame *cachedFailure=[errorProcessor processImage:image settings:s time:kCMTimeZero error:nil]; Check(cachedFailure.deformationGeometry.count==1&&MaskPeak(context,cachedFailure.skinMask)>.5&&MaskPeak(context,cachedFailure.colorMask)>.5&&MaskPeak(context,cachedFailure.eyeMask)>.5&&MaskPeak(context,cachedFailure.teethMask)>.5,@"an API error after detection-cache eviction preserves masks even when geometry hits an older successful snapshot"); PGBeautySettings offline=s;offline.trackingEnabled=1;stageError=nil; Check([errorProcessor processImage:image settings:offline time:CMTimeMake(2,25) error:&stageError]==nil && stageError!=nil,@"explicit offline tracking still reports detector errors rather than silently holding live geometry"); } static void VisionSequence(void){ PGRealtimeFaceTracker *t=[PGRealtimeFaceTracker new];CGRect seed=CGRectMake(76./256,51./256,90./256,128./256); CGRect start=[t trackedBoxForImage:ObjectFrame(0) time:kCMTimeZero seedBox:seed];Check(PGValidRect(start),@"real sequence tracker accepts an authored textured object seed"); for(NSUInteger i=1;i<=5;i++){ CGRect tracked=[t trackedBoxForImage:ObjectFrame(i) time:CMTimeMake(i,25) seedBox:CGRectNull]; Check(PGValidRect(tracked) && fabs(CGRectGetMidX(tracked)-(CGRectGetMidX(seed)+2.*i/256))<.07 && fabs(CGRectGetMidY(tracked)-CGRectGetMidY(seed))<.07,@"real sequence tracker follows a known synthetic translation without losing the box"); CGRect again=[t trackedBoxForImage:ObjectFrame(i) time:CMTimeMake(i,25) seedBox:CGRectNull];Check(CGRectEqualToRect(tracked,again),@"real sequence same-frame reuse does not advance tracking twice"); } CGRect rewind=[t trackedBoxForImage:ObjectFrame(3) time:CMTimeMake(3,25) seedBox:CGRectNull]; Check(!PGValidRect(rewind),@"a real sequence rewind cannot return a future tracked box without a fresh seed"); CGRect reseed=CGRectOffset(seed,6./256,0);CGRect restarted=[t trackedBoxForImage:ObjectFrame(3) time:CMTimeMake(3,25) seedBox:reseed]; Check(CGRectEqualToRect(reseed,restarted),@"a rewind with an explicit box restarts the real Vision sequence at that seed"); PGRealtimeFaceTracker *gap=[PGRealtimeFaceTracker new]; [gap trackedBoxForImage:ObjectFrame(0) time:kCMTimeZero seedBox:seed]; Check(!PGValidRect([gap trackedBoxForImage:ObjectFrame(8) time:CMTimeMake(8,25) seedBox:CGRectNull]),@"a gap over .2 seconds invalidates an unseeded continuous object request"); Check(CGRectEqualToRect([gap trackedBoxForImage:ObjectFrame(9) time:CMTimeMake(9,25) seedBox:CGRectNull],seed),@"after gap invalidation the next unseeded frame follows the recovered short lost-box hold path"); } int main(void){@autoreleasepool{QualityAndAcceptance();SequenceAndCache();DetectionCacheAndHeldMasks();VisionSequence();printf("%s: %lu tracking sequence checks (%lu failures); synthetic data only\n",failures?"FAIL":"PASS",(unsigned long)checks,(unsigned long)failures);return failures?1:0;}}