【OpenCV學習筆記】之霍夫變換(Hough Transform)

一、霍夫變換(Hough transform)

常見的理論概述是這樣的:

1、簡單介紹

       霍夫變換(Hough Transform)是圖像處理中的一種特徵提取技術,它通過一種投票算法檢測具有特定形狀的物體。Hough變換是圖像處理中從圖像中識別幾何形狀的基本方法之一。Hough變換的基本原理在於利用點與線的對偶性,將原始圖像空間的給定的曲線通過曲線表達形式變爲參數空間的一個點。這樣就把原始圖像中給定曲線的檢測問題轉化爲尋找參數空間中的峯值問題。也即把檢測整體特性轉化爲檢測局部特性。比如直線、橢圓、圓、弧線等。

2、Hough變換的基本思想

       設已知一黑白圖像上畫了一條直線,要求出這條直線所在的位置。我們知道,直線的方程可以用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)。這個性質就爲我們解決問題提供了方法,就是把圖像平面上的點對應到參數平面上的線,最後通過統計特性來解決問題。假如圖像平面上有兩條直線,那麼最終在參數平面上就會看到兩個峯值點,依此類推。

        簡而言之,Hough變換思想爲:在原始圖像座標系下的一個點對應了參數座標系中的一條直線,同樣參數座標系的一條直線對應了原始座標系下的一個點,然後,原始座標系下呈現直線的所有點,它們的斜率和截距是相同的,所以它們在參數座標系下對應於同一個點。這樣在將原始座標系下的各個點投影到參數座標系下之後,看參數座標系下有沒有聚集點,這樣的聚集點就對應了原始座標系下的直線。

        在實際應用中,y=k*x+b形式的直線方程沒有辦法表示x=c形式的直線(這時候,直線的斜率爲無窮大)。所以實際應用中,是採用參數方程p=x*cos(theta)+y*sin(theta)。這樣,圖像平面上的一個點就對應到參數p---theta平面上的一條曲線上,其它的還是一樣。

3、Hough transform的原理

        要理解Hough 變換極其注意的是,原笛卡爾座標系裏的直線 上所有的點帶入直線方程後,進行變換後在斜率k截距b爲橫縱座標的hough座標系裏面又是另外一條直線 其中 是已知的,相當於笛卡爾座標系裏面的斜率和截距,即

是一條Hough座標系裏面的一條直線。如果所有的參數點 均來自 上,那麼其對應的Hough座標系裏面的k和b,即橫縱座標。且必有一條直線和原直線 重合,在Hough座標系裏面顯示,就是過 點。

下面圖片是自己對hough變換的理解:

1.1  霍夫直線變換(Hough Lines Transform)

1.1.1 直線座標參數空間

       在圖像x−y座標空間中,經過點(xi,yi)的直線表示爲: 

                                       

        其中,參數a爲斜率,b爲截矩。

       通過點(xi,yi)的直線有無數條,且對應於不同的a和b值。如果將xi和yi視爲常數,而將原本的參數a和b看作變量,則式子(1)可以表示爲:

                                                                         (2)

這樣就變換到了參數平面a−b。這個變換就是直角座標中對於(xi,yi)點的Hough變換。該直線是圖像座標空間中的點(xi,yi)在參數空間的唯一方程。考慮到圖像座標空間中的另一點(xj,yj),它在參數空間中也有相應的一條直線,表示爲:

                                                                       (3)

這條直線與點(xi,yi)在參數空間的直線相交於一點 (a0,b0),如圖所示: 

                               

       圖像座標空間中過點(xi,yi)和點(xj,yj)的直線上的每一點在參數空間a−b上各自對應一條直線,這些直線都相交於點(a0,b0),而a0、b0就是圖像座標空間x−y中點(xi,yi)和點(xj,yj)所確定的直線的參數,即其中a0和b0就是原圖像座標空間直線的斜率和截距。

      反之,在參數空間相交於同一點的所有直線,在圖像座標空間都有共線的點與之對應。根據這個特性,給定圖像座標空間的一些邊緣點,就可以通過Hough變換確定連接這些點的直線方程。

     具體計算時,可以將參數空間視爲離散的。建立一個二維累加數組A(a,b),第一維的範圍是圖像座標空間中直線斜率的可能範圍,第二維的範圍是圖像座標空間中直線截矩的可能範圍。開始時A(a,b)初始化爲0,然後對圖像座標空間的每一個前景點(xi,yi),將參數空間中每一個aa的離散值代入式子(2)中,從而計算出對應的b值。每計算出一對(a,b),都將對應的數組元素A(a,b)加1,即A(a,b)=A(a,b)+1。所有的計算結束之後,在參數計算表決結果中找到A(a,b)的最大峯值,所對應的a0、b0就是源圖像中共線點數目最多(共A(a,b個共線點)的直線方程的參數;接下來可以繼續尋找次峯值和第3峯值和第4峯值等等,它們對應於原圖中共線點略少一些的直線。

注意:由於原圖中的直線往往具有一定的寬度,實際上相當於多條參數極其接近的單像素寬直線,往往對應於參數空間中相鄰的多個累加器。因此每找到一個當前最大的峯值點後,需要將該點及其附近點清零,以防算法檢測出多條極其鄰近的直線

對於上圖的Hough變換空間情況如下圖所示。

                      

       這種利用二維累加器的離散方法大大簡化了Hough變換的計算,參數空間a−ba−b上的細分程度決定了最終找到直線上點的共線精度。上述的二維累加數組A也被稱爲Hough矩陣

注意:使用直角座標表示直線,當直線爲一條垂直直線或者接近垂直直線時,該直線的斜率爲無限大或者接近無限大,從而無法在參數空間$a-b$上表示出來。爲了解決這個問題,可以採用極座標。

1.1.2 極座標參數空間

極座標中用如下參數方程表示一條直線。

                                                                        (4)

其中,ρ代表直線到原點的垂直距離,θ代表x軸到直線垂線的角度,取值範圍爲±90°,如圖所示。

                

       與直角座標類似,極座標中的Hough變換也將圖像座標空間中的點變換到參數空間中。 

       在極座標表示下,圖像座標空間中共線的點變換到參數空間中後,在參數空間都相交於同一點,此時所得到的ρ、θ即爲所求的直線的極座標參數。與直角座標不同的是,用極座標表示時,圖像座標空間的共線的兩點(xi,yi)和(xj,yj)映射到參數空間是兩條正弦曲線,相交於點(ρ0,θ0),如上圖所示。

      具體計算時,與直角座標類似,也要在參數空間中建立一個二維數組累加器A,只是取值範圍不同。對於一副大小爲D×D的圖像,通常ρ的取值範圍爲[−2D/2,2D/2],θ的取值範圍爲[−90°,90°]。計算方法與直角座標系中累加器的計算方法相同,最後得到最大的A所對應的(ρ,θ)。

1.2 霍夫圓變換(Hough Circles Transform)

        Hough變換同樣適用於方程已知的曲線檢測。

        圖像座標空間中的一條已知的曲線方程也可以建立其相應的參數空間。由此,圖像座標空間中的一點,在參數空間中就可以映射爲相應的軌跡曲線或者曲面。若參數空間中對應各個間斷點的曲線或者曲面能夠相交,就能找到參數空間的極大值以及對應的參數;若參數空間中對應各個間斷點的曲線或者曲面不能相交,則說明間斷點不符合某已知曲線。

Hough變換做曲線檢測時,最重要的是寫出圖像座標空間到參數空間的變換公式。

例如,對於已知的圓方程,其直角座標的一般方程爲: 

                                                                        (5)

其中,(a,b)爲圓心座標,r爲圓的半徑。 

        那麼,參數空間可以表示爲(a,b,r),圖像座標空間中的一個圓對應參數空間中的一個點。具體計算時,與前面討論的方法相同,只是數組累加器爲三維A(a,b,r) 計算過程是讓a,b在取值範圍內增加,解出滿足上式的r值,每計算出一個(a,b,r)值,就對相應的數組元素A(a,b,r)加1.計算結束後,找到的最大的A(a,b,r)所對應的a,b,r就是所求的圓的參數。與直線檢測一樣,曲線檢測也可以通過極座標形式計算。 
注意:通過Hough變換做曲線檢測,參數空間的大小將隨着參數個數的增加呈指數增長的趨勢。所以在實際使用時,要儘量減少描述曲線的參數數目。因此,這種曲線檢測的方法只對檢測參數較少的曲線有意義。

霍夫圓檢測原理:

                                              

        

  1. 從平面座標到極座標轉換三個參數Cx0,y0,r其中x0,y0是圓心
  2. 假設平面座標的任意一個圓上的點,轉換到極座標中:      處有最大值,霍夫變換正是利用這個原理實現圓的檢測。

                                       

1.3 任意形狀的檢測       

        這裏所說的任意形狀的檢測,是指應用廣義Hough變換去檢測某一任意形狀邊界的圖形。它首先選取該形狀中的任意點(a,b)爲參考點,然後從該任意形狀圖形的邊緣每一點上,計算其切線方向ϕ和到參考點(a,b)位置的偏移矢量r,以及rx軸的夾角α

參考點(a,b)的位置可由下式算出: 

                                                            a=x+r(ϕ)cos(α(ϕ))                (6)

                                                            b=x+r(ϕ)sin(α(ϕ))                  (7)

OpenCV裏面API介紹:

霍夫直線檢測

  1. 標準的霍夫變換 cv::HoughLines從平面座標轉換到霍夫空間,最終輸出是  表示極座標空間
  2. 霍夫變換直線概率 cv::HoughLinesP最終輸出是直線的兩個點
cv::HoughLines(
InputArray src, // 輸入圖像,必須8-bit的灰度圖像
OutputArray lines, // 輸出的極座標來表示直線
double rho, // 生成極座標時候的像素掃描步長
double theta, //生成極座標時候的角度步長,一般
CV_PI/180
int threshold, // 閾值,只有獲得足夠交點的極座標點才被看成是直線
double srn=0,// 是否應用多尺度的霍夫變換,如果不是設置0表示經典霍夫變換
double stn=0,//是否應用多尺度的霍夫變換,如果不是設置0表示經典霍夫變換
double min_theta=0, // 表示角度掃描範圍 0 ~180之間, 默認即可
double max_theta=CV_PI) 
// 一般情況是有經驗的開發者使用,需要自己反變換到平面空間
cv::HoughLinesP(
InputArray src, // 輸入圖像,必須8-bit的灰度圖像
OutputArray lines, // 輸出的極座標來表示直線
double rho, // 生成極座標時候的像素掃描步長
double theta, //生成極座標時候的角度步長,一般取值CV_PI/180
Int threshold, // 閾值,只有獲得足夠交點的極座標點才被看成是直線
double minLineLength=0,// 最小直線長度
double maxLineGap=0// 最大間隔)

霍夫圓檢測

相關API cv::HoughCircles

  • 因爲霍夫圓檢測對噪聲比較敏感,所以首先要對圖像做濾波處理。
  • 基於效率考慮,Opencv中實現的霍夫變換圓檢測是基於圖像梯度的實現,分爲兩步:

    1. 檢測邊緣,發現可能的圓心

    2. 基於第一步的基礎上從候選圓心開始計算最佳半徑大小

cv::HoughCircles(
InputArray image, // 輸入圖像 ,必須是8位的單通道灰度圖像
OutputArray circles, // 輸出結果,發現的圓信息
Int method, // 方法 - HOUGH_GRADIENT
Double dp, // dp = 1; 
Double mindist, // 10 最短距離-可以分辨是兩個圓的,否則認爲是同心圓- src_gray.rows/8
Double param1, // canny edge detection low threshold
Double param2, // 中心點累加器閾值 – 候選圓心
Int minradius, // 最小半徑
Int maxradius//最大半徑 )

示例程序:

一、直線檢測

//霍夫變換-直線檢測
#include "stdafx.h"
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>

using namespace cv;
using namespace std;

int main(int argc, char*argv)
{
	Mat src;
	src = imread("C:\\Users\\59235\\Desktop\\imag\\LineAndWord2.png");
	if (!src.data)
	{
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("input", CV_WINDOW_AUTOSIZE);
	imshow("input", src);

	Mat gray_src, dst;
	cvtColor(dst, gray_src, CV_GRAY2BGR);
	//Canny邊緣檢測
	Canny(gray_src, dst, 100, 200, 3);
	imshow("1", gray_src);
	imshow("2", dst);

	vector<Vec4f>plines;//定義一個存放直線信息的向量
						//Hough直線檢測API
	HoughLinesP(dst, plines, 1, CV_PI / 180, 70, 100, 50);
	//InputArray src, // 輸入圖像,必須8-bit的灰度圖像;OutputArray lines, // 輸出的極座標來表示直線;double rho, // 生成極座標時候的像素掃描步長;double theta, //生成極座標時候的角度步長,一般取值CV_PI/180;int threshold, // 閾值,只有獲得足夠交點的極座標點才被看成是直線;double minLineLength = 0;// 最小直線長度;double maxLineGap = 0;// 最大間隔

	//標記出直線
	for (size_t i = 0; i < plines.size(); i++)
	{
		Vec4f point1 = plines[i];
		line(src, Point(point1[0], point1[1]), Point(point1[2], point1[3]), Scalar(255, 255, 0), 2, LINE_AA);
	}

	imshow("result", src);
	waitKey(0);
	return 0;
}

效果圖:                                                               (原圖)

                                   

                                                                      (未進行Canny邊緣檢測得到的效果圖)

                          (Canny邊緣檢測)                                                                 (繪製的直線圖)

            

分析:由實驗結果圖,沒有對灰度圖像進行邊緣檢測的,而直接進行霍夫變換直線檢測,顯然不能很好的檢測出圖像的直線。因此,可以知道Canny邊緣檢測有助於霍夫直線變換更好的過濾干擾點,從而更準確地判斷圖像中的直線。

二、圓檢測

//霍夫變換-圓檢測
#include "stdafx.h"
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include<highgui.h>

using namespace cv;
using namespace std;

int main(int argc, char*argv)
{
	Mat src;
	src = imread("C:\\Users\\59235\\Desktop\\imag\\mixed_01.png");
	if (!src.data)
	{
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("input", CV_WINDOW_AUTOSIZE);
	imshow("input", src);

	Mat gray_src, dst, dst1;
	medianBlur(src, dst, 5);//因爲霍夫圓檢測對噪聲比較敏感,故須先做中值濾波
	cvtColor(dst, gray_src, CV_BGR2GRAY);
	imshow("gray", gray_src);

	//Canny檢測邊緣
	//Canny(dst, dst1, 100, 200, 3);

	//hough變換檢測圓(基於灰度空間)
	vector<Vec3f> circles;
	HoughCircles(gray_src, circles, HOUGH_GRADIENT, 1, 10, 40, 40, 5, 100);
	//InputArray image, // 輸入圖像 ,必須是8位的單通道灰度圖像
	//OutputArray circles, // 輸出結果,發現的圓信息
	//Int method, // 方法 - HOUGH_GRADIENT
	//Double dp, // dp = 1; 
	//Double mindist, // 10 最短距離-可以分辨是兩個圓的,否則認爲是同心圓- src_gray.rows/8
	//Double param1, // canny edge detection low threshold
	//Double param2, // 中心點累加器閾值 – 候選圓心
	//Int minradius, // 最小半徑
	//Int maxradius//最大半徑 

	//cvtColor(gray_src, dst1, CV_GRAY2BGR);//重新轉回RGB色彩空間
	//標記出圓和圓心
	for (size_t i = 0; i <circles.size(); i++)
	{
		Vec3f cc = circles[i];
		cout << "=" << endl << cc << endl;//查看圖像中圓的信息
		circle(src, Point(cc[0], cc[1]), cc[2], Scalar(0, 0, 255), 2, LINE_AA);//標記出圓
		circle(src, Point(cc[0], cc[1]), 2, Scalar(255, 0, 0), 2, LINE_AA);//標記出圓心(這裏把圓的半徑設爲2,並把標記線的粗細設爲2,剛好畫出一個實心的圓心)
	}

	imshow("result", src);
	waitKey(0);
	return 0;
}

效果圖:

                                          (原圖)                                                                                  (灰度圖)

                         Canny檢測邊緣圖)                                                                        (結果圖)

                              (並未檢測邊緣,直接對灰度圖檢測的結果)

分析:本實驗有點失敗,在對灰度圖像進行邊緣檢測的基礎上,再進行霍夫圓檢測,這種正確的步驟的基礎上,卻得到一個效果相當差的結果。而不進行邊緣檢測,直接進行霍夫圓檢測的結果,是隻能檢測出工件的內接圓,其他一些外接圓則不能檢測出來。這個實驗還有待進行下一步及改進,我覺得應該出在對圖像的預處理這一塊,OpenCV裏面的圓檢測這個應該是沒什麼問題的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章