理论部分来源:霍夫变换

作者:https://home.cnblogs.com/u/php-rearch/

一、霍夫变换(Hough)

  A-基本原理

一条直线可由两个点A=(X,Y)和B=(X,Y)确定(笛卡尔坐标)

另一方面,也可以写成关于(k,q)的函数表达式(霍夫空间):

对应的变换可以通过图形直观表示:

变换后的空间成为霍夫空间。即:笛卡尔坐标系中一条直线,对应霍夫空间的一个点

反过来同样成立(霍夫空间的一条直线,对应笛卡尔坐标系的一个点):

再来看看A、B两个点,对应霍夫空间的情形:

一步步来,再看一下三个点共线的情况:

可以看出如果笛卡尔坐标系的点共线,这些点在霍夫空间对应的直线交于一点:这也是必然,共线只有一种取值可能。

如果不止一条直线呢?再看看多个点的情况(有两条直线):

其实(3,2)与(4,1)也可以组成直线,只不过它有两个点确定,而图中A、B两点是由三条直线汇成,这也是霍夫变换的后处理的基本方式选择由尽可能多直线汇成的点

看看,霍夫空间:选择由三条交汇直线确定的点(中间图),对应的笛卡尔坐标系的直线(右图)。

 到这里问题似乎解决了,已经完成了霍夫变换的求解,但是如果像下图这种情况呢?

k=∞是不方便表示的,而且q怎么取值呢,这样不是办法。因此考虑将笛卡尔坐标系换为:极坐标表示

在极坐标系下,其实是一样的:极坐标的点→霍夫空间的直线,只不过霍夫空间不再是[k,q]的参数,而是的参数,给出对比图:

是不是就一目了然了?

给出霍夫变换的算法步骤:

二、霍夫变换的实现

霍夫变换:在霍夫空间中,一个r和theta可以确定一条直线。

本质上在由r与theta组成的二维空间进行投票。

r:r的最大值取图像对角线长度。

theta:0-360度,可分也可不分。

#define PI 3.14159265
#define r      200    //120^2+160^2 = 40000  //斜边长度
#define count 90      //360度分成90份
#define Use_ROWS 120  //图像高度
#define Use_Line 160  //图像宽度 


//数据初始化
int jiaodu=0;
float Cos[count]={0},Sin[count]={0};
for(int theta = 0,int i=0;theta<=360;i++)
    {
        Cos[i]=cos(theta*PI/180);
        Sin[i]=sin(theta*PI/180);
        theta+=4;
    }

//二维投票箱
int res[count][r]={0};

/////////////////////////////////////////////////////////////////////// memset(Image,
255, sizeof(Image)); memset(res, 0, sizeof(res)); //二值化 int Threshold,i,j; Threshold = GetOSTU(Image_Use); for(i = 0; i < Use_ROWS; i++) { for(j =0; j < Use_Line; j++) { if(Image_Use[i][j] >= Threshold) Image_Use[i][j]=255;//白为背景 else Image_Use[i][j]=0; //黑为物体 } } //双向梯度 for(int i = 1; i < Use_ROWS- 1; i++) { for(int j = 1; j < Use_Line -1; j++) { Image_Use[i][j] = sqrt((Image_Use[i][j+1] - Image_Use[i][j])*(Image_Use[i][j+1] - Image_Use[i][j])+(Image_Use[i+1][j] - Image_Use[i][j])*(Image_Use[i+1][j] - Image_Use[i][j])); } } // 直线检测 for(int i = 0; i<Use_ROWS;i++) for(int j=0; j<Use_Line;j++) if(Image_Use[i][j]==255)//当该像素点为边缘点时 { for(int theta = 0;theta<=360;) { jiaodu = theta/4;//这里设置360度,分成多少份 int resu = (int)(i*Cos[jiaodu]+j*Sin[jiaodu]); res[jiaodu][resu] +=1; theta+=4; } } ////////////////////////////////// //找出次数最多的结果 int result = res[0][0]; for(int i=0;i<count;i++) for(int j=0;j<r;j++) { if(result<res[i][j]) result = res[i][j]; } //直线提取 int td =(int)(result * 0.8); int s1[30] = {0},s2[30]={0},d=0; for(int i=0;i<count;i++) { for(int j=0;j<r;j++) { if(res[i][j]>td) { s1[d] = i; //角度 s2[d] = j; //距离 d++; } } } d--; for(int i = 0; i < Use_ROWS; i++) { for(int s = 0 ;s<d;d++) { j = (int)((-1.0/tan[s1[s]])*i+(s2[s])/Sin[s1[s]])); Image[i][j] = 0; } }
///////////////////////////////////////////////////////
12-18 11:08