大家好,欢迎来到IT知识分享网。
前言:
1)基于图像边缘轮廓特征的方法。此方法需要经过图像预分割、轮廓链码提取和角点检测,如基于边界曲率的角点检测,基于边界小波变换的角点检测以及基于边界链码的角点检测。
2)基于图像灰度信息的方法。此方法主要通过计算曲率及梯度进行角点检测,通过计算边缘的曲率来判断角点的存在性。典型代表有Harris算法、Susan算法、Moravec算法等。
一、Harris算法介绍
1.1 Harris算法原理
在图像中搜索有价值的特征点时,使用角点是一种不错的方法。角点是很容易在图像中定位的局部特征,并且大量存在于人造物体中(例如墙壁、门、窗户、桌子等产生的角点)。角点的价值在于它是两条边缘线的接合点,是一种二维特征,可以被精确的定位。与此相反的是位于均匀区域或物体轮廓上的点以及在同一物体的不同图像上很难重复精确定位的点。Harris特征检测就是检测角点的经典方法。接下来将详细探讨这个方法。
如果在各个方向上移动这个小窗口,窗口内的灰度发生了较大的变化,那么说明窗口内存在角点。如果在各个方向移动,灰度几乎不变,说明是平坦区域;如果只沿着某一个方向移动,灰度几乎不变,说明是直线;如果沿各个方向移动,灰度均发生变化,说明是角点。
设图像窗口平移,产生的灰度变化为
,则:
由得:
由于:
于是对于局部微小的移动量,可以近似得到下面的表达:
为二维矩阵,且:
其中:
其中,表示 x 方向的梯度,
表示 y 方向的梯度,
为高斯函数。矩阵
的特征值是自相关函数的一阶曲率。
特征值的大小与特征点的性质息息相关。即当两个特征值都比较小时,则此点可能位于平坦区,不为角点或边界点; 当两个特征值一个较大、而另一个却相对较小时,则此点位于边界上,属于边界
点; 当两个特征值均相对较大时,则此点沿任意方向的曲率都较大,为需要提取的角点。
一般地,为了避免计算矩阵的特征值,常通过下式来计算特征点的响应函数,亦称兴趣值:
其中, 为矩阵
的行列式,
为矩阵
的迹,即
特征值之和。
为经验值常数,一般在 0. 04 ~ 0.06 范围内取值。为了计算方便,文中采用如下角点响应函数计算兴趣值从而判定出角点,即:
其中,ε 表示任意小的正数。显然采用这中方式计算可以避免 选择的随机性。
1.2 Harris算法步骤
(1)利用差分算子对图像进行滤波,并计算图像中每个像素点的 、
以及
;
(2) 对 、
以及
进行高斯平滑,以去除噪声。在此过程中可通过归一化将模板参数和置 1;
(3) 计算原图像每个对应点的兴趣值 ,并进行局部非极大值抑制;
(4) 设定阈值,如果兴趣值大于这个阈值并且在某领域内是局部最大值,则认为该点是需要提取的角点。
1.3 Harris算法的不足
虽然 Harris 算法相较于其他众多基于灰度的角点提取算法具有明显的优势,但它仍然存在一些不足:
在经典的 Harris 角点检测中,当对角点的兴趣值进行非极大值抑制来确定局部极大值的时候,角点的提取效果几乎完全由设定的阈值大小决定。而阈值的大小也与所提取的角点数量息息相关,一般情况下,阈值越大提取的角点越少,极易造成正确角点的丢失; 阈值越小提取的角点数越多,也会带来很多伪角点。因此,在运用 Harris 算法进行角点检测时,阈值这个经验值的选取和设定对角点提取具有很大的影响。
二、代码实现
#include <opencv2/opencv.hpp> #include <iostream> #include <math.h> using namespace cv; using namespace std; // void ConvertBGR_img2GRAY_img(const Mat &BGR_img, Mat &Gray_img) { //这个函数实现将彩色图转化成灰度图,利用常用公式:Gray = R*0.299 + G*0.587 + B*0.114进行自编程实现 //第一个参数BGR_img表示输入的彩色RGB图像的引用; //第二个参数Gray_img表示转换后输出的灰度图像的引用; if (BGR_img.empty() || BGR_img.channels() != 3) { return; } Gray_img = Mat::zeros(BGR_img.size(), CV_8UC1); //创建一张与输入图像同大小的单通道灰度图像 uchar *pointImage = BGR_img.data; //取出存储图像像素的数组的指针 uchar *pointImageGray = Gray_img.data; int height = BGR_img.rows; int width = BGR_img.cols; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { *(pointImageGray + i*Gray_img.step[0] + j*Gray_img.step[1]) = (uchar)(0.114*(pointImage + i*BGR_img.step[0] + j*BGR_img.step[1])[0] + 0.587*(pointImage + i*BGR_img.step[0] + j*BGR_img.step[1])[1] + 0.299*(pointImage + i*BGR_img.step[0] + j*BGR_img.step[1])[2]); } } } // /*/ //非极大值抑制和阈值检测 void LocalMaxValue(Mat &resultData, Mat &ResultImage, int kSize) { int r = kSize / 2; for (int i = r; i < ResultImage.rows - r; i++) { for (int j = r; j < ResultImage.cols - r; j++) { if (resultData.at<double>(i, j) > resultData.at<double>(i - 1, j - 1) && resultData.at<double>(i, j) > resultData.at<double>(i - 1, j) && resultData.at<double>(i, j) > resultData.at<double>(i - 1, j + 1) && resultData.at<double>(i, j) > resultData.at<double>(i, j - 1) && resultData.at<double>(i, j) > resultData.at<double>(i, j + 1) && resultData.at<double>(i, j) > resultData.at<double>(i + 1, j - 1) && resultData.at<double>(i, j) > resultData.at<double>(i + 1, j) && resultData.at<double>(i, j) > resultData.at<double>(i + 1, j + 1)) { if ((int)resultData.at<double>(i, j) >15000) { circle(ResultImage, Point(j, i), 5, Scalar(0, 0, 255), 2, 8, 0); } } } } } /*/ // 主函数 int main() { const Mat src_img = imread("test11.png"); if (src_img.empty()) { printf("could not load image...\n"); return -1; } namedWindow("原图:", CV_WINDOW_AUTOSIZE); imshow("原图:", src_img); // 将彩色图转化为灰度图,调用OpenCV提供的cvtColor接口 Mat gray_img; ConvertBGR_img2GRAY_img(src_img, gray_img); namedWindow("灰度图", CV_WINDOW_AUTOSIZE); imshow("灰度图", gray_img); // 第一步:用sobel算子计算度图像的水平和垂直方向上的差分 Mat image_con_sobel_Ix, image_con_sobel_Iy; //image_con_sobel_Ix = sobel(gray_img,1,0, 3, BORDER_DEFAULT); //image_con_sobel_Iy = sobel(gray_img,0,1, 3, BORDER_DEFAULT); Sobel(gray_img, image_con_sobel_Ix, CV_64F, 1, 0, 3); Sobel(gray_img, image_con_sobel_Iy, CV_64F, 0, 1, 3); //convertScaleAbs(image_con_sobel_Ix, image_con_sobel_Ix); //convertScaleAbs(image_con_sobel_Iy, image_con_sobel_Iy); // 第二步:计算Ixx,Iyy,Ixy Mat image_con_sobel_Ixx, image_con_sobel_Iyy, image_con_sobel_Ixy; multiply(image_con_sobel_Ix, image_con_sobel_Ix, image_con_sobel_Ixx, 1.0, CV_64FC1); multiply(image_con_sobel_Iy, image_con_sobel_Iy, image_con_sobel_Iyy, 1.0, CV_64FC1); multiply(image_con_sobel_Ix, image_con_sobel_Iy, image_con_sobel_Ixy, 1.0, CV_64FC1); //第三步:对Ixx,Iyy,Ixy进行高斯平滑 Mat blurimage_Ixx, blurimage_Iyy, blurimage_Ixy; GaussianBlur(image_con_sobel_Ixx, blurimage_Ixx, Size(3, 3), 5); GaussianBlur(image_con_sobel_Iyy, blurimage_Iyy, Size(3, 3), 5); GaussianBlur(image_con_sobel_Ixy, blurimage_Ixy, Size(3, 3), 5); //第四步:计算响应函数,为避免K值的随机性,我采用另外一种响应函数表达式 Mat Response_data; Response_data = (blurimage_Ixx.mul(blurimage_Iyy) - blurimage_Ixy.mul(blurimage_Ixy)) / (blurimage_Ixx + blurimage_Iyy); //第五步:非极大值抑制和阀值检测 Mat ResultImage = gray_img.clone(); LocalMaxValue(Response_data, ResultImage, 3); namedWindow("结果", CV_WINDOW_AUTOSIZE); imshow("结果", ResultImage); waitKey(0); return 0; }
运行程序,结果如下:
单独观察:
三、使用API实现Harris角点检测
在OpenCV中提供了cornerHarris() 函数来对图像实现Harris角点检测。其函数原型和参数解析:
其中:
- 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位或者浮点型图像。
- 第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放Harris角点检测的输出结果,和源图片有一样的尺寸和类型。
- 第三个参数,int类型的blockSize,表示邻域的大小,对每个像素,考虑blockSize×blockSize大小的邻域,在邻域上计算图像的2×2梯度的协方差矩阵M。
- 第四个参数,int类型的ksize,为Soble算子核尺寸,如果小于0,采用3×3的Scharr滤波器。
- 第五个参数,double类型的k,为角点响应函数中的经验常数(0.04~0.06)。
- 第六个参数,int类型的borderType,图像像素的边界模式,注意它有默认值BORDER_DEFAULT。更详细的解释,参考borderInterpolate( )函数。
代码实现:
#include <opencv2/opencv.hpp> #include <iostream> #include <math.h> using namespace cv; using namespace std; string out_title = "Harris角点检测"; int thresh = 100; int max_count = 255; Mat src_img, gray_img; void Harris_callback(int, void*); int main() { src_img = imread("test11.png"); if (src_img.empty()) { printf("could not load image...\n"); return -1; } namedWindow("原图:", CV_WINDOW_AUTOSIZE); imshow("原图:", src_img); namedWindow(out_title, CV_WINDOW_AUTOSIZE); cvtColor(src_img, gray_img, CV_BGR2GRAY); createTrackbar("阈值", out_title, &thresh, max_count, Harris_callback); Harris_callback(0, 0); waitKey(0); return 0; } void Harris_callback(int, void*) { Mat dst_img, norm_dst_img, scale_dst_img; dst_img = Mat::zeros(gray_img.size(), CV_32FC1); int blockSize = 2; int ksize = 3; double k = 0.04; cornerHarris(gray_img, dst_img, blockSize, ksize, k, BORDER_DEFAULT); //调用API normalize(dst_img, norm_dst_img, 0, 255, NORM_MINMAX, CV_32FC1, Mat()); // 调用归一化函数进行归一化处理 convertScaleAbs(norm_dst_img, scale_dst_img); //将图像转化为8位图 Mat result_img = src_img.clone(); for (int r = 0; r < result_img.rows; ++r) { uchar *ptr_row = scale_dst_img.ptr<uchar>(r); for (int c = 0; c < result_img.cols; ++c) { int value = int(ptr_row[c]); if (value > thresh) { circle(result_img, Point(c, r), 2, Scalar(0, 0, 255), 2, 8, 0); } } } imshow(out_title, result_img); }
运行程序,结构如下:
拖动TrackBar以获得不同阈值条件下的结果:
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/153405.html