00001 /*!@file Image/FilterOps.C Filtering operations on Image */ 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: Rob Peters <rjpeters@klab.caltech.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Image/FilterOps.C $ 00035 // $Id: FilterOps.C 13815 2010-08-22 17:58:48Z lior $ 00036 // 00037 00038 #include "Image/FilterOps.H" 00039 00040 #include "Image/Image.H" 00041 #include "Image/Kernels.H" 00042 #include "Image/MathOps.H" 00043 #include "Image/Pixels.H" 00044 #include "Util/Assert.H" 00045 #include "Util/MathFunctions.H" 00046 #include "Util/Promotions.H" 00047 #include "Util/log.H" 00048 #include "rutz/trace.h" 00049 00050 #include <algorithm> 00051 #include <cmath> 00052 #include <limits> 00053 00054 // ###################################################################### 00055 template <class T> 00056 Image<typename promote_trait<T, float>::TP> 00057 correlation(const Image<T>& src, const Image<float>& filter) 00058 { 00059 ASSERT(src.initialized()); 00060 ASSERT(filter.initialized()); 00061 const int src_w = src.getWidth(); 00062 const int src_h = src.getHeight(); 00063 00064 Image<float>::const_iterator fptrBegin = filter.begin(); 00065 const int fil_w = filter.getWidth(); 00066 const int fil_h = filter.getHeight(); 00067 ASSERT((fil_w & 1) && (fil_h & 1)); //check if the filter is odd size 00068 00069 // promote the source image to float if necessary, so that we do the 00070 // promotion only once for all, rather than many times as we access 00071 // the pixels of the image; if no promotion is necessary, "source" 00072 // will just point to the original data of "src" through the 00073 // copy-on-write/ref-counting behavior of Image: 00074 00075 typedef typename promote_trait<T, float>::TP TF; 00076 const Image<TF> source = src; 00077 Image<TF> result(src_w, src_h, NO_INIT); 00078 typename Image<TF>::const_iterator sptr = source.begin(); 00079 typename Image<TF>::iterator dptr = result.beginw(); 00080 00081 //const int fil_end = fil_w * fil_h - 1; 00082 // const int Nx2 = (fil_w - 1) / 2; 00083 // const int Ny2 = (fil_h - 1) / 2; 00084 00085 const int srow_skip = src_w-fil_w; 00086 00087 00088 for (int dst_y = 0; dst_y < src_h-fil_h; dst_y++) { 00089 00090 for (int dst_x = 0; dst_x < src_w-fil_w; dst_x++, dptr++) { 00091 00092 float dst_val = 0.0f; 00093 00094 Image<float>::const_iterator fptr = fptrBegin; 00095 00096 // Image<float>::const_iterator srow_ptr = 00097 // sptr + src_w*(dst_y-Ny2) + (dst_x - Nx2); 00098 00099 Image<float>::const_iterator srow_ptr = 00100 sptr + (src_w*dst_y) + dst_x; 00101 00102 // LINFO("DD:%ix%i %i", dst_x,dst_y, src_w*(dst_y) + (dst_x)); 00103 00104 00105 for (int f_y = 0; f_y < fil_h; ++f_y) 00106 { 00107 for (int f_x = 0; f_x < fil_w; ++f_x){ 00108 dst_val += fabs((*srow_ptr++) - (*fptr++)); 00109 } 00110 00111 srow_ptr += srow_skip; 00112 00113 } 00114 *dptr = dst_val; 00115 continue; 00116 } 00117 } 00118 00119 return result; 00120 00121 } 00122 00123 // ###################################################################### 00124 template <class T> 00125 Image<typename promote_trait<T, float>::TP> 00126 templMatch(const Image<T>& src, const Image<float>& filter, int method) 00127 { 00128 ASSERT(src.initialized()); 00129 ASSERT(filter.initialized()); 00130 const int src_w = src.getWidth(); 00131 const int src_h = src.getHeight(); 00132 00133 00134 // promote the source image to float if necessary, so that we do the 00135 // promotion only once for all, rather than many times as we access 00136 // the pixels of the image; if no promotion is necessary, "source" 00137 // will just point to the original data of "src" through the 00138 // copy-on-write/ref-counting behavior of Image: 00139 00140 const int fil_w = filter.getWidth(); 00141 const int fil_h = filter.getHeight(); 00142 00143 typedef typename promote_trait<T, float>::TP TF; 00144 const Image<TF> source = src; 00145 Image<TF> result(src_w-fil_w+1, src_h-fil_h+1, NO_INIT); 00146 00147 result = correlation(source, filter); 00148 00149 return result; 00150 } 00151 00152 00153 00154 // ###################################################################### 00155 template <class T> 00156 Image<typename promote_trait<T, float>::TP> 00157 spatialPoolMax(const Image<T>& src, const int si, const int sj, 00158 const int sti, const int stj) 00159 { 00160 GVX_TRACE(__PRETTY_FUNCTION__); 00161 const int w = src.getWidth(), h = src.getHeight(); 00162 // promote the source image to float if necessary, so that we do the 00163 // promotion only once for all, rather than many times as we access 00164 // the pixels of the image; if no promotion is necessary, "source" 00165 // will just point to the original data of "src" through the 00166 // copy-on-write/ref-counting behavior of Image: 00167 typedef typename promote_trait<T, float>::TP TF; 00168 const Image<TF> source = src; 00169 Image<TF> result((int)ceil((float)w / (float)si), 00170 (int)ceil((float)h / (float)sj), 00171 NO_INIT); 00172 typename Image<TF>::const_iterator sptr = source.begin(); 00173 typename Image<TF>::iterator dptr = result.beginw(); 00174 00175 // very inefficient implementation, ha ha 00176 for (int j = 0; j < h; j += sj) 00177 { 00178 int jmax = std::min(j + stj, h); 00179 for (int i = 0; i < w; i += si) 00180 { 00181 TF val = std::numeric_limits<TF>::min(); 00182 int imax = std::min(i + sti, w); 00183 00184 for (int jj = j; jj < jmax; jj ++) 00185 for (int ii = i; ii < imax; ii ++) 00186 if (sptr[jj * w + ii] > val) val = sptr[jj * w + ii]; 00187 *dptr++ = val; 00188 } 00189 } 00190 return result; 00191 } 00192 00193 // ###################################################################### 00194 template<class T> 00195 float featurePoolHmax(const Image<T>& img1, const Image<T>& img2, 00196 const Image<T>& img3, const Image<T>& img4, 00197 const int si, const int sj, const float s2t) 00198 { 00199 GVX_TRACE(__PRETTY_FUNCTION__); 00200 ASSERT(img1.isSameSize(img2)&&img1.isSameSize(img3)&&img1.isSameSize(img4)); 00201 00202 float res = std::numeric_limits<float>::min(); 00203 int w = img1.getWidth(), h = img1.getHeight(); 00204 typename Image<T>::const_iterator i1 = img1.begin() + si + w * sj; 00205 typename Image<T>::const_iterator i2 = img2.begin() + si; 00206 typename Image<T>::const_iterator i3 = img3.begin() + w * sj; 00207 typename Image<T>::const_iterator i4 = img4.begin(); 00208 00209 for (int y = sj; y < h; y++) 00210 { 00211 for (int x = si; x < w; x++) 00212 { 00213 float s2r = (*i1 - s2t) * (*i1 - s2t) + (*i2 - s2t) * (*i2 - s2t) + 00214 (*i3 - s2t) * (*i3 - s2t) + (*i4 - s2t) * (*i4 - s2t); 00215 if (s2r > res) res = s2r; 00216 i1++; i2++; i3++; i4++; 00217 } 00218 i1 += si; i2 += si; i3 += si; i4 += si; 00219 } 00220 return res; 00221 } 00222 00223 namespace 00224 { 00225 pthread_once_t trigtab_init_once = PTHREAD_ONCE_INIT; 00226 00227 const int TRIGTAB_SIZE = 256; 00228 00229 double sintab[TRIGTAB_SIZE]; 00230 double costab[TRIGTAB_SIZE]; 00231 00232 void trigtab_init() 00233 { 00234 for (int i = 0; i < 256; ++i) 00235 { 00236 const double arg = (2.0*M_PI*i) / 256.0; 00237 00238 #if defined(HAVE_ASM_FSINCOS) 00239 asm("fsincos" 00240 :"=t"(costab[i]), "=u"(sintab[i]) 00241 :"0"(arg)); 00242 #elif defined(HAVE_SINCOS) 00243 sincos(arg, &sintab[i], &costab[i]); 00244 #else 00245 sintab[i] = sin(arg); 00246 costab[i] = cos(arg); 00247 #endif 00248 } 00249 } 00250 } 00251 00252 // ###################################################################### 00253 template <class T> 00254 Image<typename promote_trait<T, float>::TP> 00255 orientedFilter(const Image<T>& src, const float k, 00256 const float theta, const float intensity, 00257 const bool usetab) 00258 { 00259 GVX_TRACE(__PRETTY_FUNCTION__); 00260 double kx = double(k) * cos((theta + 90.0) * M_PI / 180.0); 00261 double ky = double(k) * sin((theta + 90.0) * M_PI / 180.0); 00262 00263 typedef typename promote_trait<T, float>::TP TF; 00264 Image<TF> re(src.getDims(), NO_INIT), im(src.getDims(), NO_INIT); 00265 typename Image<T>::const_iterator sptr = src.begin(); 00266 typename Image<TF>::iterator reptr = re.beginw(), imptr = im.beginw(); 00267 00268 // (x,y) = (0,0) at center of image: 00269 int w2l = src.getWidth() / 2, w2r = src.getWidth() - w2l; 00270 int h2l = src.getHeight() / 2, h2r = src.getHeight() - h2l; 00271 00272 if (usetab) 00273 { 00274 pthread_once(&trigtab_init_once, &trigtab_init); 00275 00276 const double kx2 = (256.0 * kx) / (2.0*M_PI); 00277 const double ky2 = (256.0 * ky) / (2.0*M_PI); 00278 00279 for (int j = -h2l; j < h2r; ++j) 00280 for (int i = -w2l; i < w2r; ++i) 00281 { 00282 const double arg2 = kx2 * i + ky2 * j; 00283 int idx = int(arg2) % 256; 00284 if (idx < 0) idx += 256; 00285 const TF val = (*sptr++) * intensity; 00286 00287 const double sinarg = sintab[idx]; 00288 const double cosarg = costab[idx]; 00289 00290 *reptr++ = TF(val * cosarg); 00291 *imptr++ = TF(val * sinarg); 00292 } 00293 } 00294 else 00295 { 00296 for (int j = -h2l; j < h2r; ++j) 00297 for (int i = -w2l; i < w2r; ++i) 00298 { 00299 const double arg = kx * i + ky * j; 00300 const TF val = (*sptr++) * intensity; 00301 00302 #if defined(HAVE_ASM_FSINCOS) 00303 // If we the asm "fsincos" instruction is available, then 00304 // that will be the fastest way to compute paired sin and 00305 // cos calls. 00306 double sinarg, cosarg; 00307 asm("fsincos" 00308 :"=t"(cosarg), "=u"(sinarg) 00309 :"0"(arg)); 00310 00311 #elif defined(HAVE_SINCOS) 00312 // Otherwise we use a sincos() library function if it is 00313 // available as an extension (it's present at least in GNU 00314 // libc, maybe on other systems as well) then it is faster 00315 // (~30% overall speedup of orientedFilter()) to compute 00316 // both sin+cos at once rather than doing separate calls 00317 // -- this allows the use of the x87 assembly instruction 00318 // fsincos (see /usr/include/bits/mathinline.h for the 00319 // glibc sincos()). 00320 double sinarg, cosarg; 00321 sincos(arg, &sinarg, &cosarg); 00322 00323 #else 00324 // Otherwise we resort to explicit separate sin() and 00325 // cos() calls. 00326 double sinarg = sin(arg); 00327 double cosarg = cos(arg); 00328 #endif 00329 00330 *reptr++ = TF(val * cosarg); 00331 *imptr++ = TF(val * sinarg); 00332 } 00333 } 00334 00335 re = ::lowPass9(re); 00336 im = ::lowPass9(im); 00337 00338 return quadEnergy(re, im); 00339 } 00340 00341 // ###################################################################### 00342 template <class T> 00343 Image<T> centerSurround(const Image<T>& center, 00344 const Image<T>& surround, 00345 const bool absol) 00346 { 00347 GVX_TRACE(__PRETTY_FUNCTION__); 00348 const int lw = center.getWidth(), lh = center.getHeight(); 00349 const int sw = surround.getWidth(), sh = surround.getHeight(); 00350 00351 if (sw > lw || sh > lh) LFATAL("center must be larger than surround"); 00352 00353 int scalex = lw / sw, remx = lw - 1 - (lw % sw); 00354 int scaley = lh / sh, remy = lh - 1 - (lh % sh); 00355 00356 // result has the size of the larger image: 00357 Image<T> result(center.getDims(), NO_INIT); 00358 00359 // scan large image and subtract corresponding pixel from small image: 00360 int ci = 0, cj = 0; 00361 typename Image<T>::const_iterator lptr = center.begin(); 00362 typename Image<T>::const_iterator sptr = surround.begin(); 00363 typename Image<T>::iterator dptr = result.beginw(); 00364 T zero = T(); 00365 00366 if (absol == true) // compute abs(hires - lowres): 00367 { 00368 for (int j = 0; j < lh; ++j) 00369 { 00370 for (int i = 0; i < lw; ++i) 00371 { 00372 if (*lptr > *sptr) 00373 *dptr++ = T(*lptr++ - *sptr); 00374 else 00375 *dptr++ = T(*sptr - *lptr++); 00376 00377 if ((++ci) == scalex && i != remx) { ci = 0; ++sptr; } 00378 } 00379 if (ci) { ci = 0; ++sptr; } // in case the reduction is not round 00380 if ((++cj) == scaley && j != remy) cj = 0; else sptr -= sw; 00381 } 00382 } 00383 else // compute hires - lowres, clamped to 0: 00384 { 00385 for (int j = 0; j < lh; ++j) 00386 { 00387 for (int i = 0; i < lw; ++i) 00388 { 00389 if (*lptr > *sptr) 00390 *dptr++ = T(*lptr++ - *sptr); 00391 else 00392 { *dptr++ = zero; lptr++; } 00393 00394 if ((++ci) == scalex && i != remx) { ci = 0; ++sptr; } 00395 } 00396 if (ci) { ci = 0; ++sptr; } // in case the reduction is not round 00397 if ((++cj) == scaley && j != remy) cj = 0; else sptr -= sw; 00398 } 00399 } 00400 00401 // attenuate borders: 00402 inplaceAttenuateBorders(result, result.getDims().max() / 20); 00403 00404 return result; 00405 } 00406 00407 // ###################################################################### 00408 template <class T> 00409 void centerSurround(const Image<T>& center, const Image<T>& surround, 00410 Image<T>& pos, Image<T>& neg) 00411 { 00412 GVX_TRACE(__PRETTY_FUNCTION__); 00413 const int lw = center.getWidth(), lh = center.getHeight(); 00414 const int sw = surround.getWidth(), sh = surround.getHeight(); 00415 00416 if (sw > lw || sh > lh) LFATAL("center must be larger than surround"); 00417 00418 int scalex = lw / sw, remx = lw - 1 - (lw % sw); 00419 int scaley = lh / sh, remy = lh - 1 - (lh % sh); 00420 00421 // result has the size of the larger image: 00422 pos.resize(center.getDims(), NO_INIT); 00423 neg.resize(center.getDims(), NO_INIT); 00424 00425 // scan large image and subtract corresponding pixel from small image: 00426 int ci = 0, cj = 0; 00427 typename Image<T>::const_iterator lptr = center.begin(); 00428 typename Image<T>::const_iterator sptr = surround.begin(); 00429 typename Image<T>::iterator pptr = pos.beginw(), nptr = neg.beginw(); 00430 T zero = T(); 00431 00432 for (int j = 0; j < lh; ++j) 00433 { 00434 for (int i = 0; i < lw; ++i) 00435 { 00436 if (*lptr > *sptr) { *pptr++ = T(*lptr++ - *sptr); *nptr++ = zero; } 00437 else { *pptr++ = zero; *nptr++ = T(*sptr - *lptr++); } 00438 00439 if ((++ci) == scalex && i != remx) { ci = 0; ++sptr; } 00440 } 00441 if (ci) { ci = 0; ++sptr; } // in case the reduction is not round 00442 if ((++cj) == scaley && j != remy) cj = 0; else sptr -= sw; 00443 } 00444 00445 // attenuate borders: 00446 inplaceAttenuateBorders(pos, pos.getDims().max() / 20); 00447 inplaceAttenuateBorders(neg, neg.getDims().max() / 20); 00448 } 00449 00450 // ###################################################################### 00451 template <class T> 00452 Image<typename promote_trait<T, float>::TP> 00453 doubleOpp(const Image<T>& cplus, const Image<T>& cminus, 00454 const Image<T>& splus, const Image<T>& sminus) 00455 { 00456 GVX_TRACE(__PRETTY_FUNCTION__); 00457 ASSERT(cplus.isSameSize(cminus)); ASSERT(splus.isSameSize(sminus)); 00458 00459 typedef typename promote_trait<T, float>::TP TF; 00460 00461 // compute difference between both center arrays 00462 Image<TF> cdiff = cplus; cdiff -= cminus; 00463 00464 // compute difference between both surround arrays 00465 Image<TF> sdiff = splus; sdiff -= sminus; 00466 00467 // compute center-surround cdiff - sdiff = (cp - cm) [-] (sp - sm) 00468 return centerSurround(cdiff, sdiff, true); // take abs 00469 } 00470 00471 // ###################################################################### 00472 template <class T> 00473 void avgOrient(const Image<T>& src, 00474 Image<float>& orient, Image<float>& strength) 00475 { 00476 GVX_TRACE(__PRETTY_FUNCTION__); 00477 float Gf1[9] = { 0.0094, 0.1148, 0.3964, -0.0601, -0.9213, -0.0601, 00478 0.3964, 0.1148, 0.0094 }; 00479 float Gf2[9] = { 0.0008, 0.0176, 0.1660, 0.6383, 1.0, 0.6383, 0.1660, 00480 0.0176, 0.0008 }; 00481 float Gf3[9] = { -0.0028, -0.0480, -0.3020, -0.5806, 0.0, 0.5806, 0.3020, 00482 0.0480, 0.0028 }; 00483 float Hf1[9] = { -0.0098, -0.0618, 0.0998, 0.7551, 0.0, -0.7551, -0.0998, 00484 0.0618, 0.0098 }; 00485 float Hf2[9] = { 0.0008, 0.0176, 0.1660, 0.6383, 1.0, 0.6383, 0.1660, 00486 0.0176, 0.0008 }; 00487 float Hf3[9] = { -0.0020, -0.0354, -0.2225, -0.4277, 0.0, 0.4277, 0.2225, 00488 0.0354, 0.0020 }; 00489 float Hf4[9] = { 0.0048, 0.0566, 0.1695, -0.1889, -0.7349, -0.1889, 0.1695, 00490 0.0566, 0.0048 }; 00491 00492 Image<float> fima = src; 00493 00494 // apply Nine-Tap filters to create the separable bases of G2 and H2 00495 Image<float> G2a = sepFilter(fima, Gf1, Gf2, 9, 9, CONV_BOUNDARY_ZERO); 00496 Image<float> G2b = sepFilter(fima, Gf3, Gf3, 9, 9, CONV_BOUNDARY_ZERO); 00497 Image<float> G2c = sepFilter(fima, Gf2, Gf1, 9, 9, CONV_BOUNDARY_ZERO); 00498 Image<float> H2a = sepFilter(fima, Hf1, Hf2, 9, 9, CONV_BOUNDARY_ZERO); 00499 Image<float> H2b = sepFilter(fima, Hf4, Hf3, 9, 9, CONV_BOUNDARY_ZERO); 00500 Image<float> H2c = sepFilter(fima, Hf3, Hf4, 9, 9, CONV_BOUNDARY_ZERO); 00501 Image<float> H2d = sepFilter(fima, Hf2, Hf1, 9, 9, CONV_BOUNDARY_ZERO); 00502 00503 // combine the basis-filtered images: 00504 orient = Image<float>(src.getDims(), NO_INIT); 00505 strength = Image<float>(src.getDims(), NO_INIT); 00506 00507 const int siz = src.getSize(); 00508 00509 typename Image<float>::const_iterator 00510 pG2a = G2a.begin(), 00511 pG2b = G2b.begin(), 00512 pG2c = G2c.begin(), 00513 pH2a = H2a.begin(), 00514 pH2b = H2b.begin(), 00515 pH2c = H2c.begin(), 00516 pH2d = H2d.begin(); 00517 00518 Image<float>::iterator porient = orient.beginw(); 00519 Image<float>::iterator pstrength = strength.beginw(); 00520 00521 for (int k = 0; k < siz; ++k) 00522 { 00523 const float c2 = 0.5 * (squareOf(pG2a[k]) - squareOf(pG2c[k])) 00524 + 0.46875 * (squareOf(pH2a[k]) - squareOf(pH2d[k])) 00525 + 0.28125 * (squareOf(pH2b[k]) - squareOf(pH2c[k])) 00526 + 0.1875 * (pH2a[k] * pH2c[k] - pH2b[k] * pH2d[k]); 00527 const float c3 = 00528 - pG2a[k] * pG2b[k] 00529 - pG2b[k] * pG2c[k] 00530 - 0.9375 * (pH2c[k] * pH2d[k] + pH2a[k] * pH2b[k]) 00531 - 1.6875 * pH2b[k] * pH2c[k] 00532 - 0.1875 * pH2a[k] * pH2d[k]; 00533 porient[k] = (atan2(c3, c2) + M_PI)/ 2.0; // between 0 and PI 00534 pstrength[k] = sqrt(squareOf(c2) + squareOf(c3)); 00535 } 00536 } 00537 00538 // ###################################################################### 00539 template <class T> 00540 Image<T> energyNorm(const Image<T>& img) 00541 { 00542 GVX_TRACE(__PRETTY_FUNCTION__); 00543 // Need to bump everything above zero here so that we don't have 00544 // divide-by-zero problems when we later divide by the energy. FIXME 00545 // should be a better way??? 00546 Image<float> result = img + 1.0f; 00547 00548 // Compute the local image energy 00549 const Image<float> energy = sqrt(lowPass9(result * result)); 00550 00551 // Normalize by the local image energy 00552 result = result / energy; // watch divbyzero here!!! 00553 00554 // Set to zero-mean 00555 result -= mean(result); 00556 00557 return result; 00558 } 00559 00560 // ###################################################################### 00561 template <class T> 00562 Image<T> junctionFilterFull(const Image<T>& i0, const Image<T>& i45, 00563 const Image<T>& i90, const Image<T>& i135, 00564 const bool r[8], const int dx = 6, 00565 const int dy = 6, 00566 const bool useEuclidDiag = true) 00567 { 00568 GVX_TRACE(__PRETTY_FUNCTION__); 00569 00570 ASSERT(i0.isSameSize(i45)); 00571 ASSERT(i0.isSameSize(i90)); 00572 ASSERT(i0.isSameSize(i135)); 00573 00574 Image<T> result(i0.getDims(), ZEROS); 00575 00576 const int w = i0.getWidth(), h = i0.getHeight(); 00577 00578 /* example L junction: upright L(x,y) = |(x, y-1) and (NOT 00579 /(x+1,y-1)) and -(x+1, y) and (NOT \(x+1,y+1)) and (NOT |(x, 00580 y+1)) and (NOT /(x-1,y+1)) and (NOT -(x-1, y)) and (NOT 00581 \(x-1,y-1)) 00582 00583 To find NOT of an irrelevant feature, just consider how strong it 00584 is in comparison to the weakest relevant feature, i.e. d = 00585 min(all Rel responses)/2 - Irr. response 00586 00587 d is high <=> the absence of this Irr. feature 00588 d is low <=> the presence of this Irr. feature 00589 00590 ******* 00591 00592 3rd party comment: 00593 00594 Nathan: This filter basically will give either a proportional 00595 response to a proper feature set or should return 00596 zero given 1 or more irr features. That is, notice 00597 that for any feature <= min rel feature is tacked 00598 into the multiplication. Thus, weak irr features still 00599 add to the strength. So this filter is sensitive to noise. 00600 However, it is simple and will brutally weed out any 00601 response given a strong irr feature. 00602 */ 00603 00604 // pixel offsets for (x+dx, y), (x+dx, y-dy), (x, y-dy), etc. going 00605 // counterclockwise: 00606 00607 int dx_diag, dy_diag; 00608 00609 // Compute the diagonal offsets if needed as a euclidian distance 00610 // from the center in dx,dy or should we just put it on a "grid" 00611 if(useEuclidDiag) 00612 { 00613 dx_diag = (int)round(sqrt(pow(dx,2)/2.0f)); 00614 dy_diag = (int)round(sqrt(pow(dy,2)/2.0f)); 00615 } 00616 else 00617 { 00618 dx_diag = dx; 00619 dy_diag = dy; 00620 } 00621 00622 // non diagonal elements 00623 const int o0 = dx; 00624 const int o2 = -dy*w; 00625 const int o4 = -dx; 00626 const int o6 = dy*w; 00627 00628 // diagonal elements 00629 const int o1 = dx_diag - dy_diag*w; 00630 const int o3 = -dx_diag - dy_diag*w; 00631 const int o5 = -dx_diag + dy_diag*w; 00632 const int o7 = dx_diag + dy_diag*w; 00633 00634 // get pointers to the image pixel data: 00635 typename Image<T>::const_iterator 00636 ip0 = i0.begin() + dx + dy*w, ip45 = i45.begin() + dx + dy*w, 00637 ip90 = i90.begin() + dx + dy*w, ip135 = i135.begin() + dx + dy*w; 00638 typename Image<T>::iterator rp = result.beginw() + dx + dy*w; 00639 00640 // loop over the bulk of the images (i.e., all except a border of 00641 // width dx and height dy): 00642 00643 const T max = std::numeric_limits<T>::max(); 00644 00645 for (int j = (dy*2); j < h; j ++) 00646 { 00647 for (int i = (dx*2); i < w; i ++) 00648 { 00649 00650 // get the various feature values: 00651 const T r0 = ip0[o0], r4 = ip0[o4]; ++ip0; 00652 const T r1 = ip45[o1], r5 = ip45[o5]; ++ip45; 00653 const T r2 = ip90[o2], r6 = ip90[o6]; ++ip90; 00654 const T r3 = ip135[o3], r7 = ip135[o7]; ++ip135; 00655 00656 // for this full implementation, let's start by finding out the 00657 // minimum relevant response, so that we can then check the 00658 // irrelevant responses against that baseline: 00659 T minRel = max; 00660 if (r[0] && r0 < minRel) minRel = r0; 00661 if (r[1] && r1 < minRel) minRel = r1; 00662 if (r[2] && r2 < minRel) minRel = r2; 00663 if (r[3] && r3 < minRel) minRel = r3; 00664 if (r[4] && r4 < minRel) minRel = r4; 00665 if (r[5] && r5 < minRel) minRel = r5; 00666 if (r[6] && r6 < minRel) minRel = r6; 00667 if (r[7] && r7 < minRel) minRel = r7; 00668 minRel /= 2; 00669 00670 // now let's compute the response of the junction: 00671 float resp = 1.0f; 00672 00673 // One at a time we test to see if an irr feature is stronger 00674 // than one of our rel features. If so, we return a zero reponse 00675 // and quit. Otherwise we keep going and normalize the final response. 00676 // Notice we only get a non-zero reponse of all irr features are 00677 // less than 1/2 of the minimum rel feature. 00678 00679 if (r[0]) resp *= r0; else resp *= (minRel - r0); 00680 if (resp <= 0.0f) resp = 0.0f; 00681 else 00682 { 00683 if (r[1]) resp *= r1; else resp *= (minRel - r1); 00684 if (resp <= 0.0f) resp = 0.0f; 00685 else 00686 { 00687 if (r[2]) resp *= r2; else resp *= (minRel - r2); 00688 if (resp <= 0.0f) resp = 0.0f; 00689 else 00690 { 00691 if (r[3]) resp *= r3; else resp *= (minRel - r3); 00692 if (resp <= 0.0f) resp = 0.0f; 00693 else 00694 { 00695 if (r[4]) resp *= r4; else resp *= (minRel - r4); 00696 if (resp <= 0.0f) resp = 0.0f; 00697 else 00698 { 00699 if (r[5]) resp *= r5; else resp *= (minRel - r5); 00700 if (resp <= 0.0f) resp = 0.0f; 00701 else 00702 { 00703 if (r[6]) resp *= r6; else resp *= (minRel - r6); 00704 if (resp <= 0.0f) resp = 0.0f; 00705 else 00706 { 00707 if (r[7]) resp *= r7; else resp *= (minRel - r7); 00708 if (resp <= 0.0f) resp = 0.0f; 00709 else 00710 { 00711 // normalize it by the number of features: 00712 resp = pow(resp, 1.0/8.0); 00713 } 00714 } 00715 } 00716 } 00717 } 00718 } 00719 } 00720 } 00721 00722 // store the response 00723 *rp++ = T(resp); 00724 } 00725 // skip border and go to next row of pixels: 00726 ip0 += dx*2; ip45 += dx*2; ip90 += dx*2; ip135 += dx*2; rp += dx*2; 00727 } 00728 00729 return result; 00730 } 00731 00732 // ###################################################################### 00733 namespace 00734 { 00735 // Trivial helper function for junctionFilterPartial() 00736 template <class DstItr, class SrcItr> 00737 inline void JFILT(DstItr dptr, SrcItr sptr, 00738 const int w, const int jmax, const int imax) 00739 { 00740 for(int j = 0; j < jmax; ++j) 00741 { 00742 for (int i = 0; i < imax; ++i) 00743 dptr[i] *= sptr[i]; 00744 00745 sptr += w; dptr += w; 00746 } 00747 } 00748 } 00749 00750 template <class T> 00751 Image<T> junctionFilterPartial(const Image<T>& i0, const Image<T>& i45, 00752 const Image<T>& i90, const Image<T>& i135, 00753 const bool r[8], const int dx = 6, 00754 const int dy = 6, 00755 const bool useEuclidDiag = false) 00756 { 00757 GVX_TRACE(__PRETTY_FUNCTION__); 00758 // the preamble is identical to junctionFilterFull: 00759 00760 ASSERT(i0.isSameSize(i45) && i0.isSameSize(i90) && i0.isSameSize(i135)); 00761 Image<T> result(i0.getDims(), ZEROS); 00762 00763 const int w = i0.getWidth(), h = i0.getHeight(); 00764 00765 int dx_diag, dy_diag; 00766 00767 // Compute the diagonal offsets if needed as a euclidian distance 00768 // from the center in dx,dy or should we just put it on a "grid" 00769 if(useEuclidDiag) 00770 { 00771 dx_diag = (int)round(fastSqrt((dx*dx)/2.0f)); 00772 dy_diag = (int)round(fastSqrt((dy*dy)/2.0f)); 00773 } 00774 else 00775 { 00776 dx_diag = dx; 00777 dy_diag = dy; 00778 } 00779 00780 // non diagonal elements 00781 const int o0 = dx; 00782 const int o2 = -dy*w; 00783 const int o4 = -dx; 00784 const int o6 = dy*w; 00785 00786 // diagonal elements 00787 const int o1 = dx_diag - dy_diag*w; 00788 const int o3 = -dx_diag - dy_diag*w; 00789 const int o5 = -dx_diag + dy_diag*w; 00790 const int o7 = dx_diag + dy_diag*w; 00791 00792 const int offset = dx + dy*w; 00793 00794 typename Image<T>::iterator const rpp = result.beginw() + offset; 00795 00796 // compute the number of relevant features (for normalization): 00797 int nr = 0; for (int i = 0; i < 8; i++) if (r[i]) nr++; 00798 00799 const int imax = w - dx*2; 00800 const int jmax = h - dy*2; 00801 00802 // initialize the valid portion of the response array to 1.0's 00803 { 00804 typename Image<T>::iterator rp = rpp; 00805 for (int j = 0; j < jmax; ++j) 00806 { 00807 for (int i = 0; i < imax; ++i) 00808 rp[i] = T(1.0); 00809 rp += w; 00810 } 00811 } 00812 00813 // loop over the bulk of the images, computing the responses of 00814 // the junction filter: 00815 { 00816 if (r[0]) JFILT(rpp, i0.begin() + o0 + offset, w, jmax, imax); 00817 if (r[1]) JFILT(rpp, i45.begin() + o1 + offset, w, jmax, imax); 00818 if (r[2]) JFILT(rpp, i90.begin() + o2 + offset, w, jmax, imax); 00819 if (r[3]) JFILT(rpp, i135.begin() + o3 + offset, w, jmax, imax); 00820 if (r[4]) JFILT(rpp, i0.begin() + o4 + offset, w, jmax, imax); 00821 if (r[5]) JFILT(rpp, i45.begin() + o5 + offset, w, jmax, imax); 00822 if (r[6]) JFILT(rpp, i90.begin() + o6 + offset, w, jmax, imax); 00823 if (r[7]) JFILT(rpp, i135.begin() + o7 + offset, w, jmax, imax); 00824 } 00825 00826 // normalize the responses by the number of relevant features, 00827 // optimizing by using sqrt() or cbrt() where possible (this gives 00828 // an average speedup of ~3x across all junction filter types): 00829 { 00830 typename Image<T>::iterator rp = rpp; 00831 if (nr == 1) 00832 { 00833 // nothing to do here 00834 } 00835 else if (nr == 2) 00836 { 00837 for (int j = 0; j < jmax; ++j) 00838 { 00839 for (int i = 0; i < imax; ++i) 00840 rp[i] = T(fastSqrt(rp[i])); 00841 rp += w; 00842 } 00843 } 00844 else if (nr == 3) 00845 { 00846 for (int j = 0; j < jmax; ++j) 00847 { 00848 for (int i = 0; i < imax; ++i) 00849 rp[i] = T(cbrt(rp[i])); 00850 rp += w; 00851 } 00852 } 00853 else if (nr == 4) 00854 { 00855 for (int j = 0; j < jmax; ++j) 00856 { 00857 for (int i = 0; i < imax; ++i) 00858 rp[i] = T(fastSqrt(fastSqrt(rp[i]))); 00859 rp += w; 00860 } 00861 } 00862 else 00863 { 00864 const double power = 1.0 / nr; 00865 for (int j = 0; j < jmax; ++j) 00866 { 00867 for (int i = 0; i < imax; ++i) 00868 rp[i] = T(pow(rp[i], power)); 00869 rp += w; 00870 } 00871 } 00872 } 00873 return result; 00874 } 00875 00876 // ###################################################################### 00877 00878 // ###################################################################### 00879 template <class T> 00880 Image<T> MSTFilterFull(const Image<T>& i0, const Image<T>& i45, 00881 const Image<T>& i90, const Image<T>& i135, 00882 const bool r[8], const int dx = 6, 00883 const int dy = 6, 00884 const bool useEuclidDiag = true) 00885 { 00886 GVX_TRACE(__PRETTY_FUNCTION__); 00887 00888 ASSERT(i0.isSameSize(i45)); 00889 ASSERT(i0.isSameSize(i90)); 00890 ASSERT(i0.isSameSize(i135)); 00891 00892 Image<T> result(i0.getDims(), ZEROS); 00893 00894 const int w = i0.getWidth(), h = i0.getHeight(); 00895 00896 /* example star MST: star star(x,y) = |(x, y-1) and ( 00897 /(x+1,y-1)) and -(x+1, y) and \(x+1,y+1) and ( |(x, 00898 y+1)) and ( /(x-1,y+1)) and ( -(x-1, y)) and ( 00899 \(x-1,y-1)) 00900 00901 To find NOT of an irrelevant feature, just consider how strong it 00902 is in comparison to the weakest relevant feature, i.e. d = 00903 min(all Rel responses)/2 - Irr. response 00904 00905 d is high <=> the absence of this Irr. feature 00906 d is low <=> the presence of this Irr. feature 00907 00908 ******* 00909 00910 */ 00911 00912 // pixel offsets for (x+dx, y), (x+dx, y-dy), (x, y-dy), etc. going 00913 // counterclockwise: 00914 00915 int dx_diag, dy_diag; 00916 00917 // Compute the diagonal offsets if needed as a euclidian distance 00918 // from the center in dx,dy or should we just put it on a "grid" 00919 if(useEuclidDiag) 00920 { 00921 dx_diag = (int)round(sqrt(pow(dx,2)/2.0f)); 00922 dy_diag = (int)round(sqrt(pow(dy,2)/2.0f)); 00923 } 00924 else 00925 { 00926 dx_diag = dx; 00927 dy_diag = dy; 00928 } 00929 00930 // non diagonal elements 00931 const int o0 = dx; 00932 const int o2 = -dy*w; 00933 const int o4 = -dx; 00934 const int o6 = dy*w; 00935 00936 const int o8 = dx*2; 00937 const int o10 = -dy*w*2; 00938 const int o12 = -dx*2; 00939 const int o14 = dy*w*2; 00940 00941 // diagonal elements 00942 const int o1 = dx_diag - dy_diag*w; 00943 const int o3 = -dx_diag - dy_diag*w; 00944 const int o5 = -dx_diag + dy_diag*w; 00945 const int o7 = dx_diag + dy_diag*w; 00946 00947 const int o9 = (dx_diag - dy_diag*w)*2; 00948 const int o11 = (-dx_diag - dy_diag*w)*2; 00949 const int o13 = (-dx_diag + dy_diag*w)*2; 00950 const int o15 = (dx_diag + dy_diag*w)*2; 00951 00952 // get pointers to the image pixel data: 00953 typename Image<T>::const_iterator 00954 ip0 = i0.begin() + dx + dy*w, ip45 = i45.begin() + dx + dy*w, 00955 ip90 = i90.begin() + dx + dy*w, ip135 = i135.begin() + dx + dy*w; 00956 typename Image<T>::iterator rp = result.beginw() + dx + dy*w; 00957 00958 // loop over the bulk of the images (i.e., all except a border of 00959 // width dx and height dy): 00960 00961 const T max = std::numeric_limits<T>::max(); 00962 00963 for (int j = (dy*2); j < h; j ++) 00964 { 00965 for (int i = (dx*2); i < w; i ++) 00966 { 00967 00968 // get the various feature values: 00969 const T r0 = ip0[o0], r4 = ip0[o4], r8 = ip0[o8], r12 = ip0[o12]; ++ip0; 00970 const T r1 = ip45[o1], r5 = ip45[o5], r9 = ip45[o9], r13 = ip45[o13]; ++ip45; 00971 const T r2 = ip90[o2], r6 = ip90[o6], r10 = ip90[o10], r14 = ip90[o14]; ++ip90; 00972 const T r3 = ip135[o3], r7 = ip135[o7], r11 = ip135[o11], r15 = ip135[o15]; ++ip135; 00973 00974 // for this full implementation, let's start by finding out the 00975 // minimum relevant response, so that we can then check the 00976 // irrelevant responses against that baseline: 00977 T minRel = max; 00978 if (r[0] && r0 < minRel) minRel = r0; 00979 if (r[1] && r1 < minRel) minRel = r1; 00980 if (r[2] && r2 < minRel) minRel = r2; 00981 if (r[3] && r3 < minRel) minRel = r3; 00982 if (r[4] && r4 < minRel) minRel = r4; 00983 if (r[5] && r5 < minRel) minRel = r5; 00984 if (r[6] && r6 < minRel) minRel = r6; 00985 if (r[7] && r7 < minRel) minRel = r7; 00986 00987 if (r[0] && r8 < minRel) minRel = r8; 00988 if (r[1] && r9 < minRel) minRel = r9; 00989 if (r[2] && r10 < minRel) minRel = r10; 00990 if (r[3] && r11 < minRel) minRel = r11; 00991 if (r[4] && r12 < minRel) minRel = r12; 00992 if (r[5] && r13 < minRel) minRel = r13; 00993 if (r[6] && r14 < minRel) minRel = r14; 00994 if (r[7] && r15 < minRel) minRel = r15; 00995 minRel /= 2; 00996 00997 // now let's compute the response of the MST: 00998 float resp = 1.0f; 00999 01000 // One at a time we test to see if an irr feature is stronger 01001 // than one of our rel features. If so, we return a zero reponse 01002 // and quit. Otherwise we keep going and normalize the final response. 01003 // Notice we only get a non-zero reponse of all irr features are 01004 // less than 1/2 of the minimum rel feature. 01005 01006 if (r[0]) resp *= r0; else resp *= (minRel - r0); 01007 if (resp <= 0.0f) 01008 resp = 0.0f; 01009 else 01010 { 01011 if (r[1]) resp *= r1; else resp *= (minRel - r1); 01012 if (resp <= 0.0f) 01013 resp = 0.0f; 01014 else 01015 { 01016 if (r[2]) resp *= r2; else resp *= (minRel - r2); 01017 if (resp <= 0.0f) 01018 resp = 0.0f; 01019 else 01020 { 01021 if (r[3]) resp *= r3; else resp *= (minRel - r3); 01022 if (resp <= 0.0f) 01023 resp = 0.0f; 01024 else 01025 { 01026 if (r[4]) resp *= r4; else resp *= (minRel - r4); 01027 if (resp <= 0.0f) resp = 0.0f; 01028 else 01029 { 01030 if (r[5]) resp *= r5; else resp *= (minRel - r5); 01031 if (resp <= 0.0f) resp = 0.0f; 01032 else 01033 { 01034 if (r[6]) resp *= r6; else resp *= (minRel - r6); 01035 if (resp <= 0.0f) resp = 0.0f; 01036 else 01037 { 01038 if (r[7]) resp *= r7; else resp *= (minRel - r7); 01039 if (resp <= 0.0f) resp = 0.0f; 01040 else 01041 { 01042 if (r[0]) resp *= r8; else resp *= (minRel - r8); 01043 if (resp <= 0.0f && r8<r0) 01044 resp = 0.0f; 01045 else 01046 { 01047 if (r[1]) resp *= r9; else resp *= (minRel - r9); 01048 if (resp <= 0.0f && r9<r1) 01049 resp = 0.0f; 01050 else 01051 { 01052 if (r[2]) resp *= r10; else resp *= (minRel - r10); 01053 if (resp <= 0.0f && r10<r2) 01054 resp = 0.0f; 01055 else 01056 { 01057 if (r[3]) resp *= r11; else resp *= (minRel - r11); 01058 if (resp <= 0.0f && r11<r3) 01059 resp = 0.0f; 01060 else 01061 { 01062 if (r[4]) resp *= r12; else resp *= (minRel - r12); 01063 if (resp <= 0.0f && r12<r4) resp = 0.0f; 01064 else 01065 { 01066 if (r[5]) resp *= r13; else resp *= (minRel - r13); 01067 if (resp <= 0.0f && r13<r5) resp = 0.0f; 01068 else 01069 { 01070 if (r[6]) resp *= r14; else resp *= (minRel - r14); 01071 if (resp <= 0.0f && r14<r6) resp = 0.0f; 01072 else 01073 { 01074 if (r[7]) resp *= r15; else resp *= (minRel - r15); 01075 if (resp <= 0.0f && r15<r7) resp = 0.0f; 01076 else 01077 { 01078 // normalize it by the number of features: 01079 resp = pow(resp, 1.0/8.0); 01080 } 01081 } 01082 } 01083 } 01084 } 01085 } 01086 } 01087 } 01088 // normalize it by the number of features: 01089 // resp = pow(resp, 1.0/8.0); 01090 } 01091 } 01092 } 01093 } 01094 } 01095 } 01096 } 01097 } 01098 01099 // store the response 01100 *rp++ = T(resp); 01101 } 01102 // skip border and go to next row of pixels: 01103 ip0 += dx*2; ip45 += dx*2; ip90 += dx*2; ip135 += dx*2; rp += dx*2; 01104 } 01105 01106 return result; 01107 } 01108 01109 // ###################################################################### 01110 namespace 01111 { 01112 // Trivial helper function for MSTFilterPartial() 01113 template <class DstItr, class SrcItr> 01114 inline void MSTFILT(DstItr dptr, SrcItr sptr, 01115 const int w, const int jmax, const int imax) 01116 { 01117 for(int j = 0; j < jmax; ++j) 01118 { 01119 for (int i = 0; i < imax; ++i) 01120 dptr[i] *= sptr[i]; 01121 01122 sptr += w; dptr += w; 01123 } 01124 } 01125 } 01126 01127 template <class T> 01128 Image<T> MSTFilterPartial(const Image<T>& i0, const Image<T>& i45, 01129 const Image<T>& i90, const Image<T>& i135, 01130 const bool r[8], const int dx = 6, 01131 const int dy = 6, 01132 const bool useEuclidDiag = false) 01133 { 01134 GVX_TRACE(__PRETTY_FUNCTION__); 01135 // the preamble is identical to MSTFilterFull: 01136 01137 ASSERT(i0.isSameSize(i45) && i0.isSameSize(i90) && i0.isSameSize(i135)); 01138 Image<T> result(i0.getDims(), ZEROS); 01139 01140 const int w = i0.getWidth(), h = i0.getHeight(); 01141 01142 int dx_diag, dy_diag; 01143 01144 // Compute the diagonal offsets if needed as a euclidian distance 01145 // from the center in dx,dy or should we just put it on a "grid" 01146 if(useEuclidDiag) 01147 { 01148 dx_diag = (int)round(fastSqrt((dx*dx)/2.0f)); 01149 dy_diag = (int)round(fastSqrt((dy*dy)/2.0f)); 01150 } 01151 else 01152 { 01153 dx_diag = dx; 01154 dy_diag = dy; 01155 } 01156 01157 // non diagonal elements 01158 const int o0 = dx; 01159 const int o2 = -dy*w; 01160 const int o4 = -dx; 01161 const int o6 = dy*w; 01162 01163 const int o8 = dx*2; 01164 const int o10 = -dy*w*2; 01165 const int o12 = -dx*2; 01166 const int o14 = dy*w*2; 01167 01168 // diagonal elements 01169 const int o1 = dx_diag - dy_diag*w; 01170 const int o3 = -dx_diag - dy_diag*w; 01171 const int o5 = -dx_diag + dy_diag*w; 01172 const int o7 = dx_diag + dy_diag*w; 01173 01174 const int o9 = (dx_diag - dy_diag*w)*2; 01175 const int o11 = (-dx_diag - dy_diag*w)*2; 01176 const int o13 = (-dx_diag + dy_diag*w)*2; 01177 const int o15 = (dx_diag + dy_diag*w)*2; 01178 01179 const int offset = dx + dy*w; 01180 01181 typename Image<T>::iterator const rpp = result.beginw() + offset; 01182 01183 // compute the number of relevant features (for normalization): 01184 int nr = 0; for (int i = 0; i < 8; i++) if (r[i]) nr++; 01185 01186 const int imax = w - dx*2; 01187 const int jmax = h - dy*2; 01188 01189 // initialize the valid portion of the response array to 1.0's 01190 { 01191 typename Image<T>::iterator rp = rpp; 01192 for (int j = 0; j < jmax; ++j) 01193 { 01194 for (int i = 0; i < imax; ++i) 01195 rp[i] = T(1.0); 01196 rp += w; 01197 } 01198 } 01199 01200 // loop over the bulk of the images, computing the responses of 01201 // the MST filter: 01202 { 01203 if (r[0]) MSTFILT(rpp, i0.begin() + o0 + offset, w, jmax, imax); 01204 if (r[1]) MSTFILT(rpp, i45.begin() + o1 + offset, w, jmax, imax); 01205 if (r[2]) MSTFILT(rpp, i90.begin() + o2 + offset, w, jmax, imax); 01206 if (r[3]) MSTFILT(rpp, i135.begin() + o3 + offset, w, jmax, imax); 01207 if (r[4]) MSTFILT(rpp, i0.begin() + o4 + offset, w, jmax, imax); 01208 if (r[5]) MSTFILT(rpp, i45.begin() + o5 + offset, w, jmax, imax); 01209 if (r[6]) MSTFILT(rpp, i90.begin() + o6 + offset, w, jmax, imax); 01210 if (r[7]) MSTFILT(rpp, i135.begin() + o7 + offset, w, jmax, imax); 01211 01212 if (r[0]) MSTFILT(rpp, i0.begin() + o8 + offset, w, jmax, imax); 01213 if (r[1]) MSTFILT(rpp, i45.begin() + o9 + offset, w, jmax, imax); 01214 if (r[2]) MSTFILT(rpp, i90.begin() + o10 + offset, w, jmax, imax); 01215 if (r[3]) MSTFILT(rpp, i135.begin() + o11 + offset, w, jmax, imax); 01216 if (r[4]) MSTFILT(rpp, i0.begin() + o12 + offset, w, jmax, imax); 01217 if (r[5]) MSTFILT(rpp, i45.begin() + o13 + offset, w, jmax, imax); 01218 if (r[6]) MSTFILT(rpp, i90.begin() + o14 + offset, w, jmax, imax); 01219 if (r[7]) MSTFILT(rpp, i135.begin() + o15 + offset, w, jmax, imax); 01220 01221 } 01222 01223 // normalize the responses by the number of relevant features, 01224 // optimizing by using sqrt() or cbrt() where possible (this gives 01225 // an average speedup of ~3x across all MST filter types): 01226 { 01227 typename Image<T>::iterator rp = rpp; 01228 if (nr == 1) 01229 { 01230 // nothing to do here 01231 } 01232 else if (nr == 2) 01233 { 01234 for (int j = 0; j < jmax; ++j) 01235 { 01236 for (int i = 0; i < imax; ++i) 01237 rp[i] = T(fastSqrt(rp[i])); 01238 rp += w; 01239 } 01240 } 01241 else if (nr == 3) 01242 { 01243 for (int j = 0; j < jmax; ++j) 01244 { 01245 for (int i = 0; i < imax; ++i) 01246 rp[i] = T(cbrt(rp[i])); 01247 rp += w; 01248 } 01249 } 01250 else if (nr == 4) 01251 { 01252 for (int j = 0; j < jmax; ++j) 01253 { 01254 for (int i = 0; i < imax; ++i) 01255 rp[i] = T(fastSqrt(fastSqrt(rp[i]))); 01256 rp += w; 01257 } 01258 } 01259 else 01260 { 01261 const double power = 1.0 / nr; 01262 for (int j = 0; j < jmax; ++j) 01263 { 01264 for (int i = 0; i < imax; ++i) 01265 rp[i] = T(pow(rp[i], power)); 01266 rp += w; 01267 } 01268 } 01269 } 01270 return result; 01271 } 01272 01273 01274 // ###################################################################### 01275 01276 template <class T> 01277 Image<typename promote_trait<T, float>::TP> gradientmag(const Image<T>& input) 01278 { 01279 GVX_TRACE(__PRETTY_FUNCTION__); 01280 typedef typename promote_trait<T, float>::TP TF; 01281 01282 Image<TF> result(input.getDims(), NO_INIT); 01283 typename Image<T>::const_iterator src = input.begin(); 01284 typename Image<TF>::iterator dst = result.beginw(); 01285 const int w = input.getWidth(), h = input.getHeight(); 01286 TF zero = TF(); 01287 01288 // first row is all zeros: 01289 for (int i = 0; i < w; i ++) *dst ++ = zero; 01290 src += w; 01291 01292 // loop over inner rows: 01293 for (int j = 1; j < h-1; j ++) 01294 { 01295 // leftmost pixel is zero: 01296 *dst ++ = zero; ++ src; 01297 01298 // loop over inner columns: 01299 for (int i = 1; i < w-1; i ++) 01300 { 01301 TF valx = src[1] - src[-1]; 01302 TF valy = src[w] - src[-w]; 01303 01304 *dst++ = sqrt(valx * valx + valy * valy); 01305 ++ src; 01306 } 01307 01308 // rightmost pixel is zero: 01309 *dst ++ = zero; ++ src; 01310 } 01311 01312 // last row is all zeros: 01313 for (int i = 0; i < w; i ++) *dst ++ = zero; 01314 01315 return result; 01316 } 01317 01318 // ###################################################################### 01319 template <class T> 01320 Image<typename promote_trait<T, float>::TP> gradientori(const Image<T>& input) 01321 { 01322 GVX_TRACE(__PRETTY_FUNCTION__); 01323 typedef typename promote_trait<T, float>::TP TF; 01324 01325 Image<TF> result(input.getDims(), NO_INIT); 01326 typename Image<T>::const_iterator src = input.begin(); 01327 typename Image<TF>::iterator dst = result.beginw(); 01328 const int w = input.getWidth(), h = input.getHeight(); 01329 TF zero = TF(); 01330 01331 // first row is all zeros: 01332 for (int i = 0; i < w; i ++) *dst ++ = zero; 01333 src += w; 01334 01335 // loop over inner rows: 01336 for (int j = 1; j < h-1; j ++) 01337 { 01338 // leftmost pixel is zero: 01339 *dst ++ = zero; ++ src; 01340 01341 // loop over inner columns: 01342 for (int i = 1; i < w-1; i ++) 01343 { 01344 TF valx = src[1] - src[-1]; 01345 TF valy = src[w] - src[-w]; 01346 01347 *dst++ = atan2(valy, valx); 01348 ++ src; 01349 } 01350 01351 // rightmost pixel is zero: 01352 *dst ++ = zero; ++ src; 01353 } 01354 01355 // last row is all zeros: 01356 for (int i = 0; i < w; i ++) *dst ++ = zero; 01357 01358 return result; 01359 } 01360 01361 // ###################################################################### 01362 template <class T> 01363 void gradient(const Image<T>& input, 01364 Image<typename promote_trait<T, float>::TP>& mag, 01365 Image<typename promote_trait<T, float>::TP>& ori) 01366 { 01367 GVX_TRACE(__PRETTY_FUNCTION__); 01368 typedef typename promote_trait<T, float>::TP TF; 01369 01370 mag.resize(input.getDims()); ori.resize(input.getDims()); 01371 typename Image<T>::const_iterator src = input.begin(); 01372 typename Image<TF>::iterator m = mag.beginw(), o = ori.beginw(); 01373 const int w = input.getWidth(), h = input.getHeight(); 01374 TF zero = TF(); 01375 01376 // first row is all zeros: 01377 for (int i = 0; i < w; i ++) { *m ++ = zero; *o ++ = zero; } 01378 src += w; 01379 01380 // loop over inner rows: 01381 for (int j = 1; j < h-1; j ++) 01382 { 01383 // leftmost pixel is zero: 01384 *m ++ = zero; *o ++ = zero; ++ src; 01385 01386 // loop over inner columns: 01387 for (int i = 1; i < w-1; i ++) 01388 { 01389 TF valx = src[1] - src[-1]; 01390 TF valy = src[w] - src[-w]; 01391 01392 *m++ = sqrt(valx * valx + valy * valy); 01393 *o++ = atan2(valy, valx); 01394 ++ src; 01395 } 01396 01397 // rightmost pixel is zero: 01398 *m ++ = zero; *o ++ = zero; ++ src; 01399 } 01400 01401 // last row is all zeros: 01402 for (int i = 0; i < w; i ++) { *m ++ = zero; *o ++ = zero; } 01403 } 01404 01405 // ###################################################################### 01406 template <class T> 01407 void gradientSobel(const Image<T>& input, 01408 Image<typename promote_trait<T, float>::TP>& mag, 01409 Image<typename promote_trait<T, float>::TP>& ori, 01410 int kernelSize) 01411 { 01412 GVX_TRACE(__PRETTY_FUNCTION__); 01413 typedef typename promote_trait<T, float>::TP TF; 01414 01415 ASSERT( (kernelSize == 3 ) | (kernelSize == 5)); 01416 mag.resize(input.getDims(), true); ori.resize(input.getDims(), true); 01417 typename Image<T>::const_iterator src = input.begin(); 01418 typename Image<TF>::iterator m = mag.beginw(), o = ori.beginw(); 01419 const int w = input.getWidth(), h = input.getHeight(); 01420 TF zero = TF(); 01421 01422 // first rows are all zeros: 01423 switch (kernelSize) { 01424 case 3: 01425 for (int i = 0; i < w; i ++) { *m ++ = zero; *o ++ = zero; } 01426 src += w; 01427 break; 01428 case 5: 01429 for (int i = 0; i < w*2; i ++) { *m ++ = zero; *o ++ = zero; } 01430 src += w*2; 01431 break; 01432 } 01433 01434 switch (kernelSize){ 01435 case 3: 01436 // loop over inner rows: 01437 for (int j = 1; j < h-1; j ++) 01438 { 01439 // leftmost pixel is zero: 01440 *m ++ = zero; *o ++ = zero; ++ src; 01441 // loop over inner columns: 01442 for (int i = 1; i < w-1; i ++) 01443 { 01444 TF valx = -1*src[-1*w + -1] + 0*src[-1*w + 0] + 1*src[-1*w + 1] 01445 + -2*src[ 0*w + -1] + 0*src[ 0*w + 0] + 2*src[ 0*w + 1] 01446 + -1*src[ 1*w + -1] + 0*src[ 1*w + 0] + 1*src[ 1*w + 1]; 01447 01448 TF valy = 1*src[-1*w + -1] + 2*src[-1*w + 0] + 1*src[-1*w + 1] 01449 + 0*src[ 0*w + -1] + 0*src[ 0*w + 0] + 0*src[ 0*w + 1] 01450 + -1*src[ 1*w + -1] + -2*src[ 1*w + 0] + -1*src[ 1*w + 1]; 01451 01452 *m++ = sqrt(valx * valx + valy * valy); 01453 *o++ = atan2(valy, valx); 01454 ++ src; 01455 } 01456 // rightmost pixel is zero: 01457 *m ++ = zero; *o ++ = zero; ++ src; 01458 } 01459 break; 01460 01461 case 5: 01462 // loop over inner rows: 01463 for (int j = 2; j < h-2; j ++) 01464 { 01465 // leftmost pixel is zero: 01466 *m ++ = zero; *o ++ = zero; ++ src; 01467 *m ++ = zero; *o ++ = zero; ++ src; 01468 // loop over inner columns: 01469 for (int i = 2; i < w-2; i ++) 01470 { 01471 TF valx = -1*src[-2*w + -2] + -2*src[-2*w + -1] + 0*src[-2*w + 0] + 2*src[-2*w + 1] + 1*src[-2*w + 2] 01472 + -4*src[-1*w + -2] + -8*src[-1*w + -1] + 0*src[-1*w + 0] + 8*src[-1*w + 1] + 4*src[-1*w + 2] 01473 + -6*src[ 0*w + -2] + -12*src[ 0*w + -1] + 0*src[ 0*w + 0] + 12*src[ 0*w + 1] + 6*src[ 0*w + 2] 01474 + -4*src[ 1*w + -2] + -8*src[ 1*w + -1] + 0*src[ 1*w + 0] + 8*src[ 1*w + 1] + 4*src[ 1*w + 2] 01475 + -1*src[ 2*w + -2] + -2*src[ 2*w + -1] + 0*src[ 2*w + 0] + 2*src[ 2*w + 1] + 1*src[ 2*w + 2]; 01476 01477 TF valy = 1*src[-2*w + -2] + 4*src[-2*w + -1] + 6*src[-2*w + 0] + 4*src[-2*w + 1] + 1*src[-2*w + 2] 01478 + 2*src[-1*w + -2] + 8*src[-1*w + -1] + 12*src[-1*w + 0] + 8*src[-1*w + 1] + 2*src[-1*w + 2] 01479 + 0*src[ 0*w + -2] + 0*src[ 0*w + -1] + 0*src[ 0*w + 0] + 0*src[ 0*w + 1] + 0*src[ 0*w + 2] 01480 + -2*src[ 1*w + -2] + -8*src[ 1*w + -1] + -12*src[ 1*w + 0] + -8*src[ 1*w + 1] + -2*src[ 1*w + 2] 01481 + -1*src[ 2*w + -2] + -4*src[ 2*w + -1] + -6*src[ 2*w + 0] + -4*src[ 2*w + 1] + -1*src[ 2*w + 2]; 01482 01483 *m++ = sqrt(valx * valx + valy * valy); 01484 *o++ = atan2(valy, valx); 01485 ++ src; 01486 } 01487 // rightmost pixel is zero: 01488 *m ++ = zero; *o ++ = zero; ++ src; 01489 } 01490 break; 01491 } 01492 01493 01494 // last rows are all zeros: 01495 switch (kernelSize){ 01496 case 3: 01497 for (int i = 0; i < w; i ++) { *m ++ = zero; *o ++ = zero; } 01498 break; 01499 case 5: 01500 for (int i = 0; i < w*2; i ++) { *m ++ = zero; *o ++ = zero; } 01501 break; 01502 } 01503 } 01504 01505 // ###################################################################### 01506 Image<float> nonMaxSuppr(const Image<float>& mag, const Image<float>& ori) 01507 { 01508 //Non maximal suppersion 01509 Image<float> outImg = mag; //(mag.getDims(), ZEROS); 01510 for(int y=0; y<mag.getHeight(); y++) 01511 for(int x=0; x<mag.getWidth(); x++) 01512 { 01513 if (mag.getVal(x,y) > 0) 01514 { 01515 float t = ori.getVal(x,y); 01516 //if (t<0.57 && t > -0.57) 01517 //{ 01518 // if (mag.getVal(x,y) >= mag.getVal(x,y+1) && 01519 // mag.getVal(x,y) > mag.getVal(x,y-1)) 01520 // outImg.setVal(x,y, mag.getVal(x,y)); 01521 //} else if (t > 1.04 || t < -1.04) 01522 //{ 01523 // if (mag.getVal(x,y) >= mag.getVal(x+1,y) && 01524 // mag.getVal(x,y) > mag.getVal(x-1,y)) 01525 // outImg.setVal(x,y, mag.getVal(x,y)); 01526 //} else if (t>0.57) 01527 //{ 01528 // if (mag.getVal(x,y) >= mag.getVal(x-1,y+1) && 01529 // mag.getVal(x,y) > mag.getVal(x+1,y-1)) 01530 // outImg.setVal(x,y, mag.getVal(x,y)); 01531 //} else { 01532 // if (mag.getVal(x,y) >= mag.getVal(x-1,y-1) && 01533 // mag.getVal(x,y) > mag.getVal(x+1,y+1)) 01534 // outImg.setVal(x,y, mag.getVal(x,y)); 01535 //} 01536 01537 01538 int dx = int(1.5*cos(t)); 01539 int dy = int(1.5*sin(t)); 01540 01541 if (mag.coordsOk(x+dx, y-dy) && 01542 mag.coordsOk(x-dx, y+dy) ) 01543 { 01544 //Remove the edge if its not a local maxima 01545 if (mag.getVal(x,y) < mag.getVal(x+dx, y-dy) || 01546 mag.getVal(x,y) <= mag.getVal(x-dx, y+dy) ) 01547 outImg.setVal(x,y,0); 01548 01549 } 01550 01551 } 01552 } 01553 01554 return outImg; 01555 } 01556 01557 // ###################################################################### 01558 template <class T> 01559 Image<T> shuffleImage(const Image<T> &img) 01560 { 01561 //result image 01562 Image<T> result(img.getDims(), NO_INIT); 01563 01564 uint imgSize = img.getSize(); 01565 for(uint i=0; i < imgSize; i++) 01566 { 01567 uint j = i + rand() / (RAND_MAX / (imgSize-i) + 1); 01568 result[j] = img[i]; 01569 result[i] = img[j]; 01570 } 01571 01572 return result; 01573 } 01574 01575 01576 // Include the explicit instantiations 01577 #include "inst/Image/FilterOps.I" 01578 01579 // ###################################################################### 01580 /* So things look consistent in everyone's emacs... */ 01581 /* Local Variables: */ 01582 /* indent-tabs-mode: nil */ 01583 /* End: */