当前位置: 首页 > news >正文

Opencv计算机视觉编程攻略-第九节 检测兴趣点

       一般而言,如果一个物体在一幅图像中被检测到关键点,那么同一个物体在其他图像中也会检测到同一个关键点。图像匹配是关键点的常用功能之一,它的作用包括关联同一场景的两幅图像、检测图像中事物的发生地点等等。

1.局部模板匹配

        凭单个像素就判断两个关键点的相似度显然是不够的,因此要在匹配过程中考虑每个关键
点周围的图像块,
对图像块中的像素进行逐个比较,但是并不是最可靠的。

      第一步,使用FAST 检测器进行关键点提取:

// 定义特征检测器
cv::Ptr<cv::FeatureDetector> ptrDetector; // 泛型检测器指针
ptrDetector= // 这里选用FAST 检测器
cv::FastFeatureDetector::create(80);
// 检测关键点
ptrDetector->detect(image1,keypoints1);
ptrDetector->detect(image2,keypoints2);

      第二步,定义匹配框,在每个图像对的关键点之间进行匹配,这里使用逐像素相差匹配:

// 在第二幅图像中找出与第一幅图像中的每个关键点最匹配的
cv::Mat result;
std::vector<cv::DMatch> matches;
// 针对图像一的全部关键点
for (int i=0; i<keypoints1.size(); i++) {
    // 定义图像块
    neighborhood.x = keypoints1[i].pt.x-nsize/2;
    neighborhood.y = keypoints1[i].pt.y-nsize/2;
    // 如果邻域超出图像范围,就继续处理下一个点
    if (neighborhood.x<0 || neighborhood.y<0 ||neighborhood.x+nsize >= image1.cols ||neighborhood.y+nsize >= image1.rows)
        continue;
    // 第一幅图像的块
    patch1 = image1(neighborhood);
    // 存放最匹配的值
    cv::DMatch bestMatch;
    // 针对第二幅图像的全部关键点
    for (int j=0; j<keypoints2.size(); j++) {
        // 定义图像块
        neighborhood.x = keypoints2[j].pt.x-nsize/2;
        neighborhood.y = keypoints2[j].pt.y-nsize/2;
        // 如果邻域超出图像范围,就继续处理下一个点
        if (neighborhood.x<0 || neighborhood.y<0 ||neighborhood.x + nsize >= image2.cols ||neighborhood.y + nsize >= image2.rows)
            continue;
        // 第二幅图像的块
        patch2 = image2(neighborhood);
        // 匹配两个图像块
        cv::matchTemplate(patch1,patch2,result, cv::TM_SQDIFF);
        // 检查是否为最佳匹配
        if (result.at<float>(0,0) < bestMatch.distance) {
            bestMatch.distance= result.at<float>(0,0);
            bestMatch.queryIdx= i;
            bestMatch.trainIdx= j;
        }
                }
        // 添加最佳匹配
        matches.push_back(bestMatch);
    }

      第三步,选择置信度最高的一些点,进行展示:

// 提取25 个最佳匹配项
std::nth_element(matches.begin(),matches.begin() + 25,matches.end());
matches.erase(matches.begin() + 25,matches.end());

// 绘制图像
cv::Mat matchImage;
cv::drawMatches(image1,keypoints1, // 第一幅图像
                image2,keypoints2, // 第二幅图像
                matches, // 匹配项的向量
                cv::Scalar(255,255,255), // 线条颜色
                cv::Scalar(255,255,255)); // 点的颜色

        上述方法使用图块之间相似度进行评估,也可以使用opencv中的区域模板匹配方法进一步增大搜索精确度:

// 定义搜索区域
cv::Mat roi(image2, // 这里用图像的上半部分
cv::Rect(0,0,image2.cols,image2.rows/2));
// 进行模板匹配
cv::matchTemplate(roi, // 搜索区域
target, // 模板
result, // 结果
cv::TM_SQDIFF); // 相似度
// 找到最相似的位置
double minVal, maxVal;
cv::Point minPt, maxPt;
cv::minMaxLoc(result, &minVal, &maxVal, &minPt, &maxPt);
// 在相似度最高的位置绘制矩形
// 本例中为minPt
cv::rectangle(roi, cv::Rect(minPt.x, minPt.y,
target.cols, target.rows), 255);

2.描述并匹配局部强度值模式

       在图像分析中,可以用邻域包含的视觉信息来标识每个特征点,以便区分各个特征点。特征描述子通常是一个N 维的向量,在光照变化和拍摄角度发生微小扭曲时,它描述特征点的方式不会发生变化, 通常可以用简单的差值矩阵来比较描述子,例如用欧几里得距离等

       基于特征的方法都包含一个检测器和一个描述子组件,与cv::Feature2D 相关的类也一样,它们都有一个检测函数(用于检测兴趣点)和一个计算函数(用于计算兴趣点的描述子)cv::SURF
和cv::SIFT,检测流程和上述一致。

// 1. 定义关键点的容器
std::vector<cv::KeyPoint> keypoints1;
std::vector<cv::KeyPoint> keypoints2;

// 2. 定义特征检测器
cv::Ptr<cv::Feature2D> ptrFeature2D =
cv::xfeatures2d::SURF::create(2000.0);

// 3. 检测关键点
ptrFeature2D->detect(image1,keypoints1);
ptrFeature2D->detect(image2,keypoints2);

// 4. 提取描述子
cv::Mat descriptors1;
cv::Mat descriptors2;
ptrFeature2D->compute(image1,keypoints1,descriptors1);
ptrFeature2D->compute(image2,keypoints2,descriptors2);

// 5. 构造匹配器
cv::BFMatcher matcher(cv::NORM_L2);

cv::BFMatcher matcher2(cv::NORM_L2, // 度量差距
true); // 可以开启 交叉检查标志


// 匹配两幅图像的描述子
std::vector<cv::DMatch> matches;
matcher.match(descriptors1,descriptors2, matches);

      好的特征描述子不受照明和视角微小变动的影响,也不受图像中噪声的影响,因此它们通常
基于局部强度值的差值,SURF 描述子在关键点周围局部地应用下面的简易内核:

        第一个内核度量水平方向的局部强度值差值(标为dx),第二个内核度量垂直方向的差值(标为dy)。通常将用于提取描述子向量的邻域尺寸定为特征值缩放因子的20 倍(即20σ)。然后把这个正方形区域划分成更小的4×4 子区域。对于每个子区域,在5×5 等分的位置上(用尺寸为2σ的内核)计算内核反馈值(dx 和dy)。

     使用SURF 和SIFT 的特征和描述子可以进行尺度无关的匹配,能够取得较好的效果。

3.用二值描述子匹配关键点

        上述描述子是浮点数类型的向量,大小为64、128等,这导致对它们的操作将耗资巨大,为了减少内存使用、降低计算量,人们引入了将一组比特位(0 和1)组合成二值描述子的概念。这里的难点在于,既要易于计算,又要在场景和视角变化时保持鲁棒性。

// 1. 定义特征检测器/描述子
// Construct the ORB feature object
cv::Ptr<cv::Feature2D> feature = cv::ORB::create(60);
// 大约60 个特征点
// 检测并描述关键点
// 2. 检测ORB 特征
feature->detectAndCompute(image1, cv::noArray(),
keypoints1, descriptors1);
feature->detectAndCompute(image2, cv::noArray(),
keypoints2, descriptors2);

// 3. 构建匹配器
cv::BFMatcher matcher(cv::NORM_HAMMING); // 二值描述子一律使用Hamming 规范】

// 4.匹配两幅图像的描述子
std::vector<cv::DMatch> matches;
matcher.match(descriptors1, descriptors2, matches);

       ORB 算法在多个尺度下检测特征点,这些特征点含有方向。基于这些特征点,ORB 描述子通过简单比较强度值,提取出每个关键点的表征,在BRIEF 描述子的基础上构建的,然后在关键点周围的邻域内随机选取一对像素点,创建一个二值描述子。

       比较这两个像素点的强度值,如果第一个点的强度值较大,就把对应描述子的位(bit)设为1,否则就设为0。对一批随机像素点对进行上述处理,就产生了一个由若干位(bit)组成的描述子,通常采用128 到512 位(成对地测试)。


文章转载自:
http://apogee.isnyv.cn
http://box.isnyv.cn
http://abm.isnyv.cn
http://backmost.isnyv.cn
http://bisexed.isnyv.cn
http://aculeated.isnyv.cn
http://backwardation.isnyv.cn
http://accommodation.isnyv.cn
http://bulgy.isnyv.cn
http://armour.isnyv.cn
http://busby.isnyv.cn
http://bradawl.isnyv.cn
http://catchpole.isnyv.cn
http://antelucan.isnyv.cn
http://austroasiatic.isnyv.cn
http://autosuggestion.isnyv.cn
http://beget.isnyv.cn
http://cando.isnyv.cn
http://biafra.isnyv.cn
http://bhakta.isnyv.cn
http://caladium.isnyv.cn
http://cabbagehead.isnyv.cn
http://calyceal.isnyv.cn
http://betweenmaid.isnyv.cn
http://bibliographer.isnyv.cn
http://actinotheraphy.isnyv.cn
http://barpque.isnyv.cn
http://bitterish.isnyv.cn
http://aptitudinal.isnyv.cn
http://anthurium.isnyv.cn
http://www.dtcms.com/a/111428.html

相关文章:

  • Linux systemd 服务全面详解
  • SQL语句(三)—— DQL
  • 详解AI采集框架Crawl4AI,打造智能网络爬虫
  • poetry安装
  • Transformer+BO-SVM时间序列预测(Matlab)
  • 第十五届蓝桥杯大赛软件赛省赛Python 大学 C 组:5.回文数组
  • 系统分析师-前6章总结
  • STM32单片机入门学习——第14节: [6-2] 定时器定时中断定时器外部时钟
  • PGSQL 对象创建函数生成工具
  • RSA和ECC在密钥长度相同的情况下哪个更安全?
  • 深度学习中的 Batch 机制:从理论到实践的全方位解析
  • AcWing 6118. 蛋糕游戏
  • Ubuntu安装Podman教程
  • Spring 核心技术解析【纯干货版】- XXI:Spring 第三方工具整合模块 Spring-Context-Suppor 模块精讲
  • 《古龙群侠传》游戏秘籍
  • 【 <二> 丹方改良:Spring 时代的 JavaWeb】之 Spring Boot 中的监控:使用 Actuator 实现健康检查
  • 【spring cloud Netflix】Eureka注册中心
  • 关于uint8_t、uint16_t、uint32_t、uint64_t的区别与分析
  • Linux(2025.3.15)
  • 安装 TabbyAPI+Exllamav2 和 vLLM 的详细步骤
  • 前后端通信指南
  • C# Winform 入门(7)之简单的抽奖系统邮件
  • #管理Node.js的多个版本
  • 虚拟现实 UI 设计:打造沉浸式用户体验
  • MINIQMT学习课程Day10
  • 欧几里得算法求最大公约数、最小公倍数
  • chromium魔改——CDP(Chrome DevTools Protocol)检测01
  • CCF GESP C++编程 八级认证真题 2025年3月
  • MySQL 性能调优:数据库的极限运动训练
  • [ deepseek 指令篇章 ]300个领域和赛道喂饭级deepseek指令