Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/imgproc/test/test_goodfeaturetotrack.cpp
16339 views
1
/*M///////////////////////////////////////////////////////////////////////////////////////
2
//
3
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4
//
5
// By downloading, copying, installing or using the software you agree to this license.
6
// If you do not agree to this license, do not download, install,
7
// copy or use the software.
8
//
9
//
10
// Intel License Agreement
11
// For Open Source Computer Vision Library
12
//
13
// Copyright (C) 2000, Intel Corporation, all rights reserved.
14
// Third party copyrights are property of their respective owners.
15
//
16
// Redistribution and use in source and binary forms, with or without modification,
17
// are permitted provided that the following conditions are met:
18
//
19
// * Redistribution's of source code must retain the above copyright notice,
20
// this list of conditions and the following disclaimer.
21
//
22
// * Redistribution's in binary form must reproduce the above copyright notice,
23
// this list of conditions and the following disclaimer in the documentation
24
// and/or other materials provided with the distribution.
25
//
26
// * The name of Intel Corporation may not be used to endorse or promote products
27
// derived from this software without specific prior written permission.
28
//
29
// This software is provided by the copyright holders and contributors "as is" and
30
// any express or implied warranties, including, but not limited to, the implied
31
// warranties of merchantability and fitness for a particular purpose are disclaimed.
32
// In no event shall the Intel Corporation or contributors be liable for any direct,
33
// indirect, incidental, special, exemplary, or consequential damages
34
// (including, but not limited to, procurement of substitute goods or services;
35
// loss of use, data, or profits; or business interruption) however caused
36
// and on any theory of liability, whether in contract, strict liability,
37
// or tort (including negligence or otherwise) arising in any way out of
38
// the use of this software, even if advised of the possibility of such damage.
39
//
40
//M*/
41
42
#include "test_precomp.hpp"
43
44
namespace opencv_test { namespace {
45
46
enum { MINEIGENVAL=0, HARRIS=1, EIGENVALSVECS=2 };
47
48
49
#if 0 //set 1 to switch ON debug message
50
#define TEST_MESSAGE( message ) std::cout << message;
51
#define TEST_MESSAGEL( message, val) std::cout << message << val << std::endl;
52
#else
53
#define TEST_MESSAGE( message )
54
#define TEST_MESSAGEL( message, val)
55
#endif
56
57
/////////////////////ref//////////////////////
58
59
struct greaterThanPtr
60
{
61
bool operator () (const float * a, const float * b) const
62
{ return *a > *b; }
63
};
64
65
static void
66
test_cornerEigenValsVecs( const Mat& src, Mat& eigenv, int block_size,
67
int _aperture_size, double k, int mode, int borderType, const Scalar& _borderValue )
68
{
69
int i, j;
70
Scalar borderValue = _borderValue;
71
int aperture_size = _aperture_size < 0 ? 3 : _aperture_size;
72
Point anchor( aperture_size/2, aperture_size/2 );
73
74
CV_Assert( src.type() == CV_8UC1 || src.type() == CV_32FC1 );
75
CV_Assert( eigenv.type() == CV_32FC1 );
76
CV_Assert( ( src.rows == eigenv.rows ) &&
77
(((mode == MINEIGENVAL)||(mode == HARRIS)) && (src.cols == eigenv.cols)) );
78
79
int type = src.type();
80
int ftype = CV_32FC1;
81
double kernel_scale = 1;
82
83
Mat dx2, dy2, dxdy(src.size(), CV_32F), kernel;
84
85
kernel = cvtest::calcSobelKernel2D( 1, 0, _aperture_size );
86
cvtest::filter2D( src, dx2, ftype, kernel*kernel_scale, anchor, 0, borderType, borderValue );
87
kernel = cvtest::calcSobelKernel2D( 0, 1, _aperture_size );
88
cvtest::filter2D( src, dy2, ftype, kernel*kernel_scale, anchor, 0, borderType,borderValue );
89
90
double denom = (1 << (aperture_size-1))*block_size;
91
denom = denom * denom;
92
93
if( _aperture_size < 0 )
94
denom *= 4;
95
if(type != ftype )
96
denom *= 255.;
97
98
denom = 1./denom;
99
100
for( i = 0; i < src.rows; i++ )
101
{
102
float* dxdyp = dxdy.ptr<float>(i);
103
float* dx2p = dx2.ptr<float>(i);
104
float* dy2p = dy2.ptr<float>(i);
105
106
for( j = 0; j < src.cols; j++ )
107
{
108
double xval = dx2p[j], yval = dy2p[j];
109
dxdyp[j] = (float)(xval*yval*denom);
110
dx2p[j] = (float)(xval*xval*denom);
111
dy2p[j] = (float)(yval*yval*denom);
112
}
113
}
114
115
kernel = Mat::ones(block_size, block_size, CV_32F);
116
anchor = Point(block_size/2, block_size/2);
117
118
cvtest::filter2D( dx2, dx2, ftype, kernel, anchor, 0, borderType, borderValue );
119
cvtest::filter2D( dy2, dy2, ftype, kernel, anchor, 0, borderType, borderValue );
120
cvtest::filter2D( dxdy, dxdy, ftype, kernel, anchor, 0, borderType, borderValue );
121
122
if( mode == MINEIGENVAL )
123
{
124
for( i = 0; i < src.rows; i++ )
125
{
126
float* eigenvp = eigenv.ptr<float>(i);
127
const float* dxdyp = dxdy.ptr<float>(i);
128
const float* dx2p = dx2.ptr<float>(i);
129
const float* dy2p = dy2.ptr<float>(i);
130
131
for( j = 0; j < src.cols; j++ )
132
{
133
double a = dx2p[j], b = dxdyp[j], c = dy2p[j];
134
double d = sqrt( ( a - c )*( a - c ) + 4*b*b );
135
eigenvp[j] = (float)( 0.5*(a + c - d));
136
}
137
}
138
}
139
else if( mode == HARRIS )
140
{
141
142
for( i = 0; i < src.rows; i++ )
143
{
144
float* eigenvp = eigenv.ptr<float>(i);
145
const float* dxdyp = dxdy.ptr<float>(i);
146
const float* dx2p = dx2.ptr<float>(i);
147
const float* dy2p = dy2.ptr<float>(i);
148
149
for( j = 0; j < src.cols; j++ )
150
{
151
double a = dx2p[j], b = dxdyp[j], c = dy2p[j];
152
eigenvp[j] = (float)(a*c - b*b - k*(a + c)*(a + c));
153
}
154
}
155
}
156
}
157
158
159
static void
160
test_goodFeaturesToTrack( InputArray _image, OutputArray _corners,
161
int maxCorners, double qualityLevel, double minDistance,
162
InputArray _mask, int blockSize, int gradientSize,
163
bool useHarrisDetector, double harrisK )
164
{
165
166
CV_Assert( qualityLevel > 0 && minDistance >= 0 && maxCorners >= 0 );
167
CV_Assert( _mask.empty() || (_mask.type() == CV_8UC1 && _mask.sameSize(_image)) );
168
169
170
Mat image = _image.getMat(), mask = _mask.getMat();
171
int aperture_size = gradientSize;
172
int borderType = BORDER_DEFAULT;
173
174
Mat eig, tmp, tt;
175
176
eig.create( image.size(), CV_32F );
177
178
if( useHarrisDetector )
179
test_cornerEigenValsVecs( image, eig, blockSize, aperture_size, harrisK, HARRIS, borderType, 0 );
180
else
181
test_cornerEigenValsVecs( image, eig, blockSize, aperture_size, 0, MINEIGENVAL, borderType, 0 );
182
183
double maxVal = 0;
184
185
cvtest::minMaxIdx( eig, 0, &maxVal, 0, 0, mask );
186
cvtest::threshold( eig, eig, (float)(maxVal*qualityLevel), 0.f,THRESH_TOZERO );
187
cvtest::dilate( eig, tmp, Mat(),Point(-1,-1),borderType,0);
188
189
Size imgsize = image.size();
190
191
vector<const float*> tmpCorners;
192
193
// collect list of pointers to features - put them into temporary image
194
for( int y = 1; y < imgsize.height - 1; y++ )
195
{
196
const float* eig_data = (const float*)eig.ptr(y);
197
const float* tmp_data = (const float*)tmp.ptr(y);
198
const uchar* mask_data = mask.data ? mask.ptr(y) : 0;
199
200
for( int x = 1; x < imgsize.width - 1; x++ )
201
{
202
float val = eig_data[x];
203
if( val != 0 && val == tmp_data[x] && (!mask_data || mask_data[x]) )
204
{
205
tmpCorners.push_back(eig_data + x);
206
}
207
}
208
}
209
210
vector<Point2f> corners;
211
size_t i, j, total = tmpCorners.size(), ncorners = 0;
212
213
std::sort( tmpCorners.begin(), tmpCorners.end(), greaterThanPtr() );
214
215
if(minDistance >= 1)
216
{
217
// Partition the image into larger grids
218
int w = image.cols;
219
int h = image.rows;
220
221
const int cell_size = cvRound(minDistance);
222
const int grid_width = (w + cell_size - 1) / cell_size;
223
const int grid_height = (h + cell_size - 1) / cell_size;
224
225
std::vector<std::vector<Point2f> > grid(grid_width*grid_height);
226
227
minDistance *= minDistance;
228
229
for( i = 0; i < total; i++ )
230
{
231
int ofs = (int)((const uchar*)tmpCorners[i] - eig.data);
232
int y = (int)(ofs / eig.step);
233
int x = (int)((ofs - y*eig.step)/sizeof(float));
234
235
bool good = true;
236
237
int x_cell = x / cell_size;
238
int y_cell = y / cell_size;
239
240
int x1 = x_cell - 1;
241
int y1 = y_cell - 1;
242
int x2 = x_cell + 1;
243
int y2 = y_cell + 1;
244
245
// boundary check
246
x1 = std::max(0, x1);
247
y1 = std::max(0, y1);
248
x2 = std::min(grid_width-1, x2);
249
y2 = std::min(grid_height-1, y2);
250
251
for( int yy = y1; yy <= y2; yy++ )
252
{
253
for( int xx = x1; xx <= x2; xx++ )
254
{
255
vector <Point2f> &m = grid[yy*grid_width + xx];
256
257
if( m.size() )
258
{
259
for(j = 0; j < m.size(); j++)
260
{
261
float dx = x - m[j].x;
262
float dy = y - m[j].y;
263
264
if( dx*dx + dy*dy < minDistance )
265
{
266
good = false;
267
goto break_out;
268
}
269
}
270
}
271
}
272
}
273
274
break_out:
275
276
if(good)
277
{
278
grid[y_cell*grid_width + x_cell].push_back(Point2f((float)x, (float)y));
279
280
corners.push_back(Point2f((float)x, (float)y));
281
++ncorners;
282
283
if( maxCorners > 0 && (int)ncorners == maxCorners )
284
break;
285
}
286
}
287
}
288
else
289
{
290
for( i = 0; i < total; i++ )
291
{
292
int ofs = (int)((const uchar*)tmpCorners[i] - eig.data);
293
int y = (int)(ofs / eig.step);
294
int x = (int)((ofs - y*eig.step)/sizeof(float));
295
296
corners.push_back(Point2f((float)x, (float)y));
297
++ncorners;
298
if( maxCorners > 0 && (int)ncorners == maxCorners )
299
break;
300
}
301
}
302
303
Mat(corners).convertTo(_corners, _corners.fixedType() ? _corners.type() : CV_32F);
304
305
}
306
307
/////////////////end of ref code//////////////////////////
308
309
310
311
class CV_GoodFeatureToTTest : public cvtest::ArrayTest
312
{
313
public:
314
CV_GoodFeatureToTTest();
315
316
protected:
317
int prepare_test_case( int test_case_idx );
318
void run_func();
319
int validate_test_results( int test_case_idx );
320
321
Mat src, src_gray;
322
Mat src_gray32f, src_gray8U;
323
Mat mask;
324
325
int maxCorners;
326
vector<Point2f> corners;
327
vector<Point2f> Refcorners;
328
double qualityLevel;
329
double minDistance;
330
int blockSize;
331
int gradientSize;
332
bool useHarrisDetector;
333
double k;
334
int SrcType;
335
};
336
337
338
CV_GoodFeatureToTTest::CV_GoodFeatureToTTest()
339
{
340
RNG& rng = ts->get_rng();
341
maxCorners = rng.uniform( 50, 100 );
342
qualityLevel = 0.01;
343
minDistance = 10;
344
blockSize = 3;
345
gradientSize = 3;
346
useHarrisDetector = false;
347
k = 0.04;
348
mask = Mat();
349
test_case_count = 4;
350
SrcType = 0;
351
}
352
353
int CV_GoodFeatureToTTest::prepare_test_case( int test_case_idx )
354
{
355
const static int types[] = { CV_32FC1, CV_8UC1 };
356
357
cvtest::TS& tst = *cvtest::TS::ptr();
358
src = imread(string(tst.get_data_path()) + "shared/fruits.png", IMREAD_COLOR);
359
360
CV_Assert(src.data != NULL);
361
362
cvtColor( src, src_gray, CV_BGR2GRAY );
363
SrcType = types[test_case_idx & 0x1];
364
useHarrisDetector = test_case_idx & 2 ? true : false;
365
return 1;
366
}
367
368
369
void CV_GoodFeatureToTTest::run_func()
370
{
371
int cn = src_gray.channels();
372
373
CV_Assert( cn == 1 );
374
CV_Assert( ( CV_MAT_DEPTH(SrcType) == CV_32FC1 ) || ( CV_MAT_DEPTH(SrcType) == CV_8UC1 ));
375
376
TEST_MESSAGEL (" maxCorners = ", maxCorners)
377
if (useHarrisDetector)
378
{
379
TEST_MESSAGE (" useHarrisDetector = true\n");
380
}
381
else
382
{
383
TEST_MESSAGE (" useHarrisDetector = false\n");
384
}
385
386
if( CV_MAT_DEPTH(SrcType) == CV_32FC1)
387
{
388
if (src_gray.depth() != CV_32FC1 ) src_gray.convertTo(src_gray32f, CV_32FC1);
389
else src_gray32f = src_gray.clone();
390
391
TEST_MESSAGE ("goodFeaturesToTrack 32f\n")
392
393
goodFeaturesToTrack( src_gray32f,
394
corners,
395
maxCorners,
396
qualityLevel,
397
minDistance,
398
Mat(),
399
blockSize,
400
gradientSize,
401
useHarrisDetector,
402
k );
403
}
404
else
405
{
406
if (src_gray.depth() != CV_8UC1 ) src_gray.convertTo(src_gray8U, CV_8UC1);
407
else src_gray8U = src_gray.clone();
408
409
TEST_MESSAGE ("goodFeaturesToTrack 8U\n")
410
411
goodFeaturesToTrack( src_gray8U,
412
corners,
413
maxCorners,
414
qualityLevel,
415
minDistance,
416
Mat(),
417
blockSize,
418
gradientSize,
419
useHarrisDetector,
420
k );
421
}
422
}
423
424
425
int CV_GoodFeatureToTTest::validate_test_results( int test_case_idx )
426
{
427
static const double eps = 2e-6;
428
429
if( CV_MAT_DEPTH(SrcType) == CV_32FC1 )
430
{
431
if (src_gray.depth() != CV_32FC1 ) src_gray.convertTo(src_gray32f, CV_32FC1);
432
else src_gray32f = src_gray.clone();
433
434
TEST_MESSAGE ("test_goodFeaturesToTrack 32f\n")
435
436
test_goodFeaturesToTrack( src_gray32f,
437
Refcorners,
438
maxCorners,
439
qualityLevel,
440
minDistance,
441
Mat(),
442
blockSize,
443
gradientSize,
444
useHarrisDetector,
445
k );
446
}
447
else
448
{
449
if (src_gray.depth() != CV_8UC1 ) src_gray.convertTo(src_gray8U, CV_8UC1);
450
else src_gray8U = src_gray.clone();
451
452
TEST_MESSAGE ("test_goodFeaturesToTrack 8U\n")
453
454
test_goodFeaturesToTrack( src_gray8U,
455
Refcorners,
456
maxCorners,
457
qualityLevel,
458
minDistance,
459
Mat(),
460
blockSize,
461
gradientSize,
462
useHarrisDetector,
463
k );
464
}
465
466
double e = cv::norm(corners, Refcorners); // TODO cvtest
467
468
if (e > eps)
469
{
470
TEST_MESSAGEL ("Number of features: Refcorners = ", Refcorners.size())
471
TEST_MESSAGEL (" TestCorners = ", corners.size())
472
TEST_MESSAGE ("\n")
473
474
ts->printf(cvtest::TS::CONSOLE, "actual error: %g, expected: %g", e, eps);
475
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
476
477
for(int i = 0; i < (int)std::min((unsigned int)(corners.size()), (unsigned int)(Refcorners.size())); i++){
478
if ( (corners[i].x != Refcorners[i].x) || (corners[i].y != Refcorners[i].y))
479
printf("i = %i X %2.2f Xref %2.2f Y %2.2f Yref %2.2f\n",i,corners[i].x,Refcorners[i].x,corners[i].y,Refcorners[i].y);
480
}
481
}
482
else
483
{
484
TEST_MESSAGEL (" Refcorners = ", Refcorners.size())
485
TEST_MESSAGEL (" TestCorners = ", corners.size())
486
TEST_MESSAGE ("\n")
487
488
ts->set_failed_test_info(cvtest::TS::OK);
489
}
490
491
return BaseTest::validate_test_results(test_case_idx);
492
493
}
494
495
TEST(Imgproc_GoodFeatureToT, accuracy) { CV_GoodFeatureToTTest test; test.safe_run(); }
496
497
498
}} // namespace
499
/* End of file. */
500
501