mirror of
https://github.com/opencv/opencv.git
synced 2026-07-21 19:33:03 +04:00
Merge pull request #29506 from manand881:fix/bfmatcher-crosscheck-mask
Fix: BFMatcher isMaskSupported false on crossCheck
This commit is contained in:
@@ -1220,7 +1220,7 @@ public:
|
|||||||
|
|
||||||
virtual ~BFMatcher() {}
|
virtual ~BFMatcher() {}
|
||||||
|
|
||||||
virtual bool isMaskSupported() const CV_OVERRIDE { return true; }
|
virtual bool isMaskSupported() const CV_OVERRIDE { return !crossCheck; }
|
||||||
|
|
||||||
/** @brief Brute-force matcher create method.
|
/** @brief Brute-force matcher create method.
|
||||||
@param normType One of NORM_L1, NORM_L2, NORM_HAMMING, NORM_HAMMING2. L1 and L2 norms are
|
@param normType One of NORM_L1, NORM_L2, NORM_HAMMING, NORM_HAMMING2. L1 and L2 norms are
|
||||||
|
|||||||
@@ -996,8 +996,9 @@ void BFMatcher::knnMatchImpl( InputArray _queryDescriptors, std::vector<std::vec
|
|||||||
for( iIdx = 0; iIdx < imgCount; iIdx++ )
|
for( iIdx = 0; iIdx < imgCount; iIdx++ )
|
||||||
{
|
{
|
||||||
CV_Assert( trainDescCollection[iIdx].rows < IMGIDX_ONE );
|
CV_Assert( trainDescCollection[iIdx].rows < IMGIDX_ONE );
|
||||||
|
Mat mask = (crossCheck || masks.empty()) ? Mat() : masks[iIdx];
|
||||||
batchDistance(queryDescriptors, trainDescCollection[iIdx], dist, dtype, nidx,
|
batchDistance(queryDescriptors, trainDescCollection[iIdx], dist, dtype, nidx,
|
||||||
normType, knn, masks.empty() ? Mat() : masks[iIdx], update, crossCheck);
|
normType, knn, mask, update, crossCheck);
|
||||||
update += IMGIDX_ONE;
|
update += IMGIDX_ONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -672,4 +672,79 @@ TEST(Features2d_BFMatcher_CrossCheck, ocl_matches_cpu)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regression test for https://github.com/opencv/opencv/issues/22093
|
||||||
|
// A BFMatcher built with crossCheck enabled must report isMaskSupported()
|
||||||
|
// as false and must not assert/crash when a non-empty mask is supplied.
|
||||||
|
// Before the fix the mask reached batchDistance's CV_Assert(mask.empty())
|
||||||
|
// and aborted the process.
|
||||||
|
TEST(Features2d_BFMatcher_CrossCheck, issue_22093_mask)
|
||||||
|
{
|
||||||
|
const string imgPath = cvtest::findDataFile(
|
||||||
|
"cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png");
|
||||||
|
Mat img = imread(imgPath, IMREAD_GRAYSCALE);
|
||||||
|
ASSERT_FALSE(img.empty());
|
||||||
|
|
||||||
|
Ptr<ORB> orb = ORB::create();
|
||||||
|
vector<KeyPoint> keypoints;
|
||||||
|
Mat descriptors;
|
||||||
|
orb->detectAndCompute(img, noArray(), keypoints, descriptors);
|
||||||
|
ASSERT_FALSE(descriptors.empty());
|
||||||
|
|
||||||
|
Ptr<BFMatcher> matcher = BFMatcher::create(NORM_HAMMING, true /*crossCheck*/);
|
||||||
|
ASSERT_FALSE(matcher->isMaskSupported());
|
||||||
|
|
||||||
|
// Register the descriptors as the train set, then match query against
|
||||||
|
// train with a non-empty per-image mask. This is the exact call chain
|
||||||
|
// from the issue (match -> knnMatch -> knnMatchImpl -> batchDistance).
|
||||||
|
// Before the fix the mask reached batchDistance's CV_Assert(mask.empty())
|
||||||
|
// and aborted the process.
|
||||||
|
matcher->add(descriptors);
|
||||||
|
Mat mask = Mat::ones(descriptors.rows, descriptors.rows, CV_8UC1);
|
||||||
|
vector<Mat> masks(1, mask);
|
||||||
|
|
||||||
|
vector<DMatch> matches;
|
||||||
|
EXPECT_NO_THROW(matcher->match(descriptors, matches, masks));
|
||||||
|
ASSERT_FALSE(matches.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// OCL coverage for https://github.com/opencv/opencv/issues/22093
|
||||||
|
// SIFT is used only to obtain float (CV_32FC1) descriptors, which are then
|
||||||
|
// wrapped as UMat so the OCL BFMatcher dispatch is eligible. With an empty
|
||||||
|
// mask the OCL cross-check kernel (ocl_matchWithCrossCheck) runs; with a
|
||||||
|
// non-empty mask the OCL dispatch refuses it and falls back to the CPU path,
|
||||||
|
// which (thanks to the fix) must not assert or crash.
|
||||||
|
TEST(Features2d_BFMatcher_CrossCheck, issue_22093_mask_ocl)
|
||||||
|
{
|
||||||
|
const string imgPath = cvtest::findDataFile(
|
||||||
|
"cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png");
|
||||||
|
Mat img = imread(imgPath, IMREAD_GRAYSCALE);
|
||||||
|
ASSERT_FALSE(img.empty());
|
||||||
|
|
||||||
|
Ptr<SIFT> sift = cv::SIFT::create();
|
||||||
|
vector<KeyPoint> keypoints;
|
||||||
|
Mat descriptors;
|
||||||
|
sift->detectAndCompute(img, noArray(), keypoints, descriptors);
|
||||||
|
ASSERT_EQ(descriptors.type(), CV_32FC1);
|
||||||
|
ASSERT_FALSE(descriptors.empty());
|
||||||
|
|
||||||
|
Ptr<BFMatcher> matcher = BFMatcher::create(NORM_L2, true /*crossCheck*/);
|
||||||
|
ASSERT_FALSE(matcher->isMaskSupported());
|
||||||
|
|
||||||
|
matcher->add(descriptors);
|
||||||
|
UMat query = descriptors.getUMat(ACCESS_READ);
|
||||||
|
|
||||||
|
// (1) explicit empty mask -> OCL cross-check kernel is eligible
|
||||||
|
vector<Mat> emptyMasks(1, Mat());
|
||||||
|
vector<DMatch> matchesEmpty;
|
||||||
|
EXPECT_NO_THROW(matcher->match(query, matchesEmpty, emptyMasks));
|
||||||
|
ASSERT_FALSE(matchesEmpty.empty());
|
||||||
|
|
||||||
|
// (2) non-empty mask -> OCL refused, CPU fallback must not crash
|
||||||
|
Mat mask = Mat::ones(descriptors.rows, descriptors.rows, CV_8UC1);
|
||||||
|
vector<Mat> masks(1, mask);
|
||||||
|
vector<DMatch> matchesMasked;
|
||||||
|
EXPECT_NO_THROW(matcher->match(query, matchesMasked, masks));
|
||||||
|
ASSERT_FALSE(matchesMasked.empty());
|
||||||
|
}
|
||||||
|
|
||||||
}} // namespace
|
}} // namespace
|
||||||
|
|||||||
Reference in New Issue
Block a user