特征检测应用场景
(1)图像搜索
(2)拼图游戏
(3)图像拼接
将ABCDEF可以大致分为三组,AB,CD,EF 为什么这样分组呢?
首先对于AB来说,AB难以确认,正如A所示,整张图片在上半部分都是以蓝色为主体,同理B也是这样。
其次对于CD来说,观察CD应该属于边沿,那整张图片边沿的部分相较于第一种情况比较特殊。
最后对于EF来说,观察EF是属于角,相较于第二种情况就更特殊了。
所以通过这样的例子我们可以知道,平坦部分很难找到他在原图中的位置;边缘相较平坦好找一些,但是不能一下确定;角点可以一下就找到在原图中的位置。
什么是特征?图像特征就是指有意义的图像区域,具有独特性,易识别性(如角点,斑点,高密度区域)
Harris角点
Harris角点相关知识
(1)理解哈里斯角点检测原理
正如上面三张图片所示:可以把每张图片中的矩形看成一个搜索的窗口,窗口会移动
对于第一张图片,检测窗口在上下左右移动时,周围没有出现像素的变化,说明这个是一个平坦的区域。
对于第二张图片,检测窗口在上下移动时,没有出现像素变化,但是左右移动时,出现了像素的变化,说明这个是在一个边沿上。
对于第三张图片,检测窗口上下左右移动时都会出现像素的变化,说明这个是在一个角点上。
(2)Harris角点检测API
cornerHarris(img,dst,blockSize,ksize,k)
img:对哪张图进行角点检测
dst:如果检测出角点,会有一个输出矩阵
blockSize:检测窗口的大小(越大越敏感)
ksize:Sober的卷积核
K:权重系数,经验值,一般取0.02-0.04之间
Harris角点检测实战
import cv2
import numpy as np
blockSize = 2
ksize = 3
k = 0.04
img = cv2.imread('chess.png')
# 由于connerHarris需要识别一个灰度图,需要将原bgr的图变成gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Harris角点检测
dst = cv2.cornerHarris(gray, blockSize, ksize, k)
# Harris角点显示
img[dst > 0.01 * dst.max()] = [0, 0, 255]
cv2.imshow('harris', img)
cv2.waitKey(0)
原图处理后的图
发现在原图的角点画出了红色的点(0,0,255)
Shi-Tomasi角点检测(来自harris,进行简单微调)
Shi-Tomasi角点检测相关知识
(1)Shi-Tomasi相较于harris改进了什么?
cornerHarris(img,dst,blockSize,ksize,k)这个API中需要设置k值,k值与harris的稳定性有关,但是k是经验值,不好设置最佳值,所以Shi-Tomasi改进了这一点。
(2)Shi-Tomasi角点检测API
goodFeaturesToTrack(img,maxCorners,……)
img:对哪张图进行角点检测
maxCorners:角点的个数最大数,值为0表示无限制
qualitLevel:小于1.0的正数,一般在0.01-0.1之间 (这个的任务对应harris角点检测如下代码)
dst > 0.01 * dst.max()
minDistance:角之间最小欧式距离,忽略小于此距离的点(在角很密集的情况下且忽略部分时可以使用)
mask:感兴趣的区域(指定区域,如果没有指定就是全局)
blockSize:检测窗口的大小(越大越敏感)
useHarrisDetector:是否使用harris算法(True 使用,默认False)
k:默认是0.04(在useHarrisDetector是True时)
Shi-Tomasi角点检测实战
import cv2
import numpy as np
maxCorners = 1000
ql = 0.01
minDistance = 10
img = cv2.imread('chess.png')
# 由于connerHarris需要识别一个灰度图,需要将原bgr的图变成gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Shi-Tomasi绘制角点角点检测
corners = cv2.goodFeaturesToTrack(gray, maxCorners, ql, minDistance) # 获得的corners是32位的
# 由于坐标中的都是整型,不是浮点型 需要进行转换
corners = np.intp(corners)
# Shi-Tomasi绘制角点
for i in corners:
x, y = i.ravel() # 由于corners存放的是多为数组,要变成一维的
cv2.circle(img, (x, y), 3, (255, 0, 0), -1)
cv2.imshow('Tomasi', img)
cv2.waitKey(0)
原图处理后的图
在原图的角点区域画出了蓝色(255,0,0)的实心圆
SIFT特征检测
SIFT特征相关知识
(1)与Harris的区别:harris角点具有旋转不变的特性(就是角点进行旋转后,依旧可以在原图中找到角点) harris角点进过缩放后就有可能不是角点了(SIFT可以解决这个问题)
左边的曲线放大到右边的曲线,harris检测不到了
(2)使用SIFT的步骤:创建SIFT对象;进行检测,kp=sift.detect(img,……);绘制关键点,drawKeypoints(gray,kp,img)
(3)sift关键点和描述子:关键点有位置,大小和方向信息;关键点的描述子,记录了关键点周围对其有贡献的像素点的一组向量值,其不受仿射变换,光照变换等影响。
(4)计算描述子API:kp,des=sift.compute(img,kp) ;为了进行特征匹配
(5)升级计算描述子的方法:由于4中计算描述子需要先计算出kp关键子,所以相对比较麻烦。同时计算关键子和描述子API:kp,des=sift.detectAndCompute(img……)
SIFT特征实战
import cv2
import numpy as np
# 读文件
img = cv2.imread('chess.png')
# 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 创建sift对象
sift = cv2.SIFT_create()
# 进行关键点检测
kp = sift.detect(gray, None)
# 绘制keypoints
cv2.drawKeypoints(gray, kp, img)
cv2.imshow('img', img)
cv2.waitKey(0)
原图处理后图像
kp, des = sift.detectAndCompute(gray, None)
print(des)
输出图像还是相同的,因为都是关键子显示。
SURF特征检测
SURF特征检测相关知识
(1)SIFT最大的问题是速度慢,因此才会有了SURF。
(2)使用步骤:创建SURF对象;进行检测,kp,des=surf.detectAndCompute(img……);绘制关键点,drawKeypoints(gray,kp,img)
SURF特征检测实战
import cv2
import numpy as np
# 读文件
img = cv2.imread('chess.png')
# 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 创建surf对象
surf = cv2.SURF_create()
kp, des = surf.detectAndCompute(gray, None)
print(des)
# 绘制keypoints
cv2.drawKeypoints(gray, kp, img)
cv2.imshow('img', img)
cv2.waitKey(0)
ORB特征检测(GOOD)
ORB特征检测相关知识
(1)ORB可以做到实时检测(速度相较于sift,surf更快,但是准确性下降)
(2)ORB= Oriented FAST+Rotated BRIEF
(3)使用步骤:创建对象 orb=cv2.ORB_create();kp,des=orb.detectAndCompute(img,mask)
ORB特征检测实战
原图处理后图像
暴力特征匹配
暴力特征匹配相关知识
(1)暴力特征匹配原理:使用第一张图的每个特征的描述子与第二组中的所有特征描述子进行匹配,计算他们之间的差距,然后将最接近的一个匹配返回。
(2)暴力特征匹配步骤:创建匹配器,BFMatcher(normType,crossCheck);进行特征匹配,bf.match(des1,des2)。绘制匹配点,cv2.drawMatches(img1,kp1,img2,kp2,……)
(3)normType:NORM_L1(与SIFT相关),NORM_L2(与SURF相关,默认),HAMMING1
crossCheck:是否进行交叉匹配,默认为False
暴力特征匹配实战
import cv2
import numpy as np
# 读文件
img1 = cv2.imread('opencv_search.png')
img2 = cv2.imread('opencv_orig.png')
# 灰度化
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
# 创建sift对象
sift = cv2.SIFT_create()
# 特征点和描述子的检测
kp1, des1 = sift.detectAndCompute(g1, None)
kp2, des2 = sift.detectAndCompute(g2, None)
# 创建匹配器
bf = cv2.BFMatcher(cv2.NORM_L1, True)
# 特征匹配
match = bf.match(des1, des2)
img3 = cv2.drawMatches(img1, kp1, img2, kp2, match, None)
cv2.imshow('img3', img3)
cv2.waitKey(0)
FLANN特征匹配
FLANN特征匹配相关知识
(1)在进行批量特征匹配时,flann速度更快
(2)由于使用的是邻近近似值,所以精度较差
(3)FLANN特征匹配步骤:创建匹配器,FlannBasedMatcher;进行特征匹配,flann.match/knn.Match。绘制匹配点,cv2.drawMatches/drawMatchesKnn
(4) FlannBasedMatcher:index_params字典,匹配算法KDTREE(使用SIFT,SURF时),LSH(使用ORD);search_params字典,指定KDTREE算法中遍历树的次数。
(5)KDTREE:index_params=dict(algorithm=FLANN_INDEX_KDTREE,trees=5)
(6)searc_params=dict(checks=50)
(7)knn.Match:参数为SIFT,SURF,ORD等计算的描述子,k表示第一幅图和第二幅图欧式距离最近的前k个关键点,最后返回的时DMatch对象。
(8)DMatch的内容:distance,描述子之间的距离,值越低越好;queryIdx,第一个图像的描述子的索引值;trainIdx,第二个图像的描述子的索引值;imgIdx,第二个图的索引值。
(9)drawMatchesKnn:搜索im,kp;匹配图img,kp;match()方法返回的匹配结果。
FLANN特征匹配实战
import cv2
import numpy as np
img1 = cv2.imread('opencv_search.png')
img2 = cv2.imread('opencv_orig.png')
# 创建sift特征检测器
sift = cv2.SIFT_create()
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
# 计算描述子和特征点
kp1, des1 = sift.detectAndCompute(g1, None)
kp2, des2 = sift.detectAndCompute(g2, None)
# 创建匹配器
index_params = dict(algorithm=1, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, index_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)
ret = cv2.drawMatchesKnn(img1, kp1, img2, kp2, [good], None)
cv2.imshow('result', ret)
cv2.waitKey(0)
图像查找
特征匹配+单应性矩阵
(1)单应性矩阵:
从图像1,图像2和原始的图可以经过运算相互推导
(2)应用:pdf扫描,现实生活中的广告牌进行替换内容。
图像查找实战(FLANN)
# 基于flann特征匹配进行图像查找
import cv2
import numpy as np
img1 = cv2.imread('opencv_search.png')
img2 = cv2.imread('opencv_orig.png')
# 创建sift特征检测器
sift = cv2.SIFT_create()
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
# 计算描述子和特征点
kp1, des1 = sift.detectAndCompute(g1, None)
kp2, des2 = sift.detectAndCompute(g2, None)
# 创建匹配器
index_params = dict(algorithm=1, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, index_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:
# 查找单应性矩阵
# 从匹配点中获取 (-1,1,2)无数行,每一行有一个元素,每个元素由两个子元素构成
srcPts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
desPts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
# 获取到单应性矩阵
# srcPts原pts desPts目的pts cv2.RANSAC对错误的匹配点进行一个过滤
H, _ = cv2.findHomography(srcPts, desPts, 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) # pts是要搜索图的四个角点
# 获取到的图框起来
cv2.polylines(img2, [np.int32(dst)], True, (0, 0, 255))
else:
print('the number of good is less than 4')
exit()
ret = cv2.drawMatchesKnn(img1, kp1, img2, kp2, [good], None)
cv2.imshow('result', ret)
cv2.waitKey(0)
结果: