contrib/mul/mipa/mipa_orientation_histogram.txx
Go to the documentation of this file.
00001 #ifndef vaip_orientation_histogram_txx_
00002 #define vaip_orientation_histogram_txx_
00003 //:
00004 // \file
00005 // \brief Functions to compute histogram of orientations (HOGs)
00006 // \author Tim Cootes
00007 
00008 #include "mipa_orientation_histogram.h"
00009 #include <vcl_cmath.h>
00010 #include <vnl/vnl_math.h> // for pi
00011 #include <vcl_cassert.h>
00012 
00013 //: Generate an image containing histograms of oriented gradients (HOG)
00014 //  At each pixel in src, compute angle and quantise into n_angles.
00015 //  If full360, then angle range is 0-360, else it is 0-180.
00016 //  hog_image is set to have n_angles planes.
00017 //  hog_image(i,j,k) gives the weighted sum of pixels with angle k
00018 //  in cell (i,j), corresponding to the i,j-th cell_size square block.
00019 //
00020 //  The corner of cell(0,0) is at src(1,1), to ignore border pixels.
00021 //
00022 //  Number of cells (size of hog_image) chosen so every cell entirely
00023 //  within src.  Thus hog_image.ni()=(src.ni()-2)/cell_size.
00024 template<class srcT, class sumT>
00025 void mipa_orientation_histogram(const vil_image_view<srcT>& src,
00026                                 vil_image_view<sumT>& hog_image,
00027                                 unsigned n_angles,
00028                                 unsigned cell_size,
00029                                 bool full360,
00030                                 bool bilin_interp)
00031 {
00032   assert(src.nplanes()==1);
00033 
00034   unsigned h_ni=(src.ni()-2)/cell_size;
00035   unsigned h_nj=(src.nj()-2)/cell_size;
00036 
00037   // Create image with correct size of memory, with interleaved planes
00038   vil_image_view<sumT> h_im0(h_ni,h_nj,1,n_angles);
00039   h_im0.fill(0);
00040 
00041   // This forces hog_image to have interleaved planes.
00042   hog_image = h_im0;
00043 
00044   // Compute size of angle bins (1/sA) and an offset to ensure 0rad is
00045   // in centre of bin 0
00046   double sA;
00047   if (full360)
00048   {
00049     sA=n_angles/vnl_math::pi/2;
00050   }
00051   else
00052   {
00053     // Allow for wrap at 180
00054     sA=n_angles/vnl_math::pi;
00055   }
00056   double binwid=1.0/sA;
00057   double dA=2*vnl_math::pi - 0.5/sA;
00058   const double wrapNeg = full360 ? 2.0*vnl_math::pi : vnl_math::pi;
00059   // Process each block
00060   const srcT* s_row = &src(1,1);
00061   vcl_ptrdiff_t si_step = src.istep();
00062   vcl_ptrdiff_t sj_step = src.jstep();
00063 
00064   sumT *h_row=&hog_image(0,0);
00065   vcl_ptrdiff_t hi_step = hog_image.istep();
00066   vcl_ptrdiff_t hj_step = hog_image.jstep();
00067   const sumT* h_row_end = h_row+h_nj*hj_step;
00068   for (;h_row!=h_row_end;h_row+=hj_step)
00069   {
00070     const srcT* s_row_end = s_row + cell_size*sj_step;
00071     for (;s_row!=s_row_end;s_row+=sj_step)
00072     {
00073       // Process row of h_ni cells
00074       sumT* h_end=h_row+hi_step*h_ni;
00075       const srcT* s=s_row;
00076       for (sumT* h=h_row;h!=h_end;h+=hi_step)
00077       {
00078         // Process cell_size pixels in current cell (histo h[A])
00079         const srcT* s_end=s+si_step*cell_size;
00080         for (;s!=s_end;s+=si_step)
00081         {
00082           // Compute angle from gradient
00083           sumT gi = sumT(s[si_step]-s[-si_step]);
00084           sumT gj = sumT(s[sj_step]-s[-sj_step]);
00085           double theta=vcl_atan2(gj,gi);
00086           double magwt=vcl_sqrt(gi*gi+gj*gj);
00087           if(!bilin_interp)
00088           {
00089               unsigned A = unsigned((theta+dA)*sA);
00090               // Update histo bin with gradient magnitude:
00091               h[A%n_angles]+=sumT(magwt);
00092           }
00093           else
00094           {
00095               //interpolate theta over the two neighbouring bins
00096               //Note in this scheme we don't worry about the wrap around factor
00097               //as twopi-epsilon gets evenly(ish) interpolated between the last and zero bins
00098               if(theta<0.0) //convert to [0,2pi) or [0,pi)
00099               {
00100                   theta += wrapNeg; //add twopi (or pi if signs don't matter)
00101               }
00102               int iA = int(theta*sA);
00103               
00104               const double centre0 = 0.5*binwid;
00105               double b0 = centre0+double(iA)*binwid; //bin centres
00106               double d0 = theta-b0; //distances to bin centres
00107               bool nextUp=(d0>0.0) ? true : false;
00108               double w0 = 1.0-vcl_fabs(d0)*sA; //interpolation weights
00109               double w1 = 1.0-w0;
00110               
00111               h[iA%n_angles]+=sumT(magwt*w0);
00112               int iAnext=((nextUp) ? iA+1 : iA -1); //NB can be -1 for twopi-epsilon
00113               h[(iAnext)%n_angles]+=sumT(magwt*w1); //NB -1 remapped to n-1 by mod n
00114           }
00115         }
00116       }
00117     }
00118   }
00119 }
00120 
00121 #undef MIPA_ORIENTATION_HISTOGRAM_INSTANTIATE
00122 #define MIPA_ORIENTATION_HISTOGRAM_INSTANTIATE(srcT, sumT) \
00123 template void mipa_orientation_histogram(const vil_image_view<srcT >& src, \
00124                                          vil_image_view<sumT >& hog_image, \
00125                                          unsigned n_angles, \
00126                                          unsigned cell_size, \
00127                                          bool full360, \
00128                                          bool bilin_intrep)
00129 
00130 #endif // vaip_orientation_histogram_txx_