标签:
一、角点
图像处理和与计算机视觉领域,兴趣点(interest points),或称作关键点(keypoints)、特征点(feature points) 被大量用于解决物体识别,图像识别、图像匹配、视觉跟踪、三维重建等一系列的问题。我们不再观察整幅图,而是选择某些特殊的点,然后对他们进行局部有的放矢的分析。如果能检测到足够多的这种点,同时他们的区分度很高,并且可以精确定位稳定的特征,那么这个方法就有使用价值。
图像特征类型可以被分为如下三种:
<1>边缘 <2>角点 (感兴趣关键点) <3>斑点(Blobs)(感兴趣区域)
其中,角点是个很特殊的存在。他们在图像中可以轻易地定位,同时,他们在人造物体场景,比如门、窗、桌等出随处可见。因为角点位于两条边缘的交点处,代表了两个边缘变化的方向上的点,,所以他们是可以精确定位的二维特征,甚至可以达到亚像素的精度。且其图像梯度有很高的变化,这种变化是可以用来帮助检测角点的。需要注意的是,角点与位于相同强度区域上的点不同,与物体轮廓上的点也不同,因为轮廓点难以在相同的其他物体上精确定位。
角点检测(Corner Detection)是计算机视觉系统中用来获得图像特征的一种方法,广泛应用于运动检测、图像匹配、视频跟踪、三维建模和目标识别等领域中。也称为特征点检测。
角点通常被定义为两条边的交点,更严格的说,角点的局部邻域应该具有两个不同区域的不同方向的边界。而实际应用中,大多数所谓的角点检测方法检测的是拥有特定特征的图像点,而不仅仅是“角点”。这些特征点在图像中有具体的坐标,并具有某些数学特征,如局部最大或最小灰度、某些梯度特征等。
现有的角点检测算法并不是都十分的健壮。很多方法都要求有大量的训练集和冗余数据来防止或减少错误特征的出现。另外,角点检测方法的一个很重要的评价标准是其对多幅图像中相同或相似特征的检测能力,并且能够应对光照变化、图像旋转等图像变化。
如果某一点在任意方向的一个微小变动都会引起灰度很大的变化,那么我们就把它称之为角点
首先我们来看三幅图片理解什么是角点:
我们在图片以某像素点为中心,取一窗口,当窗口向各个方向移动时,其内部灰度值变化不是很明显,则该点即处在平坦区域(如左边图);当其内部灰度值只在几个固定的方向上变化较为明显,那么该点则处在边缘区域(如图中间部分);当向各个方向移动,其变化都是很明显,则该点为角点(如图右)。
另外,关于角点的具体描述可以有几种:
二、moravvec角点
Moravec 在1981年提出Moravec角点检测算子[1],并将它应用于立体匹配。
首先, 计算每个像素点的兴趣值, 即以该像素点为中心, 取一个w*w(如:5×5)的方形窗口, 计算0度、45度、90度、135度四个方向灰度差的平方和, 取其中的最小值作为该像素点的兴趣值.E就是像素的变化值。Moravec算子对四个方向进行加权求和来确定变化的大小,然和设定阈值,来确定到底是边还是角点。
图 以3×3为例 黑色窗口为I(x,y) 红色窗口为I(x+u,y+v)
其中四种移位 (u,v) = (1,0), (1,1), (0,1), (-1, 1).w(x,y)为方形二值窗口,若像素点在窗口内,则取值为1, 否则为0。
moravec角点检测步骤:
(1)对于每一个像素点,计算在E(u,v),在我们的算法中,(u,v)的取值是((1,0), (1,1),(0,1), (-1, 1).当然,你自己可以改成(1,0),(1,1),(0,1),(-1,1),(-1,0),(-1,-1),(0,-1),(1,-1)8种情况
(2)计算最小值对每个位置minValue = min{E(u,v)},其中(u,v) = (1,0),(1,1), (0,1), (-1, 1).
(3)对每个位置minValue 进行判断,是不是大于设定阈值,如果是大于设定阈值,接着判断是不是局部极大,在判断角点的时候,必须判断每个方向的patch的变化。
moravec角点检测主要有两个缺点:
<span style="font-size:18px;">#include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> using namespace std; using namespace cv; // MoravecCorners角点检测 cv::Mat MoravecCorners(cv::Mat srcImage, int kSize, int threshold) { cv::Mat resMorMat = srcImage.clone(); int r = kSize / 2; const int nRows = srcImage.rows; const int nCols = srcImage.cols; int nConut = 0; CvPoint *pPoint = new CvPoint[nRows*nCols]; for (int i = r; i < srcImage.rows-r; i++) { for (int j = r; j<srcImage.cols-r; j++) { int wV1, wV2, wV3, wV4; wV1 = wV2 = wV3 = wV4 = 0; for (int k = -r; k < r; k++) wV1 += (srcImage.at<uchar>(i,j+k)- srcImage.at<uchar>(i,j+k+1))*(srcImage.at <uchar>(i,j+k)-srcImage.at<uchar>(i,j+k+1)); for (int k = -r; k < r; k++) wV2 += (srcImage.at<uchar>(i+k,j)- srcImage.at<uchar>(i+k+1,j))*(srcImage.at <uchar>(i+k,j)-srcImage.at<uchar>(i+k+1,j)); for (int k = -r; k < r; k++) wV3 += (srcImage.at<uchar>(i+k,j+k)- srcImage.at<uchar>(i+k+1,j+k+1))*(srcImage.at <uchar>(i+k,j+k)-srcImage.at<uchar>(i+k+1,j+k+1)); for (int k = -r; k < r; k++) wV4 += (srcImage.at<uchar>(i+k,j-k)- srcImage.at<uchar>(i+k+1,j-k-1))*(srcImage.at <uchar>(i+k,j-k)-srcImage.at<uchar>(i+k+1,j-k-1)); int value = min(min(wV1,wV2), min(wV3,wV4)); if (value > threshold) { pPoint[nConut] = cvPoint(j,i); nConut++; } } } for (int i = 0; i < nConut; i++) cv::circle(resMorMat, pPoint[i], 5, cv::Scalar(255,0,0)); return resMorMat; } int main() { cv::Mat srcImage = imread("lena.jpg",0); if (!srcImage.data) return -1; cv::Mat resMorMat = MoravecCorners(srcImage, 5,10000); cv::imshow("srcImage", srcImage); cv::imshow("resMorMat",resMorMat); cv::waitKey(0); return 0; }</span>
三、harris角点检测
在harris的角点检测中,使用的是高斯窗口,所以w(x,y)表示的是高斯窗口中的权重。此时 当u和v取两组相互垂直的值时,E(u,v)都有较大值的点。
<1>计算图像I(x,y)在x和y两个方向的梯度Ix,Iy
结果解释:
Harris角点检测最直观的解释是:在任意两个相互垂直的方向上,都有较大变化的点。
在moravec角点检测中,w(x,y)的取值是二元的,在窗口内部就取值为1,在窗口外部就取值为0,在harris的角点检测中,使用的是高斯窗口,所以w(x,y)表示的是高斯窗口中的权重。此时 当u和v取两组相互垂直的值时,E(u,v)都有较大值的。
Harris角点检测算法有诸多优点:
当然Harris也有许多不完善的地方:它对尺度很敏感,不具备几何尺度不变性。
cornerHarris 函数用于在OpenCV中运行Harris角点检测算子处理图像。和cornerMinEigenVal( )以及cornerEigenValsAndVecs( )函数类似,cornerHarris 函数对于每一个像素(x,y)在邻域内,计算2x2梯度的协方差矩阵,接着它计算如下式子:
即可以找出输出图中的局部最大值,即找出了角点。
<span style="font-size:18px;">C++: void cornerHarris(InputArraysrc,OutputArray dst, int blockSize, int ksize, double k,intborderType=BORDER_DEFAULT ) 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位或者浮点型图像。 第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放Harris角点检测的输出结果,和源图片有一样的尺寸和类型。 第三个参数,int类型的blockSize,表示邻域的大小,更多的详细信息在cornerEigenValsAndVecs()中有讲到。 第四个参数,int类型的ksize,表示Sobel()算子的孔径大小。 第五个参数,double类型的k,Harris参数。 第六个参数,int类型的borderType,图像像素的边界模式,注意它有默认值BORDER_DEFAULT。更详细的解释,参考borderInterpolate( )函数。</span>
<span style="font-size:18px;">#include <opencv2/opencv.hpp> #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" using namespace cv; using namespace std; #define WINDOW_NAME1 "【程序窗口1】" #define WINDOW_NAME2 "【程序窗口2】" Mat g_srcImage, g_srcImage1,g_grayImage; int thresh = 30; int max_thresh = 175; void on_CornerHarris( int, void* );//回调函数 int main( int argc, char** argv ) { g_srcImage = imread( "1.jpg", 1 ); if(!g_srcImage.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; } imshow("原始图",g_srcImage); g_srcImage1=g_srcImage.clone( ); cvtColor( g_srcImage1, g_grayImage, CV_BGR2GRAY ); namedWindow( WINDOW_NAME1, CV_WINDOW_AUTOSIZE ); createTrackbar( "阈值: ", WINDOW_NAME1, &thresh, max_thresh, on_CornerHarris ); on_CornerHarris( 0, 0 ); waitKey(0); return(0); } void on_CornerHarris( int, void* ) { Mat dstImage; Mat normImage; Mat scaledImage; dstImage = Mat::zeros( g_srcImage.size(), CV_32FC1 ); g_srcImage1=g_srcImage.clone( ); cornerHarris( g_grayImage, dstImage, 2, 3, 0.04, BORDER_DEFAULT ); normalize( dstImage, normImage, 0, 255, NORM_MINMAX, CV_32FC1, Mat() ); convertScaleAbs( normImage, scaledImage ); for( int j = 0; j < normImage.rows ; j++ ) { for( int i = 0; i < normImage.cols; i++ ) { if( (int) normImage.at<float>(j,i) > thresh+80 ) { circle( g_srcImage1, Point( i, j ), 5, Scalar(10,10,255), 2, 8, 0 ); circle( scaledImage, Point( i, j ), 5, Scalar(0,10,255), 2, 8, 0 ); } } } imshow( WINDOW_NAME1, g_srcImage1 ); imshow( WINDOW_NAME2, scaledImage ); } </span>
四、知识补充
关于矩阵知识的一点补充:好长时间没看过线性代数的话,这一段比较难理解。可以看到M是实对称矩阵,这里简单温习一下实对称矩阵和二次型的一些知识点吧。
1. 关于特征值和特征向量:
特征值的特征向量的概念忘了就自己查吧,这里只说关键的。对于实对称矩阵M(设阶数为n),则一定有n个实特征值,每个特征值对应一组特征向量(这组向量中所有向量共线),不同特征值对应的特征向量间相互正交;(注意这里说的是实对称矩阵,不是所有的矩阵都满足这些条件)
2. 关于对角化:
对角化是指存在一个正交矩阵Q,使得 Q’MQ 能成为一个对角阵(只有对角元素非0),其中Q’是Q的转置(同时也是Q的逆,因为正交矩阵的转置就是其逆)。一个矩阵对角化后得到新矩阵的行列式和矩阵的迹(对角元素之和)均与原矩阵相同。如果M是n阶实对称矩阵,则Q中的第 j 列就是第 j 个特征值对应的一个特征向量(不同列的特征向量两两正交)。
3. 关于二次型:
对于一个n元二次多项式,f(x1,x2....xn)= ∑ ( aij*xi*xj ) ,其中 i 和 j 的求和区间均为 [1,n] ,
可将其各次的系数 aij 写成一个n*n矩阵M,由于 aij 和 aji 的对称等价关系,一般将 aij 和 aji 设为一样的值,均为xi*xj 的系数的二分之一。这样,矩阵M就是实对称矩阵了。即二次型的矩阵默认都是实对称矩阵
4. 关于二次型的标准化(正交变换法):
二次型的标准化是指通过构造一个n阶可逆矩阵 C,使得向量 ( x1,x2...xn ) = C * (y1,y2...yn),把n维向量 x 变换成n维向量 y ,并代入f(x1,x2....xn) 后得到 g(y1,y2...yn),而后者的表达式中的二次项中不包含任何交叉二次项 yi*yj(全部都是平方项 yi^2),也即表达式g的二次型矩阵N是对角阵。用公式表示一下 f 和 g ,(下面的表达式中 x 和 y都代表向量,x‘ 和 y‘ 代表转置)
f = x‘ * M * x ;
g = f = x‘ * M * x = (Cy)‘ * M * (Cy) = y‘* (C‘MC) * y = y‘ * N * y ;
因此 C‘MC = N。正交变换法,就是直接将M对角化得到N,而N中对角线的元素就是M的特征值。正交变换法中得到的 C 正好是一个正交矩阵,其每一列都是两两正交的单位向量,因此 C 的作用仅仅是将坐标轴旋转(不会有放缩)。
http://blog.csdn.net/newthinker_wei/article/details/45603583
http://www.360doc.com/content/15/1212/23/20007814_519967668.shtml
http://blog.csdn.net/crzy_sparrow/article/details/7391511
http://blog.csdn.net/lu597203933/article/details/15088485
http://blog.csdn.net/poem_qianmo/article/details/29356187
图像识别算法交流 QQ群:145076161,欢迎图像识别与图像算法,共同学习与交流
Opencv图像识别从零到精通(33)----moravec角点、harris角点
标签:
原文地址:http://blog.csdn.net/qq_20823641/article/details/52275543