一、问题:怎么判断"两张图是同一个东西"
"小陈,拍照搜书的核心问题:你拍一张封面,怎么在 5 万本书里找到最像的那一本?"老周问。
"……像素逐个比对?"
"天真。你拍的封面和数据库里的原图,光线不同、角度不同、大小不同、还有透视变形。 逐像素比对,一样都匹配不上。我们要的是——不管怎么变,都能认出来的'不变特征'。"
图像匹配的难点:
① 尺度变化:近拍/远拍,大小不一样
② 旋转变化:书歪了
③ 光照变化:暗/亮/阴影
④ 视角变化:有一点透视
⑤ 遮挡:手指挡了一角
解法:提取"特征点"(关键点 Keypoint)
= 图像里"与众不同、稳定可重复"的位置
角点、边缘交点、纹理斑块……
每个特征点有一个"描述子"(Desriptor)= 它的"指纹"
→ 两张图有大量"相似指纹" → 是同一物体!
"特征提取 = 给图像的'标志性位置'做'指纹'——不管图怎么变,同一物体的特征指纹不会变。这是传统视觉的基石。"
二、角点检测:什么是"特征点"
"先看最简单的特征点——角点(Corner)。"老周说:
角点(Corner Point):
图像里"两个方向亮度都剧烈变化"的位置
(如书角、文字拐角、窗户角)
为什么角点是好的特征?
平移后还能找到(位置独特)
旋转后还是角点(性质不变)
光照变化影响小(局部对比)
Harris 角点检测(经典):
cv2.cornerHarris(gray, blockSize, ksize, k)
→ 每个像素一个"角点响应值"
→ 响应值大的 = 角点
Shi-Tomasi 角点(改进版,OpenCV 常用):
corners = cv2.goodFeaturesToTrack(gray, maxCorners, qualityLevel, minDistance)
→ 找出 N 个"质量最高"的角点
import cv2
import numpy as np
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Harris 角点检测
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
img_corners = img.copy()
img_corners[dst > 0.01 * dst.max()] = [0, 0, 255] # 角点标红
# Shi-Tomasi 角点(更常用)
corners = cv2.goodFeaturesToTrack(gray, maxCorners=100,
qualityLevel=0.01, minDistance=10)
for c in corners:
x, y = c.ravel()
cv2.circle(img, (int(x), int(y)), 3, (0, 255, 0), -1)
"角点是特征的'入门款'——它告诉你'哪里值得关注'。但角点太'弱',尺度一变大就找不到了。要更强,上 SIFT/ORB。"
三、SIFT:尺度不变特征变换(经典王者)
"真正让视觉界沸腾的是 SIFT(尺度不变特征变换)——2004 年 Lowe 提出,能同时应对尺度、旋转、光照变化。"老周说:
SIFT(Scale-Invariant Feature Transform):
核心思想:在不同尺度(放大/缩小)下都能找到稳定的特征点
每个特征点有 128 维描述子(向量)= 它的"DNA 指纹"
为什么强:
尺度不变:近拍远拍都能匹配(尺度空间金字塔)
旋转不变:旋转后描述子不变
光照鲁棒:基于梯度方向,不受亮度整体变化影响
→ 教科书级的经典算法
注意:SIFT 有专利(已过期),OpenCV 需要 contrib 模块
OpenCV:
sift = cv2.SIFT_create()
kp, des = sift.detectAndCompute(gray, None)
# kp = 关键点列表;des = 描述子矩阵 (N×128)
四、ORB:又快又免费的"实用派"
"SIFT 强但慢。移动端要快,用 ORB。"老周说:
ORB(Oriented FAST and Rotated BRIEF):
结合了 FAST(快速角点检测)+ BRIEF(二进制描述子)
特点:
快(比 SIFT 快 1-2 个数量级)
免费(无专利)
描述子是二进制的(匹配用汉明距离,超快)
缺点:尺度不变性不如 SIFT(可配合金字塔改善)
OpenCV:
orb = cv2.ORB_create(nfeatures=500)
kp, des = orb.detectAndCompute(gray, None)
选型:
要精度(匹配质量)→ SIFT
要速度(实时/移动端)→ ORB
现在很多场景已被深度学习替代,但传统特征
在"图像拼接、SLAM、纹理匹配"仍有价值
五、特征匹配:找相同指纹
"有了特征点,怎么判断两张图相似?特征匹配(Feature Matching)。"老周说:
import cv2
# 两张图提取特征
orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
# 暴力匹配(ORB 用汉明距离)
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)
# 按距离排序(距离小 = 相似)
matches = sorted(matches, key=lambda m: m.distance)
# 取前 N 个好匹配画出来
good = matches[:50]
result = cv2.drawMatches(img1, kp1, img2, kp2, good, None)
# FLANN 匹配(SIFT 大数据量用,更快)
# flann = cv2.FlannBasedMatcher(dict(algorithm=1, trees=5), {})
# matches = flann.knnMatch(des1, des2, k=2)
匹配评估:
匹配数量:越多越好
匹配质量:平均距离越小越好(指纹越接近)
比率测试(Lowe's ratio test):
最匹配距离 / 次匹配距离 < 0.7 → 保留(更可靠)
→ 过滤错误匹配(误匹配的克星)
怎么判断"是同一本书"?
匹配数 > 阈值 且 质量好 → 判定同一封面
云间书店:拍照封面 vs 数据库封面做匹配
→ 匹配分最高的书 = 搜索结果
"特征匹配 = 比指纹——两张图的特征描述子大量相似,就是同一个东西。比率测试是过滤误匹配的关键技巧,面试常考。"
六、图像拼接与全景:特征匹配的经典应用
"特征匹配最经典的应用之一:图像拼接(Image Stitching)——把多张照片拼成全景图。"老周说:
图像拼接流程:
① 拍多张有重叠的图(云间书店书架全景)
② 提取特征(SIFT/ORB)
③ 匹配相邻图特征
④ 计算变换矩阵(Homography,单应性矩阵)
⑤ 图像变换对齐 + 融合拼接
⑥ 输出全景图
OpenCV 一行实现:
stitcher = cv2.Stitcher_create()
status, pano = stitcher.stitch([img1, img2, img3])
# status 0 = 成功,pano = 全景图
背后的数学:单应性变换
找到 4 对以上匹配点 → 解出 8 参数变换矩阵
→ 一张图可以"变形"到另一张图的角度
应用:手机全景拍照、地图街景拼接、文档拼接
"图像拼接是'特征提取+匹配'的综合大作业——手机的全景拍照,就是这个流程。看懂它,你就懂了传统视觉的'经典三件套':特征、匹配、变换。"
七、章末:老周的第四章总结
第三层:特征提取与匹配
├── 问题:图像会变(尺度/旋转/光照/视角),怎么认得出
├── 特征点 = 图像的"标志位置"(角点/斑块)
├── Harris / Shi-Tomasi:角点检测(入门款)
├── SIFT:尺度不变王者(128维描述子,慢但准)
├── ORB:快速免费(二进制描述子,实时首选)
├── 特征匹配:比指纹(汉明距离 + 比率测试过滤误匹配)
├── 应用:图像拼接(特征+匹配+单应性变换=全景)
└── 现代:很多场景被深度学习替代,但经典仍有用武之地
第五层(预告):传统机器学习视觉 —— 用 SVM 给图书分类
"小陈,你现在能给图像'提取指纹'、'比对指纹'了——这是传统视觉的看家本领。"老周说,"但光有特征还不够:拍照搜书,我们要的是'这张封面属于哪个类别(哪本书)'。下一章,我们进入机器学习——用 HOG 特征 + SVM 分类器,让机器学会'看图归类'。这是深度学习的'前辈',理解了它,后面的 CNN 会好懂很多。"