00001 #include "CUDA/CudaImage.H" 00002 #include "CUDA/CudaImageSet.H" 00003 #include "Util/log.H" 00004 #include "Image/PyramidOps.H" 00005 #include "CudaPyramidOps.H" 00006 #include "CUDA/CudaLowPass.H" 00007 #include "CUDA/CudaKernels.H" 00008 #include "CUDA/CudaMathOps.H" 00009 #include "CUDA/CudaShapeOps.H" 00010 #include "CUDA/CudaFilterOps.H" 00011 00012 // ###################################################################### 00013 // ##### Pyramid builder functions: 00014 // ###################################################################### 00015 00016 // ###################################################################### 00017 CudaImageSet<float> cudaBuildPyrGaussian(const CudaImage<float>& image, 00018 int firstlevel, int depth, int filterSize) 00019 { 00020 ASSERT(image.initialized()); 00021 00022 CudaImageSet<float> result(depth); 00023 00024 if (0 >= firstlevel) 00025 result[0] = image; 00026 00027 CudaImage<float> a = CudaImage<float>(image); 00028 for (int lev = 1; lev < depth; ++lev) 00029 { 00030 switch(filterSize) 00031 { 00032 case 5: 00033 a = cudaLowPass5yDec(cudaLowPass5xDec(a)); 00034 break; 00035 default: 00036 a = cudaDecX(cudaLowPassX(filterSize, a)); 00037 a = cudaDecY(cudaLowPassY(filterSize, a)); 00038 break; 00039 } 00040 00041 // NOTE: when lev < firstlevel, we leave result[lev] empty even 00042 // though it isn't any extra work to fill it in; that way, other 00043 // functions that see the result ImageSet might be able to gain 00044 // additional speed-ups by seeing that some of the images are 00045 // empty and avoid processing them further 00046 if (lev >= firstlevel) 00047 result[lev] = a; 00048 } 00049 00050 return result; 00051 } 00052 00053 00054 00055 // ###################################################################### 00056 CudaImageSet<float> cudaBuildPyrLaplacian(const CudaImage<float>& image, int firstlevel, int depth, int filterSize) 00057 { 00058 ASSERT(image.initialized()); 00059 00060 CudaImageSet<float> result(depth); 00061 00062 CudaImage<float> lpf = cudaLowPass(filterSize, image); 00063 00064 if (0 >= firstlevel) 00065 result[0] = cudaSubtractImages(image,lpf); 00066 for (int lev = 1; lev < depth; ++lev) 00067 { 00068 const CudaImage<float> dec = cudaDecXY(lpf); 00069 lpf = cudaLowPass(filterSize, dec); 00070 00071 if (lev >= firstlevel) 00072 result[lev] = cudaSubtractImages(dec,lpf); 00073 00074 } 00075 00076 return result; 00077 } 00078 00079 00080 // ###################################################################### 00081 CudaImageSet<float> cudaBuildPyrOrientedFromLaplacian(const CudaImageSet<float>& laplacian, 00082 int filterSize, 00083 float theta, float intens) 00084 { 00085 int attenuation_width = -1; 00086 float spatial_freq = -1.0; 00087 00088 switch (filterSize) 00089 { 00090 case 5: attenuation_width = 3; spatial_freq = M_PI_2; break; 00091 case 9: attenuation_width = 5; spatial_freq = 2.6; break; 00092 default: 00093 LFATAL("Filter size %d is not supported", filterSize); 00094 } 00095 00096 // compute oriented filter from laplacian: 00097 CudaImageSet<float> result(laplacian.size()); 00098 00099 for (size_t lev = 0; lev < result.size(); ++lev) 00100 { 00101 if (laplacian[lev].initialized()) 00102 { 00103 result[lev] = cudaOrientedFilter(laplacian[lev], spatial_freq, 00104 theta, intens); 00105 // attenuate borders that are overestimated due to filter trunctation: 00106 cudaInplaceAttenuateBorders(result[lev], attenuation_width); 00107 } 00108 } 00109 00110 return result; 00111 } 00112 00113 // ###################################################################### 00114 CudaImageSet<float> cudaBuildPyrOriented(const CudaImage<float>& image, int firstlevel, int depth, int filterSize, 00115 float theta, float intens) 00116 { 00117 ASSERT(image.initialized()); 00118 00119 const CudaImageSet<float> laplacian = cudaBuildPyrLaplacian(image, firstlevel, depth, filterSize); 00120 00121 const CudaImageSet<float> orifilt = 00122 cudaBuildPyrOrientedFromLaplacian(laplacian, filterSize, theta, 00123 intens); 00124 00125 CudaImageSet<float> result(orifilt.size()); 00126 for (size_t i = 0; i < result.size(); ++i) 00127 result[i] = orifilt[i]; 00128 00129 return result; 00130 } 00131 00132 00133 00134 // ###################################################################### 00135 CudaImageSet<float> cudaBuildPyrLocalAvg(const CudaImage<float>& image, int depth) 00136 { 00137 ASSERT(image.initialized()); 00138 ASSERT(depth >= 1); 00139 00140 CudaImageSet<float> result(depth); 00141 00142 // only deepest level of pyr is filled with local avg: 00143 const int scale = int(pow(2.0, depth - 1)); 00144 result[depth - 1] = cudaQuickLocalAvg(image, scale); 00145 00146 return result; 00147 } 00148 00149 // ###################################################################### 00150 CudaImageSet<float> cudaBuildPyrLocalAvg2x2(const CudaImage<float>& image, int depth) 00151 { 00152 ASSERT(depth >= 0); 00153 00154 if (depth == 0) 00155 return CudaImageSet<float>(0); 00156 00157 CudaImageSet<float> result(depth); 00158 result[0] = image; 00159 00160 for (int i = 1; i < depth; ++i) 00161 result[i] = cudaQuickLocalAvg2x2(result[i-1]); 00162 00163 return result; 00164 } 00165 00166 // ###################################################################### 00167 CudaImageSet<float> cudaBuildPyrLocalMax(const CudaImage<float>& image, int depth) 00168 { 00169 ASSERT(image.initialized()); 00170 ASSERT(depth >= 1); 00171 00172 CudaImageSet<float> result(depth); 00173 00174 // only deepest level of pyr is filled with local max: 00175 const int scale = int(pow(2.0, depth - 1)); 00176 result[depth - 1] = cudaQuickLocalMax(image, scale); 00177 00178 return result; 00179 } 00180 00181 00182 CudaImageSet<float> cudaBuildPyrGeneric(const CudaImage<float>& image, 00183 int firstlevel, int depth, 00184 const PyramidType typ, const float gabor_theta, 00185 const float intens) 00186 { 00187 switch(typ) 00188 { 00189 case Gaussian3: return cudaBuildPyrGaussian(image, firstlevel, depth, 3); 00190 case Gaussian5: return cudaBuildPyrGaussian(image, firstlevel, depth, 5); 00191 case Gaussian9: return cudaBuildPyrGaussian(image, firstlevel, depth, 9); 00192 00193 case Laplacian5: return cudaBuildPyrLaplacian(image, firstlevel, depth, 5); 00194 case Laplacian9: return cudaBuildPyrLaplacian(image, firstlevel, depth, 9); 00195 00196 case Oriented5: return cudaBuildPyrOriented(image, firstlevel, depth, 5, gabor_theta, intens); 00197 case Oriented9: return cudaBuildPyrOriented(image, firstlevel, depth, 9, gabor_theta, intens); 00198 00199 case QuickLocalAvg: return cudaBuildPyrLocalAvg(image, depth); 00200 case QuickLocalMax: return cudaBuildPyrLocalMax(image, depth); 00201 00202 default: 00203 LFATAL("Attempt to create Pyramid of unknown type %d", typ); 00204 } 00205 00206 ASSERT(false); 00207 return CudaImageSet<float>(0); // "can't happen", but placate compiler 00208 } 00209 00210 00211 00212 // ###################################################################### 00213 // CudaImageSet<float> cudaBuildPyrGabor(const CudaImageSet<float>& gaussianPyr, 00214 // float angle, float filter_period, 00215 // float elongation, int size, int flags, MemoryPolicy mp, int dev) 00216 // { 00217 // const double major_stddev = filter_period / 3.0; 00218 // const double minor_stddev = major_stddev * elongation; 00219 00220 // // We have to add 90 to the angle here when constructing the gabor 00221 // // filter. That's because the angle used to build the gabor filter 00222 // // actually specifies the direction along which the grating 00223 // // varies. This direction is orthogonal to the the direction of the 00224 // // contours that the grating will detect. 00225 // const double theta = angle + 90.0f; 00226 00227 // // In concept, we want to filter with four phases of the filter: odd 00228 // // on+off, and even on+off (that would be xox, oxo, xo, and ox). But 00229 // // since the on version just produces the negation of the off version, 00230 // // we can get the summed output of both by just taking the absolute 00231 // // value of the outputs of one of them. So we only need to convolve 00232 // // with the filter in two phases, then take the absolute value (i.e., 00233 // // we're doing |xox| and |xo|). 00234 00235 // CudaImage<float> g0 = cudaGaborFilter3(major_stddev, minor_stddev, 00236 // filter_period, 0.0f, theta, size, mp, dev); 00237 // CudaImage<float> g90 = cudaGaborFilter3(major_stddev, minor_stddev, 00238 // filter_period, 90.0f, theta, size, mp, dev); 00239 // LDEBUG("angle = %.2f, period = %.2f pix, size = %dx%d pix", 00240 // angle, filter_period, g0.getWidth(), g0.getHeight()); 00241 00242 // CudaImage<float> f0, f90; 00243 // CudaImageSet<float> result(gaussianPyr.size()); 00244 00245 // for (uint i = 0; i < gaussianPyr.size(); ++i) 00246 // { 00247 // // if the i'th level in our input is empty, then leave it empty 00248 // // in the output as well: 00249 // if (!gaussianPyr[i].initialized()) 00250 // continue; 00251 // if (flags & DO_ENERGY_NORM) 00252 // { 00253 // CudaImage<float> temp = cudaEnergyNorm(in); 00254 // f0 = cudaOptConvolve(temp, g0); 00255 // f90 = cudaOptConvolve(temp, g90); 00256 // } 00257 // else 00258 // { 00259 // f0 = cudaOptConvolve(in, g0); 00260 // f90 = cudaOptConvolve(in, g90); 00261 // } 00262 00263 // if (!(flags & NO_ABS)) 00264 // { 00265 // f0 = cudaAbs(f0); 00266 // f90 = cudaAbs(f90); 00267 // } 00268 // result[i] = f0; 00269 // result[i] += f90; 00270 // } 00271 // return result; 00272 // } 00273 00274 00275 CudaImage<float> cudaCenterSurround(const CudaImageSet<float>& pyr, 00276 const int lev1, const int lev2, 00277 const bool absol, 00278 const CudaImageSet<float>* clipPyr) 00279 { 00280 ASSERT(lev1 >= 0 && lev2 >= 0); 00281 ASSERT(uint(lev1) < pyr.size() && uint(lev2) < pyr.size()); 00282 00283 const int largeLev = std::min(lev1, lev2); 00284 const int smallLev = std::max(lev1, lev2); 00285 00286 if (clipPyr != 0 && clipPyr->isNonEmpty()) 00287 { 00288 ASSERT((*clipPyr)[largeLev].getDims() == pyr[largeLev].getDims()); 00289 ASSERT((*clipPyr)[smallLev].getDims() == pyr[smallLev].getDims()); 00290 00291 return 00292 ::cudaCenterSurround(pyr[largeLev]*(*clipPyr)[largeLev], 00293 pyr[smallLev]*(*clipPyr)[smallLev], 00294 absol); 00295 } 00296 else 00297 return ::cudaCenterSurround(pyr[largeLev], pyr[smallLev], absol); 00298 } 00299 00300 void cudaCenterSurround(const CudaImageSet<float>& pyr, 00301 const int lev1, const int lev2, 00302 CudaImage<float>& pos, CudaImage<float>& neg, 00303 const CudaImageSet<float>* clipPyr) 00304 { 00305 ASSERT(lev1 >= 0 && lev2 >= 0); 00306 ASSERT(uint(lev1) < pyr.size() && uint(lev2) < pyr.size()); 00307 00308 const int largeLev = std::min(lev1, lev2); 00309 const int smallLev = std::max(lev1, lev2); 00310 00311 if (clipPyr != 0 && clipPyr->isNonEmpty()) 00312 { 00313 ASSERT((*clipPyr)[largeLev].getDims() == pyr[largeLev].getDims()); 00314 ASSERT((*clipPyr)[smallLev].getDims() == pyr[smallLev].getDims()); 00315 00316 ::cudaCenterSurround(pyr[largeLev]*(*clipPyr)[largeLev], 00317 pyr[smallLev]*(*clipPyr)[smallLev], 00318 pos, neg); 00319 } 00320 else 00321 ::cudaCenterSurround(pyr[largeLev], pyr[smallLev], pos, neg); 00322 } 00323 00324 CudaImage<float> cudaCenterSurroundSingleOpponent(const CudaImageSet<float>& cpyr, 00325 const CudaImageSet<float>& spyr, 00326 const int lev1, const int lev2, 00327 const bool absol, 00328 const CudaImageSet<float>* clipPyr) 00329 { 00330 ASSERT(lev1 >= 0 && lev2 >= 0); 00331 ASSERT(uint(lev1) < cpyr.size() && uint(lev2) < cpyr.size()); 00332 ASSERT(uint(lev1) < spyr.size() && uint(lev2) < spyr.size()); 00333 00334 const int largeLev = std::min(lev1, lev2); 00335 const int smallLev = std::max(lev1, lev2); 00336 00337 if (clipPyr != 0 && clipPyr->isNonEmpty()) 00338 { 00339 ASSERT((*clipPyr)[largeLev].getDims() == cpyr[largeLev].getDims()); 00340 ASSERT((*clipPyr)[smallLev].getDims() == cpyr[smallLev].getDims()); 00341 ASSERT((*clipPyr)[largeLev].getDims() == spyr[largeLev].getDims()); 00342 ASSERT((*clipPyr)[smallLev].getDims() == spyr[smallLev].getDims()); 00343 00344 return 00345 ::cudaCenterSurround(cpyr[largeLev]*(*clipPyr)[largeLev], 00346 spyr[smallLev]*(*clipPyr)[smallLev], 00347 absol); 00348 } 00349 else 00350 return ::cudaCenterSurround(cpyr[largeLev], spyr[smallLev], absol); 00351 } 00352 00353 void cudaCenterSurroundSingleOpponent(const CudaImageSet<float>& cpyr, 00354 const CudaImageSet<float>& spyr, 00355 const int lev1, const int lev2, 00356 CudaImage<float>& pos, CudaImage<float>& neg, 00357 const CudaImageSet<float>* clipPyr) 00358 { 00359 ASSERT(lev1 >= 0 && lev2 >= 0); 00360 ASSERT(uint(lev1) < cpyr.size() && uint(lev2) < cpyr.size()); 00361 ASSERT(uint(lev1) < spyr.size() && uint(lev2) < spyr.size()); 00362 00363 const int largeLev = std::min(lev1, lev2); 00364 const int smallLev = std::max(lev1, lev2); 00365 00366 if (clipPyr != 0 && clipPyr->isNonEmpty()) 00367 { 00368 ASSERT((*clipPyr)[largeLev].getDims() == cpyr[largeLev].getDims()); 00369 ASSERT((*clipPyr)[smallLev].getDims() == cpyr[smallLev].getDims()); 00370 ASSERT((*clipPyr)[largeLev].getDims() == spyr[largeLev].getDims()); 00371 ASSERT((*clipPyr)[smallLev].getDims() == spyr[smallLev].getDims()); 00372 00373 ::cudaCenterSurround(cpyr[largeLev]*(*clipPyr)[largeLev], 00374 spyr[smallLev]*(*clipPyr)[smallLev], 00375 pos, neg); 00376 } 00377 else 00378 ::cudaCenterSurround(cpyr[largeLev], spyr[smallLev], pos, neg); 00379 } 00380 00381 CudaImage<float> cudaCenterSurroundDiff(const CudaImageSet<float>& pyr1, 00382 const CudaImageSet<float>& pyr2, 00383 const int lev1, const int lev2, 00384 const bool absol, 00385 const CudaImageSet<float>* clipPyr) 00386 { 00387 ASSERT(lev1 >= 0 && lev2 >= 0); 00388 ASSERT(uint(lev1) < pyr1.size() && uint(lev2) < pyr1.size()); 00389 00390 const int largeLev = std::min(lev1, lev2); 00391 const int smallLev = std::max(lev1, lev2); 00392 00393 ASSERT(pyr1[largeLev].getDims() == pyr2[largeLev].getDims()); 00394 ASSERT(pyr1[smallLev].getDims() == pyr2[smallLev].getDims()); 00395 00396 // compute differences between the two pyramids: 00397 const CudaImage<float> limg = pyr1[largeLev]-pyr2[largeLev]; 00398 const CudaImage<float> simg = pyr1[smallLev]-pyr2[smallLev]; 00399 00400 if (clipPyr != 0 && clipPyr->isNonEmpty()) 00401 { 00402 ASSERT((*clipPyr)[largeLev].getDims() == limg.getDims()); 00403 ASSERT((*clipPyr)[smallLev].getDims() == simg.getDims()); 00404 00405 return ::cudaCenterSurround(limg*(*clipPyr)[largeLev], 00406 simg*(*clipPyr)[smallLev], 00407 absol); 00408 } 00409 else 00410 return ::cudaCenterSurround(limg, simg, absol); 00411 } 00412 00413 void cudaCenterSurroundDiff(const CudaImageSet<float>& pyr1, 00414 const CudaImageSet<float>& pyr2, 00415 const int lev1, const int lev2, 00416 CudaImage<float>& pos, CudaImage<float>& neg, 00417 const CudaImageSet<float>* clipPyr) 00418 { 00419 ASSERT(lev1 >= 0 && lev2 >= 0); 00420 ASSERT(uint(lev1) < pyr1.size() && uint(lev2) < pyr1.size()); 00421 00422 const int largeLev = std::min(lev1, lev2); 00423 const int smallLev = std::max(lev1, lev2); 00424 00425 ASSERT(pyr1[largeLev].getDims() == pyr2[largeLev].getDims()); 00426 ASSERT(pyr1[smallLev].getDims() == pyr2[smallLev].getDims()); 00427 00428 // compute differences between the two pyramids: 00429 const CudaImage<float> limg = pyr1[largeLev]-pyr2[largeLev]; 00430 const CudaImage<float> simg = pyr1[smallLev]-pyr2[smallLev]; 00431 00432 if (clipPyr != 0 && clipPyr->isNonEmpty()) 00433 { 00434 ASSERT((*clipPyr)[largeLev].getDims() == limg.getDims()); 00435 ASSERT((*clipPyr)[smallLev].getDims() == simg.getDims()); 00436 00437 ::cudaCenterSurround(limg*(*clipPyr)[largeLev], 00438 simg*(*clipPyr)[smallLev], 00439 pos, neg); 00440 } 00441 else 00442 ::cudaCenterSurround(limg, simg, pos, neg); 00443 } 00444 00445 00446 CudaImage<float> cudaCenterSurroundDiffSingleOpponent(const CudaImageSet<float>& cpyr1, 00447 const CudaImageSet<float>& cpyr2, 00448 const CudaImageSet<float>& spyr1, 00449 const CudaImageSet<float>& spyr2, 00450 const int lev1, const int lev2, 00451 const bool absol, 00452 const CudaImageSet<float>* clipPyr) 00453 { 00454 ASSERT(lev1 >= 0 && lev2 >= 0); 00455 ASSERT(uint(lev1) < cpyr1.size() && uint(lev2) < cpyr1.size()); 00456 00457 const int largeLev = std::min(lev1, lev2); 00458 const int smallLev = std::max(lev1, lev2); 00459 00460 ASSERT(cpyr1[largeLev].getDims() == cpyr2[largeLev].getDims()); 00461 ASSERT(cpyr1[smallLev].getDims() == cpyr2[smallLev].getDims()); 00462 ASSERT(spyr1[largeLev].getDims() == spyr2[largeLev].getDims()); 00463 ASSERT(spyr1[smallLev].getDims() == spyr2[smallLev].getDims()); 00464 00465 // compute differences between the two pyramids: 00466 const CudaImage<float> limg = cpyr1[largeLev]-cpyr2[largeLev]; 00467 const CudaImage<float> simg = spyr1[smallLev]-spyr2[smallLev]; 00468 00469 if (clipPyr != 0 && clipPyr->isNonEmpty()) 00470 { 00471 ASSERT((*clipPyr)[largeLev].getDims() == limg.getDims()); 00472 ASSERT((*clipPyr)[smallLev].getDims() == simg.getDims()); 00473 00474 return ::cudaCenterSurround(limg*(*clipPyr)[largeLev], 00475 simg*(*clipPyr)[smallLev], 00476 absol); 00477 } 00478 else 00479 return ::cudaCenterSurround(limg, simg, absol); 00480 } 00481 00482 00483 void cudaCenterSurroundDiffSingleOpponent(const CudaImageSet<float>& cpyr1, 00484 const CudaImageSet<float>& cpyr2, 00485 const CudaImageSet<float>& spyr1, 00486 const CudaImageSet<float>& spyr2, 00487 const int lev1, const int lev2, 00488 CudaImage<float>& pos, CudaImage<float>& neg, 00489 const CudaImageSet<float>* clipPyr) 00490 { 00491 ASSERT(lev1 >= 0 && lev2 >= 0); 00492 ASSERT(uint(lev1) < cpyr1.size() && uint(lev2) < cpyr1.size()); 00493 00494 const int largeLev = std::min(lev1, lev2); 00495 const int smallLev = std::max(lev1, lev2); 00496 00497 ASSERT(cpyr1[largeLev].getDims() == cpyr2[largeLev].getDims()); 00498 ASSERT(cpyr1[smallLev].getDims() == cpyr2[smallLev].getDims()); 00499 ASSERT(spyr1[largeLev].getDims() == spyr2[largeLev].getDims()); 00500 ASSERT(spyr1[smallLev].getDims() == spyr2[smallLev].getDims()); 00501 00502 // compute differences between the two pyramids: 00503 const CudaImage<float> limg = cpyr1[largeLev]-cpyr2[largeLev]; 00504 const CudaImage<float> simg = spyr1[smallLev]-spyr2[smallLev]; 00505 00506 if (clipPyr != 0 && clipPyr->isNonEmpty()) 00507 { 00508 ASSERT((*clipPyr)[largeLev].getDims() == limg.getDims()); 00509 ASSERT((*clipPyr)[smallLev].getDims() == simg.getDims()); 00510 00511 ::cudaCenterSurround(limg*(*clipPyr)[largeLev], 00512 simg*(*clipPyr)[smallLev], 00513 pos, neg); 00514 } 00515 else 00516 ::cudaCenterSurround(limg, simg, pos, neg); 00517 }