導讀:
1. houghlines的算法思想
2. houghlines實現需要考慮的要素
3. houghlines的opencv實現,代碼分析
4. houghlines的效率分析,改進
- houghlines的算法思想
檢測直線,houghlines標準算法,不考慮線段,不檢測線段端點。
在直角座標系和極座標系的對應關係,點、直線在兩個座標系中是對偶關係。
即直角座標系中的點是極座標系中的線,直角座標系中的線是極座標系中的點。
反過來,也成立。
圖像可知看做直角座標系,檢測圖像中的直線,可以轉化爲統計檢測極座標系中的點(r,theta)。
- houghlines實現需要考慮的因素
hough空間(離散極座標)的表示
原因:
圖像中直線的表示,由斜率和截距表示,而極座標中用(r, theta)表示.
r = cos(theta)*x + sin(theta)*y
對於點(x0, y0) , 在極座標中就是一條直線(很多對(r,theta)點):
r = cos(theta)*x0 + sin(theta)*y0
r,theta就是一對hough空間的變量表示。
旋轉的theta不容易表示,若將r,theta看成直角座標空間。
一個點(x0, y0), 就是一個正弦曲線。
r = cos(theta)*x0 + sin(theta)*y0
直角座標系中的一點
對應於r-theta空間的一條正弦曲線
多個點在(r,theta)平面上就是多條正弦曲線,而多條正弦曲線會相交,交點就是直角座標系中的直線。
直角座標系中的一條直線上的三個點
對應於r-theta空間中三條曲線,並交於一點
接下來,就是要考慮 將r,theta離散化,形成離散化的hough空間,類似於一個mat矩陣/圖像,用於統計交點的個數。
opencv取rtho,theta參數作爲離散度量,空間分辨率。
則hough空間的大小爲:
numangle = cvRound(CV_PI / theta);
numrho = cvRound(((width + height) * 2 + 1) / rho); // 有冗餘,在r上留有一定的空白
- 代碼分析
rho: 霍夫空間的r粒度大小
theta: 旋轉角度的粒度
threshold:直線上有多少個點的閾值
lines:輸出lines結果
linesMax:lines的最大個數
[html] view plain copy
static void
icvHoughLinesStandard( const CvMat* img, float rho, float theta,
int threshold, CvSeq *lines, int linesMax )
{
cv::AutoBuffer _accum, _sort_buf;
cv::AutoBuffer _tabSin, _tabCos;
const uchar* image;
int step, width, height;
int numangle, numrho;
int total = 0;
float ang;
int r, n;
int i, j;
float irho = 1 / rho;
double scale;
CV_Assert( CV_IS_MAT(img) && CV_MAT_TYPE(img->type) == CV_8UC1 );
image = img->data.ptr;
step = img->step;
width = img->cols;
height = img->rows;
numangle = cvRound(CV_PI / theta); // 霍夫空間,角度方向的大小
numrho = cvRound(((width + height) * 2 + 1) / rho); // r的空間範圍
_accum.allocate((numangle+2) * (numrho+2));
_sort_buf.allocate(numangle * numrho);
_tabSin.allocate(numangle);
_tabCos.allocate(numangle);
int *accum = _accum, *sort_buf = _sort_buf;
float *tabSin = _tabSin, *tabCos = _tabCos;
memset( accum, 0, sizeof(accum[0]) * (numangle+2) * (numrho+2) );
for( ang = 0, n = 0; n < numangle; ang += theta, n++ ) // 計算正弦曲線的準備工作,查表
{
tabSin[n] = (float)(sin(ang) * irho);
tabCos[n] = (float)(cos(ang) * irho);
}
// stage 1. fill accumulator
for( i = 0; i < height; i++ )
for( j = 0; j < width; j++ )
{
if( image[i * step + j] != 0 ) // 將每個非零點,轉換爲霍夫空間的離散正弦曲線,並統計。
for( n = 0; n < numangle; n++ )
{
r = cvRound( j * tabCos[n] + i * tabSin[n] );
r += (numrho - 1) / 2;
accum[(n+1) * (numrho+2) + r+1]++;
}
}
// stage 2. find local maximums // 霍夫空間,局部最大點,採用四鄰域判斷,比較。(也可以使8鄰域或者更大的方式), 如果不判斷局部最大值,同時選用次大值與最大值,就可能會是兩個相鄰的直線,但實際上是一條直線。選用最大值,也是去除離散的近似計算帶來的誤差,或合併近似曲線。
for( r = 0; r < numrho; r++ )
for( n = 0; n < numangle; n++ )
{
int base = (n+1) * (numrho+2) + r+1;
if( accum[base] > threshold &&
accum[base] > accum[base - 1] && accum[base] >= accum[base + 1] &&
accum[base] > accum[base - numrho - 2] && accum[base] >= accum[base + numrho + 2] )
sort_buf[total++] = base;
}
// stage 3. sort the detected lines by accumulator value // 由點的個數排序,依次找出哪些最有可能是直線
icvHoughSortDescent32s( sort_buf, total, accum );
// stage 4. store the first min(total,linesMax) lines to the output buffer
linesMax = MIN(linesMax, total);
scale = 1./(numrho+2);
for( i = 0; i < linesMax; i++ ) // 依據霍夫空間分辨率,計算直線的實際r,theta參數
{
CvLinePolar line;
int idx = sort_buf[i];
int n = cvFloor(idx*scale) - 1;
int r = idx - (n+1)*(numrho+2) - 1;
line.rho = (r - (numrho - 1)*0.5f) * rho;
line.angle = n * theta;
cvSeqPush( lines, &line );
}
}
效率分析
houghlines的計算效率比較低O(n*n*m),耗時較長,而且沒有檢測出直線的端點。
改進
統計概論霍夫直線檢測houghlinesP是一個改進,不僅執行效率較高,而且能檢測到直線的兩個端點。
思想:
先隨機檢測出一部分直線,然後將直線上點的排查掉,再進行其他直線的檢測首先僅統計圖像中非零點的個數,對於已經確認是某條直線上的點就不再變換了。
- 對所以有非零點逐個變換到霍夫空間
a. 並累加到霍夫統計表(圖像)中,並統計最大值
b. 最大值與閾值比較,小於閾值,則繼續下一個點的變換
c. 若大於閾值,則有一個新的直線段要產生了
d. 計算直線上線段的端點、長度,如果符合條件,則保存此線段,並mark這個線段上的點不參與其他線段檢測的變換