00001 /*!@file CUDA/CudaHmax.C Riesenhuber & Poggio's HMAX model for object recognition */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // 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: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/CUDA/CudaHmax.C $ 00035 // $Id: CudaHmax.C 12962 2010-03-06 02:13:53Z irock $ 00036 // 00037 00038 #include "CUDA/CudaHmax.H" 00039 00040 #include "CUDA/CudaFilterOps.H" 00041 #include "CUDA/CudaImage.H" 00042 //#include "CUDA/CudaKernels.H" // for cudaDogFilter() 00043 #include "Image/Image.H" 00044 #include "Image/Kernels.H" // for dogFilter() 00045 #include "CUDA/CudaMathOps.H" 00046 #include "CUDA/CudaConvolutions.H" // for cudaConvolve() etc. 00047 #include "Image/MathOps.H" 00048 00049 #include "Util/MathFunctions.H" 00050 #include "Util/Types.H" 00051 #include "Util/log.H" 00052 #include "Util/safecopy.H" 00053 00054 #include <cmath> 00055 #include <fstream> 00056 #include <iostream> 00057 #include <iomanip> 00058 #include <cstdio> 00059 #include <cstdlib> 00060 #include <limits> 00061 00062 // ###################################################################### 00063 CudaHmax::CudaHmax() 00064 { initialized = false; } 00065 00066 // ###################################################################### 00067 CudaHmax::CudaHmax(MemoryPolicy mp, int dev, const int nori, const std::vector<int>& spacess, 00068 const std::vector<int>& scaless, const int c1spaceol, 00069 const bool angleflag, const float s2t, const float s2s, 00070 const float stdmin, const float stdstep, 00071 const int fsmin, const int fsstep) 00072 { 00073 initialized = false; 00074 init(mp,dev,nori, spacess, scaless, c1spaceol, angleflag, s2t, s2s); 00075 initFilters(stdmin,stdstep,fsmin,fsstep); 00076 } 00077 00078 // ###################################################################### 00079 void CudaHmax::init(MemoryPolicy mp, int dev, const int nori, const std::vector<int>& spacess, 00080 const std::vector<int>& scaless, const int c1spaceol, 00081 const bool angleflag, const float s2t, const float s2s) 00082 { 00083 itsMp = mp; 00084 itsDev = dev; 00085 freeMem(); initialized = true; ns = scaless[scaless.size() - 1]; no = nori; 00086 c1SpaceOL = c1spaceol; angleFlag = angleflag; s2Target = s2t; s2Sigma = s2s; 00087 spaceSS.resize(spacess.size()); scaleSS.resize(scaless.size()); 00088 00089 // detrmine number of scale bands from length of vector scaleSS: 00090 nsb = scaleSS.size() - 1; 00091 00092 for (unsigned int i = 0; i < spacess.size(); i ++) spaceSS[i] = spacess[i]; 00093 for (unsigned int i = 0; i < scaless.size(); i ++) scaleSS[i] = scaless[i]; 00094 00095 } 00096 00097 void CudaHmax::initFilters(const float stdmin, const float stdstep, const int fsmin, const int fsstep) 00098 { 00099 // create the filters: 00100 typedef CudaImage<float>* FloatImagePtr; 00101 filter = new FloatImagePtr[ns]; 00102 for(int s = 0; s < ns; s++) 00103 { 00104 filter[s] = new CudaImage<float>[no]; 00105 for(int o = 0; o < no; o ++) 00106 { 00107 // create DoG filter: 00108 Image<float> tmp = dogFilter<float>(stdmin + stdstep * s, 00109 (float)o * 180.0F / (float)no, 00110 fsmin + fsstep * s); 00111 filter[s][o] = CudaImage<float>(tmp,itsMp,itsDev); 00112 // filter[s][o] = cudaDogFilter(itsMp,itsDev,stdmin + stdstep * s, 00113 // (float)o * 180.0F / (float)no, 00114 // fsmin + fsstep * s); 00115 // normalize to zero mean: 00116 filter[s][o] -= cudaGetAvg(filter[s][o]); 00117 00118 // normalize to unit of sum-of-squares: 00119 filter[s][o] /= cudaGetSum(cudaSquared(filter[s][o])); 00120 } 00121 } 00122 } 00123 00124 // ###################################################################### 00125 void CudaHmax::freeMem() 00126 { 00127 if (initialized) 00128 { 00129 for(int s = 0; s < ns; s++) delete [] filter[s]; 00130 delete [] filter; 00131 initialized = false; 00132 } 00133 } 00134 00135 // ###################################################################### 00136 CudaHmax::~CudaHmax() 00137 { freeMem(); } 00138 00139 // ###################################################################### 00140 std::vector<std::string> CudaHmax::readDir(std::string inName) 00141 { 00142 DIR *dp = opendir(inName.c_str()); 00143 if(dp == NULL) 00144 { 00145 LFATAL("Directory does not exist %s",inName.c_str()); 00146 } 00147 dirent *dirp; 00148 std::vector<std::string> fList; 00149 while ((dirp = readdir(dp)) != NULL ) { 00150 if (dirp->d_name[0] != '.') 00151 fList.push_back(inName + '/' + std::string(dirp->d_name)); 00152 } 00153 LINFO("%"ZU" files in the directory\n", fList.size()); 00154 LINFO("file list : \n"); 00155 for (unsigned int i=0; i<fList.size(); i++) 00156 LINFO("\t%s", fList[i].c_str()); 00157 std::sort(fList.begin(),fList.end()); 00158 return fList; 00159 } 00160 00161 // ###################################################################### 00162 std::vector<std::string> CudaHmax::readList(std::string inName) 00163 { 00164 std::ifstream inFile; 00165 inFile.open(inName.c_str(),std::ios::in); 00166 if(!inFile){ 00167 LFATAL("Unable to open image path list file: %s",inName.c_str()); 00168 } 00169 std::string sLine; 00170 std::vector<std::string> fList; 00171 while (std::getline(inFile, sLine)) { 00172 std::cout << sLine << std::endl; 00173 fList.push_back(sLine); 00174 } 00175 LINFO("%"ZU" paths in the file\n", fList.size()); 00176 LINFO("file list : \n"); 00177 for (unsigned int i=0; i<fList.size(); i++) 00178 LINFO("\t%s", fList[i].c_str()); 00179 inFile.close(); 00180 return fList; 00181 } 00182 00183 00184 00185 00186 // ###################################################################### 00187 void CudaHmax::getC1(const CudaImage<float>& input, CudaImage<float>** &c1Res) 00188 { 00189 CudaImage<float> *c1Buff = new CudaImage<float>[no]; 00190 CudaImage<float> s1Res; 00191 // loop over scale bands: 00192 00193 00194 for(int sb = 0; sb < nsb; sb ++) { 00195 00196 // clear our buffers: 00197 for(int i = 0; i < no; i ++) 00198 c1Buff[i] = CudaImage<float>(input.getWidth(), input.getHeight(), ZEROS, itsMp, itsDev);; 00199 00200 // loop over scales within current scale band: 00201 for(int s = scaleSS[sb]; s < scaleSS[sb + 1]; s++) { 00202 // loop over orientations: 00203 for(int o = 0; o < no; o ++) { 00204 // convolve image by filter at current orient & scale: 00205 if (angleFlag) { 00206 s1Res = cudaConvolveHmax(input, filter[s][o]); // normalize by image energy 00207 //printCorners("s1Res",s1Res,s==scaleSS[0]&&sb==0&&o==0); 00208 } 00209 else { 00210 s1Res = cudaConvolve(input, filter[s][o], CONV_BOUNDARY_CLEAN); // no normalization 00211 // take absolute value of the convolution result: 00212 cudaAbs(s1Res); 00213 } 00214 00215 // take max between this convolution and previous ones for 00216 // that orientation but other scales within current scale band: 00217 c1Buff[o] = cudaTakeMax(c1Buff[o], s1Res); 00218 } 00219 } 00220 00221 // compute RF spacing (c1R) and pool range (c1PR): 00222 int c1R = (int)ceil((float)spaceSS[sb] / (float)c1SpaceOL); 00223 int c1PR = spaceSS[sb]; 00224 00225 // pool over space for each orientation (and scale band): 00226 for(int o = 0; o < no; o ++){ 00227 c1Res[sb][o] = cudaSpatialPoolMax(c1Buff[o], c1R, c1R, c1PR, c1PR); 00228 //printCorners("c1Res",c1Res[sb][o],sb==0&&o==0); 00229 } 00230 00231 } 00232 00233 delete [] c1Buff; 00234 } 00235 00236 void CudaHmax::initC1(CudaImage<float> **&c1Res) 00237 { 00238 c1Res = new CudaImage<float>*[nsb]; 00239 for (int sb = 0; sb < nsb; sb ++) c1Res[sb] = new CudaImage<float>[no]; 00240 } 00241 00242 void CudaHmax::clearC1(CudaImage<float> **&c1Res) 00243 { 00244 for (int sb = 0; sb < nsb; sb ++) delete [] c1Res[sb]; 00245 delete [] c1Res; 00246 } 00247 00248 void CudaHmax::printCorners(const char name[], const CudaImage<float>& cim, bool cond) 00249 { 00250 Image<float> im = cim.exportToImage(); 00251 if(cond) { 00252 printf("%s\n",name); 00253 int w = im.getWidth(); 00254 int h = im.getHeight(); 00255 std::string s; 00256 if(w>2 && h>2) { 00257 printf("%f\t%f\t%f\t%f\t%f\n",im.getVal(0,0),im.getVal(1,0),im.getVal(2,0),im.getVal(w-2,0),im.getVal(w-1,0)); 00258 printf("%f\t%f\t\t\t%f\t%f\n\n", im.getVal(0,1),im.getVal(1,1),im.getVal(w-2,1),im.getVal(w-1,1)); 00259 printf("%f\t%f\t\t\t%f\t%f\n", im.getVal(0,h-2),im.getVal(1,h-2),im.getVal(w-2,h-2),im.getVal(w-1,h-2)); 00260 printf("%f\t%f\t%f\t%f\t%f\n",im.getVal(0,h-1),im.getVal(1,h-1),im.getVal(2,h-1),im.getVal(w-2,h-1),im.getVal(w-1,h-1)); 00261 } 00262 else if(w>1 && h>1) { 00263 printf("%f\t%f\n",im.getVal(0,0),im.getVal(1,0)); 00264 printf("%f\t%f\n", im.getVal(0,1),im.getVal(1,1)); 00265 } 00266 else if(w>0 && h>0){ 00267 printf("%f\n",im.getVal(0,0)); 00268 } 00269 std::cout << "Mean of " << name << " " << mean(im) << std::endl; 00270 std::cout << "Var of " << name << " " << (stdev(im)*stdev(im)) << std::endl; 00271 std::cout << "Width of " << w << " and height of " << h << std::endl; 00272 //float mi,ma; getMinMax(input,mi,ma); 00273 //writeOutImage(inputf,name); 00274 //std::cout << "Min " << mi << " Max " << ma << std::endl; 00275 } 00276 } 00277 00278 void CudaHmax::writeOutImage(const CudaImage<float>& cim,std::string & fName) 00279 { 00280 std::ofstream oFile; 00281 Image<float> d; 00282 oFile.open(fName.c_str(),std::ios::out); 00283 d = cim.exportToImage(); 00284 int w,h; 00285 w = d.getWidth(); 00286 h = d.getHeight(); 00287 for(int i=0;i<w;i++){ 00288 for(int j=0;j<h;j++){ 00289 oFile << d.getVal(i,j) <<" "; 00290 } 00291 if(i!=w-1) 00292 oFile << std::endl; 00293 } 00294 oFile.close(); 00295 00296 } 00297 00298 00299 // ###################################################################### 00300 CudaImage<float> CudaHmax::getC2(const CudaImage<float>& input) 00301 { 00302 00303 // allocate buffers for intermediary results: 00304 CudaImage<float> **c1Res; 00305 initC1(c1Res); 00306 00307 // ****************************** 00308 // *** Compute S1/C1 output: 00309 // ****************************** 00310 getC1(input, c1Res); 00311 00312 // ****************************** 00313 // *** Compute S2/C2 output: 00314 // ****************************** 00315 CudaImage<float> c2Res(no * no, no * no, NO_INIT,itsMp,itsDev); 00316 cudaClear(c2Res,-1.0E10F); 00317 00318 // // detrmine number of scale bands from length of vector scaleSS: 00319 // int nsb = scaleSS.size() - 1; 00320 // int idx = 0; 00321 // // loop over four filters giving inputs to an S2 cell: 00322 // for (int f1 = 0; f1 < no; f1++) 00323 // for (int f2 = 0; f2 < no; f2++) 00324 // for (int f3 = 0; f3 < no; f3++) 00325 // for (int f4 = 0; f4 < no; f4++) { 00326 00327 // float c2r = -1.0E10; 00328 // // loop over scale band: 00329 // for (int sb = 0; sb < nsb; sb ++) { 00330 00331 // float s2r = featurePoolHmax(c1Res[sb][f1], c1Res[sb][f2], 00332 // c1Res[sb][f3], c1Res[sb][f4], 00333 // c1SpaceOL, c1SpaceOL, s2Target); 00334 // if (s2r > c2r) c2r = s2r; 00335 // } 00336 // c2Res.setVal(idx, c2r); idx ++; 00337 // } 00338 00339 // // free allocated temporary images: 00340 // clearC1(c1Res); 00341 00342 // return hmaxActivation(c2Res, s2Sigma); 00343 00344 LFATAL("Generic HMAX C2 Calculation not supported"); 00345 return c2Res; 00346 } 00347 00348 void CudaHmax::sumFilter(const CudaImage<float>& image, const float radius, CudaImage<float>& newImage) 00349 { 00350 Rectangle sumSupport = Rectangle::tlbrI(0,0,int(radius*2.0F),int(radius*2.0F)); 00351 sumFilter(image,sumSupport,newImage); 00352 } 00353 00354 void CudaHmax::sumFilter(const CudaImage<float>& image, const Rectangle& support, CudaImage<float>& newImage) 00355 { 00356 00357 Dims d(support.top()+support.bottomI()+1,support.left()+support.rightI()+1); 00358 //Dims d(support.bottomI()-support.top()+1,support.rightI()-support.left()+1); 00359 CudaImage<float> a(d,NO_INIT,itsMp,itsDev); 00360 cudaClear(a,1.0F); 00361 00362 //convolve the image with a matrix of 1's that is as big as the rectangle 00363 // This two step process is doing effectively the same thing by taking the center part of the convolution 00364 //I2 = conv2(ones(1,radius(2)+radius(4)+1), ones(radius(1)+radius(3)+1,1), I); 00365 //I3 = I2((radius(4)+1:radius(4)+size(I,1)), (radius(3)+1:radius(3)+size(I,2))); 00366 //CudaImage<float> i; 00367 //i = convolution(image,a,MATLAB_STYLE_CONVOLUTION); 00368 //Rectangle r = Rectangle::tlbrI(support.bottomI()+1,support.rightI()+1,support.bottomI()+image.getHeight(),support.rightI()+image.getWidth()); 00369 //newImage = crop(i,r); 00370 // Can be done in one step 00371 newImage = cudaConvolve(image,a,CONV_BOUNDARY_ZERO); 00372 } 00373 00374 00375 // ###################################################################### 00376 /* So things look consistent in everyone's emacs... */ 00377 /* Local Variables: */ 00378 /* indent-tabs-mode: nil */ 00379 /* End: */