Advertisement

OpenCV AKAZE本地特征匹配

阅读量:

OpenCV AKAZE本地特征匹配

  • AKAZE-based local feature matching
    • Overview
      • Source code

      • Explanation

        • Load an image and find homographies
          First, AKAZE is employed to detect key points and compute their descriptors.
          The brute-force matcher is then utilized to identify up to the two nearest neighbors for each query descriptor.
          Finally, the correspondence between the matched descriptors is evaluated to determine if they are suitable for a homography model.
      • 结果

        • 找到匹配项

AKAZE本地特征匹配

简介

在本教程中, 我们将向大家介绍AKAZE局部特征的应用, 重点讲解如何通过该方法识别并配对两张图像的关键点. 在一对具有预设单应性矩阵的图像中, 我们将定位关键点并计算其内蕴一致的数量(即与该单应性矩阵匹配的成功配对). 将采用Graffiti序列中的第1和第3幅图像作为研究对象.

在这里插入图片描述

单应性由3 x 3矩阵给出:

复制代码
6285898e-01  -2.9922929e-01   2.2567123e+02
    3.3443473e-01   1.0143901e+00  -7.6999973e+01
    3.4663091e-04  -1.4364524e-05   1.0000000e+00

源代码

复制代码
    #include <opencv2/features2d.hpp>
    #include <opencv2/imgproc.hpp>
    #include <opencv2/highgui.hpp>
    #include <iostream>
    using namespace std;
    using namespace cv;
    const float inlier_threshold = 2.5f; // Distance threshold to identify inliers with homography check
    const float nn_match_ratio = 0.8f;   // Nearest neighbor matching ratio
    int main(int argc, char* argv[])
    {
    CommandLineParser parser(argc, argv,
                             "{@img1 | graf1.png | input image 1}"
                             "{@img2 | graf3.png | input image 2}"
                             "{@homography | H1to3p.xml | homography matrix}");
    Mat img1 = imread( samples::findFile( parser.get<String>("@img1") ), IMREAD_GRAYSCALE);
    Mat img2 = imread( samples::findFile( parser.get<String>("@img2") ), IMREAD_GRAYSCALE);
    Mat homography;
    FileStorage fs( samples::findFile( parser.get<String>("@homography") ), FileStorage::READ);
    fs.getFirstTopLevelNode() >> homography;
    vector<KeyPoint> kpts1, kpts2;
    Mat desc1, desc2;
    Ptr<AKAZE> akaze = AKAZE::create();
    akaze->detectAndCompute(img1, noArray(), kpts1, desc1);
    akaze->detectAndCompute(img2, noArray(), kpts2, desc2);
    BFMatcher matcher(NORM_HAMMING);
    vector< vector<DMatch> > nn_matches;
    matcher.knnMatch(desc1, desc2, nn_matches, 2);
    vector<KeyPoint> matched1, matched2;
    for(size_t i = 0; i < nn_matches.size(); i++) {
        DMatch first = nn_matches[i][0];
        float dist1 = nn_matches[i][0].distance;
        float dist2 = nn_matches[i][1].distance;
        if(dist1 < nn_match_ratio * dist2) {
            matched1.push_back(kpts1[first.queryIdx]);
            matched2.push_back(kpts2[first.trainIdx]);
        }
    }
    vector<DMatch> good_matches;
    vector<KeyPoint> inliers1, inliers2;
    for(size_t i = 0; i < matched1.size(); i++) {
        Mat col = Mat::ones(3, 1, CV_64F);
        col.at<double>(0) = matched1[i].pt.x;
        col.at<double>(1) = matched1[i].pt.y;
        col = homography * col;
        col /= col.at<double>(2);
        double dist = sqrt( pow(col.at<double>(0) - matched2[i].pt.x, 2) +
                            pow(col.at<double>(1) - matched2[i].pt.y, 2));
        if(dist < inlier_threshold) {
            int new_i = static_cast<int>(inliers1.size());
            inliers1.push_back(matched1[i]);
            inliers2.push_back(matched2[i]);
            good_matches.push_back(DMatch(new_i, new_i, 0));
        }
    }
    Mat res;
    drawMatches(img1, inliers1, img2, inliers2, good_matches, res);
    imwrite("akaze_result.png", res);
    double inlier_ratio = inliers1.size() / (double) matched1.size();
    cout << "A-KAZE Matching Results" << endl;
    cout << "*******************************" << endl;
    cout << "# Keypoints 1:                        \t" << kpts1.size() << endl;
    cout << "# Keypoints 2:                        \t" << kpts2.size() << endl;
    cout << "# Matches:                            \t" << matched1.size() << endl;
    cout << "# Inliers:                            \t" << inliers1.size() << endl;
    cout << "# Inliers Ratio:                      \t" << inlier_ratio << endl;
    cout << endl;
    imshow("result", res);
    waitKey();
    return 0;
    }

解释

加载图像和单应性

复制代码
    CommandLineParser parser(argc, argv,
                             "{@img1 | graf1.png | input image 1}"
                             "{@img2 | graf3.png | input image 2}"
                             "{@homography | H1to3p.xml | homography matrix}");
    Mat img1 = imread( samples::findFile( parser.get<String>("@img1") ), IMREAD_GRAYSCALE);
    Mat img2 = imread( samples::findFile( parser.get<String>("@img2") ), IMREAD_GRAYSCALE);
    Mat homography;
    FileStorage fs( samples::findFile( parser.get<String>("@homography") ), FileStorage::READ);
    fs.getFirstTopLevelNode() >> homography;

我们正在这里加载灰度图像。单应性存储在用FileStorage创建的xml中。

使用AKAZE检测关键点并计算描述符

复制代码
    vector<KeyPoint> kpts1, kpts2;
    Mat desc1, desc2;
    Ptr<AKAZE> akaze = AKAZE::create();
    akaze->detectAndCompute(img1, noArray(), kpts1, desc1);
    akaze->detectAndCompute(img2, noArray(), kpts2, desc2);

我们生成AKAZE并识别并提取AKAZE的关键点与描述符。因为mask参数无需配置而故采用noArray函数。

使用蛮力匹配器查找2-nn个匹配项

复制代码
    vector<KeyPoint> matched1, matched2;
    for(size_t i = 0; i < nn_matches.size(); i++) {
        DMatch first = nn_matches[i][0];
        float dist1 = nn_matches[i][0].distance;
        float dist2 = nn_matches[i][1].distance;
        if(dist1 < nn_match_ratio * dist2) {
            matched1.push_back(kpts1[first.queryIdx]);
            matched2.push_back(kpts2[first.trainIdx]);
        }
    }

如果某对象的最近邻距离显著低于第二近邻的距离,则判定该对象为正确的最近邻(存在多于一个最近邻的情况)

检查我们的匹配项是否适合单应性模型

复制代码
    vector<DMatch> good_matches;
    vector<KeyPoint> inliers1, inliers2;
    for(size_t i = 0; i < matched1.size(); i++) {
        Mat col = Mat::ones(3, 1, CV_64F);
        col.at<double>(0) = matched1[i].pt.x;
        col.at<double>(1) = matched1[i].pt.y;
        col = homography * col;
        col /= col.at<double>(2);
        double dist = sqrt( pow(col.at<double>(0) - matched2[i].pt.x, 2) +
                            pow(col.at<double>(1) - matched2[i].pt.y, 2));
        if(dist < inlier_threshold) {
            int new_i = static_cast<int>(inliers1.size());
            inliers1.push_back(matched1[i]);
            inliers2.push_back(matched2[i]);
            good_matches.push_back(DMatch(new_i, new_i, 0));
        }
    }

当两个关键点在投影空间中的距离低于某个阈值时,则认为其满足单应性模型的适用条件

我们为内部线创建了一组新的匹配项,因为绘图功能需要它。

输出结果

复制代码
    Mat res;
    drawMatches(img1, inliers1, img2, inliers2, good_matches, res);
    imwrite("akaze_result.png", res);
    double inlier_ratio = inliers1.size() / (double) matched1.size();
    cout << "A-KAZE Matching Results" << endl;
    cout << "*******************************" << endl;
    cout << "# Keypoints 1:                        \t" << kpts1.size() << endl;
    cout << "# Keypoints 2:                        \t" << kpts2.size() << endl;
    cout << "# Matches:                            \t" << matched1.size() << endl;
    cout << "# Inliers:                            \t" << inliers1.size() << endl;
    cout << "# Inliers Ratio:                      \t" << inlier_ratio << endl;
    cout << endl;
    imshow("result", res);
    waitKey();

在这里,我们保存生成的图像并打印一些统计信息。

结果

找到匹配项

在这里插入图片描述

根据您的OpenCV版本,您应该获得与以下内容一致的结果:

复制代码
    Keypoints 1:   2943
    Keypoints 2:   3511
    Matches:       447
    Inliers:       308
    Inlier Ratio: 0.689038

全部评论 (0)

还没有任何评论哟~