|
|
|
@ -53,7 +53,7 @@ const string IMAGE_BIKES = "/detectors_descriptors_evaluation/images_datasets/bi |
|
|
|
|
static |
|
|
|
|
Mat generateHomography(float angle) |
|
|
|
|
{ |
|
|
|
|
// angle - rotation around Oz in degrees
|
|
|
|
|
// angle - rotation around Oz in degrees
|
|
|
|
|
float angleRadian = angle * CV_PI / 180.; |
|
|
|
|
Mat H = Mat::eye(3, 3, CV_32FC1); |
|
|
|
|
H.at<float>(0,0) = H.at<float>(1,1) = std::cos(angleRadian); |
|
|
|
@ -66,7 +66,7 @@ Mat generateHomography(float angle) |
|
|
|
|
static |
|
|
|
|
Mat rotateImage(const Mat& srcImage, float angle, Mat& dstImage, Mat& dstMask) |
|
|
|
|
{ |
|
|
|
|
// angle - rotation around Oz in degrees
|
|
|
|
|
// angle - rotation around Oz in degrees
|
|
|
|
|
float diag = std::sqrt(static_cast<float>(srcImage.cols * srcImage.cols + srcImage.rows * srcImage.rows)); |
|
|
|
|
Mat LUShift = Mat::eye(3, 3, CV_32FC1); // left up
|
|
|
|
|
LUShift.at<float>(0,2) = -srcImage.cols/2; |
|
|
|
@ -85,6 +85,32 @@ Mat rotateImage(const Mat& srcImage, float angle, Mat& dstImage, Mat& dstMask) |
|
|
|
|
return H; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void rotateKeyPoints(const vector<KeyPoint>& src, const Mat& H, float angle, vector<KeyPoint>& dst) |
|
|
|
|
{ |
|
|
|
|
// suppose that H is rotation given from rotateImage() and angle has value passed to rotateImage()
|
|
|
|
|
vector<Point2f> srcCenters, dstCenters; |
|
|
|
|
KeyPoint::convert(src, srcCenters); |
|
|
|
|
|
|
|
|
|
perspectiveTransform(srcCenters, dstCenters, H); |
|
|
|
|
|
|
|
|
|
dst = src; |
|
|
|
|
for(size_t i = 0; i < dst.size(); i++) |
|
|
|
|
{ |
|
|
|
|
dst[i].pt = dstCenters[i]; |
|
|
|
|
float dstAngle = src[i].angle + angle; |
|
|
|
|
if(dstAngle >= 360.f) |
|
|
|
|
dstAngle -= 360.f; |
|
|
|
|
dst[i].angle = dstAngle; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void scaleKeyPoints(const vector<KeyPoint>& src, vector<KeyPoint>& dst, float scale) |
|
|
|
|
{ |
|
|
|
|
dst.resize(src.size()); |
|
|
|
|
for(size_t i = 0; i < src.size(); i++) |
|
|
|
|
dst[i] = KeyPoint(src[i].pt.x * scale, src[i].pt.y * scale, src[i].size * scale); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static |
|
|
|
|
float calcCirclesIntersectArea(const Point2f& p0, float r0, const Point2f& p1, float r1) |
|
|
|
|
{ |
|
|
|
@ -119,45 +145,45 @@ float calcIntersectRatio(const Point2f& p0, float r0, const Point2f& p1, float r |
|
|
|
|
return intersectArea / unionArea; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static
|
|
|
|
|
static |
|
|
|
|
void matchKeyPoints(const vector<KeyPoint>& keypoints0, const Mat& H, |
|
|
|
|
const vector<KeyPoint>& keypoints1, |
|
|
|
|
vector<DMatch>& matches) |
|
|
|
|
const vector<KeyPoint>& keypoints1, |
|
|
|
|
vector<DMatch>& matches) |
|
|
|
|
{ |
|
|
|
|
vector<Point2f> points0; |
|
|
|
|
vector<Point2f> points0; |
|
|
|
|
KeyPoint::convert(keypoints0, points0); |
|
|
|
|
Mat points0t; |
|
|
|
|
if(H.empty()) |
|
|
|
|
points0t = Mat(points0); |
|
|
|
|
else |
|
|
|
|
perspectiveTransform(Mat(points0), points0t, H); |
|
|
|
|
|
|
|
|
|
matches.clear(); |
|
|
|
|
vector<uchar> usedMask(keypoints1.size(), 0); |
|
|
|
|
for(size_t i0 = 0; i0 < keypoints0.size(); i0++) |
|
|
|
|
{ |
|
|
|
|
int nearestPointIndex = -1; |
|
|
|
|
if(H.empty()) |
|
|
|
|
points0t = Mat(points0); |
|
|
|
|
else |
|
|
|
|
perspectiveTransform(Mat(points0), points0t, H); |
|
|
|
|
|
|
|
|
|
matches.clear(); |
|
|
|
|
vector<uchar> usedMask(keypoints1.size(), 0); |
|
|
|
|
for(size_t i0 = 0; i0 < keypoints0.size(); i0++) |
|
|
|
|
{ |
|
|
|
|
int nearestPointIndex = -1; |
|
|
|
|
float maxIntersectRatio = 0.f; |
|
|
|
|
const float r0 = 0.5f * keypoints0[i0].size; |
|
|
|
|
for(size_t i1 = 0; i1 < keypoints1.size(); i1++) |
|
|
|
|
{ |
|
|
|
|
if(nearestPointIndex >= 0 && usedMask[i1]) |
|
|
|
|
continue; |
|
|
|
|
if(nearestPointIndex >= 0 && usedMask[i1]) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
float r1 = 0.5f * keypoints1[i1].size; |
|
|
|
|
float r1 = 0.5f * keypoints1[i1].size; |
|
|
|
|
float intersectRatio = calcIntersectRatio(points0t.at<Point2f>(i0), r0, |
|
|
|
|
keypoints1[i1].pt, r1); |
|
|
|
|
if(intersectRatio > maxIntersectRatio) |
|
|
|
|
{ |
|
|
|
|
maxIntersectRatio = intersectRatio; |
|
|
|
|
nearestPointIndex = i1; |
|
|
|
|
maxIntersectRatio = intersectRatio; |
|
|
|
|
nearestPointIndex = i1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
matches.push_back(DMatch(i0, nearestPointIndex, maxIntersectRatio)); |
|
|
|
|
if(nearestPointIndex >= 0) |
|
|
|
|
usedMask[nearestPointIndex] = 1; |
|
|
|
|
} |
|
|
|
|
matches.push_back(DMatch(i0, nearestPointIndex, maxIntersectRatio)); |
|
|
|
|
if(nearestPointIndex >= 0) |
|
|
|
|
usedMask[nearestPointIndex] = 1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class DetectorRotationInvarianceTest : public cvtest::BaseTest |
|
|
|
@ -166,9 +192,9 @@ public: |
|
|
|
|
DetectorRotationInvarianceTest(const Ptr<FeatureDetector>& _featureDetector, |
|
|
|
|
float _minKeyPointMatchesRatio, |
|
|
|
|
float _minAngleInliersRatio) : |
|
|
|
|
featureDetector(_featureDetector),
|
|
|
|
|
minKeyPointMatchesRatio(_minKeyPointMatchesRatio),
|
|
|
|
|
minAngleInliersRatio(_minAngleInliersRatio) |
|
|
|
|
featureDetector(_featureDetector), |
|
|
|
|
minKeyPointMatchesRatio(_minKeyPointMatchesRatio), |
|
|
|
|
minAngleInliersRatio(_minAngleInliersRatio) |
|
|
|
|
{ |
|
|
|
|
CV_Assert(!featureDetector.empty()); |
|
|
|
|
} |
|
|
|
@ -177,7 +203,7 @@ protected: |
|
|
|
|
|
|
|
|
|
void run(int) |
|
|
|
|
{ |
|
|
|
|
const string imageFilename = string(ts->get_data_path()) + IMAGE_TSUKUBA; |
|
|
|
|
const string imageFilename = string(ts->get_data_path()) + IMAGE_TSUKUBA; |
|
|
|
|
|
|
|
|
|
// Read test data
|
|
|
|
|
Mat image0 = imread(imageFilename), image1, mask1; |
|
|
|
@ -191,7 +217,7 @@ protected: |
|
|
|
|
vector<KeyPoint> keypoints0; |
|
|
|
|
featureDetector->detect(image0, keypoints0); |
|
|
|
|
if(keypoints0.size() < 15) |
|
|
|
|
CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); |
|
|
|
|
CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); |
|
|
|
|
|
|
|
|
|
const int maxAngle = 360, angleStep = 15; |
|
|
|
|
for(int angle = 0; angle < maxAngle; angle += angleStep) |
|
|
|
@ -201,23 +227,23 @@ protected: |
|
|
|
|
vector<KeyPoint> keypoints1; |
|
|
|
|
featureDetector->detect(image1, keypoints1, mask1); |
|
|
|
|
|
|
|
|
|
vector<DMatch> matches; |
|
|
|
|
matchKeyPoints(keypoints0, H, keypoints1, matches); |
|
|
|
|
vector<DMatch> matches; |
|
|
|
|
matchKeyPoints(keypoints0, H, keypoints1, matches); |
|
|
|
|
|
|
|
|
|
int angleInliersCount = 0; |
|
|
|
|
|
|
|
|
|
const float minIntersectRatio = 0.5f; |
|
|
|
|
int keyPointMatchesCount = 0; |
|
|
|
|
const float minIntersectRatio = 0.5f; |
|
|
|
|
int keyPointMatchesCount = 0; |
|
|
|
|
for(size_t m = 0; m < matches.size(); m++) |
|
|
|
|
{ |
|
|
|
|
if(matches[m].distance < minIntersectRatio) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
keyPointMatchesCount++; |
|
|
|
|
if(matches[m].distance < minIntersectRatio) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
keyPointMatchesCount++; |
|
|
|
|
|
|
|
|
|
// Check does this inlier have consistent angles
|
|
|
|
|
// Check does this inlier have consistent angles
|
|
|
|
|
const float maxAngleDiff = 15.f; // grad
|
|
|
|
|
float angle0 = keypoints0[matches[m].queryIdx].angle; |
|
|
|
|
float angle0 = keypoints0[matches[m].queryIdx].angle; |
|
|
|
|
float angle1 = keypoints1[matches[m].trainIdx].angle; |
|
|
|
|
if(angle0 == -1 || angle1 == -1) |
|
|
|
|
CV_Error(CV_StsBadArg, "Given FeatureDetector is not rotation invariant, it can not be tested here.\n"); |
|
|
|
@ -230,13 +256,13 @@ protected: |
|
|
|
|
|
|
|
|
|
float angleDiff = std::max(rotAngle0, angle1) - std::min(rotAngle0, angle1); |
|
|
|
|
angleDiff = std::min(angleDiff, static_cast<float>(360.f - angleDiff)); |
|
|
|
|
CV_Assert(angleDiff >= 0.f); |
|
|
|
|
CV_Assert(angleDiff >= 0.f); |
|
|
|
|
bool isAngleCorrect = angleDiff < maxAngleDiff; |
|
|
|
|
if(isAngleCorrect) |
|
|
|
|
angleInliersCount++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
float keyPointMatchesRatio = static_cast<float>(keyPointMatchesCount) / keypoints0.size(); |
|
|
|
|
float keyPointMatchesRatio = static_cast<float>(keyPointMatchesCount) / keypoints0.size(); |
|
|
|
|
if(keyPointMatchesRatio < minKeyPointMatchesRatio) |
|
|
|
|
{ |
|
|
|
|
ts->printf(cvtest::TS::LOG, "Incorrect keyPointMatchesRatio: curr = %f, min = %f.\n", |
|
|
|
@ -245,9 +271,9 @@ protected: |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(keyPointMatchesCount) |
|
|
|
|
if(keyPointMatchesCount) |
|
|
|
|
{ |
|
|
|
|
float angleInliersRatio = static_cast<float>(angleInliersCount) / keyPointMatchesCount; |
|
|
|
|
float angleInliersRatio = static_cast<float>(angleInliersCount) / keyPointMatchesCount; |
|
|
|
|
if(angleInliersRatio < minAngleInliersRatio) |
|
|
|
|
{ |
|
|
|
|
ts->printf(cvtest::TS::LOG, "Incorrect angleInliersRatio: curr = %f, min = %f.\n", |
|
|
|
@ -258,7 +284,7 @@ protected: |
|
|
|
|
} |
|
|
|
|
#if SHOW_DEBUG_LOG |
|
|
|
|
std::cout << "keyPointMatchesRatio - " << keyPointMatchesRatio |
|
|
|
|
<< " - angleInliersRatio " << static_cast<float>(angleInliersCount) / keyPointMatchesCount << std::endl; |
|
|
|
|
<< " - angleInliersRatio " << static_cast<float>(angleInliersCount) / keyPointMatchesCount << std::endl; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
ts->set_failed_test_info( cvtest::TS::OK ); |
|
|
|
@ -269,29 +295,20 @@ protected: |
|
|
|
|
float minAngleInliersRatio; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
void scaleKeyPoints(const vector<KeyPoint>& src, vector<KeyPoint>& dst, float scale) |
|
|
|
|
{ |
|
|
|
|
dst.resize(src.size()); |
|
|
|
|
for(size_t i = 0; i < src.size(); i++) |
|
|
|
|
dst[i] = KeyPoint(src[i].pt.x * scale, src[i].pt.y * scale, src[i].size * scale); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class DescriptorRotationInvarianceTest : public cvtest::BaseTest |
|
|
|
|
{ |
|
|
|
|
public: |
|
|
|
|
DescriptorRotationInvarianceTest(const Ptr<FeatureDetector>& _featureDetector, |
|
|
|
|
const Ptr<DescriptorExtractor>& _descriptorExtractor, |
|
|
|
|
int _normType, |
|
|
|
|
float _minKeyPointMatchesRatio, |
|
|
|
|
float _minDescInliersRatio) : |
|
|
|
|
featureDetector(_featureDetector),
|
|
|
|
|
descriptorExtractor(_descriptorExtractor), |
|
|
|
|
normType(_normType), |
|
|
|
|
minKeyPointMatchesRatio(_minKeyPointMatchesRatio), |
|
|
|
|
minDescInliersRatio(_minDescInliersRatio) |
|
|
|
|
const Ptr<DescriptorExtractor>& _descriptorExtractor, |
|
|
|
|
int _normType, |
|
|
|
|
float _minDescInliersRatio) : |
|
|
|
|
featureDetector(_featureDetector), |
|
|
|
|
descriptorExtractor(_descriptorExtractor), |
|
|
|
|
normType(_normType), |
|
|
|
|
minDescInliersRatio(_minDescInliersRatio) |
|
|
|
|
{ |
|
|
|
|
CV_Assert(!featureDetector.empty()); |
|
|
|
|
CV_Assert(!descriptorExtractor.empty()); |
|
|
|
|
CV_Assert(!descriptorExtractor.empty()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected: |
|
|
|
@ -310,80 +327,59 @@ protected: |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
vector<KeyPoint> keypoints0; |
|
|
|
|
Mat descriptors0; |
|
|
|
|
Mat descriptors0; |
|
|
|
|
featureDetector->detect(image0, keypoints0); |
|
|
|
|
if(keypoints0.size() < 15) |
|
|
|
|
CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); |
|
|
|
|
descriptorExtractor->compute(image0, keypoints0, descriptors0); |
|
|
|
|
if(keypoints0.size() < 15) |
|
|
|
|
CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); |
|
|
|
|
descriptorExtractor->compute(image0, keypoints0, descriptors0); |
|
|
|
|
|
|
|
|
|
BFMatcher bfmatcher(normType); |
|
|
|
|
BFMatcher bfmatcher(normType); |
|
|
|
|
|
|
|
|
|
const float minIntersectRatio = 0.5f; |
|
|
|
|
const int maxAngle = 360, angleStep = 15; |
|
|
|
|
for(int angle = 0; angle < maxAngle; angle += angleStep) |
|
|
|
|
{ |
|
|
|
|
Mat H = rotateImage(image0, angle, image1, mask1); |
|
|
|
|
|
|
|
|
|
vector<KeyPoint> keypoints1; |
|
|
|
|
Mat descriptors1; |
|
|
|
|
featureDetector->detect(image1, keypoints1, mask1); |
|
|
|
|
descriptorExtractor->compute(image1, keypoints1, descriptors1); |
|
|
|
|
|
|
|
|
|
vector<DMatch> descMatches; |
|
|
|
|
bfmatcher.match(descriptors0, descriptors1, descMatches); |
|
|
|
|
|
|
|
|
|
vector<DMatch> keyPointMatches; |
|
|
|
|
matchKeyPoints(keypoints0, H, keypoints1, keyPointMatches); |
|
|
|
|
|
|
|
|
|
const float minIntersectRatio = 0.5f; |
|
|
|
|
int keyPointMatchesCount = 0; |
|
|
|
|
for(size_t m = 0; m < keyPointMatches.size(); m++) |
|
|
|
|
{ |
|
|
|
|
if(keyPointMatches[m].distance >= minIntersectRatio) |
|
|
|
|
keyPointMatchesCount++; |
|
|
|
|
} |
|
|
|
|
int descInliersCount = 0; |
|
|
|
|
for(size_t m = 0; m < descMatches.size(); m++) |
|
|
|
|
{ |
|
|
|
|
int queryIdx = descMatches[m].queryIdx; |
|
|
|
|
if(keyPointMatches[queryIdx].distance >= minIntersectRatio && |
|
|
|
|
descMatches[m].trainIdx == keyPointMatches[queryIdx].trainIdx) |
|
|
|
|
descInliersCount++; |
|
|
|
|
} |
|
|
|
|
rotateKeyPoints(keypoints0, H, angle, keypoints1); |
|
|
|
|
Mat descriptors1; |
|
|
|
|
descriptorExtractor->compute(image1, keypoints1, descriptors1); |
|
|
|
|
|
|
|
|
|
float keyPointMatchesRatio = static_cast<float>(keyPointMatchesCount) / keypoints0.size(); |
|
|
|
|
if(keyPointMatchesRatio < minKeyPointMatchesRatio) |
|
|
|
|
{ |
|
|
|
|
ts->printf(cvtest::TS::LOG, "Incorrect keyPointMatchesRatio: curr = %f, min = %f.\n", |
|
|
|
|
keyPointMatchesRatio, minKeyPointMatchesRatio); |
|
|
|
|
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
vector<DMatch> descMatches; |
|
|
|
|
bfmatcher.match(descriptors0, descriptors1, descMatches); |
|
|
|
|
|
|
|
|
|
if(keyPointMatchesCount) |
|
|
|
|
int descInliersCount = 0; |
|
|
|
|
for(size_t m = 0; m < descMatches.size(); m++) |
|
|
|
|
{ |
|
|
|
|
float descInliersRatio = static_cast<float>(descInliersCount) / keyPointMatchesCount; |
|
|
|
|
if(descInliersRatio < minDescInliersRatio) |
|
|
|
|
const KeyPoint& transformed_p0 = keypoints1[descMatches[m].queryIdx]; |
|
|
|
|
const KeyPoint& p1 = keypoints1[descMatches[m].trainIdx]; |
|
|
|
|
if(calcIntersectRatio(transformed_p0.pt, 0.5f * transformed_p0.size, |
|
|
|
|
p1.pt, 0.5f * p1.size) >= minIntersectRatio) |
|
|
|
|
{ |
|
|
|
|
ts->printf(cvtest::TS::LOG, "Incorrect descInliersRatio: curr = %f, min = %f.\n", |
|
|
|
|
descInliersRatio, minDescInliersRatio); |
|
|
|
|
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); |
|
|
|
|
return; |
|
|
|
|
descInliersCount++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
float descInliersRatio = static_cast<float>(descInliersCount) / keypoints0.size(); |
|
|
|
|
if(descInliersRatio < minDescInliersRatio) |
|
|
|
|
{ |
|
|
|
|
ts->printf(cvtest::TS::LOG, "Incorrect descInliersRatio: curr = %f, min = %f.\n", |
|
|
|
|
descInliersRatio, minDescInliersRatio); |
|
|
|
|
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
#if SHOW_DEBUG_LOG |
|
|
|
|
std::cout << "keyPointMatchesRatio - " << keyPointMatchesRatio |
|
|
|
|
<< " - descInliersRatio " << static_cast<float>(descInliersCount) / keyPointMatchesCount << std::endl; |
|
|
|
|
std::cout << "descInliersRatio " << static_cast<float>(descInliersCount) / keypoints0.size() << std::endl; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
ts->set_failed_test_info( cvtest::TS::OK ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ptr<FeatureDetector> featureDetector; |
|
|
|
|
Ptr<DescriptorExtractor> descriptorExtractor; |
|
|
|
|
Ptr<DescriptorExtractor> descriptorExtractor; |
|
|
|
|
int normType; |
|
|
|
|
float minKeyPointMatchesRatio; |
|
|
|
|
float minDescInliersRatio; |
|
|
|
|
|
|
|
|
|
float minDescInliersRatio; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class DetectorScaleInvarianceTest : public cvtest::BaseTest |
|
|
|
@ -392,9 +388,9 @@ public: |
|
|
|
|
DetectorScaleInvarianceTest(const Ptr<FeatureDetector>& _featureDetector, |
|
|
|
|
float _minKeyPointMatchesRatio, |
|
|
|
|
float _minScaleInliersRatio) : |
|
|
|
|
featureDetector(_featureDetector),
|
|
|
|
|
minKeyPointMatchesRatio(_minKeyPointMatchesRatio),
|
|
|
|
|
minScaleInliersRatio(_minScaleInliersRatio) |
|
|
|
|
featureDetector(_featureDetector), |
|
|
|
|
minKeyPointMatchesRatio(_minKeyPointMatchesRatio), |
|
|
|
|
minScaleInliersRatio(_minScaleInliersRatio) |
|
|
|
|
{ |
|
|
|
|
CV_Assert(!featureDetector.empty()); |
|
|
|
|
} |
|
|
|
@ -403,7 +399,7 @@ protected: |
|
|
|
|
|
|
|
|
|
void run(int) |
|
|
|
|
{ |
|
|
|
|
const string imageFilename = string(ts->get_data_path()) + IMAGE_BIKES; |
|
|
|
|
const string imageFilename = string(ts->get_data_path()) + IMAGE_BIKES; |
|
|
|
|
|
|
|
|
|
// Read test data
|
|
|
|
|
Mat image0 = imread(imageFilename); |
|
|
|
@ -417,58 +413,59 @@ protected: |
|
|
|
|
vector<KeyPoint> keypoints0; |
|
|
|
|
featureDetector->detect(image0, keypoints0); |
|
|
|
|
if(keypoints0.size() < 15) |
|
|
|
|
CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); |
|
|
|
|
CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); |
|
|
|
|
|
|
|
|
|
for(int scale = 2; scale <= 4; scale++) |
|
|
|
|
for(float scaleIdx = 1; scaleIdx <= 3; scaleIdx++) |
|
|
|
|
{ |
|
|
|
|
Mat image1;
|
|
|
|
|
resize(image0, image1, Size(), 1./scale, 1./scale); |
|
|
|
|
float scale = 1.f + scaleIdx * 0.5f; |
|
|
|
|
Mat image1; |
|
|
|
|
resize(image0, image1, Size(), 1./scale, 1./scale); |
|
|
|
|
|
|
|
|
|
vector<KeyPoint> keypoints1, osiKeypoints1; // osi - original size image
|
|
|
|
|
featureDetector->detect(image1, keypoints1); |
|
|
|
|
if(keypoints1.size() < 15) |
|
|
|
|
CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); |
|
|
|
|
if(keypoints1.size() < 15) |
|
|
|
|
CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); |
|
|
|
|
|
|
|
|
|
if(keypoints1.size() > keypoints0.size()) |
|
|
|
|
{ |
|
|
|
|
if(keypoints1.size() > keypoints0.size()) |
|
|
|
|
{ |
|
|
|
|
ts->printf(cvtest::TS::LOG, "Strange behavior of the detector. " |
|
|
|
|
"It gives more points count in an image of the smaller size.\n" |
|
|
|
|
"original size (%d, %d), keypoints count = %d\n" |
|
|
|
|
"reduced size (%d, %d), keypoints count = %d\n", |
|
|
|
|
image0.cols, image0.rows, keypoints0.size(), |
|
|
|
|
image1.cols, image1.rows, keypoints1.size()); |
|
|
|
|
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); |
|
|
|
|
"It gives more points count in an image of the smaller size.\n" |
|
|
|
|
"original size (%d, %d), keypoints count = %d\n" |
|
|
|
|
"reduced size (%d, %d), keypoints count = %d\n", |
|
|
|
|
image0.cols, image0.rows, keypoints0.size(), |
|
|
|
|
image1.cols, image1.rows, keypoints1.size()); |
|
|
|
|
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
scaleKeyPoints(keypoints1, osiKeypoints1, scale);
|
|
|
|
|
scaleKeyPoints(keypoints1, osiKeypoints1, scale); |
|
|
|
|
|
|
|
|
|
vector<DMatch> matches; |
|
|
|
|
// image1 is query image (it's reduced image0)
|
|
|
|
|
// image0 is train image
|
|
|
|
|
matchKeyPoints(osiKeypoints1, Mat(), keypoints0, matches); |
|
|
|
|
vector<DMatch> matches; |
|
|
|
|
// image1 is query image (it's reduced image0)
|
|
|
|
|
// image0 is train image
|
|
|
|
|
matchKeyPoints(osiKeypoints1, Mat(), keypoints0, matches); |
|
|
|
|
|
|
|
|
|
const float minIntersectRatio = 0.5f; |
|
|
|
|
int keyPointMatchesCount = 0; |
|
|
|
|
int scaleInliersCount = 0; |
|
|
|
|
const float minIntersectRatio = 0.5f; |
|
|
|
|
int keyPointMatchesCount = 0; |
|
|
|
|
int scaleInliersCount = 0; |
|
|
|
|
|
|
|
|
|
for(size_t m = 0; m < matches.size(); m++) |
|
|
|
|
for(size_t m = 0; m < matches.size(); m++) |
|
|
|
|
{ |
|
|
|
|
if(matches[m].distance < minIntersectRatio) |
|
|
|
|
continue; |
|
|
|
|
if(matches[m].distance < minIntersectRatio) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
keyPointMatchesCount++; |
|
|
|
|
keyPointMatchesCount++; |
|
|
|
|
|
|
|
|
|
// Check does this inlier have consistent sizes
|
|
|
|
|
// Check does this inlier have consistent sizes
|
|
|
|
|
const float maxSizeDiff = 0.8;//0.9f; // grad
|
|
|
|
|
float size0 = keypoints0[matches[m].trainIdx].size; |
|
|
|
|
float size0 = keypoints0[matches[m].trainIdx].size; |
|
|
|
|
float size1 = osiKeypoints1[matches[m].queryIdx].size; |
|
|
|
|
CV_Assert(size0 > 0 && size1 > 0); |
|
|
|
|
if(std::min(size0, size1) > maxSizeDiff * std::max(size0, size1)) |
|
|
|
|
CV_Assert(size0 > 0 && size1 > 0); |
|
|
|
|
if(std::min(size0, size1) > maxSizeDiff * std::max(size0, size1)) |
|
|
|
|
scaleInliersCount++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
float keyPointMatchesRatio = static_cast<float>(keyPointMatchesCount) / keypoints1.size(); |
|
|
|
|
float keyPointMatchesRatio = static_cast<float>(keyPointMatchesCount) / keypoints1.size(); |
|
|
|
|
if(keyPointMatchesRatio < minKeyPointMatchesRatio) |
|
|
|
|
{ |
|
|
|
|
ts->printf(cvtest::TS::LOG, "Incorrect keyPointMatchesRatio: curr = %f, min = %f.\n", |
|
|
|
@ -477,9 +474,9 @@ protected: |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(keyPointMatchesCount) |
|
|
|
|
if(keyPointMatchesCount) |
|
|
|
|
{ |
|
|
|
|
float scaleInliersRatio = static_cast<float>(scaleInliersCount) / keyPointMatchesCount; |
|
|
|
|
float scaleInliersRatio = static_cast<float>(scaleInliersCount) / keyPointMatchesCount; |
|
|
|
|
if(scaleInliersRatio < minScaleInliersRatio) |
|
|
|
|
{ |
|
|
|
|
ts->printf(cvtest::TS::LOG, "Incorrect scaleInliersRatio: curr = %f, min = %f.\n", |
|
|
|
@ -490,21 +487,8 @@ protected: |
|
|
|
|
} |
|
|
|
|
#if SHOW_DEBUG_LOG |
|
|
|
|
std::cout << "keyPointMatchesRatio - " << keyPointMatchesRatio |
|
|
|
|
<< " - scaleInliersRatio " << static_cast<float>(scaleInliersCount) / keyPointMatchesCount << std::endl; |
|
|
|
|
<< " - scaleInliersRatio " << static_cast<float>(scaleInliersCount) / keyPointMatchesCount << std::endl; |
|
|
|
|
#endif |
|
|
|
|
/*vector<DMatch> filteredMatches;
|
|
|
|
|
for(size_t i = 0; i < matches.size(); i++) |
|
|
|
|
{ |
|
|
|
|
if(matches[i].distance >= minIntersectRatio) |
|
|
|
|
filteredMatches.push_back(matches[i]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Mat out; |
|
|
|
|
namedWindow("out", CV_WINDOW_NORMAL); |
|
|
|
|
drawMatches(image1, keypoints1, image0, keypoints0, filteredMatches, out, |
|
|
|
|
Scalar::all(-1), Scalar(-1), vector<char>(), DrawMatchesFlags::DEFAULT + DrawMatchesFlags::DRAW_RICH_KEYPOINTS); |
|
|
|
|
imshow("out", out); |
|
|
|
|
waitKey();*/ |
|
|
|
|
} |
|
|
|
|
ts->set_failed_test_info( cvtest::TS::OK ); |
|
|
|
|
} |
|
|
|
@ -518,25 +502,23 @@ class DescriptorScaleInvarianceTest : public cvtest::BaseTest |
|
|
|
|
{ |
|
|
|
|
public: |
|
|
|
|
DescriptorScaleInvarianceTest(const Ptr<FeatureDetector>& _featureDetector, |
|
|
|
|
const Ptr<DescriptorExtractor>& _descriptorExtractor, |
|
|
|
|
int _normType, |
|
|
|
|
float _minKeyPointMatchesRatio, |
|
|
|
|
const Ptr<DescriptorExtractor>& _descriptorExtractor, |
|
|
|
|
int _normType, |
|
|
|
|
float _minDescInliersRatio) : |
|
|
|
|
featureDetector(_featureDetector),
|
|
|
|
|
descriptorExtractor(_descriptorExtractor), |
|
|
|
|
normType(_normType), |
|
|
|
|
minKeyPointMatchesRatio(_minKeyPointMatchesRatio),
|
|
|
|
|
minDescInliersRatio(_minDescInliersRatio) |
|
|
|
|
featureDetector(_featureDetector), |
|
|
|
|
descriptorExtractor(_descriptorExtractor), |
|
|
|
|
normType(_normType), |
|
|
|
|
minDescInliersRatio(_minDescInliersRatio) |
|
|
|
|
{ |
|
|
|
|
CV_Assert(!featureDetector.empty()); |
|
|
|
|
CV_Assert(!descriptorExtractor.empty()); |
|
|
|
|
CV_Assert(!descriptorExtractor.empty()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected: |
|
|
|
|
|
|
|
|
|
void run(int) |
|
|
|
|
{ |
|
|
|
|
const string imageFilename = string(ts->get_data_path()) + IMAGE_BIKES; |
|
|
|
|
const string imageFilename = string(ts->get_data_path()) + IMAGE_BIKES; |
|
|
|
|
|
|
|
|
|
// Read test data
|
|
|
|
|
Mat image0 = imread(imageFilename); |
|
|
|
@ -549,145 +531,126 @@ protected: |
|
|
|
|
|
|
|
|
|
vector<KeyPoint> keypoints0; |
|
|
|
|
featureDetector->detect(image0, keypoints0); |
|
|
|
|
if(keypoints0.size() < 15) |
|
|
|
|
CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); |
|
|
|
|
Mat descriptors0; |
|
|
|
|
descriptorExtractor->compute(image0, keypoints0, descriptors0); |
|
|
|
|
if(keypoints0.size() < 15) |
|
|
|
|
CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); |
|
|
|
|
Mat descriptors0; |
|
|
|
|
descriptorExtractor->compute(image0, keypoints0, descriptors0); |
|
|
|
|
|
|
|
|
|
BFMatcher bfmatcher(normType); |
|
|
|
|
for(int scale = 2; scale <= 4; scale++) |
|
|
|
|
BFMatcher bfmatcher(normType); |
|
|
|
|
for(float scaleIdx = 1; scaleIdx <= 3; scaleIdx++) |
|
|
|
|
{ |
|
|
|
|
Mat image1;
|
|
|
|
|
resize(image0, image1, Size(), 1./scale, 1./scale); |
|
|
|
|
float scale = 1.f + scaleIdx * 0.5f; |
|
|
|
|
|
|
|
|
|
vector<KeyPoint> keypoints1, osiKeypoints1; // osi - original size image
|
|
|
|
|
featureDetector->detect(image1, keypoints1); |
|
|
|
|
if(keypoints1.size() < 15) |
|
|
|
|
CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); |
|
|
|
|
if(keypoints1.size() > keypoints0.size() ) |
|
|
|
|
{ |
|
|
|
|
ts->printf(cvtest::TS::LOG, "Strange behavior of the detector. " |
|
|
|
|
"It gives more points count in an image of the smaller size.\n" |
|
|
|
|
"original size (%d, %d), keypoints count = %d\n" |
|
|
|
|
"reduced size (%d, %d), keypoints count = %d\n", |
|
|
|
|
image0.cols, image0.rows, keypoints0.size(), |
|
|
|
|
image1.cols, image1.rows, keypoints1.size()); |
|
|
|
|
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
Mat image1; |
|
|
|
|
resize(image0, image1, Size(), 1./scale, 1./scale); |
|
|
|
|
|
|
|
|
|
Mat descriptors1; |
|
|
|
|
descriptorExtractor->compute(image1, keypoints1, descriptors1); |
|
|
|
|
|
|
|
|
|
vector<DMatch> keyPointMatches, descMatches; |
|
|
|
|
// image1 is query image (it's reduced image0)
|
|
|
|
|
// image0 is train image
|
|
|
|
|
bfmatcher.match(descriptors1, descriptors0, descMatches); |
|
|
|
|
|
|
|
|
|
scaleKeyPoints(keypoints1, osiKeypoints1, scale);
|
|
|
|
|
matchKeyPoints(osiKeypoints1, Mat(), keypoints0, keyPointMatches); |
|
|
|
|
|
|
|
|
|
const float minIntersectRatio = 0.5f; |
|
|
|
|
int keyPointMatchesCount = 0; |
|
|
|
|
for(size_t m = 0; m < keyPointMatches.size(); m++) |
|
|
|
|
{ |
|
|
|
|
if(keyPointMatches[m].distance >= minIntersectRatio) |
|
|
|
|
keyPointMatchesCount++; |
|
|
|
|
} |
|
|
|
|
int descInliersCount = 0; |
|
|
|
|
for(size_t m = 0; m < descMatches.size(); m++) |
|
|
|
|
{ |
|
|
|
|
int queryIdx = descMatches[m].queryIdx; |
|
|
|
|
if(keyPointMatches[queryIdx].distance >= minIntersectRatio && |
|
|
|
|
descMatches[m].trainIdx == keyPointMatches[queryIdx].trainIdx) |
|
|
|
|
descInliersCount++; |
|
|
|
|
} |
|
|
|
|
vector<KeyPoint> keypoints1; |
|
|
|
|
scaleKeyPoints(keypoints0, keypoints1, 1./scale); |
|
|
|
|
Mat descriptors1; |
|
|
|
|
descriptorExtractor->compute(image1, keypoints1, descriptors1); |
|
|
|
|
|
|
|
|
|
float keyPointMatchesRatio = static_cast<float>(keyPointMatchesCount) / keypoints1.size(); |
|
|
|
|
if(keyPointMatchesRatio < minKeyPointMatchesRatio) |
|
|
|
|
{ |
|
|
|
|
ts->printf(cvtest::TS::LOG, "Incorrect keyPointMatchesRatio: curr = %f, min = %f.\n", |
|
|
|
|
keyPointMatchesRatio, minKeyPointMatchesRatio); |
|
|
|
|
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
vector<DMatch> descMatches; |
|
|
|
|
bfmatcher.match(descriptors0, descriptors1, descMatches); |
|
|
|
|
|
|
|
|
|
if(keyPointMatchesCount) |
|
|
|
|
const float minIntersectRatio = 0.5f; |
|
|
|
|
int descInliersCount = 0; |
|
|
|
|
for(size_t m = 0; m < descMatches.size(); m++) |
|
|
|
|
{ |
|
|
|
|
float descInliersRatio = static_cast<float>(descInliersCount) / keyPointMatchesCount; |
|
|
|
|
if(descInliersRatio < minDescInliersRatio) |
|
|
|
|
const KeyPoint& transformed_p0 = keypoints0[descMatches[m].queryIdx]; |
|
|
|
|
const KeyPoint& p1 = keypoints0[descMatches[m].trainIdx]; |
|
|
|
|
if(calcIntersectRatio(transformed_p0.pt, 0.5f * transformed_p0.size, |
|
|
|
|
p1.pt, 0.5f * p1.size) >= minIntersectRatio) |
|
|
|
|
{ |
|
|
|
|
ts->printf(cvtest::TS::LOG, "Incorrect descInliersRatio: curr = %f, min = %f.\n", |
|
|
|
|
descInliersRatio, minDescInliersRatio); |
|
|
|
|
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); |
|
|
|
|
return; |
|
|
|
|
descInliersCount++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
float descInliersRatio = static_cast<float>(descInliersCount) / keypoints0.size(); |
|
|
|
|
if(descInliersRatio < minDescInliersRatio) |
|
|
|
|
{ |
|
|
|
|
ts->printf(cvtest::TS::LOG, "Incorrect descInliersRatio: curr = %f, min = %f.\n", |
|
|
|
|
descInliersRatio, minDescInliersRatio); |
|
|
|
|
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
#if SHOW_DEBUG_LOG |
|
|
|
|
std::cout << "keyPointMatchesRatio - " << keyPointMatchesRatio |
|
|
|
|
<< " - descInliersRatio " << static_cast<float>(descInliersCount) / keyPointMatchesCount << std::endl; |
|
|
|
|
std::cout << "descInliersRatio " << static_cast<float>(descInliersCount) / keypoints0.size() << std::endl; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
ts->set_failed_test_info( cvtest::TS::OK ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ptr<FeatureDetector> featureDetector; |
|
|
|
|
Ptr<DescriptorExtractor> descriptorExtractor; |
|
|
|
|
int normType; |
|
|
|
|
Ptr<DescriptorExtractor> descriptorExtractor; |
|
|
|
|
int normType; |
|
|
|
|
float minKeyPointMatchesRatio; |
|
|
|
|
float minDescInliersRatio; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// Tests registration
|
|
|
|
|
|
|
|
|
|
// Detector's rotation invariance check
|
|
|
|
|
/*
|
|
|
|
|
* Detector's rotation invariance check |
|
|
|
|
*/ |
|
|
|
|
TEST(Features2d_RotationInvariance_Detector_ORB, regression) |
|
|
|
|
{ |
|
|
|
|
DetectorRotationInvarianceTest test(Algorithm::create<FeatureDetector>("Feature2D.ORB"), |
|
|
|
|
0.45f,
|
|
|
|
|
0.75f); |
|
|
|
|
0.47f, |
|
|
|
|
0.77f); |
|
|
|
|
test.safe_run(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Descriptors's rotation invariance check
|
|
|
|
|
/*
|
|
|
|
|
* Descriptors's rotation invariance check |
|
|
|
|
*/ |
|
|
|
|
TEST(Features2d_RotationInvariance_Descriptor_ORB, regression) |
|
|
|
|
{ |
|
|
|
|
DescriptorRotationInvarianceTest test(Algorithm::create<FeatureDetector>("Feature2D.ORB"),
|
|
|
|
|
Algorithm::create<DescriptorExtractor>("Feature2D.ORB"),
|
|
|
|
|
NORM_HAMMING,
|
|
|
|
|
0.45f, |
|
|
|
|
0.53f); |
|
|
|
|
0.99f); |
|
|
|
|
test.safe_run(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO: Uncomment test for FREAK when it will work; add test for scale invariance for FREAK
|
|
|
|
|
//TEST(Features2d_RotationInvariance_Descriptor_FREAK, regression)
|
|
|
|
|
//{
|
|
|
|
|
// DescriptorRotationInvarianceTest test(Algorithm::create<FeatureDetector>("Feature2D.ORB"),
|
|
|
|
|
// Algorithm::create<DescriptorExtractor>("Feature2D.FREAK"),
|
|
|
|
|
// NORM_HAMMING(?),
|
|
|
|
|
// 0.45f,
|
|
|
|
|
// 0.?f);
|
|
|
|
|
// DescriptorRotationInvarianceTest test(Algorithm::create<FeatureDetector>("Feature2D.ORB"),
|
|
|
|
|
// Algorithm::create<DescriptorExtractor>("Feature2D.FREAK"),
|
|
|
|
|
// NORM_HAMMING,
|
|
|
|
|
// 0.f);
|
|
|
|
|
// test.safe_run();
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
/* TODO: Why ORB has bad scale invariance in this tests?
|
|
|
|
|
// Detector's scale invariance check
|
|
|
|
|
TEST(Features2d_ScaleInvariance_Detector_ORB, regression) |
|
|
|
|
{ |
|
|
|
|
DetectorScaleInvarianceTest test(Algorithm::create<FeatureDetector>("Feature2D.ORB"), |
|
|
|
|
0.13f,
|
|
|
|
|
0.0f); |
|
|
|
|
test.safe_run(); |
|
|
|
|
} |
|
|
|
|
/*
|
|
|
|
|
* Detector's scale invariance check |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
// Descriptor's scale invariance check
|
|
|
|
|
TEST(Features2d_ScaleInvariance_Descriptor_ORB, regression) |
|
|
|
|
{ |
|
|
|
|
DescriptorScaleInvarianceTest test(Algorithm::create<FeatureDetector>("Feature2D.ORB"), |
|
|
|
|
Algorithm::create<DescriptorExtractor>("Feature2D.ORB"), |
|
|
|
|
NORM_HAMMING, |
|
|
|
|
0.13f,
|
|
|
|
|
0.36f); |
|
|
|
|
test.safe_run(); |
|
|
|
|
}*/ |
|
|
|
|
//TEST(Features2d_ScaleInvariance_Detector_ORB, regression)
|
|
|
|
|
//{
|
|
|
|
|
// DetectorScaleInvarianceTest test(Algorithm::create<FeatureDetector>("Feature2D.ORB"),
|
|
|
|
|
// 0.22f,
|
|
|
|
|
// 0.83f);
|
|
|
|
|
// test.safe_run();
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Descriptor's scale invariance check |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
//TEST(Features2d_ScaleInvariance_Descriptor_ORB, regression)
|
|
|
|
|
//{
|
|
|
|
|
// DescriptorScaleInvarianceTest test(Algorithm::create<FeatureDetector>("Feature2D.ORB"),
|
|
|
|
|
// Algorithm::create<DescriptorExtractor>("Feature2D.ORB"),
|
|
|
|
|
// NORM_HAMMING,
|
|
|
|
|
// 0.01f);
|
|
|
|
|
// test.safe_run();
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
//TEST(Features2d_ScaleInvariance_Descriptor_FREAK, regression)
|
|
|
|
|
//{
|
|
|
|
|
// DescriptorScaleInvarianceTest test(Algorithm::create<FeatureDetector>("Feature2D.ORB"),
|
|
|
|
|
// Algorithm::create<DescriptorExtractor>("Feature2D.FREAK"),
|
|
|
|
|
// NORM_HAMMING,
|
|
|
|
|
// 0.01f);
|
|
|
|
|
// test.safe_run();
|
|
|
|
|
//}
|
|
|
|
|