一、特征检测
应用场景
- 图像搜索:通过提取图像的特征点进行搜索,提高搜索效率。
- 拼图游戏:通过特征查找,减少搜索范围,提高拼图效率。
- 图片拼接:通过特征点检测,实现多张图片的拼接,例如生成全景图像。
拼图方法
- 寻找特征
- 特征是唯一的
- 可追踪的
- 能比较的
图像特征
- 图像特征是有意义的图像区域,具有独特性和易识别性。
- 角点、斑点及高密度区域都是明显的特征。
- 特征必须是独立、可识别和可比较的。
角点
在特征中,最重要的就是角点
- 灰度梯度的最大值对应的像素
- 两条线的交点
- 极值点(一阶导数的最大值,但二阶导数为零)
一、Harris角点检测 cornerHarris
Harris角点检测是一种常用的角点检测方法,将角点检测分为三种情况:平坦区域、边沿和角点。
- 光滑地区,无论向哪里移动,衡量系数不变
- 边缘地址,垂直边缘移动时,衡量系统变化剧烈
- 在交点处,往那个方向移动,衡量系统都变化剧烈
import cv2
import numpy as npblockSize = 2 # 检测窗口大小
ksize = 3 # Sobel的卷积核
k = 0.04 # 权重系数,经验值,一般取 0.02 ~ 0.04img = cv2.imread('img/chess.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
dst = cv2.cornerHarris(gray, blockSize, ksize, k)## Harris角点的展示
img[dst > 0.01 * dst.max()] = [0, 0, 255]cv2.imshow('harris', img)
cv2.waitKey(0)
二、Shi-Tomasi角点检测 goodFeaturesToTrack
Shi-Tomasi是Harris角点检测的改进
Harris角点检测的稳定性和k有关,而k是个经验值,不好设定最佳值。
import cv2
import numpy as np# Shi-Tomasi
maxCorners = 0
ql = 0.01
minDistance = 20img = cv2.imread('img/chess.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
corners = cv2.goodFeaturesToTrack(gray, maxCorners, ql, minDistance)
corners = corners.astype(np.int32)# 角点的展示
for i in corners:x, y = i.ravel()cv2.circle(img, (x,y), 3, (0, 0, 255), -1)cv2.imshow('Shi-Tomasi', img)
cv2.waitKey(0)
三、SIFT
SIFT关键点检测 detect
Harris角点的判断会受到图片缩放影响。
例如下图中,原本的角点放大后,就检测不到了
import cv2
import numpy as np# 读文件
img = cv2.imread('img/chess.jpg')# 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 创建SIFT对象
sift = cv2.xfeatures2d.SIFT_create()# 进行检测
kp = sift.detect(gray, None)# 绘制keypoints
cv2.drawKeypoints(gray, kp, img)cv2.imshow('SIFT', img)
cv2.waitKey(0)
计算描述子 detectAndCompute
关键点:位置、大小和方向
关键点描述子:记录关键点周围对其有贡献的像素点的一组向量值,其不受仿射变换、光照变换等影响
import cv2
import numpy as np# 读文件
img = cv2.imread('img/chess.jpg')# 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 创建SIFT对象
sift = cv2.xfeatures2d.SIFT_create()# 进行检测
kp, des = sift.detectAndCompute(gray, None)
print(des[0])# 绘制keypoints
cv2.drawKeypoints(gray, kp, img)cv2.imshow('SIFT', img)
cv2.waitKey(0)
四、SURF
SIFT最大的问题是速度慢,因此才有SURF。
如下代码运行会报错,因为这个算法是专利算法,要配置比较麻烦。
这里就不折腾了,只把教程中的代码贴在下面。
import cv2
import numpy as np# 读文件
img = cv2.imread('img/chess.jpg')# 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 创建SURF对象
surf = cv2.xfeatures2d.SURF_create()# 进行检测
kp, des = surf.detectAndCompute(gray, None)
print(des[0])# 绘制keypoints
cv2.drawKeypoints(gray, kp, img)cv2.imshow('SURF', img)
cv2.waitKey(0)
五、ORB特征检测
ORB = Oriented FAST + Rotated BRIEF
ORB可以做到实时检测
import cv2
import numpy as np# 读文件
img = cv2.imread('img/chess.jpg')# 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 创建ORB对象
orb = cv2.ORB_create()# 进行检测
kp, des = orb.detectAndCompute(gray, None)
print(des[0])# 绘制keypoints
cv2.drawKeypoints(gray, kp, img)cv2.imshow('ORB', img)
cv2.waitKey(0)
二、特征匹配
暴力特征匹配
使用第一组中的每个特征的描述子,与第二组中的所有特征描述子进行匹配。
计算它们之间的差距,然后将最接近的一个匹配返回。
import cv2
import numpy as np# 读文件
img1 = cv2.imread('img/dog2.jpg')
img2 = cv2.imread('img/dog.jpg')# 灰度化
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)# 创建SIFT对象
sift = cv2.xfeatures2d.SIFT_create()# 进行特征点检测
kp1, des1 = sift.detectAndCompute(gray1, None)
kp2, des2 = sift.detectAndCompute(gray2, None)# 创建匹配器
bf = cv2.BFMatcher(cv2.NORM_L1)# 匹配
match = bf.match(des1, des2)# 绘制
img3 = cv2.drawMatches(img1, kp1, img2, kp2, match, None)cv2.imshow('img3', img3)
cv2.waitKey(0)
FLANN 特征匹配
进行批量特征匹配时,FLANN速度更快
由于使用邻近近似值,精度较差
import cv2
import numpy as np# 读文件
img1 = cv2.imread('img/dog2.jpg') # search
img2 = cv2.imread('img/dog.jpg') # origin# 灰度化
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)# 创建SIFT特征检测器
sift = cv2.xfeatures2d.SIFT_create()# 进行特征点检测
kp1, des1 = sift.detectAndCompute(gray1, None)
kp2, des2 = sift.detectAndCompute(gray2, None)# 创建匹配器
index_params = dict(algorithm=1, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)# 对描述子进行匹配计算
matchs = flann.knnMatch(des1, des2, k=2)good = []
for i, (m, n) in enumerate(matchs):if m.distance < 0.7 * n.distance:good.append(m)# 绘制
img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, [good], None)cv2.imshow('img3', img3)
cv2.waitKey(0)
三、图像查找
import cv2
import numpy as np# 读文件
img1 = cv2.imread('img/dog2.jpg') # search
img2 = cv2.imread('img/dog.jpg') # origin# 灰度化
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)# 创建SIFT特征检测器
sift = cv2.xfeatures2d.SIFT_create()# 进行特征点检测
kp1, des1 = sift.detectAndCompute(gray1, None)
kp2, des2 = sift.detectAndCompute(gray2, None)# 创建匹配器
index_params = dict(algorithm=1, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)# 对描述子进行匹配计算
matchs = flann.knnMatch(des1, des2, k=2)good = []
for i, (m, n) in enumerate(matchs):if m.distance < 0.7 * n.distance:good.append(m)if len(good) >= 4:srcPts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)dstPts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)H, _ = cv2.findHomography(srcPts, dstPts, cv2.RANSAC, 5.0)h, w = img1.shape[:2]pts = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1, 1, 2)dst = cv2.perspectiveTransform(pts, H)cv2.polylines(img2, [np.int32(dst)], True, (0, 0, 255))
else:print('the number of good is less than 4.')exit()# 绘制
img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, [good], None)cv2.imshow('img3', img3)
cv2.waitKey(0)