/* * main.cpp * * Copyright (c) 2006 Machine Perception Laboratory * University of California San Diego. * * Authors: Andrew Salamon, Josh Susskind * * Please read the disclaimer and notes about redistribution * at the end of this file. * */ #include // for cout and cerr #include #include #include #include #include // For system independent path support #include #include #include // For basename and extension namespace bf = boost::filesystem; #include "rimage.h" #include "ImageDirIterator.h" #include "ImageLoader.h" #include "VideoIterator.h" #include "CERT_Config.h" #include "CERT_Arguments.h" #include "CERT_Loader.h" #include "CERT_Scheduler.h" #include "CERT_Task.h" //#ifdef CERT_WIN_DEMO #include "mp_CERT_Public.h" typedef MP_CERT_Public CERT_CLASS; //#else //#include "mp_CERT.h" //typedef MP_CERT CERT_CLASS; //#endif #include "PluginController.h" #include "AfterFacePlugin.h" #include "FacePlugin.h" #include "SVMPlugin.h" #include "FeaturePlugin.h" std::ostream &mout(); ///< Get an output stream, either cout or a file stream depending on user options. void demoSchedulerTaskCallback( CERT::Task task ); CERT::Config *defaultConfig(); std::string getimagepath(); void processPath( CERT_CLASS &cert ); bool processImage( CERT_CLASS &cert ); void processDir( CERT_CLASS &cert ); bool processRimage( CERT_CLASS &cert ); // Default paths to data files #ifdef CERT_WIN_DEMO // Library only distribution std::string defaultFD( "../../DataFiles/featuredetector/" ); std::string defaultSVM( "../../DataFiles/SVMWeights" ); std::string defaultConfigPath( "../../DataFiles/CERT_Lib.cfg" ); #elif WIN32 // Source distribution std::string defaultFD( "../../featuredetector/frozenData/" ); std::string defaultSVM( "../../mp_auCoder/SVMWeights" ); std::string defaultConfigPath( "../../CERTWrapper/CERT_Win.cfg" ); #else // Mac OS command line std::string defaultFD( "./featuredetector/frozenData/" ); std::string defaultSVM( "SVMWeights" ); std::string defaultConfigPath( "./CERT.cfg" ); #endif // End Default paths to data files CERT::VideoIterator videos; CERT::ImageLoader loader; int main (int argc, char *argv[]) { if( !CERT::Arguments::processArgs( argc, argv, defaultConfigPath ) ) { // We still want to output the verision info std::cout << "CERT v" << CERT_CLASS::getVersion() << " (build " << CERT_CLASS::getBuildString() << ")" << std::endl; return -1; } mout() << "CERT v" << CERT_CLASS::getVersion() << " (build " << CERT_CLASS::getBuildString() << ")" << std::endl; // Now set up the plugin controller and let it load the config file try { CERT::PluginController::getPluginController( CERT::Arguments::configPath() ); } catch( const CERT::Config::exception &error ) { std::cerr << "Unable to read configuration file from: " << CERT::Arguments::configPath() << ". Using default values." << std::endl; std::cerr << "The error was: " << error.what() << std::endl; // If we failed to load from the given config file, create a new controller with the default config CERT::PluginController *pc = CERT::PluginController::getPluginController(); pc->loadPluginsFromConfig( defaultConfig() ); } std::cout << "Loading data files... " << std::endl; const std::vector< std::string > &paths( CERT::Arguments::paths() ); bool doSched = false; if( CERT::Arguments::webcam() || (CERT::Arguments::numberOfThreads() != 1) ) { try { CERT::Scheduler scheduler( paths, CERT::Arguments::numberOfThreads() ); scheduler.setTaskCallback( &demoSchedulerTaskCallback ); std::cout << "Finished loading data files" << std::endl; doSched = true; if( CERT::Arguments::webcam() ) scheduler.startWebcam(); else scheduler.start(); } catch( const CERT::Scheduler::exception &error ) { std::cerr << "Unable to set up multiple CERT threads, continuing with one CERT thread." << std::endl; std::cerr << "The error was: " << error.what() << std::endl; } catch( const std::exception &error ) { std::cerr << error.what() << std::endl; return -4; } catch( const std::string error ) { // The feature detector throws strings if there is an error loading data files. std::cerr << error << std::endl; return -2; } } if( !doSched ) { CERT_CLASS *cert(NULL); // CERT will load all SVM and Feature plugins from the plugin controller. try { cert = new CERT_CLASS( true ); } catch( const std::exception &error ) { std::cerr << error.what() << std::endl; return -4; } catch( const std::string error ) { // The feature detector throws strings if there is an error loading data files. std::cerr << error << std::endl; return -2; } std::auto_ptr certDeleter(cert); // make sure cert get's deleted std::cout << "Finished loading data files" << std::endl; CERT::Task &task( cert->getTask() ); unsigned int pathCount = paths.size(); if( pathCount > 0 ) { for( unsigned int index = 0; index < pathCount; ++index ) { task.clear(); // Make sure to clear the task first. Should be done right before setting the path. task.path = paths[index]; processPath( *cert ); } } else { do { std::string path = getimagepath(); if( "quit" == path ) break; task.clear(); task.path = path; processPath( *cert ); } while( true ); } } CERT::PluginController::cleanup(); CERT::Arguments::cleanup(); loader.cleanup(); videos.cleanup(); return 0; } void printMessage( const std::string &path, const std::string &msg ) { mout() << path; if( !CERT::Arguments::tabDelim() ) mout() << std::endl; mout() << '\t' << msg << std::endl; if( !CERT::Arguments::tabDelim() ) mout() << std::endl; } void processPath( CERT_CLASS &cert ) { CERT::Task &task( cert.getTask() ); bf::path bfPath( task.path, bf::native ); if( bf::exists( bfPath ) ) { if( bf::is_directory( bfPath ) ) processDir( cert ); else { processImage( cert ); } } else { printMessage( task.path, "File does not exist" ); } } void processDir( CERT_CLASS &cert ) { CERT::Task &task( cert.getTask() ); std::string dirPath = task.path; ImageDirIterator images( dirPath ); int count = 0; while( ++images ) { task.clear(); task.path = *images; processImage( cert ); ++count; } if( 0 == count ) { printMessage( task.path, "Directory is empty" ); } } bool processImage( CERT_CLASS &cert ) { bool ok(false); CERT::Task &task( cert.getTask() ); if( loader.loadRImage( task.rimage, task.path ) ) // handles loading an image from file into an RImage { if( processRimage( cert ) ) ok = true; } else { // Unlike the image handlers, some video handlers will try to open and process text files // so we have to explicitly handle them here as best we can. bf::path bfPath( task.path, bf::native ); std::string ext = bf::extension(bfPath); if( (".txt" == ext) || (".TXT" == ext) || (".text" == ext) || (".TEXT" == ext) ) { printMessage( task.path, "Skipping text file" ); } else { videos.setPath( task.path ); if( videos.isVideo() ) { while( videos.nextFrame( task.rimage ) ) { std::ostringstream frameStream; frameStream << task.path << ":" << videos.getFrameNumber(); task.clear(); task.path = frameStream.str(); processRimage( cert ); } ok = true; } else { // This really ought to be in the StandardOutput plugin. // Maybe a processNoFile( path ) method on cert which just calls doResultsPlugins. printMessage( task.path, "Unable to read file" ); } videos.setPath( "" ); // This should release the movie, so it doesn't stick around in memory. } } return ok; } bool processRimage( CERT_CLASS &cert ) { try { CERT::Task &task( cert.getTask() ); cert.processTask(); CERT::Scheduler::postProcessTask( task ); // At this point, all CERT processing and post processing is done, you can look through the results in task. // The data in task will be overwritten the next time processTask is called, so if you need to keep it around // make a copy of task. CERT::Task task2 = task; } catch( std::runtime_error &error ) { // This is probably because the images being run are not the right size and are not actually cropped faces. // mout() << "\tImage is not the right size for a cropped face. It needs to be 96x96 pixels."; // We could also output the error message itself. mout() << '\t' << error.what() << std::endl; return false; } if( CERT::Arguments::inputIsFaces() || cert.didFindFace() ) { return true; } return false; } /*************************************************************/ std::string getimagepath() { std::string path; std::cout << "Enter path to image"; #ifdef WIN32 std::cout << " (Windows demo only accepts 24 bit bitmaps)"; #endif std::cout << " or ctrl-c to exit" << std::endl; std::cout << "--> "; // std::cin >> path; std::getline( std::cin, path ); if( std::cin.eof() ) path = "quit"; return(path); } std::ostream &mout() { return CERT::Arguments::outputStream(); } CERT::Config *defaultConfig() { CERT::Config *tmp = new CERT::Config; CERT::ConfigItem *item = new CERT::ConfigItem; item->setPluginID( "defaultID" ); item->setName( "FACS Codes 4.0" ); item->setInternalName( "CERT4_0_Weights" ); item->setPluginType( "MPT_PluginStep_SVM" ); item->setEnabled( true ); item->setValueForKey( defaultSVM, "dir" ); std::vector labels; labels.push_back( "(AU 1) Inner Brow Raise" ); labels.push_back( "(AU 2) Outer Brow Raise" ); labels.push_back( "(AU 4) Brow Lower" ); labels.push_back( "(AU 5) Eye Widen" ); labels.push_back( "(AU 9) Nose Wrinkle" ); labels.push_back( "(AU 10) Lip Raise" ); labels.push_back( "(AU 12) Lip Corner Pull" ); labels.push_back( "(AU 14) Dimpler" ); labels.push_back( "(AU 15) Lip Corner Depressor" ); labels.push_back( "(AU 17) Chin Raise" ); labels.push_back( "(AU 20) Lip stretch" ); item->setValueForKey( labels, "labels" ); tmp->addItem( item ); CERT::ConfigItem *feature = new CERT::ConfigItem; feature->setPluginID( "left_eye" ); feature->setName( "Left Eye" ); feature->setInternalName( "left_eye" ); feature->setPluginType( "MPT_PluginStep_Features" ); feature->setEnabled( true ); feature->setDisplay( false ); feature->setValueForKey( (defaultFD + "left_eye_CERT3.fdtxml"), "feature" ); feature->setValueForKey( (defaultFD + "left_eye_CERT3.gpriorxml"), "gprior" ); tmp->addItem( feature ); feature = new CERT::ConfigItem; feature->setPluginID( "right_eye" ); feature->setName( "Right Eye" ); feature->setInternalName( "right_eye" ); feature->setPluginType( "MPT_PluginStep_Features" ); feature->setEnabled( true ); feature->setDisplay( false ); feature->setValueForKey( (defaultFD + "right_eye_CERT3.fdtxml"), "feature" ); feature->setValueForKey( (defaultFD + "right_eye_CERT3.gpriorxml"), "gprior" ); tmp->addItem( feature ); feature = new CERT::ConfigItem; feature->setPluginID( "nose" ); feature->setName( "Nose" ); feature->setInternalName( "nose" ); feature->setPluginType( "MPT_PluginStep_Features" ); feature->setEnabled( true ); feature->setDisplay( false ); feature->setValueForKey( (defaultFD + "nose_CERT3.fdtxml"), "feature" ); feature->setValueForKey( (defaultFD + "nose_CERT3.gpriorxml"), "gprior" ); tmp->addItem( feature ); feature = new CERT::ConfigItem; feature->setPluginID( "mouth" ); feature->setName( "Mouth" ); feature->setInternalName( "mouth" ); feature->setPluginType( "MPT_PluginStep_Features" ); feature->setEnabled( true ); feature->setDisplay( false ); feature->setValueForKey( (defaultFD + "mouth_CERT3_1.fdtxml"), "feature" ); feature->setValueForKey( (defaultFD + "mouth_CERT3_1.gpriorxml"), "gprior" ); tmp->addItem( feature ); item = new CERT::ConfigItem; item->setPluginID( "standardoutput" ); item->setName( "Standard Output" ); item->setInternalName( "standardoutput" ); item->setPluginType( "MPT_PluginStep_Output" ); item->setEnabled( true ); item->setDisplay( true ); tmp->addItem( item ); return tmp; } /** The Scheduler will pass each task to this function as the task is finished. * You need to set this function as the callback on the scheduler before starting it. */ void demoSchedulerTaskCallback( CERT::Task task ) { if( task.didFindFace ) { std::cout << " ====> Found a face for "; } else { std::cout << " ====> Did Not find a face for "; } std::cout << task.path << std::endl; // task.results; // This is a map, you could iterate over its contents or search for a particular entry by plugin ID. } /* * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */