cvd/convolution.h

00001 /*
00002         This file is part of the CVD Library.
00003 
00004         Copyright (C) 2005 The Authors
00005 
00006         This library is free software; you can redistribute it and/or
00007         modify it under the terms of the GNU Lesser General Public
00008         License as published by the Free Software Foundation; either
00009         version 2.1 of the License, or (at your option) any later version.
00010 
00011         This library is distributed in the hope that it will be useful,
00012         but WITHOUT ANY WARRANTY; without even the implied warranty of
00013         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014         Lesser General Public License for more details.
00015 
00016         You should have received a copy of the GNU Lesser General Public
00017         License along with this library; if not, write to the Free Software
00018         Foundation, Inc.,
00019     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
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 //void convolveGaussian5_1(BasicImage<byte>& I);
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     //std::cerr << "sigma: " << sigma << " kernel: " << ksize << std::endl;
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     // beginning of row
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     // middle of row
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     // end of row
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     // vertical
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 } // namespace CVD
00536 
00537 #endif

Generated on Wed Feb 18 10:23:00 2009 for CVD by  doxygen 1.5.3