BFmatcher with crossCheck doesn't crossCheck

asked 2018-06-28 04:05:12 -0600

crogre gravatar image

If I understand the purpose and documentation of the brute force matcher with cross check enabled, I don't think it works as expected. See the example code below

import numpy
import cv2

sources = numpy.array([[1, 1, 0], [1, 1, 1]], dtype=numpy.uint8)
targets = numpy.array([[1, 1, 1], [0, 0, 0]], dtype=numpy.uint8)
for si, source in enumerate(sources):
    for ti, target in enumerate(targets):
        print('%d -> %d: %d' % (si, ti, cv2.norm(source, target, cv2.NORM_HAMMING)))

bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.knnMatch(sources, targets, k=1)
print('-- crosscheck=True')
for match in matches:
    if match:
        print('%d -> %d: %f' % (match[0].queryIdx, match[0].trainIdx, match[0].distance))

bf_nocc = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=False)
matches_nocc = bf.knnMatch(sources, targets, k=1)
print('-- crosscheck=False')
for match in matches_nocc:
    if match:
        print('%d -> %d: %f' % (match[0].queryIdx, match[0].trainIdx, match[0].distance))

With output

0 -> 0: 1
0 -> 1: 2
1 -> 0: 0
1 -> 1: 3
-- crossCheck=True
0 -> 1: 2.000000
1 -> 0: 0.000000
-- crossCheck=False
0 -> 0: 1.000000
1 -> 0: 0.000000

I would expect the crossCheck to remove the 0 -> 1 match since source 0 is closest to target 0. Have I misunderstood how the cross check is supposed to work or is this an issue with the matcher?

edit retag flag offensive close merge delete

Comments

Yesit's weird. I can reproduce your issue in C++ :

Mat sources = (Mat_<uchar>(2, 3) <<  1, 1, 1, 1, 1, 0);
Mat targets = (Mat_<uchar>(2, 3) << 1, 1, 1, 0,0,0);
cout << sources << "\n";
cout << targets << "\n";
cout << "SOURCES TARGETS\n";
    for (int i = 0; i < sources.rows; i++)
        for (int j = 0; j < targets.rows; j++)
            cout << i << " -> " << j << " = " << norm(sources.row(i), targets.row(j), NORM_HAMMING) << "\n";
    cout << "TARGETS SOURCES\n";
    for (int i = 0; i < targets.rows; i++)
        for (int j = 0; j < sources.rows; j++)
            cout << i << " -> " << j << " = " << norm(targets.row(i), sources.row(j), NORM_HAMMING) << "\n";
LBerger gravatar imageLBerger ( 2018-06-29 02:24:27 -0600 )edit

//

Ptr<BFMatcher> bf = BFMatcher::create(NORM_HAMMING, true);
vector<vector<DMatch>> match;
cout << "Match with crosscheck TRUE\n";
bf->knnMatch(sources, targets, match, 1);

for (auto m : match)
    cout << m[0].queryIdx << " -> " << m[0].trainIdx << " = " << m[0].distance << "\n";
Ptr<BFMatcher> bf2 = BFMatcher::create(NORM_HAMMING, false);
cout << "Match with crosscheck FALSE \n";
match.clear();
bf2->knnMatch(sources, targets, match, 1);

for (auto m : match)
    cout << m[0].queryIdx << " -> " << m[0].trainIdx << " = " << m[0].distance << "\n";
return 0;
LBerger gravatar imageLBerger ( 2018-06-29 02:24:58 -0600 )edit

results :

[  1,   1,   1;
   1,   1,   0]
[  1,   1,   1;
   0,   0,   0]
SOURCES TARGETS
0 -> 0 = 0
0 -> 1 = 3
1 -> 0 = 1
1 -> 1 = 2
TARGETS SOURCES
0 -> 0 = 0
0 -> 1 = 1
1 -> 0 = 3
1 -> 1 = 2
Match with crosscheck TRUE
0 -> 0 = 0
1 -> 1 = 2
Match with crosscheck FALSE
0 -> 0 = 0
1 -> 0 = 1

Data order changes results : I don't understand.

LBerger gravatar imageLBerger ( 2018-06-29 02:55:09 -0600 )edit

Good catch! Indeed, if I switch sources and targets at the top, I get only 0 -> 1 with crossCheck=True (the correct one in this example).

crogre gravatar imagecrogre ( 2018-06-29 06:05:14 -0600 )edit
1

Source code is here and doc

IMHO something is wrong Post an issue

crossmacth doesnot mean best match? If it is not an optional parameter is necessary cross best match !

LBerger gravatar imageLBerger ( 2018-06-29 06:13:57 -0600 )edit
1

Thanks! I have submitted issue #11855.

crogre gravatar imagecrogre ( 2018-06-29 06:57:24 -0600 )edit