1. 程式人生 > >經典霍夫線性變換演算法和原始碼

經典霍夫線性變換演算法和原始碼

          霍夫變換是一種無需影象內容先驗知識的特徵提取技術,可以廣泛的應用影象中直線、圓、橢圓等形狀的識別。本文主要基於opencv原始碼實現經典的線性霍夫變換。

      在直角座標系下,直線被定義為:

              y=mx+b    (1)

其中, m為斜率,b為與y軸的截距,只要確定了mb,一條直線就可以被唯一地確定下來。如果用ρ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;
};
經典線性霍夫變換缺點:執行耗時,不能定位線段的起始終止點。