这样想象一下,直线上的一个点在垂直于直线的方向上有最强的梯度。沿着直线的方向梯度较低,意思是直线上的像素点与它周围的像素点看起来相似。我们进行的角点检测是梯度强度明显高于其他像素的点,可能就是目标处的拐点。它的这个特性可以被应用到同一场景的多视角检测及运动中估计。
$${\rm{c}}(x,y,\Delta x,\Delta y) = {\sum\limits_{(i,j) \in W(x,y)} {W(i,j)(I(i,j) - I(i + \Delta x,j + \Delta y))} ^2}$$
W(x,y)是一个小窗口N*N的局部正方形图像,要检测的角点就存在于这个局部图形中,对于图像强度I(x,y),计算结果只是点(x,y)处的图像与位移变化处的平方差加权和。W(i,j)是高斯加权,保证靠近窗口中心的平方差比离中心远的平方差权重更大。由于变化量很小,用一阶偏导数表示。自相关形式如下表示(一阶泰勒近似):
$${\rm{c}}(x,y,\Delta x,\Delta y) = \left[ {\begin{array}{*{20}{c}}
{\Delta x}&{\Delta y}
\end{array}} \right]M(x,y)\left[ {\begin{array}{*{20}{c}}
{\Delta x}\\
{\Delta y}
\end{array}} \right]$$
M(x,y)为自相关矩阵Ix,Iy为I(x,y)在x和y方向的一阶偏导数。
\[{\rm{M(x,y)}} = \sum\limits_{ - K \le i,j \le K} {W(i,j)} \left[ {\begin{array}{*{20}{c}}
{I_x^2(x + i,y + j)}&{{I_x}(x + i,y + j){I_y}(x + i,y + j)}\\
{{I_x}(x + i,y + j){I_y}(x + i,y + j)}&{I_y^2(x + i,y + j)}
\end{array}} \right]\]
角点是自相关矩阵中具有两个大的特征值的位置,实际意义是,在不考虑图片旋转的情况下,两个大的特征值表示在任何方向上移动一小段距离都会造成图像改变。Harris的定义如下:
\[{\rm{H = }}\det (M) - ktrac{e^2}(M) = {\lambda _1}{\lambda _2} - k{({\lambda _1} + {\lambda _2})^2}\]
通过搜索该函数的局部最大值找到关键角点(k为灵敏度)。检测结果如下图所示:
cornerHarris(dst,aim,blocksize,ksize,k,BORDER_DEFAULT);
关键参数说明:blocksize表示邻域的大小,ksize表示Sobel算子的卷积核大小,k表示公式中的Harris灵敏度.
- 参考代码如下:
1 #include2 #include 3 4 using namespace cv; 5 using namespace std; 6 int init_value = 30; 7 int max_value = 255; 8 void Harris_Demo(int, void*); 9 Mat src, dst; 10 int main(int argc,char** argv) 11 { 12 13 src = imread("H:/cv_code/image/home.jpg"); 14 if (src.empty()) 15 { 16 printf("could not find image"); 17 return -1; 18 } 19 namedWindow("src",CV_WINDOW_AUTOSIZE); 20 imshow("src",src); 21 22 cvtColor(src,dst,COLOR_BGR2GRAY); 23 namedWindow("huidu", CV_WINDOW_AUTOSIZE); 24 imshow("huidu", dst); 25 26 namedWindow("output", CV_WINDOW_AUTOSIZE); 27 createTrackbar("value:","output",&init_value,max_value,Harris_Demo); 28 Harris_Demo(0,0); 29 waitKey(0); 30 return 0; 31 } 32 void Harris_Demo(int, void*) 33 { 34 Mat aim, norm_dst, normsc_dst; 35 aim = Mat::zeros(src.size(),CV_32FC1); 36 int blocksize = 2; 37 int ksize = 3; 38 double k = 0.04; 39 cornerHarris(dst,aim,blocksize,ksize,k,BORDER_DEFAULT); 40 41 normalize(aim,norm_dst,0,255,NORM_MINMAX,CV_32FC1,Mat()); 42 convertScaleAbs(norm_dst,normsc_dst); 43 44 Mat resulting = src.clone(); 45 for (int row = 0; row < norm_dst.rows; row++) 46 { 47 48 for (int col = 0; col < norm_dst.cols; col++) 49 { 50 51 if ((int)norm_dst.at<float>(row,col)> init_value+80) 52 circle(resulting,Point(col,row),5,Scalar(0,0,255),2,8,0); 53 54 } 55 56 } 57 imshow("output",resulting); 58 }