Bjoern Froehlich před 12 roky
rodič
revize
788a4c00fd

+ 556 - 0
progs/testActiveSemanticSegmentation.cpp

@@ -0,0 +1,556 @@
+// Beispielhafter Aufruf: BUILD_x86_64/progs/testActiveSemanticSegmentation -config <CONFIGFILE>
+
+/**
+* @file testActiveSemanticSegmentation.cpp
+* @brief test semantic segmentation routines with actively selecting regions for labeling
+* @author Alexander Freytag
+* @date 27-02-2013
+*/
+
+#ifdef NICE_USELIB_OPENMP
+#include <omp.h>
+#endif
+
+#include "core/basics/Config.h"
+#include "core/basics/StringTools.h"
+#include <vislearning/baselib/ICETools.h>
+
+#include <semseg/semseg/SemanticSegmentation.h>
+#include <semseg/semseg/SemSegLocal.h>
+#include <semseg/semseg/SemSegCsurka.h>
+#include <semseg/semseg/SemSegNovelty.h>
+#include <semseg/semseg/SemSegNoveltyBinary.h>
+#include <semseg/semseg/SemSegContextTree.h>
+
+#include "core/image/FilterT.h"
+
+#include <core/basics/ResourceStatistics.h>
+
+#include <fstream>
+
+using namespace OBJREC;
+
+using namespace NICE;
+
+using namespace std;
+
+void updateMatrix( const NICE::Image & img, const NICE::Image & gt,
+                   NICE::Matrix & M, const set<int> & forbidden_classes )
+{
+  double subsamplex = gt.width() / ( double )img.width();
+  double subsampley = gt.height() / ( double )img.height();
+
+  for ( int y = 0 ; y < gt.height() ; y++ )
+    for ( int x = 0 ; x < gt.width() ; x++ )
+    {
+      int xx = ( int )( x / subsamplex );
+      int yy = ( int )( y / subsampley );
+
+      if ( xx < 0 ) xx = 0;
+
+      if ( yy < 0 ) yy = 0;
+
+      if ( xx > img.width() - 1 ) xx = img.width() - 1;
+
+      if ( yy > img.height() - 1 ) yy = img.height() - 1;
+
+      int cimg = img.getPixel( xx, yy );
+
+      int gimg = gt.getPixel( x, y );
+
+      if ( forbidden_classes.find( gimg ) == forbidden_classes.end() )
+      {
+        M( gimg, cimg )++;
+      }
+    }
+}
+
+/**
+ test semantic segmentation routines
+*/
+int main( int argc, char **argv )
+{
+  std::set_terminate( __gnu_cxx::__verbose_terminate_handler );
+
+  Config conf( argc, argv );
+  
+  ResourceStatistics rs;
+  
+  bool show_result = conf.gB( "debug", "show_results", false );
+
+  bool write_results = conf.gB( "debug", "write_results", false );
+
+  bool write_results_pascal = conf.gB( "debug", "write_results_pascal", false );
+
+  std::string resultdir = conf.gS( "debug", "resultdir", "." );
+  
+  //how often do we want to iterate between sem-seg and active query?
+  int activeIterations = conf.gI("main", "activeIterations", 1 );
+    
+  if ( write_results )
+  {
+    cerr << "Writing Results to " << resultdir << endl;
+  }
+
+  MultiDataset md( &conf );
+
+  const ClassNames & classNames = md.getClassNames( "train" );
+
+  string method = conf.gS( "main", "method", "SSCsurka" );
+
+  //currently, we only allow SemSegNovelty, because it implements addNovelExamples()
+  SemanticSegmentation *semseg = NULL;
+  
+      Timer timer;
+      timer.start();
+  if ( method == "SSCsurka" )
+  {
+    semseg = new SemSegCsurka( &conf, &md );
+  }
+  else if ( method == "SSContext" )
+  {
+    semseg = new SemSegContextTree( &conf, &md );
+  }
+  else if( method == "SSNovelty" )
+  {
+    semseg = new SemSegNovelty( &conf, &md );
+  }
+  else if( method == "SSNoveltyBinary" )
+  {
+    semseg = new SemSegNoveltyBinary( &conf, &md );
+  }  
+  timer.stop();
+  std::cerr << "AL time for training: " << timer.getLast() << std::endl;
+
+  const LabeledSet *testFiles = md["test"];
+
+  NICE::Matrix M( classNames.getMaxClassno() + 1, classNames.getMaxClassno() + 1 );
+
+  M.set( 0 );
+
+  std::set<int> forbidden_classes;
+  std::string forbidden_classes_s = conf.gS( "analysis", "forbidden_classesTrain", "" );
+  classNames.getSelection( forbidden_classes_s, forbidden_classes );
+  
+  std::set<int> forbidden_classesForActiveLearning;
+  std::string forbidden_classesForActiveLearning_s = conf.gS( "analysis", "forbidden_classesForActiveLearning", "" );
+  classNames.getSelection( forbidden_classesForActiveLearning_s, forbidden_classesForActiveLearning );
+  
+
+  for (int iterationCount = 0; iterationCount < activeIterations; iterationCount++)
+  {
+      //TODO shouldn't we clean the confusion matrix at the beginning of each iteration?
+    
+    std::cerr << "SemSeg AL Iteration: " << iterationCount << std::endl;
+    semseg->setIterationCountSuffix(iterationCount);
+    
+//     ProgressBar pb( "Semantic Segmentation Analysis" );
+// 
+//     pb.show();
+
+    int fileno = 0;
+
+    std::cerr << "start looping over all files" << std::endl;
+    LOOP_ALL_S( *testFiles )
+    {
+      EACH_INFO( classno, info );
+      std::string file = info.img();
+
+      NICE::Image lm;
+      NICE::MultiChannelImageT<double> probabilities;
+
+      if ( info.hasLocalizationInfo() )
+      {
+        const LocalizationResult *l_gt = info.localization();
+
+        lm.resize( l_gt->xsize, l_gt->ysize );
+        //lm.set( 0 );
+        l_gt->calcLabeledImage( lm, classNames.getBackgroundClass() );
+      }
+
+      semseg->semanticseg( file, lm, probabilities );
+
+      fprintf( stderr, "testSemanticSegmentation: Segmentation finished !\n" );
+
+      //ground truth image, needed for updating the confusion matrix
+      //TODO check whether this is really needed, since we computed such a label image already within SemSegNovelty
+      NICE::Image lm_gt;
+
+      if ( info.hasLocalizationInfo() )
+      {
+        const LocalizationResult *l_gt = info.localization();
+
+        lm_gt.resize( l_gt->xsize, l_gt->ysize );
+        lm_gt.set( 0 );
+
+        fprintf( stderr, "testSemanticSegmentation: Generating Labeled NICE::Image (Ground-Truth)\n" );
+        l_gt->calcLabeledImage( lm_gt, classNames.getBackgroundClass() );
+      }
+// // // 
+// // //       std::string fname = StringTools::baseName( file, false );
+// // // 
+// // //       if ( write_results_pascal )
+// // //       {
+// // // 
+// // //         NICE::Image pascal_lm( lm.width(), lm.height() );
+// // //         int backgroundClass = classNames.getBackgroundClass();
+// // // 
+// // //         for ( int y = 0 ; y < lm.height(); y++ )
+// // //           for ( int x = 0 ; x < lm.width(); x++ )
+// // //           {
+// // //             int v = lm.getPixel( x, y );
+// // // 
+// // //             if ( v == backgroundClass )
+// // //               pascal_lm.setPixel( x, y, 255 );
+// // //             else
+// // //               pascal_lm.setPixel( x, y, 255 - v - 1 );
+// // //           }
+// // // 
+// // //         char filename[1024];
+// // // 
+// // //         char *format = ( char * )"pgm";
+// // //         sprintf( filename, "%s/%s.%s", resultdir.c_str(), fname.c_str(), format );
+// // // 
+// // //         pascal_lm.write( filename );
+// // //       }
+// // // 
+      if ( show_result || write_results )
+      {
+        NICE::ColorImage orig( file );
+        NICE::ColorImage rgb;
+        NICE::ColorImage rgb_gt;
+
+        classNames.labelToRGB( lm, rgb );
+
+        classNames.labelToRGB( lm_gt, rgb_gt );
+
+        if ( write_results )
+        {
+  //         char filename[1024];
+  //         char *format = ( char * )"ppm";
+  //         sprintf( filename, "%06d.%s", fileno, format );
+  //         std::string origfilename = resultdir + "/orig_" + string( filename );
+  //         cerr << "Writing to file " << origfilename << endl;
+  //         orig.write( origfilename );
+  //         rgb.write( resultdir + "/result_" + string( filename ) );
+  //         rgb_gt.write( resultdir + "/groundtruth_" + string( filename ) );
+          
+          std::stringstream out;       
+          std::vector< std::string > myList;
+          StringTools::split ( Globals::getCurrentImgFN (), '/', myList );
+          out << resultdir << "/" << myList.back();
+          cerr << "Writing to file " << resultdir << "/"<< myList.back() << endl;
+          
+          std::string noveltyMethodString = conf.gS( "SemSegNovelty",  "noveltyMethod", "gp-variance");
+          orig.write ( out.str() + "_orig.ppm" );
+          rgb.write ( out.str() + "_" + noveltyMethodString + "_result_run_" + NICE::intToString(iterationCount) + ".ppm" );
+          rgb_gt.write ( out.str() + "_groundtruth.ppm" );
+        }
+
+        if ( show_result )
+        {
+  #ifndef NOVISUAL
+          showImage( rgb, "Result" );
+          showImage( rgb_gt, "Groundtruth" );
+          showImage( orig, "Input" );
+  #endif
+        }
+      }
+
+  //#pragma omp critical
+      updateMatrix( lm, lm_gt, M, forbidden_classes );
+
+      std::cerr << M << std::endl;
+
+      fileno++;
+
+//       pb.update( testFiles->count() );
+    } //Loop over all test images
+
+//     pb.hide();
+
+    //**********************************************
+    //                  EVALUATION 
+    //   COMPUTE CONFUSION MAT AND FINAL SCORES
+    //**********************************************
+    timer.start();
+    
+    long maxMemory;
+    rs.getMaximumMemory(maxMemory);
+    cerr << "Maximum memory used: " << maxMemory << " KB" << endl;
+    
+    double overall = 0.0;
+    double sumall = 0.0;
+
+    for ( int r = 0; r < ( int )M.rows(); r++ )
+    {
+      for ( int c = 0; c < ( int )M.cols(); c++ )
+      {
+        if ( r == c )
+          overall += M( r, c );
+
+        sumall += M( r, c );
+      }
+    }
+
+    overall /= sumall;
+
+    // normalizing M using rows
+
+    for ( int r = 0 ; r < ( int )M.rows() ; r++ )
+    {
+      double sum = 0.0;
+
+      for ( int c = 0 ; c < ( int )M.cols() ; c++ )
+        sum += M( r, c );
+
+      if ( fabs( sum ) > 1e-4 )
+        for ( int c = 0 ; c < ( int )M.cols() ; c++ )
+          M( r, c ) /= sum;
+    }
+
+    std::cerr << M << std::endl;
+
+    double avg_perf = 0.0;
+    int classes_trained = 0;
+
+    for ( int r = 0 ; r < ( int )M.rows() ; r++ )
+    {
+      if (( classNames.existsClassno( r ) ) && ( forbidden_classes.find( r ) == forbidden_classes.end() ) )
+      {
+        avg_perf += M( r, r );
+        double lsum = 0.0;
+        for(int r2 = 0; r2 < ( int )M.rows(); r2++)
+        {
+          lsum += M(r,r2);
+        }
+        if(lsum != 0.0)
+        {
+          classes_trained++;
+        }
+      }
+    }
+
+    if ( write_results )
+    {
+      ofstream fout(( resultdir + "/res.txt" ).c_str(), ios::out );
+      fout <<  "overall: " << overall << endl;
+      fout << "Average Performance " << avg_perf / ( classes_trained ) << endl;
+      fout << "Lower Bound " << 1.0  / classes_trained << endl;
+
+      for ( int r = 0 ; r < ( int )M.rows() ; r++ )
+      {
+        if (( classNames.existsClassno( r ) ) && ( forbidden_classes.find( r ) == forbidden_classes.end() ) )
+        {
+          std::string classname = classNames.text( r );
+          fout << classname.c_str() << ": " << M( r, r ) << endl;
+        }
+      }
+
+      fout.close();
+    }
+
+    fprintf( stderr, "overall: %f\n", overall );
+
+    fprintf( stderr, "Average Performance %f\n", avg_perf / ( classes_trained ) );
+    //fprintf(stderr, "Lower Bound %f\n", 1.0 / classes_trained);
+
+    for ( int r = 0 ; r < ( int )M.rows() ; r++ )
+    {
+      if (( classNames.existsClassno( r ) ) && ( forbidden_classes.find( r ) == forbidden_classes.end() ) )
+      {
+        std::string classname = classNames.text( r );
+        fprintf( stderr, "%s: %f\n", classname.c_str(), M( r, r ) );
+      }
+    }
+    
+    timer.stop();
+    std::cout << "AL time for evaluation: " << timer.getLastAbsolute() << std::endl;
+    
+    //**********************************************
+    //          READ QUERY SCORE IMAGES
+    //   AND SELECT THE REGION TO BE LABELED
+    //**********************************************
+    //NOTE this is not needed anymore, since we store everything within SemSegNovelty
+    //However, it is still needed if we use the NN-classifier for the feature learning approach
+    
+//     string alSection = "SemSegNovelty";
+//     std::string noveltyMethodString = conf.gS( alSection,  "noveltyMethod", "gp-variance");
+//     std::string uncertdir = conf.gS("debug", "resultdir", "result");
+//     int testWSize = conf.gI(alSection, "test_window_size", 10);   
+//     
+//     float maxVal(0);
+//     int maxValX(0);
+//     int maxValY(0);
+//     std::vector<ImageInfo *>::const_iterator maxValInfoIt = testFiles->begin()->second.begin();
+//     
+//     
+//     for(LabeledSet::const_iterator outerIt = testFiles->begin() ; outerIt != testFiles->end() ; outerIt++)
+//     {
+//       for ( std::vector<ImageInfo *>::const_iterator imageIt = outerIt->second.begin(); imageIt != outerIt->second.end(); imageIt++ )    
+//       {
+//         const ImageInfo & (info) = *(*imageIt);
+//         
+//         std::string file = info.img();
+//         
+//         std::stringstream dest;
+//         std::vector< std::string > list2;
+//         StringTools::split ( file, '/', list2 );
+//         dest << uncertdir << "/" << list2.back();      
+//         
+//         FloatImage noveltyImage;
+//         noveltyImage.readRaw(dest.str() + "_run_" +  NICE::intToString(iterationCount) + "_" + noveltyMethodString+".rawfloat");
+//         
+//         int xsize ( noveltyImage.width() );
+//         int ysize ( noveltyImage.height() );
+//         
+//         //compute the GT-image to ensure that we only query "useful" new features, i.e., not query background or similar "forbidden" stuff
+//         NICE::Image lm_gt;
+//         if ( (*maxValInfoIt)->hasLocalizationInfo() )
+//         {
+//           const LocalizationResult *l_gt = (*maxValInfoIt)->localization();
+// 
+//           lm_gt.resize( l_gt->xsize, l_gt->ysize );
+//           lm_gt.set( 0 );
+// 
+//           l_gt->calcLabeledImage( lm_gt, classNames.getBackgroundClass() );
+//         }                
+//         
+//         for ( int y = 0; y < ysize; y += testWSize )
+//         {
+//           for ( int x = 0; x < xsize; x += testWSize)
+//           {
+//             if ( (noveltyImage ( x, y ) > maxVal) && (  forbidden_classesForActiveLearning.find ( lm_gt(x, y) ) == forbidden_classesForActiveLearning.end() ) )
+//             {
+//               maxVal =  noveltyImage ( x, y );
+//               maxValX = x;
+//               maxValY = y;
+//               maxValInfoIt = imageIt;
+//             }
+//           }
+//         }
+//         
+//       }//iterate over inner loop
+//     }//iterate over testFiles
+// 
+//     
+//       std::cerr << "maxVal: " << maxVal << " maxValX: " << maxValX << " maxValY: " << maxValY << " maxValInfo: " << (*maxValInfoIt)->img() << std::endl;
+    
+    //**********************************************
+    //          INCLUDE THE NEW INFORMATION
+    //           AND UPDATE THE CLASSIFIER
+    //**********************************************    
+      
+     timer.start();
+     semseg->addNovelExamples(); 
+     
+     timer.stop();
+     std::cout << "AL time for incremental update: " << timer.getLastAbsolute() << std::endl;
+     //alternatively, we could call the destructor of semseg, and create it again, which does the same thing 
+     // (add new features, save the classifier, re-read it after initialization)
+     //BUT this would not setup the forbidden and known classes properly!!! We should fix that!
+     
+     const Examples * novelExamples = semseg->getNovelExamples(); 
+//      std::cerr << " ==================================== " << std::endl;
+//      std::cerr << "new examples to be added: " << std::endl;
+//      for ( uint i = 0 ; i < novelExamples->size() ; i++ )
+//      {
+//         std::cerr << (*novelExamples)[i].first << " "; (*novelExamples)[i].second.store(std::cerr);
+//      }
+//      std::cerr << " ==================================== " << std::endl;
+     
+    //check which classes will be added using the features from the novel region
+    std::set<int> newClassNumbers;
+    newClassNumbers.clear(); //just to be sure  
+    for ( uint i = 0 ; i < novelExamples->size() ; i++ )
+    {
+      if (newClassNumbers.find( (*novelExamples)[i].first /* classNumber*/) == newClassNumbers.end() )
+      {
+        newClassNumbers.insert( (*novelExamples)[i].first );
+      }
+    }
+
+    //accept the new classes as valid information
+    for (std::set<int>::const_iterator clNoIt = newClassNumbers.begin(); clNoIt != newClassNumbers.end(); clNoIt++)
+    {
+      if ( forbidden_classes.find ( *clNoIt ) != forbidden_classes.end() )
+      {
+        forbidden_classes.erase(*clNoIt);
+      }
+    }       
+      
+    //NOTE Below comes the old version:
+    // it is not needed anymore, since we store everything within SemSegNovelty
+    //However, it is still needed if we use the NN-classifier for the feature learning approach      
+//     //  ----------------------------------------------------
+//     //  therefore, we first recompute the features for the whole image and
+//     //take the one which we desire
+//       
+//     //this is NOT efficient, but a nice and easy first step
+//       
+//     NICE::ColorImage img ( (*maxValInfoIt)->img() );
+//     
+//     MultiChannelImageT<double> feats;
+// 
+//     // extract features
+//     LFColorWeijer * featExtract = new LFColorWeijer ( &conf );
+//     featExtract->getFeats ( img, feats );
+//     int featdim = feats.channels();
+//     feats.addChannel(featdim);
+// 
+//     for (int c = 0; c < featdim; c++)
+//     {
+//       ImageT<double> tmp = feats[c];
+//       ImageT<double> tmp2 = feats[c+featdim];
+// 
+//       NICE::FilterT<double, double, double>::gradientStrength (tmp, tmp2);
+//     }
+//     featdim += featdim;
+// 
+//     // compute integral images
+//     for ( int c = 0; c < featdim; c++ )
+//     {
+//       feats.calcIntegral ( c );
+//     }    
+//     
+//     //  ----------------------------------------------------
+//     //now take the feature
+//     NICE::Vector newFeature(featdim);
+//     for ( int f = 0; f < featdim; f++ )
+//     {
+//       double val = feats.getIntegralValue ( maxValX - testWSize, maxValY - testWSize, maxValX + testWSize, maxValY + testWSize, f );
+//       newFeature[f] = val;
+//     }
+//     newFeature.normalizeL1();    
+//     
+//     NICE::Image lm_gt;
+//     // take the gt class number as well    
+//     if ( (*maxValInfoIt)->hasLocalizationInfo() )
+//     {
+//       const LocalizationResult *l_gt = (*maxValInfoIt)->localization();
+// 
+//       lm_gt.resize( l_gt->xsize, l_gt->ysize );
+//       lm_gt.set( 0 );
+// 
+//       l_gt->calcLabeledImage( lm_gt, classNames.getBackgroundClass() );
+//     }
+//     int classNoGT = lm_gt(maxValX, maxValY);
+//     std::cerr << "class number GT: " << classNoGT << std::endl;
+//     
+//     
+//     semseg->addNewExample(newFeature, classNoGT);
+//     
+//     //accept the new class as valid information
+//     if ( forbidden_classes.find ( classNoGT ) != forbidden_classes.end() )
+//     {
+//       forbidden_classes.erase(classNoGT);
+//     }    
+    
+    std::cerr << "iteration finished - start the next round" << std::endl;
+    
+  } //iterationCount
+
+  delete semseg;
+
+  return 0;
+}

+ 284 - 0
progs/testActiveSemanticSegmentationBinary.cpp

@@ -0,0 +1,284 @@
+// Beispielhafter Aufruf: BUILD_x86_64/progs/testActiveSemanticSegmentationBinary -config <CONFIGFILE>
+
+/**
+* @file testActiveSemanticSegmentationBinary.cpp
+* @brief test semantic segmentation routines with actively selecting regions for labeling
+* @author Alexander Freytag
+* @date 27-02-2013
+*/
+
+#ifdef NICE_USELIB_OPENMP
+#include <omp.h>
+#endif
+
+#include "core/basics/Config.h"
+#include "core/basics/StringTools.h"
+#include <vislearning/baselib/ICETools.h>
+
+#include <semseg/semseg/SemanticSegmentation.h>
+#include <semseg/semseg/SemSegLocal.h>
+#include <semseg/semseg/SemSegCsurka.h>
+#include <semseg/semseg/SemSegNovelty.h>
+#include <semseg/semseg/SemSegNoveltyBinary.h>
+#include <semseg/semseg/SemSegContextTree.h>
+
+#include "core/image/FilterT.h"
+
+#include <core/basics/ResourceStatistics.h>
+
+#include <fstream>
+
+using namespace OBJREC;
+
+using namespace NICE;
+
+using namespace std;
+
+/**
+ test semantic segmentation routines
+*/
+int main( int argc, char **argv )
+{
+  std::set_terminate( __gnu_cxx::__verbose_terminate_handler );
+
+  Config conf( argc, argv );
+  
+  ResourceStatistics rs;
+  
+  NICE::MatrixT<double> matTemp;
+  std::cerr << "foo " << std::endl;
+  std::cerr << matTemp << std::endl;
+  
+  bool show_result = conf.gB( "debug", "show_results", false );
+
+  bool write_results = conf.gB( "debug", "write_results", false );
+
+  bool write_results_pascal = conf.gB( "debug", "write_results_pascal", false );
+
+  std::string resultdir = conf.gS( "debug", "resultdir", "." );
+  
+  //how often do we want to iterate between sem-seg and active query?
+  int activeIterations = conf.gI("main", "activeIterations", 1 );
+    
+  if ( write_results )
+  {
+    cerr << "Writing Results to " << resultdir << endl;
+  }
+
+  MultiDataset md( &conf );
+
+  const ClassNames & classNames = md.getClassNames( "train" );
+
+  string method = conf.gS( "main", "method", "SSCsurka" );
+
+  //currently, we only allow SemSegNoveltyBinary, because it implements addNovelExamples()
+  SemSegNoveltyBinary *semseg = NULL;
+  
+  Timer timer;
+  timer.start();
+
+  semseg = new SemSegNoveltyBinary( &conf, &md );
+
+  timer.stop();
+  
+  std::cerr << "AL time for training: " << timer.getLast() << std::endl;
+
+  const LabeledSet *testFiles = md["test"];
+
+  std::set<int> forbidden_classes;
+  std::string forbidden_classes_s = conf.gS( "analysis", "forbidden_classesTrain", "" );
+  classNames.getSelection( forbidden_classes_s, forbidden_classes );
+  
+  std::set<int> forbidden_classesForActiveLearning;
+  std::string forbidden_classesForActiveLearning_s = conf.gS( "analysis", "forbidden_classesForActiveLearning", "" );
+  classNames.getSelection( forbidden_classesForActiveLearning_s, forbidden_classesForActiveLearning );
+  
+  
+  int positiveClass;
+  
+  //check whether we have a single positive class
+  std::string positiveClass_s = conf.gS ( "SemSegNoveltyBinary", "positiveClass", "" );
+  std::set<int> positiveClassNumberTmp;
+  classNames.getSelection ( positiveClass_s, positiveClassNumberTmp );  
+
+  switch ( positiveClassNumberTmp.size() )
+  {
+    case 0:
+    {
+      positiveClass = 0;
+//       std::cerr << "no positive class given, assume 0 as positive class" << std::endl;
+      break;
+    }
+    case 1:
+    {
+      positiveClass = *(positiveClassNumberTmp.begin());
+//       std::cerr << "positive class will be number" << positiveClass << " with the name: " << positiveClass_s << std::endl;
+      break;
+    }
+    default:
+    {
+      //we specified more than a single positive class. right now, this is not what we are interested in, but 
+      //in theory we could also accept this and convert positiveClass into a set of ints of possible positive classes
+      positiveClass = 0;
+//       std::cerr << "no positive class given, assume 0 as positive class" << std::endl;
+      break;
+    }
+  }  
+ 
+  
+  std::cerr << "number of AL iterations: " << activeIterations << std::endl;
+  for (int iterationCount = 0; iterationCount < activeIterations; iterationCount++)
+  {    
+    std::cerr << "SemSeg AL Iteration: " << iterationCount << std::endl;
+    semseg->setIterationCountSuffix(iterationCount);
+
+    int fileno = 0;
+
+    std::cerr << "start looping over all files" << std::endl;
+    LOOP_ALL_S( *testFiles )
+    {
+      EACH_INFO( classno, info );
+      std::string file = info.img();
+
+      NICE::Image lm;
+      NICE::MultiChannelImageT<double> probabilities;
+
+      if ( info.hasLocalizationInfo() )
+      {
+        const LocalizationResult *l_gt = info.localization();
+
+        lm.resize( l_gt->xsize, l_gt->ysize );
+        //lm.set( 0 );
+        l_gt->calcLabeledImage( lm, classNames.getBackgroundClass() );
+      }
+
+      ((SemanticSegmentation*)semseg)->semanticseg( file, lm, probabilities );
+
+      fprintf( stderr, "testSemanticSegmentation: Segmentation finished !\n" );
+
+      //ground truth image, needed for updating the confusion matrix
+      //TODO check whether this is really needed, since we computed such a label image already within SemSegNovelty
+      NICE::Image lm_gt;
+
+      if ( info.hasLocalizationInfo() )
+      {
+        const LocalizationResult *l_gt = info.localization();
+
+        lm_gt.resize( l_gt->xsize, l_gt->ysize );
+        lm_gt.set( 0 );
+
+        fprintf( stderr, "testSemanticSegmentation: Generating Labeled NICE::Image (Ground-Truth)\n" );
+        l_gt->calcLabeledImage( lm_gt, classNames.getBackgroundClass() );
+      }
+
+      if ( show_result || write_results )
+      {
+        NICE::ColorImage orig( file );
+        NICE::ColorImage rgb;
+        NICE::ColorImage rgb_gt;
+
+        classNames.labelToRGB( lm, rgb );
+
+        classNames.labelToRGB( lm_gt, rgb_gt );
+
+        if ( write_results )
+        {
+          
+          std::stringstream out;       
+          std::vector< std::string > myList;
+          StringTools::split ( Globals::getCurrentImgFN (), '/', myList );
+          out << resultdir << "/" << myList.back();
+          cerr << "Writing to file " << resultdir << "/"<< myList.back() << endl;
+          
+          std::string noveltyMethodString = conf.gS( "SemSegNoveltyBinary",  "noveltyMethod", "gp-variance");
+          orig.write ( out.str() + "_orig.ppm" );
+          rgb.write ( out.str() + "_" + noveltyMethodString + "_result_run_" + NICE::intToString(iterationCount) + ".ppm" );
+          rgb_gt.write ( out.str() + "_groundtruth.ppm" );
+        }
+
+        if ( show_result )
+        {
+  #ifndef NOVISUAL
+          showImage( rgb, "Result" );
+          showImage( rgb_gt, "Groundtruth" );
+          showImage( orig, "Input" );
+  #endif
+        }
+      }
+
+
+      fileno++;
+
+    } //Loop over all test images
+
+
+    //**********************************************
+    //                  EVALUATION 
+    //   COMPUTE CONFUSION MAT AND FINAL SCORES
+    //**********************************************
+    timer.start();
+    
+    double score = semseg->getAUCPerformance();
+    std::cerr << "auc scores of run : " << iterationCount << " : " << score << std::endl;
+    
+    long maxMemory;
+    rs.getMaximumMemory(maxMemory);
+    cerr << "Maximum memory used: " << maxMemory << " KB" << endl;
+
+    
+    timer.stop();
+    std::cout << "AL time for evaluation: " << timer.getLastAbsolute() << std::endl;
+    
+
+    
+    //**********************************************
+    //          INCLUDE THE NEW INFORMATION
+    //           AND UPDATE THE CLASSIFIER
+    //**********************************************    
+      
+     timer.start();
+     semseg->addNovelExamples(); 
+     
+     timer.stop();
+     std::cout << "AL time for incremental update: " << timer.getLastAbsolute() << std::endl;
+     //alternatively, we could call the destructor of semseg, and create it again, which does the same thing 
+     // (add new features, save the classifier, re-read it after initialization)
+     //BUT this would not setup the forbidden and known classes properly!!! We should fix that!
+     
+     const Examples * novelExamples = semseg->getNovelExamples(); 
+//      std::cerr << " ==================================== " << std::endl;
+//      std::cerr << "new examples to be added: " << std::endl;
+//      for ( uint i = 0 ; i < novelExamples->size() ; i++ )
+//      {
+//         std::cerr << (*novelExamples)[i].first << " "; (*novelExamples)[i].second.store(std::cerr);
+//      }
+//      std::cerr << " ==================================== " << std::endl;
+     
+    //check which classes will be added using the features from the novel region
+    std::set<int> newClassNumbers;
+    newClassNumbers.clear(); //just to be sure  
+    for ( uint i = 0 ; i < novelExamples->size() ; i++ )
+    {
+      if (newClassNumbers.find( (*novelExamples)[i].first /* classNumber*/) == newClassNumbers.end() )
+      {
+        newClassNumbers.insert( (*novelExamples)[i].first );
+      }
+    }
+
+    //accept the new classes as valid information
+    for (std::set<int>::const_iterator clNoIt = newClassNumbers.begin(); clNoIt != newClassNumbers.end(); clNoIt++)
+    {
+      if ( forbidden_classes.find ( *clNoIt ) != forbidden_classes.end() )
+      {
+        forbidden_classes.erase(*clNoIt);
+      }
+    }       
+
+    std::cerr << "iteration finished - start the next round" << std::endl;
+    
+  } //iterationCount
+
+  delete semseg;
+
+  return 0;
+}

+ 8 - 8
progs/testSemanticSegmentation.cpp

@@ -202,14 +202,14 @@ int main( int argc, char **argv )
 
       if ( write_results )
       {
-        //char filename[1024];
-        //char *format = ( char * )"ppm";
-        //sprintf( filename, "%06d.%s", fileno, format );
-        std::string origfilename = resultdir + "/"+fname+".jpg";
-        cerr << "Writing to file " << origfilename << endl;
-        orig.write( origfilename );
-        rgb.write( resultdir + "/"+fname+"_result.png" );
-        rgb_gt.write( resultdir + "/"+fname+"_gt.png" );
+        std::stringstream out;       
+        std::vector< std::string > myList;
+        StringTools::split ( Globals::getCurrentImgFN (), '/', myList );
+        out << resultdir << "/" << myList.back();
+        cerr << "Writing to file " << resultdir << "/"<< myList.back() << endl;
+        orig.write ( out.str() + "_orig.jpg" );
+        rgb.write ( out.str() + "_result.png" );
+        rgb_gt.write ( out.str() + "_groundtruth.png" );
       }
 
       if ( show_result )

+ 1092 - 333
semseg/SemSegNovelty.cpp

@@ -3,13 +3,16 @@
 
 #include "SemSegNovelty.h"
 
-#include "core/image/FilterT.h"
-#include "gp-hik-exp/GPHIKClassifierNICE.h"
-#include "vislearning/baselib/ICETools.h"
-#include "vislearning/baselib/Globals.h"
-#include "vislearning/features/fpfeatures/SparseVectorFeature.h"
-#include "core/basics/StringTools.h"
-#include "core/basics/Timer.h"
+#include <core/image/FilterT.h>
+#include <core/basics/numerictools.h>
+#include <core/basics/StringTools.h>
+#include <core/basics/Timer.h>
+
+#include <gp-hik-exp/GPHIKClassifierNICE.h>
+#include <vislearning/baselib/ICETools.h>
+#include <vislearning/baselib/Globals.h>
+#include <vislearning/features/fpfeatures/SparseVectorFeature.h>
+
 #include "segmentation/GenericRegionSegmentationMethodSelection.h"
 
 using namespace std;
@@ -28,17 +31,39 @@ SemSegNovelty::SemSegNovelty ( const Config *conf,
 
   featExtract = new LFColorWeijer ( conf );
 
-  save_cache = conf->gB ( "FPCPixel", "save_cache", true );
-  read_cache = conf->gB ( "FPCPixel", "read_cache", false );
-  uncertdir = conf->gS("debug", "uncertainty", "uncertainty");
+  this->reuseSegmentation = conf->gB ( "FPCPixel", "reuseSegmentation", true ); //save and read segmentation results from files
+  this->save_classifier = conf->gB ( "FPCPixel", "save_classifier", true ); //save the classifier to a file
+  this->read_classifier = conf->gB ( "FPCPixel", "read_classifier", false ); //read the classifier from a file
+
+  //write uncertainty results in the same folder as done for the segmentation results
+  resultdir = conf->gS("debug", "resultdir", "result");
   cache = conf->gS ( "cache", "root", "" );
   
-  classifier = new GPHIKClassifierNICE ( conf, "ClassiferGPHIK" );;
+  
+  //stupid work around of the const attribute
+  Config confCopy = *conf;
+  
+  //just to make sure, that we do NOT perform an optimization after every iteration step
+  //this would just take a lot of time, which is not desired so far
+  confCopy.sB("ClassifierGPHIK","performOptimizationAfterIncrement",false);
+  
+  classifierString = conf->gS ( section, "classifier", "ClassifierGPHIK" );  
+  classifier = NULL;
+  vclassifier = NULL;
+  if ( classifierString.compare("ClassifierGPHIK") == 0)
+    classifier = new GPHIKClassifierNICE ( &confCopy, "ClassifierGPHIK" );
+  else
+    vclassifier = GenericClassifierSelection::selectVecClassifier ( conf, classifierString );
+  
+
 
   findMaximumUncert = conf->gB(section, "findMaximumUncert", true);
   whs = conf->gI ( section, "window_size", 10 );
-  featdist = conf->gI ( section, "grid", 10 );
+  //distance to next descriptor during training
+  trainWsize = conf->gI ( section, "train_window_size", 10 );
+  //distance to next descriptor during testing
   testWSize = conf->gI (section, "test_window_size", 10);
+  // select your segmentation method here
   string rsMethode = conf->gS ( section, "segmentation", "none" );
  
   if(rsMethode == "none")
@@ -48,7 +73,7 @@ SemSegNovelty::SemSegNovelty ( const Config *conf,
   else
   {
     RegionSegmentationMethod *tmpRegionSeg = GenericRegionSegmentationMethodSelection::selectRegionSegmentationMethod(conf, rsMethode);    
-    if ( save_cache )
+    if ( reuseSegmentation )
       regionSeg = new RSCache ( conf, tmpRegionSeg );
     else
       regionSeg = tmpRegionSeg;
@@ -56,17 +81,23 @@ SemSegNovelty::SemSegNovelty ( const Config *conf,
   
   cn = md->getClassNames ( "train" );
 
-  if ( read_cache )
+  if ( read_classifier )
   {
-    string classifierdst = "/classifier.data";
-    fprintf ( stderr, "SemSegNovelty:: Reading classifier data from %s\n", ( cache + classifierdst ).c_str() );
-
     try
     {
       if ( classifier != NULL )
       {
+        string classifierdst = "/classifier.data";        
+        fprintf ( stderr, "SemSegNovelty:: Reading classifier data from %s\n", ( cache + classifierdst ).c_str() );        
         classifier->read ( cache + classifierdst );
       }
+      else
+      {
+        string classifierdst = "/veccl.data";        
+        fprintf ( stderr, "SemSegNovelty:: Reading classifier data from %s\n", ( cache + classifierdst ).c_str() );          
+        vclassifier->read ( cache + classifierdst );      
+      }
+      
 
       fprintf ( stderr, "SemSegNovelty:: successfully read\n" );
     }
@@ -79,28 +110,90 @@ SemSegNovelty::SemSegNovelty ( const Config *conf,
   {
     train ( md );
   }
+  
+  //define which measure for "novelty" we want to use
+  noveltyMethodString = conf->gS( section,  "noveltyMethod", "gp-variance");
+  if (noveltyMethodString.compare("gp-variance") == 0)  // novel = large variance
+  {
+    this->noveltyMethod = GPVARIANCE;
+    this->mostNoveltyWithMaxScores = true;
+  }
+  else if (noveltyMethodString.compare("gp-uncertainty") == 0) //novel = large uncertainty (mean / var)
+  {
+    this->noveltyMethod = GPUNCERTAINTY;
+    this->mostNoveltyWithMaxScores = false;
+    globalMaxUncert = numeric_limits<double>::max();
+  } 
+  else if (noveltyMethodString.compare("gp-mean") == 0) //novel = small mean
+  {
+    this->noveltyMethod = GPMINMEAN;
+    this->mostNoveltyWithMaxScores = false;
+    globalMaxUncert = numeric_limits<double>::max();
+  }
+  else if (noveltyMethodString.compare("gp-meanRatio") == 0)  //novel = small difference between mean of most plausible class and mean of snd
+                                                              //        most plausible class (not useful in binary settings)
+  {
+    this->noveltyMethod = GPMEANRATIO;
+    this->mostNoveltyWithMaxScores = false;
+    globalMaxUncert = numeric_limits<double>::max();
+  }
+  else if (noveltyMethodString.compare("gp-weightAll") == 0) // novel = large weight in alpha vector after updating the model (can be predicted exactly)
+  {
+    this->noveltyMethod = GPWEIGHTALL;
+    this->mostNoveltyWithMaxScores = true;
+  }
+  else if (noveltyMethodString.compare("gp-weightRatio") == 0) // novel = small difference between weights for alpha vectors 
+                                                               //     with assumptions of GT label to be the most 
+                                                               //     plausible against the second most plausible class   
+  {
+    this->noveltyMethod = GPWEIGHTRATIO;
+    this->mostNoveltyWithMaxScores = false;
+    globalMaxUncert = numeric_limits<double>::max();
+  }
+  else if (noveltyMethodString.compare("random") == 0) 
+  {
+     initRand(); 
+     this->noveltyMethod = RANDOM;
+  }
+  else
+  {
+    this->noveltyMethod = GPVARIANCE;
+    this->mostNoveltyWithMaxScores = true;
+  }
+  
+  //we don't have queried any region so far
+  queriedRegions.clear();
+  visualizeALimages = conf->gB(section, "visualizeALimages", false);
 }
 
 SemSegNovelty::~SemSegNovelty()
 {
   if(newTrainExamples.size() > 0)
   {
-    // most uncertain region
-    showImage(maskedImg);
-    //classifier->add(newTrainExamples)
+    // show most uncertain region
+    if (visualizeALimages)
+      showImage(maskedImg);
+    
+    //incorporate new information into the classifier
+    if (classifier != NULL)
+      classifier->addMultipleExamples(newTrainExamples);
+    
+    //store the classifier, such that we can read it again in the next round (if we like that)
     classifier->save ( cache + "/classifier.data" );
   }
   
   // clean-up
   if ( classifier != NULL )
     delete classifier;
+  if ( vclassifier != NULL )
+    delete vclassifier;
   if ( featExtract != NULL )
     delete featExtract;
 }
 
 void SemSegNovelty::visualizeRegion(const NICE::ColorImage &img, const NICE::Matrix &regions, int region, NICE::ColorImage &outimage)
 {
-  vector<uchar> color;
+  std::vector<uchar> color;
   color.push_back(255);
   color.push_back(0);
   color.push_back(0);
@@ -140,14 +233,7 @@ void SemSegNovelty::train ( const MultiDataset *md )
   ////////////////////////
   // feature extraction //
   ////////////////////////
-
-  std::string forbidden_classes_s = conf->gS ( "analysis", "donttrain", "" );
-  if ( forbidden_classes_s == "" )
-  {
-    forbidden_classes_s = conf->gS ( "analysis", "forbidden_classes", "" );
-  }
-  cn.getSelection ( forbidden_classes_s, forbidden_classes );
-  
+ 
   //check the same thing for the training classes - this is very specific to our setup 
   std::string forbidden_classesTrain_s = conf->gS ( "analysis", "donttrainTrain", "" );
   if ( forbidden_classesTrain_s == "" )
@@ -155,7 +241,7 @@ void SemSegNovelty::train ( const MultiDataset *md )
     forbidden_classesTrain_s = conf->gS ( "analysis", "forbidden_classesTrain", "" );
   }
   cn.getSelection ( forbidden_classesTrain_s, forbidden_classesTrain );
-
+  
 
   ProgressBar pb ( "Local Feature Extraction" );
   pb.show();
@@ -225,9 +311,9 @@ void SemSegNovelty::train ( const MultiDataset *md )
       feats.calcIntegral ( c );
     }
 
-    for ( int y = 0; y < ysize; y += featdist )
+    for ( int y = 0; y < ysize; y += trainWsize)
     {
-      for ( int x = 0; x < xsize; x += featdist )
+      for ( int x = 0; x < xsize; x += trainWsize )
       {
 
         int classnoTmp = labels.getPixel ( x, y );
@@ -256,8 +342,12 @@ void SemSegNovelty::train ( const MultiDataset *md )
 
         example.position = imgnb;
         examples.push_back ( pair<int, Example> ( classnoTmp, example ) );
+
       }
     }
+ 
+    
+    
 
     delete ce;
     imgnb++;
@@ -288,19 +378,39 @@ void SemSegNovelty::train ( const MultiDataset *md )
   delete f;
 
   if ( classifier != NULL )
+  {
+    std::cerr << "train FP-classifier with " << examples.size() << " examples" << std::endl;
     classifier->train ( fp, examples );
+    std::cerr << "training finished" << std::endl;
+  }
   else
   {
-    cerr << "no classifier selected?!" << endl;
-    exit ( -1 );
-  }
+    LabeledSetVector lvec;
+    convertExamplesToLSet ( examples, lvec );
+    vclassifier->teach ( lvec );
+//     if ( usegmm )
+//       convertLSetToSparseExamples ( examples, lvec );
+//     else
+    std::cerr << "classifierString: " << classifierString << std::endl;
+    if (this->classifierString.compare("nn") == 0)
+    {
+      convertLSetToExamples ( examples, lvec, true /* only remove pointers to the data in the LSet-struct*/);
+    }
+    else
+    {
+      convertLSetToExamples ( examples, lvec, false /* remove all training examples of the LSet-struct */);
+    }
+    vclassifier->finishTeaching();
+  }  
 
   fp.destroy();
 
-  if ( save_cache )
+  if ( save_classifier )
   {
     if ( classifier != NULL )
       classifier->save ( cache + "/classifier.data" );
+    else
+      vclassifier->save ( cache + "/veccl.data" );    
   }
 
   ////////////
@@ -315,12 +425,16 @@ void SemSegNovelty::train ( const MultiDataset *md )
   cerr << "SemSeg training finished" << endl;
 }
 
+
 void SemSegNovelty::semanticseg ( CachedExample *ce, NICE::Image & segresult, NICE::MultiChannelImageT<double> & probabilities )
-{
+{  
   Timer timer;
   timer.start();
   
+  //segResult contains the GT labels when this method is called
+  // we simply store them in labels, to have an easy access to the GT information lateron
   Image labels = segresult;
+  //just to be sure that we do not have a GT-biased result :)
   segresult.set(0);
 
   int featdim = -1;
@@ -363,237 +477,83 @@ void SemSegNovelty::semanticseg ( CachedExample *ce, NICE::Image & segresult, NI
   {
     feats.calcIntegral ( c );
   }
-
-  FloatImage uncert ( xsize, ysize );
-  uncert.set ( 0.0 );
-  
-  FloatImage gpUncertainty ( xsize, ysize );
-  FloatImage gpMean ( xsize, ysize );    
-  FloatImage gpMeanRatio ( xsize, ysize );  
-  FloatImage gpWeightAll ( xsize, ysize );
-  FloatImage gpWeightRatio ( xsize, ysize );  
-  
-  gpUncertainty.set ( 0.0 );
-  gpMean.set ( 0.0 );
-  gpMeanRatio.set ( 0.0 );
-  gpWeightAll.set ( 0.0 );
-  gpWeightRatio.set ( 0.0 );
-
-  double maxunc = -numeric_limits<double>::max();
   
-  double maxGPUncertainty = -numeric_limits<double>::max();  
-  double maxGPMean = -numeric_limits<double>::max();  
-  double maxGPMeanRatio = -numeric_limits<double>::max();  
-  double maxGPWeightAll = -numeric_limits<double>::max();  
-  double maxGPWeightRatio = -numeric_limits<double>::max();  
-
   timer.stop();
-  cout << "first: " << timer.getLastAbsolute() << endl;
+  std::cout << "AL time for preparation: " << timer.getLastAbsolute() << std::endl;
+    
+  timer.start();
+  //classification results currently only needed to be computed separately if we use the vclassifier, i.e., the nearest neighbor used 
+  // for the "novel feature learning" approach
+  //in all other settings, such as active sem seg in general, we do this within the novelty-computation-methods
+  if ( classifier == NULL )
+  {
+    this->computeClassificationResults( feats, segresult, probabilities, xsize, ysize, featdim);
+  }
+//   timer.stop();
+//   
+//   std::cerr << "classification results computed" << std::endl;
   
-  //we need this lateron for active learning stuff
-  double gpNoise =  conf->gD("GPHIK", "noise", 0.01);
+  FloatImage noveltyImage ( xsize, ysize );
+  noveltyImage.set ( 0.0 );  
   
-  timer.start();
-#pragma omp parallel for
-  for ( int y = 0; y < ysize; y += testWSize )
+  switch (noveltyMethod)
   {
-    Example example;
-    example.vec = NULL;
-    example.svec = new SparseVector ( featdim );
-    for ( int x = 0; x < xsize; x += testWSize)
+    case GPVARIANCE:
     {
-      for ( int f = 0; f < featdim; f++ )
-      {
-        double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
-        if ( val > 1e-10 )
-          ( *example.svec ) [f] = val;
-      }
-      example.svec->normalize();
-
-      ClassificationResult cr = classifier->classify ( example );
-      
-      //we need this if we want to compute GP-AL-measure lateron
-      double minMeanAbs ( numeric_limits<double>::max() );
-      double maxMeanAbs ( 0.0 );
-      double sndMaxMeanAbs ( 0.0 );       
-      double maxMean ( -numeric_limits<double>::max() );
-      double sndMaxMean ( -numeric_limits<double>::max() );     
-      
-      for ( int j = 0 ; j < cr.scores.size(); j++ )
-      {
-        if ( forbidden_classesTrain.find ( j ) != forbidden_classesTrain.end() )
-        {
-          continue;
-        }
-        
-        //check whether we found a class with higher smaller abs mean than the current minimum
-        if (abs(cr.scores[j]) < minMeanAbs)  
-          minMeanAbs = abs(cr.scores[j]);
-        //check for larger abs mean as well
-        if (abs(cr.scores[j]) > maxMeanAbs)
-        {
-          sndMaxMeanAbs = maxMeanAbs;
-          maxMeanAbs = abs(cr.scores[j]);
-        }
-        // and also for the second highest mean of all classes
-        else if (abs(cr.scores[j]) > sndMaxMeanAbs)
-        {
-          sndMaxMeanAbs = abs(cr.scores[j]);
-        }  
-        //check for larger mean without abs as well
-        if (cr.scores[j] > maxMean)
-        {
-          sndMaxMean = maxMean;
-          maxMean = cr.scores[j];
-        }
-        // and also for the second highest mean of all classes
-        else if (cr.scores[j] > sndMaxMean)
-        {
-          sndMaxMean = cr.scores[j];
-        }          
-      }
-
-      double firstTerm (1.0 / sqrt(cr.uncertainty+gpNoise));
-      
-      //compute the heuristic GP-UNCERTAINTY, as proposed by Kapoor et al. in IJCV 2010
-      // GP-UNCERTAINTY : |mean| / sqrt(var^2 + gpnoise^2)
-      double gpUncertaintyVal = maxMeanAbs*firstTerm; //firstTerm = 1.0 / sqrt(r.uncertainty+gpNoise))
-      
-      // compute results when we take the lowest mean value of all classes
-      double gpMeanVal = minMeanAbs;
-      
-      //look at the difference in the absolut mean values for the most plausible class
-      // and the second most plausible class
-      double gpMeanRatioVal= maxMean - sndMaxMean;
-      
-       double gpWeightAllVal ( 0.0 );
-       double gpWeightRatioVal ( 0.0 );
-
-       if ( numberOfClasses > 2)
-       {
-        //compute the weight in the alpha-vector for every sample after assuming it to be 
-        // added to the training set.
-        // Thereby, we measure its "importance" for the current model
-        // 
-        //double firstTerm is already computed
-        //
-        //the second term is only needed when computing impacts
-        //double secondTerm; //this is the nasty guy :/
-        
-        //--- compute the third term
-        // this is the difference between predicted label and GT label 
-        std::vector<double> diffToPositive; diffToPositive.clear();
-        std::vector<double> diffToNegative; diffToNegative.clear();
-        double diffToNegativeSum(0.0);
-        
-        for ( int j = 0 ; j < cr.scores.size(); j++ )
-        {
-          if ( forbidden_classesTrain.find ( j ) != forbidden_classesTrain.end() )
-          {
-            continue;
-          }          
-          
-          // look at the difference to plus 1          
-          diffToPositive.push_back(abs(cr.scores[j] - 1));
-          // look at the difference to -1          
-          diffToNegative.push_back(abs(cr.scores[j] + 1));
-          //sum up the difference to -1
-          diffToNegativeSum += abs(cr.scores[j] - 1);
-        }
-
-        //let's subtract for every class its diffToNegative from the sum, add its diffToPositive,
-        //and use this as the third term for this specific class.
-        //the final value is obtained by minimizing over all classes
-        //
-        // originally, we minimize over all classes after building the final score
-        // however, the first and the second term do not depend on the choice of
-        // y*, therefore we minimize here already
-        double thirdTerm (numeric_limits<double>::max()) ;
-        for(uint tmpCnt = 0; tmpCnt < diffToPositive.size(); tmpCnt++)
-        {
-          double tmpVal ( diffToPositive[tmpCnt] + (diffToNegativeSum-diffToNegative[tmpCnt])   );
-          if (tmpVal < thirdTerm)
-            thirdTerm = tmpVal;
-        }
-        gpWeightAllVal = thirdTerm*firstTerm;        
-        
-        //now look on the ratio of the resulting weights for the most plausible
-        // against the second most plausible class
-        double thirdTermMostPlausible ( 0.0 ) ;
-        double thirdTermSecondMostPlausible ( 0.0 ) ;
-        for(uint tmpCnt = 0; tmpCnt < diffToPositive.size(); tmpCnt++)
-        {
-          if (diffToPositive[tmpCnt] > thirdTermMostPlausible)
-          {
-            thirdTermSecondMostPlausible = thirdTermMostPlausible;
-            thirdTermMostPlausible = diffToPositive[tmpCnt];
-          }
-          else if (diffToPositive[tmpCnt] > thirdTermSecondMostPlausible)
-          {
-            thirdTermSecondMostPlausible = diffToPositive[tmpCnt];
-          }
-        }
-        //compute the resulting score
-        gpWeightRatioVal = (thirdTermMostPlausible - thirdTermSecondMostPlausible)*firstTerm;      
-
-        //finally, look for this feature how it would affect to whole model (summarized by weight-vector alpha), if we would 
-        //use it as an additional training example
-        //TODO this would be REALLY computational demanding. Do we really want to do this?
-  //         gpImpactAll[s] ( pce[i].second.x, pce[i].second.y ) = thirdTerm*firstTerm*secondTerm;
-  //         gpImpactRatio[s] ( pce[i].second.x, pce[i].second.y ) = (thirdTermMostPlausible - thirdTermSecondMostPlausible)*firstTerm*secondTerm;
-       }
-       else //binary scenario
-       {
-         gpWeightAllVal = std::min( abs(cr.scores[*classesInUse.begin()]+1), abs(cr.scores[*classesInUse.begin()]-1) );
-         gpWeightAllVal *= firstTerm;
-         gpWeightRatioVal = gpWeightAllVal;
-       }
-
-      int xs = std::max(0, x - testWSize/2);
-      int xe = std::min(xsize - 1, x + testWSize/2);
-      int ys = std::max(0, y - testWSize/2);
-      int ye = std::min(ysize - 1, y + testWSize/2);
-      for (int yl = ys; yl <= ye; yl++)
-      {
-        for (int xl = xs; xl <= xe; xl++)
-        {
-          for ( int j = 0 ; j < cr.scores.size(); j++ )
-          {
-            probabilities ( xl, yl, j ) = cr.scores[j];
-          }
-          segresult ( xl, yl ) = cr.classno;
-          uncert ( xl, yl ) = cr.uncertainty;
-          
-          gpUncertainty ( xl, yl ) = gpUncertaintyVal;
-          gpMean ( xl, yl ) = gpMeanVal;
-          gpMeanRatio ( xl, yl ) = gpMeanRatioVal;
-          gpWeightAll ( xl, yl ) = gpWeightAllVal;
-          gpWeightRatio ( xl, yl ) = gpWeightRatioVal;    
-        }
-      }
-
-      if (maxunc < cr.uncertainty)
-        maxunc = cr.uncertainty;
-      
-      if (maxGPUncertainty < gpUncertaintyVal)
-        maxGPUncertainty = gpUncertaintyVal;
-      if (maxGPMean < gpMeanVal)
-        maxGPMean = gpMeanVal;
-      if (maxGPMeanRatio < gpMeanRatioVal)
-        maxGPMeanRatio = gpMeanRatioVal;
-      if (maxGPWeightAll < gpMeanRatioVal)
-        maxGPWeightAll = gpWeightAllVal;
-      if (maxGPWeightRatio < gpWeightRatioVal)
-        maxGPWeightRatio = gpWeightRatioVal;      
-      
-      example.svec->clear();
+         this->computeNoveltyByVariance( noveltyImage, feats, segresult, probabilities, xsize, ysize,  featdim );
+         break;
     }
-    delete example.svec;
-    example.svec = NULL;
+    case GPUNCERTAINTY:
+    {
+         this->computeNoveltyByGPUncertainty( noveltyImage, feats, segresult, probabilities, xsize, ysize,  featdim );
+         break;         
+    }
+    case GPMINMEAN:
+    {
+         std::cerr << "compute novelty using the minimum mean" << std::endl;
+         this->computeNoveltyByGPMean( noveltyImage, feats, segresult, probabilities, xsize, ysize,  featdim );
+         break;         
+    }
+    case GPMEANRATIO:
+    {
+         this->computeNoveltyByGPMeanRatio( noveltyImage, feats, segresult, probabilities, xsize, ysize,  featdim );
+         break;         
+    }
+    case GPWEIGHTALL:
+    {
+         this->computeNoveltyByGPWeightAll( noveltyImage, feats, segresult, probabilities, xsize, ysize,  featdim );
+         break;         
+    }
+    case GPWEIGHTRATIO:
+    {
+         this->computeNoveltyByGPWeightRatio( noveltyImage, feats, segresult, probabilities, xsize, ysize,  featdim );
+         break;         
+    }    
+    case RANDOM:
+    {
+         this->computeNoveltyByRandom( noveltyImage, feats, segresult, probabilities, xsize, ysize,  featdim );
+         break;               
+    }
+    default:
+    {
+         //do nothing, keep the image constant to 0.0
+         break;
+    }
+         
+  }
+  
+  timer.stop();
+  std::cout << "AL time for novelty score computation: " << timer.getLastAbsolute() << std::endl;
+  
+  if (visualizeALimages)
+  {
+      ColorImage imgrgbTmp (xsize, ysize);
+      ICETools::convertToRGB ( noveltyImage, imgrgbTmp );
+      showImage(imgrgbTmp, "Novelty Image without Region Segmentation");  
   }
-
-  //       std::cerr << "uncertainty: " << gpUncertaintyVal << " minMean: " << gpMeanVal << " gpMeanRatio: " << gpMeanRatioVal << " weightAll: " << gpWeightAllVal << " weightRatio: "<< gpWeightRatioVal << std::endl;
   
+    
+  timer.start();
   
   //Regionen ermitteln
   if(regionSeg != NULL)
@@ -602,12 +562,14 @@ void SemSegNovelty::semanticseg ( CachedExample *ce, NICE::Image & segresult, NI
     int amountRegions = regionSeg->segRegions ( img, mask );
     
     //compute probs per region
-    vector<vector<double> > regionProb(amountRegions,vector<double>(probabilities.channels(),0.0));
-    vector<double> regionNoveltyMeasure (amountRegions, 0.0);
-    vector<int> regionCounter(amountRegions, 0);
-    for ( int y = 0; y < ysize; y++)
+    std::vector<std::vector<double> > regionProb(amountRegions, std::vector<double>(probabilities.channels(),0.0));
+    std::vector<double> regionNoveltyMeasure (amountRegions, 0.0);
+
+    std::vector<int> regionCounter(amountRegions, 0);
+    std::vector<int> regionCounterNovelty(amountRegions, 0);
+    for ( int y = 0; y < ysize; y += trainWsize) //y++)
     {
-      for (int x = 0; x < xsize; x++)
+      for (int x = 0; x < xsize; x += trainWsize) //x++)
       {
         int r = mask(x,y);
         regionCounter[r]++;
@@ -615,52 +577,99 @@ void SemSegNovelty::semanticseg ( CachedExample *ce, NICE::Image & segresult, NI
         {
           regionProb[r][j] += probabilities ( x, y, j );
         }
-        regionNoveltyMeasure[r] += uncert(x,y);
+        
+        if ( forbidden_classesActiveLearning.find( labels(x,y) ) == forbidden_classesActiveLearning.end() )
+        {
+          //count the amount of "novelty" for the corresponding region
+          regionNoveltyMeasure[r] += noveltyImage(x,y);
+          regionCounterNovelty[r]++;
+        }
       }
     }
        
     //find best class per region
-    vector<int> bestClassPerRegion(amountRegions,0);
+    std::vector<int> bestClassPerRegion(amountRegions,0);
+    
+    double maxNoveltyScore = -numeric_limits<double>::max();
+    if (!mostNoveltyWithMaxScores)
+    {
+      maxNoveltyScore = numeric_limits<double>::max();
+    }   
     
-    double maxuncert = -numeric_limits<double>::max();
     int maxUncertRegion = -1;
     
+    //loop over all regions and compute averaged novelty scores
     for(int r = 0; r < amountRegions; r++)
     {
+      
+      //check for the most plausible class per region
       double maxval = -numeric_limits<double>::max();
+      
+      //loop over all classes
       for(int c = 0; c < probabilities.channels(); c++)
       {
         regionProb[r][c] /= regionCounter[r];
-        if(maxval < regionProb[r][c] && regionProb[r][c] != 0.0)
-        {
-          maxval = regionProb[r][c];
-          bestClassPerRegion[r] = c;
+        
+        if(  (maxval < regionProb[r][c]) ) //&& (regionProb[r][c] != 0.0) ) 
+        {        
+              maxval = regionProb[r][c];
+              bestClassPerRegion[r] = c;
         }
       }
-      regionNoveltyMeasure[r] /= regionCounter[r];
-      if(maxuncert < regionNoveltyMeasure[r])
+       
+      //if the region only contains unvalid information (e.g., background) skip it
+      if (regionCounterNovelty[r] == 0)
+      {
+        continue;
+      }
+      
+      //normalize summed novelty scores to region size
+      regionNoveltyMeasure[r] /= regionCounterNovelty[r];
+    
+      //did we find a region that has a higher score as the most novel region known so far within this image?
+      if(   (  mostNoveltyWithMaxScores && (maxNoveltyScore < regionNoveltyMeasure[r]) )    // if we look for large novelty scores, e.g., variance
+        || ( !mostNoveltyWithMaxScores && (maxNoveltyScore > regionNoveltyMeasure[r]) ) )  // if we look for small novelty scores, e.g., min mean
       {
-        maxuncert = regionNoveltyMeasure[r];
-        maxUncertRegion = r;
+                   //did we already query a region of this image?                --   and it was this specific region
+        if ( (queriedRegions.find( currentFile ) != queriedRegions.end() ) && ( queriedRegions[currentFile].find(r) != queriedRegions[currentFile].end() ) )
+        {
+          continue;
+        }
+        else //only accept the region as novel if we never queried it before
+        {
+          maxNoveltyScore = regionNoveltyMeasure[r];
+          maxUncertRegion = r;        
+        }
+
       }
+
     }
     
+    // after finding the most novel region for the current image, check whether this region is also the most novel with respect
+    // to all previously seen test images
+    // if so, store the corresponding features, since we want to "actively" query them to incorporate useful information
     if(findMaximumUncert)
     {
-      if(maxuncert > globalMaxUncert)
+      if(    (   mostNoveltyWithMaxScores && (maxNoveltyScore > globalMaxUncert) )
+          || (  !mostNoveltyWithMaxScores && (maxNoveltyScore < globalMaxUncert) ) )
       {
-        //save new important features
+        //current most novel region of the image has "higher" novelty score then previous most novel region of all test images worked on so far
+        // -> save new important features of this region
         Examples examples;
-        for ( int y = 0; y < ysize; y += testWSize )
+        for ( int y = 0; y < ysize; y += trainWsize )
         {
-          for ( int x = 0; x < xsize; x += testWSize)
+          for ( int x = 0; x < xsize; x += trainWsize)
           {
             if(mask(x,y) == maxUncertRegion)
             {
+              int classnoTmp = labels(x,y);
+              if ( forbidden_classesActiveLearning.find(classnoTmp) != forbidden_classesActiveLearning.end() )
+                continue;
+              
               Example example;
               example.vec = NULL;
               example.svec = new SparseVector ( featdim );
-              int classnoTmp = labels(x,y);
+              
               for ( int f = 0; f < featdim; f++ )
               {
                 double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
@@ -675,15 +684,27 @@ void SemSegNovelty::semanticseg ( CachedExample *ce, NICE::Image & segresult, NI
         
         if(examples.size() > 0)
         {
+          std::cerr << "found " << examples.size() << " new examples in the queried region" << std::endl << std::endl;
           newTrainExamples.clear();
           newTrainExamples = examples;
-          globalMaxUncert = maxuncert;
-          visualizeRegion(img,mask,maxUncertRegion,maskedImg);
+          globalMaxUncert = maxNoveltyScore;
+          //prepare for later visualization
+//           if (visualizeALimages)
+            visualizeRegion(img,mask,maxUncertRegion,maskedImg);
+        }
+        else
+        {
+          std::cerr << "the queried region has no valid information" << std::endl << std::endl;
         }
+        
+        //save filename and region index
+        currentRegionToQuery.first = currentFile;
+        currentRegionToQuery.second = maxUncertRegion;
       }
     }
 
     //write back best results per region
+    //i.e., write normalized novelty scores for every region into the novelty image
     for ( int y = 0; y < ysize; y++)
     {
       for (int x = 0; x < xsize; x++)
@@ -694,12 +715,17 @@ void SemSegNovelty::semanticseg ( CachedExample *ce, NICE::Image & segresult, NI
           probabilities ( x, y, j ) = regionProb[r][j];
         }
         segresult(x,y) = bestClassPerRegion[r];
+        // write novelty scores for every segment into the "final" image
+        noveltyImage(x,y) = regionNoveltyMeasure[r];
       }
     }
-  }
-
+  } // if regionSeg != null
+  
   timer.stop();
-  cout << "second: " << timer.getLastAbsolute() << endl;
+  std::cout << "AL time for determination of novel regions: " << timer.getLastAbsolute() << std::endl;
+
+//   timer.stop();
+//   cout << "second: " << timer.getLastAbsolute() << endl;
   timer.start();
 
   ColorImage imgrgb ( xsize, ysize );
@@ -707,54 +733,787 @@ void SemSegNovelty::semanticseg ( CachedExample *ce, NICE::Image & segresult, NI
   std::stringstream out;
   std::vector< std::string > list2;
   StringTools::split ( Globals::getCurrentImgFN (), '/', list2 );
-  out << uncertdir << "/" << list2.back();
-  
-  uncert.writeRaw(out.str() + ".rawfloat");
-  uncert(0, 0) = 0.0;
-  uncert(0, 1) = 1.0+gpNoise;
-  ICETools::convertToRGB ( uncert, imgrgb );
-  imgrgb.write ( out.str() + "rough.png" );
-
-  //invert images such that large numbers correspond to high impact, high variance, high importance, high novelty, ...
-  for ( int y = 0; y < ysize; y++)
-  {
-    for (int x = 0; x < xsize; x++)
-    {
-      gpUncertainty(x,y) =  maxGPUncertainty - gpUncertainty(x,y);
-      gpMean(x,y) = maxGPMean - gpMean(x,y);
-      gpMeanRatio(x,y) = maxGPMeanRatio - gpMeanRatio(x,y);
-      gpWeightRatio(x,y) = maxGPWeightRatio - gpWeightRatio(x,y);
-    }
-  }
-  
-  
-  //  
-  gpUncertainty(0, 0) = 0.0;
-  gpUncertainty(0, 1) = maxGPUncertainty;
-  ICETools::convertToRGB ( gpUncertainty, imgrgb );
-  imgrgb.write ( out.str() + "gpUncertainty.png" );
-  //
-  gpMean(0, 0) = 0.0;
-  gpMean(0, 1) = maxGPMean;  
-  ICETools::convertToRGB ( gpMean, imgrgb );
-  imgrgb.write ( out.str() + "gpMean.png" );
-  //
-  gpMeanRatio(0, 0) = 0.0;
-  gpMeanRatio(0, 1) = maxGPMeanRatio;   
-  ICETools::convertToRGB ( gpMeanRatio, imgrgb );
-  imgrgb.write ( out.str() + "gpMeanRatio.png" );
-  //
-  gpWeightAll(0, 0) = 0.0;
-  gpWeightAll(0, 1) = maxGPWeightAll;     
-  ICETools::convertToRGB ( gpWeightAll, imgrgb );
-  imgrgb.write ( out.str() + "gpWeightAll.png" );
-  //
-  gpWeightRatio(0, 0) = 0.0;
-  gpWeightRatio(0, 1) = maxGPWeightRatio;     
-  ICETools::convertToRGB ( gpWeightRatio, imgrgb );
-  imgrgb.write ( out.str() + "gpWeightRatio.png" );    
-
+  out << resultdir << "/" << list2.back();
+  
+  noveltyImage.writeRaw(out.str() + "_run_" +  NICE::intToString(this->iterationCountSuffix) + "_" + noveltyMethodString+".rawfloat");
+  
+  if (visualizeALimages)
+  {
+    ICETools::convertToRGB ( noveltyImage, imgrgb );
+    showImage(imgrgb, "Novelty Image");
+  }
 
   timer.stop();
-  cout << "last: " << timer.getLastAbsolute() << endl;
+  cout << "AL time for writing the raw novelty image: " << timer.getLastAbsolute() << endl;
+}
+
+inline void SemSegNovelty::computeClassificationResults( const NICE::MultiChannelImageT<double> & feats, 
+                                                   NICE::Image & segresult,
+                                                   NICE::MultiChannelImageT<double> & probabilities,
+                                                   const int & xsize,
+                                                   const int & ysize,
+                                                   const int & featdim
+                                                       )
+{
+  std::cerr << "featdim: " << featdim << std::endl;
+  
+  if ( classifier != NULL )
+  {  
+  
+    #pragma omp parallel for
+    for ( int y = 0; y < ysize; y += testWSize )
+    {
+      Example example;
+      example.vec = NULL;
+      example.svec = new SparseVector ( featdim );
+      for ( int x = 0; x < xsize; x += testWSize)
+      {
+        for ( int f = 0; f < featdim; f++ )
+        {
+          double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+          if ( val > 1e-10 )
+            ( *example.svec ) [f] = val;
+        }
+        example.svec->normalize();
+
+        ClassificationResult cr = classifier->classify ( example );
+
+        int xs = std::max(0, x - testWSize/2);
+        int xe = std::min(xsize - 1, x + testWSize/2);
+        int ys = std::max(0, y - testWSize/2);
+        int ye = std::min(ysize - 1, y + testWSize/2);
+        for (int yl = ys; yl <= ye; yl++)
+        {
+          for (int xl = xs; xl <= xe; xl++)
+          {
+            for ( int j = 0 ; j < cr.scores.size(); j++ )
+            {
+              probabilities ( xl, yl, j ) = cr.scores[j];
+            }
+            segresult ( xl, yl ) = cr.classno;
+          }
+        }
+        
+        example.svec->clear();
+      }
+      delete example.svec;
+      example.svec = NULL;
+    }
+  }
+  else //vclassifier
+  {
+    std::cerr << "compute classification results with vclassifier" << std::endl;
+    #pragma omp parallel for
+    for ( int y = 0; y < ysize; y += testWSize )
+    {
+      for ( int x = 0; x < xsize; x += testWSize)
+      {
+        NICE::Vector v(featdim);
+        for ( int f = 0; f < featdim; f++ )
+        {
+          double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+          v[f] = val;
+        }
+        v.normalizeL1();
+
+        ClassificationResult cr = vclassifier->classify ( v );
+
+        int xs = std::max(0, x - testWSize/2);
+        int xe = std::min(xsize - 1, x + testWSize/2);
+        int ys = std::max(0, y - testWSize/2);
+        int ye = std::min(ysize - 1, y + testWSize/2);
+        for (int yl = ys; yl <= ye; yl++)
+        {
+          for (int xl = xs; xl <= xe; xl++)
+          {
+            for ( int j = 0 ; j < cr.scores.size(); j++ )
+            {
+              probabilities ( xl, yl, j ) = cr.scores[j];
+            }
+            segresult ( xl, yl ) = cr.classno;
+          }
+        }
+      }
+    }    
+
+  }
+}
+
+// compute novelty images depending on the strategy chosen
+
+void SemSegNovelty::computeNoveltyByRandom(         NICE::FloatImage & noveltyImage, 
+                                              const NICE::MultiChannelImageT<double> & feats,  
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                             const int & xsize, const int & ysize, const int & featdim  )
+{
+#pragma omp parallel for
+  for ( int y = 0; y < ysize; y += testWSize )
+  {
+    Example example;
+    example.vec = NULL;
+    example.svec = new SparseVector ( featdim );
+    for ( int x = 0; x < xsize; x += testWSize)
+    {
+      for ( int f = 0; f < featdim; f++ )
+      {
+        double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+        if ( val > 1e-10 )
+          ( *example.svec ) [f] = val;
+      }
+      example.svec->normalize();
+
+      ClassificationResult cr = classifier->classify ( example );
+  
+      int xs = std::max(0, x - testWSize/2);
+      int xe = std::min(xsize - 1, x + testWSize/2);
+      int ys = std::max(0, y - testWSize/2);
+      int ye = std::min(ysize - 1, y + testWSize/2);
+      
+      double randVal = randDouble();
+
+      for (int yl = ys; yl <= ye; yl++)
+      {
+        for (int xl = xs; xl <= xe; xl++)
+        {
+          for ( int j = 0 ; j < cr.scores.size(); j++ )
+          {
+            probabilities ( xl, yl, j ) = cr.scores[j];
+          }
+          segresult ( xl, yl ) = cr.classno;          
+          noveltyImage ( xl, yl ) = randVal; 
+        }
+      }     
+    }
+  }  
+}
+
+
+void SemSegNovelty::computeNoveltyByVariance(       NICE::FloatImage & noveltyImage, 
+                                              const NICE::MultiChannelImageT<double> & feats,  
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                             const int & xsize, const int & ysize, const int & featdim )
+{
+#pragma omp parallel for
+  for ( int y = 0; y < ysize; y += testWSize )
+  {
+    Example example;
+    example.vec = NULL;
+    example.svec = new SparseVector ( featdim );
+    for ( int x = 0; x < xsize; x += testWSize)
+    {
+      for ( int f = 0; f < featdim; f++ )
+      {
+        double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+        if ( val > 1e-10 )
+          ( *example.svec ) [f] = val;
+      }
+      example.svec->normalize();
+
+      ClassificationResult cr = classifier->classify ( example );
+      
+      int xs = std::max(0, x - testWSize/2);
+      int xe = std::min(xsize - 1, x + testWSize/2);
+      int ys = std::max(0, y - testWSize/2);
+      int ye = std::min(ysize - 1, y + testWSize/2);
+      for (int yl = ys; yl <= ye; yl++)
+      {
+        for (int xl = xs; xl <= xe; xl++)
+        {
+          for ( int j = 0 ; j < cr.scores.size(); j++ )
+          {
+            probabilities ( xl, yl, j ) = cr.scores[j];
+          }
+          segresult ( xl, yl ) = cr.classno;          
+          noveltyImage ( xl, yl ) = cr.uncertainty; 
+        }
+      }          
+      
+      example.svec->clear();
+    }
+    delete example.svec;
+    example.svec = NULL;
+  }  
+}
+
+void SemSegNovelty::computeNoveltyByGPUncertainty(  NICE::FloatImage & noveltyImage, 
+                                              const NICE::MultiChannelImageT<double> & feats,  
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                             const int & xsize, const int & ysize, const int & featdim )
+{
+  
+  double gpNoise =  conf->gD("GPHIK", "noise", 0.01);
+  
+#pragma omp parallel for
+  for ( int y = 0; y < ysize; y += testWSize )
+  {
+    Example example;
+    example.vec = NULL;
+    example.svec = new SparseVector ( featdim );
+    for ( int x = 0; x < xsize; x += testWSize)
+    {
+      for ( int f = 0; f < featdim; f++ )
+      {
+        double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+        if ( val > 1e-10 )
+          ( *example.svec ) [f] = val;
+      }
+      example.svec->normalize();
+
+      ClassificationResult cr = classifier->classify ( example );
+      
+      double maxMeanAbs ( 0.0 ); 
+      
+      for ( int j = 0 ; j < cr.scores.size(); j++ )
+      {   
+        if ( forbidden_classesTrain.find ( j ) != forbidden_classesTrain.end() )
+        {
+          continue;
+        }
+        //check for larger abs mean
+        if (abs(cr.scores[j]) > maxMeanAbs)
+        {
+          maxMeanAbs = abs(cr.scores[j]);
+        }
+        
+      }
+
+      double firstTerm (1.0 / sqrt(cr.uncertainty+gpNoise));
+      
+      //compute the heuristic GP-UNCERTAINTY, as proposed by Kapoor et al. in IJCV 2010
+      // GP-UNCERTAINTY : |mean| / sqrt(var^2 + gpnoise^2)
+      double gpUncertaintyVal = maxMeanAbs*firstTerm; //firstTerm = 1.0 / sqrt(r.uncertainty+gpNoise))
+
+      int xs = std::max(0, x - testWSize/2);
+      int xe = std::min(xsize - 1, x + testWSize/2);
+      int ys = std::max(0, y - testWSize/2);
+      int ye = std::min(ysize - 1, y + testWSize/2);
+      for (int yl = ys; yl <= ye; yl++)
+      {
+        for (int xl = xs; xl <= xe; xl++)
+        {         
+          for ( int j = 0 ; j < cr.scores.size(); j++ )
+          {
+            probabilities ( xl, yl, j ) = cr.scores[j];
+          }
+          segresult ( xl, yl ) = cr.classno;          
+          noveltyImage ( xl, yl ) = gpUncertaintyVal;  
+        }
+      }   
+      
+      example.svec->clear();
+    }
+    delete example.svec;
+    example.svec = NULL;
+  }  
+}
+
+void SemSegNovelty::computeNoveltyByGPMean(  NICE::FloatImage & noveltyImage, 
+                                              const NICE::MultiChannelImageT<double> & feats,  
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                             const int & xsize, const int & ysize, const int & featdim )
+{
+  double gpNoise =  conf->gD("GPHIK", "noise", 0.01);  
+    
+#pragma omp parallel for
+  for ( int y = 0; y < ysize; y += testWSize )
+  {
+    Example example;
+    example.vec = NULL;
+    example.svec = new SparseVector ( featdim );
+    for ( int x = 0; x < xsize; x += testWSize)
+    {
+      for ( int f = 0; f < featdim; f++ )
+      {
+        double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+        if ( val > 1e-10 )
+          ( *example.svec ) [f] = val;
+      }
+      example.svec->normalize();
+
+      ClassificationResult cr = classifier->classify ( example );
+
+      double minMeanAbs ( numeric_limits<double>::max() );
+      
+      for ( int j = 0 ; j < probabilities.channels(); j++ )
+      {
+        if ( forbidden_classesTrain.find ( j ) != forbidden_classesTrain.end() )
+        {
+          continue;
+        }
+
+        //check whether we found a class with higher smaller abs mean than the current minimum
+        if (abs(probabilities(x,y,j)) < minMeanAbs)
+        {
+          minMeanAbs = abs(cr.scores[j]); 
+        }
+      }
+
+      // compute results when we take the lowest mean value of all classes
+      double gpMeanVal = minMeanAbs;
+  
+      int xs = std::max(0, x - testWSize/2);
+      int xe = std::min(xsize - 1, x + testWSize/2);
+      int ys = std::max(0, y - testWSize/2);
+      int ye = std::min(ysize - 1, y + testWSize/2);
+      for (int yl = ys; yl <= ye; yl++)
+      {
+        for (int xl = xs; xl <= xe; xl++)
+        {
+          for ( int j = 0 ; j < cr.scores.size(); j++ )
+          {
+            probabilities ( xl, yl, j ) = cr.scores[j];
+          }
+          segresult ( xl, yl ) = cr.classno;          
+          noveltyImage ( xl, yl ) = gpMeanVal; 
+        }
+      }     
+    }
+  }  
+}
+
+void SemSegNovelty::computeNoveltyByGPMeanRatio(  NICE::FloatImage & noveltyImage, 
+                                              const NICE::MultiChannelImageT<double> & feats,  
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                             const int & xsize, const int & ysize, const int & featdim )
+{
+  double gpNoise =  conf->gD("GPHIK", "noise", 0.01);  
+  
+#pragma omp parallel for
+  for ( int y = 0; y < ysize; y += testWSize )
+  {
+    Example example;
+    example.vec = NULL;
+    example.svec = new SparseVector ( featdim );
+    for ( int x = 0; x < xsize; x += testWSize)
+    {
+      for ( int f = 0; f < featdim; f++ )
+      {
+        double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+        if ( val > 1e-10 )
+          ( *example.svec ) [f] = val;
+      }
+      example.svec->normalize();
+
+      ClassificationResult cr = classifier->classify ( example );
+
+      double maxMean ( -numeric_limits<double>::max() );
+      double sndMaxMean ( -numeric_limits<double>::max() );     
+      
+      for ( int j = 0 ; j < cr.scores.size(); j++ )
+      {
+        if ( forbidden_classesTrain.find ( j ) != forbidden_classesTrain.end() )
+        {
+          continue;
+        }
+        
+        //check for larger mean without abs as well
+        if (cr.scores[j] > maxMean)
+        {
+          sndMaxMean = maxMean;
+          maxMean = cr.scores[j];
+        }
+        // and also for the second highest mean of all classes
+        else if (cr.scores[j] > sndMaxMean)
+        {
+          sndMaxMean = cr.scores[j];
+        }          
+      }
+      
+      //look at the difference in the absolut mean values for the most plausible class
+      // and the second most plausible class
+      double gpMeanRatioVal= maxMean - sndMaxMean;
+
+
+      int xs = std::max(0, x - testWSize/2);
+      int xe = std::min(xsize - 1, x + testWSize/2);
+      int ys = std::max(0, y - testWSize/2);
+      int ye = std::min(ysize - 1, y + testWSize/2);
+      for (int yl = ys; yl <= ye; yl++)
+      {
+        for (int xl = xs; xl <= xe; xl++)
+        {
+          for ( int j = 0 ; j < cr.scores.size(); j++ )
+          {
+            probabilities ( xl, yl, j ) = cr.scores[j];
+          }
+          segresult ( xl, yl ) = cr.classno;          
+          noveltyImage ( xl, yl ) = gpMeanRatioVal;
+        }
+      }    
+      
+      example.svec->clear();
+    }
+    delete example.svec;
+    example.svec = NULL;
+  }  
+}
+
+void SemSegNovelty::computeNoveltyByGPWeightAll(  NICE::FloatImage & noveltyImage, 
+                                              const NICE::MultiChannelImageT<double> & feats,  
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                             const int & xsize, const int & ysize, const int & featdim )
+{
+  double gpNoise =  conf->gD("GPHIK", "noise", 0.01);  
+  
+#pragma omp parallel for
+  for ( int y = 0; y < ysize; y += testWSize )
+  {
+    Example example;
+    example.vec = NULL;
+    example.svec = new SparseVector ( featdim );
+    for ( int x = 0; x < xsize; x += testWSize)
+    {
+      for ( int f = 0; f < featdim; f++ )
+      {
+        double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+        if ( val > 1e-10 )
+          ( *example.svec ) [f] = val;
+      }
+      example.svec->normalize();
+
+      ClassificationResult cr = classifier->classify ( example );
+      
+      double firstTerm (1.0 / sqrt(cr.uncertainty+gpNoise));
+      
+      double gpWeightAllVal ( 0.0 );
+
+      if ( numberOfClasses > 2)
+      {
+        //compute the weight in the alpha-vector for every sample after assuming it to be 
+        // added to the training set.
+        // Thereby, we measure its "importance" for the current model
+        // 
+        //double firstTerm is already computed
+        //
+        //the second term is only needed when computing impacts
+        //double secondTerm; //this is the nasty guy :/
+        
+        //--- compute the third term
+        // this is the difference between predicted label and GT label 
+        std::vector<double> diffToPositive; diffToPositive.clear();
+        std::vector<double> diffToNegative; diffToNegative.clear();
+        double diffToNegativeSum(0.0);
+        
+        for ( int j = 0 ; j < cr.scores.size(); j++ )
+        {
+          if ( forbidden_classesTrain.find ( j ) != forbidden_classesTrain.end() )
+          {
+            continue;
+          }          
+          
+          // look at the difference to plus 1          
+          diffToPositive.push_back(abs(cr.scores[j] - 1));
+          // look at the difference to -1          
+          diffToNegative.push_back(abs(cr.scores[j] + 1));
+          //sum up the difference to -1
+          diffToNegativeSum += abs(cr.scores[j] - 1);
+        }
+
+        //let's subtract for every class its diffToNegative from the sum, add its diffToPositive,
+        //and use this as the third term for this specific class.
+        //the final value is obtained by minimizing over all classes
+        //
+        // originally, we minimize over all classes after building the final score
+        // however, the first and the second term do not depend on the choice of
+        // y*, therefore we minimize here already
+        double thirdTerm (numeric_limits<double>::max()) ;
+        for(uint tmpCnt = 0; tmpCnt < diffToPositive.size(); tmpCnt++)
+        {
+          double tmpVal ( diffToPositive[tmpCnt] + (diffToNegativeSum-diffToNegative[tmpCnt])   );
+          if (tmpVal < thirdTerm)
+            thirdTerm = tmpVal;
+        }
+        gpWeightAllVal = thirdTerm*firstTerm;        
+      }
+      else //binary scenario
+      {
+        gpWeightAllVal = std::min( abs(cr.scores[*classesInUse.begin()]+1), abs(cr.scores[*classesInUse.begin()]-1) );
+        gpWeightAllVal *= firstTerm;
+      }
+
+      int xs = std::max(0, x - testWSize/2);
+      int xe = std::min(xsize - 1, x + testWSize/2);
+      int ys = std::max(0, y - testWSize/2);
+      int ye = std::min(ysize - 1, y + testWSize/2);
+      for (int yl = ys; yl <= ye; yl++)
+      {
+        for (int xl = xs; xl <= xe; xl++)
+        {
+          for ( int j = 0 ; j < cr.scores.size(); j++ )
+          {
+            probabilities ( xl, yl, j ) = cr.scores[j];
+          }
+          segresult ( xl, yl ) = cr.classno;          
+          noveltyImage ( xl, yl ) = gpWeightAllVal;
+        }
+      }
+   
+      
+      example.svec->clear();
+    }
+    delete example.svec;
+    example.svec = NULL;
+  }  
+}
+
+void SemSegNovelty::computeNoveltyByGPWeightRatio(  NICE::FloatImage & noveltyImage, 
+                                              const NICE::MultiChannelImageT<double> & feats,  
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                             const int & xsize, const int & ysize, const int & featdim )
+{
+  double gpNoise =  conf->gD("GPHIK", "noise", 0.01);  
+  
+#pragma omp parallel for
+  for ( int y = 0; y < ysize; y += testWSize )
+  {
+    Example example;
+    example.vec = NULL;
+    example.svec = new SparseVector ( featdim );
+    for ( int x = 0; x < xsize; x += testWSize)
+    {
+      for ( int f = 0; f < featdim; f++ )
+      {
+        double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+        if ( val > 1e-10 )
+          ( *example.svec ) [f] = val;
+      }
+      example.svec->normalize();
+
+      ClassificationResult cr = classifier->classify ( example );
+ 
+
+       double firstTerm (1.0 / sqrt(cr.uncertainty+gpNoise));
+
+       double gpWeightRatioVal ( 0.0 );
+
+       if ( numberOfClasses > 2)
+       {
+        //compute the weight in the alpha-vector for every sample after assuming it to be 
+        // added to the training set.
+        // Thereby, we measure its "importance" for the current model
+        // 
+        //double firstTerm is already computed
+        //
+        //the second term is only needed when computing impacts
+        //double secondTerm; //this is the nasty guy :/
+        
+        //--- compute the third term
+        // this is the difference between predicted label and GT label 
+        std::vector<double> diffToPositive; diffToPositive.clear();
+        std::vector<double> diffToNegative; diffToNegative.clear();
+        double diffToNegativeSum(0.0);
+        
+        for ( int j = 0 ; j < cr.scores.size(); j++ )
+        {
+          if ( forbidden_classesTrain.find ( j ) != forbidden_classesTrain.end() )
+          {
+            continue;
+          }          
+          
+          // look at the difference to plus 1          
+          diffToPositive.push_back(abs(cr.scores[j] - 1));
+        }
+
+        //let's subtract for every class its diffToNegative from the sum, add its diffToPositive,
+        //and use this as the third term for this specific class.
+        //the final value is obtained by minimizing over all classes
+        //
+        // originally, we minimize over all classes after building the final score
+        // however, the first and the second term do not depend on the choice of
+        // y*, therefore we minimize here already   
+        
+        //now look on the ratio of the resulting weights for the most plausible
+        // against the second most plausible class
+        double thirdTermMostPlausible ( 0.0 ) ;
+        double thirdTermSecondMostPlausible ( 0.0 ) ;
+        for(uint tmpCnt = 0; tmpCnt < diffToPositive.size(); tmpCnt++)
+        {
+          if (diffToPositive[tmpCnt] > thirdTermMostPlausible)
+          {
+            thirdTermSecondMostPlausible = thirdTermMostPlausible;
+            thirdTermMostPlausible = diffToPositive[tmpCnt];
+          }
+          else if (diffToPositive[tmpCnt] > thirdTermSecondMostPlausible)
+          {
+            thirdTermSecondMostPlausible = diffToPositive[tmpCnt];
+          }
+        }
+        //compute the resulting score
+        gpWeightRatioVal = (thirdTermMostPlausible - thirdTermSecondMostPlausible)*firstTerm;      
+
+        //finally, look for this feature how it would affect to whole model (summarized by weight-vector alpha), if we would 
+        //use it as an additional training example
+        //TODO this would be REALLY computational demanding. Do we really want to do this?
+  //         gpImpactAll[s] ( pce[i].second.x, pce[i].second.y ) = thirdTerm*firstTerm*secondTerm;
+  //         gpImpactRatio[s] ( pce[i].second.x, pce[i].second.y ) = (thirdTermMostPlausible - thirdTermSecondMostPlausible)*firstTerm*secondTerm;
+       }
+       else //binary scenario
+       {
+         gpWeightRatioVal = std::min( abs(cr.scores[*classesInUse.begin()]+1), abs(cr.scores[*classesInUse.begin()]-1) );
+         gpWeightRatioVal *= firstTerm;
+       }
+
+      int xs = std::max(0, x - testWSize/2);
+      int xe = std::min(xsize - 1, x + testWSize/2);
+      int ys = std::max(0, y - testWSize/2);
+      int ye = std::min(ysize - 1, y + testWSize/2);
+      for (int yl = ys; yl <= ye; yl++)
+      {
+        for (int xl = xs; xl <= xe; xl++)
+        {
+          for ( int j = 0 ; j < cr.scores.size(); j++ )
+          {
+            probabilities ( xl, yl, j ) = cr.scores[j];
+          }
+          segresult ( xl, yl ) = cr.classno;          
+          noveltyImage ( xl, yl ) = gpWeightRatioVal;  
+        }
+      }
+       
+      example.svec->clear();
+    }
+    delete example.svec;
+    example.svec = NULL;
+  }  
+}
+
+
+void SemSegNovelty::addNewExample(const NICE::Vector& newExample, const int & newClassNo)
+{
+  //accept the new class as valid information
+  if ( forbidden_classesTrain.find ( newClassNo ) != forbidden_classesTrain.end() )
+  {
+    forbidden_classesTrain.erase(newClassNo);
+    numberOfClasses++;
+  }
+  if ( classesInUse.find ( newClassNo ) == classesInUse.end() )
+  {
+    classesInUse.insert( newClassNo );
+  }    
+  
+  
+  //then add it to the classifier used
+  if ( classifier != NULL )
+  { 
+    //TODO    
+  }
+  else //vclassifier
+  {
+    if (this->classifierString.compare("nn") == 0)    
+    {
+      vclassifier->teach ( newClassNo, newExample );
+    }
+  }
+}
+
+void SemSegNovelty::addNovelExamples()
+{
+
+  Timer timer;
+  
+  //show the image that contains the most novel region
+  if (visualizeALimages)
+    showImage(maskedImg, "Most novel region");  
+  
+  timer.start();
+  
+
+    std::stringstream out;
+    std::vector< std::string > list2;
+    StringTools::split ( Globals::getCurrentImgFN (), '/', list2 );
+    out << resultdir << "/" << list2.back();     
+    
+    maskedImg.writePPM ( out.str() + "_run_" +  NICE::intToString(this->iterationCountSuffix) + "_" + noveltyMethodString+ "_query.ppm" );
+
+  
+  timer.stop();
+  std::cerr << "AL time for writing queried image: " << timer.getLast() << std::endl;
+
+  timer.start();
+  
+  //check which classes will be added using the features from the novel region
+  std::set<int> newClassNumbers;
+  newClassNumbers.clear(); //just to be sure  
+  for ( uint i = 0 ; i < newTrainExamples.size() ; i++ )
+  {
+    if (newClassNumbers.find(newTrainExamples[i].first /* classNumber*/) == newClassNumbers.end() )
+    {
+      newClassNumbers.insert(newTrainExamples[i].first );
+    }
+  }
+
+  //accept the new classes as valid information
+  for (std::set<int>::const_iterator clNoIt = newClassNumbers.begin(); clNoIt != newClassNumbers.end(); clNoIt++)
+  {
+    if ( forbidden_classesTrain.find ( *clNoIt ) != forbidden_classesTrain.end() )
+    {
+      forbidden_classesTrain.erase(*clNoIt);
+      numberOfClasses++;
+    }
+    if ( classesInUse.find ( *clNoIt ) == classesInUse.end() )
+    {
+      classesInUse.insert( *clNoIt );
+    }
+  }
+  
+  timer.stop();
+  std::cerr << "AL time for accepting possible new classes: " << timer.getLast() << std::endl;
+  
+  timer.start();
+  //then add the new features to the classifier used
+  if ( classifier != NULL )
+  { 
+    if (this->classifierString.compare("ClassifierGPHIK") == 0)    
+    {
+      classifier->addMultipleExamples ( this->newTrainExamples );
+    }    
+  }
+  else //vclassifier
+  {
+    //TODO
+  }
+  
+  timer.stop();
+  std::cerr << "AL time for actually updating the classifier: " << timer.getLast() << std::endl;
+  
+  std::cerr << "the current region to query is: " << currentRegionToQuery.first << " -- " << currentRegionToQuery.second << std::endl;
+  
+  //did we already query a region of this image?
+  if ( queriedRegions.find( currentRegionToQuery.first ) != queriedRegions.end() )
+  {
+    queriedRegions[ currentRegionToQuery.first ].insert(currentRegionToQuery.second);
+  }
+  else
+  {
+    std::set<int> tmpSet; tmpSet.insert(currentRegionToQuery.second);
+    queriedRegions.insert(std::pair<std::string,std::set<int> > (currentRegionToQuery.first, tmpSet ) );
+  }  
+  
+  std::cerr << "Write already queried regions: " << std::endl;
+  for (std::map<std::string,std::set<int> >::const_iterator it = queriedRegions.begin(); it != queriedRegions.end(); it++)
+  {
+    std::cerr << "image: " << it->first << " --   ";
+    for (std::set<int>::const_iterator itReg = it->second.begin(); itReg != it->second.end(); itReg++)
+    {
+      std::cerr << *itReg << " ";
+    } 
+    std::cerr << std::endl;
+  }
+  
+  //clear the latest results, since one iteration is over
+  globalMaxUncert = -numeric_limits<double>::max();
+  if (!mostNoveltyWithMaxScores)
+    globalMaxUncert = numeric_limits<double>::max();
+}
+
+const Examples * SemSegNovelty::getNovelExamples() const
+{
+  return &(this->newTrainExamples);
 }

+ 121 - 15
semseg/SemSegNovelty.h

@@ -1,7 +1,7 @@
 /**
  * @file SemSegNovelty.h
  * @brief semantic segmentation using the method from Csurka08
- * @author Björn Fröhlich
+ * @author Björn Fröhlich, Alexander Freytag
  * @date 04/24/2009
  */
 #ifndef SemSegNoveltyINCLUDE
@@ -11,6 +11,7 @@
 
 #include "SemSegTools.h"
 #include "vislearning/classifier/classifierbase/FeaturePoolClassifier.h"
+#include "vislearning/classifier/genericClassifierSelection.h"
 #include "vislearning/features/localfeatures/LFColorWeijer.h"
 
 #include "segmentation/RegionSegmentationMethod.h"
@@ -24,17 +25,21 @@ class SemSegNovelty : public SemanticSegmentation
 {
 
   protected:
-    //! boolean whether to save the cache or not
-    bool save_cache;
+    //! boolean whether to reuse segmentation results for single images in different runs
+    bool reuseSegmentation;
 
-    //! boolean whether to read the cache or not, if read_cache is false, everything will be trained
-    bool read_cache;
+    //! boolean whether to read the initial classifier from a file. If not, training will be performed
+    bool read_classifier;
+    
+    //! boolean whether to save the final classifier or not
+    bool save_classifier;
 
     //! The cached Data
     std::string cache;
     
     //! Classifier
     FeaturePoolClassifier *classifier;
+    VecClassifier *vclassifier;
     
     //! feature extraction
     LFColorWeijer *featExtract;
@@ -43,7 +48,7 @@ class SemSegNovelty : public SemanticSegmentation
     const NICE::Config *conf;
     
     //! distance between features for training
-    int featdist;
+    int trainWsize;
     
     //! half of the window size for local features
     int whs;
@@ -57,28 +62,106 @@ class SemSegNovelty : public SemanticSegmentation
     //! low level Segmentation method
     RegionSegmentationMethod *regionSeg;
     
-    //! set of forbidden/background classes
-    std::set<int> forbidden_classes;
+    //! set of forbidden/background classes for the initial training
     std::set<int> forbidden_classesTrain;
+    //! set of forbidden/background classes for the whole process of learning over time
+    std::set<int> forbidden_classesActiveLearning;
+    //! store the class numbers currently used
     std::set<int> classesInUse;
+        
+    //! obviously, the number of classes used for training (i.e., classesInUse.size() )
+    int numberOfClasses; 
     
-    //! obviously, the number of classes used for training
-    int numberOfClasses;
-    
-    //! where to save the uncertainty
-    std::string uncertdir;
+    //! where to save the resulting images (uncertainty and classification results)
+    std::string resultdir;
     
-    //! find the maximum uncertainty or not
+    //! find the maximum uncertainty or not within the whole test set
     bool findMaximumUncert;
     
     //! image with most uncertain region
     NICE::ColorImage maskedImg;
     
-    //! maximum uncertainty over all images
+    //! for debugging and visualization: show novelty images with and without region segmentation and the most novel region
+    bool visualizeALimages;
+    
+    //! maximum uncertainty over all images, i.e., the novelty score of the most "novel" region of all test images
     double globalMaxUncert;
     
+    //! determine whether a "novelty" method computes large scores for novel objects (e.g., variance), or small scores (e.g., min abs mean)
+    bool mostNoveltyWithMaxScores;
+    
     //! current examples for most uncertain region
     Examples newTrainExamples;
+    
+    //! contains filenames of images and indices of contained regions, that where already queried, to prevent them from being queried again
+    std::map<std::string,std::set<int> > queriedRegions;
+    
+    std::pair<std::string, int> currentRegionToQuery;
+    
+    enum NoveltyMethod{
+      GPVARIANCE, // novel = large variance
+      GPUNCERTAINTY, //novel = small uncertainty (mean / var)
+      GPMINMEAN,  //novel = small mean
+      GPMEANRATIO,  //novel = small difference between mean of most plausible class and mean of snd
+                   //        most plausible class (not useful in binary settings)
+      GPWEIGHTALL, // novel = large weight in alpha vector after updating the model (can be predicted exactly)
+      GPWEIGHTRATIO, // novel = small difference between weights for alpha vectors with assumptions of GT label to be the most 
+                    //         plausible against the second most plausible class
+      RANDOM        // query regions randomly
+    }; 
+    
+    //! specify how "novelty" shall be computed, e.g., using GP-variance, GP-uncertainty, or predicted weight entries
+    NoveltyMethod noveltyMethod;
+    std::string noveltyMethodString;
+    
+    //! just store the name of our classifier
+    std::string classifierString;
+    
+    inline void computeClassificationResults( const NICE::MultiChannelImageT<double> & feats, 
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                                    const int & xsize,
+                                                    const int & ysize,
+                                                    const int & featdim );
+
+   void computeNoveltyByRandom(         NICE::FloatImage & noveltyImage, 
+                                  const NICE::MultiChannelImageT<double> & feats,  
+                                        NICE::Image & segresult,
+                                        NICE::MultiChannelImageT<double> & probabilities,
+                                  const int & xsize, const int & ysize, const int & featdim );    
+    
+   void computeNoveltyByVariance(       NICE::FloatImage & noveltyImage, 
+                                  const NICE::MultiChannelImageT<double> & feats,  
+                                        NICE::Image & segresult,
+                                        NICE::MultiChannelImageT<double> & probabilities,
+                                  const int & xsize, const int & ysize, const int & featdim );
+   
+   void computeNoveltyByGPUncertainty ( NICE::FloatImage & noveltyImage, 
+                                  const NICE::MultiChannelImageT<double> & feats,  
+                                        NICE::Image & segresult,
+                                        NICE::MultiChannelImageT<double> & probabilities,
+                                  const int & xsize, const int & ysize, const int & featdim );
+   
+   void computeNoveltyByGPMean        ( NICE::FloatImage & noveltyImage, 
+                                  const NICE::MultiChannelImageT<double> & feats,  
+                                        NICE::Image & segresult,
+                                        NICE::MultiChannelImageT<double> & probabilities,
+                                  const int & xsize, const int & ysize, const int & featdim );  
+   void computeNoveltyByGPMeanRatio   ( NICE::FloatImage & noveltyImage, 
+                                  const NICE::MultiChannelImageT<double> & feats,  
+                                        NICE::Image & segresult,
+                                        NICE::MultiChannelImageT<double> & probabilities,
+                                  const int & xsize, const int & ysize, const int & featdim );  
+   void computeNoveltyByGPWeightAll   ( NICE::FloatImage & noveltyImage, 
+                                  const NICE::MultiChannelImageT<double> & feats,  
+                                        NICE::Image & segresult,
+                                        NICE::MultiChannelImageT<double> & probabilities,
+                                  const int & xsize, const int & ysize, const int & featdim );  
+   void computeNoveltyByGPWeightRatio ( NICE::FloatImage & noveltyImage, 
+                                  const NICE::MultiChannelImageT<double> & feats,  
+                                        NICE::Image & segresult,
+                                        NICE::MultiChannelImageT<double> & probabilities,
+                                  const int & xsize, const int & ysize, const int & featdim );     
    
   public:
 
@@ -115,6 +198,29 @@ class SemSegNovelty : public SemanticSegmentation
      * @return void
      **/
     void visualizeRegion(const NICE::ColorImage &img, const NICE::Matrix &regions, int region, NICE::ColorImage &outimage);
+
+    /**
+     * @brief Add a new example to the known training data
+     *
+     * @param newExample (NICE::Vector) the feature vector of the new examples
+     * @param newClassNo (int) the corresponding GT class number
+     * @return void
+     **/    
+    void addNewExample(const NICE::Vector & newExample, const int & newClassNo);
+    
+    /**
+     * @brief Add those examples, which belong to the most novel region seen so far
+     *
+     * @return void
+     **/    
+    virtual void addNovelExamples();    
+
+    /**
+     * @brief Get a pointer to the examples extracted from the most novel region seen so far
+     *
+     * @return Examples *
+     **/        
+    virtual const Examples * getNovelExamples() const; 
 };
 
 } //namespace

+ 1527 - 0
semseg/SemSegNoveltyBinary.cpp

@@ -0,0 +1,1527 @@
+#include <sstream>
+#include <iostream>
+
+#include "SemSegNoveltyBinary.h"
+
+#include <core/image/FilterT.h>
+#include <core/basics/numerictools.h>
+#include <core/basics/StringTools.h>
+#include <core/basics/Timer.h>
+
+#include <gp-hik-exp/GPHIKClassifierNICE.h>
+#include <vislearning/baselib/ICETools.h>
+#include <vislearning/baselib/Globals.h>
+#include <vislearning/features/fpfeatures/SparseVectorFeature.h>
+
+#include "segmentation/GenericRegionSegmentationMethodSelection.h"
+
+using namespace std;
+using namespace NICE;
+using namespace OBJREC;
+
+SemSegNoveltyBinary::SemSegNoveltyBinary ( const Config *conf,
+                               const MultiDataset *md )
+    : SemanticSegmentation ( conf, & ( md->getClassNames ( "train" ) ) )
+{
+  this->conf = conf;
+
+  globalMaxUncert = -numeric_limits<double>::max();
+  
+  string section = "SemSegNoveltyBinary";
+
+  featExtract = new LFColorWeijer ( conf );
+
+  this->reuseSegmentation = conf->gB ( "FPCPixel", "reuseSegmentation", true ); //save and read segmentation results from files
+  this->save_classifier = conf->gB ( "FPCPixel", "save_classifier", true ); //save the classifier to a file
+  this->read_classifier = conf->gB ( "FPCPixel", "read_classifier", false ); //read the classifier from a file
+
+  //write uncertainty results in the same folder as done for the segmentation results
+  resultdir = conf->gS("debug", "resultdir", "result");
+  cache = conf->gS ( "cache", "root", "" );
+  
+  
+  //stupid work around of the const attribute
+  Config confCopy = *conf;
+  
+  //just to make sure, that we do NOT perform an optimization after every iteration step
+  //this would just take a lot of time, which is not desired so far
+  confCopy.sB("ClassifierGPHIK","performOptimizationAfterIncrement",false);
+  
+  classifierString = conf->gS ( section, "classifier", "ClassifierGPHIK" );  
+  classifier = NULL;
+  vclassifier = NULL;
+  if ( classifierString.compare("ClassifierGPHIK") == 0)
+    classifier = new GPHIKClassifierNICE ( &confCopy, "ClassifierGPHIK" );
+  else
+    vclassifier = GenericClassifierSelection::selectVecClassifier ( conf, classifierString );
+  
+
+
+  findMaximumUncert = conf->gB(section, "findMaximumUncert", true);
+  whs = conf->gI ( section, "window_size", 10 );
+  //distance to next descriptor during training
+  trainWsize = conf->gI ( section, "train_window_size", 10 );
+  //distance to next descriptor during testing
+  testWSize = conf->gI (section, "test_window_size", 10);
+  // select your segmentation method here
+  string rsMethode = conf->gS ( section, "segmentation", "none" );
+ 
+  if(rsMethode == "none")
+  {
+    regionSeg = NULL;
+  }
+  else
+  {
+    RegionSegmentationMethod *tmpRegionSeg = GenericRegionSegmentationMethodSelection::selectRegionSegmentationMethod(conf, rsMethode);    
+    if ( reuseSegmentation )
+      regionSeg = new RSCache ( conf, tmpRegionSeg );
+    else
+      regionSeg = tmpRegionSeg;
+  }
+  
+  cn = md->getClassNames ( "train" );
+
+  if ( read_classifier )
+  {
+    try
+    {
+      if ( classifier != NULL )
+      {
+        string classifierdst = "/classifier.data";        
+        fprintf ( stderr, "SemSegNoveltyBinary:: Reading classifier data from %s\n", ( cache + classifierdst ).c_str() );        
+        classifier->read ( cache + classifierdst );
+      }
+      else
+      {
+        string classifierdst = "/veccl.data";        
+        fprintf ( stderr, "SemSegNoveltyBinary:: Reading classifier data from %s\n", ( cache + classifierdst ).c_str() );          
+        vclassifier->read ( cache + classifierdst );      
+      }
+      
+
+      fprintf ( stderr, "SemSegNoveltyBinary:: successfully read\n" );
+    }
+    catch ( char *str )
+    {
+      cerr << "error reading data: " << str << endl;
+    }
+  }
+  else
+  {
+    train ( md );
+  }
+  
+  //define which measure for "novelty" we want to use
+  noveltyMethodString = conf->gS( section,  "noveltyMethod", "gp-variance");
+  if (noveltyMethodString.compare("gp-variance") == 0)  // novel = large variance
+  {
+    this->noveltyMethod = GPVARIANCE;
+    this->mostNoveltyWithMaxScores = true;
+  }
+  else if (noveltyMethodString.compare("gp-uncertainty") == 0) //novel = large uncertainty (mean / var)
+  {
+    this->noveltyMethod = GPUNCERTAINTY;
+    this->mostNoveltyWithMaxScores = false;
+    globalMaxUncert = numeric_limits<double>::max();
+  } 
+  else if (noveltyMethodString.compare("gp-mean") == 0) //novel = small mean
+  {
+    this->noveltyMethod = GPMINMEAN;
+    this->mostNoveltyWithMaxScores = false;
+    globalMaxUncert = numeric_limits<double>::max();
+  }
+  else if (noveltyMethodString.compare("gp-meanRatio") == 0)  //novel = small difference between mean of most plausible class and mean of snd
+                                                              //        most plausible class (not useful in binary settings)
+  {
+    this->noveltyMethod = GPMEANRATIO;
+    this->mostNoveltyWithMaxScores = false;
+    globalMaxUncert = numeric_limits<double>::max();
+  }
+  else if (noveltyMethodString.compare("gp-weightAll") == 0) // novel = large weight in alpha vector after updating the model (can be predicted exactly)
+  {
+    this->noveltyMethod = GPWEIGHTALL;
+    this->mostNoveltyWithMaxScores = true;
+  }
+  else if (noveltyMethodString.compare("gp-weightRatio") == 0) // novel = small difference between weights for alpha vectors 
+                                                               //     with assumptions of GT label to be the most 
+                                                               //     plausible against the second most plausible class   
+  {
+    this->noveltyMethod = GPWEIGHTRATIO;
+    this->mostNoveltyWithMaxScores = false;
+    globalMaxUncert = numeric_limits<double>::max();
+  }
+  else if (noveltyMethodString.compare("random") == 0) 
+  {
+     initRand(); 
+     this->noveltyMethod = RANDOM;
+  }
+  else
+  {
+    this->noveltyMethod = GPVARIANCE;
+    this->mostNoveltyWithMaxScores = true;
+  }
+  
+  //we don't have queried any region so far
+  queriedRegions.clear();
+  visualizeALimages = conf->gB(section, "visualizeALimages", false);
+  
+  resultsOfSingleRun.clear();
+  
+  write_results = conf->gB( "debug", "write_results", false );
+}
+
+SemSegNoveltyBinary::~SemSegNoveltyBinary()
+{
+  if(newTrainExamples.size() > 0)
+  {
+    // show most uncertain region
+    if (visualizeALimages)
+      showImage(maskedImg);
+    
+    //incorporate new information into the classifier
+    if (classifier != NULL)
+      classifier->addMultipleExamples(newTrainExamples);
+    
+    //store the classifier, such that we can read it again in the next round (if we like that)
+    classifier->save ( cache + "/classifier.data" );
+  }
+  
+  // clean-up
+  if ( classifier != NULL )
+    delete classifier;
+  if ( vclassifier != NULL )
+    delete vclassifier;
+  if ( featExtract != NULL )
+    delete featExtract;
+}
+
+void SemSegNoveltyBinary::visualizeRegion(const NICE::ColorImage &img, const NICE::Matrix &regions, int region, NICE::ColorImage &outimage)
+{
+  std::vector<uchar> color;
+  color.push_back(255);
+  color.push_back(0);
+  color.push_back(0);
+    
+  int width = img.width();
+  int height = img.height();
+  
+  outimage.resize(width,height);
+  
+  for(int y = 0; y < height; y++)
+  {
+    for(int x = 0; x < width; x++)
+    {
+      if(regions(x,y) == region)
+      {
+        for(int c = 0; c < 3; c++)
+        {
+          outimage(x,y,c) = color[c];
+        }
+      }
+      else
+      {
+        for(int c = 0; c < 3; c++)
+        {
+          outimage(x,y,c) = img(x,y,c);
+        }
+      }
+    }
+  }
+}
+
+void SemSegNoveltyBinary::train ( const MultiDataset *md )
+{
+  const LabeledSet train = * ( *md ) ["train"];
+  const LabeledSet *trainp = &train;
+
+  ////////////////////////
+  // feature extraction //
+  ////////////////////////
+ 
+  //check the same thing for the training classes - this is very specific to our setup 
+  std::string forbidden_classesTrain_s = conf->gS ( "analysis", "donttrainTrain", "" );
+  if ( forbidden_classesTrain_s == "" )
+  {
+    forbidden_classesTrain_s = conf->gS ( "analysis", "forbidden_classesTrain", "" );
+  }
+  cn.getSelection ( forbidden_classesTrain_s, forbidden_classesTrain );
+  
+  //check whether we have a single positive class
+  std::string positiveClass_s = conf->gS ( "SemSegNoveltyBinary", "positiveClass", "" );
+  std::set<int> positiveClassNumberTmp;
+  cn.getSelection ( positiveClass_s, positiveClassNumberTmp );  
+
+  std::cerr << "BINARY SETTING ENABLED! " << std::endl;
+  switch ( positiveClassNumberTmp.size() )
+  {
+    case 0:
+    {
+      positiveClass = 0;
+      std::cerr << "no positive class given, assume 0 as positive class" << std::endl;
+      break;
+    }
+    case 1:
+    {
+      positiveClass = *(positiveClassNumberTmp.begin());
+      std::cerr << "positive class will be number" << positiveClass << " with the name: " << positiveClass_s << std::endl;
+      break;
+    }
+    default:
+    {
+      //we specified more than a single positive class. right now, this is not what we are interested in, but 
+      //in theory we could also accept this and convert positiveClass into a set of ints of possible positive classes
+      positiveClass = 0;
+      std::cerr << "no positive class given, assume 0 as positive class" << std::endl;
+      break;
+    }
+  }  
+  std::cerr << "============================" << std::endl << std::endl;  
+
+
+  ProgressBar pb ( "Local Feature Extraction" );
+  pb.show();
+
+  int imgnb = 0;
+
+  Examples examples;
+  examples.filename = "training";
+
+  int featdim = -1;
+
+  classesInUse.clear();  
+  
+  LOOP_ALL_S ( *trainp )
+  {
+    //EACH_S(classno, currentFile);
+    EACH_INFO ( classno, info );
+
+    std::string currentFile = info.img();
+
+    CachedExample *ce = new CachedExample ( currentFile );
+    
+    const LocalizationResult *locResult = info.localization();
+    if ( locResult->size() <= 0 )
+    {
+      fprintf ( stderr, "WARNING: NO ground truth polygons found for %s !\n",
+                currentFile.c_str() );
+      continue;
+    }
+
+    int xsize, ysize;
+    ce->getImageSize ( xsize, ysize );
+
+    Image labels ( xsize, ysize );
+    labels.set ( 0 );
+    locResult->calcLabeledImage ( labels, ( *classNames ).getBackgroundClass() );
+
+    NICE::ColorImage img;
+    try {
+      img = ColorImage ( currentFile );
+    } catch ( Exception ) {
+      cerr << "SemSegNoveltyBinary: error opening image file <" << currentFile << ">" << endl;
+      continue;
+    }
+
+    Globals::setCurrentImgFN ( currentFile );
+
+    MultiChannelImageT<double> feats;
+
+    // extract features
+    featExtract->getFeats ( img, feats );
+    featdim = feats.channels();
+    feats.addChannel(featdim);
+
+    for (int c = 0; c < featdim; c++)
+    {
+      ImageT<double> tmp = feats[c];
+      ImageT<double> tmp2 = feats[c+featdim];
+
+      NICE::FilterT<double, double, double>::gradientStrength (tmp, tmp2);
+    }
+    featdim += featdim;
+
+    // compute integral images
+    for ( int c = 0; c < featdim; c++ )
+    {
+      feats.calcIntegral ( c );
+    }
+
+    for ( int y = 0; y < ysize; y += trainWsize)
+    {
+      for ( int x = 0; x < xsize; x += trainWsize )
+      {
+
+        int classnoTmp = labels.getPixel ( x, y );
+        
+        if ( forbidden_classesTrain.find ( classnoTmp ) != forbidden_classesTrain.end() )
+        {
+          continue;
+        }
+        
+        if (classesInUse.find(classnoTmp) == classesInUse.end())
+        {
+          classesInUse.insert(classnoTmp);
+        }
+        
+        Example example;
+        example.vec = NULL;
+        example.svec = new SparseVector ( featdim );
+        for ( int f = 0; f < featdim; f++ )
+        {
+          double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+          if ( val > 1e-10 )
+            ( *example.svec ) [f] = val;
+        }
+
+        example.svec->normalize();
+
+        example.position = imgnb;
+        if ( classnoTmp == positiveClass )
+          examples.push_back ( pair<int, Example> ( 1, example ) );
+        else
+          examples.push_back ( pair<int, Example> ( 0, example ) );
+      }
+    }
+ 
+    
+    
+
+    delete ce;
+    imgnb++;
+    pb.update ( trainp->count() );
+  }
+  
+    
+  numberOfClasses = classesInUse.size();
+  std::cerr << "numberOfClasses: " << numberOfClasses << std::endl;  
+  std::cerr << "classes in use: " << std::endl;
+  for (std::set<int>::const_iterator it = classesInUse.begin(); it != classesInUse.end(); it++)
+  {
+    std::cerr << *it << " : " <<  cn.text(*it) <<  " ";
+  }    
+  std::cerr << std::endl;
+
+  pb.hide();
+
+
+  //////////////////////
+  // train classifier //
+  //////////////////////
+  FeaturePool fp;
+
+  Feature *f = new SparseVectorFeature ( featdim );
+
+  f->explode ( fp );
+  delete f;
+
+  if ( classifier != NULL )
+  {
+    std::cerr << "train FP-classifier with " << examples.size() << " examples" << std::endl;
+    classifier->train ( fp, examples );
+    std::cerr << "training finished" << std::endl;
+  }
+  else
+  {
+    LabeledSetVector lvec;
+    convertExamplesToLSet ( examples, lvec );
+    vclassifier->teach ( lvec );
+//     if ( usegmm )
+//       convertLSetToSparseExamples ( examples, lvec );
+//     else
+    std::cerr << "classifierString: " << classifierString << std::endl;
+    if (this->classifierString.compare("nn") == 0)
+    {
+      convertLSetToExamples ( examples, lvec, true /* only remove pointers to the data in the LSet-struct*/);
+    }
+    else
+    {
+      convertLSetToExamples ( examples, lvec, false /* remove all training examples of the LSet-struct */);
+    }
+    vclassifier->finishTeaching();
+  }  
+
+  fp.destroy();
+
+  if ( save_classifier )
+  {
+    if ( classifier != NULL )
+      classifier->save ( cache + "/classifier.data" );
+    else
+      vclassifier->save ( cache + "/veccl.data" );    
+  }
+
+  ////////////
+  //clean up//
+  ////////////
+  for ( int i = 0; i < ( int ) examples.size(); i++ )
+  {
+    examples[i].second.clean();
+  }
+  examples.clear();
+
+  cerr << "SemSeg training finished" << endl;
+}
+
+
+void SemSegNoveltyBinary::semanticseg ( CachedExample *ce, NICE::Image & segresult, NICE::MultiChannelImageT<double> & probabilities )
+{  
+  Timer timer;
+  timer.start();
+  
+  //segResult contains the GT labels when this method is called
+  // we simply store them in labels, to have an easy access to the GT information lateron
+  Image labels = segresult;
+  //just to be sure that we do not have a GT-biased result :)
+  segresult.set(0);
+
+  int featdim = -1;
+
+  std::string currentFile = Globals::getCurrentImgFN();
+
+
+  int xsize, ysize;
+  ce->getImageSize ( xsize, ysize );
+
+  probabilities.reInit( xsize, ysize, 2);
+  probabilities.setAll ( 0.0 );
+   
+  NICE::ColorImage img;
+  try {
+    img = ColorImage ( currentFile );
+  } catch ( Exception ) {
+    cerr << "SemSegNoveltyBinary: error opening image file <" << currentFile << ">" << endl;
+    return;
+  }
+
+  MultiChannelImageT<double> feats;
+
+  // extract features
+  featExtract->getFeats ( img, feats );
+  featdim = feats.channels();
+  feats.addChannel(featdim);
+
+  for (int c = 0; c < featdim; c++)
+  {
+    ImageT<double> tmp = feats[c];
+    ImageT<double> tmp2 = feats[c+featdim];
+
+    NICE::FilterT<double, double, double>::gradientStrength (tmp, tmp2);
+  }
+  featdim += featdim;
+
+  // compute integral images
+  for ( int c = 0; c < featdim; c++ )
+  {
+    feats.calcIntegral ( c );
+  }
+  
+  timer.stop();
+  std::cout << "AL time for preparation: " << timer.getLastAbsolute() << std::endl;
+    
+  timer.start();
+  //classification results currently only needed to be computed separately if we use the vclassifier, i.e., the nearest neighbor used 
+  // for the "novel feature learning" approach
+  //in all other settings, such as active sem seg in general, we do this within the novelty-computation-methods
+  if ( classifier == NULL )
+  {
+    this->computeClassificationResults( feats, segresult, probabilities, xsize, ysize, featdim);
+  }
+//   timer.stop();
+//   
+//   std::cerr << "classification results computed" << std::endl;
+  
+  FloatImage noveltyImage ( xsize, ysize );
+  noveltyImage.set ( 0.0 );  
+  
+  switch (noveltyMethod)
+  {
+    case GPVARIANCE:
+    {
+         this->computeNoveltyByVariance( noveltyImage, feats, segresult, probabilities, xsize, ysize,  featdim );
+         break;
+    }
+    case GPUNCERTAINTY:
+    {
+         this->computeNoveltyByGPUncertainty( noveltyImage, feats, segresult, probabilities, xsize, ysize,  featdim );
+         break;         
+    }
+    case GPMINMEAN:
+    {
+         std::cerr << "compute novelty using the minimum mean" << std::endl;
+         this->computeNoveltyByGPMean( noveltyImage, feats, segresult, probabilities, xsize, ysize,  featdim );
+         break;         
+    }
+    case GPMEANRATIO:
+    {
+         this->computeNoveltyByGPMeanRatio( noveltyImage, feats, segresult, probabilities, xsize, ysize,  featdim );
+         break;         
+    }
+    case GPWEIGHTALL:
+    {
+         this->computeNoveltyByGPWeightAll( noveltyImage, feats, segresult, probabilities, xsize, ysize,  featdim );
+         break;         
+    }
+    case GPWEIGHTRATIO:
+    {
+         this->computeNoveltyByGPWeightRatio( noveltyImage, feats, segresult, probabilities, xsize, ysize,  featdim );
+         break;         
+    }    
+    case RANDOM:
+    {
+         this->computeNoveltyByRandom( noveltyImage, feats, segresult, probabilities, xsize, ysize,  featdim );
+         break;               
+    }
+    default:
+    {
+         //do nothing, keep the image constant to 0.0
+         break;
+    }
+         
+  }
+  
+  timer.stop();
+  std::cout << "AL time for novelty score computation: " << timer.getLastAbsolute() << std::endl;
+
+  if ( write_results || visualizeALimages )
+  {
+    ColorImage imgrgbTmp (xsize, ysize);
+    ICETools::convertToRGB ( noveltyImage, imgrgbTmp );  
+  
+    this->cn.labelToRGB( segresult, imgrgbTmp );  
+    
+    if ( write_results )
+    {
+      std::stringstream out;
+      std::vector< std::string > list2;
+      StringTools::split ( currentFile, '/', list2 );
+      out << resultdir << "/" << list2.back();
+//       std::cerr << "writing to " << out.str() + "_run_" +  NICE::intToString(this->iterationCountSuffix) + "_" + noveltyMethodString+"_unsmoothed.rawfloat" << std::endl;
+    
+      noveltyImage.writeRaw("run_" +  NICE::intToString(this->iterationCountSuffix) + "_" + out.str() + "_" + noveltyMethodString+"_unsmoothed.rawfloat");
+      
+    }
+    
+    if (visualizeALimages)
+    {
+        showImage(imgrgbTmp, "Novelty Image without Region Segmentation");       
+        showImage(imgrgbTmp, "Classification Result without Region Segmentation");        
+    }    
+  }
+
+  
+    
+  timer.start();
+  
+  //Regionen ermitteln
+  if(regionSeg != NULL)
+  {
+    NICE::Matrix mask;
+    int amountRegions = regionSeg->segRegions ( img, mask );
+    
+    //compute probs per region
+    std::vector<std::vector<double> > regionProb(amountRegions, std::vector<double>(probabilities.channels(),0.0));
+    std::vector<double> regionNoveltyMeasure (amountRegions, 0.0);
+
+    std::vector<int> regionCounter(amountRegions, 0);
+    std::vector<int> regionCounterNovelty(amountRegions, 0);
+    for ( int y = 0; y < ysize; y += trainWsize) //y++)
+    {
+      for (int x = 0; x < xsize; x += trainWsize) //x++)
+      {
+        int r = mask(x,y);
+        regionCounter[r]++;
+        for(int j = 0; j < probabilities.channels(); j++)
+        {
+          regionProb[r][j] += probabilities ( x, y, j );
+        }
+        
+        if ( forbidden_classesActiveLearning.find( labels(x,y) ) == forbidden_classesActiveLearning.end() )
+        {
+          //count the amount of "novelty" for the corresponding region
+          regionNoveltyMeasure[r] += noveltyImage(x,y);
+          regionCounterNovelty[r]++;
+        }
+      }
+    }
+       
+    //find best class per region
+    std::vector<int> bestClassPerRegion(amountRegions,0);
+    
+    double maxNoveltyScore = -numeric_limits<double>::max();
+    if (!mostNoveltyWithMaxScores)
+    {
+      maxNoveltyScore = numeric_limits<double>::max();
+    }   
+    
+    int maxUncertRegion = -1;
+    
+    //loop over all regions and compute averaged novelty scores
+    for(int r = 0; r < amountRegions; r++)
+    {
+      
+      //check for the most plausible class per region
+      double maxval = -numeric_limits<double>::max();
+      
+      //loop over all classes
+      for(int c = 0; c < probabilities.channels(); c++)
+      {
+        regionProb[r][c] /= regionCounter[r];
+        
+        if(  (maxval < regionProb[r][c]) ) //&& (regionProb[r][c] != 0.0) ) 
+        {        
+              maxval = regionProb[r][c];
+              bestClassPerRegion[r] = c;
+        }
+      }
+       
+      //if the region only contains unvalid information (e.g., background) skip it
+      if (regionCounterNovelty[r] == 0)
+      {
+        continue;
+      }
+      
+      //normalize summed novelty scores to region size
+      regionNoveltyMeasure[r] /= regionCounterNovelty[r];
+    
+      //did we find a region that has a higher score as the most novel region known so far within this image?
+      if(   (  mostNoveltyWithMaxScores && (maxNoveltyScore < regionNoveltyMeasure[r]) )    // if we look for large novelty scores, e.g., variance
+        || ( !mostNoveltyWithMaxScores && (maxNoveltyScore > regionNoveltyMeasure[r]) ) )  // if we look for small novelty scores, e.g., min mean
+      {
+                   //did we already query a region of this image?                --   and it was this specific region
+        if ( (queriedRegions.find( currentFile ) != queriedRegions.end() ) && ( queriedRegions[currentFile].find(r) != queriedRegions[currentFile].end() ) )
+        {
+          continue;
+        }
+        else //only accept the region as novel if we never queried it before
+        {
+          maxNoveltyScore = regionNoveltyMeasure[r];
+          maxUncertRegion = r;        
+        }
+
+      }
+
+    }
+    
+    // after finding the most novel region for the current image, check whether this region is also the most novel with respect
+    // to all previously seen test images
+    // if so, store the corresponding features, since we want to "actively" query them to incorporate useful information
+    if(findMaximumUncert)
+    {
+      if(    (   mostNoveltyWithMaxScores && (maxNoveltyScore > globalMaxUncert) )
+          || (  !mostNoveltyWithMaxScores && (maxNoveltyScore < globalMaxUncert) ) )
+      {
+        //current most novel region of the image has "higher" novelty score then previous most novel region of all test images worked on so far
+        // -> save new important features of this region
+        Examples examples;
+        for ( int y = 0; y < ysize; y += trainWsize )
+        {
+          for ( int x = 0; x < xsize; x += trainWsize)
+          {
+            if(mask(x,y) == maxUncertRegion)
+            {
+              int classnoTmp = labels(x,y);
+              if ( forbidden_classesActiveLearning.find(classnoTmp) != forbidden_classesActiveLearning.end() )
+                continue;
+              
+              Example example;
+              example.vec = NULL;
+              example.svec = new SparseVector ( featdim );
+              
+              for ( int f = 0; f < featdim; f++ )
+              {
+                double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+                if ( val > 1e-10 )
+                  ( *example.svec ) [f] = val;
+              }
+              example.svec->normalize();
+              if ( classnoTmp == positiveClass )
+                examples.push_back ( pair<int, Example> ( 1, example ) );
+              else
+                examples.push_back ( pair<int, Example> ( 0, example ) );
+            }
+          }
+        }
+        
+        if(examples.size() > 0)
+        {
+          std::cerr << "found " << examples.size() << " new examples in the queried region" << std::endl << std::endl;
+          newTrainExamples.clear();
+          newTrainExamples = examples;
+          globalMaxUncert = maxNoveltyScore;
+          //prepare for later visualization
+          visualizeRegion(img,mask,maxUncertRegion,maskedImg);
+        }
+        else
+        {
+          std::cerr << "the queried region has no valid information" << std::endl << std::endl;
+        }
+        
+        //save filename and region index
+        currentRegionToQuery.first = currentFile;
+        currentRegionToQuery.second = maxUncertRegion;
+      }
+    }
+
+    //write back best results per region
+    //i.e., write normalized novelty scores for every region into the novelty image
+    for ( int y = 0; y < ysize; y++)
+    {
+      for (int x = 0; x < xsize; x++)
+      {
+        int r = mask(x,y);
+        for(int j = 0; j < probabilities.channels(); j++)
+        {
+          probabilities ( x, y, j ) = regionProb[r][j];
+        }
+        if ( bestClassPerRegion[r] == 0 )
+          segresult(x,y) = positiveClass;
+        else //take the various class as negative
+          segresult(x,y) = 22; //bestClassPerRegion[r];
+        
+        // write novelty scores for every segment into the "final" image
+        noveltyImage(x,y) = regionNoveltyMeasure[r];
+      }
+    }
+    
+    //compute these nice Classification results
+    for ( int y = 0; y < ysize; y++)
+    {
+      for (int x = 0; x < xsize; x++)
+      {
+        OBJREC::FullVector scoresTmp (2);
+        scoresTmp[1] = probabilities ( x, y, 0 ); //probabilities[0] == negative class == scores[1]
+        scoresTmp[0] = probabilities ( x, y, 1 ); //probabilities[1] == positive class == scores[0]
+        
+        int cno = scoresTmp[1] > 0 ? 1 : 0;
+
+        ClassificationResult cr ( cno/*doesn't matter*/, scoresTmp );
+        
+        if ( labels(x,y) == positiveClass )
+          cr.classno_groundtruth = 1;
+        else
+          cr.classno_groundtruth = 0;
+        
+        resultsOfSingleRun.push_back(cr);        
+      }      
+    }
+  } // if regionSeg != null
+  
+  timer.stop();
+  std::cout << "AL time for determination of novel regions: " << timer.getLastAbsolute() << std::endl;
+
+  timer.start();
+
+  ColorImage imgrgb ( xsize, ysize );
+
+  if ( write_results )
+  {
+    std::stringstream out;
+    std::vector< std::string > list2;
+    StringTools::split ( currentFile, '/', list2 );
+    out << resultdir << "/" << list2.back();
+    
+    noveltyImage.writeRaw(out.str() + "_run_" +  NICE::intToString(this->iterationCountSuffix) + "_" + noveltyMethodString+".rawfloat");
+  }
+  
+  if (visualizeALimages)
+  {
+    ICETools::convertToRGB ( noveltyImage, imgrgb );
+    showImage(imgrgb, "Novelty Image");
+    
+    ColorImage tmp (xsize, ysize);
+    cn.labelToRGB(segresult,tmp);
+    showImage(tmp, "Cl result after region seg");    
+  }
+
+  timer.stop();
+  cout << "AL time for writing the raw novelty image: " << timer.getLastAbsolute() << endl;
+}
+
+inline void SemSegNoveltyBinary::computeClassificationResults( const NICE::MultiChannelImageT<double> & feats, 
+                                                   NICE::Image & segresult,
+                                                   NICE::MultiChannelImageT<double> & probabilities,
+                                                   const int & xsize,
+                                                   const int & ysize,
+                                                   const int & featdim
+                                                       )
+{
+  std::cerr << "featdim: " << featdim << std::endl;
+  
+  if ( classifier != NULL )
+  {  
+
+            
+    #pragma omp parallel for
+    for ( int y = 0; y < ysize; y += testWSize )
+    {
+      Example example;
+      example.vec = NULL;
+      example.svec = new SparseVector ( featdim );
+      for ( int x = 0; x < xsize; x += testWSize)
+      {
+        for ( int f = 0; f < featdim; f++ )
+        {
+          double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+          if ( val > 1e-10 )
+            ( *example.svec ) [f] = val;
+        }
+        example.svec->normalize();
+
+        ClassificationResult cr = classifier->classify ( example );
+
+        int xs = std::max(0, x - testWSize/2);
+        int xe = std::min(xsize - 1, x + testWSize/2);
+        int ys = std::max(0, y - testWSize/2);
+        int ye = std::min(ysize - 1, y + testWSize/2);
+        for (int yl = ys; yl <= ye; yl++)
+        {
+          for (int xl = xs; xl <= xe; xl++)
+          {
+            for ( int j = 0 ; j < cr.scores.size(); j++ )
+            { 
+              probabilities ( xl, yl, j ) = cr.scores[j];
+            }
+            
+            if ( cr.classno == 1 )
+              segresult ( xl, yl ) = positiveClass;
+            else
+              segresult ( xl, yl ) = 22; //various  
+          }
+        }
+        
+        example.svec->clear();
+      }
+      delete example.svec;
+      example.svec = NULL;
+    }
+  }
+  else //vclassifier
+  {
+    std::cerr << "compute classification results with vclassifier" << std::endl;
+    #pragma omp parallel for
+    for ( int y = 0; y < ysize; y += testWSize )
+    {
+      for ( int x = 0; x < xsize; x += testWSize)
+      {
+        NICE::Vector v(featdim);
+        for ( int f = 0; f < featdim; f++ )
+        {
+          double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+          v[f] = val;
+        }
+        v.normalizeL1();
+
+        ClassificationResult cr = vclassifier->classify ( v );
+
+        int xs = std::max(0, x - testWSize/2);
+        int xe = std::min(xsize - 1, x + testWSize/2);
+        int ys = std::max(0, y - testWSize/2);
+        int ye = std::min(ysize - 1, y + testWSize/2);
+        for (int yl = ys; yl <= ye; yl++)
+        {
+          for (int xl = xs; xl <= xe; xl++)
+          {
+            for ( int j = 0 ; j < cr.scores.size(); j++ )
+            { 
+              probabilities ( xl, yl, j ) = cr.scores[j];
+            }
+            
+            if ( cr.classno == 1 )
+              segresult ( xl, yl ) = positiveClass;
+            else
+              segresult ( xl, yl ) = 22; //various  
+          }
+        }
+      }
+    }    
+
+  }
+}
+
+// compute novelty images depending on the strategy chosen
+
+void SemSegNoveltyBinary::computeNoveltyByRandom(         NICE::FloatImage & noveltyImage, 
+                                              const NICE::MultiChannelImageT<double> & feats,  
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                             const int & xsize, const int & ysize, const int & featdim  )
+{
+#pragma omp parallel for
+  for ( int y = 0; y < ysize; y += testWSize )
+  {
+    Example example;
+    example.vec = NULL;
+    example.svec = new SparseVector ( featdim );
+    for ( int x = 0; x < xsize; x += testWSize)
+    {
+      for ( int f = 0; f < featdim; f++ )
+      {
+        double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+        if ( val > 1e-10 )
+          ( *example.svec ) [f] = val;
+      }
+      example.svec->normalize();
+
+      ClassificationResult cr = classifier->classify ( example );
+  
+      int xs = std::max(0, x - testWSize/2);
+      int xe = std::min(xsize - 1, x + testWSize/2);
+      int ys = std::max(0, y - testWSize/2);
+      int ye = std::min(ysize - 1, y + testWSize/2);
+      
+      double randVal = randDouble();
+
+      for (int yl = ys; yl <= ye; yl++)
+      {
+        for (int xl = xs; xl <= xe; xl++)
+        {
+            for ( int j = 0 ; j < cr.scores.size(); j++ )
+            { 
+              if ( cr.scores[j] == 1)
+                probabilities ( xl, yl, j ) = cr.scores[j];
+              else
+                probabilities ( xl, yl, 0 ) = cr.scores[j];
+            }
+            
+            if ( cr.classno == 1 )
+              segresult ( xl, yl ) = positiveClass;
+            else
+              segresult ( xl, yl ) = 22; //various  
+              
+          noveltyImage ( xl, yl ) = randVal; 
+        }
+      }     
+    }
+  }  
+}
+
+
+void SemSegNoveltyBinary::computeNoveltyByVariance(       NICE::FloatImage & noveltyImage, 
+                                              const NICE::MultiChannelImageT<double> & feats,  
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                             const int & xsize, const int & ysize, const int & featdim )
+{
+#pragma omp parallel for
+  for ( int y = 0; y < ysize; y += testWSize )
+  {
+    Example example;
+    example.vec = NULL;
+    example.svec = new SparseVector ( featdim );
+    for ( int x = 0; x < xsize; x += testWSize)
+    {
+      for ( int f = 0; f < featdim; f++ )
+      {
+        double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+        if ( val > 1e-10 )
+          ( *example.svec ) [f] = val;
+      }
+      example.svec->normalize();
+
+      ClassificationResult cr = classifier->classify ( example );
+      
+      int xs = std::max(0, x - testWSize/2);
+      int xe = std::min(xsize - 1, x + testWSize/2);
+      int ys = std::max(0, y - testWSize/2);
+      int ye = std::min(ysize - 1, y + testWSize/2);
+      for (int yl = ys; yl <= ye; yl++)
+      {
+        for (int xl = xs; xl <= xe; xl++)
+        {
+            for ( int j = 0 ; j < cr.scores.size(); j++ )
+            { 
+              if ( cr.scores[j] == 1)
+                probabilities ( xl, yl, j ) = cr.scores[j];
+              else
+                probabilities ( xl, yl, 0 ) = cr.scores[j];
+            }
+            
+            if ( cr.classno == 1 )
+              segresult ( xl, yl ) = positiveClass;
+            else
+              segresult ( xl, yl ) = 22; //various  
+              
+          noveltyImage ( xl, yl ) = cr.uncertainty; 
+        }
+      }          
+      
+      example.svec->clear();
+    }
+    delete example.svec;
+    example.svec = NULL;
+  }  
+}
+
+void SemSegNoveltyBinary::computeNoveltyByGPUncertainty(  NICE::FloatImage & noveltyImage, 
+                                              const NICE::MultiChannelImageT<double> & feats,  
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                             const int & xsize, const int & ysize, const int & featdim )
+{
+  
+  double gpNoise =  conf->gD("GPHIK", "noise", 0.01);
+  
+#pragma omp parallel for
+  for ( int y = 0; y < ysize; y += testWSize )
+  {
+    Example example;
+    example.vec = NULL;
+    example.svec = new SparseVector ( featdim );
+    for ( int x = 0; x < xsize; x += testWSize)
+    {
+      for ( int f = 0; f < featdim; f++ )
+      {
+        double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+        if ( val > 1e-10 )
+          ( *example.svec ) [f] = val;
+      }
+      example.svec->normalize();
+
+      ClassificationResult cr = classifier->classify ( example );
+           
+      double gpMeanVal = abs(cr.scores[0]);    //very specific to the binary setting  
+
+      double firstTerm (1.0 / sqrt(cr.uncertainty+gpNoise));
+      
+      //compute the heuristic GP-UNCERTAINTY, as proposed by Kapoor et al. in IJCV 2010
+      // GP-UNCERTAINTY : |mean| / sqrt(var^2 + gpnoise^2)
+      double gpUncertaintyVal = gpMeanVal*firstTerm; //firstTerm = 1.0 / sqrt(r.uncertainty+gpNoise))
+
+      int xs = std::max(0, x - testWSize/2);
+      int xe = std::min(xsize - 1, x + testWSize/2);
+      int ys = std::max(0, y - testWSize/2);
+      int ye = std::min(ysize - 1, y + testWSize/2);
+      for (int yl = ys; yl <= ye; yl++)
+      {
+        for (int xl = xs; xl <= xe; xl++)
+        {         
+            for ( int j = 0 ; j < cr.scores.size(); j++ )
+            { 
+              if ( cr.scores[j] == 1)
+                probabilities ( xl, yl, j ) = cr.scores[j];
+              else
+                probabilities ( xl, yl, 0 ) = cr.scores[j];
+            }
+            
+            if ( cr.classno == positiveClass )
+              segresult ( xl, yl ) = cr.classno;
+            else
+              segresult ( xl, yl ) = 22; //various        
+          noveltyImage ( xl, yl ) = gpUncertaintyVal;  
+        }
+      }   
+      
+      example.svec->clear();
+    }
+    delete example.svec;
+    example.svec = NULL;
+  }  
+}
+
+void SemSegNoveltyBinary::computeNoveltyByGPMean(  NICE::FloatImage & noveltyImage, 
+                                              const NICE::MultiChannelImageT<double> & feats,  
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                             const int & xsize, const int & ysize, const int & featdim )
+{
+  double gpNoise =  conf->gD("GPHIK", "noise", 0.01);  
+    
+#pragma omp parallel for
+  for ( int y = 0; y < ysize; y += testWSize )
+  {
+    Example example;
+    example.vec = NULL;
+    example.svec = new SparseVector ( featdim );
+    for ( int x = 0; x < xsize; x += testWSize)
+    {
+      for ( int f = 0; f < featdim; f++ )
+      {
+        double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+        if ( val > 1e-10 )
+          ( *example.svec ) [f] = val;
+      }
+      example.svec->normalize();
+
+      ClassificationResult cr = classifier->classify ( example );
+
+      double gpMeanVal = abs(cr.scores[0]);  //very specific to the binary setting  
+  
+      int xs = std::max(0, x - testWSize/2);
+      int xe = std::min(xsize - 1, x + testWSize/2);
+      int ys = std::max(0, y - testWSize/2);
+      int ye = std::min(ysize - 1, y + testWSize/2);
+      for (int yl = ys; yl <= ye; yl++)
+      {
+        for (int xl = xs; xl <= xe; xl++)
+        {
+            for ( int j = 0 ; j < cr.scores.size(); j++ )
+            { 
+              probabilities ( xl, yl, 0 ) = cr.scores[j];
+            }
+            
+            if ( cr.classno == 1 )
+              segresult ( xl, yl ) = positiveClass;
+            else
+              segresult ( xl, yl ) = 22; //various  
+              
+          noveltyImage ( xl, yl ) = gpMeanVal; 
+        }
+      }     
+    }
+  }  
+}
+
+void SemSegNoveltyBinary::computeNoveltyByGPMeanRatio(  NICE::FloatImage & noveltyImage, 
+                                              const NICE::MultiChannelImageT<double> & feats,  
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                             const int & xsize, const int & ysize, const int & featdim )
+{
+  double gpNoise =  conf->gD("GPHIK", "noise", 0.01);  
+  
+  //NOTE in binary settings, this is the same as the same as 2*mean  
+  
+#pragma omp parallel for
+  for ( int y = 0; y < ysize; y += testWSize )
+  {
+    Example example;
+    example.vec = NULL;
+    example.svec = new SparseVector ( featdim );
+    for ( int x = 0; x < xsize; x += testWSize)
+    {
+      for ( int f = 0; f < featdim; f++ )
+      {
+        double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+        if ( val > 1e-10 )
+          ( *example.svec ) [f] = val;
+      }
+      example.svec->normalize();
+
+      ClassificationResult cr = classifier->classify ( example );
+     
+      //look at the difference in the absolut mean values for the most plausible class
+      // and the second most plausible class
+      double gpMeanRatioVal= 2*abs(cr.scores[0]);  //very specific to the binary setting  
+
+
+      int xs = std::max(0, x - testWSize/2);
+      int xe = std::min(xsize - 1, x + testWSize/2);
+      int ys = std::max(0, y - testWSize/2);
+      int ye = std::min(ysize - 1, y + testWSize/2);
+      for (int yl = ys; yl <= ye; yl++)
+      {
+        for (int xl = xs; xl <= xe; xl++)
+        {
+            for ( int j = 0 ; j < cr.scores.size(); j++ )
+            { 
+              if ( cr.scores[j] == 1)
+                probabilities ( xl, yl, j ) = cr.scores[j];
+              else
+                probabilities ( xl, yl, 0 ) = cr.scores[j];
+            }
+            
+            if ( cr.classno == positiveClass )
+              segresult ( xl, yl ) = cr.classno;
+            else
+              segresult ( xl, yl ) = 22; //various      
+          noveltyImage ( xl, yl ) = gpMeanRatioVal;
+        }
+      }    
+      
+      example.svec->clear();
+    }
+    delete example.svec;
+    example.svec = NULL;
+  }  
+}
+
+void SemSegNoveltyBinary::computeNoveltyByGPWeightAll(  NICE::FloatImage & noveltyImage, 
+                                              const NICE::MultiChannelImageT<double> & feats,  
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                             const int & xsize, const int & ysize, const int & featdim )
+{
+  double gpNoise =  conf->gD("GPHIK", "noise", 0.01);  
+  
+#pragma omp parallel for
+  for ( int y = 0; y < ysize; y += testWSize )
+  {
+    Example example;
+    example.vec = NULL;
+    example.svec = new SparseVector ( featdim );
+    for ( int x = 0; x < xsize; x += testWSize)
+    {
+      for ( int f = 0; f < featdim; f++ )
+      {
+        double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+        if ( val > 1e-10 )
+          ( *example.svec ) [f] = val;
+      }
+      example.svec->normalize();
+
+      ClassificationResult cr = classifier->classify ( example );
+      
+      double firstTerm (1.0 / sqrt(cr.uncertainty+gpNoise));
+      
+      double gpWeightAllVal ( 0.0 );
+
+      //binary scenario
+      gpWeightAllVal = std::min( abs(cr.scores[0]+1), abs(cr.scores[0]-1) );
+      gpWeightAllVal *= firstTerm;
+
+      int xs = std::max(0, x - testWSize/2);
+      int xe = std::min(xsize - 1, x + testWSize/2);
+      int ys = std::max(0, y - testWSize/2);
+      int ye = std::min(ysize - 1, y + testWSize/2);
+      for (int yl = ys; yl <= ye; yl++)
+      {
+        for (int xl = xs; xl <= xe; xl++)
+        {
+            for ( int j = 0 ; j < cr.scores.size(); j++ )
+            { 
+              if ( cr.scores[j] == 1)
+                probabilities ( xl, yl, j ) = cr.scores[j];
+              else
+                probabilities ( xl, yl, 0 ) = cr.scores[j];
+            }
+            
+            if ( cr.classno == positiveClass )
+              segresult ( xl, yl ) = cr.classno;
+            else
+              segresult ( xl, yl ) = 22; //various         
+          noveltyImage ( xl, yl ) = gpWeightAllVal;
+        }
+      }
+   
+      
+      example.svec->clear();
+    }
+    delete example.svec;
+    example.svec = NULL;
+  }  
+}
+
+void SemSegNoveltyBinary::computeNoveltyByGPWeightRatio(  NICE::FloatImage & noveltyImage, 
+                                              const NICE::MultiChannelImageT<double> & feats,  
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                             const int & xsize, const int & ysize, const int & featdim )
+{
+  double gpNoise =  conf->gD("GPHIK", "noise", 0.01);  
+  
+  //NOTE in binary settings, this is the same as the same as 2*weightAll
+  
+#pragma omp parallel for
+  for ( int y = 0; y < ysize; y += testWSize )
+  {
+    Example example;
+    example.vec = NULL;
+    example.svec = new SparseVector ( featdim );
+    for ( int x = 0; x < xsize; x += testWSize)
+    {
+      for ( int f = 0; f < featdim; f++ )
+      {
+        double val = feats.getIntegralValue ( x - whs, y - whs, x + whs, y + whs, f );
+        if ( val > 1e-10 )
+          ( *example.svec ) [f] = val;
+      }
+      example.svec->normalize();
+
+      ClassificationResult cr = classifier->classify ( example );
+ 
+
+       double firstTerm (1.0 / sqrt(cr.uncertainty+gpNoise));
+
+       double gpWeightRatioVal ( 0.0 );
+
+      //binary scenario
+      gpWeightRatioVal = std::min( abs(cr.scores[0]+1), abs(cr.scores[0]-1) );
+      gpWeightRatioVal *= 2*firstTerm;
+
+      int xs = std::max(0, x - testWSize/2);
+      int xe = std::min(xsize - 1, x + testWSize/2);
+      int ys = std::max(0, y - testWSize/2);
+      int ye = std::min(ysize - 1, y + testWSize/2);
+      for (int yl = ys; yl <= ye; yl++)
+      {
+        for (int xl = xs; xl <= xe; xl++)
+        {
+            for ( int j = 0 ; j < cr.scores.size(); j++ )
+            { 
+              if ( cr.scores[j] == 1)
+                probabilities ( xl, yl, j ) = cr.scores[j];
+              else
+                probabilities ( xl, yl, 0 ) = cr.scores[j];
+            }
+            
+            if ( cr.classno == positiveClass )
+              segresult ( xl, yl ) = cr.classno;
+            else
+              segresult ( xl, yl ) = 22; //various         
+          noveltyImage ( xl, yl ) = gpWeightRatioVal;  
+        }
+      }
+       
+      example.svec->clear();
+    }
+    delete example.svec;
+    example.svec = NULL;
+  }  
+}
+
+
+void SemSegNoveltyBinary::addNewExample(const NICE::Vector& newExample, const int & newClassNo)
+{
+  //accept the new class as valid information
+  if ( forbidden_classesTrain.find ( newClassNo ) != forbidden_classesTrain.end() )
+  {
+    forbidden_classesTrain.erase(newClassNo);
+    numberOfClasses++;
+  }
+  if ( classesInUse.find ( newClassNo ) == classesInUse.end() )
+  {
+    classesInUse.insert( newClassNo );
+  }    
+  
+  
+  //then add it to the classifier used
+  if ( classifier != NULL )
+  { 
+    //TODO    
+  }
+  else //vclassifier
+  {
+    if (this->classifierString.compare("nn") == 0)    
+    {
+      vclassifier->teach ( newClassNo, newExample );
+    }
+  }
+}
+
+void SemSegNoveltyBinary::addNovelExamples()
+{
+
+  Timer timer;
+  
+  //show the image that contains the most novel region
+  if (visualizeALimages)
+    showImage(maskedImg, "Most novel region");  
+  
+  timer.start();
+    
+  std::stringstream out;
+  std::vector< std::string > list;
+  StringTools::split ( currentRegionToQuery.first, '/', list );  
+  out << resultdir << "/" << list.back();     
+    
+  maskedImg.writePPM ( out.str() + "_run_" +  NICE::intToString(this->iterationCountSuffix) + "_" + noveltyMethodString+ "_query.ppm" );
+
+  
+  timer.stop();
+  std::cerr << "AL time for writing queried image: " << timer.getLast() << std::endl;
+
+  timer.start();
+  
+  //check which classes will be added using the features from the novel region
+  std::set<int> newClassNumbers;
+  newClassNumbers.clear(); //just to be sure  
+  for ( uint i = 0 ; i < newTrainExamples.size() ; i++ )
+  {
+    if (newClassNumbers.find(newTrainExamples[i].first /* classNumber*/) == newClassNumbers.end() )
+    {
+      newClassNumbers.insert(newTrainExamples[i].first );
+    }
+  }
+
+  //accept the new classes as valid information
+  for (std::set<int>::const_iterator clNoIt = newClassNumbers.begin(); clNoIt != newClassNumbers.end(); clNoIt++)
+  {
+    if ( forbidden_classesTrain.find ( *clNoIt ) != forbidden_classesTrain.end() )
+    {
+      forbidden_classesTrain.erase(*clNoIt);
+      numberOfClasses++;
+    }
+    if ( classesInUse.find ( *clNoIt ) == classesInUse.end() )
+    {
+      classesInUse.insert( *clNoIt );
+    }
+  }
+  
+  timer.stop();
+  std::cerr << "AL time for accepting possible new classes: " << timer.getLast() << std::endl;
+  
+  timer.start();
+  //then add the new features to the classifier used
+  if ( classifier != NULL )
+  { 
+    if (this->classifierString.compare("ClassifierGPHIK") == 0)    
+    {
+      classifier->addMultipleExamples ( this->newTrainExamples );
+    }    
+  }
+  else //vclassifier
+  {
+    //TODO
+  }
+  
+  timer.stop();
+  std::cerr << "AL time for actually updating the classifier: " << timer.getLast() << std::endl;
+  
+  std::cerr << "the current region to query is: " << currentRegionToQuery.first << " -- " << currentRegionToQuery.second << std::endl;
+  
+  //did we already query a region of this image?
+  if ( queriedRegions.find( currentRegionToQuery.first ) != queriedRegions.end() )
+  {
+    queriedRegions[ currentRegionToQuery.first ].insert(currentRegionToQuery.second);
+  }
+  else
+  {
+    std::set<int> tmpSet; tmpSet.insert(currentRegionToQuery.second);
+    queriedRegions.insert(std::pair<std::string,std::set<int> > (currentRegionToQuery.first, tmpSet ) );
+  }  
+  
+  std::cerr << "Write already queried regions: " << std::endl;
+  for (std::map<std::string,std::set<int> >::const_iterator it = queriedRegions.begin(); it != queriedRegions.end(); it++)
+  {
+    std::cerr << "image: " << it->first << " --   ";
+    for (std::set<int>::const_iterator itReg = it->second.begin(); itReg != it->second.end(); itReg++)
+    {
+      std::cerr << *itReg << " ";
+    } 
+    std::cerr << std::endl;
+  }
+  
+  //clear the latest results, since one iteration is over
+  globalMaxUncert = -numeric_limits<double>::max();
+  if (!mostNoveltyWithMaxScores)
+    globalMaxUncert = numeric_limits<double>::max();
+}
+
+const Examples * SemSegNoveltyBinary::getNovelExamples() const
+{
+  return &(this->newTrainExamples);
+}
+
+
+double SemSegNoveltyBinary::getAUCPerformance() const
+{
+  std::cerr << "evaluate AUC performance" << std::endl;
+  int noGTPositives ( 0 );
+  int noGTNegatives ( 0 );
+  
+  for (std::vector<OBJREC::ClassificationResult>::const_iterator it = resultsOfSingleRun.begin(); it != resultsOfSingleRun.end(); it++)
+  {
+    if (it->classno_groundtruth == 1)
+    {
+       noGTPositives++;
+    }
+    else
+      noGTNegatives++;
+  }
+  
+  std::cerr << "GT positives: " << noGTPositives << " -- GT negatives: " << noGTNegatives << std::endl;
+  
+  std::cerr << "ARR: " << resultsOfSingleRun.getAverageRecognitionRate() << std::endl;
+  
+  return resultsOfSingleRun.getBinaryClassPerformance( ClassificationResults::PERF_AUC ); 
+}

+ 245 - 0
semseg/SemSegNoveltyBinary.h

@@ -0,0 +1,245 @@
+/**
+ * @file SemSegNoveltyBinary.h
+ * @brief semantic segmentation using the method from Csurka08
+ * @author Björn Fröhlich, Alexander Freytag
+ * @date 04/24/2009
+ */
+#ifndef SemSegNoveltyBinaryINCLUDE
+#define SemSegNoveltyBinaryINCLUDE
+
+#include "SemanticSegmentation.h"
+
+#include "SemSegTools.h"
+#include "vislearning/classifier/classifierbase/FeaturePoolClassifier.h"
+#include "vislearning/classifier/genericClassifierSelection.h"
+#include "vislearning/features/localfeatures/LFColorWeijer.h"
+#include "vislearning/cbaselib/ClassificationResults.h"
+
+#include "segmentation/RegionSegmentationMethod.h"
+
+
+/** @brief pixelwise labeling systems */
+
+namespace OBJREC {
+
+class SemSegNoveltyBinary : public SemanticSegmentation
+{
+
+  protected:
+    //! boolean whether to reuse segmentation results for single images in different runs
+    bool reuseSegmentation;
+
+    //! boolean whether to read the initial classifier from a file. If not, training will be performed
+    bool read_classifier;
+    
+    //! boolean whether to save the final classifier or not
+    bool save_classifier;
+
+    //! The cached Data
+    std::string cache;
+    
+    //! Classifier
+    FeaturePoolClassifier *classifier;
+    VecClassifier *vclassifier;
+    
+    //! feature extraction
+    LFColorWeijer *featExtract;
+    
+    //! Configuration File
+    const NICE::Config *conf;
+    
+    //! distance between features for training
+    int trainWsize;
+    
+    //! half of the window size for local features
+    int whs;
+    
+    //! rectangle size for classification, 1 means pixelwise
+    int testWSize;
+    
+    //! name of all classes
+    ClassNames cn;
+    
+    //! low level Segmentation method
+    RegionSegmentationMethod *regionSeg;
+    
+    //! set of forbidden/background classes for the initial training
+    std::set<int> forbidden_classesTrain;
+    //! set of forbidden/background classes for the whole process of learning over time
+    std::set<int> forbidden_classesActiveLearning;
+    //! store the class numbers currently used
+    std::set<int> classesInUse;
+    
+    //! only needed for binary scenarios, index of the positive class
+    int positiveClass;    
+    
+    //! obviously, the number of classes used for training (i.e., classesInUse.size() )
+    int numberOfClasses; 
+    
+    //! where to save the resulting images (uncertainty and classification results)
+    std::string resultdir;
+    
+    //! find the maximum uncertainty or not within the whole test set
+    bool findMaximumUncert;
+    
+    //! image with most uncertain region
+    NICE::ColorImage maskedImg;
+    
+    //! for debugging and visualization: show novelty images with and without region segmentation and the most novel region
+    bool visualizeALimages;
+    
+    //! maximum uncertainty over all images, i.e., the novelty score of the most "novel" region of all test images
+    double globalMaxUncert;
+    
+    //! determine whether a "novelty" method computes large scores for novel objects (e.g., variance), or small scores (e.g., min abs mean)
+    bool mostNoveltyWithMaxScores;
+    
+    //! current examples for most uncertain region
+    Examples newTrainExamples;
+    
+    //! contains filenames of images and indices of contained regions, that where already queried, to prevent them from being queried again
+    std::map<std::string,std::set<int> > queriedRegions;
+        
+    std::pair<std::string, int> currentRegionToQuery;
+    
+    //! store the binary classification results from a single run to evaluate them with AUC lateron
+    ClassificationResults resultsOfSingleRun;
+    
+    bool write_results;
+    
+    enum NoveltyMethod{
+      GPVARIANCE, // novel = large variance
+      GPUNCERTAINTY, //novel = small uncertainty (mean / var)
+      GPMINMEAN,  //novel = small mean
+      GPMEANRATIO,  //novel = small difference between mean of most plausible class and mean of snd
+                   //        most plausible class (not useful in binary settings)
+      GPWEIGHTALL, // novel = large weight in alpha vector after updating the model (can be predicted exactly)
+      GPWEIGHTRATIO, // novel = small difference between weights for alpha vectors with assumptions of GT label to be the most 
+                    //         plausible against the second most plausible class
+      RANDOM        // query regions randomly
+    }; 
+    
+    //! specify how "novelty" shall be computed, e.g., using GP-variance, GP-uncertainty, or predicted weight entries
+    NoveltyMethod noveltyMethod;
+    std::string noveltyMethodString;
+    
+    //! just store the name of our classifier
+    std::string classifierString;
+    
+    inline void computeClassificationResults( const NICE::MultiChannelImageT<double> & feats, 
+                                                    NICE::Image & segresult,
+                                                    NICE::MultiChannelImageT<double> & probabilities,
+                                                    const int & xsize,
+                                                    const int & ysize,
+                                                    const int & featdim );
+
+   void computeNoveltyByRandom(         NICE::FloatImage & noveltyImage, 
+                                  const NICE::MultiChannelImageT<double> & feats,  
+                                        NICE::Image & segresult,
+                                        NICE::MultiChannelImageT<double> & probabilities,
+                                  const int & xsize, const int & ysize, const int & featdim );    
+    
+   void computeNoveltyByVariance(       NICE::FloatImage & noveltyImage, 
+                                  const NICE::MultiChannelImageT<double> & feats,  
+                                        NICE::Image & segresult,
+                                        NICE::MultiChannelImageT<double> & probabilities,
+                                  const int & xsize, const int & ysize, const int & featdim );
+   
+   void computeNoveltyByGPUncertainty ( NICE::FloatImage & noveltyImage, 
+                                  const NICE::MultiChannelImageT<double> & feats,  
+                                        NICE::Image & segresult,
+                                        NICE::MultiChannelImageT<double> & probabilities,
+                                  const int & xsize, const int & ysize, const int & featdim );
+   
+   void computeNoveltyByGPMean        ( NICE::FloatImage & noveltyImage, 
+                                  const NICE::MultiChannelImageT<double> & feats,  
+                                        NICE::Image & segresult,
+                                        NICE::MultiChannelImageT<double> & probabilities,
+                                  const int & xsize, const int & ysize, const int & featdim );  
+   void computeNoveltyByGPMeanRatio   ( NICE::FloatImage & noveltyImage, 
+                                  const NICE::MultiChannelImageT<double> & feats,  
+                                        NICE::Image & segresult,
+                                        NICE::MultiChannelImageT<double> & probabilities,
+                                  const int & xsize, const int & ysize, const int & featdim );  
+   void computeNoveltyByGPWeightAll   ( NICE::FloatImage & noveltyImage, 
+                                  const NICE::MultiChannelImageT<double> & feats,  
+                                        NICE::Image & segresult,
+                                        NICE::MultiChannelImageT<double> & probabilities,
+                                  const int & xsize, const int & ysize, const int & featdim );  
+   void computeNoveltyByGPWeightRatio ( NICE::FloatImage & noveltyImage, 
+                                  const NICE::MultiChannelImageT<double> & feats,  
+                                        NICE::Image & segresult,
+                                        NICE::MultiChannelImageT<double> & probabilities,
+                                  const int & xsize, const int & ysize, const int & featdim );     
+   
+  public:
+
+    /** constructor
+      *  @param conf needs a configfile
+      *  @param md and a MultiDataset (contains images and other things)
+      */
+    SemSegNoveltyBinary ( const NICE::Config *conf, const MultiDataset *md );
+
+    /** simple destructor */
+    virtual ~SemSegNoveltyBinary();
+
+    /** The trainingstep
+      *  @param md and a MultiDataset (contains images and other things)
+      */
+    void train ( const MultiDataset *md );
+
+    /** The main procedure. Input: Image, Output: Segmented Image with pixelwise labeles and the probabilities
+      * @param ce image data
+      * @param segresult result of the semantic segmentation with a label for each pixel
+      * @param probabilities multi-channel image with one channel for each class and corresponding probabilities for each pixel
+      */
+    void semanticseg ( CachedExample *ce,
+                       NICE::Image & segresult,
+                       NICE::MultiChannelImageT<double> & probabilities );
+    
+    
+    /**
+     * @brief visualize a specific region in the original image
+     *
+     * @param img input image
+     * @param regions map of the regions
+     * @param region visualize this region
+     * @param outimage result
+     * @return void
+     **/
+    void visualizeRegion(const NICE::ColorImage &img, const NICE::Matrix &regions, int region, NICE::ColorImage &outimage);
+
+    /**
+     * @brief Add a new example to the known training data
+     *
+     * @param newExample (NICE::Vector) the feature vector of the new examples
+     * @param newClassNo (int) the corresponding GT class number
+     * @return void
+     **/    
+    void addNewExample(const NICE::Vector & newExample, const int & newClassNo);
+    
+    /**
+     * @brief Add those examples, which belong to the most novel region seen so far
+     *
+     * @return void
+     **/    
+    virtual void addNovelExamples();    
+
+    /**
+     * @brief Get a pointer to the examples extracted from the most novel region seen so far
+     *
+     * @return Examples *
+     **/        
+    virtual const Examples * getNovelExamples() const; 
+    
+    /**
+     * @brief Compute AUC scores from the results of the images computed so far
+     *
+     * @return double
+     **/       
+    double getAUCPerformance() const;
+};
+
+} //namespace
+
+#endif

+ 21 - 4
semseg/SemanticSegmentation.cpp

@@ -45,7 +45,7 @@ void SemanticSegmentation::convertLSetToSparseExamples ( Examples &examples, Lab
 #endif
 }
 
-void SemanticSegmentation::convertLSetToExamples ( Examples &examples, LabeledSetVector &lvec )
+void SemanticSegmentation::convertLSetToExamples ( Examples &examples, LabeledSetVector &lvec, const bool & removeOldDataPointer )
 {
 #ifdef DEBUG_PRINTS
   cout << "SemSegRegionBased::convertLSetToExamples starts" << endl;
@@ -59,7 +59,20 @@ void SemanticSegmentation::convertLSetToExamples ( Examples &examples, LabeledSe
       examples.push_back ( pair<int, Example> ( iter->first, ex ) );
     }
   }
-  lvec.clear();
+  
+  if (!removeOldDataPointer)
+  {
+    //NOTE this is only useful, if our classifier does NOT need the data explicitely
+    lvec.clear();
+  }
+  else
+  {
+    lvec.removePointersToDataWithoutDeletion();
+    //after setting all the pointers to NULL, we can savely clear the LSet without deleting the previously
+    //stored features, which might be needed somewhere else, e.g., in the VCNearestNeighbour
+    lvec.clear();
+  }
+
 #ifdef DEBUG_PRINTS
   cout << "SemSegRegionBased::convertLSetToExamples finished" << endl;
 #endif
@@ -83,7 +96,11 @@ void SemanticSegmentation::convertExamplesToLSet ( Examples &examples, LabeledSe
     {
       if ( examples[i].second.svec != NULL )
       {
-        throw ( "Transform SVEC to VEC not yet implemented" );
+        NICE::Vector v;
+        examples[i].second.svec->convertToVectorT(v);
+        lvec.add ( examples[i].first, v );
+        delete examples[i].second.svec;
+        examples[i].second.svec = NULL;        
       }
       else
       {
@@ -92,7 +109,7 @@ void SemanticSegmentation::convertExamplesToLSet ( Examples &examples, LabeledSe
     }
 
   }
-  examples.clear();
+  examples.clear();  
 #ifdef DEBUG_PRINTS
   cout << "SemSegRegionBased::convertExamplesToLSet finished" << endl;
 #endif

+ 36 - 1
semseg/SemanticSegmentation.h

@@ -14,6 +14,11 @@
 #include "vislearning/cbaselib/Example.h"
 
 
+#define ROADWORKSADD fthrow(NICE::Exception, "addNewExample(const NICE::Vector & newExample, const int & newClassNo): not yet implemented!");
+#define ROADWORKSADDNOVEL fthrow(NICE::Exception, "addNovelExamples(): not yet implemented!");
+#define ROADWORKSGETNOVEL fthrow(NICE::Exception, "getNovelExamples(): not yet implemented!");
+
+
 namespace OBJREC
 {
 
@@ -35,6 +40,8 @@ class SemanticSegmentation
 
     /** whether to load images with color information */
     int imagetype;
+    
+    int iterationCountSuffix;
 
   public:
 
@@ -65,7 +72,7 @@ class SemanticSegmentation
     void convertVVectorToExamples ( NICE::VVector &feats,OBJREC::Examples &examples, std::vector<int> &label );
     void convertExamplesToVVector ( NICE::VVector &feats,OBJREC::Examples &examples, std::vector<int> &label );
     void convertExamplesToLSet ( OBJREC::Examples &examples, OBJREC::LabeledSetVector &lvec );
-    void convertLSetToExamples ( OBJREC::Examples &examples, OBJREC::LabeledSetVector &lvec );
+    void convertLSetToExamples ( OBJREC::Examples &examples, OBJREC::LabeledSetVector &lvec, const bool & removeOldDataPointer=false );
     void convertLSetToSparseExamples ( OBJREC::Examples &examples, OBJREC::LabeledSetVector &lvec );
 
 
@@ -73,6 +80,34 @@ class SemanticSegmentation
     void semanticseg ( const std::string & filename,
                        NICE::Image & segresult,
                        NICE::MultiChannelImageT<double> & probabilities );
+    
+    virtual void addNewExample(const NICE::Vector & newExample, const int & newClassNo)
+    {
+      ROADWORKSADD;      
+    };
+    /**
+     * @brief Add those examples, which belong to the most novel region seen so far
+     *
+     * @return void
+     **/    
+    virtual void addNovelExamples()
+    {
+      ROADWORKSADDNOVEL;      
+    };    
+    
+    /**
+     * @brief Get a pointer to the examples extracted from the most novel region seen so far
+     *
+     * @return Examples *
+     **/        
+    virtual const Examples * getNovelExamples() const
+    {
+      ROADWORKSGETNOVEL;      
+    };        
+    
+    void setIterationCountSuffix( const int & _iterationCountSuffix) { iterationCountSuffix = _iterationCountSuffix; };
+    
+    
 
 };