理论

hough变换的代码写了很多遍,网络上也下载了很多,但是都是一知半解,没有具体去想原理,结果每次的代码存在的周期都很短,都没有很好的得到利用,分析原因还是自己一知半解,其实后来想想蛮简单的,可就是不明白当时就怎么蒙圈了,想想也只有自己了。

本文中的图片和理论都来源于参考,请尊重原有作者的版权。

直线的表示,在笛卡尔坐标系下,其方程可以用点斜式和两点式,但是在hough变换中,其表示方法是(r,theta),r表示为直线到原点的距离,theta表示该直线垂线与x轴夹角,如下图所示:
hough直线表示

使用hough变换来检测直线的思想就是:为每一个点假设n个方向的直线,通常n=180,此时检测的直线的角度精度为1°,分别计算这n条直线的(r,theta)坐标,得到n个坐标点。如果要判断的点共有N个,最终得到的(r,theta)坐标有Nn个。有关这Nn个(r,theta)坐标,其中theta是离散的角度,共有180个取值。
最重要的地方来了,如果多个点在一条直线上,那么必有这多个点在theta=某个值theta_i时,这多个点的r近似相等于r_i。也就是说这多个点都在直线(r_i,theta_i)上。

如果空间中有3个点,如何判断这三个点在不在一个直线上,如果在,这条直线是的位置为?

这个例子中,对于每个点均求过该点的6条直线的(r,theta)坐标,共求了3*6个(r,theta)坐标。可以发现在theta=60时,三个点的r都近似为80.7,由此可判定这三个点都在直线(80.7,60)上。
通过 r0theta 坐标系可以更直观表示这种关系,如下图:图中三个点的(r,theta)曲线汇集在一起,该交点就是同时经过这三个点的直线。

Code

int HoughLine(vector<gclPoint> pointsIn, int height, int width, double rho, double theta, int thresh, vector<gclLine> &lineOut)
{
    int error = 0;
    int *score = NULL;
    int *index = NULL;
    gclLine *lineTemp = NULL;


    if (pointsIn.size() <= 2 || rho <= 0 || theta <= 0)
    {
        error = GclParamParamIsnotValid;
        goto End;
    }
    int thetaNum = ceil((180 - theta) / theta + 0.5);
    int wlen = sqrt(height * height + width * width) + 1;
    int rhoNum = ceil((wlen - rho) / rho + 0.5);

    score = new int[thetaNum * rhoNum];
    memset(score, 0, sizeof(int) * thetaNum * rhoNum);
    lineTemp = new gclLine[thetaNum * rhoNum];
    memset(lineTemp, 0, sizeof(gclLine) * rhoNum * thetaNum);

    for (int i = 0; i < pointsIn.size(); i++)
    {
        for (int m = 0; m < thetaNum; m++)
        {
            int dex = 0;
            double a = 0, b = 0, c = 0;
            if (abs(m * theta - 90) < 10e-6)
            {
                dex = int(pointsIn[i].x / rho + 0.5);
                a = 1; b = 0; c = -pointsIn[i].x;
            }
            else
            {
                a = tan(m * theta * M_PI / 180);
                b = -1;
                c = pointsIn[i].y - a * pointsIn[i].x;
                double rhoTemp = abs(c) / sqrt(a * a + 1) / rho;
                dex = int(rhoTemp + 0.5);
            }
            score[m * rhoNum + dex]++;
            lineTemp[m * rhoNum + dex] = gclLine(a, b, c);
        }
    }

    // 统计线信息
    index = new int[rhoNum * thetaNum];
    for (int i = 0; i < rhoNum * thetaNum; i++)
    {
        index[i] = i;
    }
    InsertSort(score, index, rhoNum * thetaNum);

    for (int i = rhoNum * thetaNum - 1; i >= 0; i--)
    {
        if (score[i] > thresh)
        {
            lineOut.push_back(lineTemp[index[i]]);
        }
    }

End:
    SAFE_DELETE(lineTemp);
    SAFE_DELETE(score);
    SAFE_DELETE(index);

    return error;
}