图像特征提取与描述
import cv2
import numpy as np
import matplotlib.pyplot as plt
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
'''
我们需要寻找图像的一些唯一的特征,这些特征要适于被跟踪,容易被比较。
比如我们在图像中找一些区域,无论你向那个方向移动这些区域的变化都很大。
'''
'''
角:两个特征值都大,且近似相等,自相关函数在所有方向都增大
边:一个特征值大,另一个特征值小,自相关函数值在某一方向上大,在其他方向上小
平展区域:两个特征值都打,且近似相等,自相关函数在所有方向都增大
'''
# OpenCV中的Harris角点检测
'''
cv2.cornerHarris()
img: 数据类型为float32的输入图像
blockSize:角点检测中指定区域的大小
ksize:Sobel求导中使用的窗口大小
k: 角点检测方程中的自由参数,取值参数为[0.04,0.06]
'''
img = cv2.imread('chessboard.jpg')
print('image.shape', img.shape)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
# 输入图像必须是float32,最后一个参数叜0.04到0.05之间
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
print('dst.shape', dst.shape)
# 膨胀,把角点放大
dst = cv2.dilate(dst, None, iterations=2)
img[dst > 0.01 * dst.max()] = [255, 0, 0]
cv_show('dst', img)
# 亚像素级精确度的角点
img = cv2.imread('chessboard.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
dst = cv2.dilate(dst, None)
ret, dst = cv2.threshold(dst, 0.01 * dst.max(), 255, 0)
dst = np.int8(dst)
ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
# 返回值由角点坐标组成的一个数组(而非图像)
corners = cv2.cornerSubPix(gray, np.float32(centroids), (5, 5), (-1, -1), criteria)
'''
np.float32(centroids): 是检测到的角点,
(5, 5):计算亚像素角点时考虑的区域的大小
(-1, -1):总是具有较小的范围,通常忽略(-1, -1)
criteria:计算亚像素时停止迭代的标准
cv2.TERM_CRITERIA_EPS 角带你位置变化的最小值达到停止迭代
cv2.TERM_CRITERIA_MAX_ITER 迭代次数达到最大次数时停用
可以共用可以单用
'''
# np.int0 可以用来省略小数点后面的数字(四舍五入)
corners = np.int0(corners)
for i in corners:
x, y = i.ravel()
cv2.circle(img, (x, y), 3, 255, -1)
cv_show('name', img)
# Shi-Tomasi 角点检测 & 适合于跟踪的图像特征
'''
cv2.goodFeaturesToTrack()
这个函数可以帮我们使用 Shi-Tomasi 方法获取图像中 N 个最好的角点
(如果你愿意的话也可以通过改变参数来使用 Harris 角点检测算法)。
通常情况下,输入的应该是灰度图像。然后确定你想要检测到的角点数目。
再设置角点的质量水平, 0到 1 之间。它代表了角点的最低质量,
低于这个数的所有角点都会被忽略。最后在设置两个角点之间的最短欧式距离
'''
img = cv2.imread('chessboard.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
conners = cv2.goodFeaturesToTrack(gray, 30, 0.01, 10)
'''
30: 最大角点数目
0.01:质量水平系数(小于1.0的正数,一般在0.01到0.1之间)
10: 最小距离 小于这个距离的都忽略
'''
# 返回的结果是[[]] 两层括号的数组
corners = np.int0(conners)
for i in corners:
x, y = i.ravel()
cv2.circle(img, (x, y), 3, 255, -1)
cv_show('img', img)
'''
即使图片发生了旋转,角点还是角点,如果图片发生了缩放,角点就不是角点了。
SIFT算法使用高斯差分算子DoG来对LoG做近似。
'''
'''
我们可以通过减少采样(只取奇数行或奇数列)来构成一组图像尺寸(1, 0.5, 0.25)
不同的金字塔,然后对这一组图像中的每一张图像使用具有不同的方差的高斯卷积核
构建出不同分辨率的图像金字塔(不同的尺度空间)。DoG就是这组具有不通过
分辨率的图像金字塔中相邻的两层之间的差值。
在DoG搞定后,就可以在不同的尺度空间和2D平面中搜索局部最大值了。对于图像中的
一个像素点而言,它需要与自己周围8邻域,以及尺度空间中上下两层中的相邻的18(2*9)
个点相比。如果是局部最大值,它就可能是一个关键点。
'''
'''
一旦找到关键点,我们就对它们进行修正从而得到更为准确的结果。作者使用尺度空间的
泰勒级数展开来获得极值的准确位置,如果极值点的灰度值小于阈值(0.03)就会被忽略
要先去除边界。
'''
'''
现在我们要为每一个关键点赋予一个反向参数,这样它才会具有旋转不变性,获取关键点
(所在尺度空间)的邻域,然后计算这个区域的梯度级和方向。
根据计算得到的结果创建一个含有36个bins(每10度一个bin)的方向直方图。
(使用当前尺度空间值的1.5倍为方差的圆形高斯窗口和梯度级做权重)
直方图中的峰值为主方向参数,如果其他的任何柱子的高度高于峰值的80%被认为是
辅方向。这就会在相同的尺度空间相同的位置构建除具有不同方向的关键点。
'''
'''
新的关键点描述符被创建了。选取与关键点周围一个16*16的邻域,把它分成16个
4*4的小方块,为每个小方块创建一个具有8个bin的方向直方图。总共加起来有128
个bin。由此组成为128的向量就构成了关键点描述符。除此之外还要进行几个
测量以达到对光照变化,旋转等的稳定性。
'''
'''
下一步可以采用关键点特征向量的欧式距离来作为两幅图像中关键点的相似性判定度量
取第一个图的某个关键点,通过遍历找到第二幅图中的距离最近的关键点。但有些情况下
第二个距离最近的关键点与第一个距离最近的额关键点靠的太近。这可能是由于噪声等
引起的。此时要计算最近距离与第二近距离的比值,如果比值大于0.8.就忽略,这
就会去除90%的错误匹配。
'''
img = cv2.imread('chessboard.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 没有解决这个版本的问题,这里先不用这个吧
# sift = cv2.xfeatures2d.SIFT_create()
# sift = cv2.SIFT()
'''
函数 sift.detect() 可以在图像中找到关键点。如果你只想在图像中的一个
区域搜索的话,也可以创建一个掩模图像作为参数使用。返回的关键点是一个
带有很多不同属性的特殊结构体,这些属性中包含它的坐标( x, y),有意义的
邻域大小,确定其方向的角度等。
'''
# kp = sift.detect(gray, None)
'''
cv2.drawKeyPoints()
它可以在关键点的部位绘制一个小圆圈。如果你设置参数为
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_
就会绘制代表关键点大小的圆圈甚至可以绘制出关键点的方向
'''
# img = cv2.drawKeypoints(gray, kp)
# cv_show('img', img)
# 这里先不用,以后再说
'''
1. 在图像中选取一个像素点 p,来判断它是不是关键点。 Ip 等于像素点 p
的灰度值
2.选择适当的阈值t
3.如下图所示在像素点p的周围选择16个像素点进行测试
4.如果咋这16个像素点中存在n个连续像素点的灰度值都高于Ip + t,或
者低于 Ip − t,那么像素点 p 就被认为是一个角点。
5. 为了获得更快的效果,还采用了而外的加速办法。首先对候选点的周围每
个 90 度的点: 1, 9, 5, 13 进行测试(先测试 1 和 19, 如果它们符合
阈值要求再测试 5 和 13)。如果 p 是角点,那么这四个点中至少有 3 个
要符合阈值要求。如果不是的话肯定不是角点,就放弃。对通过这步测试
的点再继续进行测试(是否有 12 的点符合阈值要求)。这个检测器的效
率很高,但是它有如下几条缺点:
200• 当 n<12 时它不会丢弃很多候选点 (获得的候选点比较多)。
• 像素的选取不是最优的,因为它的效果取决与要解决的问题和角点
的分布情况。
• 高速测试的结果被抛弃
• 检测到的很多特征点都是连在一起的。
前 3 个问题可以通过机器学习的方法解决,最后一个问题可以使用非最大值抑
制的方法解决。
'''
这一章真的是特别的难学啊。首先是SIFT这个函数编译失败。这个模块是收费的。网上搜了一些方法,也卸载了原来的版本,安装了比较低的版本的OpenCV也不行,最后没办法了,留着以后再研究吧。还有这个后面的内容是真的难,大部分用的都是SIFT这个函数,用不了,也就没有研究了。先写这吧,以后用到了再来更新。