00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #ifndef CVD_CONVOLUTION_H_
00023 #define CVD_CONVOLUTION_H_
00024
00025 #include <vector>
00026 #include <memory>
00027 #include <numeric>
00028 #include <algorithm>
00029
00030 #include <cvd/config.h>
00031 #include <cvd/exceptions.h>
00032 #include <cvd/image.h>
00033 #include <cvd/internal/pixel_operations.h>
00034 #include <cvd/internal/aligned_mem.h>
00035 #include <cvd/utility.h>
00036
00037 namespace CVD {
00038
00047 template <class T>
00048 T gaussianKernel(std::vector<T>& k, T maxval, double stddev)
00049 {
00050 double sum = 0;
00051 unsigned int i, argmax=0;
00052 std::vector<double> kernel(k.size());
00053 for (i=0;i<k.size();i++) {
00054 double x = i +0.5 - k.size()/2.0;
00055 sum += kernel[i] = exp(-x*x/(2*stddev*stddev));
00056 if (kernel[i] > kernel[argmax])
00057 argmax = i;
00058 }
00059 T finalSum = 0;
00060 for (i=0;i<k.size();i++)
00061 finalSum += k[i] = (T)(kernel[i]*maxval/kernel[argmax]);
00062 return finalSum;
00063 }
00064
00072 template <class S, class T>
00073 T scaleKernel(const std::vector<S>& k, std::vector<T>& scaled, T maxval)
00074 {
00075 unsigned int i,argmax=0;
00076 for (i=1;i<k.size();i++)
00077 if (k[i]>k[argmax])
00078 argmax = i;
00079 scaled.resize(k.size());
00080 T sum = 0;
00081 for (i=0;i<k.size();i++)
00082 sum += (scaled[i] = (T)((k[i]*maxval)/k[argmax]));
00083 return sum;
00084 }
00085
00086 template <class T>
00087 void convolveGaussian5_1(BasicImage<T>& I)
00088 {
00089 int w = I.size().x;
00090 int h = I.size().y;
00091 int i,j;
00092 for (j=0;j<w;j++) {
00093 T* src = I.data()+j;
00094 T* end = src + w*(h-4);
00095 while (src != end) {
00096 T sum= (T)(0.0544887*(src[0]+src[4*w])
00097 + 0.2442010*(src[w]+src[3*w])
00098 + 0.4026200*src[2*w]);
00099 *(src) = sum;
00100 src += w;
00101 }
00102 }
00103 for (i=h-5;i>=0;i--) {
00104 T* src = I.data()+i*w;
00105 T* end = src + w-4;
00106 while (src != end) {
00107 T sum= (T)(0.0544887*(src[0]+src[4])
00108 + 0.2442010*(src[1]+src[3])
00109 + 0.4026200*src[2]);
00110 *(src+2*w+2)=sum;
00111 ++src;
00112 }
00113 }
00114 }
00115
00116 namespace Exceptions {
00117
00120 namespace Convolution {
00123 struct All: public CVD::Exceptions::All {};
00124
00127 struct IncompatibleImageSizes : public All {
00128 IncompatibleImageSizes(const std::string & function)
00129 {
00130 what = "Incompatible image sizes in " + function;
00131 };
00132 };
00133 }
00134 }
00135
00136
00141 template <class T> void convolveWithBox(const BasicImage<T>& I, BasicImage<T>& J, ImageRef hwin)
00142 {
00143 typedef typename Pixel::traits<T>::wider_type sum_type;
00144 if (I.size() != J.size()) {
00145 throw Exceptions::Convolution::IncompatibleImageSizes("convolveWithBox");
00146 }
00147 int w = I.size().x;
00148 int h = I.size().y;
00149 ImageRef win = 2*hwin+ImageRef(1,1);
00150 const double factor = 1.0/(win.x*win.y);
00151 std::vector<sum_type> buffer(w*win.y);
00152 std::vector<sum_type> sums_v(w);
00153 sum_type* sums = &sums_v[0];
00154 sum_type* next_row = &buffer[0];
00155 sum_type* oldest_row = &buffer[0];
00156 zeroPixels(sums, w);
00157 const T* input = I.data();
00158 T* output = J[hwin.y] - hwin.x;
00159 for (int i=0; i<h; i++) {
00160 sum_type hsum=sum_type();
00161 const T* back = input;
00162 int j;
00163 for (j=0; j<win.x-1; j++)
00164 hsum += input[j];
00165 for (; j<w; j++) {
00166 hsum += input[j];
00167 next_row[j] = hsum;
00168 sums[j] += hsum;
00169 hsum -= *(back++);
00170 }
00171 if (i >= win.y-1) {
00172 assign_multiple(sums+win.x-1, factor, output+win.x-1, w-win.x+1);
00173 differences(sums+win.x-1, oldest_row+win.x-1, sums+win.x-1, w-win.x+1);
00174 output += w;
00175 oldest_row += w;
00176 if (oldest_row == &buffer[0] + w*win.y)
00177 oldest_row = &buffer[0];
00178 }
00179 input += w;
00180 next_row += w;
00181 if (next_row == &buffer[0] + w*win.y)
00182 next_row = &buffer[0];
00183 }
00184 }
00185
00186 template <class T> inline void convolveWithBox(const BasicImage<T>& I, BasicImage<T>& J, int hwin)
00187 {
00188 convolveWithBox(I, J, ImageRef(hwin,hwin));
00189 }
00190
00191 template <class T> inline void convolveWithBox(BasicImage<T>& I, int hwin) {
00192 convolveWithBox(I,I,hwin);
00193 }
00194
00195 template <class T> inline void convolveWithBox(BasicImage<T>& I, ImageRef hwin) {
00196 convolveWithBox(I,I,hwin);
00197 }
00198
00199
00200 template <class T, int A, int B, int C> void convolveSymmetric(Image<T>& I)
00201 {
00202 typedef typename Pixel::traits<T>::wider_type wider;
00203 static const wider S = (A+B+C+B+A);
00204 int width = I.size().x;
00205 int height = I.size().y;
00206 T* p = I.data();
00207 int i,j;
00208 for (i=0; i<height; i++) {
00209 wider a = p[0];
00210 wider b = p[1];
00211 wider c = p[2];
00212 wider d = p[3];
00213 p[0] = (T)(((c+c)*A+(b+b)*B + a*C) /S);
00214 p[1] = (T)(((b+d)*A+(a+c)*B + b*C) /S);
00215 for (j=0;j<width-4;j++,p++) {
00216 wider e = p[4];
00217 p[2] = (T)(((a+e)*A + (b+d)*B + c*C)/S);
00218 a = b; b = c; c = d; d = e;
00219 }
00220 p[2] = (T)(((a+c)*A + (b+d)*B + c*C) /S);
00221 p[3] = (T)(((b+b)*A + (c+c)*B + d*C) /S);
00222 p += 4;
00223 }
00224 for (j=0;j<width;j++) {
00225 p = I.data()+j;
00226 wider a = p[0];
00227 wider b = p[width];
00228 p[0] = (T)(((p[2*width]+p[2*width])*A+(b+b)*B + a*C) /S);
00229 p[width] = (T)(((b+p[width*3])*A+(a+p[2*width])*B + b*C) /S);
00230 for (i=0;i<height-4;i++) {
00231 wider c = p[2*width];
00232 p[2*width] = (T)(((a+p[4*width])*A + (b+p[3*width])*B + c*C)/S);
00233 a=b; b=c;
00234 p += width;
00235 }
00236 wider c = p[2*width];
00237 p[2*width] = (T)(((a+c)*A + (b+p[width*3])*B + c*C) /S);
00238 p[3*width] = (T)(((b+b)*A + (c+c)*B + p[width*3]*C) /S);
00239 }
00240 }
00241
00242 template <class T, int A, int B, int C, int D> void convolveSymmetric(Image<T>& I)
00243 {
00244 typedef typename Pixel::traits<T>::wider_type wider;
00245 static const wider S = (A+B+C+D+C+B+A);
00246 int width = I.size().x;
00247 int height = I.size().y;
00248 T* p = I.data();
00249 int i,j;
00250 for (i=0; i<height; i++) {
00251 wider a = p[0];
00252 wider b = p[1];
00253 wider c = p[2];
00254 wider d = p[3];
00255 p[0] = (T)(((d+d)*A + (c+c)*B + (b+b)*C + a*D)/S);
00256 p[1] = (T)(((c+p[4])*A + (b+d)*B + (a+c)*C + b*D)/S);
00257 p[2] = (T)(((b+p[5])*A + (a+p[4])*B + (b+d)*C + c*D)/S);
00258 for (j=0;j<width-6;j++,p++) {
00259 d = p[3];
00260 p[3] = (T)(((a+p[6])*A + (b+p[5])*B + (c+p[4])*C + d*D)/S);
00261 a=b; b=c; c=d;
00262 }
00263 d = p[3];
00264 wider e = p[4];
00265 p[3] = (T)(((a+e)*A + (b+p[5])*B + (c+e)*C + d*D)/S);
00266 p[4] = (T)(((b+d)*A + (c+e)*B + (d+p[5])*C + e*D)/S);
00267 p[5] = (T)(((c+c)*A + (d+d)*B + (e+e)*C + p[5]*D)/S);
00268 p += 6;
00269 }
00270 for (j=0;j<width;j++) {
00271 p = I.data()+j;
00272 wider a = p[0];
00273 wider b = p[width];
00274 wider c = p[2*width];
00275 wider d = p[3*width];
00276 p[0] = (T)(((d+d)*A + (c+c)*B + (b+b)*C + a*D)/S);
00277 p[width] = (T)(((c+p[4*width])*A + (b+d)*B + (a+c)*C + b*D)/S);
00278 p[2*width] = (T)(((b+p[5*width])*A + (a+p[4*width])*B + (b+d)*C + c*D)/S);
00279 for (i=0;i<height-6;i++) {
00280 d = p[3*width];
00281 p[3*width] = (T)(((a+p[width*6])*A + (b+p[width*5])*B + (c+p[width*4])*C+d*D)/S);
00282 a=b; b=c; c=d;
00283 p += width;
00284 }
00285 d = p[3*width];
00286 wider e = p[4*width];
00287 p[3*width] = (T)(((a+e)*A + (b+p[5*width])*B + (c+e)*C + d*D)/S);
00288 p[4*width] = (T)(((b+d)*A + (c+e)*B + (d+p[5*width])*C + e*D)/S);
00289 p[5*width] = (T)(((c+c)*A + (d+d)*B + (e+e)*C + p[5*width]*D)/S);
00290 }
00291 }
00292
00293 template <class T, class K> void convolveSeparableSymmetric(Image<T>& I, const std::vector<K>& kernel, K divisor)
00294 {
00295 typedef typename Pixel::traits<T>::wider_type sum_type;
00296 int w = I.size().x;
00297 int h = I.size().y;
00298 int r = (int)kernel.size()/2;
00299 int i,j;
00300 int m;
00301 double factor = 1.0/divisor;
00302 for (j=0;j<w;j++) {
00303 T* src = I.data()+j;
00304 for (i=0; i<h-2*r; i++,src+=w) {
00305 sum_type sum = src[r*w]*kernel[r], v;
00306 for (m=0; m<r; m++)
00307 sum += (src[m*w] + src[(2*r-m)*w]) * kernel[m];
00308 *(src) = static_cast<T>(sum * factor);
00309 }
00310 }
00311 int offset = r*w + r;
00312 for (i=h-2*r-1;i>=0;i--) {
00313 T* src = I[w];
00314 for (j=0;j<w-2*r;j++, src++) {
00315 sum_type sum = src[r] * kernel[r], v;
00316 for (m=0; m<r; m++)
00317 sum += (src[m] + src[2*r-m])*kernel[m];
00318 *(src+offset) = static_cast<T>(sum*factor);
00319 }
00320 }
00321 }
00322
00323
00324 template <class A, class B> struct GetPixelRowTyped {
00325 static inline const B* get(const A* row, int w, B* rowbuf) {
00326 std::copy(row, row+w, rowbuf);
00327 return rowbuf;
00328 }
00329 };
00330
00331 template <class T> struct GetPixelRowTyped<T,T> {
00332 static inline const T* get(const T* row, int , T* ) {
00333 return row;
00334 }
00335 };
00336
00337 template <class A, class B> const B* getPixelRowTyped(const A* row, int n, B* rowbuf) {
00338 return GetPixelRowTyped<A,B>::get(row,n,rowbuf);
00339 }
00340
00341 template <class T, class S> struct CastCopy {
00342 static inline void cast_copy(const T* from, S* to, int count) {
00343 for (int i=0; i<count; i++)
00344 to[i] = static_cast<S>(from[i]);
00345 }
00346 };
00347
00348 template <class T> struct CastCopy<T,T> {
00349 static inline void cast_copy(const T* from, T* to, int count) {
00350 std::copy(from, from+count, to);
00351 }
00352 };
00353
00354 template <class T, class S> inline void cast_copy(const T* from, S* to, int count) { CastCopy<T,S>::cast_copy(from,to,count); }
00355
00356 template <class T, int N=-1, int C = Pixel::Component<T>::count> struct ConvolveMiddle {
00357 template <class S> static inline T at(const T* input, const S& factor, const S* kernel) { return ConvolveMiddle<T,-1,C>::at(input,factor, kernel, N); }
00358 };
00359
00360 template <class T, int N> struct ConvolveMiddle<T,N,1> {
00361 template <class S> static inline T at(const T* input, const S& factor, const S* kernel) { return ConvolveMiddle<T,N-1>::at(input,factor, kernel) + (input[-N]+input[N])*kernel[N-1]; }
00362 };
00363
00364 template <class T> struct ConvolveMiddle<T,-1,1> {
00365 template <class S> static inline T at(const T* input, const S& factor, const S* kernel, int ksize) {
00366 T hsum = *input * factor;
00367 for (int k=0; k<ksize; k++)
00368 hsum += (input[-k-1] + input[k+1]) * kernel[k];
00369 return hsum;
00370 }
00371 };
00372
00373 template <class T, int C> struct ConvolveMiddle<T,-1, C> {
00374 template <class S> static inline T at(const T* input, const S& factor, const S* kernel, int ksize) {
00375 T hsum = *input * factor;
00376 for (int k=0; k<ksize; k++)
00377 hsum += (input[-k-1] + input[k+1]) * kernel[k];
00378 return hsum;
00379 }
00380 };
00381
00382 template <class T> struct ConvolveMiddle<T,0,1> {
00383 template <class S> static inline T at(const T* input, const S& factor, const S* ) { return *input * factor; }
00384 };
00385
00386 template <class T,class S> inline const T* convolveMiddle(const T* input, const S& factor, const S* kernel, int ksize, int n, T* output) {
00387 #define CALL_CM(I) for (int j=0; j<n; ++j, ++input, ++output) { *output = ConvolveMiddle<T,I>::at(input, factor, kernel); } break
00388
00389 switch (ksize) {
00390 case 0: CALL_CM(0);
00391 case 1: CALL_CM(1);
00392 case 2: CALL_CM(2);
00393 case 3: CALL_CM(3);
00394 case 4: CALL_CM(4);
00395 case 5: CALL_CM(5);
00396 case 6: CALL_CM(6);
00397 case 7: CALL_CM(7);
00398 case 8: CALL_CM(8);
00399 default: for (int j=0; j<n; j++, input++) { *(output++) = ConvolveMiddle<T,-1>::at(input, factor, kernel, ksize); }
00400 }
00401 return input;
00402 #undef CALL_CM
00403 }
00404
00405
00406 template <class T> inline void convolveGaussian(BasicImage<T>& I, double sigma, double sigmas=3.0)
00407 {
00408 convolveGaussian(I,I,sigma,sigmas);
00409 }
00410
00411 template <class T> void convolveGaussian(const BasicImage<T>& I, BasicImage<T>& out, double sigma, double sigmas=3.0)
00412 {
00413 typedef typename Pixel::traits<typename Pixel::Component<T>::type>::float_type sum_comp_type;
00414 typedef typename Pixel::traits<T>::float_type sum_type;
00415 assert(out.size() == I.size());
00416 int ksize = (int)ceil(sigmas*sigma);
00417
00418 std::vector<sum_comp_type> kernel(ksize);
00419 sum_comp_type ksum = sum_comp_type();
00420 for (int i=1; i<=ksize; i++)
00421 ksum += (kernel[i-1] = static_cast<sum_comp_type>(exp(-i*i/(2*sigma*sigma))));
00422 for (int i=0; i<ksize; i++)
00423 kernel[i] /= (2*ksum+1);
00424 double factor = 1.0/(2*ksum+1);
00425 int w = I.size().x;
00426 int h = I.size().y;
00427 int swin = 2*ksize;
00428
00429 AlignedMem<sum_type,16> buffer(w*(swin+1));
00430 AlignedMem<sum_type,16> aligned_rowbuf(w);
00431 AlignedMem<sum_type,16> aligned_outbuf(w);
00432
00433 sum_type* rowbuf = aligned_rowbuf.data();
00434 sum_type* outbuf = aligned_outbuf.data();
00435
00436 std::vector<sum_type*> rows(swin+1);
00437 for (int k=0;k<swin+1;k++)
00438 rows[k] = buffer.data() + k*w;
00439
00440 T* output = out.data();
00441 for (int i=0; i<h; i++) {
00442 sum_type* next_row = rows[swin];
00443 const sum_type* input = getPixelRowTyped(I[i], w, rowbuf);
00444
00445 for (int j=0; j<ksize; j++) {
00446 sum_type hsum = static_cast<sum_type>(input[j] * factor);
00447 for (int k=0; k<ksize; k++)
00448 hsum += (input[std::max(j-k-1,0)] + input[j+k+1]) * kernel[k];
00449 next_row[j] = hsum;
00450 }
00451
00452 input += ksize;
00453 input = convolveMiddle<sum_type, sum_comp_type>(input, static_cast<sum_comp_type>(factor), &kernel.front(), ksize, w-swin, next_row+ksize);
00454
00455 for (int j=w-ksize; j<w; j++, input++) {
00456 sum_type hsum = static_cast<sum_type>(*input * factor);
00457 const int room = w-j;
00458 for (int k=0; k<ksize; k++) {
00459 hsum += (input[-k-1] + input[std::min(k+1,room-1)]) * kernel[k];
00460 }
00461 next_row[j] = hsum;
00462 }
00463
00464 if (i >= swin) {
00465 const sum_type* middle_row = rows[ksize];
00466 assign_multiple(middle_row, factor, outbuf, w);
00467 for (int k=0; k<ksize; k++) {
00468 const sum_comp_type m = kernel[k];
00469 const sum_type* row1 = rows[ksize-k-1];
00470 const sum_type* row2 = rows[ksize+k+1];
00471 add_multiple_of_sum(row1, row2, m, outbuf, w);
00472 }
00473 cast_copy(outbuf, output, w);
00474 output += w;
00475 if (i == h-1) {
00476 for (int r=0; r<ksize; r++) {
00477 const sum_type* middle_row = rows[ksize+r+1];
00478 assign_multiple(middle_row, factor, outbuf, w);
00479 for (int k=0; k<ksize; k++) {
00480 const sum_comp_type m = kernel[k];
00481 const sum_type* row1 = rows[ksize+r-k];
00482 const sum_type* row2 = rows[std::min(ksize+r+k+2, swin)];
00483 add_multiple_of_sum(row1, row2, m, outbuf, w);
00484 }
00485 cast_copy(outbuf, output, w);
00486 output += w;
00487 }
00488 }
00489 } else if (i == swin-1) {
00490 for (int r=0; r<ksize; r++) {
00491 const sum_type* middle_row = rows[r+1];
00492 assign_multiple(middle_row, factor, outbuf, w);
00493 for (int k=0; k<ksize; k++) {
00494 const sum_comp_type m = kernel[k];
00495 const sum_type* row1 = rows[std::max(r-k-1,0)+1];
00496 const sum_type* row2 = rows[r+k+2];
00497 add_multiple_of_sum(row1, row2, m, outbuf, w);
00498 }
00499 cast_copy(outbuf, output, w);
00500 output += w;
00501 }
00502 }
00503
00504 sum_type* tmp = rows[0];
00505 for (int r=0;r<swin; r++)
00506 rows[r] = rows[r+1];
00507 rows[swin] = tmp;
00508 }
00509 }
00510
00511 void compute_van_vliet_b(double sigma, double b[]);
00512 void compute_triggs_M(const double b[], double M[][3]);
00513 void van_vliet_blur(const double b[], const SubImage<float> in, SubImage<float> out);
00514
00515 void convolveGaussian(const BasicImage<float>& I, BasicImage<float>& out, double sigma, double sigmas=3.0);
00516 void convolveGaussian_fir(const BasicImage<float>& I, BasicImage<float>& out, double sigma, double sigmas=3.0);
00517
00518 template <class T, class O, class K> void convolve_gaussian_3(const BasicImage<T>& I, BasicImage<O>& out, K k1, K k2)
00519 {
00520 assert(I.size() == out.size());
00521 const T* a=I.data();
00522 const int w = I.size().x;
00523 O* o = out.data()+w+1;
00524 int total = I.totalsize() - 2*w-2;
00525 const double cross = k1*k2;
00526 k1 *= k1;
00527 k2 *= k2;
00528 while (total--) {
00529 const double sum = k1*(a[0] + a[2] + a[w*2] + a[w*2+2]) + cross*(a[1] + a[w*2+1] + a[w] + a[w+2]) + k2*a[w+1];
00530 *o++ = Pixel::scalar_convert<O,T,double>(sum);
00531 ++a;
00532 }
00533 }
00534
00535 }
00536
00537 #endif