Input: Original image in the form of an array: Image1[xMax][yMax]
Output: New image : Image2 [xMax][yMax] containing the detected straight lines.
Intermediate data structure: Hough [tMax][rMax] to calculate the corresponding lines.
Algorithm:
/* Fill in the Hough array*/
for (x=0x<xMaxx++){
for (y=0 y<yMax y++){
if ( Image1[x][y] >IThresh){
for (t=0 t<tMax t++){
r = (x-xMax/2)*cos(t) +(y-yMax/2)*sin(t)
if (r >0) {Hough [t][r] ++}
}
/* Process Hough array to find strong maxima */
for (t = 0t <tMax / 2t++)
for (r = 0r <rMaxr++)
if (Hough[t][r] >Hthresh)
{
/* Check 5x5 neighborhood for max */
max = TRUE
for (dt = (t - 2)dt <= (t + 2)dt++)
for (dr = (r - 2)dr <= (r + 2)dr++)
if ((dr >= 0) &&(dr <rMax) &&(dt >= 0) &&(dt <tMax) && (Hough[dt][dr] >Hough[t][r]))
{
max = FALSE
break
}
/* Process Hough array to create output image array */
if (max == TRUE)
{
makeLine(r, t)
}
}
void makeLine (r,t)
{
for (x=0 x<xMax x++)
/* calculate y = r/sin (t) ?x*cotg(t) and fill in the Image2 array */察薯枝
}
/********************************************************/
Hough变换是图像处理中从图像中识别几何形状的基本方法之一。Hough变换的基本原理在于利用点与线的对偶性,将原始图像空间的给定的曲线通过曲线表达形式变为参数空间的一个点。这样就把原始图像中给定曲线的检测问题转化为寻找参数空间中的峰值问题。也即把检测整体特性转化为检测局部特性。比如直线、椭圆、圆、弧线等。Hough变换的基本思想
设已知一黑白图像上画了一条直线,要求出这条直线所在的位置。我们知道,直线的方程可以用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)。这个性质就为我们解决问题提供了方法,就是把图像平面上的点对应到参数平面上的线,最后通过统计特性来解决问题。假如图像平面上有两条直线,那么最终在参数平面上就会看到两个峰值点,依此类推。
简而言之,Hough变换思想为:在原始图像坐标系下的一个点对应了参数坐标系中的一条直线,同样参数坐标系的一条直线对应了原始坐标系下的一个点,然后,原始坐标系下呈现直线的所有点,它们的斜率和截距是相同的,所以它们在参数坐标贺冲系下对应于同一个点。这样在将原始坐标系下的各个点投影到参数坐标系下之后,看参数坐标系下有没有聚集点,这样的聚集点就对应了原始坐标系下的直线。
首先,初始化一块缓冲区,对应于参数平面,将其所有数据置为0.对于图像上每一前景点,求出参数平面对应的直线,把这直线上的所有点的值都加1。最后,找到参数平面上最大点的位置,这个位置就是原图像上直线的参数。
在实际应用中,y=k*x+b形式的直线方程没有办法表示x=c形式的直线(这时候,直线的斜率为无穷大)。所以实际应用中,是采用参数方程p=x*cos(theta)+y*sin(theta)。这样,图像平面上的一个点就对应到参数p---theta平面上的一条曲线上,其它的还是一样。
Hough变换推广
1、已知半径的圆
其实Hough变换可以检测任意的已知表达形式的曲线,关键是看其参数空间的选择,参数空间的选择可以根据它的表达形式而定。比如圆的表达形式为,所以当检测某一半径的圆的时候,可以选择与原图像空间同样的空间作为参数空间。那么圆图像空间中的一个圆对应了参数空间中的一个点,参数空间中的一个点对应了图像空间中的一个圆,圆图像空间中在同一个圆上的点,它们的参数相同即a,b相同,那么它们在参数空间中的对应的圆就会过同一个点(a,b),所以,将原图像空间中的所有点变换到参数空间后,根据参数空间中点的聚集程度就可以判断出图像空间中有没有近似于圆的图形。如果有的话,这个参数就是圆的参数。
2、未知半径的圆对于圆的半径未知的情况下,可以看作是有三个参数的圆的检测,中心和半径。这个时候原理仍然相同,只是参数空间的维数升高,计算量增大。图像空间中的任意一个点都对应了参数空间好正中的一簇圆曲线。,其实是一个圆锥型。参数空间中的任意一个点对应了图像空间中的一个圆。
3、椭圆椭圆有5个自由参数,所以它的参数空间是5维的,因此他的计算量非常大,所以提出了许多的改进算法。
图像空间中的在同一个圆,直线,椭圆上的点,每一个点都对应了参数空间中的一个图形,在图像空间中这些点都满足它们的方程这一个条件,所以这些点,每个投影后得到的图像都会经过这个参数空间中的点。也就是在参数空间中它们会相交于一点。所以,当参数空间中的这个相交点的越大的话,那么说明元图像空间中满足这个参数的图形越饱满。越象我们要检测的东西。 Hough变换能够查找任意的曲线,只要你给定它的方程。Hough变换在检验已知形状的目标方面具有受曲线间断影响小和不受图形旋转的影响的优点,即使目标有稍许缺损或污染也能被正确识别。
/**************************************************************************
* 函数名称:
* HoughDIB()
*
* 参数:
* LPSTR lpDIBBits - 指向源DIB图像指针
* LONG lWidth - 源图像宽度(象素数)
* LONG lHeight - 源图像高度(象素数)
* LONG lLineBytes - 源图像每行所占的字节耐衫笑数
* WORD wBitsPerPixel - 源图像位深
* LPSTR lpNewDIBBits - 目标图像数据区的指针
*
* 返回值:
* BOOL - 运算成功返回TRUE,否则返回FALSE。
*
* 说明:
* 该函数用于对检测图像中的平行直线。如果图像中有两条平行的直线,则将这两条平行直线
* 提取出来。
*
* 要求目标图像为只有0和255两个灰度值的灰度图像。
************************************************************************/
BOOL WINAPI HoughDIB(LPSTR lpDIBBits,LONG lWidth,LONG lHeight,LONG lLineBytes,
WORD wBitsPerPixel,LPSTR lpNewDIBBits)
{
// 指向源图像的指针
LPSTR lpSrc
// 指向缓存图像的指针
LPSTR lpDst
// 指向变换域的指针
LPSTR lpTrans
// 图像每行的字节数
// LONG 塌野lLineBytes
// 指向缓存DIB图像的指针
//LPSTR lpNewDIBBits
//HLOCAL hNewDIBBits
//指向变换域的指针
LPSTR lpTransArea
HLOCAL hTransArea
//变换域的尺寸
int iMaxDist
int iMaxAngleNumber
//变换域的坐标
int iDist
int iAngleNumber
//循昌含环变量
long i
long j
//像素值
unsigned char pixel
//存储变换域中的两个最大值
MaxValue MaxValue1
MaxValue MaxValue2
// 暂时分配内存,以保存新图像
//hNewDIBBits = LocalAlloc(LHND, lWidth * lHeight)
// 初始化新分配的内存,设定初始值为255
lpDst = (char *)lpNewDIBBits
memset(lpDst, (BYTE)255, lLineBytes * lHeight)
//计算变换域的尺寸
//最大距离
iMaxDist = (int) sqrt(lWidth*lWidth + lHeight*lHeight)
//角度从0-180,每格2度
iMaxAngleNumber = 90
//为变换域分配内存
hTransArea = LocalAlloc(LHND, lWidth * lHeight * sizeof(int))
// 锁定内存
lpTransArea = (char * )LocalLock(hTransArea)
// 初始化新分配的内存,设定初始值为0
lpTrans = (char *)lpTransArea
memset(lpTrans, 0, lWidth * lHeight * sizeof(int))
for(j = 0 j <lHeight j++)
{
for(i = 0i <lWidth i++)
{
// 指向源图像倒数第j行,第i个象素的指针
lpSrc = (char *)lpDIBBits + lLineBytes * j + i
//取得当前指针处的像素值,注意要转换为unsigned char型
pixel = (unsigned char)*lpSrc
//目标图像中含有0和255外的其它灰度值
if(pixel != 255 && *lpSrc != 0)
{
MessageBox(NULL,"目标图像中含有0和255外的其它灰度值","错误",MB_OK)
return FALSE
}
//如果是黑点,则在变换域的对应各点上加1
if(pixel == 0)
{
//注意步长是2度
for(iAngleNumber=0 iAngleNumber<iMaxAngleNumber iAngleNumber++)
{
iDist = (int) fabs(i*cos(iAngleNumber*2*PI/180.0) + \
j*sin(iAngleNumber*2*PI/180.0))
//变换域的对应点上加1
*(lpTransArea+iDist*iMaxAngleNumber+iAngleNumber) = \
*(lpTransArea+iDist*iMaxAngleNumber+iAngleNumber) +1
}
}
}
}
//找到变换域中的两个最大值点
MaxValue1.Value=0
MaxValue2.Value=0
//找到第一个最大值点
for (iDist=0 iDist<iMaxDistiDist++)
{
for(iAngleNumber=0 iAngleNumber<iMaxAngleNumber iAngleNumber++)
{
if((int)*(lpTransArea+iDist*iMaxAngleNumber+iAngleNumber)>MaxValue1.Value)
{
MaxValue1.Value = (int)*(lpTransArea+iDist*iMaxAngleNumber+iAngleNumber)
MaxValue1.Dist = iDist
MaxValue1.AngleNumber = iAngleNumber
}
}
}
//将第一个最大值点附近清零
for (iDist = -9iDist < 10iDist++)
{
for(iAngleNumber=-1 iAngleNumber<2 iAngleNumber++)
{
if(iDist+MaxValue1.Dist>=0 && iDist+MaxValue1.Dist<iMaxDist \
&& iAngleNumber+MaxValue1.AngleNumber>=0 && iAngleNumber+MaxValue1.AngleNumber<=iMaxAngleNumber)
{
*(lpTransArea+(iDist+MaxValue1.Dist)*iMaxAngleNumber+\
(iAngleNumber+MaxValue1.AngleNumber))=0
}
}
}
//找到第二个最大值点
for (iDist=0 iDist<iMaxDistiDist++)
{
for(iAngleNumber=0 iAngleNumber<iMaxAngleNumber iAngleNumber++)
{
if((int)*(lpTransArea+iDist*iMaxAngleNumber+iAngleNumber)>MaxValue2.Value)
{
MaxValue2.Value = (int)*(lpTransArea+iDist*iMaxAngleNumber+iAngleNumber)
MaxValue2.Dist = iDist
MaxValue2.AngleNumber = iAngleNumber
}
}
}
//判断两直线是否平行
if(abs(MaxValue1.AngleNumber-MaxValue2.AngleNumber)<=2)
{
//两直线平行,在缓存图像中重绘这两条直线
for(j = 0 j <lHeight j++)
{
for(i = 0i <lWidth i++)
{
// 指向缓存图像倒数第j行,第i个象素的指针
lpDst = (char *)lpNewDIBBits + lLineBytes * j + i
//如果该点在某一条平行直线上,则在缓存图像上将该点赋为黑
//在第一条直线上
iDist = (int) fabs(i*cos(MaxValue1.AngleNumber*2*PI/180.0) + \
j*sin(MaxValue1.AngleNumber*2*PI/180.0))
if (iDist == MaxValue1.Dist)
*lpDst = (unsigned char)0
//在第二条直线上
iDist = (int) fabs(i*cos(MaxValue2.AngleNumber*2*PI/180.0) + \
j*sin(MaxValue2.AngleNumber*2*PI/180.0))
if (iDist == MaxValue2.Dist)
*lpDst = (unsigned char)0
}
}
}
// 释放内存
LocalUnlock(hTransArea)
LocalFree(hTransArea)
// 返回
return TRUE
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)