00001 /*! 00002 \file Neuro/GistEstimatorSurfPMK.C 00003 00004 This file defines the member functions, static members, etc. of the 00005 GistEstimatorSurfPMK class. Further details are in the header file. 00006 */ 00007 00008 // //////////////////////////////////////////////////////////////////// // 00009 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00010 // by the University of Southern California (USC) and the iLab at USC. // 00011 // See http://iLab.usc.edu for information about this project. // 00012 // //////////////////////////////////////////////////////////////////// // 00013 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00014 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00015 // in Visual Environments, and Applications'' by Christof Koch and // 00016 // Laurent Itti, California Institute of Technology, 2001 (patent // 00017 // pending; application number 09/912,225 filed July 23, 2001; see // 00018 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00019 // //////////////////////////////////////////////////////////////////// // 00020 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00023 // redistribute it and/or modify it under the terms of the GNU General // 00024 // Public License as published by the Free Software Foundation; either // 00025 // version 2 of the License, or (at your option) any later version. // 00026 // // 00027 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00028 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00029 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00030 // PURPOSE. See the GNU General Public License for more details. // 00031 // // 00032 // You should have received a copy of the GNU General Public License // 00033 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00034 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00035 // Boston, MA 02111-1307 USA. // 00036 // //////////////////////////////////////////////////////////////////// // 00037 // 00038 // Primary maintainer for this file: Manu Viswanathan <mviswana at usc dot edu> 00039 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Neuro/GistEstimatorSurfPMK.C $ 00040 // $Id: GistEstimatorSurfPMK.C 13065 2010-03-28 00:01:00Z itti $ 00041 // 00042 00043 //--------------------------- LIBRARY CHECK ----------------------------- 00044 00045 #if !defined(INVT_HAVE_LIBSURF) || !defined(HAVE_OPENCV) 00046 00047 // Gist specific headers 00048 #include "Neuro/GistEstimatorSurfPMK.H" 00049 00050 // INVT utils 00051 #include "Util/log.H" 00052 00053 // Dummy class definition 00054 GistEstimatorSurfPMK:: 00055 GistEstimatorSurfPMK(OptionManager& mgr, 00056 const std::string& descrName, const std::string& tagName) 00057 : GistEstimatorAdapter(mgr, descrName, tagName) 00058 { 00059 LFATAL("Sorry, GistEstimatorSurfPMK requires OpenSURF and OpenCV") ; 00060 } 00061 00062 GistEstimatorSurfPMK::~GistEstimatorSurfPMK(){} 00063 00064 #else // the regular SURF-PMK gist estimator in all its hideous glory 00065 00066 //------------------------------ HEADERS -------------------------------- 00067 00068 #include "Image/OpenCVUtil.H" // must be first to avoid conflicting defs of int64, uint64 00069 00070 // Gist specific headers 00071 #include "Neuro/GistEstimatorSurfPMK.H" 00072 00073 // Other INVT headers 00074 #include "Neuro/VisualCortex.H" 00075 #include "Neuro/NeuroSimEvents.H" 00076 00077 #include "Simulation/SimEventQueue.H" 00078 00079 #include "Image/Kernels.H" 00080 #include "Image/Convolutions.H" 00081 #include "Image/MathOps.H" 00082 #include "Image/ShapeOps.H" 00083 #include "Image/CutPaste.H" 00084 #include "Image/Point2D.H" 00085 #include "Image/Dims.H" 00086 00087 #include "Util/Timer.H" 00088 00089 #include "nub/ref.h" 00090 #include "rutz/shared_ptr.h" 00091 00092 // Standard C++ headers 00093 #include <sstream> 00094 #include <numeric> 00095 #include <algorithm> 00096 #include <functional> 00097 #include <map> 00098 #include <list> 00099 #include <stdexcept> 00100 #include <utility> 00101 #include <limits> 00102 #include <ctime> 00103 00104 //------------------------------ DEFINES -------------------------------- 00105 00106 // Error message for exceptions when vocabulary is required but not 00107 // specified. 00108 #define GE_SPMK_ERR_NO_VOCABULARY \ 00109 "GistEstimatorSurfPMK requires vocabulary of " \ 00110 "prototypical SURF descriptors" 00111 00112 //----------------------------- TYPEDEFS -------------------------------- 00113 00114 // Some useful shortcuts 00115 typedef GistEstimatorSurfPMK SurfPMK ; 00116 typedef SurfPMK::Vocabulary Vocabulary ; 00117 typedef SurfPMK::SurfDescriptor SurfDescriptor ; 00118 typedef SurfPMK::SurfKeypoints SurfKeypoints ; 00119 00120 // Other frequently used types 00121 typedef float PixelType ; 00122 typedef Image<PixelType> ImageType ; 00123 typedef Image<double> GistVectorType ; 00124 00125 //-------------- STATIC DATA MEMBERS AND OTHER CONSTANTS ---------------- 00126 00127 const int GistEstimatorSurfPMK::GIST_VECTOR_SIZE = 200 ; // FIXME! 00128 00129 //-------------------------- INITIALIZATION ----------------------------- 00130 00131 GistEstimatorSurfPMK:: 00132 GistEstimatorSurfPMK(OptionManager& mgr, 00133 const std::string& descrName, 00134 const std::string& tagName) 00135 : GistEstimator(mgr, descrName, tagName), 00136 SIMCALLBACK_INIT(SimEventRetinaImage), 00137 itsTrainingHook(0) 00138 {} 00139 00140 // Quick helper to extract the r-th row of an Image 00141 static inline ImageType get_row(int r, const ImageType& I) 00142 { 00143 return crop(I, Point2D<int>(0, r), Dims(I.getWidth(), 1)) ; 00144 } 00145 00146 // The vocabulary is a list of SURF descriptors. But for convenience, 00147 // clients can pass it in as an image. The image must have 128 columns 00148 // (for the 128 values that make up a SURF descriptor) and will usually 00149 // have 200 rows (the size of the vocabulary). Thus, the dimensions of 00150 // the input image used to represent the SURF descriptor vocabulary in 00151 // client space will be 128x200. 00152 // 00153 // The following method simply converts this image into the list of SURF 00154 // descriptors used to represent the vocabulary internally by this class. 00155 void GistEstimatorSurfPMK::setVocabulary(const ImageType& V) 00156 { 00157 itsVocabulary.clear() ; 00158 00159 const int H = V.getHeight() ; 00160 for (int i = 0; i < H; ++i) 00161 //itsVocabulary.push_back(SurfDescriptor(get_row(i, V))) ; 00162 itsVocabulary.push_back(SurfDescriptor()) ; // FIXME! 00163 } 00164 00165 //----------------------------- CLEAN-UP -------------------------------- 00166 00167 GistEstimatorSurfPMK::~GistEstimatorSurfPMK(){} 00168 00169 //------------------ GIST FEATURE VECTOR COMPUTATION -------------------- 00170 00171 // Forward declarations 00172 static SurfKeypoints apply_surf_on_image(ImageType I) ; 00173 00174 // The processing method divides the current frame of the series of input 00175 // images into 16x16 pixel patches and computes the SURF descriptors for 00176 // each of these patches. Then, it either passes this grid of 00177 // descriptors back to its client (in training mode) or computes the 00178 // required gist vector using the supplied vocabulary of prototypical 00179 // descriptors (in normal operational mode). 00180 // 00181 // COMPLEXITY ANALYSIS: O(?) [? time] 00182 // --------------------------------------- 00183 // During normal operation (i.e., non-training mode), this function 00184 // calls apply_surf_on_patches() and flattened_multi_level_histogram(). 00185 // The time complexity latter is O(n). That of the former is not yet 00186 // known as it depends on how the SURF black box will work. 00187 // 00188 // The other operations in this function are all constant time or at 00189 // worst O(n), where n is the number of pixels in the input image. 00190 // Therefore, as it stands now, the overall complexity of this function 00191 // will be determined by that of the SURF black box; if that is O(n), 00192 // then so is this; if that is O(n^2), so is this; so on and so forth. 00193 void GistEstimatorSurfPMK:: 00194 onSimEventRetinaImage(SimEventQueue& q, rutz::shared_ptr<SimEventRetinaImage>& e) 00195 { 00196 Timer T ; 00197 ImageType current_frame = e->frame().grayFloat() ; 00198 SurfKeypoints surf_descriptors = apply_surf_on_image(current_frame) ; 00199 LINFO("MVN: %g seconds to compute SURF grid", T.getSecs()) ; 00200 if (itsTrainingHook) 00201 itsTrainingHook(surf_descriptors) ; 00202 else 00203 { 00204 if (itsVocabulary.empty()) 00205 throw std::runtime_error(GE_SPMK_ERR_NO_VOCABULARY) ; 00206 00207 T.reset() ; 00208 //itsGistVector = 00209 //flattened_multi_level_histogram(surf_descriptors, itsVocabulary) ; 00210 LINFO("MVN: %g seconds to compute %dx%d gist vector", T.getSecs(), 00211 itsGistVector.getHeight(), itsGistVector.getWidth()) ; 00212 } 00213 00214 rutz::shared_ptr<SimEventGistOutput> 00215 gist_output_event(new SimEventGistOutput(this, itsGistVector)) ; 00216 q.post(gist_output_event) ; 00217 } 00218 00219 //---------------------- INPUT IMAGE FILTERATION ------------------------ 00220 00221 // A quick helper to convert INVT images to IPL images and properly 00222 // release the IPL images when we're done with them. This is essentially 00223 // a convenience class whose constructor does the conversion and whose 00224 // destructor takes care of releasing the IPL data structures so that 00225 // client functions don't have to worry about these details. 00226 namespace { 00227 00228 class IplImg { 00229 IplImage* img ; 00230 public: 00231 IplImg(Image<float>&) ; 00232 ~IplImg() ; 00233 operator IplImage*() const {return img ;} // cast operator 00234 } ; 00235 00236 // Constructor to create float images 00237 IplImg::IplImg(Image<float>& I) 00238 : img(cvCreateImageHeader(cvSize(I.getWidth(), I.getHeight()), 00239 IPL_DEPTH_32F, 1)) 00240 { 00241 if (! img) 00242 throw std::runtime_error("IplImage init error") ; 00243 img->imageData = reinterpret_cast<char*>(I.getArrayPtr()) ; 00244 } 00245 00246 // Release the OpenCV resources created by constructor 00247 IplImg::~IplImg() 00248 { 00249 cvReleaseImageHeader(& img) ; 00250 } 00251 00252 } // end of local namespace encapsulating above helper 00253 00254 /* 00255 00256 COMPLEXITY ANALYSIS: O(?) [linear time] 00257 --------------------------------------- 00258 */ 00259 static SurfKeypoints apply_surf_on_image(ImageType I) 00260 { 00261 return opensurf::doSurf(IplImg(I)) ; 00262 } 00263 00264 //---------------------- HISTOGRAM COMPUTATIONS ------------------------- 00265 00266 namespace { 00267 00268 } // end of local namespace encapsulating histogram computations section 00269 00270 //---------------------- MISCELLANEOUS FUNCTIONS ------------------------ 00271 00272 // Stream I/O for SURF descriptors 00273 std::ostream& operator<<(std::ostream& os, const SurfDescriptor& d) 00274 { 00275 for (int i = 0; i < GistEstimatorSurfPMK::SURF_DESCRIPTOR_SIZE; ++i) 00276 os << d.descriptor[i] << ' ' ; 00277 return os ; 00278 } 00279 00280 //----------------------------------------------------------------------- 00281 00282 #endif // #if !defined(INVT_HAVE_LIBSURF) || !defined(HAVE_OPENCV) 00283 00284 /* So things look consistent in everyone's emacs... */ 00285 /* Local Variables: */ 00286 /* indent-tabs-mode: nil */ 00287 /* End: */