00001 /*!@file TIGS/Scorer.C Score the fit between predicted and actual eye positions */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00005 // by the University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Rob Peters <rjpeters at usc dot edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/TIGS/Scorer.C $ 00035 // $Id: Scorer.C 9412 2008-03-10 23:10:15Z farhan $ 00036 // 00037 00038 #ifndef TIGS_SCORER_C_DEFINED 00039 #define TIGS_SCORER_C_DEFINED 00040 00041 #include "TIGS/Scorer.H" 00042 00043 #include "Image/MathOps.H" 00044 #include "Util/MathFunctions.H" // for clampValue() 00045 #include "Util/sformat.H" 00046 #include "rutz/trace.h" 00047 00048 #include <ostream> 00049 00050 Scorer::~Scorer() {} 00051 00052 KLScorer::KLScorer(int nbins, int nrand) 00053 : 00054 itsNbins(nbins), 00055 itsNrand(nrand), 00056 itsObservedBins(1, nbins, ZEROS), 00057 itsRandomBins(nrand, nbins, ZEROS), 00058 itsNtrials(0) 00059 {} 00060 00061 KLScorer::~KLScorer() {} 00062 00063 void KLScorer::accum(const Image<float>& eyeposmap, int pos) 00064 { 00065 GVX_TRACE(__PRETTY_FUNCTION__); 00066 00067 float mi, ma; getMinMax(eyeposmap, mi, ma); 00068 00069 // rescale into the range [0..nbins] 00070 Image<float> scaledmap = eyeposmap - mi; 00071 if ((ma-mi) > 0.0f) 00072 scaledmap /= ((ma-mi) / itsNbins); 00073 00074 ASSERT(pos >= 0); 00075 ASSERT(pos < scaledmap.getSize()); 00076 const int humanval = clampValue(int(scaledmap[pos]), 0, itsNbins - 1); 00077 00078 ASSERT(humanval >= 0); 00079 ASSERT(humanval < itsObservedBins.getSize()); 00080 itsObservedBins[humanval] += 1; 00081 00082 for (int i = 0; i < itsNrand; ++i) 00083 { 00084 const int randpos = theirGenerator.idraw(scaledmap.getSize()); 00085 00086 ASSERT(randpos >= 0); 00087 ASSERT(randpos < scaledmap.getSize()); 00088 const int randval = clampValue(int(scaledmap[randpos]), 0, itsNbins - 1); 00089 00090 ASSERT(i >= 0); 00091 ASSERT(i < itsRandomBins.getWidth()); 00092 ASSERT(randval >= 0); 00093 ASSERT(randval < itsRandomBins.getHeight()); 00094 itsRandomBins[Point2D<int>(i, randval)] += 1; 00095 } 00096 00097 ++itsNtrials; 00098 } 00099 00100 std::string KLScorer::getScoreString(const std::string& name) 00101 { 00102 if (itsNtrials == 0) 00103 { 00104 return sformat("[%s] no klscore observations", name.c_str()); 00105 } 00106 00107 Image<double> scores(itsNrand, 1, ZEROS); 00108 00109 // count how many zeros we encounter in the histograms; we only 00110 // have a "non-tainted" kl score if there are no zeros 00111 int tainted = 0; 00112 00113 for (int i = 0; i < itsNrand; ++i) 00114 { 00115 double currentscore = 0.0; 00116 00117 for (int j = 0; j < itsNbins; ++j) 00118 { 00119 ASSERT(j < itsObservedBins.getSize()); 00120 const double aa = itsObservedBins[j] / double(itsNtrials); 00121 ASSERT(i < itsRandomBins.getWidth()); 00122 ASSERT(j < itsRandomBins.getHeight()); 00123 const double bb = itsRandomBins[Point2D<int>(i,j)] / double(itsNtrials); 00124 00125 if (aa != 0.0 && bb != 0.0) 00126 { 00127 currentscore += 0.5 * (aa*log(aa/bb) + bb*log(bb/aa)); 00128 } 00129 else 00130 { 00131 ++tainted; 00132 } 00133 } 00134 00135 ASSERT(i < scores.getSize()); 00136 scores[i] = currentscore; 00137 } 00138 00139 const double klmean = mean(scores); 00140 const double klstdev = stdev(scores); 00141 00142 return sformat("[%s] %s (nt=%d) klscore: %10.5f +/- %10.5f", 00143 name.c_str(), tainted > 0 ? "tainted" : "notaint", 00144 tainted, klmean, klstdev); 00145 } 00146 00147 rutz::urand KLScorer::theirGenerator(time((time_t*)0)+getpid()); 00148 00149 NssScorer::NssScorer() 00150 : 00151 currentZscore(0.0), 00152 observedZscore(0.0), 00153 maxZscore(0.0), 00154 observedCount(0) 00155 {} 00156 00157 void NssScorer::accum(const Image<float>& eyeposmap, int pos) 00158 { 00159 GVX_TRACE(__PRETTY_FUNCTION__); 00160 00161 float mi, ma, me; getMinMaxAvg(eyeposmap, mi, ma, me); 00162 00163 float std = stdev(eyeposmap); 00164 00165 if (std > 0.0) 00166 { 00167 currentZscore = (eyeposmap[pos] - me) / std; 00168 observedZscore += currentZscore; 00169 maxZscore += (ma - me) / std; 00170 } 00171 else 00172 { 00173 currentZscore = 0.0; 00174 } 00175 00176 ++observedCount; 00177 } 00178 00179 std::string NssScorer::getScoreString(const std::string& name) 00180 { 00181 if (observedCount > 0) 00182 return sformat("[%s] observed zscore: %10.5f max zscore: %10.5f", 00183 name.c_str(), observedZscore / observedCount, 00184 maxZscore / observedCount); 00185 // else... 00186 return sformat("[%s] no zscore observations", name.c_str()); 00187 } 00188 00189 SwpeScorer::SwpeScorer() 00190 : 00191 itsDims(), 00192 itsEyeScore(0.0), 00193 itsRandEyeScore(0.0), 00194 itsRandMapScore(0.0), 00195 itsObservedCount(0), 00196 itsGenerator(time((time_t*)0)+getpid()) 00197 {} 00198 00199 namespace 00200 { 00201 double getSwpe(const Image<float>& smap, const Point2D<int>& pos) 00202 { 00203 const int w = smap.getWidth(); 00204 const int h = smap.getHeight(); 00205 00206 double result = 0.0; 00207 double mapsum = 0.0; 00208 00209 for (int y = 0; y < h; ++y) 00210 for (int x = 0; x < w; ++x) 00211 { 00212 const double dist = 00213 sqrt((double(x-pos.i) * double(x-pos.i)) + 00214 (double(y-pos.j) * double(y-pos.j))); 00215 00216 const double mapval = smap.getVal(x,y); 00217 00218 result += mapval * dist; 00219 mapsum += mapval; 00220 } 00221 00222 return (result / mapsum); 00223 } 00224 } 00225 00226 void SwpeScorer::accum(const Image<float>& eyeposmap, int pos) 00227 { 00228 GVX_TRACE(__PRETTY_FUNCTION__); 00229 00230 if (itsDims.isEmpty()) 00231 { 00232 itsDims = eyeposmap.getDims(); 00233 } 00234 else 00235 { 00236 if (itsDims != eyeposmap.getDims()) 00237 LFATAL("wrong eyeposmap dims (expected %dx%d, got %dx%d)", 00238 itsDims.w(), itsDims.h(), 00239 eyeposmap.getWidth(), eyeposmap.getHeight()); 00240 } 00241 00242 const Point2D<int> eye(pos % eyeposmap.getWidth(), 00243 pos / eyeposmap.getWidth()); 00244 00245 const Point2D<int> randpos(itsGenerator.idraw_range(0, eyeposmap.getWidth()), 00246 itsGenerator.idraw_range(0, eyeposmap.getHeight())); 00247 00248 Image<float> randmap(eyeposmap.getDims(), NO_INIT); 00249 for (Image<float>::iterator itr = randmap.beginw(), stop = randmap.endw(); 00250 itr != stop; ++itr) 00251 *itr = itsGenerator.fdraw(); 00252 00253 Image<float> flatmap(eyeposmap.getDims(), NO_INIT); 00254 flatmap.clear(1.0f); 00255 00256 itsEyeScore += getSwpe(eyeposmap, eye); 00257 itsRandEyeScore += getSwpe(eyeposmap, randpos); 00258 itsRandMapScore += getSwpe(randmap, eye); 00259 itsFlatMapScore += getSwpe(flatmap, eye); 00260 00261 ++itsObservedCount; 00262 } 00263 00264 std::string SwpeScorer::getScoreString(const std::string& name) 00265 { 00266 if (itsObservedCount > 0) 00267 return sformat("[%s] swpe (dims %dx%d) " 00268 "eye@smap: %10.5f " 00269 "eye@randmap: %10.5f " 00270 "eye@flatmap: %10.5f " 00271 "rand@smap: %10.5f ", 00272 name.c_str(), 00273 itsDims.w(), itsDims.h(), 00274 itsEyeScore / itsObservedCount, 00275 itsRandMapScore / itsObservedCount, 00276 itsFlatMapScore / itsObservedCount, 00277 itsRandEyeScore / itsObservedCount); 00278 00279 // else... 00280 return sformat("[%s] swpe no data", name.c_str()); 00281 } 00282 00283 PercentileScorer::PercentileScorer() 00284 : 00285 currentPrctile(0.0), 00286 observedPrctile(0.0), 00287 observedCount(0) 00288 {} 00289 00290 void PercentileScorer::accum(const Image<float>& eyeposmap, int pos) 00291 { 00292 GVX_TRACE(__PRETTY_FUNCTION__); 00293 const float observed = eyeposmap[pos]; 00294 00295 int nless = 0, nequal = 0; 00296 for (int i = 0; i < eyeposmap.getSize(); ++i) 00297 { 00298 if (eyeposmap[i] < observed) ++nless; 00299 else if (eyeposmap[i] == observed) ++nequal; 00300 } 00301 00302 ASSERT(eyeposmap.getSize() > 0); 00303 00304 currentPrctile = 00305 (nless + nequal/2.0)/double(eyeposmap.getSize()); 00306 00307 observedPrctile += currentPrctile; 00308 00309 ++observedCount; 00310 } 00311 00312 std::string PercentileScorer::getScoreString(const std::string& name) 00313 { 00314 if (observedCount > 0) 00315 return sformat("[%s] current prctile: %10.5f overall prctile: %10.5f", 00316 name.c_str(), currentPrctile, 00317 observedPrctile / observedCount); 00318 // else... 00319 return sformat("[%s] no prctile observations", name.c_str()); 00320 } 00321 00322 MulticastScorer::MulticastScorer() 00323 : 00324 observedCount(0), 00325 itsPrctileScorer(), 00326 itsNssScorer(), 00327 itsKLScorer(10, 100), 00328 itsSwpeScorer() 00329 {} 00330 00331 void MulticastScorer::score(const std::string& name, 00332 const Image<float>& eyeposmap, int pos) 00333 { 00334 ++observedCount; 00335 00336 itsPrctileScorer.accum(eyeposmap, pos); 00337 itsNssScorer.accum(eyeposmap, pos); 00338 itsKLScorer.accum(eyeposmap, pos); 00339 itsSwpeScorer.accum(eyeposmap, pos); 00340 00341 if (observedCount % 100 == 1) 00342 this->showScore(name); 00343 } 00344 00345 void MulticastScorer::showScore(const std::string& name) 00346 { 00347 if (observedCount > 0) 00348 { 00349 LINFO("%s", itsPrctileScorer.getScoreString(name).c_str()); 00350 LINFO("%s", itsNssScorer.getScoreString(name).c_str()); 00351 LINFO("%s", itsKLScorer.getScoreString(name).c_str()); 00352 LINFO("%s", itsSwpeScorer.getScoreString(name).c_str()); 00353 } 00354 } 00355 00356 void MulticastScorer::writeScore(const std::string& name, 00357 std::ostream& os) 00358 { 00359 if (observedCount > 0) 00360 { 00361 os << itsPrctileScorer.getScoreString(name) << std::endl; 00362 os << itsNssScorer.getScoreString(name) << std::endl; 00363 os << itsKLScorer.getScoreString(name) << std::endl; 00364 os << itsSwpeScorer.getScoreString(name) << std::endl; 00365 } 00366 } 00367 00368 // ###################################################################### 00369 /* So things look consistent in everyone's emacs... */ 00370 /* Local Variables: */ 00371 /* mode: c++ */ 00372 /* indent-tabs-mode: nil */ 00373 /* End: */ 00374 00375 #endif // TIGS_SCORER_C_DEFINED