mirror of
https://github.com/opencv/opencv.git
synced 2026-07-21 19:33:03 +04:00
Fix: BFMatcher isMaskSupported false on crossCheck
isMaskSupported now returns false when the matcher is created with crossCheck enabled, because the mask path is unsupported in that mode. knnMatchImpl also drops the mask so it cannot reach batchDistance and trigger its mask.empty() assertion. https://github.com/opencv/opencv/issues/22093
This commit is contained in:
@@ -1220,7 +1220,7 @@ public:
|
||||
|
||||
virtual ~BFMatcher() {}
|
||||
|
||||
virtual bool isMaskSupported() const CV_OVERRIDE { return true; }
|
||||
virtual bool isMaskSupported() const CV_OVERRIDE { return !crossCheck; }
|
||||
|
||||
/** @brief Brute-force matcher create method.
|
||||
@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++ )
|
||||
{
|
||||
CV_Assert( trainDescCollection[iIdx].rows < IMGIDX_ONE );
|
||||
Mat mask = (crossCheck || masks.empty()) ? Mat() : masks[iIdx];
|
||||
batchDistance(queryDescriptors, trainDescCollection[iIdx], dist, dtype, nidx,
|
||||
normType, knn, masks.empty() ? Mat() : masks[iIdx], update, crossCheck);
|
||||
normType, knn, mask, update, crossCheck);
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user