// // MPT_CERT.mm // AUCoderDemo // // Created by Andrew Salamon on 5/17/06. // Copyright (c) 2006 Machine Perception Laboratory // University of California San Diego. // #import "MPT_CERT.h" #import #import #import #import #import #import #import #import #import #import #import #import #import "NSImageToRimage.h" #import "MPT_Face.h" //typedef MP_Gabor GABOR_CLASS; typedef mp_threadedGabor GABOR_CLASS; typedef std::map filterMapType; static const int auValues[] = { 1, 2, 4, 5, 10, 12, 14, 20, 101 }; @interface MPT_CERT(PrivateMethods) - (void)processRImageImp:(RImage *)rimage; - (void)setSmile:(double)value; - (double)filterValue:(double)value forAU:(int)AU withFace:(bool)faceFound; @end @implementation MPT_CERT - (id)init { if( self = [super init] ) { results = [[NSMutableArray alloc] init]; faceArray = [[NSMutableArray alloc] init]; mp_SVMWeights *weights = mp_SVMWeights::getSVMWeights(); NSString *weightsPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"SVMWeights"]; weights->setDirectory( [weightsPath UTF8String] ); doCERT = YES; findFaces = YES; displayGray = NO; doSmile = NO; doFilters = NO; mpeyefinder = new MP_eyefinderWrapper(); // ((MP_eyefinderWrapper *)mpeyefinder)->setWinshift( 0.0 ); ((MP_eyefinderWrapper *)mpeyefinder)->setUseSequence( true ); // ((MP_eyefinderWrapper *)mpeyefinder)->setMinScale( 1 ); gabor = NULL; smileDetector = new MPSmile(); ((MPSmile *)smileDetector)->setPixelMax( 1.0 ); rotator = new MP_RotateFace( 96 ); // face patches are 96x96 pixels ((MP_RotateFace *)rotator)->setEyeLenRate( 0.60 ); //((MP_RotateFace *)rotator)->setEyeTopRate( 0.20 ); patch = new RImage(); { NSBundle *CERTFramework = [NSBundle bundleWithIdentifier:@"edu.ucsd.mplab.CERTFramework"]; NSString *tablesDir = [[CERTFramework resourcePath] stringByAppendingPathComponent:@"likelihood_tables"]; std::string dir( ([tablesDir length]?[tablesDir UTF8String]:"") ); std::vector auLabels = MP_Gabor::getAULabels(); auLabels.push_back(101); filters = new ExpressionFilterWrapper( auLabels, dir ); } [self setEyefinderOffset:1.0]; [self setEyefinderDropoff:1.0]; } return self; } - (void)dealloc { [grayImage release]; [results release]; [faceArray release]; if( mpeyefinder ) delete (MP_eyefinderWrapper *)mpeyefinder; if( gabor ) delete (GABOR_CLASS *)gabor; if( smileDetector ) delete (MPSmile *)smileDetector; if( rotator ) delete (MP_RotateFace *)rotator; if( patch ) delete (RImage *)patch; if( filters ) delete (ExpressionFilterWrapper *)filters; [super dealloc]; } static const int niMax = 9; static const int muMax = 8; - (void)loadAllKernelsWithProgress:(NSProgressIndicator *)progress { mp_SVMWeights *weights = mp_SVMWeights::getSVMWeights(); [progress setMinValue:1.0]; [progress setMaxValue:niMax * muMax]; [progress setDoubleValue:1.0]; [progress setIndeterminate:NO]; for( int ni=1; ni <= niMax; ++ni ) { for( int mu=1; mu <= muMax; ++mu ) { weights->getWeights( ni, mu ); [progress incrementBy:1.0]; [progress displayIfNeeded]; if( weights->getLoadError() ) { [[NSAlert alertWithMessageText:@"Unable to load all SVM Weights." defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"AU calculations will not work correctly and have been turned off."] runModal]; [self setDoCERT:NO]; return; } } } } - (void)processImage:(NSImage *)image { RImage *rimage = [image rimageRep]; //#ifdef USE_IMAGEMAGICK // RImage *rimage = [image rimageFromImageMagick]; //#endif if( rimage ) { [self processRImageImp:rimage]; if( displayGray ) { [grayImage release]; grayImage = [[NSImage imageFromRImage:rimage] retain]; } delete rimage; } } - (void)processRImageFile:(NSString *)rimagePath { RImage rimage; std::string filename( [rimagePath UTF8String] ); if( restoreRImageFromFile( rimage, filename ) ) { [self processRImageImp:&rimage]; [grayImage release]; grayImage = [[NSImage imageFromRImage:&rimage] retain]; } } - (void)processRImageImp:(RImage *)rimage { EyeFaceList faces; [self clearResults]; // rimage->print( 5 ); if( findFaces ) { try { ((MP_eyefinderWrapper *)mpeyefinder)->findEyesSpeed( *rimage, faces ); // ((MP_eyefinderWrapper *)mpeyefinder)->findEyes( *rimage, faces ); } catch( ... ) { // Probably not much we can do, except possibly keep track of how many exceptions are thrown in what amount of time // and possibly warn the user if it happens too often NSLog( @"Exception thrown while trying to find eyes." ); return; } } double largestArea = 0.0; for( EyeFaceList::iterator faceIter = faces.begin(), last_face = faces.end(); faceIter != last_face; ++faceIter ) { NSRect oneFace; NSPoint oneLeftEye; NSPoint oneRightEye; oneFace.origin.x = faceIter->x; oneFace.origin.y = faceIter->y; oneFace.size.width = faceIter->xSize; oneFace.size.height = faceIter->ySize; oneLeftEye.x = faceIter->both_eyes.xLeft; oneLeftEye.y = faceIter->both_eyes.yLeft; oneRightEye.x = faceIter->both_eyes.xRight; oneRightEye.y = faceIter->both_eyes.yRight; double faceArea = faceIter->xSize * faceIter->ySize; if( (faceIter->x > 0.0) && (faceIter->y > 0.0) && (faceArea > largestArea) ) { face = oneFace; leftEye = oneLeftEye; rightEye = oneRightEye; largestArea = faceArea; } MPT_Face *mptFace = [[MPT_Face alloc] initWithFace:oneFace left:oneLeftEye right:oneRightEye]; [faceArray addObject:mptFace]; [mptFace release]; } // Criteria are eye x-positions are nonzero and at least 50 pix apart. // That criteria is really only for data sets, but we might want it to be a preference. if( (leftEye.x > 0) && (rightEye.x > 0) && ( (leftEye.x-rightEye.x) > 0) ) { if( doCERT ) { RImage &lpatch = *((RImage *)patch); if( ((MP_RotateFace *)rotator)->rotateImageIntoPatch( rimage, leftEye.x, rightEye.x, leftEye.y, rightEye.y, lpatch ) ) { if( !gabor ) gabor = new GABOR_CLASS( lpatch, lpatch.width, lpatch.height ); else ((GABOR_CLASS *)gabor)->setPixels( lpatch ); std::vector au = ((GABOR_CLASS *)gabor)->CERT_Gabor(); for( int i=0; i < au.size(); ++i ) { double filtered = [self filterValue:au[i] forAU:[self AUAtIndex:i] withFace:true]; [results addObject:[NSNumber numberWithDouble:filtered]]; } } } if( doSmile ) { RIntegral* pfIntegral = static_cast*>(((MP_eyefinderWrapper *)mpeyefinder)->getIntegralPtr().get()); if( SMD_NO_ERR == ((MPSmile *)smileDetector)->findSmilesGivenEyes( *rimage, faces, 1.0f, pfIntegral ) ) { double filtered = [self filterValue:faces.begin()->activation forAU:101 withFace:true]; [self setSmile:filtered]; } else { [self setSmile:0.0]; } } else { // still need to update the filters, if we're using them [self setSmile:[self filterValue:0.0 forAU:101 withFace:false]]; } } else { // No face found for( int i=0; i < [self AUCnt]; ++i ) { [self filterValue:0.0 forAU:[self AUAtIndex:i] withFace:false]; } [self setSmile:[self filterValue:0.0 forAU:101 withFace:false]]; } } - (unsigned int)AUCnt { static const unsigned int auCnt = 9; return auCnt; } - (int)AUAtIndex:(unsigned int)ind { if( ind < [self AUCnt] ) return auValues[ind]; return 0; } - (unsigned int)indexForAU:(int)au { unsigned int ind=-1; switch( au ) { case 1: ind=0; case 2: ind=1; case 4: ind=2; case 5: ind=3; case 10: ind=4; case 12: ind=5; case 14: ind=6; case 20: ind=7; case 101: ind=8; } return ind; } - (NSString *)nameForAU:(int)au { static const NSString *auNames[] = { @"AU 1", @"AU 2", @"AU 4", @"AU 5", @"AU 10", @"AU 12", @"AU 14", @"AU 20", @"Smile" }; unsigned int ind = [self indexForAU:au]; if( ind != -1 ) return auNames[ind]; return @"Unkown"; } - (double)filterValue:(double)value forAU:(int)AU withFace:(bool)faceFound { if( doFilters ) { ExpressionFilterWrapper *locFilter = static_cast(filters); return locFilter->filterValue( AU, value, faceFound ); } else { return value; } } - (NSArray *)results { return results; } - (NSImage *)grayImage { return grayImage; } - (NSImage *)videoImage { return videoImage; } - (NSPoint)left { return leftEye; } - (NSPoint)right { return rightEye; } - (NSRect)face { return face; } - (NSArray *)faces { if( [faceArray count] > 0 ) return faceArray; else return nil; } - (MPT_Face *)largestFace { MPT_Face *largest = nil; float area = 0.0; NSEnumerator *faceEnum = [faceArray objectEnumerator]; MPT_Face *aFace; while( aFace = [faceEnum nextObject] ) { NSRect faceRect = [aFace face]; float faceArea = faceRect.size.width * faceRect.size.height; if( faceArea > area ) { largest = aFace; area = faceArea; } } return largest; } - (void)clearResults { leftEye = NSMakePoint( -1.0, -1.0 ); rightEye = leftEye; face = NSMakeRect( -1.0, -1.0, -1.0, -1.0 ); [results removeAllObjects]; [faceArray removeAllObjects]; } - (double)smile { return smile; } - (void)setSmile:(double)value { smile = value; } - (BOOL)doCERT { return doCERT; } - (void)setDoCERT:(BOOL)value { if (doCERT != value) { doCERT = value; } } - (void)setDoingSequence:(BOOL)seq { ((MP_eyefinderWrapper *)mpeyefinder)->setUseSequence( seq ); } - (BOOL)findFaces { return findFaces; } - (void)setFindFaces:(BOOL)value { if( findFaces != value ) { findFaces = value; } } - (BOOL)displayGray { return displayGray; } - (void)setDisplayGray:(BOOL)value { if (displayGray != value) { displayGray = value; } } - (BOOL)doSmile { return doSmile; } - (void)setDoSmile:(BOOL)value { doSmile = value; } - (BOOL)doFilters { return doFilters; } - (void)setDoFilters:(BOOL)value { doFilters = value; } - (float)eyefinderOffset { return eyefinderOffset; } - (float)defaultEyefinderOffset { return 1.0f; // cascadeThreshAdjustor::default_cascade_adjust[0]; } - (void)setEyefinderOffset:(float)value { eyefinderOffset = value; ((MP_eyefinderWrapper *)mpeyefinder)->setCascadeThresholds( eyefinderOffset, eyefinderDropoff ); } - (float)eyefinderDropoff { return eyefinderDropoff; } - (float)defaultEyefinderDropoff { return 1.0f; // cascadeThreshAdjustor::default_cascade_adjust[1]; } - (void)setEyefinderDropoff:(float)value { eyefinderDropoff = value; ((MP_eyefinderWrapper *)mpeyefinder)->setCascadeThresholds( eyefinderOffset, eyefinderDropoff ); } @end