当一个窗口在图像上移动,在平滑区域如图(a),窗口在各个方向上没有变化。在边缘上如图(b),窗口在边缘的方向上没有变化。在角点处如图(c),窗口在各个方向上具有变化。Harris角点检测正是利用了这个直观的物理现象,通过窗口在各个方向上的变化程度,决定是否为角点。
将图像窗口平移[u,v]产生灰度变化E(u,v)
由:, 得到:
对于局部微小的移动量 [u,v],近似表达为:
其中M是 2*2 矩阵,可由图像的导数求得:
E(u,v)的椭圆形式如下图:
定义角点响应函数 R 为:
Harris角点检测算法就是对角点响应函数R进行阈值处理:R > threshold,即提取R的局部极大值。
【相关代码】
OpenCV中定义了 cornerHarris 函数:
- void cornerHarris( InputArray src, OutputArray dst, int blockSize,
- int ksize, double k,
- int borderType=BORDER_DEFAULT );
可以结合 convertScaleAbs 函数,通过阈值取角点:
- void cornerHarris_demo( int, void* )
- {
- Mat dst, dst_norm;
- dst = Mat::zeros( src.size(), CV_32FC1 );
- /// Detector parameters
- int blockSize = 2;
- int apertureSize = 3;
- double k = 0.04;
- /// Detecting corners
- cornerHarris( src_gray, dst, blockSize, apertureSize, k, BORDER_DEFAULT );
- /// Normalizing
- normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );
- convertScaleAbs( dst_norm, dst_norm_scaled );
- /// Drawing a circle around corners
- for( int j = 0; j < dst_norm.rows ; j++ )
- { for( int i = 0; i < dst_norm.cols; i++ )
- {
- if( (int) dst_norm.at<float>(j,i) > thresh )
- {
- circle( dst_norm_scaled, Point( i, j ), 5, Scalar(0), 2, 8, 0 );
- circle(src,Point( i, j ), 5, Scalar(255,0,0), -1, 8, 0 );
- }
- }
- }
- /// Showing the result
- imshow( corners_window, dst_norm_scaled );
- imshow( source_window, src );
- }
Shi-Tomasi 算法
Shi-Tomasi 算法是Harris 算法的改进。Harris 算法最原始的定义是将矩阵 M 的行列式值与 M 的迹相减,再将差值同预先给定的阈值进行比较。后来Shi 和Tomasi 提出改进的方法,若两个特征值中较小的一个大于最小阈值,则会得到强角点。
如上面第二幅图中,对自相关矩阵 M 进行特征值分析,产生两个特征值和两个特征方向向量。因为较大的不确定度取决于较小的特征值,也就是,所以通过寻找最小特征值的最大值来寻找好的特征点也就解释的通了。
Shi 和Tomasi 的方法比较充分,并且在很多情况下可以得到比使用Harris 算法更好的结果。
【相关代码】
由于这种Shi-Tomasi算子与1994年在文章 Good Features to Track [1]中提出,OpenCV 实现的算法的函数名定义为 goodFeaturesToTrack:
- void goodFeaturesToTrack( InputArray image, OutputArray corners,
- int maxCorners, double qualityLevel, double minDistance,
- InputArray mask=noArray(), int blockSize=3,
- bool useHarrisDetector=false, double k=0.04 );
自定义使用函数(以方便createTrackbar的响应)如下:
- void cornerShiTomasi_demo( int, void* )
- {
- if( maxCorners < 1 ) { maxCorners = 1; }
- /// Parameters for Shi-Tomasi algorithm
- vector<Point2f> corners;
- double qualityLevel = 0.01;
- double minDistance = 10;
- int blockSize = 3;
- bool useHarrisDetector = false;
- double k = 0.04;
- /// Copy the source image
- Mat cormat;
- /// Apply corner detection :Determines strong corners on an image.
- goodFeaturesToTrack( src_gray,
- corners,
- maxCorners,
- qualityLevel,
- minDistance,
- Mat(),
- blockSize,
- useHarrisDetector,
- k );
- /// Draw corners detected
- for( int i = 0; i < corners.size(); i++ ){
- circle( dst_norm_scaled, corners[i], 5, Scalar(255), 2, 8, 0 );
- circle( src, corners[i], 4, Scalar(0,255,0), 2, 8, 0 );
- }
- /// Show what you got
- imshow( corners_window, dst_norm_scaled );
- imshow( source_window, src );
- }
实践
在主函数中定义两个进度条方便调整阈值:
- namedWindow( source_window, CV_WINDOW_AUTOSIZE );
- createTrackbar( "Threshold: ", source_window, &thresh, max_thresh, cornerHarris_demo );
- createTrackbar( "Max corners:", source_window, &maxCorners, maxTrackbar, cornerShiTomasi_demo );
- namedWindow( corners_window, CV_WINDOW_AUTOSIZE );
- namedWindow( source_window, CV_WINDOW_AUTOSIZE );
- imshow( source_window, src );
- cornerHarris_demo( 0, 0 );
- cornerShiTomasi_demo( 0, 0 );
这里还需要说的是OpenCV 2.4.2中给的角点检测跟踪的示例代码有些问题,是应为SURF等不再定义在 feature2d模块中,而是legacy和nonfree,所以需要加入引用:
- #include "opencv2/legacy/legacy.hpp"
- #include "opencv2/nonfree/nonfree.hpp"
角点检测结果:
蓝色实心点为Harris检测结果,绿色空心圈为goodFeaturetoTrack检测结果。
M特征值分解后每个像素点相减的图(也就是Harris阈值判断的图)如下:
黑色实心点为Harris阈值检测结果,白色空心圈为阈值为27时Shi-Tomasi检测结果。
转载请注明出处:http://blog.csdn.net/xiaowei_cqu/article/details/7805206
源码及资料下载: http://download.csdn.net/detail/xiaowei_cqu/4466627
参考资料:
[1] Shi and C. Tomasi. Good Features to Track. Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition, pages 593-600, June 1994.
[2] Richard Szeliski. Computer Vision: Algorithms and Applications. Springer, New York, 2010.
[3] 图像特征点提取PPT http://wenku.baidu.com/view/f61bc369561252d380eb6ef0.html
角点
特征检测与匹配是Computer Vision 应用总重要的一部分,这需要寻找图像之间的特征建立对应关系。点,也就是图像中的特殊位置,是很常用的一类特征,点的局部特征也可以叫做“关键特征点”(keypoint feature),或“兴趣点”(interest point),或“角点”(conrner)。
关于角点的具体描述可以有几种:
- 一阶导数(即灰度的梯度)的局部最大所对应的像素点;
- 两条及两条以上边缘的交点;
- 图像中梯度值和梯度方向的变化速率都很高的点;
- 角点处的一阶导数最大,二阶导数为零,指示物体边缘变化不连续的方向。
Harris角点检测
当一个窗口在图像上移动,在平滑区域如图(a),窗口在各个方向上没有变化。在边缘上如图(b),窗口在边缘的方向上没有变化。在角点处如图(c),窗口在各个方向上具有变化。Harris角点检测正是利用了这个直观的物理现象,通过窗口在各个方向上的变化程度,决定是否为角点。
将图像窗口平移[u,v]产生灰度变化E(u,v)
由:, 得到:
对于局部微小的移动量 [u,v],近似表达为:
其中M是 2*2 矩阵,可由图像的导数求得:
E(u,v)的椭圆形式如下图:
定义角点响应函数 R 为:
Harris角点检测算法就是对角点响应函数R进行阈值处理:R > threshold,即提取R的局部极大值。
【相关代码】
OpenCV中定义了 cornerHarris 函数:
- void cornerHarris( InputArray src, OutputArray dst, int blockSize,
- int ksize, double k,
- int borderType=BORDER_DEFAULT );
可以结合 convertScaleAbs 函数,通过阈值取角点:
- void cornerHarris_demo( int, void* )
- {
- Mat dst, dst_norm;
- dst = Mat::zeros( src.size(), CV_32FC1 );
- /// Detector parameters
- int blockSize = 2;
- int apertureSize = 3;
- double k = 0.04;
- /// Detecting corners
- cornerHarris( src_gray, dst, blockSize, apertureSize, k, BORDER_DEFAULT );
- /// Normalizing
- normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );
- convertScaleAbs( dst_norm, dst_norm_scaled );
- /// Drawing a circle around corners
- for( int j = 0; j < dst_norm.rows ; j++ )
- { for( int i = 0; i < dst_norm.cols; i++ )
- {
- if( (int) dst_norm.at<float>(j,i) > thresh )
- {
- circle( dst_norm_scaled, Point( i, j ), 5, Scalar(0), 2, 8, 0 );
- circle(src,Point( i, j ), 5, Scalar(255,0,0), -1, 8, 0 );
- }
- }
- }
- /// Showing the result
- imshow( corners_window, dst_norm_scaled );
- imshow( source_window, src );
- }
Shi-Tomasi 算法
Shi-Tomasi 算法是Harris 算法的改进。Harris 算法最原始的定义是将矩阵 M 的行列式值与 M 的迹相减,再将差值同预先给定的阈值进行比较。后来Shi 和Tomasi 提出改进的方法,若两个特征值中较小的一个大于最小阈值,则会得到强角点。
如上面第二幅图中,对自相关矩阵 M 进行特征值分析,产生两个特征值和两个特征方向向量。因为较大的不确定度取决于较小的特征值,也就是,所以通过寻找最小特征值的最大值来寻找好的特征点也就解释的通了。
Shi 和Tomasi 的方法比较充分,并且在很多情况下可以得到比使用Harris 算法更好的结果。
【相关代码】
由于这种Shi-Tomasi算子与1994年在文章 Good Features to Track [1]中提出,OpenCV 实现的算法的函数名定义为 goodFeaturesToTrack:
- void goodFeaturesToTrack( InputArray image, OutputArray corners,
- int maxCorners, double qualityLevel, double minDistance,
- InputArray mask=noArray(), int blockSize=3,
- bool useHarrisDetector=false, double k=0.04 );
自定义使用函数(以方便createTrackbar的响应)如下:
- void cornerShiTomasi_demo( int, void* )
- {
- if( maxCorners < 1 ) { maxCorners = 1; }
- /// Parameters for Shi-Tomasi algorithm
- vector<Point2f> corners;
- double qualityLevel = 0.01;
- double minDistance = 10;
- int blockSize = 3;
- bool useHarrisDetector = false;
- double k = 0.04;
- /// Copy the source image
- Mat cormat;
- /// Apply corner detection :Determines strong corners on an image.
- goodFeaturesToTrack( src_gray,
- corners,
- maxCorners,
- qualityLevel,
- minDistance,
- Mat(),
- blockSize,
- useHarrisDetector,
- k );
- /// Draw corners detected
- for( int i = 0; i < corners.size(); i++ ){
- circle( dst_norm_scaled, corners[i], 5, Scalar(255), 2, 8, 0 );
- circle( src, corners[i], 4, Scalar(0,255,0), 2, 8, 0 );
- }
- /// Show what you got
- imshow( corners_window, dst_norm_scaled );
- imshow( source_window, src );
- }
实践
在主函数中定义两个进度条方便调整阈值:
- namedWindow( source_window, CV_WINDOW_AUTOSIZE );
- createTrackbar( "Threshold: ", source_window, &thresh, max_thresh, cornerHarris_demo );
- createTrackbar( "Max corners:", source_window, &maxCorners, maxTrackbar, cornerShiTomasi_demo );
- namedWindow( corners_window, CV_WINDOW_AUTOSIZE );
- namedWindow( source_window, CV_WINDOW_AUTOSIZE );
- imshow( source_window, src );
- cornerHarris_demo( 0, 0 );
- cornerShiTomasi_demo( 0, 0 );
这里还需要说的是OpenCV 2.4.2中给的角点检测跟踪的示例代码有些问题,是应为SURF等不再定义在 feature2d模块中,而是legacy和nonfree,所以需要加入引用:
- #include "opencv2/legacy/legacy.hpp"
- #include "opencv2/nonfree/nonfree.hpp"
角点检测结果:
蓝色实心点为Harris检测结果,绿色空心圈为goodFeaturetoTrack检测结果。
M特征值分解后每个像素点相减的图(也就是Harris阈值判断的图)如下:
黑色实心点为Harris阈值检测结果,白色空心圈为阈值为27时Shi-Tomasi检测结果。
转载请注明出处:http://blog.csdn.net/xiaowei_cqu/article/details/7805206
源码及资料下载: http://download.csdn.net/detail/xiaowei_cqu/4466627
参考资料:
[1] Shi and C. Tomasi. Good Features to Track. Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition, pages 593-600, June 1994.
[2] Richard Szeliski. Computer Vision: Algorithms and Applications. Springer, New York, 2010.
[3] 图像特征点提取PPT http://wenku.baidu.com/view/f61bc369561252d380eb6ef0.html
二、opencv代码实现
harris类
- #ifndef HARRIS_H
- #define HARRIS_H
- #include "opencv2/opencv.hpp"
- class harris
- {
- private:
- cv::Mat cornerStrength; //opencv harris函数检测结果,也就是每个像素的角点响应函数值
- cv::Mat cornerTh; //cornerStrength阈值化的结果
- cv::Mat localMax; //局部最大值结果
- int neighbourhood; //邻域窗口大小
- int aperture;//sobel边缘检测窗口大小(sobel获取各像素点x,y方向的灰度导数)
- double k;
- double maxStrength;//角点响应函数最大值
- double threshold;//阈值除去响应小的值
- int nonMaxSize;//这里采用默认的3,就是最大值抑制的邻域窗口大小
- cv::Mat kernel;//最大值抑制的核,这里也就是膨胀用到的核
- public:
- harris():neighbourhood(3),aperture(3),k(0.01),maxStrength(0.0),threshold(0.01),nonMaxSize(3){
- };
- void setLocalMaxWindowsize(int nonMaxSize){
- this->nonMaxSize = nonMaxSize;
- };
- //计算角点响应函数以及非最大值抑制
- void detect(const cv::Mat &image){
- //opencv自带的角点响应函数计算函数
- cv::cornerHarris (image,cornerStrength,neighbourhood,aperture,k);
- double minStrength;
- //计算最大最小响应值
- cv::minMaxLoc (cornerStrength,&minStrength,&maxStrength);
- cv::Mat dilated;
- //默认3*3核膨胀,膨胀之后,除了局部最大值点和原来相同,其它非局部最大值点被
- //3*3邻域内的最大值点取代
- cv::dilate (cornerStrength,dilated,cv::Mat());
- //与原图相比,只剩下和原图值相同的点,这些点都是局部最大值点,保存到localMax
- cv::compare(cornerStrength,dilated,localMax,cv::CMP_EQ);
- }
- //获取角点图
- cv::Mat getCornerMap(double qualityLevel) {
- cv::Mat cornerMap;
- // 根据角点响应最大值计算阈值
- threshold= qualityLevel*maxStrength;
- cv::threshold(cornerStrength,cornerTh,
- threshold,255,cv::THRESH_BINARY);
- // 转为8-bit图
- cornerTh.convertTo(cornerMap,CV_8U);
- // 和局部最大值图与,剩下角点局部最大值图,即:完成非最大值抑制
- cv::bitwise_and(cornerMap,localMax,cornerMap);
- return cornerMap;
- }
- void getCorners(std::vector<cv::Point> &points,
- double qualityLevel) {
- //获取角点图
- cv::Mat cornerMap= getCornerMap(qualityLevel);
- // 获取角点
- getCorners(points, cornerMap);
- }
- // 遍历全图,获得角点
- void getCorners(std::vector<cv::Point> &points,
- const cv::Mat& cornerMap) {
- for( int y = 0; y < cornerMap.rows; y++ ) {
- const uchar* rowPtr = cornerMap.ptr<uchar>(y);
- for( int x = 0; x < cornerMap.cols; x++ ) {
- // 非零点就是角点
- if (rowPtr[x]) {
- points.push_back(cv::Point(x,y));
- }
- }
- }
- }
- //用圈圈标记角点
- void drawOnImage(cv::Mat &image,
- const std::vector<cv::Point> &points,
- cv::Scalar color= cv::Scalar(255,255,255),
- int radius=3, int thickness=2) {
- std::vector<cv::Point>::const_iterator it=points.begin();
- while (it!=points.end()) {
- // 角点处画圈
- cv::circle(image,*it,radius,color,thickness);
- ++it;
- }
- }
- };
- #endif // HARRIS_H
- cv::Mat image, image1 = cv::imread ("test.jpg");
- //灰度变换
- cv::cvtColor (image1,image,CV_BGR2GRAY);
- // 经典的harris角点方法
- harris Harris;
- // 计算角点
- Harris.detect(image);
- //获得角点
- std::vector<cv::Point> pts;
- Harris.getCorners(pts,0.01);
- // 标记角点
- Harris.drawOnImage(image,pts);
- cv::namedWindow ("harris");
- cv::imshow ("harris",image);
- cv::waitKey (0);
- return 0;
三、改进的Harris角点检测
从经典的Harris角点检测方法不难看出,该算法的稳定性和k有关,而k是个经验值,不好把握,浮动也有可能较大。鉴于此,改进的Harris方法()直接计算出两个特征值,通过比较两个特征值直接分类,这样就不用计算Harris响应函数了。
另一方面,我们不再用非极大值抑制了,而选取容忍距离:容忍距离内只有一个特征点。
该算法首先选取一个具有最大 最小特征值的点(即:max(min(e1,e2)),e1,e2是harris矩阵的特征值)作为角点,然后依次按照最大最小特征值顺序寻找余下的角点,当然和前一角点距离在容忍距离内的新角点呗忽略。
opencv测试该算法代码如下:
- cv::Mat image, image1 = cv::imread ("test.jpg");
- //灰度变换
- cv::cvtColor (image1,image,CV_BGR2GRAY);
- // 改进的harris角点检测方法
- std::vector<cv::Point> corners;
- cv::goodFeaturesToTrack(image,corners,
- 200,
- //角点最大数目
- 0.01,
- // 质量等级,这里是0.01*max(min(e1,e2)),e1,e2是harris矩阵的特征值
- 10);
- // 两个角点之间的距离容忍度
- harris().drawOnImage(image,corners);//标记角点
四、FAST角点检测
算法原理比较简单,但实时性很强。
该算法的角点定义为:若某像素点圆形邻域圆周上有3/4的点和该像素点不同(编程时不超过某阈值th),则认为该点就是候选角点。opencv更极端,选用半径为3的圆周上(上下左右)四个点,若超过三个点和该像素点不同,则该点为候选角点。
和Harris算法类似,该算法需要非极大值抑制。
opencv代码:
- cv::Mat image, image1 = cv::imread ("test.jpg");
- cv::cvtColor (image1,image,CV_BGR2GRAY);
- //快速角点检测
- std::vector<cv::KeyPoint> keypoints;
- cv::FastFeatureDetector fast(40,true);
- fast .detect (image,keypoints);
- cv::drawKeypoints (image,keypoints,image,cv::Scalar::all(255),cv::DrawMatchesFlags::DRAW_OVER_OUTIMG);
测试结果如下: