OpenCV matchGMS doesn’t write to output vector

  c++, image-processing, opencv, pattern-matching, python

I build an application that should find the best matching images in two directories of image-pairs. The application builds on an ORB-descriptor-extractor and a GMS-matcher. I have a working version in Python and am now in the process of porting the code to C++.

The C++-version works as expected with one exception: It doesn’t write the matches found by GMS to the output-vector!

I know for a fact, that there are matches, since i could look at them with the Python-version.

I looked at the OpenCV-sample to construct the C++-version. In particular the lines 101-106 of the sample are almost identically present in my version. Additionally i built and executed the sample and it does what it should; which includes writing the GMS-matches to an output-vector.

Here is the Python-code:

def compute_gms(query_path, train_path, nfeatures):
    query_files = sorted([x for x in query_path.iterdir() if x.is_file()], key=lambda x: int('_')[-1].split('.')[0]))
    train_files = sorted([x for x in train_path.iterdir() if x.is_file()], key=lambda x: int('_')[-1].split('.')[0]))

    orb = cv2.ORB_create(nfeatures)

    query_kpts = []
    query_descs = np.empty((nfeatures, 32, len(query_files)), dtype=np.uint8)

    t1 = timer()

    for i, q in enumerate(query_files):
        img = cv2.imread(str(q), 0)
        img = cv2.equalizeHist(img)
        kpts, descs = orb.detectAndCompute(img, None)  # Use background subtraction to remove further outliers!
        query_descs[...,i] = descs

    t2 = timer()
    print(f"Computing query-descs took {round(t2-t1)} seconds.")

    train_kpts = []
    train_descs = np.empty((nfeatures, 32, len(train_files)), dtype=np.uint8)

    t3 = timer()

    for i, t in enumerate(train_files):
        img = cv2.imread(str(t), 0)
        img = cv2.equalizeHist(img)
        kpts, descs = orb.detectAndCompute(img, None)
        train_descs[...,i] = descs

    t4 = timer()
    print(f"Computing train-descs took {round(t4-t3)} seconds.")

    scores = np.empty((len(query_files), len(train_files)))
    img_shape = cv2.imread(str(query_files[0]), 0).shape
    matcher = cv2.BFMatcher_create(cv2.NORM_HAMMING)

    for i in pb.progressbar(range(scores.shape[0])):
        for j in range(scores.shape[1]):
            matches = matcher.match(query_descs[:,:,i], train_descs[:,:,j])
            matches_gms = cv2.xfeatures2d.matchGMS(img_shape, img_shape, query_kpts[i], train_kpts[j], matches, withRotation=True)
            ratio = len(matches_gms) / nfeatures
            scores[i, j] = ratio

    return scores

It expectes to pathlib-paths that point to the query- and train-directories. The same holds true for the C++-Version of the code, where the paths are supplied as std::strings:

Mat computeGms(const std::string &queryPath, const std::string &trainPath, const unsigned int _nfeatures=500) {
    // Creating ORB-object
    Ptr<Feature2D> orb {ORB::create(_nfeatures)};

    // Compute descriptors and keypoints for query-data
    std::vector<Mat> queryDescriptorsVec;
    std::vector<std::vector<KeyPoint>> queryKpts;

    auto queryDirIter = std::filesystem::directory_iterator(queryPath);
    int queryFileCount = std::count_if(begin(queryDirIter), end(queryDirIter),
                                       [](auto& entry) { return entry.is_regular_file(); });

    // Declaring needed variables
    Mat descriptors;
    std::vector<KeyPoint> kpts;

    for (const auto &entry : std::filesystem::directory_iterator(queryPath)) {
        Mat img {imread(entry.path(), IMREAD_GRAYSCALE)};
        equalizeHist(img, img);
        orb->detectAndCompute(img, noArray(), kpts, descriptors);

    std::cout << "Computed query-datan";

    // Compute descriptors and keypoints for train-data
    std::vector<Mat> trainDescriptorsVec;
    std::vector<std::vector<KeyPoint>> trainKpts;

    auto trainDirIter = std::filesystem::directory_iterator(trainPath);
    int trainFileCount = std::count_if(begin(trainDirIter), end(trainDirIter),
                                       [](auto& entry) { return entry.is_regular_file();});


    for (const auto &entry : std::filesystem::directory_iterator(trainPath)) {
        Mat img {imread(entry.path(), IMREAD_GRAYSCALE)};
        equalizeHist(img, img);
        orb->detectAndCompute(img, noArray(), kpts, descriptors);

    std::cout << "Computed train-datan";

    // Compute matches
    Mat scores(queryDescriptorsVec.size(), trainDescriptorsVec.size(), 0.0f);
    Ptr<DescriptorMatcher> matcher {BFMatcher::create(NORM_HAMMING)};
    float ratio, max, maxes{0};
    const Size imsize = imread(std::filesystem::directory_iterator(queryPath)->path(), 0).size(); // ALL IMAGES HAVE SAME SIZE
    //                                                             ^^^ get dimensions of first image the iterator points to

    for (size_t i = 0; i < queryDescriptorsVec.size(); ++i) {
        max = 0;
        for (size_t j = 0; j < trainDescriptorsVec.size(); ++j) {
            std::vector<DMatch> matchesAll, matchesGMS;
            matcher->match(queryDescriptorsVec[i], trainDescriptorsVec[j], matchesAll);
            xfeatures2d::matchGMS(imsize, imsize, queryKpts[i], trainKpts[j], matchesAll, matchesGMS, true);
            ratio = matchesGMS.size() / _nfeatures;
  <float>(i,j) = ratio;
            if (ratio > max)
                max = ratio;
        maxes += max;
        std::cout << i << "tmean: " << maxes/(i+1) << "n";  // Should print out the mean scores, but only prints 0s
    return scores;

Source: Windows Questions C++