
graf1.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 特征点匹配和图像拼接


此代码为功能强大的特征点匹配和图像拼接程序,主要使用OPENCV库的AKAZE算法。流程如下:
读取两张图片及齐次图矩阵。
利用AKAZE算法提取图像的特征点和描述子。
使用Brute-Force匹配器,对两组描述子执行近邻搜索,找出最佳匹配对。
进行比率测试来剔除不良匹配。
利用齐次图矩阵验证匹配点,筛选出内点。
我们对之前通过比例测试找到的匹配点对进行进一步的同质性检查,通过计算变换后的点与原始匹配点之间的欧氏距离来评估匹配质量。如果点对对应的变换后的距离小于设定的内点阈值,这对点匹配就被认定为好的匹配,并分别添加到内点向量
inliers1、inliers2和良好匹配向量good_matches中。最终,good_matches中包含了所有通过同质性检查的内点匹配对,可以用于后续绘制匹配结果或其他处理。绘制和保存匹配点的结果图片。
打印关于匹配的统计信息,并显示结果。
整个代码实现的是对两张图片进行特征点匹配,并通过一定的筛选条件去除错误的匹配点,最后绘制出内点并计算内点比例。
#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
590

被折叠的 条评论
为什么被折叠?



