角点检测

  • 理解图像特征

  我们大多数人都玩过拼图游戏,首先会拿到一张完整图片的一堆碎片,要做的就是把这些碎片以正确的方式排列起来从而重建这幅图像。问题是,你怎样做到的呢?答案就是:我们要寻找一些唯一的特征,这些特征要易于被追踪和比较。以下图为例:

  在图像的上方给出了六幅小图。你要做的就是找到这些小图在原始图像中的位置。A和B是平面,而且它们的图像中很多地方都存在。很难找到这些小图的准确位置。C和D简单一点,它们是建筑的边缘。你可以找到它们的近似位置,但是准确位置还是很难找到。这是因为沿着边缘,所有的地方都一样。所以边缘是比平面更好的特征,但是还不够好(在拼图游戏中要找连续的边缘)。最后E和F是建筑的一些角点,它们能很容易的被找到。因为在角点的地方,无论你向哪个方向移动小图,结果都会有很大的不同。所以可以把它们当成一个好的特征。为了更好的理解这个概念我们举个更简单的例子:

  如上图所示,蓝色框中的区域是一个平面,很难被找到和跟踪:无论你向哪个方向移动蓝色框,长的都一样。对于黑色框中的区域,它是一个边缘。如果你沿垂直方向移动,它会改变;但是如果沿水平方向移动就不会改变。而红色框中的角点,无论你向哪个方向移动,得到的结果都不同,这说明它是唯一的。所以角点是一个比较好的图像特征,因为角点是两个边缘的连接点,它代表了两个边缘变化的方向上的点。图像梯度有很高的变化。这种变化是可以用来帮助检测角点的。

  • Harris角点检测原理

  我们已经知道了角点的一个特性:向任何方向移动变化都很大。

  Chris_Harris 和 Mike_Stephens 早在 1988 年的文章《A Combined Corner and Edge Detector》中就已经提出了角点检测的方法,被称为Harris角点检测。他把这个简单的想法转换成了数学形式。将窗口向各个方向移动(u, v)然后计算所有差异的总和。

  其中:

  为了寻找带角点的窗口,我们搜索像素灰度变化较大的窗口。于是,我们希望找到使下式的值尽量大的点:

   根据二元函数的泰勒展开公式有:

   则式子可以展开为:

  也可以写为下面的矩阵形式:

   对于局部微小的移动量(u, v),E(u,v)可近似表达为:

  其中M是2×2的对称矩阵(将窗函数考虑进去),可由图像的导数求得(IxIy是图像在xy方向的导数,可以使用函数cv2.Sobel()计算得到)

  将实对称矩阵M对角化处理,λ1λ2是矩阵M的特征值 ,这里可以把R看成旋转因子:

  可以把矩阵M理解为一个二维随机分布的协方差矩阵。协方差矩阵中对角线元素表示各方向(维度)上的方差,而非对角线上的元素表示的是各个维度之间的相关性。

  角点:最直观的印象就是在水平、竖直两个方向上灰度值变化均较大的点,即 λ1、λ2都较大 ;
  边缘:仅在水平、或者仅在竖直方向有较大的变化量,即λ1和λ2只有一个较大 ;
  平坦地区:在水平和竖直方向上的灰度值变化均较小,即λ2、λ2都很小。

  解特征向量需要比较大的计算量,由于两个特征值的和等于矩阵M的迹,两个特征值的积等于矩阵M的行列式。所以用下式来判定角点质量。(k常取0.04-0.06)

$$R=det(M)-k \cdot trace(M)^2$$

  其中:det(M) = λλ2trace(M) = λ1 λ2 

  • λ1λ2都小时, |R|也小,这个区域就是一个平坦区域; 
  • λ1λ2 或者 λ1λ2,时R小于0,这个区域是边缘; 
  • λ1 λ2 都很大,并且λ1λ2 中的时,R也很大,说明这个区域是角点。 

  Harris角点检测算法就是对角点响应函数R进行阈值处理:R > threshold,即提取R的局部极大值

  • OpenCV中的Harris角点检测

  Open中的函数cv2.cornerHarris()可以用来进行角点检测:

import cv2
import numpy as np

img = cv2.imread('chessboard.jpg')

gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)

# cv2.cornerHarris(src, blockSize, ksize, k) --> dst
# 参数 img - 数据类型为float32的输入图像
# 参数 blockSize –  角点检测中要考虑的领域大小
# 参数 ksize - Sobel求导中使用的窗口大小
# 参数 k - Harris角点检测方程中的自由参数,取值范围为[0,04, 0.06]
# 返回值:dst – Image to store the Harris detector responses. It has the type CV_32FC1 and the same size as src
dst = cv2.cornerHarris(gray, 2, 3, 0.04)

# result is dilated for marking the corners, not important
dst = cv2.dilate(dst, None)

# Threshold for an optimal value, it may vary depending on the image.
img[dst > 0.01*dst.max()] = (0,0,255)

cv2.imshow('dst', img)

if cv2.waitKey(0) & 0xff == 27:
    cv2.destroyAllWindows()

  有时我们需要最大精度的角点检测。OpenCV为我们提供了函数cv2.cornerSubPix()它可以提供亚像素级别的角点检测。下面是一个例子。首先我们要找到 Harris角点,然后将角点的重心传给这个函数进行修正。 Harris角点用红色像素标出,绿色像素是修正后的像素。在使用这个函数是我们要定义一个迭代停止条件。当迭代次数达到或者精度条件满足后迭代就会停止。我们同样需要定义进行角点搜索的邻域大小。 

  下面代码使用OpenCV3.2.0,需要注意的是与OpenCV2相比有一些函数调用方式发生了改变。比如在OpenCV2.X版本中画矩形的函数没有返回值

  Python: cv2.rectangle(img, pt1, pt2, color[, thickness[, lineType[, shift]]]) --> None

  而在OpenCV3.2中画矩形的函数有返回值:

  Python:cv2.rectangle(img, pt1, pt2, color[, thickness[, lineType[, shift]]]) --> img

import cv2
import numpy as np

img = cv2.imread('test.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# find Harris corners
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,cv2.THRESH_BINARY)
dst = np.uint8(dst)

# find centroids
ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst)

# define the criteria to stop and refine the corners: (type, maxCount, epsilon) 
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)

# Refines the corner locations
corners = cv2.cornerSubPix(gray,np.float32(centroids),(5,5),(-1,-1),criteria) 

# Now draw them
res = np.hstack((centroids, corners))

# np.int0 可以用来省略小数点后面的数字
res = np.int0(res)

img[res[:,1],res[:,0]] = [0,0,255]  
img[res[:,3],res[:,2]] = [0,255,0]
cv2.imwrite('subpixel.png',img)
View Code

  下图为寻找到的黑色填充五角星(无轮廓)的一个角点处放大图

  对于棋盘格,OpenCV有专门的函数findChessboardCorners用来寻找棋盘图的内角点位置:

import numpy as np
import cv2

# termination criteria
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)

img = cv2.imread("Chessboard.png")
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# Finds the positions of internal corners of the chessboard.
# The function returns a non-zero value if all of the corners are found, otherwise it returns 0
# Parameters:
#     image – Source chessboard view. It must be an 8-bit grayscale or color image.
#     patternSize – Number of inner corners per a chessboard row and column
#     flags – Various operation flags that can be zero or a combination of values
ret, corners = cv2.findChessboardCorners(gray, (9,6), None)


# The detected coordinates are approximate, and to determine their positions more accurately, the function 
# calls cornerSubPix(). You also may use the function cornerSubPix() with different parameters if returned 
# coordinates are not accurate enough.
corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)

# Draw and display the corners

# Python: cv2.drawChessboardCorners(image, patternSize, corners, patternWasFound) --> image
# Parameters:
#     patternWasFound – Parameter indicating whether the complete board was found or not.
#     The return value of findChessboardCorners() should be passed here.
img = cv2.drawChessboardCorners(img, (9,6), corners2, ret)

cv2.imwrite('img.png',img)

  • Shi-Tomasi角点检测

  Shi-Tomasi算法是他们在1994年发表的文章《 Good Features to Track》中提出的一种Harris算法的改进。我们知道 Harris 角点检测的打分公式为: 

$$R=\lambda_1\lambda_2-k(\lambda_1+\lambda_2)^2$$

  但Shi-Tomasi使用的打分函数为:

 $$R = min(\lambda_1,\lambda_2)$$

  即若两个特征值中较小的一个大于最小阈值,则认为它是一个角点。我们可以把它绘制到λ1 ~ λ2空间中,就会得到下图:

  从这幅图中,我们可以看出来只有当λ1λ2 都大于最小值时,才被认为是角点(绿色区域)。

  OpenCV提供了函数:cv2.goodFeaturesToTrack()。这个函数可以帮我们使用Shi-Tomasi方法获取图像中N个最好的角点。通常情况下,输入的应该是灰度图像。然后确定你想要检测到的角点数目,再设置角点的质量水平。其值在01之间,它代表了角点的最低质量,低于这个数的所有角点都会被忽略。最后在设置两个角点之间的最短欧式距离。根据这些信息,函数就能在图像上找到角点。所有低于质量水平的角点都会被忽略,然后再把合格角点按角点质量进行降序排列。函数会采用角点质量最高的那个角点(排序后的第一个),然后将它附近(最小距离之内)的角点都删掉。按着这样的方式最后返回 N 个最佳角点。 

  在下面的例子中,我们试着找出图中的30个最佳角点: 

import numpy as np
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('test.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

'''
 Python: cv2.goodFeaturesToTrack(image, maxCorners, qualityLevel, minDistance) --> corners
 Parameters:
 maxCorners – Maximum number of corners to return. If there are more corners than are
                  found, the strongest of them is returned.
 qualityLevel – Parameter characterizing the minimal accepted quality of image corners. 
                The parameter value is multiplied by the best corner quality measure, which is
                the minimal eigenvalue (see cornerMinEigenVal() ) or the Harris function response
                (see cornerHarris() ). The corners with the quality measure less than the product
                are rejected. For example, if the best corner has the quality measure = 1500, and the
                qualityLevel=0.01 , then all the corners with the quality measure less than 15 are rejected.
 minDistance – Minimum possible Euclidean distance between the returned corners
'''
# finds the most prominent corners in the image
corners = cv2.goodFeaturesToTrack(gray, 30, 0.01, 10)

corners = np.int0(corners)

for i in corners:
    x, y = i.ravel()
    cv2.circle(img,(x,y), 3, 255, -1) 

cv2.namedWindow("image", cv2.WINDOW_NORMAL)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindow("image")

 

 

参考:

Harris角点检测子

OpenCV-Python Tutorials

PIL:Python图像处理类库

Harris角点检测原理

角点检测的几种基本方法

Harris角点及Shi-Tomasi角点检测

Opencv学习笔记(五)Harris角点检测

posted @ 2017-06-30 17:49  XXX已失联  阅读(1113)  评论(0编辑  收藏  举报