理論部分來源:霍夫變換
作者:https://home.cnblogs.com/u/php-rearch/
一、霍夫變換(Hough)
A-基本原理
一條直線可由兩個點A=(X1,Y1)和B=(X2,Y2)確定(笛卡爾坐標)
另一方面,也可以寫成關於(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; } }
///////////////////////////////////////////////////////