00001 /*!@file TIGS/FeatureExtractor.C Base class for topdown feature extractors. */ 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/FeatureExtractor.C $ 00035 // $Id: FeatureExtractor.C 9412 2008-03-10 23:10:15Z farhan $ 00036 // 00037 00038 #ifndef TIGS_FEATUREEXTRACTOR_C_DEFINED 00039 #define TIGS_FEATUREEXTRACTOR_C_DEFINED 00040 00041 #include "TIGS/FeatureExtractor.H" 00042 00043 #include "Component/ModelOptionDef.H" 00044 #include "Image/CutPaste.H" 00045 #include "Image/MathOps.H" 00046 #include "Raster/Raster.H" 00047 #include "TIGS/TigsOpts.H" 00048 #include "Util/log.H" 00049 #include "rutz/error_context.h" 00050 #include "rutz/sfmt.h" 00051 #include "rutz/trace.h" 00052 00053 // Used by: FeatureExtractor 00054 static const ModelOptionDef OPT_CacheSavePrefix = 00055 { MODOPT_ARG_STRING, "CacheSavePrefix", &MOC_TIGS, OPTEXP_CORE, 00056 "Filename stem name for feature-extractor caches", 00057 "cache-save-prefix", '\0', "<string>", "" }; 00058 00059 namespace 00060 { 00061 template <class T> 00062 Image<T> asRow(const Image<T>& in) 00063 { 00064 GVX_TRACE(__PRETTY_FUNCTION__); 00065 return Image<T>(in.getArrayPtr(), in.getSize(), 1); 00066 } 00067 } 00068 00069 FeatureExtractor::FeatureExtractor(OptionManager& mgr, 00070 const std::string& name) 00071 : 00072 ModelComponent(mgr, name, name), 00073 itsCacheSavePrefix(&OPT_CacheSavePrefix, this), 00074 itsName(name), 00075 itsCache(), 00076 itsNumHits(0), 00077 itsCheckFrequency(100), 00078 itsPrevTime(SimTime::ZERO()) 00079 {} 00080 00081 FeatureExtractor::~FeatureExtractor() {} 00082 00083 void FeatureExtractor::start2() 00084 { 00085 this->load(itsCacheSavePrefix.getVal().c_str()); 00086 } 00087 00088 void FeatureExtractor::stop1() 00089 { 00090 this->save(itsCacheSavePrefix.getVal().c_str()); 00091 } 00092 00093 Image<float> FeatureExtractor::extract(const TigsInputFrame& fin) 00094 { 00095 GVX_TRACE(__PRETTY_FUNCTION__); 00096 00097 GVX_ERR_CONTEXT(rutz::sfmt("extracting features in FeatureExtractor %s", 00098 itsName.c_str())); 00099 00100 const bool isnewtime = (fin.t() > itsPrevTime); 00101 itsPrevTime = fin.t(); 00102 00103 if (!this->isCacheable()) 00104 return doExtract(fin); 00105 00106 const Digest<16> digest = fin.getHash();; 00107 00108 typedef std::map<Digest<16>, Image<float> > CacheType; 00109 00110 CacheType::iterator itr = itsCache.find(digest); 00111 00112 if (itr != itsCache.end()) 00113 { 00114 LINFO("cache hit in %s on md5 digest %s", 00115 itsName.c_str(), digest.asString().c_str()); 00116 00117 ++itsNumHits; 00118 00119 // periodically check things to make sure the cache is valid: 00120 if (isnewtime 00121 && 00122 !fin.isGhost() 00123 && 00124 (itsNumHits == 1 || 00125 (itsCheckFrequency > 0 00126 && itsNumHits % itsCheckFrequency == 0))) 00127 { 00128 const Image<float> f = doExtract(fin); 00129 00130 const Image<float> actual = asRow(f); 00131 const Image<float> expected = asRow((*itr).second); 00132 00133 if (!(actual == expected)) 00134 { 00135 LINFO("RMSerr(actual,expected)=%g", RMSerr(actual,expected)); 00136 LINFO("corrcoef(actual,expected)=%g", corrcoef(actual,expected)); 00137 00138 LFATAL("cache integrity check failed " 00139 "in %s after %d hits", 00140 itsName.c_str(), itsNumHits); 00141 } 00142 else 00143 LINFO("cache integrity check OK in %s after %d hits", 00144 itsName.c_str(), itsNumHits); 00145 } 00146 00147 return (*itr).second; 00148 } 00149 00150 // else... 00151 const Image<float> f = doExtract(fin); 00152 itsCache.insert(CacheType::value_type(digest, f)); 00153 return f; 00154 } 00155 00156 void FeatureExtractor::save(const char* pfx) const 00157 { 00158 GVX_TRACE(__PRETTY_FUNCTION__); 00159 if (!this->isCacheable()) 00160 { 00161 LINFO("%s not cacheable; save() skipped", itsName.c_str()); 00162 return; 00163 } 00164 00165 if (itsCache.size() == 0) 00166 { 00167 LINFO("%s has no cache entries; save() skipped", itsName.c_str()); 00168 return; 00169 } 00170 00171 const std::string dfile = sformat("%s-%s-digests.pgm", 00172 pfx, itsName.c_str()); 00173 const std::string ffile = sformat("%s-%s-features.pfm", 00174 pfx, itsName.c_str()); 00175 00176 if (Raster::fileExists(dfile)) 00177 { 00178 LINFO("%s cache file %s already exists; save() skipped", 00179 itsName.c_str(), dfile.c_str()); 00180 return; 00181 } 00182 00183 if (Raster::fileExists(ffile)) 00184 { 00185 LINFO("%s cache file %s already exists; save() skipped", 00186 itsName.c_str(), ffile.c_str()); 00187 return; 00188 } 00189 00190 ASSERT(itsCache.size() > 0); 00191 00192 Image<byte> digests(16, itsCache.size(), NO_INIT); 00193 Image<float> features((*(itsCache.begin())).second.getSize(), 00194 itsCache.size(), NO_INIT); 00195 00196 typedef std::map<Digest<16>, Image<float> > CacheType; 00197 00198 int row = 0; 00199 00200 for (CacheType::const_iterator 00201 itr = itsCache.begin(), stop = itsCache.end(); 00202 itr != stop; ++itr) 00203 { 00204 inplacePaste(digests, (*itr).first.asImage(), 00205 Point2D<int>(0, row)); 00206 00207 inplacePaste(features, asRow((*itr).second), 00208 Point2D<int>(0, row)); 00209 00210 ++row; 00211 } 00212 00213 ASSERT(size_t(row) == itsCache.size()); 00214 00215 Raster::WriteGray(digests, dfile, RASFMT_PNM); 00216 Raster::WriteFloat(features, FLOAT_NORM_PRESERVE, ffile, RASFMT_PFM); 00217 00218 LINFO("saved %d cache entries to %s", row, dfile.c_str()); 00219 LINFO("saved %d cache entries to %s", row, ffile.c_str()); 00220 } 00221 00222 void FeatureExtractor::load(const char* pfx) 00223 { 00224 GVX_TRACE(__PRETTY_FUNCTION__); 00225 if (!this->isCacheable()) 00226 { 00227 LINFO("%s not cacheable; load() skipped", itsName.c_str()); 00228 return; 00229 } 00230 00231 const std::string dfile = sformat("%s-%s-digests.pgm", 00232 pfx, itsName.c_str()); 00233 const std::string ffile = sformat("%s-%s-features.pfm", 00234 pfx, itsName.c_str()); 00235 00236 if (!Raster::fileExists(dfile)) 00237 { 00238 LINFO("%s cache file %s not found; load() skipped", 00239 itsName.c_str(), dfile.c_str()); 00240 return; 00241 } 00242 00243 if (!Raster::fileExists(ffile)) 00244 { 00245 LINFO("%s cache file %s not found; load() skipped", 00246 itsName.c_str(), ffile.c_str()); 00247 return; 00248 } 00249 00250 const Image<byte> digests = Raster::ReadGray(dfile, RASFMT_PNM); 00251 const Image<float> features = Raster::ReadFloat(ffile, RASFMT_PFM); 00252 00253 ASSERT(digests.getHeight() == features.getHeight()); 00254 00255 const int nrows = digests.getHeight(); 00256 00257 typedef std::map<Digest<16>, Image<float> > CacheType; 00258 00259 itsCache.clear(); 00260 00261 LINFO("digests=%dx%d", digests.getWidth(), digests.getHeight()); 00262 00263 for (int i = 0; i < nrows; ++i) 00264 { 00265 Digest<16> d = Digest<16>::asDigest(&digests[Point2D<int>(0, i)], 00266 digests.getWidth()); 00267 00268 Image<float> f(&features[Point2D<int>(0, i)], 00269 features.getWidth(), 1); 00270 00271 itsCache.insert(CacheType::value_type(d, f)); 00272 } 00273 00274 ASSERT(itsCache.size() == size_t(nrows)); 00275 00276 LINFO("loaded %d cache entries from %s", nrows, dfile.c_str()); 00277 LINFO("loaded %d cache entries from %s", nrows, ffile.c_str()); 00278 } 00279 00280 // ###################################################################### 00281 /* So things look consistent in everyone's emacs... */ 00282 /* Local Variables: */ 00283 /* mode: c++ */ 00284 /* indent-tabs-mode: nil */ 00285 /* End: */ 00286 00287 #endif // TIGS_FEATUREEXTRACTOR_C_DEFINED