news 2026/2/18 3:53:56

OPENCV(python)--初学之路(十八)特征匹配+ Homography查找对象

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OPENCV(python)--初学之路(十八)特征匹配+ Homography查找对象

一前言

今天的更新依旧是周末合刊,而且这是OPENCV的第一阶段的最后一次更新了,我们的下一个系列是YOLOv8,不过需要时间,可能未来一周都会更新,而且我会同时开一个系列,是关于蓝桥杯JAVA赛道的备赛之路,说实话,我没有一点JAVA基础,所以这是真的零基础备赛,至于这两个系列怎么安排,我也会调整。今天的章节也很有意思,我和十八似乎有种缘分,我家是十八层,高中是十八中,,我现在十八岁。哈哈哈

二主要内容

蛮力匹配的基础知识

蛮力匹配器很简单。它采用第一组中的一个特征点描述子,并使用一些距离计算与第二组中的所有其他特征点匹配。并返回距离最近的一个特征点。

对于 BF 匹配器,首先我们必须使用cv.BFMatcher()

bf =cv.BFMatcher(normType=cv.NORM_L2, crossCheck=False)

创建 BFMatcher 对象。它需要两个可选的参数。第一个是 normType。它指定要使用的距离测量方法。默认情况下,它是 cv.NORM_L2 。它很适合于 SIFT 和 SURF 等( cv.NORM_L1 也可以)。对于基于二进制字符串的描述子,如 ORB,BRIEF,BRISK 等,应使用 cv.NORM_HAMMING ,它使用汉明距离作为度量。如果 ORB 使用 WTA_K == 3 或 4,则应使用 cv.NORM_HAMMING2 。

第二个参数是布尔变量 crossCheck,默认为 false。如果为 True,则 Matcher 仅返回具有值(i,j)的匹配,使得集合 A 中的第 i 个描述子具有集合 B 中的第 j 个描述子作为最佳匹配,反之亦然。也就是说,两组中的两个特征点应该相互匹配。它提供了一致的结果,是 D.Lowe 在 SIFT 论文中提出的比率测试的一个很好的替代方案。

一旦创建,两个重要的方法是BFMatcher.match()BFMatcher.knnMatch()。第一个返回最佳匹配。第二种方法返回 k 个最佳匹配,其中 k 由用户指定。当我们需要做更多的工作时,它可能会有用。

BFMatcher.match()BFMatcher.knnMatch()对比

特性match()knnMatch()
返回数量1个最佳匹配k个最佳匹配
返回值类型List[DMatch]List[List[DMatch]]
常用筛选方法按距离阈值筛选Lowe's ratio test
计算复杂度较低较高(需计算k个)
匹配质量可能有误匹配通过ratio test可提高准确性

就像我们使用 cv.drawKeypoints()绘制特征点一样,cv.drawMatches()(之前我们的文章有过介绍)帮助我们绘制匹配。它水平堆叠两个图像,并从第一个图像到第二个图像绘制线条,显示最佳匹配。还有cv.drawMatchesKnn,它绘制了所有 k 个最佳匹配。如果 k = 2,它将为每个关键点绘制两条匹配线。因此,如果我们想要有选择地绘制它,我们必须传递一个掩模。

对 ORB 描述子使用蛮力匹配

在这里,我们将看到一个关于如何匹配两个图像之间的特征的简单示例。在这种情况下,我有一个 queryImage 和一个 trainImage。我们将尝试使用特征匹配在 trainImage 中查找 queryImage。 (图片为/samples/c/box.png 和/samples/c/box_in_scene.png)

我们使用 ORB 描述符来匹配功能。所以让我们从加载图像,查找描述子等开始。

我们使用 cv.NORM_HAMMING (因为我们使用的是 ORB)创建一个 BFMatcher 对象并且启用了 crossCheck 以获得更好的结果。然后我们使用 Matcher.match()方法在两个图像中获得最佳匹配。我们按照距离的升序对它们进行排序,以便最佳匹配(距离最小)出现在前面。然后我们只画出前 10 个匹配(仅为了能见度,你可以随意增加匹配的个数)。

代码如下

import numpy as np import cv2 as cv import matplotlib.pyplot as plt # 读取查询图像和训练图像 img1 = cv.imread(r'D:\python_code\pic\sumoiao.webp', 0) # queryImage img2 = cv.imread(r'D:\python_code\pic\smt.png', 0) # trainImage # 初始化ORB检测器 orb = cv.ORB_create() # 使用ORB检测关键点和描述符 kp1, des1 = orb.detectAndCompute(img1, None) kp2, des2 = orb.detectAndCompute(img2, None) # 创建BFMatcher对象 bf = cv.BFMatcher(cv.NORM_HAMMING, crossCheck=True) # 匹配描述符 matches = bf.match(des1, des2) # 根据距离排序匹配结果 matches = sorted(matches, key=lambda x: x.distance) # 绘制前10个匹配点 img3 = cv.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flags=2) # 显示匹配结果 plt.imshow(img3) plt.show()

这里的图片可以使用一张图片然后截一部分,就有了两张图片,完整的放上面,截图放下面

效果如下

这个匹配器对象是什么?

matches = bf.match(des1,des2)的结果是 DMatch 对象的列表。此 DMatch 对象具有以下属性:

  • DMatch.distance - 描述子之间的距离。越低越好。
  • DMatch.trainIdx - 目标图像中描述子的索引
  • DMatch.queryIdx - 查询图像中描述子的索引
  • DMatch.imgIdx - 目标图像的索引。

对 SIFT 描述符进行蛮力匹配和比率测试

这一次,我们将使用 BFMatcher.knnMatch()来获得最佳匹配。在这个例子中,我们将采用 k = 2,以便我们可以在使用 D.Lowe 论文中的比率测试。

代码如下

import numpy as np import cv2 as cv from matplotlib import pyplot as plt # 图像路径设置 query_img_path = r'D:\python_code\pic\sumoiao.webp' train_img_path = r'D:\python_code\pic\smt.png' # 灰度模式读取图像 img1 = cv.imread(query_img_path, 0) img2 = cv.imread(train_img_path, 0) # 初始化SIFT检测器 sift = cv.SIFT_create() # 关键点检测与描述符计算 kp1, des1 = sift.detectAndCompute(img1, None) kp2, des2 = sift.detectAndCompute(img2, None) # 创建暴力匹配器 bf = cv.BFMatcher(cv.NORM_L2, crossCheck=False) # K近邻匹配 matches = bf.knnMatch(des1, des2, k=2) # Lowe's比率测试筛选 ratio_threshold = 0.75 good_matches = [] for m,n in matches: if m.distance < ratio_threshold * n.distance: good_matches.append(m) # 匹配结果可视化 if len(good_matches) > 10: # 仅当有足够匹配时才绘制 result_img = cv.drawMatches(img1, kp1, img2, kp2, good_matches[:50], None, flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS) plt.figure(figsize=(15,10)) plt.imshow(result_img) plt.axis('off') plt.show() else: print("未找到足够数量的可靠匹配点")

效果如下

还是很精准的,效果很不错

基础

那我们上面做了什么?我们使用了一个 queryImage,在其中找到了一些特征点,我们采用了另一个 trainImage,找到了该图像中的特征,最后找到它们之间特征点的最佳匹配。简而言之,我们在另一个杂乱的图像中找到了一个对象的某些部分的位置。这些信息足以在 trainImage 上准确找到对象。

为此,我们可以使用来自 calib3d 模块的函数,即cv.findHomography()

H, mask =cv.findHomography(srcPoints, dstPoints, method=None, ransacReprojThreshold=None, maxIters=None, confidence=None)

参数详解

输入参数表格
参数名称类型描述默认值常用值
srcPointsnp.ndarray源图像中的点坐标必需形状:(N, 2)(N, 1, 2)
dstPointsnp.ndarray目标图像中的对应点必需形状与srcPoints相同
methodint计算方法0选项见下方方法表格
ransacReprojThresholdfloatRANSAC重投影误差阈值-1.0-5.0
maxItersintRANSAC最大迭代次数20001000-5000
confidencefloat置信度0.9950.95-0.999
返回值表格
返回值类型描述可能值
Hnp.ndarray3×3单应性矩阵形状:(3, 3)None
masknp.ndarrayNone内点掩码形状:(N,),仅当使用RANSAC/LMEDS时返回

这就是cv.findHomography()的详细用法。它在计算机视觉的许多应用中都非常重要,特别是图像配准、全景拼接和增强现实等领域。

如果将两个图像中的特征点集传递给这个函数,它将找到该对象的透视变换。然后我们可以使用cv.perspectiveTransform()

dst =cv.perspectiveTransform(src, M)

参数说明

参数类型描述
srcnp.ndarray输入点集,形状为(N, 1, 2)(N, 2)
Mnp.ndarray3×3 透视变换矩阵
dstnp.ndarray输出点集,形状与输入相同

来查找对象。它需要至少四个正确的点来找到这种变换。

我们已经看到匹配时可能存在一些可能的错误,这可能会影响结果。为了解决这个问题,算法使用 RANSAC 或 LEAST_MEDIAN(可以由标志位决定)。因此,提供正确估计的良好匹配称为内点,剩余称为外点。cv.findHomography()返回一个指定了内点和外点的掩模。

代码

首先,像往常一样,让我们在图像中找到 SIFT 特征并应用比率测试来找到最佳匹配。

代码如下

import numpy as np import cv2 as cv from matplotlib import pyplot as plt MIN_MATCH_COUNT = 10 img1 = cv.imread(r'D:\python_code\pic\sumoiao.webp',0) # 查询图像 img2 = cv.imread(r'D:\python_code\pic\smt.png',0) # 训练图像 # 初始化SIFT检测器 sift = cv.SIFT_create() # 检测关键点并计算描述符 kp1, des1 = sift.detectAndCompute(img1,None) kp2, des2 = sift.detectAndCompute(img2,None) # 设置FLANN匹配器参数 FLANN_INDEX_KDTREE = 1 index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5) search_params = dict(checks = 50) # 创建FLANN匹配器 flann = cv.FlannBasedMatcher(index_params, search_params) matches = flann.knnMatch(des1,des2,k=2) # 应用Lowe's比率测试筛选优质匹配点 good = [] for m,n in matches: if m.distance < 0.7*n.distance: good.append(m) # 如果找到足够多的匹配点,计算单应性矩阵 if len(good) > MIN_MATCH_COUNT: src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2) dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2) # 使用RANSAC方法计算单应性矩阵 M, mask = cv.findHomography(src_pts, dst_pts, cv.RANSAC,5.0) matchesMask = mask.ravel().tolist() # 获取查询图像的尺寸 h,w = img1.shape pts = np.float32([[0,0],[0,h-1],[w-1,h-1],[w-1,0]]).reshape(-1,1,2) # 对查询图像的四个角点进行透视变换 dst = cv.perspectiveTransform(pts,M) # 在训练图像上绘制匹配区域的多边形 img2_with_box = img2.copy() img2_with_box = cv.polylines(img2_with_box,[np.int32(dst)],True,255,3, cv.LINE_AA) else: print(f"Not enough matches are found - {len(good)}/{MIN_MATCH_COUNT}") matchesMask = None img2_with_box = img2 # 设置绘制参数 draw_params = dict(matchColor = (0,255,0), singlePointColor = None, matchesMask = matchesMask, flags = 2) # 绘制匹配结果 img3 = cv.drawMatches(img1,kp1,img2_with_box,kp2,good,None,**draw_params) # 显示结果 plt.imshow(img3, 'gray') plt.show()

效果如下

这个效果更加明显,效果更好

三最后一语

其实我也很想继续写下去,但是我想你们也应该感觉过迷茫,不知道该如何使用,不知道学习这个对比赛或是项目有这个那么用,我也感受到了,所以为了尽快到达比赛水平,我将采取学用结合,我会买一块k230的板子进行研究,大家一起努力吧。

我祝福你
愿你经得起长久的离别
种种考验、古凶未卜的折磨
漫长的昏暗的路程
依照你的意愿安排生活吧
只要你觉得好就行
——帕斯捷尔纳克《日瓦戈医生》

感谢观看,定会再见,共勉!!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/17 23:33:21

创意工作者必备:探索3D资源与图片搜索的完美融合工具

创意工作者必备&#xff1a;探索3D资源与图片搜索的完美融合工具 【免费下载链接】search-photos-by-model-tool https://x6ud.github.io 项目地址: https://gitcode.com/gh_mirrors/se/search-photos-by-model-tool 在这个视觉创意的黄金时代&#xff0c;找到合适的工具…

作者头像 李华
网站建设 2026/2/15 22:30:36

ConcurrentHashMap size () 流程

要彻底理解 ConcurrentHashMap 的 size() 流程&#xff0c;核心是抓住不同JDK版本的设计差异&#xff08;JDK7基于分段锁&#xff0c;JDK8基于无锁计数&#xff09;&#xff0c;以及「并发下计数准确性」与「性能」的平衡思路。以下是分版本的详细拆解&#xff1a; 一、核心背景…

作者头像 李华
网站建设 2026/2/8 10:39:35

IndraDB:Rust语言构建的高性能图数据库

IndraDB&#xff1a;Rust语言构建的高性能图数据库 【免费下载链接】indradb A graph database written in rust 项目地址: https://gitcode.com/gh_mirrors/in/indradb 在当今数据驱动的时代&#xff0c;图数据库正成为处理复杂关系数据的首选工具。IndraDB作为一款用R…

作者头像 李华
网站建设 2026/2/16 6:24:43

Abracadabra魔曰:终极汉字加密工具完全指南

在现代数字安全领域&#xff0c;一款名为Abracadabra(魔曰)的开源加密工具正在重新定义文本保护的概念。这款工具巧妙地将传统文言文美学与现代加密技术相结合&#xff0c;为用户提供前所未有的安全体验。 【免费下载链接】Abracadabra Abracadabra 魔曰&#xff0c;下一代文本…

作者头像 李华
网站建设 2026/2/11 17:14:19

基于Dlib的疲劳驾驶检测系统:从零开始的终极安装使用指南

基于Dlib的疲劳驾驶检测系统&#xff1a;从零开始的终极安装使用指南 【免费下载链接】Fatigue-Driving-Detection-Based-on-Dlib 项目地址: https://gitcode.com/gh_mirrors/fa/Fatigue-Driving-Detection-Based-on-Dlib 想要实现精准的疲劳驾驶实时检测吗&#xff1f…

作者头像 李华