【OpenCV 入门到精通 09】特征检测与匹配:从 Harris 到 ORB 图像配准
标签 :
OpenCV特征检测ORBSIFT特征匹配Harris难度 :⭐⭐⭐ | 阅读时长 :约 70 分钟 | 系列 :第 09/12 集

写在前面
「怎么判断两张图是不是同一个场景?」「怎么把一张小图贴到另一张大图的正确位置?」答案在于 特征点 ------图像中稳定、可区分的「关键点」。本集对应官方 第5章 特征检测与描述。
本集学习目标
- 理解特征点与描述子的概念
- 使用 Harris 角点和 ORB 特征检测
- 进行特征匹配和单应性变换
- 完成图像拼接/物体查找实战
文章目录
- [【OpenCV 入门到精通 09】特征检测与匹配:从 Harris 到 ORB 图像配准](#【OpenCV 入门到精通 09】特征检测与匹配:从 Harris 到 ORB 图像配准)
9.1 什么是特征
好的特征应具备
| 特性 | 说明 |
|---|---|
| 可重复性 | 同一位置在不同条件下能检测到 |
| 独特性 | 能与其他区域区分 |
| 局部性 | 对遮挡有一定鲁棒性 |
| 可量化 | 可用描述子向量表示 |
特征 = 关键点 + 描述子
关键点 (KeyPoint): 位置 (x,y)、尺度、方向
描述子 (Descriptor): 128/256/512 维向量,描述周围像素模式
9.2 Harris 角点检测
python
import cv2
import numpy as np
img = cv2.imread("chessboard.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
# Harris 角点
dst = cv2.cornerHarris(gray, blockSize=2, ksize=3, k=0.04)
dst = cv2.dilate(dst, None)
img[dst > 0.01 * dst.max()] = [0, 0, 255] # 红色标记角点
cv2.imshow("Harris", img)
cv2.waitKey(0)
| 参数 | 说明 |
|---|---|
| blockSize | 邻域大小 |
| ksize | Sobel 核大小 |
| k | Harris 参数,通常 0.04~0.06 |
9.3 ORB 特征检测
ORB = Oriented FAST and Rotated BRIEF,免费开源,速度快。
SIFT/SURF 需
opencv-contrib-python;ORB 在标准版即可用。
python
import cv2
img = cv2.imread("scene.jpg", 0)
orb = cv2.ORB_create(nfeatures=500)
keypoints, descriptors = orb.detectAndCompute(img, None)
# 绘制关键点
img_kp = cv2.drawKeypoints(img, keypoints, None, color=(0,255,0),
flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imshow("ORB", img_kp)
cv2.waitKey(0)
python
# SIFT(需 contrib 版)
sift = cv2.SIFT_create()
kp, des = sift.detectAndCompute(img, None)
9.4 特征匹配
python
import cv2
img1 = cv2.imread("object.jpg", 0)
img2 = cv2.imread("scene.jpg", 0)
orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
# BF 暴力匹配
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)
matches = sorted(matches, key=lambda x: x.distance)
# 绘制前 20 个最佳匹配
img_match = cv2.drawMatches(img1, kp1, img2, kp2, matches[:20], None,
flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
cv2.imshow("Matches", img_match)
cv2.waitKey(0)
匹配器选择
| 匹配器 | 距离度量 | 适用 |
|---|---|---|
| BFMatcher + HAMMING | 汉明距离 | ORB, BRIEF |
| BFMatcher + L2 | 欧氏距离 | SIFT, SURF |
| FlannBasedMatcher | 近似最近邻 | 大量特征时更快 |
比率测试(过滤差匹配)
python
bf = cv2.BFMatcher()
matches = bf.knnMatch(des1, des2, k=2)
good = []
for m, n in matches:
if m.distance < 0.75 * n.distance:
good.append(m)
9.5 单应性变换与物体查找
在场景图中定位模板物体:
python
import cv2
import numpy as np
MIN_MATCH = 10
if len(good) > MIN_MATCH:
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)
# 计算单应性矩阵
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
h, w = img1.shape
pts = np.float32([[0,0],[0,h-1],[w-1,h-1],[w-1,0]]).reshape(-1,1,2)
dst = cv2.perspectiveTransform(pts, M)
scene_color = cv2.imread("scene.jpg")
cv2.polylines(scene_color, [np.int32(dst)], True, (0,255,0), 3)
cv2.imshow("Found", scene_color)
cv2.waitKey(0)
9.6 特征算法对比
| 算法 | 速度 | 精度 | 专利 | 包 |
|---|---|---|---|---|
| Harris | 快 | 中 | 无 | 标准 |
| FAST | 很快 | 中 | 无 | 标准/contrib |
| ORB | 快 | 较好 | 无 | 标准 |
| BRIEF | 很快 | 中 | 无 | contrib |
| SIFT | 慢 | 高 | 已过期 | contrib |
| SURF | 中 | 高 | 已过期 | contrib |
💡 推荐:学习用 ORB;高精度用 SIFT(contrib)。
9.7 踩坑指南
| 坑 | 解决 |
|---|---|
| SIFT 找不到 | 安装 opencv-contrib-python |
| 匹配太少 | 增加 nfeatures;换更好的图 |
| findHomography 失败 | 确保 good matches > 10 |
| 描述子为 None | 图像太简单,无特征点 |
9.8 本集小结
| 步骤 | API |
|---|---|
| 检测 | ORB_create().detectAndCompute() |
| 匹配 | BFMatcher().match() / knnMatch() |
| 筛选 | 比率测试 0.75 |
| 定位 | findHomography() + perspectiveTransform() |
| ← 第08集 | 第10集:视频分析 → |
|---|