CVD 0.8
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                 
00137         struct OddSizedKernelRequired : public All {
00138             OddSizedKernelRequired(const std::string & function)
00139             {
00140                 what = "Odd sized kernel required in " + function;
00141             };
00142         };
00143     }
00144 }
00145 //void convolveGaussian5_1(BasicImage<byte>& I);
00146 
00151 template <class T> void convolveWithBox(const BasicImage<T>& I, BasicImage<T>& J, ImageRef hwin)
00152 {
00153     typedef typename Pixel::traits<T>::wider_type sum_type;
00154     if (I.size() != J.size()) {
00155     throw Exceptions::Convolution::IncompatibleImageSizes("convolveWithBox");
00156     }
00157     int w = I.size().x;
00158     int h = I.size().y;
00159     ImageRef win = 2*hwin+ImageRef(1,1);
00160     const double factor = 1.0/(win.x*win.y);
00161     std::vector<sum_type> buffer(w*win.y);
00162     std::vector<sum_type> sums_v(w);
00163     sum_type* sums = &sums_v[0];
00164     sum_type* next_row = &buffer[0];
00165     sum_type* oldest_row = &buffer[0];
00166     zeroPixels(sums, w);
00167     const T* input = I.data();
00168     T* output = J[hwin.y] - hwin.x;
00169     for (int i=0; i<h; i++) {
00170     sum_type hsum=sum_type();
00171     const T* back = input;
00172     int j;
00173     for (j=0; j<win.x-1; j++)
00174         hsum += input[j];
00175     for (; j<w; j++) {
00176         hsum += input[j];
00177         next_row[j] = hsum;
00178         sums[j] += hsum;
00179         hsum -= *(back++);
00180     }
00181     if (i >= win.y-1) {
00182         assign_multiple(sums+win.x-1, factor, output+win.x-1, w-win.x+1);
00183         differences(sums+win.x-1, oldest_row+win.x-1, sums+win.x-1, w-win.x+1);
00184         output += w;
00185         oldest_row += w;
00186         if (oldest_row == &buffer[0] + w*win.y)
00187         oldest_row = &buffer[0];
00188     }    
00189     input += w;
00190     next_row += w;
00191     if (next_row == &buffer[0] + w*win.y)
00192         next_row = &buffer[0];
00193     }
00194 }
00195 
00196 template <class T> inline void convolveWithBox(const BasicImage<T>& I, BasicImage<T>& J, int hwin)
00197 {
00198     convolveWithBox(I, J, ImageRef(hwin,hwin));
00199 }
00200 
00201 template <class T> inline void convolveWithBox(BasicImage<T>& I, int hwin) {
00202     convolveWithBox(I,I,hwin);
00203 }
00204 
00205 template <class T> inline void convolveWithBox(BasicImage<T>& I, ImageRef hwin) {
00206     convolveWithBox(I,I,hwin);
00207 }
00208     
00209 
00210 template <class T, int A, int B, int C> void convolveSymmetric(Image<T>& I)
00211   {
00212     typedef typename Pixel::traits<T>::wider_type wider;
00213     static const wider S = (A+B+C+B+A);
00214     int width = I.size().x;
00215     int height = I.size().y;
00216     T* p = I.data();
00217     int i,j;
00218     for (i=0; i<height; i++) {
00219       wider a = p[0];
00220       wider b = p[1];
00221       wider c = p[2];
00222       wider d = p[3];
00223       p[0] = (T)(((c+c)*A+(b+b)*B + a*C) /S);
00224       p[1] = (T)(((b+d)*A+(a+c)*B + b*C) /S);
00225       for (j=0;j<width-4;j++,p++) {
00226         wider e = p[4];
00227         p[2] = (T)(((a+e)*A + (b+d)*B + c*C)/S);
00228         a = b; b = c; c = d; d = e;
00229       }
00230       p[2] = (T)(((a+c)*A + (b+d)*B + c*C) /S);
00231       p[3] = (T)(((b+b)*A + (c+c)*B + d*C) /S);
00232       p += 4;
00233     }
00234     for (j=0;j<width;j++) {
00235       p = I.data()+j;
00236       wider a = p[0];
00237       wider b = p[width];
00238       p[0] = (T)(((p[2*width]+p[2*width])*A+(b+b)*B + a*C) /S);
00239       p[width] = (T)(((b+p[width*3])*A+(a+p[2*width])*B + b*C) /S);
00240       for (i=0;i<height-4;i++) {
00241         wider c = p[2*width];
00242         p[2*width] = (T)(((a+p[4*width])*A + (b+p[3*width])*B + c*C)/S);
00243         a=b; b=c;
00244         p += width;
00245       }
00246       wider c = p[2*width];
00247       p[2*width] = (T)(((a+c)*A + (b+p[width*3])*B + c*C) /S);
00248       p[3*width] = (T)(((b+b)*A + (c+c)*B + p[width*3]*C) /S);
00249     }
00250   }
00251 
00252   template <class T, int A, int B, int C, int D> void convolveSymmetric(Image<T>& I)
00253   {
00254     typedef typename Pixel::traits<T>::wider_type wider;
00255     static const wider S = (A+B+C+D+C+B+A);
00256     int width = I.size().x;
00257     int height = I.size().y;
00258     T* p = I.data();
00259     int i,j;
00260     for (i=0; i<height; i++) {
00261       wider a = p[0];
00262       wider b = p[1];
00263       wider c = p[2];
00264       wider d = p[3];
00265       p[0] = (T)(((d+d)*A + (c+c)*B + (b+b)*C + a*D)/S);
00266       p[1] = (T)(((c+p[4])*A + (b+d)*B + (a+c)*C + b*D)/S);
00267       p[2] = (T)(((b+p[5])*A + (a+p[4])*B + (b+d)*C + c*D)/S);
00268       for (j=0;j<width-6;j++,p++) {
00269         d = p[3];
00270         p[3] = (T)(((a+p[6])*A + (b+p[5])*B + (c+p[4])*C + d*D)/S);
00271         a=b; b=c; c=d;
00272       }
00273       d = p[3];
00274       wider e = p[4];
00275       p[3] = (T)(((a+e)*A + (b+p[5])*B + (c+e)*C + d*D)/S);
00276       p[4] = (T)(((b+d)*A + (c+e)*B + (d+p[5])*C + e*D)/S);
00277       p[5] = (T)(((c+c)*A + (d+d)*B + (e+e)*C + p[5]*D)/S);
00278       p += 6;
00279     }
00280     for (j=0;j<width;j++) {
00281       p = I.data()+j;
00282       wider a = p[0];
00283       wider b = p[width];
00284       wider c = p[2*width];
00285       wider d = p[3*width];
00286       p[0] = (T)(((d+d)*A + (c+c)*B + (b+b)*C + a*D)/S);
00287       p[width] = (T)(((c+p[4*width])*A + (b+d)*B + (a+c)*C + b*D)/S);
00288       p[2*width] = (T)(((b+p[5*width])*A + (a+p[4*width])*B + (b+d)*C + c*D)/S);
00289       for (i=0;i<height-6;i++) {
00290         d = p[3*width];
00291         p[3*width] = (T)(((a+p[width*6])*A + (b+p[width*5])*B + (c+p[width*4])*C+d*D)/S);
00292         a=b; b=c; c=d;
00293         p += width;
00294       }
00295       d = p[3*width];
00296       wider e = p[4*width];
00297       p[3*width] = (T)(((a+e)*A + (b+p[5*width])*B + (c+e)*C + d*D)/S);
00298       p[4*width] = (T)(((b+d)*A + (c+e)*B + (d+p[5*width])*C + e*D)/S);
00299       p[5*width] = (T)(((c+c)*A + (d+d)*B + (e+e)*C + p[5*width]*D)/S);
00300     }
00301   }
00302 
00303 template <class T, class K> void convolveSeparableSymmetric(Image<T>& I, const std::vector<K>& kernel, K divisor)
00304   {
00305     typedef typename Pixel::traits<T>::wider_type sum_type;
00306     int w = I.size().x;
00307     int h = I.size().y;
00308     int r = (int)kernel.size()/2;
00309     int i,j;
00310     int m;
00311     double factor = 1.0/divisor;
00312     for (j=0;j<w;j++) {
00313       T* src = I.data()+j;
00314       for (i=0; i<h-2*r; i++,src+=w) {
00315         sum_type sum = src[r*w]*kernel[r], v;
00316         for (m=0; m<r; m++)
00317       sum += (src[m*w] + src[(2*r-m)*w]) * kernel[m];
00318     *(src) = static_cast<T>(sum * factor);
00319       }
00320     }
00321     int offset = r*w + r;
00322     for (i=h-2*r-1;i>=0;i--) {
00323       T* src = I[w];
00324       for (j=0;j<w-2*r;j++, src++) {
00325         sum_type sum = src[r] * kernel[r], v;
00326         for (m=0; m<r; m++)
00327       sum += (src[m] + src[2*r-m])*kernel[m];
00328     *(src+offset) = static_cast<T>(sum*factor);
00329       }
00330     }
00331   }
00332   
00333 
00334 template <class A, class B> struct GetPixelRowTyped {
00335   static inline const B* get(const A* row, int w, B* rowbuf) {
00336     std::copy(row, row+w, rowbuf);
00337     return rowbuf;
00338   }
00339 };
00340 
00341 template <class T> struct GetPixelRowTyped<T,T> {
00342   static inline const T* get(const T* row, int , T* ) {
00343     return row;
00344   }
00345 };
00346 
00347 template <class A, class B> const B* getPixelRowTyped(const A* row, int n, B* rowbuf) {
00348   return GetPixelRowTyped<A,B>::get(row,n,rowbuf);
00349 }
00350 
00351 template <class T, class S> struct CastCopy {
00352   static inline void cast_copy(const T* from, S* to, int count) {
00353     for (int i=0; i<count; i++)
00354       to[i] = static_cast<S>(from[i]);
00355   }
00356 };
00357 
00358 template <class T> struct CastCopy<T,T> {
00359   static inline void cast_copy(const T* from, T* to, int count) {
00360     std::copy(from, from+count, to);
00361   }
00362 };
00363 
00364 template <class T, class S> inline void cast_copy(const T* from, S* to, int count) { CastCopy<T,S>::cast_copy(from,to,count); }
00365 
00366 template <class T, int N=-1, int C = Pixel::Component<T>::count> struct ConvolveMiddle {
00367   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); }
00368 };
00369 
00370 template <class T, int N> struct ConvolveMiddle<T,N,1> {
00371   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]; }
00372 };
00373 
00374 template <class T> struct ConvolveMiddle<T,-1,1> {
00375   template <class S> static inline T at(const T* input, const S& factor, const S* kernel, int ksize) {
00376     T hsum = *input * factor;
00377     for (int k=0; k<ksize; k++)
00378       hsum += (input[-k-1] + input[k+1]) * kernel[k];
00379     return hsum;
00380   }
00381 };
00382 
00383 template <class T, int C> struct ConvolveMiddle<T,-1, C> {
00384   template <class S> static inline T at(const T* input, const S& factor, const S* kernel, int ksize) {
00385     T hsum = *input * factor;
00386     for (int k=0; k<ksize; k++)
00387       hsum += (input[-k-1] + input[k+1]) * kernel[k];
00388     return hsum;
00389   }
00390 };
00391 
00392 template <class T> struct ConvolveMiddle<T,0,1> {
00393   template <class S> static inline T at(const T* input, const S& factor, const S* ) { return *input * factor; }
00394 };
00395 
00396 template <class T,class S> inline const T* convolveMiddle(const T* input, const S& factor, const S* kernel, int ksize, int n, T* output) {
00397 #define CALL_CM(I) for (int j=0; j<n; ++j, ++input, ++output) { *output = ConvolveMiddle<T,I>::at(input, factor, kernel); } break
00398     
00399   switch (ksize) {
00400   case 0: CALL_CM(0);
00401   case 1: CALL_CM(1);
00402   case 2: CALL_CM(2);
00403   case 3: CALL_CM(3);
00404   case 4: CALL_CM(4);
00405   case 5: CALL_CM(5);
00406   case 6: CALL_CM(6);
00407   case 7: CALL_CM(7);
00408   case 8: CALL_CM(8);
00409   default: for (int j=0; j<n; j++, input++) { *(output++) = ConvolveMiddle<T,-1>::at(input, factor, kernel, ksize); }     
00410   }
00411   return input;
00412 #undef CALL_CM
00413 }
00414 
00415 
00416 template <class T> inline void convolveGaussian(BasicImage<T>& I, double sigma, double sigmas=3.0)
00417 {
00418   convolveGaussian(I,I,sigma,sigmas);
00419 }
00420 
00421 template <class T> void convolveGaussian(const BasicImage<T>& I, BasicImage<T>& out, double sigma, double sigmas=3.0)
00422 {
00423     typedef typename Pixel::traits<typename Pixel::Component<T>::type>::float_type sum_comp_type;
00424     typedef typename Pixel::traits<T>::float_type sum_type;
00425     assert(out.size() == I.size());
00426     int ksize = (int)ceil(sigmas*sigma);
00427     //std::cerr << "sigma: " << sigma << " kernel: " << ksize << std::endl;
00428     std::vector<sum_comp_type> kernel(ksize);
00429     sum_comp_type ksum = sum_comp_type();
00430     for (int i=1; i<=ksize; i++)
00431     ksum += (kernel[i-1] = static_cast<sum_comp_type>(exp(-i*i/(2*sigma*sigma))));
00432     for (int i=0; i<ksize; i++)
00433     kernel[i] /= (2*ksum+1);
00434     double factor = 1.0/(2*ksum+1);
00435     int w = I.size().x;
00436     int h = I.size().y;
00437     int swin = 2*ksize;
00438 
00439     AlignedMem<sum_type,16> buffer(w*(swin+1));
00440     AlignedMem<sum_type,16> aligned_rowbuf(w);
00441     AlignedMem<sum_type,16> aligned_outbuf(w);
00442 
00443     sum_type* rowbuf = aligned_rowbuf.data();
00444     sum_type* outbuf = aligned_outbuf.data();
00445 
00446     std::vector<sum_type*> rows(swin+1);
00447     for (int k=0;k<swin+1;k++)
00448     rows[k] = buffer.data() + k*w;
00449 
00450     T* output = out.data();
00451     for (int i=0; i<h; i++) {
00452     sum_type* next_row = rows[swin];
00453     const sum_type* input = getPixelRowTyped(I[i], w, rowbuf);
00454     // beginning of row
00455     for (int j=0; j<ksize; j++) {
00456         sum_type hsum = static_cast<sum_type>(input[j] * factor);
00457         for (int k=0; k<ksize; k++)
00458         hsum += (input[std::max(j-k-1,0)] + input[j+k+1]) * kernel[k];
00459         next_row[j] = hsum;
00460     }
00461     // middle of row
00462     input += ksize;
00463     input = convolveMiddle<sum_type, sum_comp_type>(input, static_cast<sum_comp_type>(factor), &kernel.front(), ksize, w-swin, next_row+ksize);
00464     // end of row
00465     for (int j=w-ksize; j<w; j++, input++) {
00466         sum_type hsum = static_cast<sum_type>(*input * factor);
00467         const int room = w-j;
00468         for (int k=0; k<ksize; k++) {
00469         hsum += (input[-k-1] + input[std::min(k+1,room-1)]) * kernel[k];
00470         }
00471         next_row[j] = hsum;
00472     }
00473     // vertical
00474     if (i >= swin) {
00475         const sum_type* middle_row = rows[ksize];
00476         assign_multiple(middle_row, factor, outbuf, w);
00477         for (int k=0; k<ksize; k++) {
00478         const sum_comp_type m = kernel[k];
00479         const sum_type* row1 = rows[ksize-k-1];
00480         const sum_type* row2 = rows[ksize+k+1]; 
00481         add_multiple_of_sum(row1, row2, m, outbuf, w);
00482         }
00483         cast_copy(outbuf, output, w);
00484         output += w;
00485         if (i == h-1) {
00486         for (int r=0; r<ksize; r++) {
00487             const sum_type* middle_row = rows[ksize+r+1];
00488             assign_multiple(middle_row, factor, outbuf, w);
00489             for (int k=0; k<ksize; k++) {
00490             const sum_comp_type m = kernel[k];
00491             const sum_type* row1 = rows[ksize+r-k];
00492             const sum_type* row2 = rows[std::min(ksize+r+k+2, swin)];
00493             add_multiple_of_sum(row1, row2, m, outbuf, w);
00494             }
00495             cast_copy(outbuf, output, w);
00496             output += w;
00497         }   
00498         }
00499     } else if (i == swin-1) {
00500         for (int r=0; r<ksize; r++) {
00501         const sum_type* middle_row = rows[r+1];
00502         assign_multiple(middle_row, factor, outbuf, w);
00503         for (int k=0; k<ksize; k++) {
00504             const sum_comp_type m = kernel[k];
00505             const sum_type* row1 = rows[std::max(r-k-1,0)+1];
00506             const sum_type* row2 = rows[r+k+2]; 
00507             add_multiple_of_sum(row1, row2, m, outbuf, w);
00508         }
00509         cast_copy(outbuf, output, w);
00510         output += w;
00511         }
00512     }
00513     
00514     sum_type* tmp = rows[0];
00515     for (int r=0;r<swin; r++)
00516         rows[r] = rows[r+1];
00517     rows[swin] = tmp;
00518     }
00519 }
00520 
00521 void compute_van_vliet_b(double sigma, double b[]);
00522 void compute_triggs_M(const double b[], double M[][3]);
00523 void van_vliet_blur(const double b[], const SubImage<float> in, SubImage<float> out);
00524 
00525 void convolveGaussian(const BasicImage<float>& I, BasicImage<float>& out, double sigma, double sigmas=3.0);
00526 void convolveGaussian_fir(const BasicImage<float>& I, BasicImage<float>& out, double sigma, double sigmas=3.0);
00527 
00528 template <class T, class O, class K> void convolve_gaussian_3(const BasicImage<T>& I, BasicImage<O>& out, K k1, K k2)
00529 {    
00530     assert(I.size() == out.size());
00531     const T* a=I.data();
00532     const int w = I.size().x;
00533     O* o = out.data()+w+1;
00534     int total = I.totalsize() - 2*w-2;
00535     const double cross = k1*k2;
00536     k1 *= k1;
00537     k2 *= k2;
00538     while (total--) {
00539     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];
00540     *o++ = Pixel::scalar_convert<O,T,double>(sum);
00541     ++a;
00542     }
00543 }
00544 
00545 } // namespace CVD
00546 
00547 #endif