OpenCV实现图像特征提取与匹配

一、特征检测与描述子提取

  1. 选择特征检测器
    常用算法包括:

    • ORB‌:一种高效的替代SIFT和SURF的算法,主要用于移动机器人和增强现实等领域。适合实时应用,结合FAST关键点与BRIEF描述子‌。
    • ‌SIFT(尺度不变特征变换):一种用于图像特征检测与描述的经典算法,具有尺度、旋转、光照不变性等特点。计算成本较高,不适合实时应用‌ 。(需OpenCV contrib模块)‌。
    • SURF‌:是对SIFT的改进,主要用于实时应用中。(需OpenCV contrib模块)‌。
    • BRISK‌:二进制描述子,速度快且对噪声鲁棒‌。主要用于实时应用中。
      算法特点
      SIFT‌尺度不变性‌:通过构建图像金字塔和高斯差分金字塔来检测关键点,具有较好的尺度不变性。
      ‌旋转不变性‌:为每个关键点分配一个基准方向,确保描述子的旋转不变性。
      ‌光照和视角变化鲁棒性‌:部分抵抗光照变化,对视角变化也有较好的适应性。
      ‌计算复杂度高‌:由于需要构建多尺度空间,计算成本较高,不适合实时应用‌。
      SURF‌速度更快‌:通过使用Hessian矩阵近似替代SIFT中的高斯差分函数,提高了计算速度。
      ‌鲁棒性‌:在保持SIFT的优点基础上,进一步优化了特征点的检测和描述符的构建过程。
      ‌光照和尺度不变性‌:与SIFT类似,具有较好的光照和尺度不变性‌。
      ORB‌速度快‌:基于FAST角点检测和BRIEF描述符,计算速度比SIFT和SURF快两个数量级。
      ‌旋转不变性‌:通过计算关键点的主方向,实现旋转不变性。
      ‌二进制描述符‌:使用二进制描述符,有利于加速特征匹配过程‌。
      BRISK‌快速计算‌:通过使用圆形邻域和高效的采样模式,计算速度较快。
      ‌二进制描述符‌:使用二进制描述符,有利于加速特征匹配过程。
      ‌尺度不变性‌:通过构建多尺度空间,具有一定的尺度不变性‌。
      // ORB检测器初始化
      cv::Ptr<cv::ORB> detector = cv::ORB::create(500);  // 提取500个特征点
      std::vector<cv::KeyPoint> keypoints1, keypoints2;
      cv::Mat descriptors1, descriptors2;// 检测关键点并计算描述子
      detector->detectAndCompute(img1, cv::noArray(), keypoints1, descriptors1);
      detector->detectAndCompute(img2, cv::noArray(), keypoints2, descriptors2);
      // BRISK检测器‌
      cv::Ptr<cv::BRISK> brisk = cv::BRISK::create();
      // SURF检测器‌
      cv::Ptr<cv::xfeatures2d::SURF> surf = cv::xfeatures2d::SURF::create(400);
      //SIFT// 创建 SIFT 检测器对象Ptr<SIFT> sift = SIFT::create();// 检测关键点并计算描述子std::vector<KeyPoint> keypoints;Mat descriptors;sift->detectAndCompute(img, noArray(), keypoints, descriptors);
      // 初始化SURF检测器,可以选择设置不同的阈值、半径等参数double hessianThreshold = 400;int nOctaves = 4;int nOctaveLayers = 3;bool extended = false;bool upright = false;cv::Ptr<cv::xfeatures2d::SURF> detector = cv::xfeatures2d::SURF::create(hessianThreshold, nOctaves, nOctaveLayers, extended, upright);// 检测关键点和提取特征描述子std::vector<cv::KeyPoint> keypoints1, keypoints2;cv::Mat descriptors1, descriptors2;detector->detectAndCompute(img1, cv::noArray(), keypoints1, descriptors1);detector->detectAndCompute(img2, cv::noArray(), keypoints2, descriptors2);

二、特征匹配

  1. 暴力匹配(BFMatcher)
    计算所有特征对的距离,适合小规模数据集‌。

    cv::BFMatcher matcher(cv::NORM_HAMMING);  // ORB/BRIEF用汉明距离
    std::vector<cv::DMatch> matches;
    matcher.match(descriptors1, descriptors2, matches);
  2. FLANN匹配
    使用近似最近邻搜索,适合大规模数据集‌。

    cv::FlannBasedMatcher matcher(new cv::flann::LshIndexParams(20, 10, 2));
    matcher.match(descriptors1, descriptors2, matches);

三、匹配结果优化

  1. 过滤低质量匹配
    通过距离阈值筛选:

    double min_dist = DBL_MAX, max_dist = 0;
    for (auto& m : matches) {if (m.distance < min_dist) min_dist = m.distance;if (m.distance > max_dist) max_dist = m.distance;
    }
    std::vector<cv::DMatch> good_matches;
    for (auto& m : matches) {if (m.distance < 3 * min_dist)  // 经验阈值good_matches.push_back(m);
    }
  2. 可视化匹配结果

    cv::Mat img_matches;
    cv::drawMatches(img1, keypoints1, img2, keypoints2, good_matches, img_matches);
    cv::imshow("Matches", img_matches);
    cv::waitKey(0);

四、完整流程示例

4.1 ORB
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>int main() {// 读取图像cv::Mat img1 = cv::imread("image1.jpg", cv::IMREAD_GRAYSCALE);cv::Mat img2 = cv::imread("image2.jpg", cv::IMREAD_GRAYSCALE);// 初始化ORB检测器‌cv::Ptr<cv::ORB> detector = cv::ORB::create(500);std::vector<cv::KeyPoint> kp1, kp2;cv::Mat desc1, desc2;detector->detectAndCompute(img1, cv::noArray(), kp1, desc1);detector->detectAndCompute(img2, cv::noArray(), kp2, desc2);// 暴力匹配‌cv::BFMatcher matcher(cv::NORM_HAMMING);std::vector<cv::DMatch> matches;matcher.match(desc1, desc2, matches);// 过滤匹配点‌double min_dist = 100;std::vector<cv::DMatch> good_matches;for (auto& m : matches) {if (m.distance < 2 * min_dist)good_matches.push_back(m);}// 可视化cv::Mat img_matches;cv::drawMatches(img1, kp1, img2, kp2, good_matches, img_matches);//可调整参数显示匹配连线颜色/粗细‌cv::imshow("Matches", img_matches);cv::waitKey(0);return 0;
}

关键参数说明‌
detector->create()    控制特征点数量(如ORB::create(500))
NORM_HAMMING    二进制描述子(ORB、BRISK)的距离计算方式‌
drawMatches()    可调整参数显示匹配连线颜色/粗细‌

4.2 SIFT
//SIFT特征提取与显示代码
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>  // SIFT 头文件‌using namespace cv;
using namespace cv::xfeatures2d;int main() {// 1. 读取图像Mat img = imread("image.jpg", IMREAD_GRAYSCALE);if (img.empty()) {std::cerr << "图像读取失败!" << std::endl;return -1;}// 2. 创建 SIFT 检测器对象Ptr<SIFT> sift = SIFT::create();  // 新版本接口‌// 3. 检测关键点并计算描述子std::vector<KeyPoint> keypoints;Mat descriptors;sift->detectAndCompute(img, noArray(), keypoints, descriptors);  // 核心函数‌// 4. 绘制关键点(带尺度和方向)Mat img_keypoints;drawKeypoints(img, keypoints, img_keypoints, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);  // 可视化增强‌// 5. 显示结果imshow("SIFT 关键点", img_keypoints);waitKey(0);return 0;
}
//SIFT特征匹配代码
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>  // SIFT 头文件‌using namespace cv;
using namespace cv::xfeatures2d;int main()
{
// 1. 读取两幅图像并计算特征
Mat img1 = imread("image1.jpg", IMREAD_GRAYSCALE);
Mat img2 = imread("image2.jpg", IMREAD_GRAYSCALE);
std::vector<KeyPoint> kp1, kp2;
Mat desc1, desc2;
sift->detectAndCompute(img1, noArray(), kp1, desc1);
sift->detectAndCompute(img2, noArray(), kp2, desc2);// 2. 使用 FLANN 匹配器(需转换为 CV_32F)
desc1.convertTo(desc1, CV_32F);
desc2.convertTo(desc2, CV_32F);
FlannBasedMatcher matcher;
std::vector<DMatch> matches;
matcher.match(desc1, desc2, matches);  // 快速近邻匹配‌// 3. 筛选优质匹配(阈值法)
std::sort(matches.begin(), matches.end());
const int keep = matches.size() * 0.15;
matches.erase(matches.begin() + keep, matches.end());// 4. 绘制匹配结果
Mat img_matches;
drawMatches(img1, kp1, img2, kp2, matches, img_matches);
imshow("特征匹配结果", img_matches);
waitKey(0);return 0;
}

关键参数说明‌
‌SIFT::create()‌:创建 SIFT 检测器对象,可设置参数(如特征点数量、对比度阈值等)‌。
‌detectAndCompute()‌:单函数完成关键点检测与描述子计算,提升效率‌。
‌drawKeypoints()‌:DRAW_RICH_KEYPOINTS 参数绘制关键点的尺度圆和方向线‌。


4.3 SURF
//SURF特征描述子匹配代码
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>int main() {// 加载图片cv::Mat img1 = cv::imread("path_to_image1.jpg", cv::IMREAD_GRAYSCALE);cv::Mat img2 = cv::imread("path_to_image2.jpg", cv::IMREAD_GRAYSCALE);// 初始化SURF检测器,可以选择设置不同的阈值、半径等参数double hessianThreshold = 400;int nOctaves = 4;int nOctaveLayers = 3;bool extended = false;bool upright = false;cv::Ptr<cv::xfeatures2d::SURF> detector = cv::xfeatures2d::SURF::create(hessianThreshold, nOctaves, nOctaveLayers, extended, upright);// 检测关键点和提取特征描述子std::vector<cv::KeyPoint> keypoints1, keypoints2;cv::Mat descriptors1, descriptors2;detector->detectAndCompute(img1, cv::noArray(), keypoints1, descriptors1);detector->detectAndCompute(img2, cv::noArray(), keypoints2, descriptors2);// 使用FLANN匹配器进行特征匹配cv::FlannBasedMatcher matcher(cv::makePtr<cv::flann::LshIndexParams>(12, 20, 2));std::vector<cv::DMatch> matches;matcher.match(descriptors1, descriptors2, matches);// 根据匹配距离进行排序std::sort(matches.begin(), matches.end());// 绘制匹配结果cv::Mat matchesImage;cv::drawMatches(img1, keypoints1, img2, keypoints2, matches, matchesImage,cv::Scalar::all(-1), cv::Scalar::all(-1), std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);// 显示匹配结果图像cv::imshow("Matches", matchesImage);cv::waitKey(0);return 0;
}
4.4 BRISK
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>  // 需要 OpenCV contrib 模块using namespace cv;int main() {// 1. 读取图像Mat img1 = imread("image1.jpg", IMREAD_GRAYSCALE);Mat img2 = imread("image2.jpg", IMREAD_GRAYSCALE);// 2. 初始化 BRISK 检测器Ptr<BRISK> brisk = BRISK::create();std::vector<KeyPoint> keypoints1, keypoints2;Mat descriptors1, descriptors2;// 3. 提取关键点和描述子brisk->detectAndCompute(img1, noArray(), keypoints1, descriptors1);brisk->detectAndCompute(img2, noArray(), keypoints2, descriptors2);// 4. 使用暴力匹配器(汉明距离)BFMatcher matcher(NORM_HAMMING);std::vector<DMatch> matches;matcher.match(descriptors1, descriptors2, matches);// 5. 过滤匹配点(基于距离阈值)double min_dist = 100;for (const auto& m : matches) {if (m.distance < min_dist) min_dist = m.distance;}std::vector<DMatch> good_matches;for (const auto& m : matches) {if (m.distance < 2 * min_dist) {  // 调整阈值以控制筛选严格度good_matches.push_back(m);}}// 6. 可视化关键点Mat img_kp1, img_kp2;drawKeypoints(img1, keypoints1, img_kp1, Scalar(0, 255, 0), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);drawKeypoints(img2, keypoints2, img_kp2, Scalar(0, 255, 0), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);imshow("BRISK Keypoints 1", img_kp1);imshow("BRISK Keypoints 2", img_kp2);// 7. 可视化匹配结果Mat img_matches;drawMatches(img1, keypoints1, img2, keypoints2, good_matches, img_matches,Scalar::all(-1), Scalar::all(-1), std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);imshow("BRISK Matches", img_matches);waitKey(0);return 0;
}

‌关键参数说明‌
‌1)BRISK 参数‌:
Ptr<BRISK> brisk = BRISK::create(
    int thresh = 30,       // FAST角点检测阈值
    int octaves = 3,       // 图像金字塔层数
    float patternScale = 1.0  // 描述子采样模式缩放
);
修改 thresh 可调整检测到的关键点数量(值越小,检测到的点越多)。

‌2)匹配筛选‌:
调整 if (m.distance < 2 * min_dist) 中的倍数系数(如 2 → 3)可放宽或收紧筛选条件。
使用 ‌RANSAC 几何验证‌ 进一步优化匹配(参考 SIFT/SURF 代码中的 findHomography 步骤)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/72426.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

向量检索在AI中的应用与技术解析

关键要点 向量检索在AI中用于信息检索、推荐系统和图像搜索&#xff0c;研究表明其通过高维空间中的向量表示数据来提升搜索相关性。它依赖于嵌入技术&#xff08;如Word2Vec、BERT&#xff09;和近邻算法&#xff08;如kNN、ANN&#xff09;&#xff0c;证据倾向于其在处理大…

事务与异步方法(@Async)协同工作

目录 1. 问题场景与风险 &#xff08;1&#xff09;典型场景 &#xff08;2&#xff09;风险分析 2. 解决方案&#xff1a;事务提交后触发异步操作 &#xff08;1&#xff09;代码示例 &#xff08;2&#xff09;关键注解 3. 原理解析 &#xff08;1&#xff09;事务同…

关于进程的实验(子进程和父进程相关的)

文章目录 1.第一个问题2.第二个问题3.第三个问题 1.第一个问题 编写一段程序&#xff0c;利用系统调用fork( )创建两个进程。当此程序运行时&#xff0c;在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符&#xff1a;父进程显示字符“a”;子进程分别显…

MyBatis 如何创建 SqlSession 对象的?

MyBatis 创建 SqlSession 对象的过程主要由 SqlSessionFactory 接口及其实现类来完成。以下是详细步骤&#xff1a; 1. SqlSessionFactory 接口: SqlSessionFactory 是 MyBatis 的核心接口之一&#xff0c;它负责创建 SqlSession 对象。 你可以将 SqlSessionFactory 视为 Sql…

深度优先搜索(DFS)剪枝技术详解与C++实现

深度优先搜索&#xff08;DFS&#xff09;剪枝技术通过提前终止无效路径的搜索&#xff0c;大幅提升算法效率。以下是五种核心剪枝技术的详细解析及C代码示例&#xff1a; 目录 一、可行性剪枝 C实现示例 二、搜索顺序剪枝 伪代码逻辑 三、最优性剪枝 C实现示例 四、排除…

【双指针】移动零

题目描述&#xff1a; 算法分析&#xff1a; 观察输入输出&#xff1a; 输出中一共分为两个区域&#xff0c;0区和非零区。 但是在处理未完成之前&#xff0c;必然存在着一个零和非零数共存的区域&#xff0c;所以在处理的过程当中一共有三个区域&#xff0c;0区&#xff0c;…

学习15天:pytest

1、.pytest强大的插件 pytest-html(生成html格式的自动化测试报告) pytest-xdist测试用例分布式执行。多CPU分发。 pytest-ordering 用于改变测试用例的执行顺序 pytest-rerunfailures用例失败后重跑 allure-pytest 用于生成美观的测试报告。 2、规则&#xff1a; 模块…

股票交易所官方api接口有哪些?获取和使用需要满足什么条件

炒股自动化&#xff1a;申请官方API接口&#xff0c;散户也可以 python炒股自动化&#xff08;0&#xff09;&#xff0c;申请券商API接口 python炒股自动化&#xff08;1&#xff09;&#xff0c;量化交易接口区别 Python炒股自动化&#xff08;2&#xff09;&#xff1a;获取…

2.7 滑动窗口专题:串联所有单词的子串

LeetCode 30. 串联所有单词的子串算法对比分析 1. 题目链接 LeetCode 30. 串联所有单词的子串 2. 题目描述 给定一个字符串 s 和一个字符串数组 words&#xff0c;words 中所有单词长度相同。要求找到 s 中所有起始索引&#xff0c;使得从该位置开始的连续子串包含 words 中所…

【区块链】区块链密码学基础

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 区块链密码学基础引言一、哈希函数1.1 基本概念1.2 数学表达 二、非对称加密2.1…

Spring Boot配置类原理、Spring Boot核心机制理解,以及实现自动装置的底层原理

目的:从底层源码角度分析 Spring Boot 配置类以及自动装载的底层原理 文章目录 1. Spring Boot 配置类实现自动装载1.1 @Configuration注解1.2 @Configuration 注解完成 bean 注入流程图1.3 @ConfigurationProperties注解赋值2. Spring Boot的核心机制:自动装配2.1 @SpringBo…

docker桌面版启动redis,解决无法连接

docker run -d --name redis -p 6379:6379 -v E:\2\redis\redis.conf:/usr/local/etc/redis/redis.conf redis redis-server /usr/local/etc/redis/redis.conf 在本地创建一个目录&#xff0c;里面有个redis.conf文件&#xff0c;内容如下&#xff0c;启动时绑定这个配置文件目…

[网络][tcp协议]:tcp报头

tcp(传输控制协议)是一种面向字节流的传输层协议,相较于udp协议,tcp能保证传输数据的可靠性与准确性,tcp也是目前最常见的传输层协议 本文主要介绍tcp报头各个字段的含义与用途 注:保留6位和6位标记位是目前最普遍的写法,在我查资料时,发现有一些拓展情况,会在后文细说 最简单的…

【虚幻C++笔记】引擎源码下载及编译步骤

目录 1.在GitHub上访问虚幻引擎源代码2.安装Visual Studio 20223.解压完成以后&#xff0c;打开源码的根目录&#xff0c;选择Setup.bat运行4.选择GenerateProjectFiles.bat运行,生成uE5.sln文件&#xff0c;点击这个文件打开项目5.设置编译的选项&#xff0c;选择DevelopmentE…

【数学建模】层次分析法(AHP)详解及其应用

层次分析法(AHP)详解及其应用 引言 在现实生活和工作中&#xff0c;我们经常面临复杂的决策问题&#xff0c;这些问题通常涉及多个评价准则&#xff0c;且各准则之间可能存在相互影响。如何在这些复杂因素中做出合理的决策&#xff1f;层次分析法(Analytic Hierarchy Process…

科普:为何要对特征进行分箱?

一、为何要对特征进行分箱&#xff1f; 分箱&#xff08;Binning&#xff09;是将连续型或离散型特征转化为区间型变量的过程&#xff0c;其核心目标是提升模型效果和解释性&#xff0c;具体原因如下&#xff1a; 1. 业务需求 可解释性&#xff1a;将特征转化为业务可理解的…

理解langgraph工作流的驱动逻辑,以适应langgraph工作流模式的编程。

langgraph的工作流模式虽然方便直观&#xff0c;但习惯了普通函数式编程的数据流处理。刚开始接触时&#xff0c;确实容易试图用函数式编程的思维去适配它&#xff0c;特别是langgraph数据传递由状态字典管理&#xff0c;而非函数返回值&#xff0c;导致代码不够自然&#xff0…

线性dp(数字三角形,LIS,LCS,LCIS)

文章目录 线性dp数字三角形题目思路 LIS&#xff08;最长上升子序列&#xff09;代码&#xff08;n^2&#xff09;二分优化&#xff08;nlogn&#xff09; LCS(最长公共子序列)代码 LCS——>>LIS思路代码 最长公共子串最长公共上升子序列&#xff08;LCIS&#xff09; 线…

Spring Validation参数校验

Spring Validation是Spring框架中用于数据校验的核心模块&#xff0c;通过注解简化数据校验逻辑。 1. 依赖引入&#xff08;SpringBoot项目&#xff09; Spring Boot项目&#xff1a;自动包含spring-boot-starter-validation <dependency><groupId>org.springfra…

《AI大模型趣味实战》No2 : 快速搭建一个漂亮的AI家庭网站-相册/时间线/日历/多用户/个性化配色(中)

快速搭建一个漂亮的AI家庭网站-相册/时间线/日历/多用户/个性化配色(中) 摘要 在上一篇文章中&#xff0c;我们介绍了如何搭建一个基础的家庭网站&#xff08;V1.0版本&#xff09;&#xff0c;包含了用户管理、相册管理、时间线和日历等功能。本文将继续深入&#xff0c;详细…