#include #include #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" #include "SemSegNovelty.h" using namespace std; using namespace NICE; using namespace OBJREC; SemSegNovelty::SemSegNovelty ( const Config *conf, const MultiDataset *md ) : SemanticSegmentation ( conf, & ( md->getClassNames ( "train" ) ) ) { this->conf = conf; globalMaxUncert = -numeric_limits::max(); string section = "SemSegNovelty"; featExtract = new LocalFeatureColorWeijer ( 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, "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" ); } 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::max(); } else if (noveltyMethodString.compare("gp-mean") == 0) //novel = small mean { this->noveltyMethod = GPMINMEAN; this->mostNoveltyWithMaxScores = false; globalMaxUncert = numeric_limits::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::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::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) { // 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 ®ions, int region, NICE::ColorImage &outimage) { std::vector 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 SemSegNovelty::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 ); 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 << "SemSegNovelty: error opening image file <" << currentFile << ">" << endl; continue; } Globals::setCurrentImgFN ( currentFile ); MultiChannelImageT feats; // extract features featExtract->getFeats ( img, feats ); featdim = feats.channels(); feats.addChannel(featdim); for (int c = 0; c < featdim; c++) { ImageT tmp = feats[c]; ImageT tmp2 = feats[c+featdim]; NICE::FilterT::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; examples.push_back ( pair ( classnoTmp, 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::const_iterator it = classesInUse.begin(); it != classesInUse.end(); it++) { std::cerr << *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 SemSegNovelty::semanticseg ( CachedExample *ce, NICE::Image & segresult, NICE::MultiChannelImageT & 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, cn.getMaxClassno() + 1); probabilities.setAll ( 0.0 ); NICE::ColorImage img; try { img = ColorImage ( currentFile ); } catch ( Exception ) { cerr << "SemSegNovelty: error opening image file <" << currentFile << ">" << endl; return; } MultiChannelImageT feats; // extract features featExtract->getFeats ( img, feats ); featdim = feats.channels(); feats.addChannel(featdim); for (int c = 0; c < featdim; c++) { ImageT tmp = feats[c]; ImageT tmp2 = feats[c+featdim]; NICE::FilterT::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 (visualizeALimages) { ColorImage imgrgbTmp (xsize, ysize); ICETools::convertToRGB ( noveltyImage, imgrgbTmp ); showImage(imgrgbTmp, "Novelty Image 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 > regionProb(amountRegions, std::vector(probabilities.channels(),0.0)); std::vector regionNoveltyMeasure (amountRegions, 0.0); std::vector regionCounter(amountRegions, 0); std::vector 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 bestClassPerRegion(amountRegions,0); double maxNoveltyScore = -numeric_limits::max(); if (!mostNoveltyWithMaxScores) { maxNoveltyScore = numeric_limits::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::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(NULL, x, y); 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(); examples.push_back ( pair ( classnoTmp, 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 // 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++) { int r = mask(x,y); for(int j = 0; j < probabilities.channels(); j++) { 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(); 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 ); std::stringstream out; std::vector< std::string > list2; StringTools::split ( Globals::getCurrentImgFN (), '/', 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"); } timer.stop(); cout << "AL time for writing the raw novelty image: " << timer.getLastAbsolute() << endl; } inline void SemSegNovelty::computeClassificationResults( const NICE::MultiChannelImageT & feats, NICE::Image & segresult, NICE::MultiChannelImageT & 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 & feats, NICE::Image & segresult, NICE::MultiChannelImageT & 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 & feats, NICE::Image & segresult, NICE::MultiChannelImageT & 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 & feats, NICE::Image & segresult, NICE::MultiChannelImageT & 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 & feats, NICE::Image & segresult, NICE::MultiChannelImageT & 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::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 & feats, NICE::Image & segresult, NICE::MultiChannelImageT & 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::max() ); double sndMaxMean ( -numeric_limits::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 & feats, NICE::Image & segresult, NICE::MultiChannelImageT & 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 diffToPositive; diffToPositive.clear(); std::vector 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::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 & feats, NICE::Image & segresult, NICE::MultiChannelImageT & 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 diffToPositive; diffToPositive.clear(); std::vector 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 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::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 tmpSet; tmpSet.insert(currentRegionToQuery.second); queriedRegions.insert(std::pair > (currentRegionToQuery.first, tmpSet ) ); } std::cerr << "Write already queried regions: " << std::endl; for (std::map >::const_iterator it = queriedRegions.begin(); it != queriedRegions.end(); it++) { std::cerr << "image: " << it->first << " -- "; for (std::set::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::max(); if (!mostNoveltyWithMaxScores) globalMaxUncert = numeric_limits::max(); } const Examples * SemSegNovelty::getNovelExamples() const { return &(this->newTrainExamples); }