本文参考@浅墨_毛星云 文章链接: http://blog.csdn.net/poem_qianmo/article/details/26977557
今天学习了教程【OpenCV入门教程之十四】OpenCV霍夫变换:霍夫线变换,霍夫圆变换合辑,表示对霍夫线变换如何由概念转化为代码,理解了很久,坐标图都画了好几页,因为是小白,理论知识不清楚,所以遇到一些问题,可能对有些人很基础,但对于我来说,还是难理解,不过终于明白,代码是怎么来的了,一定要好好记录一下,以免日后忘记~
还是老步骤,整理笔记
OpenCV中霍夫变换相关的知识点,以及了解了OpenCV中实现霍夫线变换的HoughLines、HoughLinesP函数的使用方法,实现霍夫圆变换的HoughCircles函数的使用方法。
一、引言
二、霍夫变换概述
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
int main()
{
//载入原始图和Mat变量定义
Mat src=imread("img/line1.png",1);
Mat dst,mid;
//进行边缘检测和转化为灰度图
Canny(src,mid,50,200,3);//进行一此canny边缘检测
cvtColor(mid,dst,CV_GRAY2BGR);//转化边缘检测后的灰度图转化为三通道图
//进行霍夫线变换
vector lines;//定义一个矢量结构lines用于存放得到的线段矢量集合
HoughLines(mid,lines,1,CV_PI/180,150,0,0);
//依次在图中绘制出每条线段
for (size_t i = 0; i < lines.size(); i++)
{
float rho=lines[i][0],theta=lines[i][1];
Point pt1,pt2;
double a=cos(theta),b=sin(theta);
double x0=a*rho,y0=b*rho;
//为什么这样?有配套的图片解释见下面效果图之后的补充部分,1000表示点(x0,y0),在直线,向上,向下1000的距离
pt1.x=cvRound(x0+1000*(-b));
pt1.y=cvRound(y0+1000*(a));
pt2.x=cvRound(x0-1000*(-b));
pt2.y=cvRound(y0-1000*(a));
line(dst,pt1,pt2,Scalar(100,200,100),1,CV_AA);
}
//显示原始图
imshow("1",src);
//边缘检测后的图
imshow("2",mid);
//显示效果图
imshow("3",dst);
waitKey(0);
return 0;
}
canny检测的边缘图:
对应的HoughLines变换的效果图:
补充部分:pt1.x=cvRound(x0+1000*(-b));有些不理解?pt1与pt2点的计算方法 。 这里已知下图中的rh0 和θ,现在只需要求图中“任意”两点, 就能用cvLine画出pt1 -> pt2的直线 。 看看下页图, 就明白这里1000什么的是为什么了。
这里是取了点(x0,y0)在直线上上下1000的距离,那么用cvLine画出来的线段就是从pt1 -> pt2的了。那么pt1->pt2的直线距离就是2000。可以取其他的距离,不一定去1000,如600也可以。1.这个地方也许会出现检测出来的线段长度比pt1->pt2还大,即包含了我们画的线段 , 这是肯定的。2. 还会出现本来线段没有pt1->pt2这么长,那么我们画的就会过长了。 也是肯定会出现的情况。因为:CV_HOUGH_STANDARD方法 只能得出rh0 和 θ的值。 这两个值只能确定直线,而不能确定线段是从哪开始到哪结束。
通过上面的关系,可以推导出下面的式子成立:
pt1.x=cvRound(x0+1000*(-b));
pt1.y=cvRound(y0+1000*(a));
pt2.x=cvRound(x0-1000*(-b));
pt2.y=cvRound(y0-1000*(a));
#include
#include
#include
#include
using namespace cv;
using namespace std;
int main()
{
Mat src,dst,mid;
src=imread("img/line2.png",1);
//进行边缘检测和转化为灰度图
Canny(src,mid,80,200,3);///进行一次canny边缘检测
cvtColor(mid,dst,CV_GRAY2BGR);//将边缘检测后的灰度图转化为BRG三通道图
//进行霍夫线变换
vector lines;//定义一个矢量结构lines用于存放得到的线段矢量集合(vec4i)整型
HoughLinesP(mid,lines,1,CV_PI/180,40,20,3);
//依次在图中绘制出每条线段
for (int i = 0; i
canny检测边缘图:
HoughLinesP变换效果图:
四、霍夫圆变换
HoughCircles( )函数示例代码
#include
#include
using namespace cv;
using namespace std;
int main()
{
Mat src,dst,mid;
src=imread("img/circle.png");
imshow("1",src);
//转为灰度图,进行GaussianBlur图像平滑
cvtColor(src,mid,CV_BGR2GRAY);
GaussianBlur(mid,mid,Size(9,9),0,0);
//进行霍夫圆变换,每个向量中包含3个值,x,y,r所以
vector circles;
//第4,5个参数需要反复测试,不然检测出来的圆个数会很多
HoughCircles(mid,circles,CV_HOUGH_GRADIENT,1.5,60,200,100,0,0);
//依次在图中绘制出圆
for (int i = 0; i < circles.size(); i++)
{
//遍历每个圆轨的圆心center
Point center(cvRound(circles[i][0]),cvRound(circles[i][1]));
//遍历每个圆轨迹的半径r
int r=cvRound(circles[i][2]);
//绘制圆心 当第5个参数为负数时,表示填充,当为正数时,表示画圆的粗细
circle(src,center,3,Scalar(0,0,100),-1,8,0);
//绘制圆轮廓
circle(src,center,r,Scalar(10,100,30),2,8,0);
}
imshow("2",src);
waitKey(0);
return 0;
}
原图:
HoughCircles变换效果图:
五、综合案例
的HoughLinesP函数的基础上,为其添加了用于控制其第五个参数阈值threshold的滚动条。能通过调节滚动条,改变阈值,动态地控制霍夫线变换检测的线条多少。
代码如下:
#include
#include
using namespace cv;
using namespace std;
//原始图、中间图和效果图
Mat src,dst,mid;
//定义一个矢量结构lines用于存放得到的线段矢量集合
vector lines;
//变量接收的TrackBar位置参数
int HoughLinesP_value=100;
//回调函数
void HoughLinesP_on(int,void*)
{
//定义局部变量储存全局变量
Mat mid_p=mid.clone();
Mat dst_p=dst.clone();
vector mylines;
//调用HoughLinesP函数
HoughLinesP(mid_p,mylines,1,CV_PI/180,HoughLinesP_value+1,50,10);
//循环遍历绘制每一条线段
for (int i = 0; i < mylines.size(); i++)
{
Vec4i ll=mylines[i];
Point p1(ll[0],ll[1]),p2(ll[2],ll[3]);
line(dst_p,p1,p2,Scalar(100,200,100),1,8);
}
//显示图像
imshow("效果图",dst_p);
}
int main()
{
src=imread("img/line1.png",1);
imshow("原图",src);
//进行边缘检测和转化为灰度图
Canny(src,mid,50,200,3);
cvtColor(mid,dst,CV_GRAY2BGR);
//创建滚动条
namedWindow("效果图",1);
createTrackbar("阈值","效果图",&HoughLinesP_value,200,HoughLinesP_on);
//调用一次回调函数,调用一次HoughLinesP函数
HoughLinesP_on(HoughLinesP_value,0);
HoughLinesP(mid,lines,1,CV_PI/180,80,50,10);
imshow("效果图",dst);
waitKey(0);
return 0;
}
不同阈值对应的HoughLinesP霍夫变换效果图: