在 QtC++ 中调用 OpenCV 实现特征检测与匹配及图像配准应用
引言
在当今的计算机视觉领域,特征检测与匹配技术成了最为常用的功能,无论是我们日常使用的图像拼接功能,将多张局部照片合成为一张全景图;还是在目标识别中,准确找出图像中特定的物体;亦或是在运动跟踪里,实时追踪物体的移动轨迹,特征检测与匹配都扮演着至关重要的角色。
QtC++ 作为一款强大的跨平台 C++ 图形用户界面应用程序开发框架,凭借其出色的界面设计能力和跨平台特性,被广泛应用于各类应用程序开发。而 OpenCV 作为开源的计算机视觉库,拥有丰富的图像处理和计算机视觉算法,为特征检测与匹配提供了强大的技术支持。将 QtC++ 与 OpenCV 相结合,既能利用 QtC++ 便捷的界面开发能力,又能借助 OpenCV 强大的算法库,为实现特征检测与匹配功能提供了高效且便捷的解决方案。本文将详细介绍在 QtC++ 中调用 OpenCV 进行特征检测与匹配的方法,包括多种算法的对比以及实际应用案例。
一. OpenCV 简介
OpenCV(Open Source Computer Vision Library)是一个基于开源发行的跨平台计算机视觉库,可以运行在 Linux、Windows、Android 和 Mac OS 操作系统上。它轻量级而且高效 —— 由一系列 C 函数和少量 C++ 类构成,同时提供了 Python、Ruby、MATLAB 等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。
OpenCV 的主要功能包括:
- 图像处理:如图像滤波、边缘检测、图像分割等。
- 特征检测与匹配:提供了多种特征检测算法(如 SIFT、SURF、ORB 等)和特征匹配算法(如 BFMatcher、FLANN 等)。
- 目标检测与跟踪:可以实现对特定目标的检测和实时跟踪。
- 相机标定与三维重建:用于相机参数的标定和从二维图像重建三维场景。
二. 特征检测算法详解
2.1 ORB 算法
ORB(Oriented FAST and Rotated BRIEF)算法是一种高效的特征检测与描述算法,它结合了 FAST 角点检测和 BRIEF 描述符的优点,并进行了改进,使其具有旋转不变性和更好的性能。
2.1.1 FAST 角点检测原理
FAST(Features from Accelerated Segment Test)算法是一种快速的角点检测算法。其基本思想是:在图像中选取一个候选点,以该点为中心,考虑一个半径为 3 像素的圆上的 16 个邻点。如果在这 16 个邻点中,有连续 12 个以上的点的亮度与候选点的亮度差值超过一个预设的阈值,那么该候选点就被认为是一个角点。
具体步骤如下:
- 遍历图像中的每个像素点作为候选点;
- 对于每个候选点,计算其亮度值;
- 检查以候选点为中心的 3x3 邻域周围的 16 个点的亮度值,分别做标记;
- 设定一个阈值,如果存在连续 12 个点满足阈值,则该候选点被确定为角点。
为了提高检测速度,FAST 算法还采用了一种加速策略:首先检查圆上的四个点(比如上下左右四个点),如果其中有三个点的亮度与候选点的亮度差值超过阈值,才继续检查其他点,否则直接排除该候选点。
2.1.2 BRIEF 描述符生成方式
BRIEF(Binary Robust Independent Elementary Features)是一种二进制描述符,它通过对特征点周围的像素进行简单的比较来生成描述符,具有计算速度快、存储效率高的特点。
其生成过程如下:
- 对于每个检测到的角点,首先对其周围的区域进行高斯平滑处理,以减少噪声的影响。
- 在特征点周围定义一个特定大小的邻域(通常为 32x32 或 64x64 像素)。
- 从邻域内随机选取若干对像素点,
- 对于每一对像素点,比较它们的亮度值,如果检测点的亮度大于对标点的亮度,则在描述符中记为 1,否则记为 0。通过多对像素点的比较,生成一个二进制字符串作为该特征点的描述符。
2.1.3 ORB 算法的改进
ORB 算法在 FAST 角点检测和 BRIEF 描述符的基础上进行了两项重要改进:
- 方向估计:为了使算法具有旋转不变性,ORB 算法计算特征点的方向。它通过计算特征点周围邻域的灰度质心来确定方向。具体来说,计算特征点邻域内所有像素的灰度值与坐标的乘积之和,然后根据质心与特征点的位置关系确定特征点的方向。
- 旋转 BRIEF:根据特征点的方向,对 BRIEF 描述符进行旋转,使描述符具有旋转不变性。通过将随机点对按照特征点的方向进行旋转,生成具有旋转不变性的二进制描述符。
2.2 SIFT 算法
SIFT(Scale-Invariant Feature Transform)算法是一种具有尺度不变性和旋转不变性的特征检测与描述算法,由 David Lowe 于 1999 年提出。它能够在不同尺度、旋转、光照条件下稳定地检测出图像中的特征点。
2.2.1 尺度空间极值检测:
SIFT 算法首先在尺度空间中检测极值点,以实现尺度不变性。尺度空间是通过对图像进行高斯模糊得到的,不同的高斯标准差对应不同的尺度。
具体步骤如下:
- 构建高斯金字塔:将图像与不同标准差的高斯核进行卷积,得到一系列不同尺度的模糊图像,构成高斯金字塔的一层。然后对每一层的图像进行下采样(通常是缩小一半),得到下一层的图像,从而构建出完整的高斯金字塔。
- 构建差分高斯金字塔(DoG):将高斯金字塔中相邻两层的图像相减,得到差分高斯图像,构成差分高斯金字塔。差分高斯图像近似于高斯拉普拉斯图像,能够很好地检测出图像中的极值点。
- 检测极值点:在差分高斯金字塔中,每个像素点都与它周围相邻的 8 个像素点(同一层)以及上下相邻层的 18 个像素点(共 26 个像素点)进行比较,如果该像素点是这 26 个像素点中的最大值或最小值,则被认为是一个潜在的极值点。
2.2.2 关键点定位与筛选
对于检测到的极值点,需要进行精确定位和筛选,去除低对比度的点和边缘响应点。
- 精确定位:通过泰勒展开式对极值点进行曲线拟合,计算出极值点在尺度空间中的精确位置和尺度。
- 对比度筛选:如果极值点的对比度小于一个预设的阈值,则将其去除,以减少噪声的影响。
- 边缘响应筛选:利用 Hessian 矩阵的迹和行列式来计算关键点的主曲率比,如果主曲率比大于一个预设的阈值,则认为该点是边缘点,将其去除。
2.2.3 方向赋值
为了使 SIFT 描述符具有旋转不变性,需要为每个关键点赋予一个方向。
- 计算梯度:在关键点所在的尺度层上,计算以关键点为中心的邻域内所有像素的梯度大小和方向。梯度大小及梯度方向通过公式计算得到。
- 生成方向直方图:将梯度方向划分为 36 个 bins(每个 bin 对应 10 度),统计每个 bin 内的梯度大小之和,得到方向直方图。
- 确定主方向:方向直方图中峰值对应的方向即为关键点的主方向。如果存在其他峰值与主峰值的差值小于主峰值的 80%,则将该方向也作为关键点的方向,从而实现多方向的特征描述。
2.2.4 生成描述符
SIFT 描述符是一个 128 维的向量,具有良好的独特性和稳定性。
- 旋转坐标系:将关键点周围的邻域旋转到主方向,以实现旋转不变性。
- 划分区域:将旋转后的邻域划分为 4x4 个小区域,每个小区域计算 8 个方向的梯度直方图,得到一个 8 维的向量。
- 生成描述符:将 4x4 个小区域的 8 维向量组合起来,形成一个 128 维的向量,即为 SIFT 描述符。为了提高描述符的鲁棒性,还需要对描述符进行归一化处理,使其不受光照变化的影响。
2.3 SURF 算法
SURF(Speeded Up Robust Features)算法是在 SIFT 算法的基础上发展而来的,它在保持 SIFT 算法优点的同时,提高了计算速度,是一种快速且鲁棒的特征检测与描述算法。
2.3.1 尺度空间构建
- SURF 算法采用盒式滤波器(Box Filter)来近似高斯滤波器,从而加快尺度空间的构建速度。
构建积分图像:积分图像可以快速计算图像中任意矩形区域的像素和,为盒式滤波器的应用提供了便利。积分图像中每个像素点的值是原图像中左上角到该点的所有像素值之和。 - 构建尺度金字塔:与 SIFT 算法类似,SURF 算法也构建尺度金字塔,但它通过改变盒式滤波器的大小来实现不同的尺度,而不是对图像进行下采样。不同尺度的盒式滤波器与积分图像进行卷积,得到不同尺度的响应图像。
- 关键点检测:SURF 算法在尺度空间中检测 Hessian 矩阵的局部极值点作为关键点。
- Hessian 矩阵计算:对于图像中的每个像素点,在不同的尺度下计算其 Hessian 矩阵。Hessian 矩阵的行列式用于衡量该点的局部极值情况。
- 极值点检测:与 SIFT 算法类似,将每个像素点与周围的像素点进行比较,检测出局部极值点作为潜在的关键点。
2.3.2 方向赋值
- SURF 算法通过计算关键点周围的 Haar 小波响应来确定方向,以实现旋转不变性。
计算 Haar 小波响应:在关键点周围的邻域内,计算沿 x 和 y 方向的 Haar 小波响应(通常使用 5x5 的 Haar 窗口)。 - 生成方向直方图:将 Haar 小波响应的方向划分为 60 个 bins(每个 bin 对应 6 度),统计每个 bin 内的响应值之和,得到方向直方图。
- 确定主方向:方向直方图中峰值对应的方向即为关键点的主方向。
2.3.3 生成描述符
SURF 描述符是一个 64 维或 128 维的向量,具有较好的独特性和计算效率。
- 旋转坐标系:将关键点周围的邻域旋转到主方向。
- 划分区域:将旋转后的邻域划分为 4x4 个小区域,每个小区域计算 8 个方向的 Haar 小波响应(包括 x 方向、y 方向、x 方向绝对值、y 方向绝对值),得到一个 8 维的向量。
- 生成描述符:将 4x4 个小区域的 8 维向量组合起来,形成一个 64 维的向量。如果需要更高的独特性,还可以扩展为 128 维向量。最后对描述符进行归一化处理,以提高其对光照变化的鲁棒性。
2.4 特征检测算法对比
2.4.1 速度方面
- ORB 算法:速度最快。由于其采用了 FAST 角点检测和二进制 BRIEF 描述符,计算量小,非常适合实时性要求高的场景,如视频处理、实时目标跟踪等。
- SIFT 算法:速度较慢。其尺度空间构建、关键点精确定位和 128 维描述符生成等过程都需要大量的计算,不适合实时应用。
- SURF 算法:速度比 SIFT 快,但比 ORB 慢。它通过使用盒式滤波器和积分图像提高了计算速度,但相比 ORB 仍有一定差距。
2.4.2 精度方面
- SIFT 算法:精度最高。其 128 维的描述符具有很强的独特性,能够在复杂场景下准确地检测和匹配 特征点,即使在存在噪声、模糊和遮挡的情况下,也能保持较好的性能。
- SURF 算法:精度较高,与 SIFT 相当。在大多数情况下,SURF 算法的匹配精度可以满足实际应用需求,但在一些极端情况下,可能略逊于 SIFT。
- ORB 算法:精度相对较低。由于其采用的是二进制描述符,信息量相对较少,在特征点相似性较高的情况下,容易出现误匹配。但对于一般的应用场景,其精度已经足够。
2.4.3 对光照和旋转的鲁棒性
- 光照鲁棒性:SIFT 和 SURF 算法都通过对描述符进行归一化处理,具有较好的光照鲁棒性。ORB 算法的二进制描述符对光照变化也有一定的容忍度,但相对而言稍弱于 SIFT 和 SURF。
- 旋转鲁棒性:三种算法都具有旋转不变性。ORB 算法通过方向估计和旋转 BRIEF 描述符实现旋转不变性;SIFT 和 SURF 算法通过为关键点赋予主方向,并将描述符旋转到主方向来实现旋转不变性。在旋转角度较大的情况下,三种算法都能保持较好的性能。
2.4.4 其他方面:
SURF 算法存在专利问题,在商业应用中需要获得授权才能使用。而 ORB 和 SIFT 算法(SIFT 算法的专利已过期)则不存在专利问题,可以自由使用。
2.4.5 尺度不变性:
SIFT 和 SURF 算法都具有良好的尺度不变性,能够在不同尺度的图像中检测到相同的特征点。ORB 算法在尺度不变性方面稍弱,对尺度变化较为敏感。
三. 特征匹配算法详解
3.1 BFMatcher(暴力匹配)
BFMatcher(Brute-Force Matcher)是一种简单直接的特征匹配算法,它通过逐个比较两个图像中特征点的描述符,找出最相似的特征点对。
3.1.1 匹配原理
BFMatcher 的匹配过程如下:
- 对于第一幅图像中的每个特征点的描述符,与第二幅图像中所有特征点的描述符进行比较。
- 计算两个描述符之间的距离(通常使用欧氏距离或汉明距离),距离越小表示两个描述符越相似。
- 为第一幅图像中的每个特征点选择距离最小的特征点作为匹配点,从而得到初步的匹配结果。
- 为了提高匹配的准确性,可以采用 K - 近邻匹配(K-NN)策略。对于每个特征点,找出距离最近的两个特征点,如果最近距离与次近距离的比值小于一个预设的阈值,则认为该匹配是可靠的,否则将其剔除。
3.1.2 优缺点
- 优点:匹配结果准确,能够找到所有可能的匹配对,没有遗漏。实现简单,易于理解和实现。
- 缺点:计算量巨大,尤其是当图像中的特征点数量较多时,匹配速度非常慢。不适合处理大规模的特征点匹配问题。
3.2 FLANN(近似最近邻搜索)
FLANN(Fast Library for Approximate Nearest Neighbors)是一种近似最近邻搜索算法,它通过构建特殊的数据结构来加速特征点描述符的匹配过程,在保证一定匹配精度的前提下,大大提高了匹配速度。
3.2.1 匹配原理
FLANN 的匹配过程主要包括以下步骤:
- 构建索引:对于第二幅图像中的特征点描述符,构建一个高效的索引结构(如 KD-Tree、随机森林等),以便快速查找近似最近邻。
- 搜索匹配:对于第一幅图像中的每个特征点描述符,利用构建好的索引结构在第二幅图像的描述符中搜索其近似最近邻。
= 筛选匹配:同样可以采用 K - 近邻匹配策略,对搜索到的近似最近邻进行筛选,保留可靠的匹配对。
3.2.2 优缺点
- 优点:匹配速度快,尤其在特征点数量较多的情况下,优势更加明显。适用于大规模的特征点匹配问题,能够满足实时性要求较高的应用场景。
- 缺点:由于是近似匹配,可能会出现一些误匹配,匹配精度相对 BFMatcher 稍低。索引结构的构建需要一定的时间和内存开销。
3.3 特征匹配算法对比
速度
FLANN 的速度远快于 BFMatcher。在特征点数量较少时,两者的速度差距可能不太明显,但随着特征点数量的增加,BFMatcher 的速度会急剧下降,而 FLANN 则能保持较高的匹配速度。例如,当特征点数量达到 10000 个时,FLANN 的匹配速度可能是 BFMatcher 的几十倍甚至上百倍。
精度
BFMatcher 的精度高于 FLANN。BFMatcher 通过精确计算每个特征点描述符之间的距离,能够找到最相似的匹配对,误匹配率较低。而 FLANN 是近似匹配,可能会因为搜索策略的原因漏掉一些最佳匹配对,或者出现一些误匹配,导致匹配精度有所下降。但在大多数实际应用中,FLANN 的精度已经能够满足需求。
适用场景
- BFMatcher 适用于特征点数量较少、对匹配精度要求极高的场景,如精确的图像配准、文物识别等。
- FLANN 适用于特征点数量较多、对实时性要求较高的场景,如视频跟踪、实时图像拼接等。
四. Qt调用 OpenCV 实现特征检测与匹配的步骤
4.1 环境配置
在 QtC++ 中调用 OpenCV,需要进行相应的环境配置,包括添加 OpenCV 的库和头文件路径。
Windows 系统下,先下载并安装 OpenCV:从 OpenCV 官方网站下载适合 Windows 系统的 OpenCV 安装包,按照提示进行安装,假设安装路径为D:\opencv。
配置系统环境变量:将 OpenCV 的 bin 目录(如D:\opencv\build\x64\vc15\bin)添加到系统的 PATH 环境变量中,以便 Qt 能够找到 OpenCV 的动态链接库。
在 Qt 项目中配置:打开 Qt Creator,创建一个新的 Qt Widgets Application 项目。在项目的.pro 文件中添加以下内容:
INCLUDEPATH += D:\opencv\build\include
LIBS += -LD:\opencv\build\x64\vc15\lib \-lopencv_core454 \-lopencv_imgproc454 \-lopencv_highgui454 \-lopencv_features2d454 \-lopencv_flann455 \-lopencv_xfeatures2d454
其中,454是 OpenCV 的版本号,根据实际安装的版本进行修改。-lopencv_xfeatures2d454用于支持 SIFT 和 SURF 算法。
Linux 系统下的配置:由于Linux主要应用于一些嵌入式设备,需要用交叉编译器编译源码安装。而在 Qt 项目中配置大致与Windows差不多,在此不作赘述。
4.2 代码编写
4.2.1 图像读取
在 QtC++ 中,可以使用 OpenCV 的imread函数读取图像,支持多种图像格式,如 BMP、JPG、PNG 等。读取图像的代码如下:
#include <opencv2/opencv.hpp>
using namespace cv;// 读取图像
Mat img1 = imread("image1.jpg");
Mat img2 = imread("image2.jpg");if (img1.empty() || img2.empty()) {// 图像读取失败处理qDebug() << "Failed to read images!";return -1;
}
4.2.2 特征检测
- 先以 ORB 算法为例,进行特征检测的代码如下:
// 初始化ORB检测器
Ptr<ORB> orb = ORB::create();// 检测特征点并计算描述符
std::vector<KeyPoint> keypoints1, keypoints2;
Mat descriptors1, descriptors2;orb->detectAndCompute(img1, noArray(), keypoints1, descriptors1);
orb->detectAndCompute(img2, noArray(), keypoints2, descriptors2);
- 对于 SIFT 算法,代码如下(需要包含opencv2/xfeatures2d.hpp头文件):
#include <opencv2/xfeatures2d.hpp>
using namespace cv::xfeatures2d;// 初始化SIFT检测器
Ptr<SIFT> sift = SIFT::create();// 检测特征点并计算描述符
std::vector<KeyPoint> keypoints1, keypoints2;
Mat descriptors1, descriptors2;sift->detectAndCompute(img1, noArray(), keypoints1, descriptors1);
sift->detectAndCompute(img2, noArray(), keypoints2, descriptors2);
- SURF 算法的代码与 SIFT 类似,只需将SIFT::create()改为SURF::create()。
4.2.3 特征匹配
- 使用 BFMatcher 进行特征匹配的代码如下:
// 初始化BFMatcher
Ptr<BFMatcher> bfMatcher = BFMatcher::create(NORM_HAMMING, false);// 进行匹配
std::vector<DMatch> matches;
bfMatcher->match(descriptors1, descriptors2, matches);
- 使用 FLANN 进行特征匹配的代码如下(对于二进制描述符,如 ORB 的描述符,需要使用FLANN_INDEX_LSH索引):
// 对于二进制描述符,配置FLANN参数
int indexParams[5] = {12, 20, 2, 1, 200};
Ptr<FlannBasedMatcher> flannMatcher = FlannBasedMatcher::create();
flannMatcher->add(descriptors2);
flannMatcher->train();// 进行匹配
std::vector<DMatch> matches;
flannMatcher->knnMatch(descriptors1, matches, 2);// 应用K-近邻筛选
std::vector<DMatch> goodMatches;
for (int i = 0; i < matches.size(); i++) {if (matches[i][0].distance < 0.75 * matches[i][1].distance) {goodMatches.push_back(matches[i][0]);}
}
可以使用 OpenCV 的drawKeypoints函数绘制特征点,使用drawMatches函数绘制匹配结果,并通过 Qt 的界面显示图像。
- 绘制特征点的代码:
Mat img1WithKeypoints, img2WithKeypoints;
drawKeypoints(img1, keypoints1, img1WithKeypoints, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
drawKeypoints(img2, keypoints2, img2WithKeypoints, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
Mat img1WithKeypoints, img2WithKeypoints;
drawKeypoints(img1, keypoints1, img1WithKeypoints, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
drawKeypoints(img2, keypoints2, img2WithKeypoints, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
可以使用 OpenCV 的drawKeypoints函数绘制特征点,使用drawMatches函数绘制匹配结果,并通过 Qt 的界面显示图像。
- 绘制特征点的代码:
Mat img1WithKeypoints, img2WithKeypoints;
drawKeypoints(img1, keypoints1, img1WithKeypoints, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
drawKeypoints(img2, keypoints2, img2WithKeypoints, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
- 绘制匹配结果的代码:
Mat matchResult;
drawMatches(img1, keypoints1, img2, keypoints2, goodMatches, matchResult, Scalar::all(-1), Scalar::all(-1), std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
- 将 OpenCV 的 Mat 图像转换为 Qt 的 QImage,并在 Qt 界面中显示:
// 将Mat转换为QImage
QImage matToQImage(const Mat& mat) {if (mat.type() == CV_8UC3) {cvtColor(mat, mat, COLOR_BGR2RGB);return QImage((const uchar*)mat.data, mat.cols, mat.rows, mat.step, QImage::Format_RGB888).copy();} else if (mat.type() == CV_8UC1) {return QImage((const uchar*)mat.data, mat.cols, mat.rows, mat.step, QImage::Format_Grayscale8).copy();}return QImage();
}// 在Qt界面中显示
ui->label1->setPixmap(QPixmap::fromImage(matToQImage(img1WithKeypoints)));
ui->label2->setPixmap(QPixmap::fromImage(matToQImage(img2WithKeypoints)));
ui->label3->setPixmap(QPixmap::fromImage(matToQImage(matchResult)));
五. 图像配准应用案例
下面例子目标是对两张具有重叠区域的图像进行配准。图像配准是将不同时间、不同视角或不同传感器获取的同一场景的图像进行对齐的过程,在遥感图像处理、医学图像分析、计算机视觉等领域有着广泛的应用。
在此例中,我们选取的两张图像分别为image1.jpg和image2.jpg,假设它们拍摄的是同一建筑物,但拍摄角度略有不同,存在一定的旋转和位移。我们将使用前面介绍的特征检测与匹配算法,找到两张图像中的对应特征点,然后通过计算变换矩阵将其中一张图像进行变换,实现与另一张图像的配准。
5.1 实现过程
代码实现如下:
- 图像读取与预处理:读取两张图像,并将其转换为灰度图像(可选,特征检测算法可以处理彩色图像,但灰度图像可以减少计算量)。
Mat img1 = imread("image1.jpg");
Mat img2 = imread("image2.jpg");if (img1.empty() || img2.empty()) {qDebug() << "Failed to read images!";return -1;
}Mat gray1, gray2;
cvtColor(img1, gray1, COLOR_BGR2GRAY);
cvtColor(img2, gray2, COLOR_BGR2GRAY);
- 特征检测与描述符计算:这里我们选择 SIFT 算法进行特征检测,因为它具有较高的精度和鲁棒性。
Ptr<SIFT> sift = SIFT::create();std::vector<KeyPoint> keypoints1, keypoints2;
Mat descriptors1, descriptors2;sift->detectAndCompute(gray1, noArray(), keypoints1, descriptors1);
sift->detectAndCompute(gray2, noArray(), keypoints2, descriptors2);
- 特征匹配:使用 FLANN 进行特征匹配,并应用 K - 近邻筛选去除误匹配。
Ptr<FlannBasedMatcher> flannMatcher = FlannBasedMatcher::create();std::vector<std::vector<DMatch>> matches;
flannMatcher->knnMatch(descriptors1, descriptors2, matches, 2);std::vector<DMatch> goodMatches;
for (int i = 0; i < matches.size(); i++) {if (matches[i][0].distance < 0.75 * matches[i][1].distance) {goodMatches.push_back(matches[i][0]);}
}
- 计算变换矩阵:根据匹配的特征点对,使用 RANSAC 算法计算两张图像之间的变换矩阵(通常是仿射变换或透视变换矩阵)。这里我们假设两张图像之间的变换为透视变换。
std::vector<Point2f> points1, points2;
for (size_t i = 0; i < goodMatches.size(); i++) {points1.push_back(keypoints1[goodMatches[i].queryIdx].pt);points2.push_back(keypoints2[goodMatches[i].trainIdx].pt);
}Mat homography;
std::vector<uchar> inliers;
homography = findHomography(points1, points2, RANSAC, 5.0, inliers);
- 图像配准:使用计算得到的变换矩阵对其中一张图像进行透视变换,实现与另一张图像的配准。
Mat img1Registered;
warpPerspective(img1, img1Registered, homography, img2.size());
- 结果显示:将配准前后的图像以及匹配结果显示在 Qt 界面中。
Mat matchResult;
drawMatches(img1, keypoints1, img2, keypoints2, goodMatches, matchResult, Scalar::all(-1), Scalar::all(-1), inliers, DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);ui->labelMatch->setPixmap(QPixmap::fromImage(matToQImage(matchResult)));
ui->labelOriginal->setPixmap(QPixmap::fromImage(matToQImage(img1)));
ui->labelRegistered->setPixmap(QPixmap::fromImage(matToQImage(img1Registered)));
5.2 结果分析
通过上述步骤,我们会得到了配准后的图像。从匹配结果来看,大部分特征点都能够正确匹配,误匹配的数量较少,这表明我们选择的特征检测与匹配算法是有效的。
配准后的图像与目标图像能够较好地对齐,重叠区域的内容基本一致,说明计算得到的变换矩阵是准确的。当然,配准效果还受到多种因素的影响,如特征点的数量和质量、图像的变形程度等。如果图像的变形较大或特征点数量不足,可能需要调整算法参数或选择更合适的特征检测算法。
在实际应用中,可以通过计算配准后图像与目标图像之间的相似度(如均方误差、结构相似性指数等)来定量评估配准效果。相似度越高,说明配准效果越好。
六.总结
前面详细介绍了在 QtC++ 中调用 OpenCV 进行特征检测与匹配的方法,包括 ORB、SIFT、SURF 三种特征检测算法和 BFMatcher、FLANN 两种特征匹配算法的原理、特点及对比,并通过一个实际的图像配准应用案例展示了这些算法的具体应用。
ORB 算法速度快,适合实时性要求高的场景;SIFT 算法精度高,但速度较慢;SURF 算法在速度和精度之间取得了一定的平衡,但存在专利问题。BFMatcher 匹配精度高但速度慢,FLANN 速度快但精度稍低。在实际应用中,应根据具体需求选择合适的算法。
随着计算机视觉技术的不断发展,特征检测与匹配技术也在不断进步。未来,可能会出现更加高效、鲁棒的特征检测与匹配算法,能够更好地应对复杂的场景和各种干扰因素。同时,OpenCV 也在不断更新和完善,为开发者提供更强大的功能和更便捷的开发体验。
