cvd/tensor_voting.h

00001 #ifndef CVD_INC_TENSOR_VOTE_H
00002 #define CVD_INC_TENSOR_VOTE_H
00003 
00004 #include <cvd/image.h>
00005 #include <TooN/TooN.h>
00006 #include <TooN/helpers.h>
00007 #include <vector>
00008 #include <utility>
00009 
00010 namespace CVD
00011 {
00012     
00013     #ifndef DOXYGEN_IGNORE_INTERNAL
00014     namespace TensorVoting
00015     {
00016         struct TV_coord
00017         {
00018             ptrdiff_t o;
00019             int x;
00020             int y;
00021         };
00022 
00023         std::vector<std::pair<TV_coord, TooN::Matrix<2> > > compute_a_tensor_kernel(int radius, double cutoff, double angle, double sigma, double ratio, int row_stride);
00024         inline unsigned int quantize_half_angle(double r, int num_divs)
00025         {
00026             return  ((int)floor((r/M_PI+100) * num_divs + 0.5)) % num_divs;
00027         }
00028     }
00029 
00030     #endif
00031 
00080     template<class C> Image<TooN::Matrix<2> > dense_tensor_vote_gradients(const SubImage<C>& image, double sigma, double ratio, double cutoff=0.001, unsigned int num_divs = 4096)
00081     {
00082         using TooN::Matrix;
00083         using std::pair;
00084         using std::vector;
00085         using TensorVoting::TV_coord;
00086 
00087         Matrix<2> zero;
00088         TooN::Zero(zero);
00089         Image<Matrix<2> > field(image.size(), zero);
00090 
00091 
00092         //Kernel values go as exp(-x*x / sigma * sigma)
00093         //So, for cutoff = exp(-x*x / sigma * sigma)
00094         //ln cutoff = -x*x / sigma*sigma
00095         //x = sigma * sqrt(-ln cutoff)
00096         int kernel_radius =  (int)ceil(sigma * sqrt(-log(cutoff)));
00097 
00098 
00099         //First, build up the kernels
00100         vector<vector<pair<TV_coord, Matrix<2> > > > kernels;
00101         for(unsigned int i=0; i < num_divs; i++)
00102         {
00103             double angle =  M_PI * i / num_divs;
00104             kernels.push_back(TensorVoting::compute_a_tensor_kernel(kernel_radius, cutoff, angle, sigma, ratio, field.row_stride()));
00105         }
00106         
00107         
00108         for(int y= kernel_radius; y < field.size().y - kernel_radius; y++)
00109             for(int x= kernel_radius; x < field.size().x - kernel_radius; x++)
00110             {
00111                 double gx = ((double)image[y][x+1] - image[y][x-1])/2.;
00112                 double gy = ((double)image[y+1][x] - image[y-1][x])/2.;
00113 
00114                 double scale = sqrt(gx*gx + gy*gy);
00115                 unsigned int direction = TensorVoting::quantize_half_angle(M_PI/2 + atan2(gy,gx), num_divs);
00116 
00117                 const vector<pair<TV_coord, Matrix<2> > >& kernel = kernels[direction];
00118 
00119                 Matrix<2>* p = &field[y][x];
00120                 
00121                 //The matrices are all symmetric, so only use the upper right triangle.
00122                 for(unsigned int i=0; i < kernel.size(); i++)
00123                 {
00124                     p[kernel[i].first.o][0][0] += kernel[i].second[0][0] * scale;
00125                     p[kernel[i].first.o][0][1] += kernel[i].second[0][1] * scale;
00126                     p[kernel[i].first.o][1][1] += kernel[i].second[1][1] * scale;
00127                 }
00128             }
00129 
00130         //Now do the edges
00131         for(int y= 1; y < field.size().y-1; y++)
00132         {
00133             for(int x= 1; x < field.size().x-1; x++)
00134             {
00135                 //Skip the middle bit
00136                 if(y >= kernel_radius && y < field.size().y - kernel_radius && x == kernel_radius)
00137                     x = field.size().x - kernel_radius;
00138 
00139                 double gx = ((double)image[y][x+1] - image[y][x-1])/2.;
00140                 double gy = ((double)image[y+1][x] - image[y-1][x])/2.;
00141 
00142                 double scale = sqrt(gx*gx + gy*gy);
00143                 unsigned int direction = TensorVoting::quantize_half_angle(M_PI/2 + atan(gy / gx), num_divs);
00144 
00145                 const vector<pair<TV_coord, Matrix<2> > >& kernel = kernels[direction];
00146 
00147                 Matrix<2>* p = &field[y][x];
00148                 
00149                 //The matrices are all symmetric, so only use the upper right triangle.
00150                 for(unsigned int i=0; i < kernel.size(); i++)
00151                 {
00152                     if(kernel[i].first.y+y >= 0 && kernel[i].first.y+y < field.size().y && kernel[i].first.x+x >= 0 && kernel[i].first.x+x < field.size().x)
00153                     {
00154                         p[kernel[i].first.o][0][0] += kernel[i].second[0][0] * scale;
00155                         p[kernel[i].first.o][0][1] += kernel[i].second[0][1] * scale;
00156                         p[kernel[i].first.o][1][1] += kernel[i].second[1][1] * scale;
00157                     }
00158                 }
00159             }
00160         }
00161 
00162         //Copy over bits to make the matrices symmetric
00163         for(Image<Matrix<2> >:: iterator i=field.begin(); i != field.end(); i++)
00164             (*i)[1][0] = (*i)[0][1];
00165 
00166         return field;
00167     }
00168     
00169 
00170     #ifdef CVD_EXPERIMENTAL
00171 
00172     template<class C> Image<TooN::Matrix<2> > dense_tensor_vote_gradients_fast(const SubImage<C>& image, double sigma, double ratio, double cutoff=0.001, int num_divs = 4096)
00173     {
00174         using TooN::Matrix;
00175         using std::pair;
00176         using std::make_pair;
00177         using std::vector;
00178 
00179         Matrix<2> zero;
00180         TooN::Zero(zero);
00181         Image<Matrix<2> > ffield(image.size(), zero);
00182         Image<__m128> field(image.size());
00183         field.zero();
00184 
00185         
00186         //In much the same way as dense_tensor_vote_gradients, build up the kernel.
00187         int kernel_radius =  (int)ceil(sigma * sqrt(-log(cutoff)));
00188         vector<vector<pair<int, Matrix<2> > > > matrix_kernels;
00189         for(unsigned int i=0; i < num_divs; i++)
00190         {
00191             double angle =  M_PI * i / num_divs;
00192             matrix_kernels.push_back(TensorVoting::compute_a_tensor_kernel(kernel_radius, cutoff, angle, sigma, ratio, field.row_stride()));
00193         }
00194 
00195 
00196         //Put the kernel in aligned SSE registers.
00197         //Image<__m128> is used since it guarantees SSE aligned memory.
00198         vector<vector<int> > kernel_offsets;
00199         vector<Image<__m128> > kernel_values;
00200         for(unsigned int i=0; i < matrix_kernels.size(); i++)
00201         {
00202             vector<int>    off(matrix_kernels[i].size());
00203             Image<__m128>  val(ImageRef(matrix_kernels[i].size(), 1));
00204 
00205             for(unsigned int j=0; j < matrix_kernels[i].size(); j++)
00206             {
00207                 off[j] = matrix_kernels[i][j].first;
00208                 Matrix<2>& m = matrix_kernels[i][j].second;
00209                 val.data()[j] = _mm_setr_ps(m[0][0], m[0][1], m[1][0], m[1][1]);
00210             }
00211 
00212             kernel_offsets.push_back(off);
00213             kernel_values.push_back(val);
00214         }
00215 
00216         for(int y= kernel_radius; y < field.size().y - kernel_radius; y++)
00217             for(int x= kernel_radius; x < field.size().x - kernel_radius; x++)
00218             {
00219                 float gx = ((float)image[y][x+1] - image[y][x-1])/2.;
00220                 float gy = ((float)image[y+1][x] - image[y-1][x])/2.;
00221 
00222                 float scale = sqrt(gx*gx + gy*gy);
00223                 unsigned int direction = TensorVoting::quantize_half_angle(M_PI/2 + atan(gy / gx), num_divs);
00224 
00225                 const vector<int> & off = kernel_offsets[direction];
00226                 __m128* val = kernel_values[direction].data();
00227                 __m128* p = &field[y][x];
00228                 __m128 s = _mm_set1_ps(scale);
00229 
00230                 for(unsigned int i=0; i < off.size(); i++)
00231                     p[off[i]] = _mm_add_ps(p[off[i]], _mm_mul_ps(val[i], s));
00232             }
00233 
00234         for(int y=0; y < field.size().y; y++)
00235             for(int x=0; x < field.size().x; x++)
00236             {
00237                 float f[4];
00238                 _mm_storeu_ps(f, field[y][x]);
00239                 ffield[y][x][0][0] = f[0];
00240                 ffield[y][x][0][1] = f[1];
00241                 ffield[y][x][1][0] = f[2];
00242                 ffield[y][x][1][1] = f[3];
00243             }
00244         
00245         return ffield;
00246     }
00247     #endif
00248 
00249 }
00250 
00251 
00252 #endif

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