`

openCV入门----霍夫变换直线检测(CvHoughLine2的使用)

 
阅读更多

      上回书说道,霍夫变换检测直线的原理,以及自己编程实现,那么今天我就来使用openCV里的函数来完成这个有意思的任务,看看能收获些什么呢?

      前面我们利用openCV处理过平滑图像(高斯滤波)、边缘检测(canny算法),这些都是直接调用后可以直接得出图像的结果,然后显示出来,然而霍夫变换相对来说复杂一点点,它返回的结果是直线的参数,那么什么是直线的参数呢?

      这个问题我后面再来解释,先看看openCV里处理霍夫变换检测直线的函数有哪些?

      1.  CvHoughLine(...)

      2.  CvHoughLine2(...)

      openCV里有两个函数(比较常用)处理霍夫变换直线检测,有什么区别呢。

      CvHoughLine:是用于标准的霍夫变换方法

      CvHoughLine2:可以使用三种霍夫变换的方法,分别是标准霍夫变换(SHT)、多尺度标准霍夫变换(MSHT)、累计概率霍夫变换(PPHT)。

这些个方法是什么东西呢?暂且我们不管这个问题,但是很直观地告诉我们第二个比第一个要好,方法选择更加多,一看就是前者的升级进化版,所以我确定要学习第二个方法,那么第一个方法也自然水到渠成了。接下来我们再来具体探讨第二个方法的使用。

 

      首先,我们就从上述提到的三种方法开始解释,那三种霍夫变换究竟是什么东东呢?其实到现在我也只是简单地了解一下这玩意可能是什么,却也说不出究竟是个啥,怎么做出来的(这是不是就说明了没有经自己的手编程,那么永远都是门外汉或者说要经过长时间的纠结才能入门这个事实呢,所以有些玩意还是要自己动手才能明白的!),但是目前这个阶段,了解一下也就可以了:

     上述说是三种方法,其实归结到底是两种方法,(1)标准霍夫变换 (2)累计概率霍夫变换

     标准霍夫变换实际上就是我们上回书探讨的坐标变换的那个方法,详细细节请参考上一篇博客;

     PPHT实际上是标准霍夫变换的一个变种,计算单独线段的方向以及范围。之所以称之是概率,是因为并不将累加器平面内的所有可能得点相累加,而是只累加其中的一部分。该想法是如果峰值将要足够的高,只用一小部分时间去寻找它就足够了,这样可以大大节省时间。

      解释如上,摘自《Learning openCV》,但是总有一种看了跟没看的感觉,总的来说,主要了解一下一下几点就好了吧(个人认为):

      1.标准霍夫变换(SHT)就是满大街、书上、网上都找到的基本上相同的解释,如同我上篇介绍的那样;

      2.SHT返回的是直线的方向;

      3.PPHT以概率的方式操作累加器;

      4.PPHT可以大大节省时间;

      5.PPHT返回的是直线的方向和范围。

     知道了这几个区别,我相信聪明的你看出来了,哪个更加牛逼一些-----PPHT!不说别的,光从返回值来说,PPHT方法返回的信息将更加丰富,对编程者来说也意味着更加nice!

 

     接着,知道了以上比较笼统的概念后,我们来细看这个函数:

函数原型:

CvSeq* cvHoughLines2(
CvArr* image,
void* line_storage,
int mehtod,
double rho,
double theta,
int threshold,
double param1 =0,
double param2 =0
);

 

image
输入 8-比特、单通道 (二值) 图像,当用CV_HOUGH_PROBABILISTIC方法检测的时候其内容会被函数改变。
line_storage
检测到的线段存储仓. 可以是内存存储仓 (此种情况下,一个线段序列在存储仓中被创建,并且由函数返回),或者是包含线段参数的特殊类型(见下面)的具有单行/单列的矩阵(CvMat*)。矩阵头为函数所修改,使得它的 cols/rows 将包含一组检测到的线段。如果 line_storage 是矩阵,而实际线段的数目超过矩阵尺寸,那么最大可能数目的线段被返回(线段没有按照长度、可信度或其它指标排序).
method
Hough 变换变量,是下面变量的其中之一:
CV_HOUGH_STANDARD - 传统或标准 Hough 变换. 每一个线段由两个浮点数 (ρ, θ) 表示,其中 ρ 是直线与原点 (0,0) 之间的距离,θ 线段与 x-轴之间的夹角。因此,矩阵类型必须是 CV_32FC2 type.
CV_HOUGH_PROBABILISTIC - 概率 Hough 变换(如果图像包含一些长的线性分割,则效率更高). 它返回线段分割而不是整个线段。每个分割用起点和终点来表示,所以矩阵(或创建的序列)类型是 CV_32SC4.
CV_HOUGH_MULTI_SCALE - 传统 Hough 变换的多尺度变种。线段的编码方式与 CV_HOUGH_STANDARD 的一致。
rho
与像素相关单位的距离精度
theta
弧度测量的角度精度
threshold
阈值参数。如果相应的累计值大于 threshold, 则函数返回这条线段.
param1
第一个方法相关的参数:
对传统 Hough 变换,不使用(0).
对概率 Hough 变换,它是最小线段长度.
多尺度 Hough 变换,它是距离精度 rho 的分母 (大致的距离精度是 rho 而精确的应该是 rho / param1 ).
param2
第二个方法相关参数:
对传统 Hough 变换,不使用 (0).
对概率 Hough 变换,这个参数表示在同一条直线上进行碎线段连接的最大间隔值(gap), 即当同一条直线上的两条碎线段之间的间隔小于param2时,将其合二为一。
对多尺度 Hough 变换,它是角度精度 theta 的分母 (大致的角度精度是 theta 而精确的角度应该是 theta / param2).

(以上函数原型到参数解释内容摘抄自百度百科http://baike.baidu.com/link?url=cTXxnE2iR076usVBFRJq63PkwCLkiAvwLEMoeFUQPOdzFyc4eK-GBiVxNDI34dzo_8jbH5y1zQoEsgC93sKMrq另外解释一下,我一字不差的照搬过来并不是我懒,主要是,这玩意这没什么好写的。)

上面都是比较官方的话,接下来我想提几个我觉应该注意的问题:

1.参数返回类型:参数返回类型是CvSeq指针,CvSeq是openCV里一个很重要的数据结构,称之为“序列”,实际上类似于队列、链表这种数据结构,在后续学习笔记中我将专门探讨这种数据结构。

2.参数返回值:这里提到的返回值是针对不同的霍夫变换方法有不同的返回值,决定我们用怎样的方法来将这个返回值还原到直线。下面具体解释:

         (1)针对SHT(包括MSHT)方法,CvHoughLine2方法的返回值是直线极坐标方程的Rho、Theta参数。由于返回值是CvSeq类型,那么,我们令一个double类型的数值指针来得到这个序列中的结果:

                           double*  x = (double*)CvGetSeqElem(seq, i);

那么            

                           x[0]   -------------   Rho

                           x[1]   -------------   Theta

           那么可想而知,我们还原直线只能得到直线的方向信息,而不能得到范围信息,那么如何还原呢?主要有一下两种方法: 

               1> 
                
                如上图所示,表示的是直线可能出现的两种情况,其他情况本质上是一样的,这里不再赘述。

                第一种方法,实际上是分类讨论的思想,将特殊情况(直线平行于两坐标轴)与普通情况分开来计算,计算方法如下:

                情况1:直线平行于X轴,则令A点为(0,Rho),令B点为(img->width,Rho),直线AB即被检测直线的方向;

                情况2:直线平行于Y轴,则令A点为(Rho,0),令B点为(Rho,img->height),直线AB即被检测直线的方向;

                情况3:普通直线,令A点为(0,Rho/sin(Theta)),令B点为(0,Rho/cos(Theta)),直线AB即被检测直线的方向。

                实际上,这种方法就是通过得到被检测直线(注意这里的直线表示的就是没有范围的,只表示方向,实际上也是,线段才有范围的概念)与上图X,Y轴的交点,从而确定直线方向。初看可能很多人会误解这不就是第一张图的情况吗?其实所有的情况都是如此,直线总会与X,Y轴有交点,而交点坐标正是如上所计算。

          2>
                                   

          如图所示,我们人为设置两个比较大的数a,b(这里可以令a=b),使得图上的A,B两点计算出来再图片以外,这样可以得到A,B两点的坐标:A(X0 + b*sin(Theta)  ,  Y0 + b*cos(Theta)) B(X0 - a*sin(Theta) , Y0 - a*cos(Theta))。AB直线即检测直线的方向。
          最后上关键部分代码,并作出适当地解释:

 

          cvCvtColor(scr, gray, CV_BGR2GRAY);//转换成灰度图像

          注意:Hough变换的图像输入参数只可以接受八位图像

 

          //方法1: SHT----标准霍夫变换
 CvMemStorage* storage = cvCreateMemStorage(0);//创建一个默认大小的动态存储空间
 CvSeq* line = cvHoughLines2(gray, storage, CV_HOUGH_STANDARD, 1, CV_PI / 500, 200);//霍夫变换得到一个线的序列

 //方法2: PPHT----累计概率霍夫变换
 CvMemStorage* storage2 = cvCreateMemStorage(0);//创建一个默认大小的动态存储空间
 CvSeq* line2 = cvHoughLines2(gray, storage2, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 50, 100, 10);//霍夫变换得到一个线的序列

 //方法3:  SHT的多尺度变换
 CvMemStorage* storage3 = cvCreateMemStorage(0);//创建一个默认大小的动态存储空间
 CvSeq* line3 = cvHoughLines2(gray, storage3, CV_HOUGH_MULTI_SCALE, 1, CV_PI / 180, 120, 100, 10);//霍夫变换得到一个线的序列            

              
 /*
 在结果图像上画出检测的结果
 */

//对于SHT和MSHT方法来说,返回的是Rho值和theta值,如下还原
 for (int i = 0; i < MIN(line->total, 100); i++)
 {
  float* lines = (float*)cvGetSeqElem(line, i);
  float rho = lines[0];
  float theta = lines[1];
  CvPoint 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));
  cvLine(result, pt1, pt2, CV_RGB(255, 0, 0));
 }

for (int i = 0; i < line2->total; ++i)//对于PPHT方法,返回的就是直线端点的坐标。于是需要画出来就可以了
 {
  CvPoint* point = (CvPoint*)cvGetSeqElem(line2, i);
  cvLine(result2, point[0], point[1], CV_RGB(255, 0, 0));
  cout << "2 : " <<point[0].x << " " << point[0].y << endl;
  cout << point[1].x << " " << point[1].y << endl << endl;
 }

 

实验结果:

 

不妨对比一下上一个实验(自己编写的程序)的效果,openCV效果尤其是PPHT方法的效果简直是赞!

  • 大小: 3.5 KB
  • 大小: 3.9 KB
  • 大小: 5.8 KB
  • 大小: 91.7 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics