霍夫直線檢測的作用——計算得到輸入圖像(一般是二值化的邊緣檢測結(jié)果圖像)中包含的所有直線的數(shù)目與位置
- 在取得
圖像邊緣的基礎上,
對一些特定的幾何形狀邊緣,如直線、圓,通過圖像霍夫變換把圖像從平面坐標空間變換到霍夫坐標空間,
就可以通過求取霍夫空間的局部極大值方法(其實就是霍夫空間中的曲線交集點),
得到極坐標空間對應參數(shù)方程中直線的兩個參數(shù)(r,θ),
從而計算得到邊緣圖像中的所有直線(基于平面坐標)的數(shù)目與位置。
假設有一條直線如下圖:
(紅色部分是計算過程,遞等到右下角的結(jié)果,待會兒要用)
在笛卡兒平面坐標系統(tǒng)中的斜率參數(shù)與截距參數(shù)為(k,b);
若變換到
極坐標空間則變成求取另外兩個參數(shù)(r,θ),r 和 θ之間的關系可以表示為:
(公式的來源運算過程見上圖)
對于每個平面空間的像素點坐標(x,y),
隨著角度θ的取值不同,都會得到r值,
(%+++%要點.B)而對于任意一條直線來說,在極坐標空間它的(r,θ)都是固定不變的,
則對于邊緣圖像的每個平面空間坐標點可繪制極坐標的曲線如圖所示:

- 上圖中,
左側(cè)是一個平面空間的像素點,
基于公式r = x * cosθ + y * sinθ,
通過給定不同的θ值,得到唯一對應r值,
無數(shù)個(r,θ)數(shù)對構(gòu)成的一道極坐標曲線;
右側(cè)是三個平面空間的像素點,
基于公式r = x * cosθ + y * sinθ,
通過給定不同的θ值,得到唯一對應r值,
無數(shù)個(r,θ)數(shù)對構(gòu)成的三道極坐標曲線;
- 無論截圖的左側(cè)還是右側(cè),都是所謂
霍夫空間的一部分,所謂霍夫空間,如下圖:圖片參考于此博文
霍夫空間 概念詳析
霍夫空間就是一個基于(r,θ)兩個參數(shù)坐標軸的數(shù)據(jù)空間,
數(shù)量級規(guī)模是可以是一個邊緣圖像的像素點數(shù)量;
并且這個空間包括了這樣的一系列曲線 :
一個邊緣圖像的所有(all & each,假設為 N 個)像素點(x,y),
基于公式r = x * cosθ + y * sinθ,
通過給定不同的θ值,得到唯一對應r值,
無數(shù)個(r,θ)數(shù)對構(gòu)成對應上N個 像素點的N 道 極坐標曲線(霍夫空間的曲線);
霍夫直線檢測 的 知識要點
- (要點.A)
輸入的邊緣圖像中的每一個像素點一 一 對 應一條霍夫空間(or 極坐標參數(shù))曲線;
- (要點.B)
而對于邊緣圖像中的 任意一條直線來說,在極坐標空間它的(r,θ)都是固定不變的,
- (由上可得 要點.C)
霍夫空間中的一個交集點(若干曲線的交點(r,θ))
就是一條直線(點的參數(shù)(r,θ)可變換成直線);- 而重疊在這個
交集點上的霍夫(極坐標)曲線集,
其實就是該交集點代表的(存在邊緣圖像中 的 對應的)直線所包含的(像素)點集;
- (要點.D)
交集點上累積的曲線越多;
對應(平面坐標系的邊緣圖像上的)直線所包含的像素點集就越多;
也即對應直線的長度越大;
霍夫直線檢測 從二值化.邊緣檢測.結(jié)果圖像到檢測繪制出直線 的大概步驟
以上引用框中的內(nèi)容是個人的梳理總結(jié),下面繼續(xù)讀書筆記的內(nèi)容。
- 由在平面空間
同屬于一條直線的像素點繪制出來的曲線必然會相交于一點(上方截圖的b)右側(cè)所示的曲線), - 而這個點正是
存在邊緣對象中的對應的直線在極坐標空間中的參數(shù)方程的參數(shù),
這樣就在極坐標空間找到了直線的參數(shù)方程,
反變換回到平面坐標空間就可以求得直線的兩個參數(shù)(k,b),
得到直線位置,
而它們在極坐標的交點就是直線在霍夫空間的表達,
直線越長,其在霍夫空間這個點的累積值就越高,相對的灰度值也就越(亮)。
OpenCV關于霍夫直線變換提供了兩個相關API函數(shù),
一個是在霍夫空間求取直線兩個極坐標的參數(shù),
需要開發(fā)者自己轉(zhuǎn)換到平面坐標空間計算直線;
另外一個則會直接返回平面空間直線/線段的兩個點坐標信息。
返回極坐標參數(shù)的API函數(shù)如下:
-
HoughLines(Mat image, Mat lines, double rho, double theta, int threshold)
image:表示輸入圖像,8位單通道圖像,一般為二值圖像。
lines:表示輸出的每個直線的極坐標參數(shù)方程的兩個參數(shù)。
rho:表示極坐標空間r值每次的步長,一般設置為1。
theta:表示角度θ,每次移動1°即可。
threshold:表示霍夫空間中該點的累積數(shù),
該累積數(shù)越大,則得到的直線可能就越長,
取值范圍通常為30~50,單位是像素,
假設為30的話,則表示大于30個像素長度的線段才會被檢測到。
threshold解釋中所述的累積數(shù)可以看做我們數(shù)據(jù)處理中的投票機制,
票數(shù)大于threshold的交集點
(即累積的曲線數(shù)大于threshold的交集點),
才認定是有效直線,
才能被函數(shù)檢測到并提取出來用于返回/變換并繪制成直線;
使用該API實現(xiàn)直線檢測:
private void houghLinesDemo(Mat src, Mat dst) {
Mat edges = new Mat();
Imgproc.Canny(src, edges, 50, 150, 3, true);
Mat lines = new Mat();
Imgproc.HoughLines(edges, lines, 1,Math.PI/180.0, 200);
Mat out = Mat.zeros(src.size(), src.type());
float[] data = new float[2];
for(int i=0; i<lines.rows(); i++) {
lines.get(i, 0, data);
float rho = data[0], theta = data[1];
double a = Math.cos(theta), b = Math.sin(theta);
double x0 = a*rho, y0 = b*rho;
Point pt1 = new Point();
Point pt2 = new Point();
pt1.x = Math.round(x0 + 100*(-b));//!!!!!!!!!!!!!!!!!
pt1.y = Math.round(y0 + 100*(a));
pt2.x = Math.round(x0 - 100*(-b));
pt2.y = Math.round(y0 - 100*(a));
Imgproc.line(out, pt1, pt2, new Scalar(0,0,255), 3, Imgproc.LINE_AA, 0);
}
out.copyTo(dst);
out.release();
edges.release();
}
關于pt1.x = Math.round(x0 + 100*(-b));這一行代碼,
- 關于參數(shù)100的意義,可參考 原作者博文:
x0與y0是直線上的點,100是表示對該點到直線上分別向前后延長的距離;
在實際效果圖中(下方結(jié)果圖源程序用的原圖是lena),這個100偏移量,決定的就是這些生成直線的長度:
- 關于 Math.round()函數(shù)
- 關于 Imgproc.HoughLines() 與 Imgproc.HoughLinesP() 的 區(qū)別 以及 lines 參數(shù)位 的意義詳析
以上的這個API函數(shù)需要對得到的每對極坐標參數(shù)(r,θ)做計算,
使其變換到平面空間(x0 = r * cosθ ; y0 = r * sinθ),
接著通過對x0和y0添加偏移量并進行計算,得到直線的兩個點;
然后繪制直線。
另外一個API函數(shù)則比較簡單,
它省去了開發(fā)者自己把極坐標變換為直線坐標的過程,
直接返回每個線段/直線對應的兩個點坐標,
其API函數(shù)與參數(shù)的解釋具體如下:
-
HoughLinesP(Mat image, Mat lines, double rho, double theta, int threshold, double minLineLength, double maxLineGap)
image:表示輸入圖像,8位單通道圖像,一般為二值圖像。
lines:表示輸出的每個直線最終要繪制用的兩個 平面坐標系參數(shù)。
rho:表示極坐標空間r值每次的步長,一般設置為1。
theta:表示角度θ,每次移動1°即可。
threshold:表示極坐標中該點的累積數(shù),該累積數(shù)越大,則得到的直線可能就越長,取值范圍通常為30~50,單位是像素,假設取值為30,則表示大于30個像素長度的線段才會被檢測到。
minLineLength:表示可以檢測的最小線段長度,根據(jù)實際需要進行設置。
maxLineGap:表示線段之間的最大間隔像素,假設5表示小于5個像素的兩個相鄰線段可以連接起來。
使用該API實現(xiàn)圖像直線檢測:
private void houghLinePDemo(Mat src, Mat dst) {
Mat edges = new Mat();
Imgproc.Canny(src, edges, 50, 150, 3, true);
Mat lines = new Mat();
Imgproc.HoughLinesP(edges, lines, 1, Math.PI/180.0, 100, 50, 10);
Mat out = Mat.zeros(src.size(), src.type());
for(int i=0; i<lines.rows(); i++) {
int[] oneline = new int[4];
lines.get(i, 0, oneline);
Imgproc.line(out, new Point(oneline[0], oneline[1]),
new Point(oneline[2], oneline[3]),
new Scalar(0, 0, 255), 2, 8, 0);
}
out.copyTo(dst);
// 釋放內(nèi)存
out.release();
edges.release();
}
- 這里需要注意的是,
圖像二值化與邊緣檢測算法輸出結(jié)果的質(zhì)量在很大程度上影響 霍夫直線變換的結(jié)果,
同時在使用HoughLinesP的時候,最后兩個參數(shù)的設置也會影響霍夫直線檢測的結(jié)果。





