Convolutions.C

Go to the documentation of this file.
00001 /*!@file Image/Convolutions.C basic 1-D and 2-D filtering operations */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00005 // by the 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 at usc dot edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Image/Convolutions.C $
00035 // $Id: Convolutions.C 10532 2008-12-16 20:02:46Z dparks $
00036 //
00037 
00038 #ifndef IMAGE_CONVOLUTIONS_C_DEFINED
00039 #define IMAGE_CONVOLUTIONS_C_DEFINED
00040 
00041 #include "Image/Convolutions.H"
00042 
00043 #include "Image/Image.H"
00044 #include "Image/Pixels.H"
00045 #include "rutz/trace.h"
00046 
00047 // ######################################################################
00048 template <class T> static
00049 Image<typename promote_trait<T, float>::TP>
00050 convolveZeroHelper(const Image<T>& src, const float* filter,
00051                    const int Nx, const int Ny)
00052 {
00053 GVX_TRACE(__PRETTY_FUNCTION__);
00054   ASSERT(src.initialized()); //ASSERT((Nx & 1)  && (Ny & 1));
00055   const int w = src.getWidth(), h = src.getHeight();
00056   // promote the source image to float if necessary, so that we do the
00057   // promotion only once for all, rather than many times as we access
00058   // the pixels of the image; if no promotion is necessary, "source"
00059   // will just point to the original data of "src" through the
00060   // copy-on-write/ref-counting behavior of Image:
00061   typedef typename promote_trait<T, float>::TP TF;
00062   const Image<TF> source = src;
00063   Image<TF> result(w, h, NO_INIT);
00064   typename Image<TF>::const_iterator sptr = source.begin();
00065   typename Image<TF>::iterator dptr = result.beginw();
00066 
00067   const int kkk = Nx * Ny - 1;
00068   const int Nx2 = (Nx - 1) / 2;
00069   const int Ny2 = (Ny - 1) / 2;
00070 
00071   // very inefficient implementation; one has to be crazy to use non
00072   // separable filters anyway...
00073   for (int j = 0; j < h; ++j) // LOOP rows of src
00074     {
00075       const int j_offset = j-Ny2;
00076 
00077       for (int i = 0; i < w; ++i) // LOOP columns of src
00078         {
00079           TF sum = TF();
00080           const int i_offset = i-Nx2;
00081           const int kj_bgn = std::max(- j_offset, 0);
00082           const int kj_end = std::min(h - j_offset, Ny);
00083 
00084           for (int kj = kj_bgn; kj < kj_end; ++kj) // LOOP rows of filter
00085             {
00086               const int kjj = kj + j_offset; // row of src
00087               const int src_offset = w * kjj + i_offset;
00088               const int filt_offset = kkk - Nx*kj;
00089 
00090               const int ki_bgn = std::max(-i_offset, 0);
00091               const int ki_end = std::min(w-i_offset, Nx);
00092 
00093               for (int ki = ki_bgn; ki < ki_end; ++ki) // LOOP cols of filt
00094                 sum += sptr[ki + src_offset] * filter[filt_offset - ki];
00095             }
00096           *dptr++ = sum;
00097         }
00098     }
00099   return result;
00100 }
00101 
00102 // ######################################################################
00103 template <class T> static
00104 Image<typename promote_trait<T, float>::TP>
00105 convolveCleanHelper(const Image<T>& src, const float* filter,
00106                    const int Nx, const int Ny)
00107 {
00108 GVX_TRACE(__PRETTY_FUNCTION__);
00109   ASSERT(src.initialized()); //ASSERT((Nx & 1)  && (Ny & 1));
00110   const int w = src.getWidth(), h = src.getHeight();
00111   // promote the source image to float if necessary, so that we do the
00112   // promotion only once for all, rather than many times as we access
00113   // the pixels of the image; if no promotion is necessary, "source"
00114   // will just point to the original data of "src" through the
00115   // copy-on-write/ref-counting behavior of Image:
00116   typedef typename promote_trait<T, float>::TP TF;
00117   const Image<TF> source = src;
00118   Image<TF> result(w, h, NO_INIT);
00119   typename Image<TF>::const_iterator sptr = source.begin();
00120   typename Image<TF>::iterator dptr = result.beginw();
00121 
00122   const int kkk = Nx * Ny - 1;
00123   const int Nx2 = (Nx - 1) / 2;
00124   const int Ny2 = (Ny - 1) / 2;
00125 
00126   float sumf = 0.0f;
00127   for (int i = 0; i < Nx*Ny; ++i)
00128     sumf += filter[i];
00129 
00130   // very inefficient implementation; one has to be crazy to use non
00131   // separable filters anyway...
00132   for (int j = 0; j < h; ++j) // LOOP rows of src
00133     {
00134       const int j_offset = j-Ny2;
00135 
00136       for (int i = 0; i < w; ++i) // LOOP columns of src
00137         {
00138           TF sum = TF();
00139           float sumw = 0.0f;
00140           const int i_offset = i-Nx2;
00141           const int kj_bgn = std::max(- j_offset, 0);
00142           const int kj_end = std::min(h - j_offset, Ny);
00143 
00144           for (int kj = kj_bgn; kj < kj_end; ++kj) // LOOP rows of filter
00145             {
00146               const int kjj = kj + j_offset; // row of src
00147               const int src_offset = w * kjj + i_offset;
00148               const int filt_offset = kkk - Nx*kj;
00149 
00150               const int ki_bgn = std::max(-i_offset, 0);
00151               const int ki_end = std::min(w-i_offset, Nx);
00152 
00153               for (int ki = ki_bgn; ki < ki_end; ++ki) // LOOP cols of filt
00154                 {
00155                   sum += sptr[ki + src_offset] * filter[filt_offset - ki];
00156                   sumw += filter[filt_offset - ki];
00157                 }
00158             }
00159           *dptr++ = sum * sumf / sumw;
00160         }
00161     }
00162   return result;
00163 }
00164 
00165 // ######################################################################
00166 template <class T> static
00167 Image<typename promote_trait<T, float>::TP>
00168 convolve(const Image<T>& src, const float* filter,
00169          const int Nx, const int Ny,
00170          ConvolutionBoundaryStrategy boundary)
00171 {
00172   switch (boundary)
00173     {
00174     case CONV_BOUNDARY_ZERO:
00175       return convolveZeroHelper(src, filter, Nx, Ny);
00176     case CONV_BOUNDARY_CLEAN:
00177       return convolveCleanHelper(src, filter, Nx, Ny);
00178     case CONV_BOUNDARY_REPLICATE:
00179       // not implemented yet
00180       break;
00181     }
00182 
00183   LFATAL("convolution boundary strategy %d not supported",
00184          (int) boundary);
00185   /* can't happen */ return Image<typename promote_trait<T, float>::TP>();
00186 }
00187 
00188 // ######################################################################
00189 template <class T>
00190 Image<typename promote_trait<T, float>::TP>
00191 optConvolve(const Image<T>& src, const Image<float>& f)
00192 {
00193 GVX_TRACE(__PRETTY_FUNCTION__);
00194   ASSERT(src.initialized());
00195 
00196   const int src_w = src.getWidth();
00197   const int src_h = src.getHeight();
00198 
00199   Image<float>::const_iterator filter = f.begin();
00200   const int fil_w = f.getWidth();
00201   const int fil_h = f.getHeight();
00202 
00203   ASSERT((fil_w & 1) && (fil_h & 1));
00204 
00205   typedef typename promote_trait<T, float>::TP TF;
00206   const Image<TF> source = src;
00207   Image<TF> result(src_w, src_h, NO_INIT);
00208   typename Image<TF>::const_iterator sptr = source.begin();
00209   typename Image<TF>::iterator dptr = result.beginw();
00210 
00211   const int fil_end = fil_w * fil_h - 1;
00212   const int Nx2 = (fil_w - 1) / 2;
00213   const int Ny2 = (fil_h - 1) / 2;
00214 
00215   const int srow_skip = src_w-fil_w;
00216 
00217   for (int dst_y = 0; dst_y < src_h; ++dst_y)
00218     {
00219       // Determine if we're safely inside the image in the y-direction:
00220       const bool y_clean = dst_y >= Ny2 && dst_y <  (src_h - Nx2);
00221 
00222       for (int dst_x = 0; dst_x < src_w; ++dst_x, ++dptr)
00223         {
00224           // Determine if we're safely inside the image in the x-direction:
00225           const bool x_clean = dst_x >= Nx2 && dst_x <  (src_w - Nx2);
00226 
00227           // Here is where we pick whether we can use the optimized inner
00228           // loop (in cases where the filter and image patch overlap
00229           // completely) or whether we must use the inner loop that can
00230           // handle boundary conditions.
00231 
00232           if (x_clean && y_clean)
00233             {
00234               float dst_val = 0.0f;
00235 
00236               Image<float>::const_iterator fptr = filter+fil_end;
00237 
00238               Image<float>::const_iterator srow_ptr =
00239                 sptr + src_w*(dst_y-Nx2) + dst_x - Nx2;
00240 
00241               for (int f_y = 0; f_y < fil_h; ++f_y)
00242                 {
00243                   for (int f_x = 0; f_x < fil_w; ++f_x)
00244                     dst_val += (*srow_ptr++) * (*fptr--);
00245 
00246                   srow_ptr += srow_skip;
00247                 }
00248               *dptr = dst_val;
00249               continue;
00250             }
00251           else
00252             {
00253               // OK, we're at an image boundary, so what do we do to make
00254               // up for the missing pixels? The approach here is to
00255               // compute the average value of the non-missing pixels, and
00256               // proceed as if the missing pixels had that value. This
00257               // minimizes the introduction of edge artifacts e.g. when
00258               // convolving with an oriented filter.
00259 
00260               float dst_val = 0.0f;
00261               float src_sum = 0.0f;
00262               int src_cnt = 0;
00263               float fil_sum_skipped = 0.0f;
00264 
00265               for (int f_y = 0; f_y < fil_h; ++f_y)
00266                 {
00267                   const int src_y = f_y + dst_y - Ny2;
00268                   if (src_y >= 0 && src_y < src_h)
00269                     {
00270                       for (int f_x = 0; f_x < fil_w; ++f_x)
00271                         {
00272                           const float fil = filter[fil_end - f_x - fil_w*f_y];
00273 
00274                           const int src_x = f_x + dst_x - Nx2;
00275                           if (src_x >= 0 && src_x < src_w)
00276                             {
00277                               const float src_val = sptr[src_x + src_w * src_y];
00278                               dst_val += src_val * fil;
00279                               src_sum += src_val;
00280                               ++src_cnt;
00281                             }
00282                           else
00283                             {
00284                               fil_sum_skipped += fil;
00285                             }
00286                         }
00287                     }
00288                   else
00289                     {
00290                       for (int f_x = 0; f_x < fil_w; ++f_x)
00291                         fil_sum_skipped += filter[fil_end - f_x - fil_w*f_y];
00292                     }
00293                 }
00294               const float src_avg = src_sum / src_cnt;
00295               *dptr = dst_val + (fil_sum_skipped * src_avg);
00296             }
00297         }
00298     }
00299   return result;
00300 }
00301 
00302 // ######################################################################
00303 template <class T>
00304 Image<typename promote_trait<T, float>::TP>
00305 convolveHmax(const Image<T>& src, const Image<float>& filter)
00306 {
00307 GVX_TRACE(__PRETTY_FUNCTION__);
00308   ASSERT(src.initialized());
00309   const int Nx = filter.getWidth(), Ny = filter.getHeight();
00310   ASSERT((Nx & 1) && (Ny & 1));
00311 
00312   const int w = src.getWidth(), h = src.getHeight();
00313   // promote the source image to float if necessary, so that we do the
00314   // promotion only once for all, rather than many times as we access
00315   // the pixels of the image; if no promotion is necessary, "source"
00316   // will just point to the original data of "src" through the
00317   // copy-on-write/ref-counting behavior of Image:
00318   typedef typename promote_trait<T, float>::TP TF;
00319   const Image<TF> source = src;
00320   Image<TF> result(w, h, NO_INIT);
00321   typename Image<TF>::const_iterator sptr = source.begin();
00322   typename Image<TF>::iterator dptr = result.beginw();
00323   typename Image<float>::const_iterator const filt = filter.begin();
00324 
00325   int kkk = Nx * Ny - 1;
00326   int Nx2 = (Nx - 1) / 2, Ny2 = (Ny - 1) / 2;
00327   // very inefficient implementation; one has to be crazy to use non
00328   // separable filters anyway...
00329   for (int j = 0; j < h; ++j)
00330     for (int i = 0; i < w; ++i)
00331       {
00332         TF sum = TF(), sumw = TF();
00333         for (int kj = 0; kj < Ny; ++kj)
00334           {
00335             int kjj = kj + j - Ny2;
00336             if (kjj >= 0 && kjj < h)
00337               for (int ki = 0; ki < Nx; ++ki)
00338                 {
00339                   int kii = ki + i - Nx2;
00340                   if (kii >= 0 && kii < w)
00341                     {
00342                       float fil = filt[kkk - ki - Nx*kj];
00343                       TF img = sptr[kii + w * kjj];
00344                       sum += img * fil; sumw += img * img;
00345                     }
00346                 }
00347           }
00348         //if (sumw > 0.0F) sum /= fastSqrt(sumw);
00349         if (sumw > 0.0F) sum = fabs(sum)/sqrt(sumw);
00350         *dptr++ = sum;
00351       }
00352   return result;
00353 }
00354 
00355 // ######################################################################
00356 template <class T>
00357 static Image<typename promote_trait<T, float>::TP>
00358 xFilter(const Image<T>& src, const float* hFilt, const int hfs,
00359         ConvolutionBoundaryStrategy boundary)
00360 {
00361 GVX_TRACE(__PRETTY_FUNCTION__);
00362   const int w = src.getWidth(), h = src.getHeight();
00363   // promote the source image to float if necessary, so that we do the
00364   // promotion only once for all, rather than many times as we access
00365   // the pixels of the image; if no promotion is necessary, "source"
00366   // will just point to the original data of "src" through the
00367   // copy-on-write/ref-counting behavior of Image:
00368   typedef typename promote_trait<T, float>::TP TF;
00369   const Image<TF> source = src;
00370   if (hfs == 0) return source;  // no filter
00371   Image<TF> result(w, h, NO_INIT);
00372   typename Image<TF>::const_iterator sptr = source.begin();
00373   typename Image<TF>::iterator dptr = result.beginw();
00374 
00375   const int hfs2 = (hfs - 1) / 2;
00376 
00377   // flip the filter to accelerate convolution:
00378   float *hFilter = new float[hfs]; float sumh = 0.0f;
00379   for (int x = 0; x < hfs; x ++)
00380     { sumh += hFilt[x]; hFilter[hfs-1-x] = hFilt[x]; }
00381 
00382   // *** horizontal pass ***
00383   if (w < hfs)  // special function for very small images
00384     {
00385       switch (boundary)
00386         {
00387         case CONV_BOUNDARY_ZERO:
00388           {
00389             for (int j = 0; j < h; j ++)
00390               for (int i = 0; i < w; i ++)
00391                 {
00392                   TF val = TF();
00393                   for (int k = 0; k < hfs; k ++)
00394                     if (i + k - hfs2 >= 0 && i + k - hfs2 < w)
00395                       {
00396                         val += sptr[k - hfs2] * hFilter[k];
00397                       }
00398                   *dptr++ = val; sptr ++;
00399                 }
00400           }
00401           break;
00402         case CONV_BOUNDARY_CLEAN:
00403           {
00404             for (int j = 0; j < h; j ++)
00405               for (int i = 0; i < w; i ++)
00406                 {
00407                   TF val = TF(); float sum = 0.0F;
00408                   for (int k = 0; k < hfs; k ++)
00409                     if (i + k - hfs2 >= 0 && i + k - hfs2 < w)
00410                       {
00411                         val += sptr[k - hfs2] * hFilter[k];
00412                         sum += hFilter[k];
00413                       }
00414                   *dptr++ = val * sumh / sum; sptr ++;
00415                 }
00416           }
00417           break;
00418         case CONV_BOUNDARY_REPLICATE:
00419           {
00420             for (int j = 0; j < h; j ++)
00421               for (int i = 0; i < w; i ++)
00422                 {
00423                   TF val = TF();
00424                   for (int k = 0; k < hfs; k ++)
00425                     if (i + k - hfs2 < 0)
00426                       {
00427                         val += sptr[-i] * hFilter[k];
00428                       }
00429                     else if (i + k - hfs2 >= w)
00430                       {
00431                         val += sptr[w-1-i] * hFilter[k];
00432                       }
00433                     else
00434                       {
00435                         val += sptr[k - hfs2] * hFilter[k];
00436                       }
00437                   *dptr++ = val; sptr ++;
00438                 }
00439           }
00440           break;
00441         default:
00442           LFATAL("invalid convolution boundary strategy %d",
00443                  (int) boundary);
00444         }
00445     }
00446   else  // function for reasonably large images
00447     for (int jj = 0; jj < h; jj ++)
00448       {
00449         // leftmost points:
00450         switch (boundary)
00451           {
00452           case CONV_BOUNDARY_ZERO:
00453             {
00454               for (int j = hfs2; j > 0; --j)
00455                 {
00456                   TF val = TF();
00457                   for (int k = j; k < hfs; ++k)
00458                     {
00459                       val += sptr[k - j] * hFilter[k];
00460                     }
00461                   *dptr++ = val;
00462                 }
00463             }
00464             break;
00465           case CONV_BOUNDARY_CLEAN:
00466             {
00467               for (int j = hfs2; j > 0; --j)
00468                 {
00469                   TF val = TF(); float sum = 0.0F;
00470                   for (int k = j; k < hfs; ++k)
00471                     {
00472                       val += sptr[k - j] * hFilter[k];
00473                       sum += hFilter[k];
00474                     }
00475                   *dptr++ = val * sumh / sum;
00476                 }
00477             }
00478             break;
00479           case CONV_BOUNDARY_REPLICATE:
00480             {
00481               for (int j = hfs2; j > 0; --j)
00482                 {
00483                   TF val = TF();
00484                   for (int k = 0; k < j; ++k)
00485                     {
00486                       val += sptr[0] * hFilter[k];
00487                     }
00488                   for (int k = j; k < hfs; ++k)
00489                     {
00490                       val += sptr[k - j] * hFilter[k];
00491                     }
00492                   *dptr++ = val;
00493                 }
00494             }
00495             break;
00496 //           case CONV_BOUNDARY_REFLECT:
00497 //             {
00498 //               for (int j = hfs2; j > 0; --j)
00499 //                 {
00500 //                   TF val = TF();
00501 //                   for (int k = 0; k < j; ++k)
00502 //                     {
00503 //                       val += sptr[j - k] * hFilter[k];
00504 //                     }
00505 //                   for (int k = j; k < hfs; ++k)
00506 //                     {
00507 //                       val += sptr[k - j] * hFilter[k];
00508 //                     }
00509 //                   *dptr++ = val;
00510 //                 }
00511 //             }
00512 //             break;
00513           default:
00514             LFATAL("invalid convolution boundary strategy %d",
00515                    (int) boundary);
00516           }
00517         // bulk (far from the borders):
00518         for (int i = 0; i < w - hfs + 1; i ++)
00519           {
00520             TF val = TF();
00521             for (int k = 0; k < hfs; k ++) val += sptr[k] * hFilter[k];
00522             *dptr++ = val; sptr++;
00523           }
00524         // rightmost points:
00525         switch (boundary)
00526           {
00527           case CONV_BOUNDARY_ZERO:
00528             {
00529               for (int j = hfs - 1; j > hfs2; --j)
00530                 {
00531                   TF val = TF();
00532                   for (int k = 0; k < j; ++k)
00533                     {
00534                       val += sptr[k] * hFilter[k];
00535                     }
00536                   *dptr++ = val;
00537                   sptr++;
00538                 }
00539               }
00540             break;
00541           case CONV_BOUNDARY_CLEAN:
00542             {
00543               for (int j = hfs - 1; j > hfs2; --j)
00544                 {
00545                   TF val = TF(); float sum = 0.0F;
00546                   for (int k = 0; k < j; ++k)
00547                     {
00548                       val += sptr[k] * hFilter[k];
00549                       sum += hFilter[k];
00550                     }
00551                   *dptr++ = val * sumh / sum;
00552                   sptr++;
00553                 }
00554             }
00555             break;
00556           case CONV_BOUNDARY_REPLICATE:
00557             {
00558               for (int j = hfs - 1; j > hfs2; --j)
00559                 {
00560                   TF val = TF();
00561                   for (int k = 0; k < j; ++k)
00562                     {
00563                       val += sptr[k] * hFilter[k];
00564                     }
00565                   for (int k = j; k < hfs; ++k)
00566                     {
00567                       val += sptr[j-1] * hFilter[k];
00568                     }
00569                   *dptr++ = val;
00570                   sptr++;
00571                 }
00572               }
00573             break;
00574 //           case CONV_BOUNDARY_REFLECT:
00575 //             {
00576 //               for (int j = hfs - 1; j > hfs2; --j)
00577 //                 {
00578 //                   TF val = TF();
00579 //                   for (int k = 0; k < j; ++k)
00580 //                     {
00581 //                       val += sptr[k] * hFilter[k];
00582 //                     }
00583 //                   for (int k = j; k < hfs; ++k)
00584 //                     {
00585 //                       val += sptr[2*j-k-1] * hFilter[k];
00586 //                     }
00587 //                   *dptr++ = val;
00588 //                   sptr++;
00589 //                 }
00590 //             }
00591 //             break;
00592           default:
00593             LFATAL("invalid convolution boundary strategy %d",
00594                    (int) boundary);
00595           }
00596         sptr += hfs2;  // sptr back to same as dptr (start of next line)
00597       }
00598   delete [] hFilter;
00599   return result;
00600 }
00601 
00602 // ######################################################################
00603 template <class T>
00604 static Image<typename promote_trait<T, float>::TP>
00605 yFilter(const Image<T>& src, const float* vFilt, const int vfs,
00606         ConvolutionBoundaryStrategy boundary)
00607 {
00608 GVX_TRACE(__PRETTY_FUNCTION__);
00609   const int w = src.getWidth(), h = src.getHeight();
00610   // promote the source image to float if necessary, so that we do the
00611   // promotion only once for all, rather than many times as we access
00612   // the pixels of the image; if no promotion is necessary, "source"
00613   // will just point to the original data of "src" through the
00614   // copy-on-write/ref-counting behavior of Image:
00615   typedef typename promote_trait<T, float>::TP TF;
00616   const Image<TF> source = src;
00617   if (vfs == 0) return source;  // no filter
00618   Image<TF> result(w, h, NO_INIT);
00619   typename Image<TF>::const_iterator sptr = source.begin();
00620   typename Image<TF>::iterator dptr = result.beginw();
00621 
00622   const int vfs2 = (vfs - 1) / 2;
00623 
00624   // flip the filter to accelerate convolution:
00625   float *vFilter = new float[vfs]; float sumv = 0.0f;
00626   for (int x = 0; x < vfs; x ++)
00627     { sumv += vFilt[x]; vFilter[vfs-1-x] = vFilt[x]; }
00628 
00629   int ww[vfs];
00630   for (int i = 0; i < vfs; i ++) ww[i] = w * i; // speedup precompute
00631 
00632   if (h < vfs)  // special function for very small images
00633     {
00634       switch (boundary)
00635         {
00636         case CONV_BOUNDARY_ZERO:
00637           {
00638             for (int j = 0; j < h; j ++)
00639               for (int i = 0; i < w; i ++)
00640                 {
00641                   TF val = TF();
00642                   for (int k = 0; k < vfs; k ++)
00643                     if (j + k - vfs2 >= 0 && j + k - vfs2 < h)
00644                       {
00645                         val += sptr[w * (k - vfs2)] * vFilter[k];
00646                       }
00647                   *dptr++ = val; sptr ++;
00648                 }
00649           }
00650           break;
00651         case CONV_BOUNDARY_CLEAN:
00652           {
00653             for (int j = 0; j < h; j ++)
00654               for (int i = 0; i < w; i ++)
00655                 {
00656                   TF val = TF(); float sum = 0.0;
00657                   for (int k = 0; k < vfs; k ++)
00658                     if (j + k - vfs2 >= 0 && j + k - vfs2 < h)
00659                       {
00660                         val += sptr[w * (k - vfs2)] * vFilter[k];
00661                         sum += vFilter[k];
00662                       }
00663                   *dptr++ = val * sumv / sum; sptr ++;
00664                 }
00665           }
00666           break;
00667         case CONV_BOUNDARY_REPLICATE:
00668           {
00669             for (int j = 0; j < h; j ++)
00670               for (int i = 0; i < w; i ++)
00671                 {
00672                   TF val = TF();
00673                   for (int k = 0; k < vfs; k ++)
00674                     if (j + k - vfs2 < 0)
00675                       {
00676                         val += sptr[w * (-j)] * vFilter[k];
00677                       }
00678                     else if (j + k - vfs2 >= h)
00679                       {
00680                         val += sptr[w * (h-1-j)] * vFilter[k];
00681                       }
00682                     else
00683                       {
00684                         val += sptr[w * (k - vfs2)] * vFilter[k];
00685                       }
00686                   *dptr++ = val; sptr ++;
00687                 }
00688           }
00689           break;
00690         default:
00691           LFATAL("invalid convolution boundary strategy %d",
00692                  (int) boundary);
00693         }
00694     }
00695   else  // function for reasonably large images
00696     {
00697       // top points:
00698       switch (boundary)
00699         {
00700         case CONV_BOUNDARY_ZERO:
00701           {
00702             for (int j = vfs2; j > 0; --j)
00703               {
00704                 for (int i = 0; i < w; ++i)  // scan all points of given horiz
00705                   {
00706                     TF val = TF();
00707                     for (int k = j; k < vfs; ++k)
00708                       {
00709                         val += sptr[ww[k - j]] * vFilter[k];
00710                       }
00711                     *dptr++ = val;
00712                     sptr++;
00713                   }
00714                 sptr -= w;   // back to top-left corner
00715               }
00716           }
00717           break;
00718         case CONV_BOUNDARY_CLEAN:
00719           {
00720             for (int j = vfs2; j > 0; --j)
00721               {
00722                 for (int i = 0; i < w; ++i)  // scan all points of given horiz
00723                   {
00724                     TF val = TF(); float sum = 0.0;
00725                     for (int k = j; k < vfs; ++k)
00726                       {
00727                         val += sptr[ww[k - j]] * vFilter[k];
00728                         sum += vFilter[k];
00729                       }
00730                     *dptr++ = val * sumv / sum;
00731                     sptr++;
00732                   }
00733                 sptr -= w;   // back to top-left corner
00734               }
00735           }
00736           break;
00737         case CONV_BOUNDARY_REPLICATE:
00738           {
00739             for (int j = vfs2; j > 0; --j)
00740               {
00741                 for (int i = 0; i < w; ++i)  // scan all points of given horiz
00742                   {
00743                     TF val = TF();
00744                     for (int k = 0; k < j; ++k)
00745                       {
00746                         val += sptr[ww[0]] * vFilter[k];
00747                       }
00748                     for (int k = j; k < vfs; ++k)
00749                       {
00750                         val += sptr[ww[k - j]] * vFilter[k];
00751                       }
00752                     *dptr++ = val;
00753                     sptr++;
00754                   }
00755                 sptr -= w;   // back to top-left corner
00756               }
00757           }
00758           break;
00759         default:
00760           LFATAL("invalid convolution boundary strategy %d",
00761                  (int) boundary);
00762         }
00763       // bulk (far from edges):
00764       for (int j = 0; j < h - vfs + 1; j ++)
00765         for (int i = 0; i < w; i ++)
00766           {
00767             TF val = TF();
00768             for (int k = 0; k < vfs; k ++) val += sptr[ww[k]] * vFilter[k];
00769             *dptr++ = val; sptr ++;
00770           }
00771       // bottommost points:
00772       switch (boundary)
00773         {
00774         case CONV_BOUNDARY_ZERO:
00775           {
00776             for (int j = vfs - 1; j > vfs2; --j)
00777               for (int i = 0; i < w; ++i)
00778                 {
00779                   TF val = TF();
00780                   for (int k = 0; k < j; ++k)
00781                     {
00782                       val += sptr[ww[k]] * vFilter[k];
00783                     }
00784                   *dptr++ = val;
00785                   sptr ++;
00786                 }
00787           }
00788           break;
00789         case CONV_BOUNDARY_CLEAN:
00790           {
00791             for (int j = vfs - 1; j > vfs2; --j)
00792               for (int i = 0; i < w; ++i)
00793                 {
00794                   TF val = TF(); float sum = 0.0;
00795                   for (int k = 0; k < j; ++k)
00796                     {
00797                       val += sptr[ww[k]] * vFilter[k];
00798                       sum += vFilter[k];
00799                     }
00800                   *dptr++ = val * sumv / sum;
00801                   sptr ++;
00802                 }
00803           }
00804           break;
00805         case CONV_BOUNDARY_REPLICATE:
00806           {
00807             for (int j = vfs - 1; j > vfs2; --j)
00808               for (int i = 0; i < w; ++i)
00809                 {
00810                   TF val = TF();
00811                   for (int k = 0; k < j; ++k)
00812                     {
00813                       val += sptr[ww[k]] * vFilter[k];
00814                     }
00815                   for (int k = j; k < vfs; ++k)
00816                     {
00817                       val += sptr[ww[j-1]] * vFilter[k];
00818                     }
00819                   *dptr++ = val;
00820                   sptr ++;
00821                 }
00822           }
00823           break;
00824         default:
00825           LFATAL("invalid convolution boundary strategy %d",
00826                  (int) boundary);
00827         }
00828     }
00829   delete [] vFilter;
00830   return result;
00831 }
00832 
00833 // ######################################################################
00834 template <class T>
00835 Image<typename promote_trait<T, float>::TP>
00836 sepFilter(const Image<T>& src, const Image<float>& hFilter,
00837           const Image<float>& vFilter,
00838           ConvolutionBoundaryStrategy boundary)
00839 {
00840   ASSERT(hFilter.is1D() || hFilter.getSize() == 0);
00841   ASSERT(vFilter.is1D() || vFilter.getSize() == 0);
00842   return sepFilter(src, hFilter.getArrayPtr(), vFilter.getArrayPtr(),
00843                    hFilter.getSize(), vFilter.getSize(), boundary);
00844 }
00845 
00846 // ######################################################################
00847 template <class T>
00848 Image<typename promote_trait<T, float>::TP>
00849 sepFilter(const Image<T>& src, const float* hFilt, const float* vFilt,
00850           const int hfs, const int vfs,
00851           ConvolutionBoundaryStrategy boundary)
00852 {
00853   Image<typename promote_trait<T, float>::TP> result = src;
00854 
00855   if (hfs > 0) result = xFilter(result, hFilt, hfs, boundary);
00856   if (vfs > 0) result = yFilter(result, vFilt, vfs, boundary);
00857 
00858   return result;
00859 }
00860 
00861 // Include the explicit instantiations
00862 #include "inst/Image/Convolutions.I"
00863 
00864 // ######################################################################
00865 /* So things look consistent in everyone's emacs... */
00866 /* Local Variables: */
00867 /* mode: c++ */
00868 /* indent-tabs-mode: nil */
00869 /* End: */
00870 
00871 #endif // IMAGE_CONVOLUTIONS_C_DEFINED
Generated on Sun May 8 08:05:06 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3