理论部分来源:霍夫变换
作者: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; } }
///////////////////////////////////////////////////////