1.原理
- Harris 角点检测:将窗口向各个方向移动(u,v)然后计算所有差异的总和。窗口函数可以是正常的矩形窗口也可以是对每一个像素给予不同权重的高斯窗口。
- 根据一个用来判定窗口内是否包含角点的等式进行打分。根据图像的梯度我们可以判断一个区域是否是角点,边界或者是平面(经过公式转化后得到一个关于梯度的矩阵,再求该矩阵的特征值,根据特征值的大小来判断)。
- Harris 角点检测的结果是一个由角点分数构成的灰度图像。选取适当的阈值对结果图像进行二值化我们就检测到了图像中的角点。
2.opencv中的Harris角点检测
代码速记:
- cv2.cornerHarris()
参数解释:
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
- img : 数据类型为float32 的灰度图像。
- blockSize: 角点检测中要考虑的邻域大小。
- ksize - Sobel :求导中使用的窗口大小
- k: Harris 角点检测方程中的自由参数,取值范围在[0,04,0.06]
- 返回值:一个由角点分数构成的灰度图像
实战:
def harris(self):
#【1】把原图处理为float32的灰度图像
gray = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
# 输入图像必须是float32,最后一个参数在0.04 到0.06 之间
#【2】Harris角点检测
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
#【3】画图
dst = cv2.dilate(dst, None)#膨胀,为了突出角点
copy1=self.img.copy()
copy2 = self.img.copy()
copy1[dst > 0.01 * dst.max()] = [0, 255, 0]#大于某个阈值(最大值的0.01)的角点变成緑色
copy2[dst > 0.5 * dst.max()] = [0, 255, 0]
titles = ['raw', 'low_thresh', 'hign_thresh']
imgs = [self.img, copy1, copy2]
for i in range(3):
plt.subplot(1, 3, i + 1), plt.imshow(cv2.cvtColor(imgs[i],cv2.COLOR_BGR2RGB))
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
3.Shi-Tomasi 角点检测& 适合于跟踪的图像特征
此方法是对Harris角点检测的一个小小的修改。打分公式由 改为 。如果打分超过阈值,我们就认为它是一个角点。
代码速记:
- cv2.goodFeatureToTrack()
参数解释:
corners = cv2.goodFeaturesToTrack(gray, 25, 0.01, 10)
- image:输入的灰度图像
- maxCorners:要检测到的角点数目
- qualityLevel:角点的质量水平,0到1 之间。它代表了角点的最低质量,低于这个数的所有角点都会被忽略
- minDistance: 两个角点之间的最短欧式距离
实战:
def track(self):
copy = self.img.copy()
#【1】获得灰度图像
gray = cv2.cvtColor(copy, cv2.COLOR_BGR2GRAY)
#【2】进行角点检测
corners = cv2.goodFeaturesToTrack(gray, 25, 0.01, 10)
# 返回的结果是[[ 311., 250.]] 两层括号的数组。
corners = np.int0(corners)#可以用来省略小数点后面的数字
#画图
for i in corners:
x, y = i.ravel()
cv2.circle(copy, (x, y), 3, (0,0,255), 3)
plt.imshow(cv2.cvtColor(copy, cv2.COLOR_BGR2RGB)), plt.show()
4.亚像素级角点检测
- Harris提取角点是像素级的(像素坐标),精度不高,若我们进行图像处理的目的不是提取用于识别的特征点而是进行几何测量,这通常需要更高的精度。
- 那么如何提取亚像素级角点的位置呢?以Harris检测到的初始角点为中心,以一定半径搜索角点簇,采用最小二乘法加权角点簇与待求角点的欧几里得距离,精化初始角点坐标,从而实现 Harris 亚像素角点准确快速定位。
- OpenCV 为我们提供了函数cv2.cornerSubPix(),它可以提供亚像素级别的角点检测。下面是一个例子。首先我们要找到Harris角点,然后将角点的重心传给这个函数进行修正。Harris 角点用红色像素标出,绿色像素是修正后的像素。在使用这个函数是我们要定义一个迭代停止条件。当迭代次数达到或者精度条件满足后迭代就会停止。
代码速记:
- cv2.cornerSubPix()
参数解释:
corners = cv2.cornerSubPix(gray, np.float32(centroids), (5, 5), (-1, -1), criteria)
#cv2.cornerSubPix(image, corners, winSize, zeroZone, criteria)
- image:输入图像,即源图像
- corners:提供输入角点的初始坐标和精确的输出坐标。
- winSize:搜索窗口的半径。若winSize=(5,5),那么就表示使用 大小的搜索窗口。
- zeroZone:死区的一半尺寸。而死区为不对搜索区的中央位置做求和运算的区域,用来避免自相关矩阵出现的某些可能的奇异性。值为(-1,-1)表示没有死区。
- criteria:求角点的迭代过程的终止条件。
实战:
def subfix(self):
copy=self.img.copy()
gray = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
# 【1】Harris角点检测
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.uint8(dst)
# 【2】找到重心
ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst)
# 【3】设置迭代终止条件
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
#【4】亚像素级角点检测
# 返回值由角点坐标组成的一个数组(而非图像)
corners = cv2.cornerSubPix(gray, np.float32(centroids), (5, 5), (-1, -1), criteria)
# 画图
res = np.hstack((centroids, corners))
# np.int0 可以用来省略小数点后面的数字(非四舍五入)。
res = np.int0(res)
copy[res[:, 1], res[:, 0]] = [0, 0, 255]#红色的是像素级角点
copy[res[:, 3], res[:, 2]] = [0, 255, 0]#绿色的是亚像素级角点
plt.imshow(cv2.cvtColor(copy,cv2.COLOR_BGR2RGB))
plt.show()