您的位置:首页 > 其它

Hough Transform(霍夫变换)

2012-08-01 06:26 169 查看
图像处理之霍夫变换(直线检测算法)

霍夫变换是图像变换中的经典手段之一,主要用来从图像中分离出具有某种相同特征的几何

形状(如,直线,圆等)。霍夫变换寻找直线与圆的方法相比与其它方法可以更好的减少噪

声干扰。经典的霍夫变换常用来检测直线,圆,椭圆等。

 

霍夫变换是图像处理中从图像中识别几何形状的基本方法之一,应用很广泛,也有很多改进算法。最基本的霍夫变换是从黑白图像中检测直线(线段)。

我们先看这样一个问题:

设已知一黑白图像上画了一条直线,要求出这条直线所在的位置。


我们知道,直线的方程可以用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(22)。可以求出,过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)。这个性质就为我们解决问题提供了方法:

首先,我们初始化一块缓冲区,对应于参数平面,将其所有数据置为0.
对于图像上每一前景点,求出参数平面对应的直线,把这直线上的所有点的值都加1。
最后,找到参数平面上最大点的位置,这个位置就是原图像上直线的参数。


上面就是霍夫变换的基本思想。就是把图像平面上的点对应到参数平面上的线,最后通过统计特性来解决问题。假如图像平面上有两条直线,那么最终在参数平面上就会看到两个峰值点,依此类推。

在实际应用中,y=k*x+b形式的直线方程没有办法表示x=c形式的直线(这时候,直线的斜率为无穷大)。所以实际应用中,是采用参数方程p=x*cos(theta)+y*sin(theta)。这样,图像平面上的一个点就对应到参数p---theta平面上的一条曲线上。其它的还是一样。

再看下面一个问题:

我们要从一副图像中检测出半径以知的圆形来。这个问题比前一个还要直观。我们可以取和图像平面一样的参数平面,以图像上每一个前景点为圆心,以已知的半径在参数平面上画圆,并把结果进行累加。最后找出参数平面上的峰值点,这个位置就对应了图像上的圆心。

在这个问题里,图像平面上的每一点对应到参数平面上的一个圆。

把上面的问题改一下,假如我们不知道半径的值,而要找出图像上的圆来。这样,一个办法是把参数平面扩大称为三维空间。就是说,参数空间变为x--y--R三维,对应圆的圆心和半径。图像平面上的每一点就对应于参数空间中每个半径下的一个圆,这实际上是一个圆锥。最后当然还是找参数空间中的峰值点。不过,这个方法显然需要大量的内存,运行速度也会是很大问题。

有什么更好的方法么?

我们前面假定的图像都是黑白图像(2值图像),实际上这些2值图像多是彩色或灰度图像通过边缘提取来的。我们前面提到过,图像边缘除了位置信息,还有方向信息也很重要,这里就用上了。根据圆的性质,圆的半径一定在垂直于圆的切线的直线上,也就是说,在圆上任意一点的法线上。这样,解决上面的问题,我们仍采用2维的参数空间,对于图像上的每一前景点,加上它的方向信息,都可以确定出一条直线,圆的圆心就在这条直线上。这样一来,问题就会简单了许多。

接下来还有许多类似的问题,如检测出椭圆,正方形,长方形,圆弧等等。这些方法大都类似,关键就是需要熟悉这些几何形状的数学性质。霍夫变换的应用是很广泛的,比如我们要做一个支票识别的任务,假设支票上肯定有一个红颜色的方形印章,我们可以通过霍夫变换来对这个印章进行快速定位,在配合其它手段进行其它处理。霍夫变换由于不受图像旋转的影响,所以很容易的可以用来进行定位。

霍夫变换有许多改进方法,一个比较重要的概念是广义霍夫变换,它是针对所有曲线的,用处也很大。就是针对直线的霍夫变换也有很多改进算法,比如前面的方法我们没有考虑图像上的这一直线上的点是否连续的问题,这些都要随着应用的不同而有优化的方法


霍夫变换算法思想:

以直线检测为例,每个像素坐标点经过变换都变成都直线特质有贡献的统一度量,一个简单

的例子如下:一条直线在图像中是一系列离散点的集合,通过一个直线的离散极坐标公式,

可以表达出直线的离散点几何等式如下:

X *cos(theta) + y * sin(theta)  = r 其中角度theta指r与X轴之间的夹角,r为到直线几何垂

直距离。任何在直线上点,x, y都可以表达,其中 r, theta是常量。该公式图形表示如下:



然而在实现的图像处理领域,图像的像素坐标P(x, y)是已知的,而r, theta则是我们要寻找

的变量。如果我们能绘制每个(r, theta)值根据像素点坐标P(x, y)值的话,那么就从图像笛卡

尔坐标系统转换到极坐标霍夫空间系统,这种从点到曲线的变换称为直线的霍夫变换。变换

通过量化霍夫参数空间为有限个值间隔等分或者累加格子。当霍夫变换算法开始,每个像素

坐标点P(x, y)被转换到(r, theta)的曲线点上面,累加到对应的格子数据点,当一个波峰出现

时候,说明有直线存在。同样的原理,我们可以用来检测圆,只是对于圆的参数方程变为如

下等式:

(x –a ) ^2 + (y-b) ^ 2 = r^2其中(a, b)为圆的中心点坐标,r圆的半径。这样霍夫的参数空间就

变成一个三维参数空间。给定圆半径转为二维霍夫参数空间,变换相对简单,也比较常用。

 

编程思路解析:

1.      读取一幅带处理二值图像,最好背景为黑色。

2.      取得源像素数据

3.      根据直线的霍夫变换公式完成霍夫变换,预览霍夫空间结果

4.       寻找最大霍夫值,设置阈值,反变换到图像RGB值空间(程序难点之一)

5.      越界处理,显示霍夫变换处理以后的图像

 

在OpenCV当中,霍夫之间检测函数并不会告诉你具体的计算步骤,而只是将极坐标空间中局部峰值点予以返回。OpenCV支持两种不同的霍夫直线变换。the Standard Hough Transform(SHT,标准霍夫变换)和 Progressive Probability Houth Transform(PPHT,渐进概率式霍夫变换)。SHT就是刚才所述的在极坐标空间进行参数表示的方法。而PPHT是SHT的改进,它是在一定的范围内进行霍夫变换,从而减少计算量,缩短计算时间。在OpenCV中,以上两种霍夫直线变换,都可以用以下函数来实现:
CvSeq* cvHoughLines2(CvArr* image,  void* lineStorage,  int method,  double rho,   double theta,  int threshold,  double param1=0,  double param2=0);该函数的第一个参数,是输入图片(必须是二值化后的图片),第二个参数是一个指针,指向保存函数返回结果的内存空间。第三个参数method可以是CV_HOUTH_STANDARD也可以是CV_HOUTH_PROBABILISTIC或者CV_HOUTH_MULTI_SCALE,分别代表了标准霍夫变换(SHT),渐进概率式霍夫变换(PPHT)和多尺度标准霍夫变换。接下来的两个参数rho和theta设置极坐标系的精度,其中rho以像素为单位,theta以弧度为单位。threshold是用户设定的一个阈值,只有超过该阈值才被认定为是一条符合条件的直线。在SHT方法中,param1和param2是不会被使用到的,而在PPHT方法中,param1设置检测到的直线(准确点说,是线段)的最短长度,param2则设置共线的两条线段的最小分割间隔。对于多尺度的标准霍夫变换(multi-scale SHT)来说,首先根据用户设定的rho和theta来检测直线,之后,按照param1和param2的值对检测结果进行优化(即,rho=rho/param1,theta=theta/param2)。
下面,我给出利用OpenCV中hough transform的直线检测代码:
 

#include "stdafx.h"

#include "cv.h"

#include "highgui.h"

#include "iostream"

using namespace std;

 

bool key = false;

 

int _tmain(int argc, _TCHAR* argv[])

{

IplImage* src = cvLoadImage("c:/img1.bmp", CV_LOAD_IMAGE_GRAYSCALE);

if (!src)

{

cout<<"src load error..."<<endl;

system("pause");

exit(-1);

}

 

IplImage* dst = cvCreateImage( cvGetSize(src), 8, 1 );

IplImage* color_dst = cvCreateImage( cvGetSize(src), 8, 3 );

 

//cvCanny( src, dst, 50, 200, 3 );

cvThreshold(src, dst, 50, 255, CV_THRESH_BINARY_INV);

cvCvtColor(dst, color_dst, CV_GRAY2BGR);

 

CvMemStorage* storage = cvCreateMemStorage(0);

CvSeq* lines = 0;

int i;

 

if (key)

{

lines = cvHoughLines2( dst, storage, CV_HOUGH_STANDARD, 1, CV_PI/180, 100, 0, 0 );

 

for( i = 0; i < MIN(lines->total,100); i++ )

{

float* line = (float*)cvGetSeqElem(lines,i);

float rho = line[0];

float theta = line[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( color_dst, pt1, pt2, CV_RGB(255,0,0), 3, CV_AA, 0 );

}

}

else

{

lines = cvHoughLines2( dst, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 50, 10 );

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), 3, CV_AA, 0 );

}

}

 

 

cvNamedWindow( "Source", 1 );

cvShowImage( "Source", src );

 

cvNamedWindow( "Hough", 1 );

cvShowImage( "Hough", color_dst );

cvSaveImage("c:/result.bmp", color_dst);

 

cvWaitKey(0);

cvReleaseImage(&src);

cvReleaseImage(&dst);

cvReleaseImage(&color_dst);

cvClearSeq(lines);

cvReleaseMemStorage(&storage);

 

 

system("pause");

return 0;

}
以上代码的执行结果为:



                          原图



                                                      直线检测结果
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息