【opencv】教程代码 —features2D(8)AKAZE 特征点匹配和图像拼接

6984768f6e261b85fe9f8864dba8ffc9.png

graf1.png

a6f744ec5f12bf315c07be46170c0e6e.png

graf3.png

<?xml version="1.0"?>
<opencv_storage>
<H13 type_id="opencv-matrix">
  <rows>3</rows>
  <cols>3</cols>
  <dt>d</dt>
  <data>
  7.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 </data></H13>
</opencv_storage>

H1to3p.xml

AKAZE_match.cpp 特征点匹配和图像拼接

172f40189ba94a156e6747c7c2afe9a8.png

6582d0545d042293fc0efda481f96439.png

此代码为功能强大的特征点匹配和图像拼接程序,主要使用OPENCV库的AKAZE算法。流程如下:

  1. 读取两张图片及齐次图矩阵。

  2. 利用AKAZE算法提取图像的特征点和描述子

  3. 使用Brute-Force匹配器,对两组描述子执行近邻搜索,找出最佳匹配对。

  4. 进行比率测试来剔除不良匹配

  5. 利用齐次图矩阵验证匹配点,筛选出内点

    我们对之前通过比例测试找到的匹配点对进行进一步的同质性检查,通过计算变换后的点与原始匹配点之间的欧氏距离来评估匹配质量。如果点对对应的变换后的距离小于设定的内点阈值,这对点匹配就被认定为好的匹配,并分别添加到内点向量inliers1inliers2和良好匹配向量good_matches中。最终,good_matches中包含了所有通过同质性检查的内点匹配对,可以用于后续绘制匹配结果或其他处理。

  6. 绘制和保存匹配点的结果图片

  7. 打印关于匹配的统计信息,并显示结果。

整个代码实现的是对两张图片进行特征点匹配,并通过一定的筛选条件去除错误的匹配点,最后绘制出内点并计算内点比例

#include <opencv2/features2d.hpp> // 引入OpenCV特征检测相关头文件
#include <opencv2/imgproc.hpp>    // 引入OpenCV图像处理相关头文件
#include <opencv2/highgui.hpp>    // 引入OpenCV高级GUI(图形用户界面)相关头文件
#include <iostream>               // 引入输入输出流头文件


using namespace std; // 使用标准命名空间
using namespace cv;  // 使用OpenCV命名空间


const float inlier_threshold = 2.5f; // 设置内点距离阈值,用于同质性检查识别内点
const float nn_match_ratio = 0.8f;   // 设置最近邻匹配比例


int main(int argc, char* argv[])
{
    //! [load]
    // 解析命令行参数
    CommandLineParser parser(argc, argv,
                             "{@img1 | graf1.png | input image 1}"
                             "{@img2 | graf3.png | input image 2}"
                             "{@homography | H1to3p.xml | homography matrix}");
    // 读取图片1,以灰度模式
    Mat img1 = imread( samples::findFile( parser.get<String>("@img1") ), IMREAD_GRAYSCALE);
    // 读取图片2,以灰度模式
    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;
    //! [load]


    //! [AKAZE]
    // 初始化特征点向量和描述符矩阵
    vector<KeyPoint> kpts1, kpts2;
    Mat desc1, desc2;


    // 创建AKAZE特征检测器
    Ptr<AKAZE> akaze = AKAZE::create();
    // 对img1进行特征检测和描述符计算
    akaze->detectAndCompute(img1, noArray(), kpts1, desc1);
    // 对img2进行特征检测和描述符计算
    akaze->detectAndCompute(img2, noArray(), kpts2, desc2);
    //! [AKAZE]


    //! [2-nn matching]
    // 创建BFMatcher,用于特征匹配
    BFMatcher matcher(NORM_HAMMING);
    vector< vector<DMatch> > nn_matches;
    // 执行2近邻匹配
    matcher.knnMatch(desc1, desc2, nn_matches, 2);
    //! [2-nn matching]


    //! [ratio test filtering]
    // 初始化过滤后的匹配特征点向量
    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]);
        }
    }
    //! [ratio test filtering]


    //! [homography check]
    vector<DMatch> good_matches; // 初始化一个DMatch向量,用于存储良好匹配对
    vector<KeyPoint> inliers1, inliers2; // 初始化两个KeyPoint向量,用于存储一致性检查后的内点匹配
    // 遍历所有已匹配的特征点对
    for(size_t i = 0; i < matched1.size(); i++) {
        Mat col = Mat::ones(3, 1, CV_64F); // 创建一个三行一列的矩阵,初始化为1,用于齐次坐标表示
        col.at<double>(0) = matched1[i].pt.x; // 设置矩阵的第一个元素为当前匹配对的第一个点的x坐标
        col.at<double>(1) = matched1[i].pt.y; // 设置矩阵的第二个元素为当前匹配对的第一个点的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)); // 将这对内点添加到良好匹配向量中
        }
    }
    //! [homography check]


    //! [draw final matches]
    // 初始化结果图像
    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();
    //! [draw final matches]


    return 0;
}

终端输出:

A-KAZE Matching Results
*******************************
# Keypoints 1:                          2418
# Keypoints 2:                          2884
# Matches:                              382
# Inliers:                              267
# Inliers Ratio:                        0.698953
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值