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"
6 * liuliu.1987+opencv@gmail.com
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;
13 * However, the speed of this implementation is something comparable to original one.
15 * Copyright© 2008, Liu Liu All rights reserved.
17 * Redistribution and use in source and binary forms, with or
18 * without modification, are permitted provided that the following
20 * Redistributions of source code must retain the above
21 * copyright notice, this list of conditions and the following
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.
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
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)
55 Keypoint position and scale interpolation has been implemented as described in
56 the Brown and Lowe paper cited by the SURF paper.
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.
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
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.
76 The matrices for the determinant and trace of the Hessian are now reused in each
79 The extraction of the patch of pixels surrounding a keypoint used to build a
80 descriptor has been simplified.
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.
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.
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.
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.
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.
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.
107 Modifications by Ian Mahon
112 CvSURFParams cvSURFParams(double threshold, int extended)
115 params.hessianThreshold = threshold;
116 params.extended = extended;
118 params.nOctaveLayers = 2;
129 icvCalcHaarPattern( const int* origin, const CvSurfHF* f, int n )
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;
138 icvResizeHaarPattern( const int src[][5], CvSurfHF* dst, int n, int oldSize, int newSize, int widthStep )
140 float ratio = (float)newSize/oldSize;
141 for( int k = 0; k < n; k++ )
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));
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.
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.
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
172 * Return value is 1 if interpolation was successful, 0 on failure.
175 icvInterpolateKeypoint( float N9[3][9], int dx, int dy, int ds, CvSURFPoint *point )
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);
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 */
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 */
197 solve_ok = cvSolve( &_A, &_b, &_x );
200 point->pt.x += x[0]*dx;
201 point->pt.y += x[1]*dy;
202 point->size = cvRound( point->size + x[2]*ds );
208 static CvSeq* icvFastHessianDetector( const CvMat* sum, const CvMat* mask_sum,
209 CvMemStorage* storage, const CvSURFParams* params )
211 CvSeq* points = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSURFPoint), storage );
213 /* Wavelet size at first layer of first octave. */
214 const int HAAR_SIZE0 = 9;
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;
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;
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;
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]));
240 double dx = 0, dy = 0, dxy = 0;
241 int octave, layer, sampleStep, size, margin;
243 int i, j, sum_i, sum_j;
245 float *det_ptr, *trace_ptr;
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++ )
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 );
256 for( octave = 0, sampleStep=SAMPLE_STEP0; octave < params->nOctaves; octave++, sampleStep*=2 )
258 /* Hessian determinant and trace sample array size in this octave */
259 rows = (sum->rows-1)/sampleStep;
260 cols = (sum->cols-1)/sampleStep;
262 /* Calculate the determinant and trace of the hessian */
263 for( layer = 0; layer <= params->nOctaveLayers+1; layer++ )
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 );*/
271 margin = (size/2)/sampleStep;
272 for( sum_i=0, i=margin; sum_i<=(sum->rows-1)-size; sum_i+=sampleStep, i++ )
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++ )
279 dx = icvCalcHaarPattern( s_ptr, Dx, 3 );
280 dy = icvCalcHaarPattern( s_ptr, Dy, 3 );
281 dxy = icvCalcHaarPattern( s_ptr, Dxy, 4 );
283 *det_ptr++ = (float)(dx*dy - 0.81*dxy*dxy);
284 *trace_ptr++ = (float)(dx + dy);
289 /* Find maxima in the determinant of the hessian */
290 for( layer = 1; layer <= params->nOctaveLayers; layer++ )
293 icvResizeHaarPattern( dm, &Dm, NM, 9, size, mask_sum ? mask_sum->cols : sum->cols );
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++ )
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++ )
303 float val0 = det_ptr[j];
304 if( val0 > params->hessianThreshold )
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);
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 ] } };
328 /* Check the mask - why not just check the mask at the center of the wavelet? */
331 const int* mask_ptr = mask_sum->data.i + mask_sum->cols*sum_i + sum_j;
332 float mval = icvCalcHaarPattern( mask_ptr, &Dm, 1 );
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] )
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;
352 CvSURFPoint point = cvSURFPoint( cvPoint2D32f(center_j,center_i),
353 CV_SIGN(trace_ptr[j]), sizes[layer], 0, val0 );
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 );
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) )
364 /*printf( "Keypoint %f %f %d\n", point.pt.x, point.pt.y, point.size );*/
365 cvSeqPush( points, &point );
375 for( layer = 0; layer <= params->nOctaveLayers+1; layer++ )
377 cvReleaseMat( &dets[layer] );
378 cvReleaseMat( &traces[layer] );
386 cvExtractSURF( const CvArr* _img, const CvArr* _mask,
387 CvSeq** _keypoints, CvSeq** _descriptors,
388 CvMemStorage* storage, CvSURFParams params )
390 CvMat *sum = 0, *mask1 = 0, *mask_sum = 0, **win_bufs = 0;
397 /* Radius of the circle in which to sample gradients to assign an
399 const int ORI_RADIUS = 6;
401 /* The size of the sliding window (in degrees) used to assign an
403 const int ORI_WIN = 60;
405 /* Increment used for the orientation sliding window (in degrees) */
406 const int ORI_SEARCH_INC = 5;
408 /* Standard deviation of the Gaussian used to weight the gradient samples
409 used to assign an orientation */
410 const float ORI_SIGMA = 2.5f;
412 /* Standard deviation of the Gaussian used to weight the gradient samples
413 used to build a keypoint descriptor */
414 const float DESC_SIGMA = 3.3f;
417 /* X and Y gradient wavelet data */
418 const int NX=2, NY=2;
419 int dx_s[NX][5] = {{0, 0, 2, 4, -1}, {2, 0, 4, 4, 1}};
420 int dy_s[NY][5] = {{0, 0, 4, 2, 1}, {0, 2, 4, 4, -1}};
422 CvSeq *keypoints, *descriptors = 0;
423 CvMat imghdr, *img = cvGetMat(_img, &imghdr);
424 CvMat maskhdr, *mask = _mask ? cvGetMat(_mask, &maskhdr) : 0;
426 const int max_ori_samples = (2*ORI_RADIUS+1)*(2*ORI_RADIUS+1);
427 int descriptor_size = params.extended ? 128 : 64;
428 const int descriptor_data_type = CV_32F;
429 const int PATCH_SZ = 20;
430 float DW[PATCH_SZ][PATCH_SZ];
431 CvMat _DW = cvMat(PATCH_SZ, PATCH_SZ, CV_32F, DW);
432 CvPoint apt[max_ori_samples];
433 float apt_w[max_ori_samples];
434 int i, j, k, nangle0 = 0, N;
435 int nthreads = cvGetNumThreads();
438 CV_Assert(CV_MAT_TYPE(img->type) == CV_8UC1);
439 CV_Assert(mask == 0 || (CV_ARE_SIZES_EQ(img,mask) && CV_MAT_TYPE(mask->type) == CV_8UC1));
440 CV_Assert(storage != 0);
441 CV_Assert(params.hessianThreshold >= 0);
442 CV_Assert(params.nOctaves > 0);
443 CV_Assert(params.nOctaveLayers > 0);
445 sum = cvCreateMat( img->height+1, img->width+1, CV_32SC1 );
446 cvIntegral( img, sum );
449 mask1 = cvCreateMat( img->height, img->width, CV_8UC1 );
450 mask_sum = cvCreateMat( img->height+1, img->width+1, CV_32SC1 );
451 cvMinS( mask, 1, mask1 );
452 cvIntegral( mask1, mask_sum );
454 keypoints = icvFastHessianDetector( sum, mask_sum, storage, ¶ms );
455 N = keypoints->total;
458 descriptors = cvCreateSeq( 0, sizeof(CvSeq),
459 descriptor_size*CV_ELEM_SIZE(descriptor_data_type), storage );
460 cvSeqPushMulti( descriptors, 0, N );
463 /* Coordinates and weights of samples used to calculate orientation */
464 cv::Mat _G = cv::getGaussianKernel( 2*ORI_RADIUS+1, ORI_SIGMA, CV_32F );
465 const float* G = (const float*)_G.data;
467 for( i = -ORI_RADIUS; i <= ORI_RADIUS; i++ )
469 for( j = -ORI_RADIUS; j <= ORI_RADIUS; j++ )
471 if( i*i + j*j <= ORI_RADIUS*ORI_RADIUS )
473 apt[nangle0] = cvPoint(j,i);
474 apt_w[nangle0++] = G[i+ORI_RADIUS]*G[j+ORI_RADIUS];
479 /* Gaussian used to weight descriptor samples */
481 double c2 = 1./(DESC_SIGMA*DESC_SIGMA*2);
483 for( i = 0; i < PATCH_SZ; i++ )
485 for( j = 0; j < PATCH_SZ; j++ )
487 double x = j - (float)(PATCH_SZ-1)/2, y = i - (float)(PATCH_SZ-1)/2;
488 double val = exp(-(x*x+y*y)*c2);
489 DW[i][j] = (float)val;
493 cvScale( &_DW, &_DW, 1./gs );
496 win_bufs = (CvMat**)cvAlloc(nthreads*sizeof(win_bufs[0]));
497 for( i = 0; i < nthreads; i++ )
501 #pragma omp parallel for num_threads(nthreads) schedule(dynamic)
503 for( k = 0; k < N; k++ )
505 const int* sum_ptr = sum->data.i;
506 int sum_cols = sum->cols;
507 int i, j, kk, x, y, nangle;
508 float X[max_ori_samples], Y[max_ori_samples], angle[max_ori_samples];
509 uchar PATCH[PATCH_SZ+1][PATCH_SZ+1];
510 float DX[PATCH_SZ][PATCH_SZ], DY[PATCH_SZ][PATCH_SZ];
511 CvMat _X = cvMat(1, max_ori_samples, CV_32F, X);
512 CvMat _Y = cvMat(1, max_ori_samples, CV_32F, Y);
513 CvMat _angle = cvMat(1, max_ori_samples, CV_32F, angle);
514 CvMat _patch = cvMat(PATCH_SZ+1, PATCH_SZ+1, CV_8U, PATCH);
516 CvSurfHF dx_t[NX], dy_t[NY];
517 int thread_idx = cvGetThreadNum();
519 CvSURFPoint* kp = (CvSURFPoint*)cvGetSeqElem( keypoints, k );
521 CvPoint2D32f center = kp->pt;
523 /* The sampling intervals and wavelet sized for selecting an orientation
524 and building the keypoint descriptor are defined relative to 's' */
525 float s = (float)size*1.2f/9.0f;
527 /* To find the dominant orientation, the gradients in x and y are
528 sampled in a circle of radius 6s using wavelets of size 4s.
529 We ensure the gradient wavelet size is even to ensure the
530 wavelet pattern is balanced and symmetric around its center */
531 int grad_wav_size = 2*cvRound( 2*s );
532 if ( sum->rows < grad_wav_size || sum->cols < grad_wav_size )
534 /* when grad_wav_size is too big,
535 * the sampling of gradient will be meaningless
536 * mark keypoint for deletion. */
540 icvResizeHaarPattern( dx_s, dx_t, NX, 4, grad_wav_size, sum->cols );
541 icvResizeHaarPattern( dy_s, dy_t, NY, 4, grad_wav_size, sum->cols );
542 for( kk = 0, nangle = 0; kk < nangle0; kk++ )
546 x = cvRound( center.x + apt[kk].x*s - (float)(grad_wav_size-1)/2 );
547 y = cvRound( center.y + apt[kk].y*s - (float)(grad_wav_size-1)/2 );
548 if( (unsigned)y >= (unsigned)(sum->rows - grad_wav_size) ||
549 (unsigned)x >= (unsigned)(sum->cols - grad_wav_size) )
551 ptr = sum_ptr + x + y*sum_cols;
552 vx = icvCalcHaarPattern( ptr, dx_t, 2 );
553 vy = icvCalcHaarPattern( ptr, dy_t, 2 );
554 X[nangle] = vx*apt_w[kk]; Y[nangle] = vy*apt_w[kk];
559 /* No gradient could be sampled because the keypoint is too
560 * near too one or more of the sides of the image. As we
561 * therefore cannot find a dominant direction, we skip this
562 * keypoint and mark it for later deletion from the sequence. */
566 _X.cols = _Y.cols = _angle.cols = nangle;
567 cvCartToPolar( &_X, &_Y, 0, &_angle, 1 );
569 float bestx = 0, besty = 0, descriptor_mod = 0;
570 for( i = 0; i < 360; i += ORI_SEARCH_INC )
572 float sumx = 0, sumy = 0, temp_mod;
573 for( j = 0; j < nangle; j++ )
575 int d = abs(cvRound(angle[j]) - i);
576 if( d < ORI_WIN/2 || d > 360-ORI_WIN/2 )
582 temp_mod = sumx*sumx + sumy*sumy;
583 if( temp_mod > descriptor_mod )
585 descriptor_mod = temp_mod;
591 float descriptor_dir = cvFastArctan( besty, bestx );
592 kp->dir = descriptor_dir;
597 descriptor_dir *= (float)(CV_PI/180);
599 /* Extract a window of pixels around the keypoint of size 20s */
600 int win_size = (int)((PATCH_SZ+1)*s);
601 if( win_bufs[thread_idx] == 0 || win_bufs[thread_idx]->cols < win_size*win_size )
603 cvReleaseMat( &win_bufs[thread_idx] );
604 win_bufs[thread_idx] = cvCreateMat( 1, win_size*win_size, CV_8U );
607 CvMat win = cvMat(win_size, win_size, CV_8U, win_bufs[thread_idx]->data.ptr);
608 float sin_dir = sin(descriptor_dir);
609 float cos_dir = cos(descriptor_dir) ;
611 /* Subpixel interpolation version (slower). Subpixel not required since
612 the pixels will all get averaged when we scale down to 20 pixels */
614 float w[] = { cos_dir, sin_dir, center.x,
615 -sin_dir, cos_dir , center.y };
616 CvMat W = cvMat(2, 3, CV_32F, w);
617 cvGetQuadrangleSubPix( img, &win, &W );
620 /* Nearest neighbour version (faster) */
621 float win_offset = -(float)(win_size-1)/2;
622 float start_x = center.x + win_offset*cos_dir + win_offset*sin_dir;
623 float start_y = center.y - win_offset*sin_dir + win_offset*cos_dir;
624 uchar* WIN = win.data.ptr;
625 for( i=0; i<win_size; i++, start_x+=sin_dir, start_y+=cos_dir )
627 float pixel_x = start_x;
628 float pixel_y = start_y;
629 for( j=0; j<win_size; j++, pixel_x+=cos_dir, pixel_y-=sin_dir )
631 int x = cvRound( pixel_x );
632 int y = cvRound( pixel_y );
635 x = MIN( x, img->cols-1 );
636 y = MIN( y, img->rows-1 );
637 WIN[i*win_size + j] = img->data.ptr[y*img->step+x];
641 /* Scale the window to size PATCH_SZ so each pixel's size is s. This
642 makes calculating the gradients with wavelets of size 2s easy */
643 cvResize( &win, &_patch, CV_INTER_AREA );
645 /* Calculate gradients in x and y with wavelets of size 2s */
646 for( i = 0; i < PATCH_SZ; i++ )
647 for( j = 0; j < PATCH_SZ; j++ )
650 float vx = (PATCH[i][j+1] - PATCH[i][j] + PATCH[i+1][j+1] - PATCH[i+1][j])*dw;
651 float vy = (PATCH[i+1][j] - PATCH[i][j] + PATCH[i+1][j+1] - PATCH[i][j+1])*dw;
656 /* Construct the descriptor */
657 vec = (float*)cvGetSeqElem( descriptors, k );
658 for( kk = 0; kk < (int)(descriptors->elem_size/sizeof(vec[0])); kk++ )
660 double square_mag = 0;
661 if( params.extended )
663 /* 128-bin descriptor */
664 for( i = 0; i < 4; i++ )
665 for( j = 0; j < 4; j++ )
667 for( y = i*5; y < i*5+5; y++ )
669 for( x = j*5; x < j*5+5; x++ )
671 float tx = DX[y][x], ty = DY[y][x];
675 vec[1] += (float)fabs(tx);
678 vec[3] += (float)fabs(tx);
683 vec[5] += (float)fabs(ty);
686 vec[7] += (float)fabs(ty);
690 for( kk = 0; kk < 8; kk++ )
691 square_mag += vec[kk]*vec[kk];
697 /* 64-bin descriptor */
698 for( i = 0; i < 4; i++ )
699 for( j = 0; j < 4; j++ )
701 for( y = i*5; y < i*5+5; y++ )
703 for( x = j*5; x < j*5+5; x++ )
705 float tx = DX[y][x], ty = DY[y][x];
706 vec[0] += tx; vec[1] += ty;
707 vec[2] += (float)fabs(tx); vec[3] += (float)fabs(ty);
710 for( kk = 0; kk < 4; kk++ )
711 square_mag += vec[kk]*vec[kk];
716 /* unit vector is essential for contrast invariance */
717 vec = (float*)cvGetSeqElem( descriptors, k );
718 double scale = 1./(sqrt(square_mag) + DBL_EPSILON);
719 for( kk = 0; kk < descriptor_size; kk++ )
720 vec[kk] = (float)(vec[kk]*scale);
723 /* remove keypoints that were marked for deletion */
724 for ( i = 0; i < N; i++ )
726 CvSURFPoint* kp = (CvSURFPoint*)cvGetSeqElem( keypoints, i );
727 if ( kp->size == -1 )
729 cvSeqRemove( keypoints, i );
731 cvSeqRemove( descriptors, i );
737 for( i = 0; i < nthreads; i++ )
738 cvReleaseMat( &win_bufs[i] );
741 *_keypoints = keypoints;
743 *_descriptors = descriptors;
745 cvReleaseMat( &sum );
746 cvReleaseMat( &mask1 );
747 cvReleaseMat( &mask_sum );