1. 程式人生 > >霍夫變換的原理 及 一種引數迭代的自適應尋找最佳霍夫直線的程式碼(在條碼識別中的應用)

霍夫變換的原理 及 一種引數迭代的自適應尋找最佳霍夫直線的程式碼(在條碼識別中的應用)

       霍夫變換(Hough Transform)是一種識別幾何形狀的方法!霍夫變換的基本原理是利用點與線的對偶性,將原始影象空間的給定的曲線通過曲線表達形式變為引數空間的一個點。這樣就把原始影象中給定曲線的檢測問題轉化為尋找引數空間中的峰值問題。霍夫變換於1962年由Paul Hough 首次提出,後於1972年由Richard Duda和Peter Hart推廣使用,經典霍夫變換用來檢測影象中的直線,後來霍夫變換擴充套件到任意形狀物體的識別,多為圓和橢圓。

1、霍夫線變化

       設已知一黑白影象上畫了一條直線,要求出這條直線所在的位置。我們知道,直線的方程可以用y=k*x+b 來表示,其中k和b是引數,分別是斜率和截距。過某一點(x0,y0)的所有直線的引數都會滿足方程y0=kx0+b。即點(x0,y0)確定了一族直線。方程y0=kx0+b在引數k--b平面上是一條直線,(你也可以是方程b=-x0*k+y0對應的直線)。這樣,影象x--y平面上的一個前景畫素點就對應到引數平面上的一條直線。我們舉個例子說明解決前面那個問題的原理。設影象上的直線是y=x, 我們先取上面的三個點:A(0,0), B(1,1), C(2,2)。可以求出,過A點的直線的引數要滿足方程b=0, 過B點的直線的引數要滿足方程1=k+b, 過C點的直線的引數要滿足方程2=2k+b, 這三個方程就對應著引數平面上的三條直線,而這三條直線會相交於一點(k=1,b=0)。 同理,原影象上直線y=x上的其它點(如(3,3),(4,4)等) 對應引數平面上的直線也會通過點(k=1,b=0)。這個性質就為我們解決問題提供了方法,就是把影象平面上的點對應到引數平面上的線,最後通過統計特性來解決問題

。假如影象平面上有兩條直線,那麼最終在引數平面上就會看到兩個峰值點,依此類推。

對於看了上述概念描述還沒有頭緒的可以學習這個PPT

https://wenku.baidu.com/view/18787c768e9951e79b89270e.html?from=search:

下面截了幾張圖幫助理解:

1)對於空間座標中的一條直線,相當於是Hough座標中的一個點


2)對於空間座標中的一個點,相當於是Hough座標中的一條線


3)Hough座標中兩條線的交點用來表示過點(x0,y0)和點(x1,y1)的直線


2、OpenCv霍夫變換---在條碼識別中的應用

        霍夫變換在OpenCV中的函式名稱為HoughLines,其中最為關鍵的是設定其第三個引數rho,對於傅立葉變換後的影象要擬合霍夫直線,如果rho的值取得很大會導致所擬合的直線滿天飛,而rho的值如果取得很小則擬合不出直線。

如何設計程式碼得到最為精確的擬合直線是關鍵技術。以下方法是:通過逐漸擴大rho的值,而從達到尋找最佳霍夫直線的要求。(通過引數迭代的方式達到自適應尋找直線的效果)

        條碼識別的HoughLines可設計為如下程式碼:

void HoughAngle(Mat FourierImage, Mat &Linemat, float &angelD) 
{
	// HoughLines查詢傅立葉頻譜的直線,該直線跟原圖的一維碼方向相互垂直
	vector<Vec2f> lines;
	float pi180 = (float)CV_PI / 180;

	// 取最佳霍夫直線
	double HoughLineRho = 0.1;
	HoughLines(FourierImage, lines, HoughLineRho, pi180, 100, 0, 0);
	
	while (!lines.size() && HoughLineRho < 20)
	{
		HoughLineRho = HoughLineRho + 0.1;
		HoughLines(FourierImage, lines, HoughLineRho, pi180, 100, 0, 0);
	}

	cout << "霍夫直線為: " << lines.size() << endl;

	Linemat = FourierImage.clone();

	// 計算霍夫直線
    float theta = 0;   // 最終theta的取值問題直接影響結果
    
	// 繪製霍夫直線,若沒有將霍夫直線擬合出來,則不進行繪製
	for (int l = 0; l < lines.size(); l++) {

		float rho = lines[l][0];
		theta = lines[l][1];
		float aa = (theta / CV_PI) * 180;
		Point pt1, pt2;
		double a = cos(theta), b = sin(theta);
		double x0 = a*rho, y0 = b*rho;
		pt1.x = cvRound(x0 + 1000 * (-b));
		pt1.y = cvRound(y0 + 1000 * (a));
		pt2.x = cvRound(x0 - 1000 * (-b));
		pt2.y = cvRound(y0 - 1000 * (a));

		line(Linemat, pt1, pt2, Scalar(255, 0, 0), 3, 8, 0); // 原圖上繪製霍夫變換的曲線
	}

	 //計算霍夫直線的角度,最終theta的取值問題直接影響結果
	if (lines.size()) 
	{
		theta = lines[0][1];
	}

	angelD = 180 * theta / CV_PI - 90;

}