opencv学习笔记(二十四)霍夫线变换

来源:互联网 发布:2016网络十大神曲 编辑:程序博客网 时间:2024/05/16 17:48

霍夫变换

霍夫变换是一种在图像中寻找直线.、圆及其他简单形状的方法。
原始的霍夫变化是一种直线变换,即在二值图像中寻找直线的一种相对快速方法。变换可以推广到其他普通的情况,而不仅仅是简单的直线。
这个网站原理说的挺清楚
http://www.tuicool.com/articles/Mn2EBn

霍夫线变换

霍夫直线变换的基本理论是二值图像中的任何点都可能是一些候选直线集合的一部分。如果要确定每条线进行参数化,例如一个斜率a和截距b,原始图像中的一点会变换为(a,b)平面上的轨迹,轨迹上的点对应着所有过原始图像上点的直线(见图6-9)。如果我们将输入图像中所有非0像素转化成输出图像中的这些点集并且将其贡献相加,然后输入图像(例如(x,y)平面)出现的直线将会在输出图像(例如(a,b)平面)以局部最大值出现。因为我们将每个点的贡献相加,因此(a,b)平面通常被称为
累加平面( accumu lator plane) 。

你可能认为用斜率—截距的形式来代表所有通过的点并不是一种最好的方式(因为作为斜率函数,直线的密度有相当的差异,以及相关的事实是可能的斜率间隔的范围是从负无穷到正无穷)。正是由于这个原因,在实际数值计算中使用的变换图像的参数化略微有些不同。首选的参数化方式是每一行代表极坐标(ρ, β)中的一个点,并且隐含的直线是通过象征点,垂直于远点到此点的半径。如图6-10所示,此直线的方程如下:
这里写图片描述
这里写图片描述

OpenCV的霍夫变换算法并没有将这个算法显式地展示给用户。而是简单地返回(ρ, β)平面的局部最大值。然而,需要了解这个过程以便更好地理解OpenCV霍夫变换函数中的参数。
OpenCV支持两种不同形式的霍夫变换:标准霍夫变换(SHT)和累计概率
霍夫变换((PPHT)。刚才所说的是SHT算法。PPHT是这种算法的一个变种,计算单独线段的方向以及范围(如图6-11所示)。之所以称PPHT为“概率”的,是因为并不将累加器平面内的所有可能点累加,而只累加其中的一部分。该想法是如果峰值将要足够高,只用一小部分时间去寻找它就足够了。这个猜想的结果可以实质性地减少计算时间。尽管有一些变量的含义是取决于用哪个算法,但可以使用OpenCV中的同一个函数来访问这两个算法。

OpenCV中的霍夫线变换有如下三种:
<1> 标准霍夫变换(StandardHough Transform,SHT),由HoughLines函数调用。

<2> 多尺度霍夫变换(Multi-ScaleHough Transform,MSHT),由HoughLines函数调用。

<3> 累计概率霍夫变换(ProgressiveProbabilistic Hough Transform,PPHT),由HoughLinesP函数调用。

然而,百度到一个教程:从HoughLinesP函数可以看出,该函数会调用cvHoughLines2函数。它通过参数CV_HOUGH_PROBABILISTIC,最终调用了cvHoughLinesProbabilistic函数——我觉得HoughLines函数也同理。所以我们只管学好cvHoughLines2函数就好了。

cvHonghLines2()

功能:
利用Hough变换在二值图像中寻找直线。
定义:
CvSeq* cvHonghLines2(
CvArr* image,
void* line_storage,
int mehtod,
double rho,
double theta,
int threshold,
double param1 =0,
double param2 =0
);
参数:
第一个变量是输入图像,必须是8位的,但输入信息、可以被看成是二值的(即所有非0像素被认为是相等的)。
第二个参数是指向保存结果位置的指针,既可以是内存块(见第8章的CvMemoryStorage),也可以是N*1的矩阵数列(行数N将有助于限制直线的最大数量)。
函数的返回内容依赖于调用方式。如果line_storage是矩阵数组,最终的返回值为空(void)。在这种情形下,使用SHT或者多尺度的HT时,矩阵应该是CV_32FC2类型,当使用PPHT时,矩阵应为CV_32SC4类型。在头两种情形下,每一行中ρ和β的值应在数组中的两个通道里。在PPHT情形下,四个通道保留的是返回线段开始点和结束点的x和y的值。在所有的情形下,数组的行数将会被CvHoughLines2 ( )更新以便正确反映返回直线的数量。
如果line_storage是指向内存块的一个指针,返回值将是一个指向CvSeq序列结构的指针。在这种情况下,可以用下面的类似命令从序列中得到每一条直线或者线段。
float* line=(float*)cvGetSeqElem( lines, i );
其中lines是从cvHoughLines2()中得到的返回值,i是所关心的线的索引。在这种情形下,line是指向这条直线数据的指针,对于SHT和MSHT,line[0]和line[ l ]是浮点类型的ρ和β,对于PPHT,是线段终点的CvPoint结构。

下一个参数method可以是
CV_HOUGH_STANDARD ——传统或标准 Hough 变换。每一个线段由两个浮点数 (ρ, θ) 表示,其中 ρ 是直线与原点(0,0) 之间的距离,θ 线段与 x轴之间的夹角。因此,矩阵类型必须是 CV_32FC2。
CV_HOUGH_PROBABILISTIC—— 概率 Hough 变换(如果图像包含一些长的线性分割,则效率更高)。 它返回线段分割而不是整个线段。每个分割用起点和终点来表示,所以矩阵(或创建的序列)类型是 CV_32SC4。
CV_HOUGH_MULTI_SCALE —— 传统 Hough 变换的多尺度变种。线段的编码方式与 CV_HOUGH_STANDARD 的一致。
下面两个变量,rho和theta是用来设置直线所需要的分辨率(例如,累加平面所需要的分辨率)。rho的单位是像素而theta的单位是弧度。因此,累加平面可以看成是由rho像素和theta弧度组成的二维直方图。
rho:以像素为单位的距离精度,一般取1。
Theta:以弧度为单位角度精度,一般取CV_PI/180(π/180)。
threshold是认定为一条直线时在累计平面中必须达到的值。当在一条直线上的像素点数大于threshold时,才将该直线作为检测结果显示出来。该值越大,得到直线越少。
param1
  对传统 Hough 变换,不使用。
  对概率Hough变换,它是最小线段长度。即当线段长度大于param1时,才将该线段作为检测结果显示。与上一参数类似,不过上一参数为像素数,而该参数为线段长度。

  对多尺度 Hough 变换,它是距离精度 rho 的分母 (大致的距离精度是 rho 而精确的应该是 rho / param1 )。
  param2
  对传统 Hough 变换,不使用 。
  对概率 Hough 变换,这个参数表示在同一条直线上进行碎线段连接的最大间隔值(gap), 即当同一条直线上的两条碎线段之间的间隔小于param2时,将其合二为一条长直线。
对多尺度 Hough 变换,它是角度精度 theta 的分母 (大致的角度精度是 theta 而精确的角度应该是 theta / param2)。
程序实例:

#include<cv.h>  #include <highgui.h>  #include <math.h>  int main(void)  {      IplImage *src = cvLoadImage("a.jpg",0);      if (src)      {          IplImage *dst = cvCreateImage(cvGetSize(src),8,1);  //要进行霍夫检测的图像,必须是8位的,所以创建一个8位格式的图像        IplImage *color_dst = cvCreateImage(cvGetSize(src),8,3);          CvMemStorage *storage = cvCreateMemStorage();          CvSeq *lines = 0;    //指向所检测到的线的序列的第                                        //一条                            int i ;  //i为直线的索引        cvCanny(src,dst,50,200,3);  //首先对源图像进行边缘检测,结果以灰度图显示——保存到dst里面了。           cvCvtColor(dst,color_dst,CV_GRAY2BGR);      #if 0          lines = cvHoughLines2(dst,storage,CV_HOUGH_STANDARD,1,CV_PI/180,150,0,0);                  for (i=0;i<lines->total;i++)   //遍历每一条线        {              float *line = (float *)cvGetSeqElem(lines,i);              float rho = line[0];              float theta = line[1];              CvPoint pt1,pt2;              double a = cos(theta);              double b = sin(theta);              if (fabs(a)<0.001)              {                  pt1.x = pt2.x = cvRound(rho);                  pt1.y = 0;                  pt2.y = color_dst->height;              }              else if (fabs(b)<0.001)              {                  pt1.y = pt2.y = cvRound(rho);                  pt1.x = 0;                  pt2.x = color_dst->width;              }              else              {                  pt1.x = 0;                  pt1.y = cvRound(rho/b);                  pt2.x = cvRound(rho/a);                  pt2.y = 0;              }              cvLine(color_dst,pt1,pt2,CV_RGB(255,0,0),1,8);          }      #else          lines = cvHoughLines2(dst,storage,CV_HOUGH_PROBABILISTIC,1,CV_PI/180,80,30,5);          for (i=0;i<lines->total;i++)          {              CvPoint *line = (CvPoint *)cvGetSeqElem(lines,i);              cvLine(color_dst,line[0],line[1],CV_RGB(255,0,0),1,CV_AA);          }      #endif          cvNamedWindow("Source");          cvShowImage("Source",src);          cvNamedWindow("Hough");          cvShowImage("Hough",color_dst);          cvWaitKey(0);           cvReleaseImage(&src);          cvReleaseImage(&dst);          cvReleaseImage(&color_dst);          cvReleaseMemStorage(&storage);                cvDestroyAllWindows();                    return 1;      }  }  

概率霍夫变换程序分析:

上面程序的#else 部分就是传说中的CV_HOUGH_PROBABILISTIC——概率 Hough 变换。
我们来看这个for语句里面:
for (i=0;itotal;i++)
其中的lines->total,通过成员变量total,就会知道序列中的元素个数。
然后 CvPoint line = (CvPoint )cvGetSeqElem(lines,i);
然后cvGetSeqElem作用是返回指定类型的指针。从前面的内容——对于PPHT(概率霍夫变换),返回的是线段终点(I think 应该是两个端点)的CvPoint结构。
所以会有CvPoint *line 。
再看这一句:cvLine(color_dst,line[0],line[1],CV_RGB(255,0,0),1,CV_AA);
其中的CV_AA - antialiased 线条(百度下是“抗锯齿线条”)。
CV_RGB(255,0,0)使得画出的线条是红色的。
当初不懂为什么要创造一个3通道的图片
IplImage *color_dst = cvCreateImage(cvGetSize(src),8,3);
然后还要把原来的单通道图像转换成3通道的——cvCvtColor(dst,color_dst,CV_GRAY2BGR);
因为要在图片上把找到的直线用红色画出来,所以不可能在单通道的图片上完成,所以要有这一步——把单通道的黑白图像转换成3通道的黑白图像。
最后再来看这一句话:
cvHoughLines2(dst,storage,CV_HOUGH_PROBABILISTIC,1,CV_PI/180,80,30,5);
我们来看看其中的参数为什么是这样。
第二个参数是指向保存结果位置的指针,此处用的是内存块——所以会有
CvMemStorage *storage = cvCreateMemStorage();
rho:以像素为单位的距离精度,一般取1。
Theta:以弧度为单位角度精度,一般取CV_PI/180(π/180)。
threshold是认定为一条直线时在累计平面中必须达到的值。当在一条直线上的像素点数大于threshold时,才将该直线作为检测结果显示出来。该值越大,得到直线越少——这程序里取的是80。
param1:对概率Hough变换,它是最小线段长度。即当线段长度大于param1时,才将该线段作为检测结果显示。
param2:对概率 Hough 变换,这个参数表示在同一条直线上进行碎线段连接的最大间隔值(gap), 即当同一条直线上的两条碎线段之间的间隔小于param2时,将其合二为一条长直线。

标准霍夫变换程序分析:

#if 0 的那一部分,之所以看起来这么麻烦,因为对于SHT和MSHT,line[0]和line[ l ]是浮点类型的ρ和β。
不管了,一般都是用概率霍夫变换。

cvRound()

格式:
int cvRound (double value)
作用:
对一个double型的数进行四舍五入,并返回一个整型数!
函数 cvRound, cvFloor, cvCeil 用一种舍入方法将输入浮点数转换成整数。
cvRound 返回和参数最接近的整数值。
cvFloor 返回不大于参数的最大整数值。
cvCeil 返回不小于参数的最小整数值。

0 0
原创粉丝点击