// // MPT_EnjoymentPlugin.m // MPT_GKR // // Created by Andrew Salamon on 8/27/08. // Copyright 2008 Machine Perception Laboratory, University of California San Diego.. All rights reserved. // #import "MPT_EnjoymentPlugin.h" //#import "MPT_MeterController.h" #import #import #import "MPT_Face.h" @interface MPT_EnjoymentPlugin(PrivateMethods) - (BOOL)scanString:(NSString *)string intoX:(float *)x Y:(float *)y; @end @implementation MPT_EnjoymentPlugin + (void)initialize { } - (id)initWithBundle:(NSBundle *)_bundle { if( self = [super initWithBundle:_bundle] ) { weights = [[NSArray alloc] initWithContentsOfFile:[_bundle pathForResource:@"weights" ofType:@"plist"]]; meter = [[MPT_MeterController alloc] init]; [meter setMin:45]; [meter setMax:160]; [meter setCutoff:100]; [meter setLabel:[self localizedName]]; [self setView:[meter auView] forLabel:[[self labels] objectAtIndex:0]]; } return self; } - (id)copyWithZone:(NSZone *)zone { return nil; } - (void)dealloc { [super dealloc]; } - (void)awakeFromNib { [self initViews]; } - (NSString *)step { return MPT_PluginStep_Results; } - (NSString *)localizedName { return NSLocalizedStringFromTableInBundle(@"Joy Quotient", nil, [NSBundle bundleForClass:[self class]], @""); } - (NSString *)internalName { return @"enjoyment"; } - (NSView *)prefPane { return prefView; } - (void)setView:(MPT_AUView *)newView forLabel:(NSString *)label { [super setView:newView forLabel:label]; } - (void)pluginsFinishedLoading:(NSArray *)plugins { } /* (AU 1)4_0 Inner Brow Raise (AU 2)4_0 Outer Brow Raise (AU 4)4_0 Brow Lower (AU 5)4_0 Eye Widen (AU 9)4_0 Nose Wrinkle (AU 10)4_0 Lip Raise (AU 12)4_0 Lip Corner Pull (AU 14)4_0 Dimpler (AU 15)4_0 Lip Corner Depressor (AU 17)4_0 Chin Raise (AU 20)4_0 Lip stretch (AU 1)4_4 Inner Brow Raise (AU 2)4_4 Outer Brow Raise (AU 4)4_4 Brow Lower (AU 5)4_4 Eye Widen (AU 9)4_4 Nose Wrinkle (AU 10)4_4 Lip Raise (AU 12)4_4 Lip Corner Pull (AU 14)4_4 Dimpler (AU 15)4_4 Lip Corner Depressor (AU 17)4_4 Chin Raise (AU 20)4_4 Lip stretch AU 10 Left AU 12 Left AU 14 Left AU 10 Right AU 12 Right AU 14 Right (AU 6) Cheek Raise (AU 7) Lids Tight (AU 18) Lip Pucker (AU 23) Lip Tightener (AU 24) Lip Presser (AU 25) Lips Part (AU 26) Jaw Drop (AU 28) Lips Suck Yaw Pitch Roll Face Detector X Face Detector Y Width Height Scale Cropped Face X1 Cropped Face Y1 Cropped Face X2 Cropped Face Y2 Cropped Face X3 Cropped Face Y3 Cropped Face X4 Cropped Face Y4 Left Eye X Left Eye Y Right Eye X Right Eye Y */ /* edu.ucsd.mplab.plugins.CERT4_0_Weights edu.ucsd.mplab.plugins.CERT4_4_Weights edu.ucsd.mplab.plugins.Unilaterals edu.ucsd.mplab.plugins.features.nose edu.ucsd.mplab.plugins.features.MPT_LeftEye_Nasal edu.ucsd.mplab.plugins.features.MPT_RightEye_Nasal edu.ucsd.mplab.plugins.features.MPT_RightEye_Temporal edu.ucsd.mplab.plugins.features.rightEye edu.ucsd.mplab.plugins.features.MPT_LeftEye_Temporal edu.ucsd.mplab.plugins.AdditionalAUs3 edu.ucsd.mplab.plugins.features.MPT_Mouth_RightCorner edu.ucsd.mplab.plugins.features.MPT_Mouth_LeftCorner edu.ucsd.mplab.plugins.FaceFinderStub edu.ucsd.mplab.plugins.features.FeatureImprover edu.ucsd.mplab.plugins.features.leftEye edu.ucsd.mplab.plugins.features.mouth */ - (NSMutableDictionary *)processResults:(NSDictionary *)results usingController:(id)controller { BOOL faceFound = ([[results objectForKey:@"CERT.faces"] count] > 0); // write the results to a plist so I can grab the info I need from it. [results writeToFile:@"/tmp/res.plist" atomically:YES]; if( faceFound ) { NSMutableArray *values = [NSMutableArray array]; // Fill the two vectors with results from the pose and target plugins. { id au4_0 = [results objectForKey:@"edu.ucsd.mplab.plugins.CERT4_0_Weights"]; if( ![au4_0 isKindOfClass:[NSDictionary class]] || ([au4_0 count] != 11) ) { NSLog( @"Unexpected result types for CERT4_0_Weights in Enjoyment Plugin." ); return nil; } [values addObject:[au4_0 objectForKey:@"(AU 1) Inner Brow Raise"]]; [values addObject:[au4_0 objectForKey:@"(AU 2) Outer Brow Raise"]]; [values addObject:[au4_0 objectForKey:@"(AU 4) Brow Lower"]]; [values addObject:[au4_0 objectForKey:@"(AU 5) Eye Widen"]]; [values addObject:[au4_0 objectForKey:@"(AU 9) Nose Wrinkle"]]; [values addObject:[au4_0 objectForKey:@"(AU 10) Lip Raise"]]; [values addObject:[au4_0 objectForKey:@"(AU 12) Lip Corner Pull"]]; [values addObject:[au4_0 objectForKey:@"(AU 14) Dimpler"]]; [values addObject:[au4_0 objectForKey:@"(AU 15) Lip Corner Depressor"]]; [values addObject:[au4_0 objectForKey:@"(AU 17) Chin Raise"]]; [values addObject:[au4_0 objectForKey:@"(AU 20) Lip stretch"]]; } { id au4_4 = [results objectForKey:@"edu.ucsd.mplab.plugins.CERT4_4_Weights"]; if( ![au4_4 isKindOfClass:[NSDictionary class]] || ([au4_4 count] != 11) ) { NSLog( @"Unexpected result types for CERT4_4_Weights in Enjoyment Plugin." ); return nil; } [values addObject:[au4_4 objectForKey:@"(AU 1) Inner Brow Raise"]]; [values addObject:[au4_4 objectForKey:@"(AU 2) Outer Brow Raise"]]; [values addObject:[au4_4 objectForKey:@"(AU 4) Brow Lower"]]; [values addObject:[au4_4 objectForKey:@"(AU 5) Eye Widen"]]; [values addObject:[au4_4 objectForKey:@"(AU 9) Nose Wrinkle"]]; [values addObject:[au4_4 objectForKey:@"(AU 10) Lip Raise"]]; [values addObject:[au4_4 objectForKey:@"(AU 12) Lip Corner Pull"]]; [values addObject:[au4_4 objectForKey:@"(AU 14) Dimpler"]]; [values addObject:[au4_4 objectForKey:@"(AU 15) Lip Corner Depressor"]]; [values addObject:[au4_4 objectForKey:@"(AU 17) Chin Raise"]]; [values addObject:[au4_4 objectForKey:@"(AU 20) Lip stretch"]]; } { id unilats = [results objectForKey:@"edu.ucsd.mplab.plugins.Unilaterals"]; if( ![unilats isKindOfClass:[NSDictionary class]] || ([unilats count] != 6) ) { NSLog( @"Unexpected result types for Unilaterals in Enjoyment Plugin." ); return nil; } [values addObject:[unilats objectForKey:@"AU 10 Left"]]; [values addObject:[unilats objectForKey:@"AU 12 Left"]]; [values addObject:[unilats objectForKey:@"AU 14 Left"]]; [values addObject:[unilats objectForKey:@"AU 10 Right"]]; [values addObject:[unilats objectForKey:@"AU 12 Right"]]; [values addObject:[unilats objectForKey:@"AU 14 Right"]]; } { id auRes = [results objectForKey:@"edu.ucsd.mplab.plugins.AdditionalAUs3"]; if( ![auRes isKindOfClass:[NSDictionary class]] || ([auRes count] != 8) ) { NSLog( @"Unexpected result types for AdditionalAUs3 in Enjoyment Plugin." ); return nil; } [values addObject:[auRes objectForKey:@"(AU 6) Cheek Raise"]]; [values addObject:[auRes objectForKey:@"(AU 7) Lids Tight"]]; [values addObject:[auRes objectForKey:@"(AU 18) Lip Pucker"]]; [values addObject:[auRes objectForKey:@"(AU 23) Lip Tightener"]]; [values addObject:[auRes objectForKey:@"(AU 24) Lip Presser"]]; [values addObject:[auRes objectForKey:@"(AU 25) Lips Part"]]; [values addObject:[auRes objectForKey:@"(AU 26) Jaw Drop"]]; [values addObject:[auRes objectForKey:@"(AU 28) Lips Suck"]]; } { id auRes = [results objectForKey:@"edu.ucsd.mplab.plugins.SmileDetector2"]; if( ![auRes isKindOfClass:[NSNumber class]] ) { NSLog( @"Unexpected result types for SmileDetector in Enjoyment Plugin." ); return nil; } [values addObject:auRes]; } // { // id auRes = [results objectForKey:@"edu.ucsd.mplab.plugins.PoseDetector"]; // if( ![auRes isKindOfClass:[NSDictionary class]] || ([auRes count] != 3) ) // { // NSLog( @"Unexpected result types for PoseDetector in Enjoyment Plugin." ); // return nil; // } // // [values addObject:[auRes objectForKey:@"Yaw"]]; // [values addObject:[auRes objectForKey:@"Pitch"]]; // [values addObject:[auRes objectForKey:@"Roll"]]; // } // // { // id auRes = [results objectForKey:@"edu.ucsd.mplab.plugins.FaceFinderStub"]; // if( ![auRes isKindOfClass:[NSString class]] ) // { // NSLog( @"Unexpected result types for FaceFinderStub in Enjoyment Plugin." ); // return nil; // } // NSScanner *scanner = [NSScanner scannerWithString:auRes]; // float val; // // if( ![scanner scanString:@"{x:" intoString:nil] || ![scanner scanFloat:&val] ) // { // NSLog( @"Unexpected result types for FaceFinderStub 'x' in Enjoyment Plugin." ); // return nil; // } // [values addObject:[NSNumber numberWithFloat:val]]; // // if( ![scanner scanString:@", y:" intoString:nil] || ![scanner scanFloat:&val] ) // { // NSLog( @"Unexpected result types for FaceFinderStub 'y' in Enjoyment Plugin." ); // return nil; // } // [values addObject:[NSNumber numberWithFloat:val]]; // // if( ![scanner scanString:@", width:" intoString:nil] || ![scanner scanFloat:&val] ) // { // NSLog( @"Unexpected result types for FaceFinderStub 'width' in Enjoyment Plugin." ); // return nil; // } // [values addObject:[NSNumber numberWithFloat:val]]; // // if( ![scanner scanString:@", height:" intoString:nil] || ![scanner scanFloat:&val] ) // { // NSLog( @"Unexpected result types for FaceFinderStub 'height' in Enjoyment Plugin." ); // return nil; // } // [values addObject:[NSNumber numberWithFloat:val]]; // // if( ![scanner scanString:@", scale:" intoString:nil] || ![scanner scanFloat:&val] ) // { // NSLog( @"Unexpected result types for FaceFinderStub 'scale' in Enjoyment Plugin." ); // return nil; // } // [values addObject:[NSNumber numberWithFloat:val]]; // // // 23 : {contents = "edu.ucsd.mplab.plugins.FaceFinderStub"} = {contents = "{x:212, y:114, width:333, height:333, scale:10}"} // } // // // /* // Cropped Face X1 // Cropped Face Y1 // Cropped Face X2 // Cropped Face Y2 // Cropped Face X3 // Cropped Face Y3 // Cropped Face X4 // Cropped Face Y4 // // 1 : {contents = "CERT.faces"} = {type = mutable-small, count = 1, values = ( // 0 : // )} // // */ // { // id auRes = [results objectForKey:@"CERT.faces"]; // if( ![auRes isKindOfClass:[NSArray class]] || ([auRes count] < 1) ) // { // NSLog( @"Unexpected result types for CroppedFace in Enjoyment Plugin." ); // return nil; // } // MPT_Face *face = [auRes objectAtIndex:0]; // NSArray *points = [face points]; // float x, y; // // if( ![points isKindOfClass:[NSArray class]] || ([points count] != 4) ) // { // NSLog( @"Unexpected result types for CroppedFace points in Enjoyment Plugin." ); // return nil; // } // // // each of these points needs to be scanned: // // "{248.201, 156.49}", // if( ![self scanString:[points objectAtIndex:0] intoX:&x Y:&y] ) // { // NSLog( @"Unexpected result types for CroppedFace point 1 in Enjoyment Plugin." ); // return nil; // } // [values addObject:[NSNumber numberWithFloat:x]]; // [values addObject:[NSNumber numberWithFloat:y]]; // // if( ![self scanString:[points objectAtIndex:1] intoX:&x Y:&y] ) // { // NSLog( @"Unexpected result types for CroppedFace point 2 in Enjoyment Plugin." ); // return nil; // } // [values addObject:[NSNumber numberWithFloat:x]]; // [values addObject:[NSNumber numberWithFloat:y]]; // // if( ![self scanString:[points objectAtIndex:2] intoX:&x Y:&y] ) // { // NSLog( @"Unexpected result types for CroppedFace point 3 in Enjoyment Plugin." ); // return nil; // } // [values addObject:[NSNumber numberWithFloat:x]]; // [values addObject:[NSNumber numberWithFloat:y]]; // // if( ![self scanString:[points objectAtIndex:3] intoX:&x Y:&y] ) // { // NSLog( @"Unexpected result types for CroppedFace point 4 in Enjoyment Plugin." ); // return nil; // } // [values addObject:[NSNumber numberWithFloat:x]]; // [values addObject:[NSNumber numberWithFloat:y]]; // } // // // /* // 28 : {contents = "edu.ucsd.mplab.plugins.features.leftEye"} = {contents = "{430.962, 219.293}"} // Left Eye X // Left Eye Y // Right Eye X // Right Eye Y //*/ // { // id auRes = [results objectForKey:@"edu.ucsd.mplab.plugins.features.leftEye"]; // if( ![auRes isKindOfClass:[NSString class]] ) // { // NSLog( @"Unexpected result types for LeftEye in Enjoyment Plugin." ); // return nil; // } // float x,y; // if( ![self scanString:auRes intoX:&x Y:&y] ) // { // NSLog( @"Unexpected result types for LeftEye values in Enjoyment Plugin." ); // return nil; // } // [values addObject:[NSNumber numberWithFloat:x]]; // [values addObject:[NSNumber numberWithFloat:y]]; // } // // { // id auRes = [results objectForKey:@"edu.ucsd.mplab.plugins.features.rightEye"]; // if( ![auRes isKindOfClass:[NSString class]] ) // { // NSLog( @"Unexpected result types for RightEye in Enjoyment Plugin." ); // return nil; // } // float x,y; // if( ![self scanString:auRes intoX:&x Y:&y] ) // { // NSLog( @"Unexpected result types for LeftEye values in Enjoyment Plugin." ); // return nil; // } // [values addObject:[NSNumber numberWithFloat:x]]; // [values addObject:[NSNumber numberWithFloat:y]]; // } if( [weights count] != [values count] ) { NSLog( @"Mismatch between weights and values sizes in Enjoyment Plugin." ); return nil; } int cnt = [weights count]; float answer = 0.0; for( int x = 0; x < cnt; ++x ) { answer += [[weights objectAtIndex:x] floatValue] * [[values objectAtIndex:x] floatValue]; } /* The first one will be called the "Joy Quotient" and it is basically a linear scaling of the current output to have the same metric as an intelligence quotient. The commands to compute the Joy Quotient are y = (x - m)/s y = y*15 + 100 where x is the output of the current prototype and y is the joy quotient. Put the minimum JQ at 45 and max at 160, with the center at 100. */ NSMutableDictionary *resDict = [NSMutableDictionary dictionary]; [resDict setObject:[NSNumber numberWithFloat:answer] forKey:@"Enjoyment"]; float m = -2.2823; float s = 1.1947; answer = (answer - m) / s; answer = (answer * 15) + 100; [resDict setObject:[NSNumber numberWithFloat:answer] forKey:@"JoyQuotient"]; [resDict setObject:[NSNumber numberWithFloat:[meter cutoff]] forKey:@"cutoff"]; return resDict; } return nil; } - (BOOL)scanString:(NSString *)string intoX:(float *)x Y:(float *)y { NSScanner *scanner = [NSScanner scannerWithString:string]; if( ![scanner scanString:@"{" intoString:nil] || ![scanner scanFloat:x] ) { return NO; } if( ![scanner scanString:@", " intoString:nil] || ![scanner scanFloat:y] ) { return NO; } return YES; } @end