霍夫变换直线检测理解

举报
风吹稻花香 发表于 2021/06/04 23:31:53 2021/06/04
【摘要】 一步一步来: 1、在白纸上画出一个直角坐标系,任意给出一个点; 2、那么,对于点(x0,y0),经过这个点的直线必定满足y0=k*x0+b,其中k是直线的斜率,b是直线的截距; 3、上式可以化成b=y0-k*x0, 可以看作是以-x0为斜率,以y0为截距,在k-b空间上的一个直线方程(k,b为变量); 4、可见,k-b空间上的一条直线,代表了x-y空间经过特...

一步一步来:

1、在白纸上画出一个直角坐标系,任意给出一个点;

2、那么,对于点(x0,y0),经过这个点的直线必定满足y0=k*x0+b,其中k是直线的斜率,b是直线的截距;

3、上式可以化成b=y0-k*x0, 可以看作是以-x0为斜率,以y0为截距,在k-b空间上的一个直线方程(k,b为变量);

4、可见,k-b空间上的一条直线,代表了x-y空间经过特定点的所有直线,而x-y上的特定直线责备k-b空间上的特定点表示;

 

利用这个原理,我们可以通过一下方法检测可能出线的直线:

1、得到一副边缘图像;

2、对图像中的每一个边缘点,在k-b空间中画出一条直线;

3、在各直线的交点,我们采取“投票”(vote)的方法,即累加:n条直线的交点,改点的值为n;

4、遍历k-b空间,寻找出先局部最大值(极值)的点,这些点的坐标(k,b)就是图像中可能出线的直线的斜率和截距;

 

  为了容易理解,这里采用了直线的斜截表达法。

  事实上这种方法并不使用,因为某些直线的斜率很大的甚至不存在。

  实际操作中,检测直线的霍夫变换使用含极坐标参数的直线表示型式,简称极坐标式(不是极坐标方程,因为还是在笛卡尔坐标下表示)

其中的两个参数的意义如下图:

 

 

这样,每条直线对应于theta-p空间下的一条正弦曲线,同样采用投票求极值的方法寻找曲线

霍夫变换直线检测的matlab实现:

这里涉及到三个函数:hough,houghpeaks,houghlines:

1、[H,T,R] =hough(BW,'Theta',20:0.1:75) ; (输入二值图像BW,角度范围与步进(最大,[-90, 90)),返回H-霍夫空间,T-theta,R-p);

2、PEAKS =houghpeaks(H,NUMPEAKS) ;(输入霍夫空间和极值数量,返回极值的坐标)

3、LINES=houghlines(BW,T,R,Peaks) ; (返回lines是一个包含图像中线段首末点、p、theta的结构体)

 

代码:

I=imread('1.jpg'); 
Ihsv=rgb2hsv(I); 
Iv=Ihsv(:,:,3);%提取v空间 
Ivl=Iv(500:end,:);%截取下半部 
Iedge=edge(Ivl,'sobel');%边沿检测 
Iedge = imdilate(Iedge,ones(3));%图像膨胀
%新建窗口,绘图用 
figure (2)
imshow(Iedge);
hold on
%左方直线检测与绘制 
%得到霍夫空间 
[H1,T1,R1] = hough(Iedge,'Theta',20:0.1:75); 
%求极值点
Peaks=houghpeaks(H1,5); 
%得到线段信息 
lines=houghlines(Iedge,T1,R1,Peaks);

%绘制线段 
for k=1:length(lines) 
 xy=[lines(k).point1;
 lines(k).point2]; 
 plot(xy(:,1),xy(:,2),'LineWidth',4); 
end 
%右方直线检测与绘制 
[H2,T2,R2] = hough(Iedge,'Theta',-75:0.1:-20);
Peaks1=houghpeaks(H2,5); 
lines1=houghlines(Iedge,T2,R2,Peaks1);
for k=1:length(lines1) 
 xy1=[lines1(k).point1;lines1(k).point2];
 plot(xy1(:,1),xy1(:,2),'LineWidth',4);
end
输入图像:


 


还有一种理解思路:

设已知一黑白图像上画了一条直线,要求出这条直线所在的位置。我们知道,直线的方程可以用y=k*x+b 来表示,其中k和b是参数,分别是斜率和截距。过某一点(x0,y0)的所有直线的参数都会满足方程y0=kx0+b。即点(x0,y0)确定了一组直线。方程y0=kx0+b在参数k--b平面上是一条直线,(你也可以是方程b=-x0*k+y0对应的直线)。这样,图像x--y平面上的一个前景像素点就对应到参数平面上的一条直线。我们举个例子说明解决前面那个问题的原理。设图像上的直线是y=x, 我们先取上面的三个点:A(0,0), B(1,1), C(2,2)。可以求出,过A点的直线的参数要满足方程b=0, 过B点的直线的参数要满足方程1=k+b, 过C点的直线的参数要满足方程2=2k+b, 这三个方程就对应着参数平面上的三条直线,而这三条直线会相交于一点(k=1,b=0)。 同理,原图像上直线y=x上的其它点(如(3,3),(4,4)等) 对应参数平面上的直线也会通过点(k=1,b=0)。

  1. 众所周知, 一条直线在图像二维空间可由两个变量表示. 例如:

    1. 在 笛卡尔坐标系: 可由参数: (m,b) 斜率和截距表示.
    2. 在 极坐标系: 可由参数: (r,\theta) 极径和极角表示
    Line variables

    对于霍夫变换, 我们将用 极坐标系 来表示直线. 因此, 直线的表达式可为:

    y = \left ( -\dfrac{\cos \theta}{\sin \theta} \right ) x + \left ( \dfrac{r}{\sin \theta} \right )

    化简得: r = x \cos \theta + y \sin \theta

  2. 一般来说对于点 (x_{0}, y_{0}), 我们可以将通过这个点的一族直线统一定义为:

    r_{\theta} = x_{0} \cdot \cos \theta  + y_{0} \cdot \sin \theta

    这就意味着每一对 (r_{\theta},\theta) 代表一条通过点 (x_{0}, y_{0}) 的直线.

  3. 如果对于一个给定点 (x_{0}, y_{0}) 我们在极坐标对极径极角平面绘出所有通过它的直线, 将得到一条正弦曲线. 例如, 对于给定点 x_{0} = 8and y_{0} = 6 我们可以绘出下图 (在平面 \theta - r):

    Polar plot of a the family of lines of a point

    只绘出满足下列条件的点 r > 0 and 0< \theta < 2 \pi.

  4. 我们可以对图像中所有的点进行上述操作. 如果两个不同点进行上述操作后得到的曲线在平面 \theta - r 相交, 这就意味着它们通过同一条直线. 例如, 接上面的例子我们继续对点: x_{1} = 9y_{1} = 4 和点 x_{2} = 12y_{2} = 3 绘图, 得到下图:

    Polar plot of the family of lines for three points

    这三条曲线在 \theta - r 平面相交于点 (0.925, 9.6), 坐标表示的是参数对 (\theta, r) 或者是说点 (x_{0}, y_{0}), 点 (x_{1}, y_{1}) 和点 (x_{2}, y_{2}) 组成的平面内的的直线.

  5. 那么以上的材料要说明什么呢? 这意味着一般来说, 一条直线能够通过在平面 \theta - r 寻找交于一点的曲线数量来 检测. 越多曲线交于一点也就意味着这个交点表示的直线由更多的点组成. 一般来说我们可以通过设置直线上点的 阈值 来定义多少条曲线交于一点我们才认为 检测 到了一条直线.

  6. 这就是霍夫线变换要做的. 它追踪图像中每个点对应曲线间的交点. 如果交于一点的曲线的数量超过了 阈值, 那么可以认为这个交点所代表的参数对 (\theta, r_{\theta}) 在原图像中为一条直线.

  7. 霍夫变换直线检测的opencv实现:

    int main( )
  8. {
     //1】载入原始图和Mat变量定义
     Mat srcImage = imread("123.jpg");  //工程目录下应该有一张名为1.jpg的素材图
     Mat midImage,dstImage;//临时变量和目标图的定义
     //2】进行边缘检测和转化为灰度图
     cv::Canny(srcImage, midImage, 50, 200, 3);//进行一此canny边缘检测
     cvtColor(midImage,dstImage, CV_GRAY2BGR);//转化边缘检测后的图为灰度图
     //3】进行霍夫线变换
     std::vector<Vec2f> lines;//定义一个矢量结构lines用于存放得到的线段矢量集合
     HoughLines(midImage, lines, 1, CV_PI/180, 150, 0, 0 );
     //4】依次在图中绘制出每条线段
     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;
     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( dstImage, pt1, pt2, Scalar(55,100,195), 1, CV_AA);
     }
     //5】显示原始图
     imshow("【原始图】", srcImage);
     //6】边缘检测后的图
     imshow("【边缘检测后的图】", midImage);
     //7】显示效果图
     imshow("【效果图】", dstImage);
     waitKey(0);
     return 0;
    }
    
     


文章来源: blog.csdn.net,作者:网奇,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/jacke121/article/details/54799970

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。