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運算元的邊緣檢測,調好引數,可以使手機輪廓顯現出來
最後霍夫變換,還是要調參,只要引數設定好,手機的基本直線輪廓就出來了