經典霍夫線性變換演算法和原始碼
阿新 • • 發佈:2019-01-07
霍夫變換是一種無需影象內容先驗知識的特徵提取技術,可以廣泛的應用影象中直線、圓、橢圓等形狀的識別。本文主要基於opencv原始碼實現經典的線性霍夫變換。
在直角座標系下,直線被定義為:
y=mx+b (1)
其中, m為斜率,b為與y軸的截距,只要確定了m和b,一條直線就可以被唯一地確定下來。如果用ρ0表示原點到該直線的代數距離,θ0表示該直線的正交線與x軸的夾角,則:
(2)
(3)
則該直線又可表示為:
(4)
簡化成一般式,即:
(5)
很容易想到,( ρ,θ)是極座標的表示形式。但如果把(ρ,θ)也用直角座標的形式表示,即把ρ和θ做正交處理,則(ρ,θ)就被稱為霍夫空間。
在直角座標系中的一點,對應於霍夫空間的一條正弦曲線。直線是由無數個點組成的,在霍夫空間就是無數條正弦曲線,但這些正弦曲線會相交於一點(ρ0,θ0),把該點帶入公式2和公式3就得了直線的斜率和截距,這樣一條直線就被確定了下來。因此在用霍夫變換識別直線時,霍夫空間中的極大值就有可能對應一條直線。
則霍夫線性變換演算法實現步驟主要有:
1、對邊緣影象進行霍夫空間變換;
2、在4鄰域內找到霍夫空間變換的極大值;
3、對這些極大值安裝由大到小順序進行排序,極大值越大,越有可能是直線;
4、輸出直線。
基於opencv實現的原始碼如下:
輔助函式和資料結構:void FindLinesByHough(const BYTE* const* binaryImg, int M, int N, float rho, float theta, int threshold, LineNode* &pLines, int &linesMax) { //tho表示距離刻度,theta表示角度刻度,pLines表示返回的直線陣列指標,threshold表示直線的最少畫素值,linesMax表示要返回的最大邊數目。 int* pAccm = NULL; KVNode* pKVNodeSort = NULL; float* tabSin = NULL; float* tabCos = NULL; int total = 0; int numAngle, numRho; int i, j; double scale = 0.0; float iRho = 1 / rho; int width = M; int height = N; numAngle = Round(PI / theta); numRho = Round(((width + height) * 2 + 1) / rho); pAccm = new int[(numAngle + 2)*(numRho + 2)]{0}; pKVNodeSort = new KVNode[numAngle*numRho]; tabSin = new float[numAngle]{0.0f}; tabCos = new float[numAngle]{0.0f}; float ang = 0;//避免重複計算角度,事先計算好sinθi/ρ和cosθi/ρ for (int n = 0; n < numAngle; ang += theta, n++) { tabSin[n] = sinf(ang)*iRho; tabCos[n] = cosf(ang)*iRho; } //第一步:逐點進行或霍夫空間變換,並將結果寫入累加器陣列內 for (int i = 0; i < height; ++i) { for (int j = 0; j < width; ++j) { if (0 != (int)binaryImg[j][i]) { for (int n = 0; n < numAngle; n++) { int r = Round(j*tabCos[n] + i*tabSin[n]); r += (numRho - 1) / 2; ++pAccm[(n + 1)*(numRho + 2) + r + 1]; } } } } //第二步:找到區域性極大值,即做四方向的非極大值抑制 for (int r = 0; r < numRho; ++r) { for (int n = 0; n < numAngle; ++n) { int base = (n + 1)*(numRho + 2) + r + 1; if (pAccm[base]>threshold && pAccm[base]>pAccm[base - 1] && pAccm[base] > pAccm[base + 1] && pAccm[base] > pAccm[base - numRho - 2] && pAccm[base] > pAccm[base + numRho + 2]) { KVNode tempKVNode; tempKVNode.index = base; tempKVNode.pixelSum = pAccm[base]; pKVNodeSort[total++] = tempKVNode; } } } //第三步:對累加器值按由大到小排序 sort(pKVNodeSort, pKVNodeSort + total, Compare); //第四步:輸出linesMax條直線,如果linesMax等於total,則輸出所有直線 linesMax = (linesMax <= total) ? linesMax : total; pLines = new LineNode[linesMax]; scale = 1.0 / (numRho + 2); for (int i = 0; i < linesMax; ++i) { LineNode tempLineNode; int index = pKVNodeSort[i].index;//分離出該極大值在霍夫空間中的位置 int n = floor(index*scale) - 1; int r = index - (n + 1)*(numRho + 2) - 1; tempLineNode.rho = (r - (numRho - 1)*0.5f)*rho; tempLineNode.angle = n*theta; pLines[i] = tempLineNode; } }
bool Compare(KVNode& aNode, KVNode& bNode)
{
return (aNode.pixelSum > bNode.pixelSum);
}
struct LineNode
{
float rho;
float angle;
};
struct KVNode
{
int index;
int pixelSum;
};
經典線性霍夫變換缺點:執行耗時,不能定位線段的起始終止點。