train-surfpmk.C

Go to the documentation of this file.
00001 /*!
00002    \file Gist/train-surfpmk.C
00003 
00004    \brief Interface for training and testing GistEstimatorSurfPMK.
00005 
00006    The train-bbof program in conjunction with the GistEstimatorSurfPMK
00007    class implements the following paper within the INVT framework:
00008 
00009    Murillo, A. C., Guerrero, J. J., Sagues, C.
00010    SURF features for efficient robot localization with omnidirectional images
00011    ICRA, 2007.
00012 
00013    Whereas the GistEstimatorSurfPMK class is only concerned with the
00014    portions of the above paper that deal with gist vector computations,
00015    this program provides the remaining structure required to implement
00016    the necessary training and image classification functionalities.
00017 
00018    train-surfpmk has two modes of operation, viz., training and testing.
00019    Training mode consists of four distinct phases: SURF descriptor
00020    accumulation, hierarchical K-means clustering, training histograms
00021    collection, and SVM classifier generation. Testing mode operates in a
00022    single phase that uses the results of the hierarchical clustering,
00023    histograms collection and SVM classifier generation training phases to
00024    classify input images into appropriate categories.
00025 */
00026 
00027 // //////////////////////////////////////////////////////////////////// //
00028 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the //
00029 // University of Southern California (USC) and the iLab at USC.         //
00030 // See http://iLab.usc.edu for information about this project.          //
00031 // //////////////////////////////////////////////////////////////////// //
00032 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00033 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00034 // in Visual Environments, and Applications'' by Christof Koch and      //
00035 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00036 // pending; application number 09/912,225 filed July 23, 2001; see      //
00037 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00038 // //////////////////////////////////////////////////////////////////// //
00039 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00040 //                                                                      //
00041 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00042 // redistribute it and/or modify it under the terms of the GNU General  //
00043 // Public License as published by the Free Software Foundation; either  //
00044 // version 2 of the License, or (at your option) any later version.     //
00045 //                                                                      //
00046 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00047 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00048 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00049 // PURPOSE.  See the GNU General Public License for more details.       //
00050 //                                                                      //
00051 // You should have received a copy of the GNU General Public License    //
00052 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00053 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00054 // Boston, MA 02111-1307 USA.                                           //
00055 // //////////////////////////////////////////////////////////////////// //
00056 //
00057 // Primary maintainer for this file: Manu Viswanathan <mviswana at usc dot edu>
00058 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Gist/train-surfpmk.C $
00059 // $Id: train-surfpmk.C 13889 2010-09-07 23:54:33Z mviswana $
00060 //
00061 
00062 //--------------------------- LIBRARY CHECK -----------------------------
00063 
00064 #if   !defined(INVT_HAVE_LIBSURF) \
00065    || !defined(INVT_HAVE_LIBTORCH)
00066 
00067 #include "Util/log.H"
00068 
00069 int main()
00070 {
00071    LERROR("Sorry, this program needs the SURF, PMK and torch libraries.") ;
00072    return 255 ;
00073 }
00074 
00075 #else // the actual program in all its hideous glory
00076 
00077 //------------------------------ HEADERS --------------------------------
00078 
00079 // Gist specific headers
00080 #include "Neuro/GistEstimatorSurfPMK.H"
00081 
00082 // Other INVT headers
00083 #include "Neuro/StdBrain.H"
00084 #include "Neuro/NeuroOpts.H"
00085 #include "Neuro/NeuroSimEvents.H"
00086 
00087 #include "Media/SimFrameSeries.H"
00088 #include "Media/MediaOpts.H"
00089 
00090 #include "Simulation/SimEventQueue.H"
00091 #include "Simulation/SimEventQueueConfigurator.H"
00092 
00093 #include "Channels/ChannelOpts.H"
00094 #include "Component/ModelManager.H"
00095 #include "Component/ModelOptionDef.H"
00096 
00097 #include "Image/Point2D.H"
00098 
00099 #include "nub/ref.h"
00100 
00101 // SURF headers
00102 #include <opensurf/opensurf.hh>
00103 
00104 // PMK headers
00105 
00106 // Unix headers
00107 #include <glob.h>
00108 #include <unistd.h>
00109 
00110 // Standard C++ headers
00111 #include <fstream>
00112 #include <sstream>
00113 #include <ios>
00114 #include <numeric>
00115 #include <algorithm>
00116 #include <functional>
00117 #include <map>
00118 #include <vector>
00119 #include <iterator>
00120 #include <stdexcept>
00121 #include <utility>
00122 #include <limits>
00123 #include <cmath>
00124 
00125 // torch headers
00126 #include <torch/QCTrainer.h>
00127 #include <torch/SVMClassification.h>
00128 #include <torch/Kernel.h>
00129 #include <torch/MatDataSet.h>
00130 
00131 //------------------------ TEMPLATE UTILITIES ---------------------------
00132 
00133 // Convenient (but perhaps not the most efficient) helper to convert
00134 // various data types to strings.
00135 //
00136 // DEVNOTE: Works as long as type T defines an operator << that writes to
00137 // an ostream.
00138 template<typename T>
00139 std::string to_string(const T& t)
00140 {
00141    std::ostringstream str ;
00142    str << t ;
00143    return str.str() ;
00144 }
00145 
00146 //----------------------- COMMAND LINE OPTIONS --------------------------
00147 
00148 /*!
00149    This program has five distinct phases/modes of operation, each one
00150    specified via a suitable non-option command line argument.
00151    Additionally, it supports several command line options to allow users
00152    to tweak various parameters such as the name of the vocabulary file,
00153    the training histograms database, and so on.
00154 */
00155 namespace {
00156 
00157 const ModelOptionCateg MOC_SURFPMK = {
00158    MOC_SORTPRI_3,
00159    "Options specific to the Surf PMK program",
00160 } ;
00161 
00162 //! In the SURF descriptors accumulation phase, we collect all the
00163 //! descriptors from the training images and store them in a plain text
00164 //! file.
00165 #ifndef SPMK_DEFAULT_TRAINING_DESCRIPTORS_FILE
00166    #define SPMK_DEFAULT_TRAINING_DESCRIPTORS_FILE "surf_descriptors.txt"
00167 #endif
00168 
00169 const ModelOptionDef OPT_SurfDescriptors = {
00170    MODOPT_ARG_STRING, "SurfDescriptors", & MOC_SURFPMK, OPTEXP_CORE,
00171    "This option specifies the name of the file where SURF descriptors\n"
00172    "for the training images are to be accumulated. This is a plain text\n"
00173    "file containing the descriptors that will be fed into the hierarchical\n"
00174    "K-means procedure during the second training phase.\n",
00175    "surf-descriptors", '\0', "surf-descriptors-file",
00176    SPMK_DEFAULT_TRAINING_DESCRIPTORS_FILE,
00177 } ;
00178 
00179 //! In the second phase of training, we perform hierarchical K-means
00180 //! clustering on the SURF descriptors accumulated in the first phase and
00181 //! store the results in yet another plain text file.
00182 #ifndef SPMK_DEFAULT_VOCABULARY_FILE
00183    #define SPMK_DEFAULT_VOCABULARY_FILE "surf_vocabulary.txt"
00184 #endif
00185 
00186 const ModelOptionDef OPT_SurfVocabulary = {
00187    MODOPT_ARG_STRING, "SurfVocabulary", & MOC_SURFPMK, OPTEXP_CORE,
00188    "This option specifies the name of the file in which the \"prototypical\"\n"
00189    "SURF descriptors are (or are to be) stored. This is a plain text\n"
00190    "file containing the centroids of the hierarchical K-means clusters,\n"
00191    "which are used during gist vector computation to create feature maps\n"
00192    "and, subsequently, the flattened multi-level histograms using the\n"
00193    "pyramid matching as described in the Murillo paper.\n",
00194    "surf-vocabulary", '\0', "surf-vocabulary-file",
00195    SPMK_DEFAULT_VOCABULARY_FILE,
00196 } ;
00197 
00198 //! In the third phase of training, we compute and store the gist vectors
00199 //! for the training images. These gist vectors are used in the next
00200 //! training phase as the data points that will be used to create
00201 //! appropriate SVM classifiers for each image category.
00202 #ifndef SPMK_DEFAULT_TRAINING_HISTOGRAMS_FILE
00203    #define SPMK_DEFAULT_TRAINING_HISTOGRAMS_FILE "training_histograms.txt"
00204 #endif
00205 
00206 const ModelOptionDef OPT_HistogramsFile = {
00207    MODOPT_ARG_STRING, "HistogramsFile", & MOC_SURFPMK, OPTEXP_CORE,
00208    "This option specifies the name of the training histograms database,\n"
00209    "a plain text file containing one histogram entry per line. The\n"
00210    "first field specifies the name plus number of the entry (e.g.,\n"
00211    "foo.mpg:1, bar.mpg:5, and so on). The second field specifies the ground\n"
00212    "truth for this particular image. The remaining fields are simply the\n"
00213    "numbers making up the image's flattened out multi-level histogram,\n"
00214    "which serves as its gist vector.\n",
00215    "training-histograms", '\0', "training-histograms-file",
00216    SPMK_DEFAULT_TRAINING_HISTOGRAMS_FILE,
00217 } ;
00218 
00219 //! In the fourth phase of training, we create SVM classifiers for each
00220 //! of the categories and store the relevant parameters to a text file
00221 //! for later use during image classification. Each segment will have its
00222 //! own SVM classifier. Therefore, the default value of this symbol is
00223 //! not a good one to use and it should be explicitly specified on the
00224 //! command line.
00225 #ifndef SPMK_DEFAULT_SVM_CLASSIFIER_FILE
00226    #define SPMK_DEFAULT_SVM_CLASSIFIER_FILE "svm_classifier.bin"
00227 #endif
00228 
00229 const ModelOptionDef OPT_SvmClassifierFile = {
00230    MODOPT_ARG_STRING, "SvmClassifierFile", & MOC_SURFPMK, OPTEXP_CORE,
00231    "This option specifies the name of the file that will hold the SVM\n"
00232    "classifier for a given segment. This file is read and written by the\n"
00233    "torch library.",
00234    "svm-classifier", '\0', "svm-classifier-file",
00235    SPMK_DEFAULT_SVM_CLASSIFIER_FILE,
00236 } ;
00237 
00238 //! While creating SVM classifiers for each of the categories, we need a
00239 //! temp file to store the training histograms data in the format
00240 //! required by the torch library. Usually, it would be a good idea to
00241 //! explicitly specify this on the command line rather than relying on
00242 //! the compiled in default.
00243 #ifndef SPMK_DEFAULT_SVM_TEMP_FILE
00244    #define SPMK_DEFAULT_SVM_TEMP_FILE "/tmp/train-surfpmk-torch-dataset.txt"
00245 #endif
00246 
00247 const ModelOptionDef OPT_SvmTempFile = {
00248    MODOPT_ARG_STRING, "SvmTempFile", & MOC_SURFPMK, OPTEXP_CORE,
00249    "This option specifies the name of the temp file that will hold the SVM\n"
00250    "training data in the format required by the torch library. This file is\n"
00251    "automatically deleted when it is no longer required.",
00252    "svm-temp", '\0', "svm-temp-file",
00253    SPMK_DEFAULT_SVM_TEMP_FILE,
00254 } ;
00255 
00256 //! In image classification mode, we write the results to a plain text
00257 //! file.
00258 #ifndef SPMK_DEFAULT_CLASSIFICATION_RESULTS_FILE
00259    #define SPMK_DEFAULT_CLASSIFICATION_RESULTS_FILE \
00260       "surfpmk_classifications.txt"
00261 #endif
00262 
00263 const ModelOptionDef OPT_ResultsFile = {
00264    MODOPT_ARG_STRING, "ResultsFile", & MOC_SURFPMK, OPTEXP_CORE,
00265    "This option specifies the name of the classification results file,\n"
00266    "a plain text file containing one result entry per line. The first\n"
00267    "field specifies the name of the input image plus number of the entry,\n"
00268    "(e.g., foo.mpg:1, bar.mpg:5, and so on). Then comes the ground truth\n"
00269    "for this image followed by its classification result.\n",
00270    "results-file", '\0', "classification-results-file",
00271    SPMK_DEFAULT_CLASSIFICATION_RESULTS_FILE,
00272 } ;
00273 
00274 //! Several of the data files output by different operational modes of
00275 //! this program require inclusion of the current image/frame name and
00276 //! number and the ground truth segment/category number. These options
00277 //! allow users to specify appropriate values for this required info.
00278 //!
00279 //! NOTE: The default values for these options are not very useful.
00280 //! They really ought to be explicitly specified on the command line.
00281 #ifndef SPMK_DEFAULT_IMAGE_NAME
00282    #define SPMK_DEFAULT_IMAGE_NAME "some_image"
00283 #endif
00284 #ifndef SPMK_DEFAULT_SEGMENT_NUMBER
00285    #define SPMK_DEFAULT_SEGMENT_NUMBER "0"
00286 #endif
00287 
00288 const ModelOptionDef OPT_ImageName = {
00289    MODOPT_ARG_STRING, "ImageName", & MOC_SURFPMK, OPTEXP_CORE,
00290    "This option specifies the \"root\" name for an image. The image number\n"
00291    "will be automatically appended to this \"root\" name with a colon as the\n"
00292    "separator between name and frame number. The current input MPEG file\n"
00293    "name is a good choice for the value of this option.\n",
00294    "image-name", '\0', "input-MPEG-file-name",
00295    SPMK_DEFAULT_IMAGE_NAME,
00296 } ;
00297 
00298 const ModelOptionDef OPT_SegmentNumber = {
00299    MODOPT_ARG_STRING, "SegmentNumber", & MOC_SURFPMK, OPTEXP_CORE,
00300    "This option specifies the segment number for an image in the training\n"
00301    "set. The segment number is used to specify the ground truth for the\n"
00302    "image classification.\n",
00303    "segment-number", '\0', "image-segment-number",
00304    SPMK_DEFAULT_SEGMENT_NUMBER,
00305 } ;
00306 
00307 /*!
00308    The different operational modes of this program must be specified as
00309    the one and only non-option command line argument. This "action"
00310    command must be one of the following strings (case-sensitive!):
00311 
00312    1. surf -- accumulate the SURF descriptors for the training images
00313       in the plain text file specified by the --surf-descriptors option.
00314       By default, the descriptors will be accumulated in
00315       ./surf_descriptors.txt.
00316 
00317       Additionally, the --image-name and --segment-number options are
00318       required as this information is also recorded in the SURF
00319       descriptors file.
00320 
00321    2. vocab -- compute the SURF descriptors vocabulary, i.e., the
00322       "protototypical" SURF descriptors, from the accumulated descriptors
00323       using the hierarchical K-means implementation in libpmk.
00324 
00325       For this action, the --surf-descriptors option specifies the input
00326       file for the K-means while the --surf-vocabulary option specifies
00327       the output file. The defaults are to read from
00328       ./surf_descriptors.txt and write to ./surf_vocabulary.txt.
00329 
00330    3. hist -- compute the flattened out multi-level histograms for the
00331       training set. The output is sent to the text file specified by the
00332       --histograms-file option.
00333 
00334       The --image-name and --segment-number options are also required.
00335 
00336    4. svm -- generate the SVM classifiers for each of the categories.
00337       The --svm-classifier file specifies the name of the file to which
00338       the SVM parameters will be stored. By default, this is
00339       ./svm_classifier.bin. Users should supply a file name different
00340       from the default. Otherwise, this file will get overwritten for
00341       each segment.
00342 
00343       The --histograms-file can be used to specify the input data for
00344       this action.
00345 
00346       In addition to the above two options, this action also needs the
00347       --svm-temp option to store the histograms data in the format
00348       required by the torch library. The default value is okay for this
00349       option. However, if several instances of this program can be
00350       executing in parallel, it would be best to supply different temp
00351       files explicitly on the command line.
00352 
00353    5. classify -- uses the vocabulary and SVM classifiers produced by the
00354       vocab and svm actions to classify the input images streaming in.
00355       Classification results are written, by default, to
00356       ./classification_results.txt; but this can be changed with the
00357       --results-file option.
00358 
00359       The --surf-vocabulary and --svm-classifier options can be used to
00360       specify appropriate values for the different pieces of input
00361       required by the classify action. Note that the --svm-classifier
00362       option does not point to a specific classifier, but really is a
00363       "root" name to use. This program will automatically load all the
00364       classifiers that begin with this root. For example, if the user
00365       specifies --svm-classifier="ACB_svm_classifier", this program will
00366       load all the classifiers whose file names begin with
00367       "ACB_svm_classifier." and append numbers starting at 1.
00368 */
00369 #ifndef SPMK_SURF_CMD
00370    #define SPMK_SURF_CMD "surf"
00371 #endif
00372 #ifndef SPMK_VOCABULARY_CMD
00373    #define SPMK_VOCABULARY_CMD "vocab"
00374 #endif
00375 #ifndef SPMK_HISTOGRAM_CMD
00376    #define SPMK_HISTOGRAM_CMD "hist"
00377 #endif
00378 #ifndef SPMK_SVM_CMD
00379    #define SPMK_SVM_CMD "svm"
00380 #endif
00381 #ifndef SPMK_CLASSIFY_CMD
00382    #define SPMK_CLASSIFY_CMD "classify"
00383 #endif
00384 
00385 // For printing usage info
00386 #ifndef SPMK_ACTIONS
00387    #define SPMK_ACTIONS ("{"SPMK_SURF_CMD"|"SPMK_VOCABULARY_CMD"|"\
00388                           SPMK_HISTOGRAM_CMD"|"SPMK_SVM_CMD"|"\
00389                           SPMK_CLASSIFY_CMD"}")
00390 #endif
00391 
00392 } // end of local namespace encapsulating command line options section
00393 
00394 //--------------------- SIMULATION ENCAPSULATION ------------------------
00395 
00396 // The following helper class wraps around the ModelManager and
00397 // associated objects, providing a neatly encapsulated API for the main
00398 // program.
00399 namespace {
00400 
00401 class SPMKSimulation {
00402    ModelManager model_manager ;
00403    nub::soft_ref<SimEventQueueConfigurator> configurator ;
00404    nub::soft_ref<StdBrain> brain ;
00405    nub::ref<SimInputFrameSeries> input_frame_series ;
00406 
00407    // Various command line options specific to this program
00408    OModelParam<std::string> sd_option ; // --surf-descriptors
00409    OModelParam<std::string> sv_option ; // --surf-vocabulary
00410    OModelParam<std::string> th_option ; // --training-histograms
00411    OModelParam<std::string> sc_option ; // --svm-classifier
00412    OModelParam<std::string> st_option ; // --svm-temp
00413    OModelParam<std::string> rf_option ; // --results-file
00414    OModelParam<std::string> in_option ; // --image-name (not --in!)
00415    OModelParam<std::string> sn_option ; // --segment-number
00416 
00417 public :
00418    SPMKSimulation(const std::string& model_name) ;
00419    void parse_command_line(int argc, const char* argv[]) ;
00420    void run() ;
00421    ~SPMKSimulation() ;
00422 
00423 private :
00424    // The different actions performed by this program
00425    typedef void (SPMKSimulation::*Action)() ;
00426    typedef std::map<std::string, Action> ActionMap ;
00427    ActionMap action_map ;
00428 
00429    void accumulate_surf_descriptors() ;
00430    void compute_surf_vocabulary() ;
00431    void compute_training_histograms() ;
00432    void generate_svm_classifier() ;
00433    void classify_input_images() ;
00434 
00435    // Accessors for retrieving some of the command line arguments
00436    std::string surf_descriptors_file() {return sd_option.getVal() ;}
00437    std::string surf_vocabulary_file()  {return sv_option.getVal() ;}
00438    std::string histograms_file()       {return th_option.getVal() ;}
00439    std::string svm_classifier_file()   {return sc_option.getVal() ;}
00440    std::string svm_temp_file()         {return st_option.getVal() ;}
00441    std::string results_file()          {return rf_option.getVal() ;}
00442    std::string image_name()            {return in_option.getVal() ;}
00443    std::string segment_number()        {return sn_option.getVal() ;}
00444 } ;
00445 
00446 // On instantiation, create the model manager and the simulation's
00447 // various components.
00448 SPMKSimulation::SPMKSimulation(const std::string& model_name)
00449    : model_manager(model_name),
00450      configurator(new SimEventQueueConfigurator(model_manager)),
00451      brain(new StdBrain(model_manager)),
00452      input_frame_series(new SimInputFrameSeries(model_manager)),
00453      sd_option(& OPT_SurfDescriptors,   & model_manager),
00454      sv_option(& OPT_SurfVocabulary,    & model_manager),
00455      th_option(& OPT_HistogramsFile,    & model_manager),
00456      sc_option(& OPT_SvmClassifierFile, & model_manager),
00457      st_option(& OPT_SvmTempFile,       & model_manager),
00458      rf_option(& OPT_ResultsFile,       & model_manager),
00459      in_option(& OPT_ImageName,         & model_manager),
00460      sn_option(& OPT_SegmentNumber,     & model_manager)
00461 {
00462    model_manager.addSubComponent(configurator) ;
00463    model_manager.addSubComponent(brain) ;
00464    model_manager.addSubComponent(input_frame_series) ;
00465 
00466    typedef SPMKSimulation me ; // typing shortcut
00467    action_map[SPMK_SURF_CMD]       = & me::accumulate_surf_descriptors ;
00468    action_map[SPMK_VOCABULARY_CMD] = & me::compute_surf_vocabulary ;
00469    action_map[SPMK_HISTOGRAM_CMD]  = & me::compute_training_histograms ;
00470    action_map[SPMK_SVM_CMD]        = & me::generate_svm_classifier ;
00471    action_map[SPMK_CLASSIFY_CMD]   = & me::classify_input_images ;
00472 }
00473 
00474 // TODO: And how can we force the gist estimator type to be always
00475 // GistEstimatorSurfPMK? This program doesn't make sense for any other
00476 // gist estimator.
00477 void SPMKSimulation::parse_command_line(int argc, const char* argv[])
00478 {
00479    model_manager.setOptionValString(& OPT_GistEstimatorType, "SurfPMK") ;
00480 
00481    model_manager.setOptionValString(& OPT_SurfDescriptors,
00482                                     SPMK_DEFAULT_TRAINING_DESCRIPTORS_FILE) ;
00483    model_manager.setOptionValString(& OPT_SurfVocabulary,
00484                                     SPMK_DEFAULT_VOCABULARY_FILE) ;
00485    model_manager.setOptionValString(& OPT_HistogramsFile,
00486                                     SPMK_DEFAULT_TRAINING_HISTOGRAMS_FILE ) ;
00487    model_manager.setOptionValString(& OPT_SvmClassifierFile,
00488                                     SPMK_DEFAULT_SVM_CLASSIFIER_FILE ) ;
00489    model_manager.setOptionValString(& OPT_SvmTempFile,
00490                                     SPMK_DEFAULT_SVM_TEMP_FILE ) ;
00491    model_manager.setOptionValString(& OPT_ResultsFile,
00492                                     SPMK_DEFAULT_CLASSIFICATION_RESULTS_FILE) ;
00493 
00494    model_manager.setOptionValString(& OPT_ImageName,
00495                                     SPMK_DEFAULT_IMAGE_NAME) ;
00496    model_manager.setOptionValString(& OPT_SegmentNumber,
00497                                     SPMK_DEFAULT_SEGMENT_NUMBER) ;
00498 
00499    if (! model_manager.parseCommandLine(argc, argv, SPMK_ACTIONS, 1, 1))
00500       throw std::runtime_error("command line parse error") ;
00501 }
00502 
00503 // To run the simulation, we simply dispatch to the function
00504 // corresponding to the action (non-option) command line argument.
00505 void SPMKSimulation::run()
00506 {
00507    std::string cmd(model_manager.getExtraArg(0)) ;
00508    ActionMap::iterator action = action_map.find(cmd) ;
00509    if (action == action_map.end())
00510       throw std::runtime_error(cmd + ": sorry, unknown action") ;
00511    (this->*(action->second))() ;
00512 }
00513 
00514 // Do we really not have to delete the configurator, brain and input
00515 // frame series? If it turns out we do, this empty destructor will have
00516 // to be filled out with the necessary delete calls...
00517 SPMKSimulation::~SPMKSimulation(){}
00518 
00519 // Quick helper class to start and stop model manager (useful when
00520 // exceptions are thrown because destructor automatically stops the model
00521 // manager without requiring an explicit call to the stop method prior to
00522 // throwing the exception).
00523 class ModelManagerStarter {
00524    ModelManager& mgr ;
00525 public :
00526    ModelManagerStarter(ModelManager& m) : mgr(m) {mgr.start() ;}
00527    ~ModelManagerStarter() {mgr.stop() ;}
00528 } ;
00529 
00530 } // end of local namespace encapsulating simulation encapsulation section
00531 
00532 //------------------------------- MAIN ----------------------------------
00533 
00534 int main(int argc, const char* argv[])
00535 {
00536    MYLOGVERB = LOG_INFO ; // suppress debug messages
00537    try
00538    {
00539       SPMKSimulation S("train-surfpmk Model") ;
00540       S.parse_command_line(argc, argv) ;
00541       S.run() ;
00542    }
00543    catch (std::exception& e)
00544    {
00545       LFATAL("%s", e.what()) ;
00546       return 1 ;
00547    }
00548    return 0 ;
00549 }
00550 
00551 //------------------- SURF DESCRIPTORS ACCUMULATION ---------------------
00552 
00553 // This section contains the code for accumulating the SURF descriptors
00554 // of the training images, i.e., phase one of training.
00555 namespace {
00556 
00557 // Useful shortcut
00558 typedef GistEstimatorSurfPMK::SurfKeypoints SurfKeypoints ;
00559 
00560 // Quick helper for storing the SURF descriptors of the training images
00561 // to a file.
00562 class surf_descriptors_accumulator {
00563    surf_descriptors_accumulator() ; // private to disallow instantiation
00564    ~surf_descriptors_accumulator() ;
00565 public :
00566    static std::string output_file ;
00567    static std::string image_name ;
00568    static int         frame_number ;
00569    static std::string segment_number ;
00570 
00571    static void write(const SurfKeypoints&) ;
00572 } ;
00573 
00574 // This method implements the simulation's main loop for the "surf"
00575 // action. Prior to starting the main loop though, it configures the
00576 // SurfPMK gist estimator's training callback, which is triggered at each
00577 // step of the brain's evolution. The SurfPMK gist estimator passes the
00578 // SURF descriptors for the current input image to this callback, which
00579 // then proceeds to accumulate them in the file specified by the
00580 // --surf-descriptors option.
00581 void SPMKSimulation::accumulate_surf_descriptors()
00582 {
00583 
00584   LFATAL("please fix me!");
00585   /*
00586    ModelManagerStarter M(model_manager) ;
00587 
00588    nub::soft_ref<GistEstimatorSurfPMK> ge =
00589       dynCastWeak<GistEstimatorSurfPMK>(brain->getGE()) ;
00590    if (ge.isInvalid())
00591       throw std::runtime_error("can only use GistEstimatorSurfPMK") ;
00592 
00593    typedef surf_descriptors_accumulator acc ;
00594    acc::output_file    = surf_descriptors_file() ;
00595    acc::image_name     = image_name() ;
00596    acc::segment_number = segment_number() ;
00597    ge->setTrainingHook(acc::write) ;
00598 
00599    LINFO("MVN: accumulating SURF descriptors") ;
00600    nub::ref<SimEventQueue> event_queue = configurator->getQ() ;
00601    for(;;)
00602    {
00603       try
00604       {
00605          input_frame_series->evolve(*event_queue) ;
00606          acc::frame_number = input_frame_series->frame() ;
00607          brain->evolve(*event_queue) ; // triggers training hook
00608          if (event_queue->evolve() != SIM_CONTINUE)
00609             break ;
00610       }
00611       catch (lfatal_exception&) // if we seek beyond end of frame series
00612       {
00613          break ; // prevent LFATAL induced abortion
00614       }
00615    }
00616    LINFO("MVN: done accumulating SURF descriptors") ;
00617   */
00618 }
00619 
00620 // Static data members for storing the SURF descriptors file name and
00621 // other pertinent info persistently across multiple invocations of the
00622 // GistEstimatorSurfPMK's training hook.
00623 std::string surf_descriptors_accumulator::output_file ;
00624 std::string surf_descriptors_accumulator::image_name ;
00625 int         surf_descriptors_accumulator::frame_number ;
00626 std::string surf_descriptors_accumulator::segment_number ;
00627 
00628 // The following function is meant to be used as the GistEstimatorSurfPMK
00629 // training hook. It simply appends the SURF descriptors passed to it
00630 // (stored as an Image<GistEstimatorSurfPMK::SurfDescriptor>) to the
00631 // output file. The format of this file is as shown below:
00632 //
00633 // MPEG-file-name:frame-number  segment-number  row  col  SURF-descriptor
00634 //
00635 // The MPEG file name should be explicitly specified with the
00636 // --image-name option. The frame number is extracted automatically from
00637 // the input frame series. The segment number represents the ground
00638 // truth for the input image's category and should be specified
00639 // explicitly on the command line with the --segment-number option. The
00640 // row and col values are the SURF grid coordinates. And, finally, the
00641 // SURF descriptor itself consists of 128 numbers.
00642 //
00643 // DEVNOTE: We could open the output file once and use that object to
00644 // avoid reopening (by using a static ostream data member rather than a
00645 // static string). However, if the program were to somehow crash halfway
00646 // through, then the SURF descriptors output file would be in an
00647 // inconsistent state and rerunning the program can result in appending
00648 // data to a possibly inconsistent dataset, which would only make things
00649 // worse.
00650 //
00651 // Thus, we choose to open and close the output file each time the
00652 // GistEstimatorSurfPMK training hook is triggered. (Of course, if the
00653 // program cashes while this function is executing, then all bets are
00654 // off; the SURF descriptors file's inconsistency will be unavoidable in
00655 // this case.)
00656 void surf_descriptors_accumulator::write(const SurfKeypoints& G)
00657 {
00658    if (output_file.empty())
00659       throw std::runtime_error("SURF descriptors accumulator output file "
00660                                "not specified") ;
00661 
00662    std::ofstream ofs(output_file.c_str(), std::ios::out | std::ios::app) ;
00663    for (unsigned int i = 0; i < G.size(); ++i)
00664       ofs << image_name << ':' << frame_number << ' '
00665           << segment_number << ' ' << i << ' ' << G[i] << '\n' ;
00666 }
00667 
00668 } // end of local namespace encapsulating SURF descriptors accumulation section
00669 
00670 //-------------------- SURF VOCABULARY COMPUTATION ----------------------
00671 
00672 // This section contains the code for the hierarchical K-means clustering
00673 // of the SURF descriptors of the training images (i.e., training phase
00674 // two).
00675 namespace {
00676 
00677 // Useful types
00678 typedef Image<float> Vocabulary ;
00679 
00680 // Forward declarations
00681 int  count_lines(const std::string& file_name) ;
00682 void load_surf_descriptors(const std::string& file_name, int num_lines) ;
00683 void kmeans(int K) ;
00684 void save_vocabulary(const std::string& file_name) ;
00685 
00686 // The following method implements the "vocab" action of this program
00687 // for clustering the SURF descriptors of the training images to obtain
00688 // the 200 "prototypical" SURF descriptors that form the basis of the
00689 // gist vector computation in terms of these "words" or "vis-terms".
00690 void SPMKSimulation::compute_surf_vocabulary()
00691 {
00692    LINFO("MVN: counting lines in %s", surf_descriptors_file().c_str()) ;
00693    int num_rows = count_lines(surf_descriptors_file()) ;
00694 
00695    LINFO("MVN: reading %d SURF descriptors from %s",
00696          num_rows, surf_descriptors_file().c_str()) ;
00697    //OpenCVMatrix surf_descriptors =
00698       load_surf_descriptors(surf_descriptors_file(), num_rows) ;
00699 
00700    //const int K = GistEstimatorSurfPMK::NUM_CHANNELS ;
00701    //LINFO("MVN: doing K-means on SURF descriptors to get %d clusters", K) ;
00702    //OpenCVMatrix vocabulary = kmeans(K, surf_descriptors) ;
00703 
00704    LINFO("MVN: K-means done; saving SURF vocabulary to %s",
00705          surf_vocabulary_file().c_str()) ;
00706    save_vocabulary(surf_vocabulary_file()) ;
00707 }
00708 
00709 // The following function reads the SURF descriptors for the training
00710 // images into an OpenCV matrix. It must know how many lines the SURF
00711 // descriptors file has. This quantity is the number of rows in resulting
00712 // matrix. The number of columns is simply the size of each SURF
00713 // descriptor (usually: 128 values make up a SURF descriptor).
00714 void load_surf_descriptors(const std::string& file_name, int num_rows)
00715 {
00716    int num_cols = GistEstimatorSurfPMK::SURF_DESCRIPTOR_SIZE ;
00717 
00718    double d ; std::string dummy ; // for ignoring first four fields
00719    std::ifstream ifs(file_name.c_str()) ;
00720    for (int i = 0; i < num_rows; ++i)
00721    {
00722       std::string str ;
00723       std::getline(ifs, str) ;
00724       if (! ifs || str.empty()) {
00725         if (i == num_rows - 1) // okay; read all rows
00726           break ;
00727         else { // descriptors file missing data or some other error
00728           throw std::runtime_error(file_name +
00729                                    ": missing SURF descriptors or other read error") ;
00730         }
00731       }
00732       std::istringstream line(str) ;
00733       line >> dummy >> dummy >> dummy >> dummy ;
00734 
00735       for (int j = 0; j < num_cols; ++j) {
00736          if (! line) {
00737             throw std::runtime_error(file_name +
00738                ": missing SURF descriptor values on line " + to_string(i)) ;
00739          }
00740          line >> d ;
00741       }
00742    }
00743 
00744    //return M ;
00745    //*/
00746 }
00747 
00748 // This function performs K-means clustering on the supplied data matrix
00749 // and returns the cluster centers.
00750 void kmeans(int K)
00751 {
00752 
00753    LINFO("MVN: computing K-means cluster assignments with libPMK") ;
00754 
00755    LINFO("MVN: cluster assignments done; computing centroids...") ;
00756    //return compute_centroids(K, data, cluster_assignments) ;
00757 
00758    LFATAL("hum, I did nothing here, please fix my code!");
00759 }
00760 
00761 // Write the SURF vocabulary, row by row, to a plain text file.
00762 void save_vocabulary(
00763                      const std::string& file_name)
00764 {
00765    std::ofstream ofs(file_name.c_str()) ;
00766    /*
00767    for (int i = 0; i < vocabulary.num_rows(); ++i) {
00768       for (int j = 0; j < vocabulary.num_cols(); ++j)
00769          ofs << vocabulary.get<float>(i, j) << ' ' ;
00770       ofs << '\n' ;
00771    }
00772    //*/
00773 }
00774 
00775 // Read the SURF vocabulary from a plain text file into an Image<T>
00776 Vocabulary load_vocabulary(const std::string& file_name)
00777 {
00778    const int M = count_lines(file_name) ;
00779    const int N = GistEstimatorSurfPMK::SURF_DESCRIPTOR_SIZE ;
00780    Vocabulary V(N, M, ZEROS) ;
00781 
00782    float f ;
00783    std::ifstream ifs(file_name.c_str()) ;
00784    for (int j = 0; j < M; ++j)
00785       for (int i = 0; i < N; ++i) {
00786          if (! ifs)
00787             throw std::runtime_error(file_name + ": out of data?!?") ;
00788          ifs >> f ;
00789          V.setVal(i, j, f) ;
00790       }
00791 
00792    return V ;
00793 }
00794 
00795 } // end of local namespace encapsulating SURF vocabulary computation section
00796 
00797 //------------------- TRAINING HISTOGRAM PROCESSING ---------------------
00798 
00799 // Training is a two step process: first, we use hierarchical K-means to
00800 // cluster the training set's SURF descriptors to create the vocabulary.
00801 // Then, we collect the histograms counting the "prototypical" SURF
00802 // descriptors in the training images. The vocabulary and training set's
00803 // histogram "database" are both used for image classification.
00804 namespace {
00805 
00806 // Some useful types for dealing with texton histograms
00807 typedef Image<double> Histogram ;
00808 
00809 // Forward declarations
00810 void save_histogram(const Histogram& histogram, const std::string& file_name,
00811                     const std::string& image_name, int frame_number,
00812                     const std::string& segment_number) ;
00813 
00814 // This method implements the "hist" action of this program. Like the
00815 // accumulate action, it implements a "main loop" for the simulation,
00816 // evolving different components with each iteration. But rather than
00817 // dipping into the GistEstimatorSurfPMK's processing pipeline, it
00818 // loads the SURF vocabulary and then uses GistEstimatorSurfPMK to
00819 // obtain the flattened out multi-level histogram for each of the
00820 // training images. These histograms are saved to the training
00821 // histograms database specified by the --histograms-file option.
00822 void SPMKSimulation::compute_training_histograms()
00823 {
00824   LFATAL("please fix me!!");
00825   /*
00826    ModelManagerStarter M(model_manager) ;
00827 
00828    nub::soft_ref<GistEstimatorSurfPMK> ge =
00829       dynCastWeak<GistEstimatorSurfPMK>(brain->getGE()) ;
00830    if (ge.isInvalid())
00831       throw std::runtime_error("can only use GistEstimatorSurfPMK") ;
00832 
00833    Vocabulary V = load_vocabulary(surf_vocabulary_file()) ;
00834    ge->setVocabulary(V) ;
00835    LINFO("MVN: loaded SURF vocabulary of %d vis-terms from %s",
00836          V.getHeight(), surf_vocabulary_file().c_str()) ;
00837 
00838    nub::ref<SimEventQueue> event_queue = configurator->getQ() ;
00839    for(;;)
00840    {
00841       try
00842       {
00843          input_frame_series->evolve(*event_queue) ;
00844          brain->evolve(*event_queue) ;
00845          SeC<SimEventGistOutput> gist_out =
00846             event_queue->check<SimEventGistOutput>(brain.get(),
00847                                                    SEQ_UNMARKED | SEQ_MARK,
00848                                                    ge.get()) ;
00849          if (gist_out) // Spmk GE has a gist vector waiting to be picked up
00850             save_histogram(ge->getGist(), histograms_file(),
00851                            image_name(), input_frame_series->frame(),
00852                            segment_number()) ;
00853          if (event_queue->evolve() != SIM_CONTINUE)
00854             break ;
00855       }
00856       catch (lfatal_exception&) // if we seek beyond end of frame series
00857       {
00858          return ; // prevent LFATAL induced abortion
00859       }
00860    }
00861   */
00862 }
00863 
00864 // This function appends a training image's histogram to the training
00865 // histograms database file under the supplied "entry" name. As we did in
00866 // the SURF descriptors accumulation function, in order to minimize
00867 // possible inconsistencies in this database, we choose to open and close
00868 // the training histograms file with each invocation of this helper
00869 // rather than keep a persistent ostream object around that obviates the
00870 // need for repeated file open/close operations.
00871 void save_histogram(const Histogram& histogram, const std::string& file_name,
00872                     const std::string& image_name, int frame_number,
00873                     const std::string& segment_number)
00874 {
00875    std::ofstream ofs(file_name.c_str(), std::ios::out | std::ios::app) ;
00876    ofs << image_name << ':' << frame_number << ' '
00877        << segment_number << ' ' ;
00878    for (int y = 0; y < histogram.getHeight(); ++y) // should be just one row
00879       for (int x = 0; x < histogram.getWidth(); ++x) // should be 4200 columns
00880          ofs << histogram.getVal(x, y) << ' ' ;
00881    ofs << '\n' ;
00882 }
00883 
00884 } // end of local namespace encapsulating training histograms section
00885 
00886 //--------------------- SVM CLASSIFIER GENERATION -----------------------
00887 
00888 namespace {
00889 
00890 // Forward declarations
00891 void create_torch_dataset(const std::string&, const std::string&,
00892                           const std::string&) ;
00893 Torch::SVMClassification* create_torch_classifier(const std::string&) ;
00894 std::string temp_file_name() ;
00895 
00896 // The following method implements this program's "svm" action.
00897 void SPMKSimulation::generate_svm_classifier()
00898 {
00899    create_torch_dataset(histograms_file(), segment_number(), svm_temp_file()) ;
00900    Torch::SVMClassification* svm = create_torch_classifier(svm_temp_file()) ;
00901    svm->save(svm_classifier_file().c_str()) ;
00902    delete svm ;
00903    unlink(svm_temp_file().c_str()) ;
00904 }
00905 
00906 // Quick helper for reading and writing gist vectors from/to a file
00907 struct GistVector {
00908    std::vector<double> values ;
00909    GistVector() ;
00910 } ;
00911 
00912 GistVector::GistVector()
00913    : values(GistEstimatorSurfPMK::GIST_VECTOR_SIZE)
00914 {}
00915 
00916 std::istream& operator>>(std::istream& is, GistVector& g)
00917 {
00918    for (int i = 0; i < GistEstimatorSurfPMK::GIST_VECTOR_SIZE; ++i)
00919       is >> g.values[i] ;
00920    return is ;
00921 }
00922 
00923 std::ostream& operator<<(std::ostream& os, const GistVector& g)
00924 {
00925    for (int i = 0; i < GistEstimatorSurfPMK::GIST_VECTOR_SIZE; ++i)
00926       os << g.values[i] << ' ' ;
00927    return os ;
00928 }
00929 
00930 // The torch library needs its datasets in a particular format.
00931 // Unfortunately, this program works with some other format. The
00932 // following function reads the histograms file saved by an earlier run
00933 // of this program and creates a corresponding torch dataset for
00934 // subsequent training of an SVM classifier for the specified target
00935 // segment.
00936 void create_torch_dataset(const std::string& hist_file,
00937                           const std::string& target,
00938                           const std::string& torch_dataset)
00939 {
00940    const int n = count_lines(hist_file) ;
00941 
00942    std::ifstream in(hist_file.c_str()) ;
00943    std::ofstream out(torch_dataset.c_str()) ;
00944 
00945    std::string dummy, segment, str ;
00946    GistVector gist_vector ;
00947    out << n << ' ' << (GistEstimatorSurfPMK::GIST_VECTOR_SIZE + 1) << '\n' ;
00948    for (int i = 0; i < n; ++i)
00949    {
00950       std::getline(in, str) ;
00951       if (! in || str.empty()) {
00952         if (i == n - 1) // okay; all training histograms read successfully
00953           break ;
00954         else {
00955           out.close() ;
00956           unlink(torch_dataset.c_str()) ;
00957           throw std::runtime_error(hist_file +
00958                                      ": missing data or other read error") ;
00959         }
00960       }
00961       std::istringstream line(str) ;
00962       line >> dummy >> segment >> gist_vector ;
00963       out << gist_vector << ' ' << ((segment == target) ? +1 : -1) << '\n' ;
00964    }
00965 }
00966 
00967 // The histogram intersection kernel for matching gist vectors of
00968 // different images.
00969 class HistIntKernel : public Torch::Kernel {
00970    //Torch::real eval(Torch::Sequence*, Torch::Sequence*) ;
00971    real eval(Torch::Sequence*, Torch::Sequence*) ;
00972 } ;
00973 
00974 //Torch::real HistIntKernel::eval(Torch::Sequence* a, Torch::Sequence* b)
00975 real HistIntKernel::eval(Torch::Sequence* a, Torch::Sequence* b)
00976 {
00977    //Torch::real sum = 0 ;
00978    real sum = 0 ;
00979    for (int i = 0; i < a->frame_size; ++i)
00980       //sum += std::min(a->frames[0][i], b->frames[0][i]) ;
00981       sum += min(a->frames[0][i], b->frames[0][i]) ;
00982    return sum ;
00983 }
00984 
00985 // The following function creates an SVM classifier using the histogram
00986 // intersection kernel defined above.
00987 Torch::SVMClassification* create_torch_classifier(const std::string& dataset)
00988 {
00989    HistIntKernel kernel ;
00990    Torch::SVMClassification* svm = new Torch::SVMClassification(& kernel) ;
00991    Torch::QCTrainer trainer(svm) ;
00992    Torch::MatDataSet data(dataset.c_str(),
00993                           GistEstimatorSurfPMK::GIST_VECTOR_SIZE, 1) ;
00994    trainer.train(& data, 0) ;
00995    return svm ;
00996 }
00997 
00998 } // end of local namespace encapsulating SVM classifier generation section
00999 
01000 //----------------------- IMAGE CLASSIFICATION --------------------------
01001 
01002 namespace {
01003 
01004 // Useful typedefs
01005 typedef std::vector<Torch::SVMClassification*> Classifiers ;
01006 
01007 // Forward declarations
01008 Classifiers load_classifiers(std::string, HistIntKernel*) ;
01009 void classify_image(const Histogram&, const Classifiers&,
01010                     const std::string&, int, const std::string&,
01011                     const std::string&) ;
01012 void nuke_classifiers(Classifiers&) ;
01013 
01014 // The following method implements this program's "classify" action. It
01015 // reads the SURF descriptors vocabulary and computes gist vectors for
01016 // input images using the SurfPMK gist estimator. Then, it uses the SVM
01017 // classifiers generated by the "svm" action to decide which category the
01018 // input image belongs to.
01019 void SPMKSimulation::classify_input_images()
01020 {
01021   LFATAL("please fixme too!");
01022   /*
01023    ModelManagerStarter M(model_manager) ;
01024 
01025    nub::soft_ref<GistEstimatorSurfPMK> ge =
01026       dynCastWeak<GistEstimatorSurfPMK>(brain->getGE()) ;
01027    if (ge.isInvalid())
01028       throw std::runtime_error("can only use GistEstimatorSurfPMK") ;
01029 
01030    Vocabulary V = load_vocabulary(surf_vocabulary_file()) ;
01031    ge->setVocabulary(V) ;
01032    LINFO("MVN: loaded SURF vocabulary of %d vis-terms from %s",
01033          V.getHeight(), surf_vocabulary_file().c_str()) ;
01034 
01035    HistIntKernel kernel ;
01036    Classifiers svm_classifiers =
01037       load_classifiers(svm_classifier_file(), & kernel) ;
01038 
01039    nub::ref<SimEventQueue> event_queue = configurator->getQ() ;
01040    for(;;)
01041    {
01042       try
01043       {
01044          input_frame_series->evolve(*event_queue) ;
01045          brain->evolve(*event_queue) ;
01046          SeC<SimEventGistOutput> gist_out =
01047             event_queue->check<SimEventGistOutput>(brain.get(),
01048                                                    SEQ_UNMARKED | SEQ_MARK,
01049                                                    ge.get()) ;
01050          if (gist_out) // SurfPMK GE has a gist vector waiting to be picked up
01051             classify_image(ge->getGist(), svm_classifiers,
01052                            image_name(), input_frame_series->frame(),
01053                            segment_number(), results_file()) ;
01054          if (event_queue->evolve() != SIM_CONTINUE)
01055             break ;
01056       }
01057       catch (lfatal_exception&) // if we seek beyond end of frame series
01058       {
01059          nuke_classifiers(svm_classifiers) ;
01060          return ; // prevent LFATAL induced abortion
01061       }
01062    }
01063 
01064    nuke_classifiers(svm_classifiers) ;
01065   */
01066 }
01067 
01068 // Given an input image's gist vector and the SVM classifiers for all the
01069 // categories, this function checks which categories the input image
01070 // belongs to and writes the results to the classification results file.
01071 void classify_image(const Histogram&   gist_vector,
01072                     const Classifiers& classifiers,
01073                     const std::string& image_name, int frame_number,
01074                     const std::string& ground_truth,
01075                     const std::string& results_file)
01076 {
01077    std::ofstream ofs(results_file.c_str(), std::ios::out | std::ios::app) ;
01078    ofs << image_name << ':' << frame_number << ' ' << ground_truth << ' ' ;
01079 
01080    Torch::Sequence gv(1, GistEstimatorSurfPMK::GIST_VECTOR_SIZE) ;
01081    std::copy(gist_vector.begin(), gist_vector.end(), gv.frames[0]) ;
01082 
01083    int n = 0 ; // num categories into which input image can be classified
01084    const int N = classifiers.size() ;
01085    for (int i = 0; i < N; ++i) {
01086       classifiers[i]->forward(& gv) ;
01087       if (classifiers[i]->outputs->frames[0][0] > 0) {
01088          ofs << (i+1) << ' ' ;
01089          ++n ;
01090       }
01091    }
01092 
01093    if (! n) // input image could not be classified into any category
01094       ofs << '0' ;
01095    ofs << '\n' ;
01096 }
01097 
01098 // This function loads all the SVM classifiers beginning with the
01099 // specified "root" name. Here's how this is supposed to work:
01100 //
01101 // Let's say we 9 categories. Earlier runs of this program ought to have
01102 // created 9 SVM classifiers. Usually, these would be named
01103 // "XXX_svm_classifier.1", "XXX_svm_classifier.2", "XXX_svm_classifier.3"
01104 // and so on. This function will read each of these files back into
01105 // memory using the torch library (which is what created those files in
01106 // the first place).
01107 //
01108 // The kernel for each of these SVM classifiers is the histogram
01109 // intersection kernel as described in the Grauman ICCV 2006 paper.
01110 Classifiers
01111 load_classifiers(std::string classifiers_root_name, HistIntKernel* kernel)
01112 {
01113    classifiers_root_name += ".*" ;
01114    glob_t buf ;
01115    if (glob(classifiers_root_name.c_str(), 0, 0, & buf) != 0)
01116       throw std::runtime_error("couldn't find/load the SVM classifiers") ;
01117 
01118    const int N = buf.gl_pathc ;
01119    Classifiers classifiers(N) ;
01120    for (int i = 0; i < N; ++i) {
01121       classifiers[i] = new Torch::SVMClassification(kernel) ;
01122       classifiers[i]->load(buf.gl_pathv[i]) ;
01123    }
01124 
01125    globfree(& buf) ;
01126    return classifiers ;
01127 }
01128 
01129 // Delete all SVM classifier objects created in previous function
01130 void nuke_classifiers(Classifiers& C)
01131 {
01132    const int N = C.size() ;
01133    for (int i = 0; i < N; ++i)
01134       delete C[i] ;
01135 }
01136 
01137 } // end of local namespace encapsulating image classification section
01138 
01139 //-------------------------- UTILITY ROUTINES ---------------------------
01140 
01141 namespace {
01142 
01143 // Count the number of lines in a file (wc -l)
01144 int count_lines(const std::string& file_name)
01145 {
01146    int n = -1 ; // because EOF is read after final \n (1 extra iter. of loop)
01147    std::ifstream ifs(file_name.c_str()) ;
01148 
01149    std::string dummy ;
01150    while (ifs) {
01151       getline(ifs, dummy) ;
01152       ++n ;
01153    }
01154    return n ;
01155 }
01156 
01157 } // end of local namespace encapsulating utility routines section
01158 
01159 //-----------------------------------------------------------------------
01160 
01161 #endif // #if !defined(HAVE_OPENCV || INVT_HAVE_LIBPMK || INVT_HAVE_LIBTORCH)
01162 
01163 /* So things look consistent in everyone's emacs... */
01164 /* Local Variables: */
01165 /* indent-tabs-mode: nil */
01166 /* End: */
Generated on Sun May 8 08:40:39 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3