/* * 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" #ifdef THREADED_LOADER #include "CERT_Loader.h" #endif //#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. CERT::Config *defaultConfig(); std::string getimagepath(); void processPath( CERT_CLASS &cert, RImage &pixels, const std::string &path ); bool processImage( CERT_CLASS &cert, RImage &pixels, const std::string &path ); void processDir( CERT_CLASS &cert, RImage &pixels, const std::string &path ); bool processRimage( CERT_CLASS &cert, RImage &pixels, const std::string &path ); // 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() ); } CERT_CLASS *cert(NULL); std::cout << "Loading data files... " << std::endl; // 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; RImage pixels; const std::vector< std::string > &paths( CERT::Arguments::paths() ); #ifdef THREADED_LOADER CERT::Loader tloader( paths ); tloader.start(); while( !tloader.finished() ) { CERT::Task task; tloader.getNextTask( task ); if( task.error.length() > 0 ) { mout() << task.path; if( !CERT::Arguments::tabDelim() ) mout() << std::endl; mout() << "\t" << task.error << std::endl; if( !CERT::Arguments::tabDelim() ) mout() << std::endl; } else { processRimage( *cert, task.rimage, task.path ); } } #else unsigned int pathCount = paths.size(); if( pathCount > 0 ) { for( unsigned int index = 0; index < pathCount; ++index ) { processPath( *cert, pixels, paths[index] ); } } else { do { std::string path = getimagepath(); if( "quit" == path ) break; processPath( *cert, pixels, path ); } while( true ); } #endif CERT::PluginController::cleanup(); CERT::Arguments::cleanup(); loader.cleanup(); videos.cleanup(); return 0; } void processPath( CERT_CLASS &cert, RImage &pixels, const std::string &path ) { bf::path bfPath( path, bf::native ); if( bf::exists( bfPath ) ) { if( bf::is_directory( bfPath ) ) processDir( cert, pixels, path ); else { processImage( cert, pixels, path ); } } } void processDir( CERT_CLASS &cert, RImage &pixels, const std::string &path ) { ImageDirIterator images( path ); while( ++images ) { std::string fileName = *images; processImage( cert, pixels, fileName ); } } bool processImage( CERT_CLASS &cert, RImage &pixels, const std::string &path ) { bool ok(false); if( loader.loadRImage(pixels, path ) ) // handles loading an image from file into an RImage { if( processRimage( cert, pixels, path ) ) ok = true; } else { videos.setPath( path ); if( videos.isVideo() ) { while( videos.nextFrame( pixels ) ) { std::ostringstream frameStream; frameStream << path << ":" << videos.getFrameNumber(); processRimage( cert, pixels, frameStream.str() ); } ok = true; } else { // This really ought to be in the StandardOutput plugin. // Maybe a processNoFile( path ) method on cert which just calls doResultsPlugins. mout() << path; if( !CERT::Arguments::tabDelim() ) mout() << std::endl; mout() << "\tUnable to read file" << std::endl; if( !CERT::Arguments::tabDelim() ) mout() << std::endl; } videos.setPath( "" ); // This should release the movie, so it doesn't stick around in memory. } return ok; } bool processRimage( CERT_CLASS &cert, RImage &pixels, const std::string &path ) { try { cert.calcAUs( pixels, path ); } 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(); } /** A Function to generate a default config object if no config file is found. * It also gives a good example of how to hard code config settings instead of using a file. */ 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; } /* * * 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. * */