1. 程式人生 > >Opencv2.4學習::霍夫變換(1)線變換

Opencv2.4學習::霍夫變換(1)線變換

霍夫變換

特點:

  • 用於識別幾何形狀
  • 不受旋轉角度影響

 線變換基本原理

對於上述不理解的,可以看下面:

對於某直線,可以過原點作其法向量,假設在 ρ為原點到直線距離 和 θ已知的情況下

因此,該直線的   截距=ρ/sin θ   ,     斜率=  -1/sin θ

可以寫出直線方程:(-1/tan θ)*x + ρ/sin θ  = y

變形後,可得:x*cos θ +y*sin θ = ρ                        (1)

 

 那麼,反過來,當我們固定一個點(x1,y1)時,由 ρ 和 θ 的不同組合,即可得到通過點(x1,y1)的不同直線。如下圖:

 由式(1),我們將x,y看作是常量, ρ 和 θ 為變數,那麼,可以重新寫成:

      x*(1/tan θ) +y= ρ /sin θ

這個變換的意義在於,表示了在直角座標系中經過點(x,y)的所有直線,在(ρ,θ)座標系下,為一條直線上不同的點(實際上應該不是直線,這裡為了方便描述,用直線代替)

 

 根據前文所述:

表示了在直角座標系中經過點(x,y)的所有直線,在(ρ,θ)座標系下,為一條直線上不同的點

 那麼,再次反過來看,根據對偶性,則有:

在(ρ,θ)霍夫空間中,經過點(ρ,θ)的所有直線,為直角座標系中一條直線上不同的點

到這裡,情況就比較明朗了:

要做的事情就是

(1)對每個點進行霍夫變換,那麼該點在霍夫空間中對應一條直線

(2)如果霍夫空間中的某一條直線與另外一些直線相交於同一點A

(3)那麼, 表示在直角座標系中這些直線所對應的畫素點是共線的。

(4)最後,再通過閾值來判斷 經過點A的直線是否達到閾值,是,則認為在直角座標系中存在對應直線

 核心函式

一、標準霍夫變換

C++: void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )
  • 第一個引數,輸入影象。即源影象,需為8位的單通道二進位制影象。能夠將隨意的源圖載入進來後由函式改動成此格式後,再填在這裡。
  • 第二個引數。經過呼叫HoughLines函式後儲存了霍夫線變換檢測到線條的輸出向量。每一條線由具有兩個元素的向量表示,當中,是離座標原點((0,0)(也就是影象的左上角)的距離。 是弧度線條旋轉角度(0~垂直線,π/2~水平線)。
  • 第三個引數,double型別的rho,以畫素為單位的距離精度。還有一種形容方式是直線搜尋時的進步尺寸的單位半徑。PS:Latex中/rho就表示 
  • 第四個引數,double型別的theta,以弧度為單位的角度精度。還有一種形容方式是直線搜尋時的進步尺寸的單位角度。
  • 第五個引數,int型別的threshold。累加平面的閾值引數,即識別某部分為圖中的一條直線時它在累加平面中必須達到的值。大於閾值threshold的線段才幹夠被檢測通過並返回到結果中。
  • 第六個引數。double型別的srn,有預設值0。對於多尺度的霍夫變換。這是第三個引數進步尺寸rho的除數距離。

    粗略的累加器進步尺寸直接是第三個引數rho,而精確的累加器進步尺寸為rho/srn。

  • 第七個引數,double型別的stn。有預設值0,對於多尺度霍夫變換,srn表示第四個引數進步尺寸的單位角度theta的除數距離。且假設srn和stn同一時候為0,就表示使用經典的霍夫變換。否則,這兩個引數應該都為正數。

二、概率霍夫變換【常用

概率霍夫變換(Progressive Probabilistic Hough Transform)的原理很簡單,如下所述:

1.隨機獲取邊緣影象上的前景點,對映到極座標系畫曲線;

2.當極座標系裡面有交點達到最小投票數,將該點對應x-y座標系的直線L找出來;

3.搜尋邊緣影象上前景點,在直線L上的點(且點與點之間距離小於maxLineGap的)連成線段,然後這些點全部刪除,並且記錄該線段的引數(起始點和終止點),當然線段長度要滿足最小長度;

4.重複1. 2. 3.。

void HoughLinesP(InputArray image,OutputArray lines, double rho, double theta, int threshold, double minLineLength=0,double maxLineGap=0 )
  • image為輸入影象,要求是單通道,8點陣圖像
  • lines為輸出引數,4個元素表示,即直線的起始和終止端點的4個座標(x1,y1),(x2,y2)
  • rho為距離解析度,一般為1
  • theta為角度的解析度,一般CV_PI/180
  • threshold為閾值,hough變換影象空間值最大點,大於則認為是直線
  • minLineLength為最小直線長度(畫素),即如果小於該值,則不輸出該結果
  • maxLineGap為直線間隙最大值,如果兩條直線間隙大於該值,則被認為是兩條線段,否則是一條。

呼叫程式碼: 

程式碼中使用的是概率霍夫變換,當然據說標準霍夫變換另有他用,若遇到,會貼出來的。

#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>
using namespace std;
using namespace cv;
void main()
{
	Mat srcImage = imread("F:\\opencv_re_learn\\5.jpg");
	if (!srcImage.data){
		cout << "failed to read" << endl;
		system("pause");
		return;
	}
	//灰度圖
	Mat srcGray;
	cvtColor(srcImage, srcGray, CV_BGR2GRAY);

	//Canny 邊緣檢測
	medianBlur(srcGray, srcGray,7);
	Canny(srcGray, srcGray, 180, 100, 3);
	
	////標準霍夫變換
	//vector<Vec2f> lines;
	//輸入影象必須為二值化影象
	//HoughLines(srcGray, lines, 1, CV_PI / 180, 100, 0, 0);
	//for (size_t i = 0; i < lines.size(); i++){
	//	//根據直線引數表示式繪製結果
	//	float rho = lines[i][0], theta = lines[i][1];
	//	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(srcImage, pt1, pt2, Scalar(255, 0, 0), 3, CV_AA);
	//}

	//統計概率霍夫變換
	vector<Vec4i> lines;
	//輸入影象必須為二值化影象
	HoughLinesP(srcGray, lines, 1, CV_PI / 180, 60, 30, 20);
	for (size_t i = 0; i < lines.size(); i++){
		Vec4i l = lines[i];
		//繪製檢測結果
		line(srcImage, Point(l[0], l[1]),
			Point(l[2], l[3]), Scalar(0, 0, 255), 3, CV_AA);
	}
	imshow("src", srcImage);
	imshow("srcGray", srcGray);
	waitKey(0);
}

實現效果:

原圖:

先進行中值濾波,把地板上的噪聲濾除

然後進行Canny運算元的邊緣檢測,調好引數,可以使手機輪廓顯現出來

最後霍夫變換,還是要調參,只要引數設定好,手機的基本直線輪廓就出來了