evaluateCompleteBoWPipeline.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /**
  2. * @file evaluateCompleteBoWPipeline.cpp
  3. * @brief A complete BoW pipeline: feature extraction, codebook creation, vector quantization, classifier training, evaluation on separate test set
  4. * @author Alexander Freytag
  5. * @date 10-05-2013
  6. */
  7. //STL
  8. #include <iostream>
  9. #include <limits>
  10. //core -- basic stuff
  11. #include <core/basics/Config.h>
  12. #include <core/basics/ResourceStatistics.h>
  13. #include <core/basics/Timer.h>
  14. #include <core/image/Convert.h>
  15. #include <core/vector/VectorT.h>
  16. //vislearning -- basic stuff
  17. #include <vislearning/baselib/Globals.h>
  18. #include <vislearning/baselib/ICETools.h>
  19. #include <vislearning/baselib/ProgressBar.h>
  20. #include <vislearning/cbaselib/MultiDataset.h>
  21. #include <vislearning/cbaselib/Example.h>
  22. #include <vislearning/cbaselib/ClassificationResult.h>
  23. #include <vislearning/cbaselib/ClassificationResults.h>
  24. //
  25. // vislearning -- classifier
  26. #include <vislearning/classifier/classifierbase/VecClassifier.h>
  27. #include <vislearning/classifier/genericClassifierSelection.h>
  28. //
  29. // vislearning -- BoW codebooks
  30. #include "vislearning/features/simplefeatures/CodebookPrototypes.h"
  31. #include "vislearning/features/simplefeatures/BoWFeatureConverter.h"
  32. //
  33. // vislearning -- local features
  34. #include <vislearning/features/localfeatures/GenericLFSelection.h>
  35. //
  36. // vislearning -- clustering methods
  37. #include <vislearning/math/cluster/GenericClusterAlgorithmSelection.h>
  38. //
  39. using namespace std;
  40. using namespace NICE;
  41. using namespace OBJREC;
  42. /**
  43. a complete BoW pipeline
  44. possibly, we can make use of objrec/progs/testClassifier.cpp
  45. */
  46. int main( int argc, char **argv )
  47. {
  48. #ifdef __GLIBCXX__
  49. std::set_terminate( __gnu_cxx::__verbose_terminate_handler );
  50. #endif
  51. Config * conf = new Config ( argc, argv );
  52. const bool writeClassificationResults = conf->gB( "main", "writeClassificationResults", true );
  53. const std::string resultsfile = conf->gS( "main", "resultsfile", "/tmp/results.txt" );
  54. ResourceStatistics rs;
  55. // ========================================================================
  56. // TRAINING STEP
  57. // ========================================================================
  58. MultiDataset md( conf );
  59. const LabeledSet *trainFiles = md["train"];
  60. //**********************************************
  61. //
  62. // FEATURE EXTRACTION FOR TRAINING IMAGES
  63. //
  64. //**********************************************
  65. std::cerr << "FEATURE EXTRACTION FOR TRAINING IMAGES" << std::endl;
  66. OBJREC::LocalFeatureRepresentation * featureExtractor = OBJREC::GenericLFSelection::selectLocalFeatureRep ( conf, "features", OBJREC::GenericLFSelection::TRAINING );
  67. //collect features in a single data structure
  68. NICE::VVector featuresFromAllTrainingImages;
  69. featuresFromAllTrainingImages.clear();
  70. //determine how many training images we actually use to easily allocate the correct amount of memory afterwards
  71. int numberOfTrainingImages ( 0 );
  72. for(LabeledSet::const_iterator classIt = trainFiles->begin() ; classIt != trainFiles->end() ; classIt++)
  73. {
  74. numberOfTrainingImages += classIt->second.size();
  75. std::cerr << "number of examples for this class: " << classIt->second.size() << std::endl;
  76. }
  77. //okay, this is redundant - but I see no way to do it more easy right now...
  78. std::vector<NICE::VVector> featuresOfImages ( numberOfTrainingImages );
  79. //this again is somehow redundant, but we need the labels lateron for easy access - change this to a better solution :)
  80. NICE::VectorT<int> labelsTrain ( numberOfTrainingImages, 0 );
  81. int imgCnt ( 0 );
  82. // the corresponding nasty makro: LOOP_ALL_S( *trainFiles )
  83. for(LabeledSet::const_iterator classIt = trainFiles->begin() ; classIt != trainFiles->end() ; classIt++)
  84. {
  85. for ( std::vector<ImageInfo *>::const_iterator imgIt = classIt->second.begin();
  86. imgIt != classIt->second.end();
  87. imgIt++, imgCnt++
  88. )
  89. {
  90. // the corresponding nasty makro: EACH_INFO( classno, info );
  91. int classno ( classIt->first );
  92. const ImageInfo imgInfo = *(*imgIt);
  93. std::string filename = imgInfo.img();
  94. NICE::ColorImage img( filename );
  95. //compute features
  96. //variables to store feature information
  97. NICE::VVector features;
  98. NICE::VVector positions;
  99. Globals::setCurrentImgFN ( filename );
  100. featureExtractor->extractFeatures ( img, features, positions );
  101. //normalization :)
  102. for ( NICE::VVector::iterator i = features.begin();
  103. i != features.end();
  104. i++)
  105. {
  106. i->normalizeL1();
  107. }
  108. //collect them all in a larger data structure
  109. featuresFromAllTrainingImages.append( features );
  110. //and store it as well in the data struct that additionally keeps the information which features belong to which image
  111. //TODO this can be made more clever!
  112. // featuresOfImages.push_back( features );
  113. featuresOfImages[imgCnt] = features;
  114. labelsTrain[imgCnt] = classno;
  115. }
  116. }
  117. //**********************************************
  118. //
  119. // CODEBOOK CREATION
  120. //
  121. //**********************************************
  122. std::cerr << "CODEBOOK CREATION" << std::endl;
  123. OBJREC::ClusterAlgorithm * clusterAlgo = OBJREC::GenericClusterAlgorithmSelection::selectClusterAlgorithm ( conf );
  124. NICE::VVector prototypes;
  125. std::vector<double> weights;
  126. std::vector<int> assignments;
  127. std::cerr << "call cluster of cluster algo " << std::endl;
  128. clusterAlgo->cluster( featuresFromAllTrainingImages, prototypes, weights, assignments );
  129. std::cerr << "create new codebook with the computed prototypes" << std::endl;
  130. OBJREC::CodebookPrototypes * codebook = new OBJREC::CodebookPrototypes ( prototypes );
  131. //**********************************************
  132. //
  133. // VECTOR QUANTIZATION OF
  134. // FEATURES OF TRAINING IMAGES
  135. //
  136. //**********************************************
  137. OBJREC::BoWFeatureConverter * bowConverter = new OBJREC::BoWFeatureConverter ( conf, codebook );
  138. OBJREC::LabeledSetVector trainSet;
  139. NICE::VVector histograms ( featuresOfImages.size() /* number of vectors*/, 0 /* dimension of vectors*/ ); //the internal vectors will be resized within calcHistogram
  140. NICE::VVector::iterator histogramIt = histograms.begin();
  141. NICE::VectorT<int>::const_iterator labelsIt = labelsTrain.begin();
  142. for (std::vector<NICE::VVector>::const_iterator imgIt = featuresOfImages.begin(); imgIt != featuresOfImages.end(); imgIt++, histogramIt++, labelsIt++)
  143. {
  144. bowConverter->calcHistogram ( *imgIt, *histogramIt );
  145. bowConverter->normalizeHistogram ( *histogramIt );
  146. //NOTE perhaps we should use add_reference here
  147. trainSet.add( *labelsIt, *histogramIt );
  148. }
  149. //**********************************************
  150. //
  151. // CLASSIFIER TRAINING
  152. //
  153. //**********************************************
  154. std::string classifierType = conf->gS( "main", "classifierType", "GPHIK" );
  155. OBJREC::VecClassifier * classifier = OBJREC::GenericClassifierSelection::selectVecClassifier( conf, classifierType );
  156. //this method adds the training data to the temporary knowledge of our classifier
  157. classifier->teach( trainSet );
  158. //now the actual training step starts (e.g., parameter estimation, ... )
  159. classifier->finishTeaching();
  160. // ========================================================================
  161. // TEST STEP
  162. // ========================================================================
  163. const LabeledSet *testFiles = md["test"];
  164. delete featureExtractor;
  165. featureExtractor = OBJREC::GenericLFSelection::selectLocalFeatureRep ( conf, "features", OBJREC::GenericLFSelection::TESTING );
  166. NICE::Matrix confusionMat ( testFiles->size() /* number of classes for testing*/, trainFiles->size() /* number of classes in training */, 0.0 );
  167. NICE::Timer t;
  168. ClassificationResults results;
  169. ProgressBar pbClasses;
  170. // the corresponding nasty makro: LOOP_ALL_S( *testFiles )
  171. for(LabeledSet::const_iterator classIt = testFiles->begin() ; classIt != testFiles->end() ; classIt++)
  172. {
  173. std::cerr <<" \n\nOverall update bar: " << std::endl;
  174. pbClasses.update ( testFiles->size() );
  175. std::cerr << "\nStart next class " << std::endl;
  176. ProgressBar pbClassExamples;
  177. for ( std::vector<ImageInfo *>::const_iterator imgIt = classIt->second.begin();
  178. imgIt != classIt->second.end();
  179. imgIt++, imgCnt++
  180. )
  181. {
  182. pbClassExamples.update ( classIt->second.size() );
  183. // the corresponding nasty makro: EACH_INFO( classno, info );
  184. int classno ( classIt->first );
  185. const ImageInfo imgInfo = *(*imgIt);
  186. std::string filename = imgInfo.img();
  187. //**********************************************
  188. //
  189. // FEATURE EXTRACTION FOR TEST IMAGES
  190. //
  191. //**********************************************
  192. NICE::ColorImage img( filename );
  193. //compute features
  194. //variables to store feature information
  195. NICE::VVector features;
  196. NICE::VVector positions;
  197. Globals::setCurrentImgFN ( filename );
  198. featureExtractor->extractFeatures ( img, features, positions );
  199. //normalization :)
  200. for ( NICE::VVector::iterator i = features.begin();
  201. i != features.end();
  202. i++)
  203. {
  204. i->normalizeL1();
  205. }
  206. //**********************************************
  207. //
  208. // VECTOR QUANTIZATION OF
  209. // FEATURES OF TEST IMAGES
  210. //
  211. //**********************************************
  212. NICE::Vector histogramOfCurrentImg;
  213. bowConverter->calcHistogram ( features, histogramOfCurrentImg );
  214. bowConverter->normalizeHistogram ( histogramOfCurrentImg );
  215. //**********************************************
  216. //
  217. // CLASSIFIER EVALUATION
  218. //
  219. //**********************************************
  220. uint classno_groundtruth = classno;
  221. t.start();
  222. ClassificationResult r = classifier->classify ( histogramOfCurrentImg );
  223. t.stop();
  224. uint classno_estimated = r.classno;
  225. r.classno_groundtruth = classno_groundtruth;
  226. //if we like to store the classification results for external post processing, uncomment this
  227. if ( writeClassificationResults )
  228. {
  229. results.push_back( r );
  230. }
  231. confusionMat( classno_groundtruth, classno_estimated ) += 1;
  232. }
  233. }
  234. confusionMat.normalizeRowsL1();
  235. std::cerr << confusionMat << std::endl;
  236. std::cerr << "average recognition rate: " << confusionMat.trace()/confusionMat.rows() << std::endl;
  237. if ( writeClassificationResults )
  238. {
  239. double avgRecogResults ( results.getAverageRecognitionRate () );
  240. std::cerr << "average recognition rate according to classificationResults: " << avgRecogResults << std::endl;
  241. results.writeWEKA ( resultsfile, 0 );
  242. }
  243. return 0;
  244. }