]> rtime.felk.cvut.cz Git - opencv.git/blob - opencv/src/cv/cvsurf.cpp
Remove redundant assertions
[opencv.git] / opencv / src / cv / cvsurf.cpp
1 /* Original code has been submitted by Liu Liu. Here is the copyright.
2 ----------------------------------------------------------------------------------
3  * An OpenCV Implementation of SURF
4  * Further Information Refer to "SURF: Speed-Up Robust Feature"
5  * Author: Liu Liu
6  * liuliu.1987+opencv@gmail.com
7  *
8  * There are still serveral lacks for this experimental implementation:
9  * 1.The interpolation of sub-pixel mentioned in article was not implemented yet;
10  * 2.A comparision with original libSurf.so shows that the hessian detector is not a 100% match to their implementation;
11  * 3.Due to above reasons, I recommanded the original one for study and reuse;
12  *
13  * However, the speed of this implementation is something comparable to original one.
14  *
15  * Copyright© 2008, Liu Liu All rights reserved.
16  *
17  * Redistribution and use in source and binary forms, with or
18  * without modification, are permitted provided that the following
19  * conditions are met:
20  *      Redistributions of source code must retain the above
21  *      copyright notice, this list of conditions and the following
22  *      disclaimer.
23  *      Redistributions in binary form must reproduce the above
24  *      copyright notice, this list of conditions and the following
25  *      disclaimer in the documentation and/or other materials
26  *      provided with the distribution.
27  *      The name of Contributor may not be used to endorse or
28  *      promote products derived from this software without
29  *      specific prior written permission.
30  *
31  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
32  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
33  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
34  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
35  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
36  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
38  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
39  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
40  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
41  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
42  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
43  * OF SUCH DAMAGE.
44  */
45
46 /* 
47    The following changes have been made, comparing to the original contribution:
48    1. A lot of small optimizations, less memory allocations, got rid of global buffers
49    2. Reversed order of cvGetQuadrangleSubPix and cvResize calls; probably less accurate, but much faster
50    3. The descriptor computing part (which is most expensive) is threaded using OpenMP
51    (subpixel-accurate keypoint localization and scale estimation are still TBD)
52 */
53
54 /*
55 Keypoint position and scale interpolation has been implemented as described in
56 the Brown and Lowe paper cited by the SURF paper.
57
58 The sampling step along the x and y axes of the image for the determinant of the
59 Hessian is now the same for each layer in an octave. While this increases the
60 computation time, it ensures that a true 3x3x3 neighbourhood exists, with
61 samples calculated at the same position in the layers above and below. This
62 results in improved maxima detection and non-maxima suppression, and I think it
63 is consistent with the description in the SURF paper.
64
65 The wavelet size sampling interval has also been made consistent. The wavelet
66 size at the first layer of the first octave is now 9 instead of 7. Along with
67 regular position sampling steps, this makes location and scale interpolation
68 easy. I think this is consistent with the SURF paper and original
69 implementation.
70
71 The scaling of the wavelet parameters has been fixed to ensure that the patterns
72 are symmetric around the centre. Previously the truncation caused by integer
73 division in the scaling ratio caused a bias towards the top left of the wavelet,
74 resulting in inconsistent keypoint positions.
75
76 The matrices for the determinant and trace of the Hessian are now reused in each
77 octave.
78
79 The extraction of the patch of pixels surrounding a keypoint used to build a
80 descriptor has been simplified.
81
82 Keypoint descriptor normalisation has been changed from normalising each 4x4 
83 cell (resulting in a descriptor of magnitude 16) to normalising the entire 
84 descriptor to magnitude 1.
85
86 The default number of octaves has been increased from 3 to 4 to match the
87 original SURF binary default. The increase in computation time is minimal since
88 the higher octaves are sampled sparsely.
89
90 The default number of layers per octave has been reduced from 3 to 2, to prevent
91 redundant calculation of similar sizes in consecutive octaves.  This decreases 
92 computation time. The number of features extracted may be less, however the 
93 additional features were mostly redundant.
94
95 The radius of the circle of gradient samples used to assign an orientation has
96 been increased from 4 to 6 to match the description in the SURF paper. This is 
97 now defined by ORI_RADIUS, and could be made into a parameter.
98
99 The size of the sliding window used in orientation assignment has been reduced
100 from 120 to 60 degrees to match the description in the SURF paper. This is now
101 defined by ORI_WIN, and could be made into a parameter.
102
103 Other options like  HAAR_SIZE0, HAAR_SIZE_INC, SAMPLE_STEP0, ORI_SEARCH_INC, 
104 ORI_SIGMA and DESC_SIGMA have been separated from the code and documented. 
105 These could also be made into parameters.
106
107 Modifications by Ian Mahon
108
109 */
110 #include "_cv.h"
111
112 CvSURFParams cvSURFParams(double threshold, int extended)
113 {
114     CvSURFParams params;
115     params.hessianThreshold = threshold;
116     params.extended = extended;
117     params.nOctaves = 4;
118     params.nOctaveLayers = 2;
119     return params;
120 }
121
122 struct CvSurfHF
123 {
124     int p0, p1, p2, p3;
125     float w;
126 };
127
128 CV_INLINE float
129 icvCalcHaarPattern( const int* origin, const CvSurfHF* f, int n )
130 {
131     double d = 0;
132     for( int k = 0; k < n; k++ )
133         d += (origin[f[k].p0] + origin[f[k].p3] - origin[f[k].p1] - origin[f[k].p2])*f[k].w;
134     return (float)d;
135 }
136
137 static void
138 icvResizeHaarPattern( const int src[][5], CvSurfHF* dst, int n, int oldSize, int newSize, int widthStep )
139 {
140     float ratio = (float)newSize/oldSize;
141     for( int k = 0; k < n; k++ )
142     {
143         int dx1 = cvRound( ratio*src[k][0] );
144         int dy1 = cvRound( ratio*src[k][1] );
145         int dx2 = cvRound( ratio*src[k][2] );
146         int dy2 = cvRound( ratio*src[k][3] );
147         dst[k].p0 = dy1*widthStep + dx1;
148         dst[k].p1 = dy2*widthStep + dx1;
149         dst[k].p2 = dy1*widthStep + dx2;
150         dst[k].p3 = dy2*widthStep + dx2;
151         dst[k].w = src[k][4]/((float)(dx2-dx1)*(dy2-dy1));
152     }
153 }
154
155 /*
156  * Maxima location interpolation as described in "Invariant Features from
157  * Interest Point Groups" by Matthew Brown and David Lowe. This is performed by
158  * fitting a 3D quadratic to a set of neighbouring samples.
159  * 
160  * The gradient vector and Hessian matrix at the initial keypoint location are 
161  * approximated using central differences. The linear system Ax = b is then
162  * solved, where A is the Hessian, b is the negative gradient, and x is the 
163  * offset of the interpolated maxima coordinates from the initial estimate.
164  * This is equivalent to an iteration of Netwon's optimisation algorithm.
165  *
166  * N9 contains the samples in the 3x3x3 neighbourhood of the maxima
167  * dx is the sampling step in x
168  * dy is the sampling step in y
169  * ds is the sampling step in size
170  * point contains the keypoint coordinates and scale to be modified
171  *
172  * Return value is 1 if interpolation was successful, 0 on failure.
173  */   
174 CV_INLINE int 
175 icvInterpolateKeypoint( float N9[3][9], int dx, int dy, int ds, CvSURFPoint *point )
176 {
177     int solve_ok;
178     float A[9], x[3], b[3];
179     CvMat _A = cvMat(3, 3, CV_32F, A);
180     CvMat _x = cvMat(3, 1, CV_32F, x);                
181     CvMat _b = cvMat(3, 1, CV_32F, b);
182
183     b[0] = -(N9[1][5]-N9[1][3])/2;  /* Negative 1st deriv with respect to x */
184     b[1] = -(N9[1][7]-N9[1][1])/2;  /* Negative 1st deriv with respect to y */
185     b[2] = -(N9[2][4]-N9[0][4])/2;  /* Negative 1st deriv with respect to s */
186
187     A[0] = N9[1][3]-2*N9[1][4]+N9[1][5];            /* 2nd deriv x, x */
188     A[1] = (N9[1][8]-N9[1][6]-N9[1][2]+N9[1][0])/4; /* 2nd deriv x, y */
189     A[2] = (N9[2][5]-N9[2][3]-N9[0][5]+N9[0][3])/4; /* 2nd deriv x, s */
190     A[3] = A[1];                                    /* 2nd deriv y, x */
191     A[4] = N9[1][1]-2*N9[1][4]+N9[1][7];            /* 2nd deriv y, y */
192     A[5] = (N9[2][7]-N9[2][1]-N9[0][7]+N9[0][1])/4; /* 2nd deriv y, s */
193     A[6] = A[2];                                    /* 2nd deriv s, x */
194     A[7] = A[5];                                    /* 2nd deriv s, y */
195     A[8] = N9[0][4]-2*N9[1][4]+N9[2][4];            /* 2nd deriv s, s */
196
197     solve_ok = cvSolve( &_A, &_b, &_x );
198     if( solve_ok )
199     {
200         point->pt.x += x[0]*dx;
201         point->pt.y += x[1]*dy;
202         point->size = cvRound( point->size + x[2]*ds ); 
203     }
204     return solve_ok;
205 }
206
207
208 static CvSeq* icvFastHessianDetector( const CvMat* sum, const CvMat* mask_sum,
209     CvMemStorage* storage, const CvSURFParams* params )
210 {
211     CvSeq* points = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSURFPoint), storage );
212     
213     /* Wavelet size at first layer of first octave. */ 
214     const int HAAR_SIZE0 = 9;    
215
216     /* Wavelet size increment between layers. This should be an even number, 
217        such that the wavelet sizes in an octave are either all even or all odd.
218        This ensures that when looking for the neighbours of a sample, the layers
219        above and below are aligned correctly. */
220     const int HAAR_SIZE_INC = 6; 
221
222     /* Sampling step along image x and y axes at first octave. This is doubled
223        for each additional octave. WARNING: Increasing this improves speed, 
224        however keypoint extraction becomes unreliable. */
225     const int SAMPLE_STEP0 = 1; 
226
227
228     /* Wavelet Data */
229     const int NX=3, NY=3, NXY=4, NM=1;
230     const int dx_s[NX][5] = { {0, 2, 3, 7, 1}, {3, 2, 6, 7, -2}, {6, 2, 9, 7, 1} };
231     const int dy_s[NY][5] = { {2, 0, 7, 3, 1}, {2, 3, 7, 6, -2}, {2, 6, 7, 9, 1} };
232     const int dxy_s[NXY][5] = { {1, 1, 4, 4, 1}, {5, 1, 8, 4, -1}, {1, 5, 4, 8, -1}, {5, 5, 8, 8, 1} };
233     const int dm[NM][5] = { {0, 0, 9, 9, 1} };
234     CvSurfHF Dx[NX], Dy[NY], Dxy[NXY], Dm;
235
236     CvMat** dets = (CvMat**)cvStackAlloc((params->nOctaveLayers+2)*sizeof(dets[0]));
237     CvMat** traces = (CvMat**)cvStackAlloc((params->nOctaveLayers+2)*sizeof(traces[0]));
238     int *sizes = (int*)cvStackAlloc((params->nOctaveLayers+2)*sizeof(sizes[0]));
239
240     double dx = 0, dy = 0, dxy = 0;
241     int octave, layer, sampleStep, size, margin;
242     int rows, cols;
243     int i, j, sum_i, sum_j;
244     const int* s_ptr;
245     float *det_ptr, *trace_ptr;
246
247     /* Allocate enough space for hessian determinant and trace matrices at the 
248        first octave. Clearing these initially or between octaves is not
249        required, since all values that are accessed are first calculated */
250     for( layer = 0; layer <= params->nOctaveLayers+1; layer++ )
251     {
252         dets[layer]   = cvCreateMat( (sum->rows-1)/SAMPLE_STEP0, (sum->cols-1)/SAMPLE_STEP0, CV_32FC1 );
253         traces[layer] = cvCreateMat( (sum->rows-1)/SAMPLE_STEP0, (sum->cols-1)/SAMPLE_STEP0, CV_32FC1 );
254     }
255
256     for( octave = 0, sampleStep=SAMPLE_STEP0; octave < params->nOctaves; octave++, sampleStep*=2 )
257     {
258         /* Hessian determinant and trace sample array size in this octave */
259         rows = (sum->rows-1)/sampleStep;
260         cols = (sum->cols-1)/sampleStep;
261
262         /* Calculate the determinant and trace of the hessian */
263         for( layer = 0; layer <= params->nOctaveLayers+1; layer++ )
264         {
265             sizes[layer] = size = (HAAR_SIZE0+HAAR_SIZE_INC*layer)<<octave;
266             icvResizeHaarPattern( dx_s, Dx, NX, 9, size, sum->cols );
267             icvResizeHaarPattern( dy_s, Dy, NY, 9, size, sum->cols );
268             icvResizeHaarPattern( dxy_s, Dxy, NXY, 9, size, sum->cols );
269             /*printf( "octave=%d layer=%d size=%d rows=%d cols=%d\n", octave, layer, size, rows, cols );*/
270             
271             margin = (size/2)/sampleStep;
272             for( sum_i=0, i=margin; sum_i<=(sum->rows-1)-size; sum_i+=sampleStep, i++ )
273             {
274                 s_ptr = sum->data.i + sum_i*sum->cols;
275                 det_ptr = dets[layer]->data.fl + i*dets[layer]->cols + margin;
276                 trace_ptr = traces[layer]->data.fl + i*traces[layer]->cols + margin;
277                 for( sum_j=0, j=margin; sum_j<=(sum->cols-1)-size; sum_j+=sampleStep, j++ )
278                 {
279                     dx  = icvCalcHaarPattern( s_ptr, Dx, 3 );
280                     dy  = icvCalcHaarPattern( s_ptr, Dy, 3 );
281                     dxy = icvCalcHaarPattern( s_ptr, Dxy, 4 );
282                     s_ptr+=sampleStep;
283                     *det_ptr++ = (float)(dx*dy - 0.81*dxy*dxy);
284                     *trace_ptr++ = (float)(dx + dy);
285                 }
286             }
287         }
288
289         /* Find maxima in the determinant of the hessian */
290         for( layer = 1; layer <= params->nOctaveLayers; layer++ )
291         {
292             size = sizes[layer];
293             icvResizeHaarPattern( dm, &Dm, NM, 9, size, mask_sum ? mask_sum->cols : sum->cols );
294             
295             /* Ignore pixels without a 3x3 neighbourhood in the layer above */
296             margin = (sizes[layer+1]/2)/sampleStep+1; 
297             for( i = margin; i < rows-margin; i++ )
298             {
299                 det_ptr = dets[layer]->data.fl + i*dets[layer]->cols;
300                 trace_ptr = traces[layer]->data.fl + i*traces[layer]->cols;
301                 for( j = margin; j < cols-margin; j++ )
302                 {
303                     float val0 = det_ptr[j];
304                     if( val0 > params->hessianThreshold )
305                     {
306                         /* Coordinates for the start of the wavelet in the sum image. There   
307                            is some integer division involved, so don't try to simplify this
308                            (cancel out sampleStep) without checking the result is the same */
309                         int sum_i = sampleStep*(i-(size/2)/sampleStep);
310                         int sum_j = sampleStep*(j-(size/2)/sampleStep);
311
312                         /* The 3x3x3 neighbouring samples around the maxima. 
313                            The maxima is included at N9[1][4] */
314                         int c = dets[layer]->cols;
315                         const float *det1 = dets[layer-1]->data.fl + i*c + j;
316                         const float *det2 = dets[layer]->data.fl   + i*c + j;
317                         const float *det3 = dets[layer+1]->data.fl + i*c + j;
318                         float N9[3][9] = { { det1[-c-1], det1[-c], det1[-c+1],          
319                                              det1[-1]  , det1[0] , det1[1],
320                                              det1[c-1] , det1[c] , det1[c+1]  },
321                                            { det2[-c-1], det2[-c], det2[-c+1],       
322                                              det2[-1]  , det2[0] , det2[1],
323                                              det2[c-1] , det2[c] , det2[c+1 ] },
324                                            { det3[-c-1], det3[-c], det3[-c+1],       
325                                              det3[-1  ], det3[0] , det3[1],
326                                              det3[c-1] , det3[c] , det3[c+1 ] } };
327
328                         /* Check the mask - why not just check the mask at the center of the wavelet? */
329                         if( mask_sum )
330                         {
331                             const int* mask_ptr = mask_sum->data.i +  mask_sum->cols*sum_i + sum_j;
332                             float mval = icvCalcHaarPattern( mask_ptr, &Dm, 1 );
333                             if( mval < 0.5 )
334                                 continue;
335                         }
336
337                         /* Non-maxima suppression. val0 is at N9[1][4]*/
338                         if( val0 > N9[0][0] && val0 > N9[0][1] && val0 > N9[0][2] &&
339                             val0 > N9[0][3] && val0 > N9[0][4] && val0 > N9[0][5] &&
340                             val0 > N9[0][6] && val0 > N9[0][7] && val0 > N9[0][8] &&
341                             val0 > N9[1][0] && val0 > N9[1][1] && val0 > N9[1][2] &&
342                             val0 > N9[1][3]                    && val0 > N9[1][5] &&
343                             val0 > N9[1][6] && val0 > N9[1][7] && val0 > N9[1][8] &&
344                             val0 > N9[2][0] && val0 > N9[2][1] && val0 > N9[2][2] &&
345                             val0 > N9[2][3] && val0 > N9[2][4] && val0 > N9[2][5] &&
346                             val0 > N9[2][6] && val0 > N9[2][7] && val0 > N9[2][8] )
347                         {
348                             /* Calculate the wavelet center coordinates for the maxima */
349                             double center_i = sum_i + (double)(size-1)/2;
350                             double center_j = sum_j + (double)(size-1)/2;
351
352                             CvSURFPoint point = cvSURFPoint( cvPoint2D32f(center_j,center_i), 
353                                                              CV_SIGN(trace_ptr[j]), sizes[layer], 0, val0 );
354                            
355                             /* Interpolate maxima location within the 3x3x3 neighbourhood  */
356                             int ds = sizes[layer]-sizes[layer-1];
357                             int interp_ok = icvInterpolateKeypoint( N9, sampleStep, sampleStep, ds, &point );
358
359                             /* Sometimes the interpolation step gives a negative size etc. */
360                             if( interp_ok && point.size >= 1 &&
361                                 point.pt.x >= 0 && point.pt.x <= (sum->cols-1) &&
362                                 point.pt.y >= 0 && point.pt.y <= (sum->rows-1) )
363                             {    
364                                 /*printf( "Keypoint %f %f %d\n", point.pt.x, point.pt.y, point.size );*/
365                                 cvSeqPush( points, &point );
366                             }    
367                         }
368                     }
369                 }
370             }
371         }
372     }
373
374     /* Clean-up */
375     for( layer = 0; layer <= params->nOctaveLayers+1; layer++ )
376     {
377         cvReleaseMat( &dets[layer] );
378         cvReleaseMat( &traces[layer] );
379     }
380
381     return points;
382 }
383
384
385 CV_IMPL void
386 cvExtractSURF( const CvArr* _img, const CvArr* _mask,
387                CvSeq** _keypoints, CvSeq** _descriptors,
388                CvMemStorage* storage, CvSURFParams params )
389 {
390     CvMat *sum = 0, *mask1 = 0, *mask_sum = 0, **win_bufs = 0;
391
392     if( _keypoints )
393         *_keypoints = 0;
394     if( _descriptors )
395         *_descriptors = 0;
396
397     CV_FUNCNAME( "cvExtractSURF" );
398
399     __BEGIN__;
400
401     /* Radius of the circle in which to sample gradients to assign an 
402        orientation */
403     const int ORI_RADIUS = 6; 
404
405     /* The size of the sliding window (in degrees) used to assign an 
406        orientation */
407     const int ORI_WIN = 60;   
408
409     /* Increment used for the orientation sliding window (in degrees) */
410     const int ORI_SEARCH_INC = 5;  
411
412     /* Standard deviation of the Gaussian used to weight the gradient samples
413        used to assign an orientation */ 
414     const float ORI_SIGMA = 2.5f;
415
416     /* Standard deviation of the Gaussian used to weight the gradient samples
417        used to build a keypoint descriptor */
418     const float DESC_SIGMA = 3.3f;
419
420
421     /* X and Y gradient wavelet data */
422     const int NX=2, NY=2;
423     int dx_s[NX][5] = {{0, 0, 2, 4, -1}, {2, 0, 4, 4, 1}};
424     int dy_s[NY][5] = {{0, 0, 4, 2, 1}, {0, 2, 4, 4, -1}};
425
426     CvSeq *keypoints, *descriptors = 0;
427     CvMat imghdr, *img = cvGetMat(_img, &imghdr);
428     CvMat maskhdr, *mask = _mask ? cvGetMat(_mask, &maskhdr) : 0;
429     
430     const int max_ori_samples = (2*ORI_RADIUS+1)*(2*ORI_RADIUS+1);
431     int descriptor_size = params.extended ? 128 : 64;
432     const int descriptor_data_type = CV_32F;
433     const int PATCH_SZ = 20;
434     float G[2*ORI_RADIUS+1]; 
435     CvMat _G = cvMat(1, (2*ORI_RADIUS+1), CV_32F, G);
436     float DW[PATCH_SZ][PATCH_SZ];
437     CvMat _DW = cvMat(PATCH_SZ, PATCH_SZ, CV_32F, DW);
438     CvPoint apt[max_ori_samples];
439     float apt_w[max_ori_samples];
440     int i, j, k, nangle0 = 0, N;
441     int nthreads = cvGetNumThreads();
442
443     CV_ASSERT(img != 0);
444     CV_ASSERT(CV_MAT_TYPE(img->type) == CV_8UC1);
445     CV_ASSERT(mask == 0 || (CV_ARE_SIZES_EQ(img,mask) && CV_MAT_TYPE(mask->type) == CV_8UC1));
446     CV_ASSERT(storage != 0);
447     CV_ASSERT(params.hessianThreshold >= 0);
448     CV_ASSERT(params.nOctaves > 0);
449     CV_ASSERT(params.nOctaveLayers > 0);
450
451     sum = cvCreateMat( img->height+1, img->width+1, CV_32SC1 );
452     cvIntegral( img, sum );
453     if( mask )
454     {
455         mask1 = cvCreateMat( img->height, img->width, CV_8UC1 );
456         mask_sum = cvCreateMat( img->height+1, img->width+1, CV_32SC1 );
457         cvMinS( mask, 1, mask1 );
458         cvIntegral( mask1, mask_sum );
459     }
460     keypoints = icvFastHessianDetector( sum, mask_sum, storage, &params );
461     N = keypoints->total;
462     if( _descriptors )
463     {
464         descriptors = cvCreateSeq( 0, sizeof(CvSeq),
465             descriptor_size*CV_ELEM_SIZE(descriptor_data_type), storage );
466         cvSeqPushMulti( descriptors, 0, N );
467     }
468
469     /* Coordinates and weights of samples used to calculate orientation */
470     CvSepFilter::init_gaussian_kernel( &_G, ORI_SIGMA );
471     for( i = -ORI_RADIUS; i <= ORI_RADIUS; i++ )
472     {
473         for( j = -ORI_RADIUS; j <= ORI_RADIUS; j++ )
474         {
475             if( i*i + j*j <= ORI_RADIUS*ORI_RADIUS )
476             {
477                 apt[nangle0] = cvPoint(j,i);
478                 apt_w[nangle0++] = G[i+ORI_RADIUS]*G[j+ORI_RADIUS];
479             }
480         }
481     }
482
483     /* Gaussian used to weight descriptor samples */
484     {
485     double c2 = 1./(DESC_SIGMA*DESC_SIGMA*2);
486     double gs = 0;
487     for( i = 0; i < PATCH_SZ; i++ )
488     {
489         for( j = 0; j < PATCH_SZ; j++ )
490         {
491             double x = j - (float)(PATCH_SZ-1)/2, y = i - (float)(PATCH_SZ-1)/2;
492             double val = exp(-(x*x+y*y)*c2);
493             DW[i][j] = (float)val;
494             gs += val;
495         }
496     }
497     cvScale( &_DW, &_DW, 1./gs );
498     }
499
500     win_bufs = (CvMat**)cvAlloc(nthreads*sizeof(win_bufs[0]));
501     for( i = 0; i < nthreads; i++ )
502         win_bufs[i] = 0;
503
504 #ifdef _OPENMP
505 #pragma omp parallel for num_threads(nthreads) schedule(dynamic)
506 #endif
507     for( k = 0; k < N; k++ )
508     {
509         const int* sum_ptr = sum->data.i;
510         int sum_cols = sum->cols;
511         int i, j, kk, x, y, nangle;
512         float X[max_ori_samples], Y[max_ori_samples], angle[max_ori_samples];
513         uchar PATCH[PATCH_SZ+1][PATCH_SZ+1];
514         float DX[PATCH_SZ][PATCH_SZ], DY[PATCH_SZ][PATCH_SZ];
515         CvMat _X = cvMat(1, max_ori_samples, CV_32F, X);
516         CvMat _Y = cvMat(1, max_ori_samples, CV_32F, Y);
517         CvMat _angle = cvMat(1, max_ori_samples, CV_32F, angle);
518         CvMat _patch = cvMat(PATCH_SZ+1, PATCH_SZ+1, CV_8U, PATCH);
519         float* vec;
520         CvSurfHF dx_t[NX], dy_t[NY];
521         int thread_idx = cvGetThreadNum();
522         
523         CvSURFPoint* kp = (CvSURFPoint*)cvGetSeqElem( keypoints, k );
524         int size = kp->size;
525         CvPoint2D32f center = kp->pt;
526
527         /* The sampling intervals and wavelet sized for selecting an orientation
528            and building the keypoint descriptor are defined relative to 's' */
529         float s = (float)size*1.2f/9.0f;
530
531         /* To find the dominant orientation, the gradients in x and y are
532            sampled in a circle of radius 6s using wavelets of size 4s.
533            We ensure the gradient wavelet size is even to ensure the 
534            wavelet pattern is balanced and symmetric around its center */
535         int grad_wav_size = 2*cvRound( 2*s ); 
536         icvResizeHaarPattern( dx_s, dx_t, NX, 4, grad_wav_size, sum->cols );
537         icvResizeHaarPattern( dy_s, dy_t, NY, 4, grad_wav_size, sum->cols );
538         for( kk = 0, nangle = 0; kk < nangle0; kk++ )
539         {
540             const int* ptr;
541             float vx, vy;
542             x = cvRound( center.x + apt[kk].x*s - (float)(grad_wav_size-1)/2 );
543             y = cvRound( center.y + apt[kk].y*s - (float)(grad_wav_size-1)/2 );
544             if( (unsigned)y >= (unsigned)sum->rows - grad_wav_size ||
545                 (unsigned)x >= (unsigned)sum->cols - grad_wav_size )
546                 continue;
547             ptr = sum_ptr + x + y*sum_cols;
548             vx = icvCalcHaarPattern( ptr, dx_t, 2 );
549             vy = icvCalcHaarPattern( ptr, dy_t, 2 );
550             X[nangle] = vx*apt_w[kk]; Y[nangle] = vy*apt_w[kk];
551             nangle++;
552         }
553         _X.cols = _Y.cols = _angle.cols = nangle;
554         cvCartToPolar( &_X, &_Y, 0, &_angle, 1 );
555
556         float bestx = 0, besty = 0, descriptor_mod = 0;
557         for( i = 0; i < 360; i += ORI_SEARCH_INC )
558         {
559             float sumx = 0, sumy = 0, temp_mod;
560             for( j = 0; j < nangle; j++ )
561             {
562                 int d = abs(cvRound(angle[j]) - i);
563                 if( d < ORI_WIN/2 || d > 360-ORI_WIN/2 )
564                 {
565                     sumx += X[j];
566                     sumy += Y[j];
567                 }
568             }
569             temp_mod = sumx*sumx + sumy*sumy;
570             if( temp_mod > descriptor_mod )
571             {
572                 descriptor_mod = temp_mod;
573                 bestx = sumx;
574                 besty = sumy;
575             }
576         }
577         
578         float descriptor_dir = cvFastArctan( besty, bestx );
579         kp->dir = descriptor_dir;
580
581         if( !_descriptors )
582             continue;
583
584         descriptor_dir *= (float)(CV_PI/180);
585         
586         /* Extract a window of pixels around the keypoint of size 20s */
587         int win_size = (int)((PATCH_SZ+1)*s);
588         if( win_bufs[thread_idx] == 0 || win_bufs[thread_idx]->cols < win_size*win_size )
589         {
590             cvReleaseMat( &win_bufs[thread_idx] );
591             win_bufs[thread_idx] = cvCreateMat( 1, win_size*win_size, CV_8U );
592         }
593         
594         CvMat win = cvMat(win_size, win_size, CV_8U, win_bufs[thread_idx]->data.ptr);
595         float sin_dir = sin(descriptor_dir);
596         float cos_dir = cos(descriptor_dir) ;
597
598         /* Subpixel interpolation version (slower). Subpixel not required since
599            the pixels will all get averaged when we scale down to 20 pixels */
600         /*  
601         float w[] = { cos_dir, sin_dir, center.x,
602                       -sin_dir, cos_dir , center.y };
603         CvMat W = cvMat(2, 3, CV_32F, w);
604         cvGetQuadrangleSubPix( img, &win, &W );
605         */
606
607         /* Nearest neighbour version (faster) */
608         float win_offset = -(float)(win_size-1)/2;
609         float start_x = center.x + win_offset*cos_dir + win_offset*sin_dir;
610         float start_y = center.y - win_offset*sin_dir + win_offset*cos_dir;
611         uchar* WIN = win.data.ptr;
612         for( i=0; i<win_size; i++, start_x+=sin_dir, start_y+=cos_dir )
613         {
614             float pixel_x = start_x;
615             float pixel_y = start_y;
616             for( j=0; j<win_size; j++, pixel_x+=cos_dir, pixel_y-=sin_dir )
617             {
618                 int x = cvRound( pixel_x );
619                 int y = cvRound( pixel_y );
620                 x = MAX( x, 0 );
621                 y = MAX( y, 0 );
622                 x = MIN( x, img->cols-1 );
623                 y = MIN( y, img->rows-1 );
624                 WIN[i*win_size + j] = img->data.ptr[y*img->step+x];
625              }
626         }
627
628         /* Scale the window to size PATCH_SZ so each pixel's size is s. This
629            makes calculating the gradients with wavelets of size 2s easy */
630         cvResize( &win, &_patch, CV_INTER_AREA );
631
632         /* Calculate gradients in x and y with wavelets of size 2s */
633         for( i = 0; i < PATCH_SZ; i++ )
634             for( j = 0; j < PATCH_SZ; j++ )
635             {
636                 float dw = DW[i][j];
637                 float vx = (PATCH[i][j+1] - PATCH[i][j] + PATCH[i+1][j+1] - PATCH[i+1][j])*dw;
638                 float vy = (PATCH[i+1][j] - PATCH[i][j] + PATCH[i+1][j+1] - PATCH[i][j+1])*dw;
639                 DX[i][j] = vx;
640                 DY[i][j] = vy;
641             }
642
643         /* Construct the descriptor */
644         vec = (float*)cvGetSeqElem( descriptors, k );
645         for( kk = 0; kk < (int)(descriptors->elem_size/sizeof(vec[0])); kk++ )
646             vec[kk] = 0;
647         double square_mag = 0;       
648         if( params.extended )
649         {
650             /* 128-bin descriptor */
651             for( i = 0; i < 4; i++ )
652                 for( j = 0; j < 4; j++ )
653                 {
654                     for( y = i*5; y < i*5+5; y++ )
655                     {
656                         for( x = j*5; x < j*5+5; x++ )
657                         {
658                             float tx = DX[y][x], ty = DY[y][x];
659                             if( ty >= 0 )
660                             {
661                                 vec[0] += tx;
662                                 vec[1] += (float)fabs(tx);
663                             } else {
664                                 vec[2] += tx;
665                                 vec[3] += (float)fabs(tx);
666                             }
667                             if ( tx >= 0 )
668                             {
669                                 vec[4] += ty;
670                                 vec[5] += (float)fabs(ty);
671                             } else {
672                                 vec[6] += ty;
673                                 vec[7] += (float)fabs(ty);
674                             }
675                         }
676                     }
677                     for( kk = 0; kk < 8; kk++ )
678                         square_mag += vec[kk]*vec[kk];
679                     vec += 8;
680                 }
681         }
682         else
683         {
684             /* 64-bin descriptor */
685             for( i = 0; i < 4; i++ )
686                 for( j = 0; j < 4; j++ )
687                 {
688                     for( y = i*5; y < i*5+5; y++ )
689                     {
690                         for( x = j*5; x < j*5+5; x++ )
691                         {
692                             float tx = DX[y][x], ty = DY[y][x];
693                             vec[0] += tx; vec[1] += ty;
694                             vec[2] += (float)fabs(tx); vec[3] += (float)fabs(ty);
695                         }
696                     }
697                     for( kk = 0; kk < 4; kk++ )
698                         square_mag += vec[kk]*vec[kk];
699                     vec+=4;
700                 }
701         }
702
703         /* unit vector is essential for contrast invariance */
704         vec = (float*)cvGetSeqElem( descriptors, k );
705         double scale = 1./(sqrt(square_mag) + DBL_EPSILON);
706         for( kk = 0; kk < descriptor_size; kk++ )
707             vec[kk] = (float)(vec[kk]*scale);
708     }
709
710     for( i = 0; i < nthreads; i++ )
711         cvReleaseMat( &win_bufs[i] );
712
713     if( _keypoints )
714         *_keypoints = keypoints;
715     if( _descriptors )
716         *_descriptors = descriptors;
717
718     __END__;
719
720     cvReleaseMat( &sum );
721     cvReleaseMat( &mask1 );
722     cvReleaseMat( &mask_sum );
723     cvFree( &win_bufs );
724 }
725