理论
我们知道角点是图像里在各个方向变化最大的区域。早起尝试找到这些角的是Chris Harris和Mike Stephens,在他们的论文A Combined Corner and Edge Detector里,所以现在叫Harris角点检测。他把这个简单想法用数学形式表达,基本上就是对(u,v)在各个方向移动找强度的变化。表达式如下:
Window function是一个矩形窗口或者高斯窗口,给其下的像素权重。
我们应该最大化这个函数E(u, v)来检测角点。这表示,我们应该放大第二个条件,应用泰勒展开上面的等式,然后经过数学步骤,我们可以得到最终的等式:
其中
这里,Ix 和Iy 是图像在x和y方向分别的导数(可以用cv2.Sobel()很容易得到)
然后是主要部分,在这个之后,它们创建了一个分数,基本上市一个等式,来决定一个窗口是否能包含一个角。
其中:
·det(M) = λ1λ2
·trace(M) = λ1 + λ2
·λ1和λ2是M的特征值
所以这些特征值决定了区域是否是角,边缘或者无反差的。
·当|R|小的时候,也就是λ1和λ2小的时候,区域是无反差的
·当R < 0, 页就是λ1 >> λ2时,区域是边缘
·当R很大,是λ1和λ2很大并且λ1 ~ λ2时区域是角
可以用图片来表示:
所以Harris角点检测是一个有这些分数的灰度图,用一个合适的阈值你就可以得到图像的角。
OpenCV里的Harris角点检测
OpenCV有一个函数cv2.cornerHarris()来做这个,参数是:
img - 输入图像,应该是灰度图和float32类型
blockSize - 做角点检测的近邻的大小
ksize - Sobel导数的孔径参数
k - Harris检测等式里的自由参数
import cv2
import numpy as npfilename = 'chessboard.jpg'
img = cv2.imread(filename)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)gray = np.float32(gray)
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角点用红色像素标记,精炼的角点用绿色像素标记,对这个函数,我们要定义什么时候停止迭代。我们也需要定义来搜索角点的近邻的大小。
import cv2
import numpy as npfilename = 'chessboard2.jpg'
img = cv2.imread(filename)
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,0)
dst = np.uint8(dst)# find centroids
ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst)# define the criteria to stop and refine the corners
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)# Now draw them
res = np.hstack((centroids,corners))
res = np.int0(res)
img[res[:,1],res[:,0]]=[0,0,255]
img[res[:,3],res[:,2]] = [0,255,0]
cv2.imwrite('subpixel5.png',img)
下面是结果,一些重要的位置放大显示了:
END
网友评论