Raghuram Krishnaswami
Raghuram Krishnaswami

Reputation: 139

OpenCV length of matches in BFMatcher

I am trying to use SIFT on two images and match the keypoints using BFMatcher in OpenCV.

However, the number of matches is not equal to the number of query descriptors. Can someone explain why they are not equal?

According to docs match() function "Finds the best match for each descriptor from a query set."

import cv2
import numpy as np

im1 = cv2.imread("trex1.png", cv2.IMREAD_GRAYSCALE)
im2 = cv2.imread("trex2.png", cv2.IMREAD_GRAYSCALE)

sift = cv2.xfeatures2d.SIFT_create()
kp1, des1 = sift.detectAndCompute(im1, None)
kp2, des2 = sift.detectAndCompute(im2, None)

im_kp1 = np.zeros(im1.shape, dtype=np.uint8)
im_kp2 = np.zeros(im1.shape, dtype=np.uint8)
im_kp1 = cv2.drawKeypoints(im1,kp1,None)
im_kp2 = cv2.drawKeypoints(im2,kp2,None)

bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=True)
matches = bf.match(des1,des2)

print len(des1)
# Result : 78
print len(des2)
# Result : 71
print len(matches)
# Result : 55

Upvotes: 4

Views: 4415

Answers (1)

alkasm
alkasm

Reputation: 23052

Because crossCheck=True is removing some of the results.

If you check the docs for the BFMatcher() constructor:

crossCheck If it is false, this is will be default BFMatcher behaviour when it finds the k nearest neighbors for each query descriptor. If crossCheck==true, then the knnMatch() method with k=1 will only return pairs (i,j) such that for i-th query descriptor the j-th descriptor in the matcher's collection is the nearest and vice versa, i.e. the BFMatcher will only return consistent pairs. Such technique usually produces best results with minimal number of outliers when there are enough matches. This is alternative to the ratio test, used by D. Lowe in SIFT paper.

This reads like it will only affect the knnMatch() method, but the match() methods actually explicitly call knnMatch():

void DescriptorMatcher::match( InputArray queryDescriptors, std::vector<DMatch>& matches, InputArrayOfArrays masks )
{
    CV_INSTRUMENT_REGION()

    std::vector<std::vector<DMatch> > knnMatches;
    knnMatch( queryDescriptors, knnMatches, 1, masks, true /*compactResult*/ );
    convertMatches( knnMatches, matches );
}

If you set crossCheck=False (or just don't specify, it's False by default), then you'll get

len(query_descriptors) == len(matches)

Upvotes: 6

Related Questions