contrib/brl/bseg/brip/brip_subpix_convolution.h
Go to the documentation of this file.
00001 // This is brl/bseg/brip/brip_subpix_convolution.h
00002 #ifndef brip_subpix_convolution_h
00003 #define brip_subpix_convolution_h
00004 //:
00005 // \file
00006 // \brief A class to compute subpixel convolutions by using shifted operators
00007 // \author Amir Tamrakar
00008 // \date 9 Sept 2006
00009 //
00010 // \verbatim
00011 //  Modifications
00012 //   <none yet>
00013 // \endverbatim
00014 
00015 #include <vil/algo/vil_correlate_1d.h>
00016 #include <vil/algo/vil_correlate_2d.h>
00017 #include <vil/vil_transpose.h>
00018 #include <vgl/vgl_point_2d.h>
00019 
00020 //: Convolve an image(src_im) with shifted kernels to compute the value at the subpixel positions
00021 //  The subpixel values are computed at 1/2^N intervals in each dimension. e.g., N=0: regular
00022 //  convolution, N=1: compute at mid-pixel locations, N=2: compute at quarter pixel locations
00023 //
00024 // I'm using vil_correlate_2d_at_pt to evaluate the dot_product at pixel/subpixel locations
00025 // dest_im is resized to [2^N*src_im.ni() x 2^N*src_im.nj()]
00026 //
00027 template <class srcT, class destT, class kernelT, class accumT>
00028 inline void brip_subpix_convolve_2d(const vil_image_view<srcT>& src_im,
00029                                     vil_image_view<destT>& dest_im,
00030                                     kernelT kernel, accumT ac,
00031                                     int N)
00032 {
00033   int two_pow_N = 1 << N;
00034 
00035   //the portion of the src_image that can be used for the convolution
00036   int src_ni = 1+src_im.ni()-kernel.ni(); assert(src_ni >= 0);
00037   int src_nj = 1+src_im.nj()-kernel.nj(); assert(src_nj >= 0);
00038 
00039   // filtered image will be 2^N time larger than the original
00040   int dest_ni = two_pow_N*src_im.ni(); assert(dest_ni >= 0);
00041   int dest_nj = two_pow_N*src_im.nj(); assert(dest_nj >= 0);
00042 
00043   //resize the dest image to this size
00044   dest_im.set_size(dest_ni,dest_nj,1);
00045   dest_im.fill(0);
00046 
00047   //determine the step sizes
00048   vcl_ptrdiff_t s_istep = src_im.istep(), s_jstep = src_im.jstep();
00049   vcl_ptrdiff_t s_pstep = src_im.planestep();
00050   vcl_ptrdiff_t d_istep = dest_im.istep(), d_jstep = dest_im.jstep();
00051 
00052   // filter with shifted versions of the original filter
00053   // we need 2^(2N) convolutions to completely compute it
00054 
00055   for (int shift_i=0; shift_i<two_pow_N; shift_i++){
00056     for (int shift_j=0; shift_j<two_pow_N; shift_j++){
00057       //instantiate a shifted kernel
00058       kernel.recompute_kernel(double(shift_i)/two_pow_N, double(shift_j)/two_pow_N);
00059 
00060       //invert kernel to use correlation code
00061       //kernelT inv_kernel = vil_flip_ud(vil_flip_lr(kernel));
00062 
00063       //***********************************************************
00064       //Now convolve the image with this kernel
00065 
00066       //Put the result in the right place on the destination image
00067       //depending on the current shifts
00068 
00069       //***********************************************************
00070 
00071       //Select the first pixel on the src image that is valid
00072       int khs = (kernel.ni()-1)/2;
00073       const srcT*  src_row  = src_im.top_left_ptr();
00074 
00075       //select the corresponding first pixel on the dest image
00076       destT* dest_row = dest_im.top_left_ptr()+(shift_i+two_pow_N*khs)+(shift_j+two_pow_N*khs)*d_jstep;
00077 
00078       for (int j=0;j<src_nj;++j,src_row+=s_jstep,dest_row+=two_pow_N*d_jstep)
00079       {
00080         const srcT* sp = src_row;
00081         destT* dp = dest_row;
00082         for (int i=0;i<src_ni;++i, sp += s_istep, dp += two_pow_N*d_istep){
00083           // Correlate at src(i,j)
00084           *dp = (destT)vil_correlate_2d_at_pt(sp, s_istep, s_jstep, s_pstep, kernel, ac);
00085         }
00086       }
00087     }
00088   }
00089 }
00090 
00091 //: Convolve the image with the given kernel at the given subpixel locations (pts) only
00092 template <class srcT, class kernelT, class accumT>
00093 inline void brip_subpix_convolve_2d(const vil_image_view<srcT>& src_im,
00094                                     const vcl_vector<vgl_point_2d<accumT> >& pts,
00095                                     vcl_vector<accumT>& res,
00096                                     kernelT kernel, accumT ac,
00097                                     int /*N*/)
00098 {
00099   res.resize(pts.size());
00100   int khs = (kernel.ni()-1)/2;
00101 
00102   //determine the step sizes for the src_image
00103   vcl_ptrdiff_t s_istep = src_im.istep(), s_jstep = src_im.jstep(), s_pstep = src_im.planestep();
00104   //for each subpixel point, we need to create a new kernel shifted to that location
00105   for (unsigned i=0; i<pts.size(); i++)
00106   {
00107     //determine closest integer coordinates of the current point
00108     int x = (int) vcl_floor(pts[i].x());
00109     int y = (int) vcl_floor(pts[i].y());
00110 
00111     //assert(x>=khs && y>=khs && x<src_im.ni()-khs && y<src_im.nj()-khs);
00112 
00113     if (x<khs || y<khs){
00114       res[i]=0;
00115       continue;
00116     }
00117 
00118     //determine the shift from it
00119     double dx = pts[i].x() - x;
00120     double dy = pts[i].y() - y;
00121 
00122     //instantiate a shifted kernel for this point
00123     kernel.recompute_kernel(dx, dy);
00124 
00125     //invert kernel to use correlation code
00126     //kernelT inv_kernel = vil_flip_ud(vil_flip_lr(kernel));
00127 
00128     //Select the first pixel on the src image that is valid
00129 
00130     const srcT*  sp  = src_im.top_left_ptr() + (x-khs)*s_istep + (y-khs)*s_jstep;
00131 
00132     // Correlate at src(x,y)
00133     res[i] = (accumT)vil_correlate_2d_at_pt(sp, s_istep, s_jstep, s_pstep, kernel, ac);
00134   }
00135 }
00136 
00137 //: Convolve the image with the given kernel at the given subpixel locations (pts) only at the given orientation
00138 template <class srcT, class kernelT, class accumT>
00139 inline void brip_subpix_convolve_2d(const vil_image_view<srcT>& src_im,
00140                                     const vcl_vector<vgl_point_2d<accumT> >& pts,
00141                                     const vcl_vector<accumT>& thetas,
00142                                     vcl_vector<accumT>& res,
00143                                     kernelT kernel, accumT ac,
00144                                     int /*N*/)
00145 {
00146   res.resize(pts.size());
00147   int khs = (kernel.ni()-1)/2;
00148 
00149   //determine the step sizes for the src_image
00150   vcl_ptrdiff_t s_istep = src_im.istep(), s_jstep = src_im.jstep(), s_pstep = src_im.planestep();
00151   //for each subpixel point, we need to create a new kernel shifted to that location
00152   for (unsigned i=0; i<pts.size(); i++)
00153   {
00154     //determine closest integer coordinates of the current point
00155     int x = (int) vcl_floor(pts[i].x());
00156     int y = (int) vcl_floor(pts[i].y());
00157 
00158     //assert(x>=khs && y>=khs && x<src_im.ni()-khs && y<src_im.nj()-khs);
00159 
00160     if (x<khs || y<khs){
00161       res[i]=0;
00162       continue;
00163     }
00164 
00165     //determine the shift from it
00166     double dx = pts[i].x() - x;
00167     double dy = pts[i].y() - y;
00168 
00169     //instantiate a shifted kernel for this point
00170     kernel.recompute_kernel(dx, dy, thetas[i]);
00171 
00172     //invert kernel to use correlation code
00173     //kernelT inv_kernel = vil_flip_ud(vil_flip_lr(kernel));
00174 
00175     //Select the first pixel on the src image that is valid
00176 
00177     const srcT*  sp  = src_im.top_left_ptr() + (x-khs)*s_istep + (y-khs)*s_jstep;
00178 
00179     // Correlate at src(x,y)
00180     res[i] = (accumT)vil_correlate_2d_at_pt(sp, s_istep, s_jstep, s_pstep, kernel, ac);
00181   }
00182 }
00183 
00184 //: Convolve an image(src_im) with shifted kernels to compute the value at the subpixel positions
00185 //  The subpixel values are computed at 1/2^N intervals in each dimension. e.g., N=0: regular
00186 //  convolution, N=1: compute at mid-pixel locations, N=2: compute at quarter pixel locations
00187 //
00188 //  To speed up the convolution, I'm performing 2 independent convolutions with 1-d kernels.
00189 //  dest_im is resized to [2^N*src_im.ni() x 2^N*src_im.nj()]
00190 //
00191 template <class srcT, class destT, class kernelT, class accumT>
00192 inline void brip_subpix_convolve_2d_sep(const vil_image_view<srcT>& src_im,
00193                                         vil_image_view<destT>& dest_im,
00194                                         kernelT kernel, accumT ac,
00195                                         int N)
00196 {
00197   int two_pow_N = 1 << N;
00198 
00199   //the portion of the src_image that can be used for the convolution
00200   int src_ni = src_im.ni(); assert(src_ni >= 0);
00201   int src_nj = src_im.nj(); assert(src_nj >= 0);
00202 
00203   // filtered image will be 2^N time larger than the original
00204   int dest_ni = two_pow_N*src_im.ni(); assert(dest_ni >= 0);
00205   int dest_nj = two_pow_N*src_im.nj(); assert(dest_nj >= 0);
00206 
00207   //resize the dest image to this size
00208   dest_im.set_size(dest_ni,dest_nj,1);
00209   dest_im.fill(0);
00210 
00211   //determine the step sizes
00212   vcl_ptrdiff_t s_istep = src_im.istep(), s_jstep = src_im.jstep();
00213   vcl_ptrdiff_t d_istep = dest_im.istep(), d_jstep = dest_im.jstep();
00214 
00215   // filter with shifted versions of the original filter
00216   // we need 2^(2N) convolutions to completely compute it
00217 
00218   for (int shift_i=0; shift_i<two_pow_N; shift_i++){
00219     for (int shift_j=0; shift_j<two_pow_N; shift_j++){
00220 
00221       // Generate filter: instantiate a shifted kernel
00222       kernel.recompute_kernel(double(shift_i)/two_pow_N, double(shift_j)/two_pow_N);
00223 
00224       //determine the half width of the kernel
00225       int khs = kernel.khs;
00226 
00227       // Apply 1D convolution along i direction using the K_x filter
00228       vil_image_view<destT> work_im;
00229       vil_correlate_1d(src_im, work_im, &kernel.K_x[khs], -khs, khs,
00230                        ac, vil_convolve_no_extend, vil_convolve_no_extend);
00231 
00232       // Apply 1D convolution along j direction by applying K_y filter to its transpose
00233       vil_image_view<destT> work_im2;
00234       work_im2.set_size(src_im.ni(), src_im.nj(), src_im.nplanes());
00235 
00236       vil_image_view<destT> work_im_t = vil_transpose(work_im);
00237       vil_image_view<destT> work_im2_t = vil_transpose(work_im2);
00238 
00239       vil_correlate_1d(work_im_t, work_im2_t,
00240                        &kernel.K_y[khs], -khs, khs,
00241                        ac, vil_convolve_no_extend, vil_convolve_no_extend);
00242 
00243       //compose this convolution, at the sub pixel location, into the larger dest image
00244       destT*  src_row  = work_im2.top_left_ptr();
00245 
00246       //select the corresponding first pixel on the dest image
00247       destT* dest_row = dest_im.top_left_ptr()+(shift_i)+(shift_j)*d_jstep;
00248 
00249       for (int j=0;j<src_nj;++j, src_row+=s_jstep, dest_row+=two_pow_N*d_jstep)
00250       {
00251         const destT* sp = src_row;
00252         destT* dp = dest_row;
00253         for (int i=0;i<src_ni;++i, sp += s_istep, dp += two_pow_N*d_istep){
00254           *dp = (destT) *sp;
00255         }
00256       }
00257     }
00258   }
00259 }
00260 
00261 #endif // brip_subpix_convolution_h