1
/*M///////////////////////////////////////////////////////////////////////////////////////
3
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
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.
10
// Intel License Agreement
11
// For Open Source Computer Vision Library
13
// Copyright (C) 2000, Intel Corporation, all rights reserved.
14
// Third party copyrights are property of their respective owners.
16
// Redistribution and use in source and binary forms, with or without modification,
17
// are permitted provided that the following conditions are met:
19
// * Redistribution's of source code must retain the above copyright notice,
20
// this list of conditions and the following disclaimer.
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.
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.
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.
42
#include "test_precomp.hpp"
47
const string IMAGE_TSUKUBA = "/features2d/tsukuba.png";
48
const string IMAGE_BIKES = "/detectors_descriptors_evaluation/images_datasets/bikes/img1.png";
50
#define SHOW_DEBUG_LOG 0
53
Mat generateHomography(float angle)
55
// angle - rotation around Oz in degrees
56
float angleRadian = static_cast<float>(angle * CV_PI / 180);
57
Mat H = Mat::eye(3, 3, CV_32FC1);
58
H.at<float>(0,0) = H.at<float>(1,1) = std::cos(angleRadian);
59
H.at<float>(0,1) = -std::sin(angleRadian);
60
H.at<float>(1,0) = std::sin(angleRadian);
66
Mat rotateImage(const Mat& srcImage, float angle, Mat& dstImage, Mat& dstMask)
68
// angle - rotation around Oz in degrees
69
float diag = std::sqrt(static_cast<float>(srcImage.cols * srcImage.cols + srcImage.rows * srcImage.rows));
70
Mat LUShift = Mat::eye(3, 3, CV_32FC1); // left up
71
LUShift.at<float>(0,2) = static_cast<float>(-srcImage.cols/2);
72
LUShift.at<float>(1,2) = static_cast<float>(-srcImage.rows/2);
73
Mat RDShift = Mat::eye(3, 3, CV_32FC1); // right down
74
RDShift.at<float>(0,2) = diag/2;
75
RDShift.at<float>(1,2) = diag/2;
76
Size sz(cvRound(diag), cvRound(diag));
78
Mat srcMask(srcImage.size(), CV_8UC1, Scalar(255));
80
Mat H = RDShift * generateHomography(angle) * LUShift;
81
warpPerspective(srcImage, dstImage, H, sz);
82
warpPerspective(srcMask, dstMask, H, sz);
87
void rotateKeyPoints(const vector<KeyPoint>& src, const Mat& H, float angle, vector<KeyPoint>& dst)
89
// suppose that H is rotation given from rotateImage() and angle has value passed to rotateImage()
90
vector<Point2f> srcCenters, dstCenters;
91
KeyPoint::convert(src, srcCenters);
93
perspectiveTransform(srcCenters, dstCenters, H);
96
for(size_t i = 0; i < dst.size(); i++)
98
dst[i].pt = dstCenters[i];
99
float dstAngle = src[i].angle + angle;
100
if(dstAngle >= 360.f)
102
dst[i].angle = dstAngle;
106
void scaleKeyPoints(const vector<KeyPoint>& src, vector<KeyPoint>& dst, float scale)
108
dst.resize(src.size());
109
for(size_t i = 0; i < src.size(); i++)
110
dst[i] = KeyPoint(src[i].pt.x * scale, src[i].pt.y * scale, src[i].size * scale, src[i].angle);
114
float calcCirclesIntersectArea(const Point2f& p0, float r0, const Point2f& p1, float r1)
116
float c = static_cast<float>(norm(p0 - p1)), sqr_c = c * c;
118
float sqr_r0 = r0 * r0;
119
float sqr_r1 = r1 * r1;
124
float minR = std::min(r0, r1);
125
float maxR = std::max(r0, r1);
127
return static_cast<float>(CV_PI * minR * minR);
129
float cos_halfA0 = (sqr_r0 + sqr_c - sqr_r1) / (2 * r0 * c);
130
float cos_halfA1 = (sqr_r1 + sqr_c - sqr_r0) / (2 * r1 * c);
132
float A0 = 2 * acos(cos_halfA0);
133
float A1 = 2 * acos(cos_halfA1);
135
return 0.5f * sqr_r0 * (A0 - sin(A0)) +
136
0.5f * sqr_r1 * (A1 - sin(A1));
140
float calcIntersectRatio(const Point2f& p0, float r0, const Point2f& p1, float r1)
142
float intersectArea = calcCirclesIntersectArea(p0, r0, p1, r1);
143
float unionArea = static_cast<float>(CV_PI) * (r0 * r0 + r1 * r1) - intersectArea;
144
return intersectArea / unionArea;
148
void matchKeyPoints(const vector<KeyPoint>& keypoints0, const Mat& H,
149
const vector<KeyPoint>& keypoints1,
150
vector<DMatch>& matches)
152
vector<Point2f> points0;
153
KeyPoint::convert(keypoints0, points0);
156
points0t = Mat(points0);
158
perspectiveTransform(Mat(points0), points0t, H);
161
vector<uchar> usedMask(keypoints1.size(), 0);
162
for(int i0 = 0; i0 < static_cast<int>(keypoints0.size()); i0++)
164
int nearestPointIndex = -1;
165
float maxIntersectRatio = 0.f;
166
const float r0 = 0.5f * keypoints0[i0].size;
167
for(size_t i1 = 0; i1 < keypoints1.size(); i1++)
169
if(nearestPointIndex >= 0 && usedMask[i1])
172
float r1 = 0.5f * keypoints1[i1].size;
173
float intersectRatio = calcIntersectRatio(points0t.at<Point2f>(i0), r0,
174
keypoints1[i1].pt, r1);
175
if(intersectRatio > maxIntersectRatio)
177
maxIntersectRatio = intersectRatio;
178
nearestPointIndex = static_cast<int>(i1);
182
matches.push_back(DMatch(i0, nearestPointIndex, maxIntersectRatio));
183
if(nearestPointIndex >= 0)
184
usedMask[nearestPointIndex] = 1;
188
class DetectorRotationInvarianceTest : public cvtest::BaseTest
191
DetectorRotationInvarianceTest(const Ptr<FeatureDetector>& _featureDetector,
192
float _minKeyPointMatchesRatio,
193
float _minAngleInliersRatio) :
194
featureDetector(_featureDetector),
195
minKeyPointMatchesRatio(_minKeyPointMatchesRatio),
196
minAngleInliersRatio(_minAngleInliersRatio)
198
CV_Assert(featureDetector);
205
const string imageFilename = string(ts->get_data_path()) + IMAGE_TSUKUBA;
208
Mat image0 = imread(imageFilename), image1, mask1;
211
ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", imageFilename.c_str());
212
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
216
vector<KeyPoint> keypoints0;
217
featureDetector->detect(image0, keypoints0);
218
if(keypoints0.size() < 15)
219
CV_Error(Error::StsAssert, "Detector gives too few points in a test image\n");
221
const int maxAngle = 360, angleStep = 15;
222
for(int angle = 0; angle < maxAngle; angle += angleStep)
224
Mat H = rotateImage(image0, static_cast<float>(angle), image1, mask1);
226
vector<KeyPoint> keypoints1;
227
featureDetector->detect(image1, keypoints1, mask1);
229
vector<DMatch> matches;
230
matchKeyPoints(keypoints0, H, keypoints1, matches);
232
int angleInliersCount = 0;
234
const float minIntersectRatio = 0.5f;
235
int keyPointMatchesCount = 0;
236
for(size_t m = 0; m < matches.size(); m++)
238
if(matches[m].distance < minIntersectRatio)
241
keyPointMatchesCount++;
243
// Check does this inlier have consistent angles
244
const float maxAngleDiff = 15.f; // grad
245
float angle0 = keypoints0[matches[m].queryIdx].angle;
246
float angle1 = keypoints1[matches[m].trainIdx].angle;
247
if(angle0 == -1 || angle1 == -1)
248
CV_Error(Error::StsBadArg, "Given FeatureDetector is not rotation invariant, it can not be tested here.\n");
249
CV_Assert(angle0 >= 0.f && angle0 < 360.f);
250
CV_Assert(angle1 >= 0.f && angle1 < 360.f);
252
float rotAngle0 = angle0 + angle;
253
if(rotAngle0 >= 360.f)
256
float angleDiff = std::max(rotAngle0, angle1) - std::min(rotAngle0, angle1);
257
angleDiff = std::min(angleDiff, static_cast<float>(360.f - angleDiff));
258
CV_Assert(angleDiff >= 0.f);
259
bool isAngleCorrect = angleDiff < maxAngleDiff;
264
float keyPointMatchesRatio = static_cast<float>(keyPointMatchesCount) / keypoints0.size();
265
if(keyPointMatchesRatio < minKeyPointMatchesRatio)
267
ts->printf(cvtest::TS::LOG, "Incorrect keyPointMatchesRatio: curr = %f, min = %f.\n",
268
keyPointMatchesRatio, minKeyPointMatchesRatio);
269
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
273
if(keyPointMatchesCount)
275
float angleInliersRatio = static_cast<float>(angleInliersCount) / keyPointMatchesCount;
276
if(angleInliersRatio < minAngleInliersRatio)
278
ts->printf(cvtest::TS::LOG, "Incorrect angleInliersRatio: curr = %f, min = %f.\n",
279
angleInliersRatio, minAngleInliersRatio);
280
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
285
std::cout << "keyPointMatchesRatio - " << keyPointMatchesRatio
286
<< " - angleInliersRatio " << static_cast<float>(angleInliersCount) / keyPointMatchesCount << std::endl;
289
ts->set_failed_test_info( cvtest::TS::OK );
292
Ptr<FeatureDetector> featureDetector;
293
float minKeyPointMatchesRatio;
294
float minAngleInliersRatio;
297
class DescriptorRotationInvarianceTest : public cvtest::BaseTest
300
DescriptorRotationInvarianceTest(const Ptr<FeatureDetector>& _featureDetector,
301
const Ptr<DescriptorExtractor>& _descriptorExtractor,
303
float _minDescInliersRatio) :
304
featureDetector(_featureDetector),
305
descriptorExtractor(_descriptorExtractor),
307
minDescInliersRatio(_minDescInliersRatio)
309
CV_Assert(featureDetector);
310
CV_Assert(descriptorExtractor);
317
const string imageFilename = string(ts->get_data_path()) + IMAGE_TSUKUBA;
320
Mat image0 = imread(imageFilename), image1, mask1;
323
ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", imageFilename.c_str());
324
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
328
vector<KeyPoint> keypoints0;
330
featureDetector->detect(image0, keypoints0);
331
if(keypoints0.size() < 15)
332
CV_Error(Error::StsAssert, "Detector gives too few points in a test image\n");
333
descriptorExtractor->compute(image0, keypoints0, descriptors0);
335
BFMatcher bfmatcher(normType);
337
const float minIntersectRatio = 0.5f;
338
const int maxAngle = 360, angleStep = 15;
339
for(int angle = 0; angle < maxAngle; angle += angleStep)
341
Mat H = rotateImage(image0, static_cast<float>(angle), image1, mask1);
343
vector<KeyPoint> keypoints1;
344
rotateKeyPoints(keypoints0, H, static_cast<float>(angle), keypoints1);
346
descriptorExtractor->compute(image1, keypoints1, descriptors1);
348
vector<DMatch> descMatches;
349
bfmatcher.match(descriptors0, descriptors1, descMatches);
351
int descInliersCount = 0;
352
for(size_t m = 0; m < descMatches.size(); m++)
354
const KeyPoint& transformed_p0 = keypoints1[descMatches[m].queryIdx];
355
const KeyPoint& p1 = keypoints1[descMatches[m].trainIdx];
356
if(calcIntersectRatio(transformed_p0.pt, 0.5f * transformed_p0.size,
357
p1.pt, 0.5f * p1.size) >= minIntersectRatio)
363
float descInliersRatio = static_cast<float>(descInliersCount) / keypoints0.size();
364
if(descInliersRatio < minDescInliersRatio)
366
ts->printf(cvtest::TS::LOG, "Incorrect descInliersRatio: curr = %f, min = %f.\n",
367
descInliersRatio, minDescInliersRatio);
368
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
372
std::cout << "descInliersRatio " << static_cast<float>(descInliersCount) / keypoints0.size() << std::endl;
375
ts->set_failed_test_info( cvtest::TS::OK );
378
Ptr<FeatureDetector> featureDetector;
379
Ptr<DescriptorExtractor> descriptorExtractor;
381
float minDescInliersRatio;
384
class DetectorScaleInvarianceTest : public cvtest::BaseTest
387
DetectorScaleInvarianceTest(const Ptr<FeatureDetector>& _featureDetector,
388
float _minKeyPointMatchesRatio,
389
float _minScaleInliersRatio) :
390
featureDetector(_featureDetector),
391
minKeyPointMatchesRatio(_minKeyPointMatchesRatio),
392
minScaleInliersRatio(_minScaleInliersRatio)
394
CV_Assert(featureDetector);
401
const string imageFilename = string(ts->get_data_path()) + IMAGE_BIKES;
404
Mat image0 = imread(imageFilename);
407
ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", imageFilename.c_str());
408
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
412
vector<KeyPoint> keypoints0;
413
featureDetector->detect(image0, keypoints0);
414
if(keypoints0.size() < 15)
415
CV_Error(Error::StsAssert, "Detector gives too few points in a test image\n");
417
for(int scaleIdx = 1; scaleIdx <= 3; scaleIdx++)
419
float scale = 1.f + scaleIdx * 0.5f;
421
resize(image0, image1, Size(), 1./scale, 1./scale);
423
vector<KeyPoint> keypoints1, osiKeypoints1; // osi - original size image
424
featureDetector->detect(image1, keypoints1);
425
if(keypoints1.size() < 15)
426
CV_Error(Error::StsAssert, "Detector gives too few points in a test image\n");
428
if(keypoints1.size() > keypoints0.size())
430
ts->printf(cvtest::TS::LOG, "Strange behavior of the detector. "
431
"It gives more points count in an image of the smaller size.\n"
432
"original size (%d, %d), keypoints count = %d\n"
433
"reduced size (%d, %d), keypoints count = %d\n",
434
image0.cols, image0.rows, keypoints0.size(),
435
image1.cols, image1.rows, keypoints1.size());
436
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
440
scaleKeyPoints(keypoints1, osiKeypoints1, scale);
442
vector<DMatch> matches;
443
// image1 is query image (it's reduced image0)
444
// image0 is train image
445
matchKeyPoints(osiKeypoints1, Mat(), keypoints0, matches);
447
const float minIntersectRatio = 0.5f;
448
int keyPointMatchesCount = 0;
449
int scaleInliersCount = 0;
451
for(size_t m = 0; m < matches.size(); m++)
453
if(matches[m].distance < minIntersectRatio)
456
keyPointMatchesCount++;
458
// Check does this inlier have consistent sizes
459
const float maxSizeDiff = 0.8f;//0.9f; // grad
460
float size0 = keypoints0[matches[m].trainIdx].size;
461
float size1 = osiKeypoints1[matches[m].queryIdx].size;
462
CV_Assert(size0 > 0 && size1 > 0);
463
if(std::min(size0, size1) > maxSizeDiff * std::max(size0, size1))
467
float keyPointMatchesRatio = static_cast<float>(keyPointMatchesCount) / keypoints1.size();
468
if(keyPointMatchesRatio < minKeyPointMatchesRatio)
470
ts->printf(cvtest::TS::LOG, "Incorrect keyPointMatchesRatio: curr = %f, min = %f.\n",
471
keyPointMatchesRatio, minKeyPointMatchesRatio);
472
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
476
if(keyPointMatchesCount)
478
float scaleInliersRatio = static_cast<float>(scaleInliersCount) / keyPointMatchesCount;
479
if(scaleInliersRatio < minScaleInliersRatio)
481
ts->printf(cvtest::TS::LOG, "Incorrect scaleInliersRatio: curr = %f, min = %f.\n",
482
scaleInliersRatio, minScaleInliersRatio);
483
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
488
std::cout << "keyPointMatchesRatio - " << keyPointMatchesRatio
489
<< " - scaleInliersRatio " << static_cast<float>(scaleInliersCount) / keyPointMatchesCount << std::endl;
492
ts->set_failed_test_info( cvtest::TS::OK );
495
Ptr<FeatureDetector> featureDetector;
496
float minKeyPointMatchesRatio;
497
float minScaleInliersRatio;
500
class DescriptorScaleInvarianceTest : public cvtest::BaseTest
503
DescriptorScaleInvarianceTest(const Ptr<FeatureDetector>& _featureDetector,
504
const Ptr<DescriptorExtractor>& _descriptorExtractor,
506
float _minDescInliersRatio) :
507
featureDetector(_featureDetector),
508
descriptorExtractor(_descriptorExtractor),
510
minDescInliersRatio(_minDescInliersRatio)
512
CV_Assert(featureDetector);
513
CV_Assert(descriptorExtractor);
520
const string imageFilename = string(ts->get_data_path()) + IMAGE_BIKES;
523
Mat image0 = imread(imageFilename);
526
ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", imageFilename.c_str());
527
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
531
vector<KeyPoint> keypoints0;
532
featureDetector->detect(image0, keypoints0);
533
if(keypoints0.size() < 15)
534
CV_Error(Error::StsAssert, "Detector gives too few points in a test image\n");
536
descriptorExtractor->compute(image0, keypoints0, descriptors0);
538
BFMatcher bfmatcher(normType);
539
for(int scaleIdx = 1; scaleIdx <= 3; scaleIdx++)
541
float scale = 1.f + scaleIdx * 0.5f;
544
resize(image0, image1, Size(), 1./scale, 1./scale);
546
vector<KeyPoint> keypoints1;
547
scaleKeyPoints(keypoints0, keypoints1, 1.0f/scale);
549
descriptorExtractor->compute(image1, keypoints1, descriptors1);
551
vector<DMatch> descMatches;
552
bfmatcher.match(descriptors0, descriptors1, descMatches);
554
const float minIntersectRatio = 0.5f;
555
int descInliersCount = 0;
556
for(size_t m = 0; m < descMatches.size(); m++)
558
const KeyPoint& transformed_p0 = keypoints0[descMatches[m].queryIdx];
559
const KeyPoint& p1 = keypoints0[descMatches[m].trainIdx];
560
if(calcIntersectRatio(transformed_p0.pt, 0.5f * transformed_p0.size,
561
p1.pt, 0.5f * p1.size) >= minIntersectRatio)
567
float descInliersRatio = static_cast<float>(descInliersCount) / keypoints0.size();
568
if(descInliersRatio < minDescInliersRatio)
570
ts->printf(cvtest::TS::LOG, "Incorrect descInliersRatio: curr = %f, min = %f.\n",
571
descInliersRatio, minDescInliersRatio);
572
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
576
std::cout << "descInliersRatio " << static_cast<float>(descInliersCount) / keypoints0.size() << std::endl;
579
ts->set_failed_test_info( cvtest::TS::OK );
582
Ptr<FeatureDetector> featureDetector;
583
Ptr<DescriptorExtractor> descriptorExtractor;
585
float minKeyPointMatchesRatio;
586
float minDescInliersRatio;
589
// Tests registration
592
* Detector's rotation invariance check
595
TEST(Features2d_RotationInvariance_Detector_BRISK, regression)
597
DetectorRotationInvarianceTest test(BRISK::create(),
603
TEST(Features2d_RotationInvariance_Detector_ORB, regression)
605
DetectorRotationInvarianceTest test(ORB::create(),
612
* Descriptors's rotation invariance check
615
TEST(Features2d_RotationInvariance_Descriptor_BRISK, regression)
617
Ptr<Feature2D> f2d = BRISK::create();
618
DescriptorRotationInvarianceTest test(f2d, f2d, f2d->defaultNorm(), 0.99f);
622
TEST(Features2d_RotationInvariance_Descriptor_ORB, regression)
624
Ptr<Feature2D> f2d = ORB::create();
625
DescriptorRotationInvarianceTest test(f2d, f2d, f2d->defaultNorm(), 0.99f);
629
//TEST(Features2d_RotationInvariance_Descriptor_FREAK, regression)
631
// DescriptorRotationInvarianceTest test(Algorithm::create<FeatureDetector>("Feature2D.ORB"),
632
// Algorithm::create<DescriptorExtractor>("Feature2D.FREAK"),
633
// Algorithm::create<DescriptorExtractor>("Feature2D.FREAK")->defaultNorm(),
639
* Detector's scale invariance check
642
TEST(Features2d_ScaleInvariance_Detector_BRISK, regression)
644
DetectorScaleInvarianceTest test(BRISK::create(), 0.08f, 0.49f);
648
TEST(Features2d_ScaleInvariance_Detector_KAZE, regression)
650
DetectorScaleInvarianceTest test(KAZE::create(), 0.08f, 0.49f);
654
TEST(Features2d_ScaleInvariance_Detector_AKAZE, regression)
656
DetectorScaleInvarianceTest test(AKAZE::create(), 0.08f, 0.49f);
660
//TEST(Features2d_ScaleInvariance_Detector_ORB, regression)
662
// DetectorScaleInvarianceTest test(Algorithm::create<FeatureDetector>("Feature2D.ORB"),
669
* Descriptor's scale invariance check
672
//TEST(Features2d_ScaleInvariance_Descriptor_BRISK, regression)
674
// DescriptorScaleInvarianceTest test(Algorithm::create<FeatureDetector>("Feature2D.BRISK"),
675
// Algorithm::create<DescriptorExtractor>("Feature2D.BRISK"),
676
// Algorithm::create<DescriptorExtractor>("Feature2D.BRISK")->defaultNorm(),
681
//TEST(Features2d_ScaleInvariance_Descriptor_ORB, regression)
683
// DescriptorScaleInvarianceTest test(Algorithm::create<FeatureDetector>("Feature2D.ORB"),
684
// Algorithm::create<DescriptorExtractor>("Feature2D.ORB"),
685
// Algorithm::create<DescriptorExtractor>("Feature2D.ORB")->defaultNorm(),
690
//TEST(Features2d_ScaleInvariance_Descriptor_FREAK, regression)
692
// DescriptorScaleInvarianceTest test(Algorithm::create<FeatureDetector>("Feature2D.ORB"),
693
// Algorithm::create<DescriptorExtractor>("Feature2D.FREAK"),
694
// Algorithm::create<DescriptorExtractor>("Feature2D.FREAK")->defaultNorm(),