Hough变换-理解篇

      霍夫变换(Hough Transform)是图像处理中的一种特征提取技术,它通过一种投票算法检测具有特定形状的物体。该过程在一个参数空间中通过计算累计结果的局部最大值得到一个符合该特定形状的集合作为霍夫变换结果。霍夫变换于1962年由Paul Hough 首次提出[53],后于1972年由Richard Duda和Peter Hart推广使用[54],经典霍夫变换用来检测图像中的直线,后来霍夫变换扩展到任意形状物体的识别,多为圆和椭圆。

霍夫变换运用两个坐标空间之间的变换将在一个空间中具有相同形状的曲线或直线映射到另一个坐标空间的一个点上形成峰值,从而把检测任意形状的问题转化为统计峰值问题,上一节中已经介绍了车道的直线特征,本节中介绍hough变换检测直线的原理和检测结果。

我们知道,一条直线在直角坐标系下可以用y=kx+b表示, 霍夫变换的主要思想是将该方程的参数和变量交换,即用x,y作为已知量k,b作为变量坐标,所以直角坐标系下的直线y=kx+b在参数空间表示为点(k,b),而一个点(x1,y1)在直角坐标系下表示为一条直线y1=x1·k+b,其中(k,b)是该直线上的任意点。为了计算方便,我们将参数空间的坐标表示为极坐标下的γ和θ。因为(极坐标下)同一条直线上的点对应的(γ,θ)是相同的,因此可以先将图片进行边缘检测,然后对图像上每一个非零像素点,在参数坐标下变换为一条直线,那么在直角坐标下属于同一条直线的点便在参数空间形成多条直线并内交于一点。因此可用该原理进行直线检测。



4-13  参数空间变换结果

如图 4‑13. 所示,对于原图内任一点(x,y)都可以在参数空间形成一条直线,以图中一条直线为例有参数(γ,θ)=(69.641,30°),所有属于同一条直线上的点会在参数空间交于一点,该点即为对应直线的参数。由该图中所有直线所得到的(γ,θ)在参数空间中得到一系列对应曲线见图 4‑14 霍夫统计变换结果。由霍夫变换检测结果见图 4‑15(c)所示。

Hough变换-理解篇_第1张图片



Hough变换-理解篇_第2张图片


第二种解释:

hough变换是图像处理里面一种检测直线等规则曲线的方法。这里介绍下检测直线的原理和实现方法,最后给出相关的代码。  先给出数学上面的里面,斜截式直线y=kx+b有两个参数k,b,注意直线方程是在笛卡儿坐标系xoy中的。任意一条直线对应于kob平面中一个点(k,b)。那么,对于xoy平面中的任意一个点肯定有无数条通过它的直线,那么直线都有对应的kob平面中的点,这些点能组成一条直线。综合起来就是,xoy平面中一条直线对应于kob平面中的一个点,xoy平面中的一个点对应于kob平面中的一条直线。
  现在有个问题是斜截式无法表示xoy平面中所有直线,那么我们希望能找到能够表示xoy平面中所有直线的参数式子。这个式子是r=x*cos(θ)+y*sin(θ)。推导过程如下:

r为原点到直线的距离,(x0,y0)为垂足,θ为垂线和x轴正向的夹角,那么对于直线上面的任意的一个点都有
(x0,y0)*(x-x0,y-y0)=0,得到x*x0-x0*x0+y*y0-y0*y0=0,即x*x0+y*y0=x0*x0+y0*y0=r*r。等式两边再除以r得到r=x*cos(θ)+y*sin(θ)。那么这个关于(θ,r)的参数式子可以表示xoy平面上所有的直线了。


  现在再来讨论下如何根据这个参数式子检测直线的。
  我们手上的只有xoy平面上的图像。而且根据我们的推理,我们的图像的原点必须在左下角,因为我们是用笛卡尔坐标系推出该参数式子的。那么,我们可以枚举图像上面每一个可能是直线上面的点(比如,我们可以对图像阈值化后,像素值为255的点就可能是直线上面的点),由于图像上的每个点对应于θor平面上面的一条曲线(这里管它是直线还是曲线了,对于检测原理没影响),那么我们就能得到很多θor平面上的曲线。我们对每个(θ,r)点计数通过其上的曲线个数,最后选取曲线个数最大的那个点(θmax,Rmax)其对应的肯定是我们要检测的直线。至于其它的预处理,一般是梯度滤波增强边缘或者拉普拉斯增强边缘,然后弄个全局二值化,就可以用hough变换检测直线了(我这次的实验就没有平滑滤波噪声,那样会使直线变粗,使检测效果变坏)

最后我给出我的检测直线的代码,并指出代码需要主要的实现点。

//针对hough变换的定义得到的直线相对的是坐标系原点的坐标系
//针对hough变换的定义得到的直线相对的是坐标系原点的坐标系
void CxBitmap::HoughLine(int* pnR, double* pfTheta)
{
    const double PI = 4.0 * atan(1.0);
    double fRate = PI / 180;
    int nWidth = GetWidth(), nHeight = GetHeight();
    int iRMax = (int)sqrt(nWidth * nWidth + nHeight * nHeight) + 1;
    int iThMax = 360;
    int iTh;
    int iR;
    int iMax = -1;
    int iThMaxIndex = -1;
    int iRMaxIndex = -1;
    int *pnArray = NULL;
    int nPos = 0;

    if (m_bBinary == FALSE)
    {
        GrayToBinary();
    }
    pnArray = new int[iRMax * iThMax];//iRMax行,每一行长度是iThMax
    memset(pnArray, 0, sizeof(int) * iRMax * iThMax);

    for (int y = 0; y < nHeight; y++)
    {
        nPos = m_nBytesPerLine * y;
        for (int x = 0; x < nWidth; x++)
        {
            if(pbyBuffer[nPos] == 255)
            {
                for(iTh = 0; iTh < iThMax; iTh++)                 {                     iR = (int)(x * cos(iTh * fRate) + y * sin(iTh * fRate));                                          if(iR > 0)
                    {
                        pnArray[iR * iThMax + iTh]++;
                    }
                }
            }
            nPos++;
        } // x
    } // y

    for(iR = 0; iR < iRMax; iR++)
    {
        for(iTh = 0; iTh < iThMax; iTh++)         {             int iCount = pnArray[iR * iThMax + iTh];             if(iCount > iMax)
            {
                iMax = iCount;
                iRMaxIndex = iR;
                iThMaxIndex = iTh;
            }
        }
    }

    *pnR = iRMaxIndex;
    *pfTheta = fRate * iThMaxIndex;

    delete [] pnArray;
}
  注意nPos 必须按m_nBytesPerLine对齐,因为即使是灰度图像每行的长度也不一定是图像的宽度,如果这个错了,检测结果就完全错了。另外一个是计算出来的iR值必须大于0才有效。



你可能感兴趣的:(Hough变换-理解篇)