天天看點

計算機視覺——邊緣檢測與霍夫變換

目的:

了解OpenCV中canny邊緣檢測函數的用法,并選取圖像進行測試,觀察門檻值對結果的影響。

實作基于霍夫變換的圖像圓檢測(邊緣檢測可以用opencv的canny函數)。

一.Canny邊緣檢測

1.實驗目的:了解OpenCV中canny邊緣檢測函數的用法,并選取圖像進行測試,觀察門檻值對結果的影響。

2.Canny邊緣檢測算子是John F. Canny于 1986 年開發出來的一個多級邊緣檢測算法。更為重要的是 Canny 創立了邊緣檢測計算理論(Computational theory of edge detection)解釋這項技術如何工作。

通常情況下邊緣檢測的目的是在保留原有圖像屬性的情況下,顯著減少圖像的資料規模。

3.步驟:Canny邊緣檢測算法可以分為以下5個步驟:

(1)應用高斯濾波來平滑圖像,目的是去除噪聲

(2)找尋圖像的強度梯度(intensity gradients)

(3)非最大抑制(non-maximum suppression)技術來消除邊誤檢(本來不是但檢測出來是)

(4)使用雙門檻值的方法來決定可能的(潛在的)邊界

(5)利用滞後技術來跟蹤邊界

3.void Canny(InputArray image,OutputArray edges, double threshold1, double threshold2, int apertureSize=3,bool L2gradient=false )

Mat srcImage = imread(Path);

Mat srcGray;

cvtColor(srcImage, srcGray, CV_BGR2GRAY);

//高斯濾波

GaussianBlur(srcGray, srcGray, Size(3, 3),

0, 0, BORDER_DEFAULT);

//Canny檢測

int edgeThresh =100;

Mat Canny_result;

Canny(srcImage, Canny_result, edgeThresh, edgeThresh * 3, 3);

imshow(“src”, srcImage);

imshow(“Canny_result”, Canny_result);

waitKey(0);

計算機視覺——邊緣檢測與霍夫變換

下面是門檻值從小到大的調節過程:

計算機視覺——邊緣檢測與霍夫變換
計算機視覺——邊緣檢測與霍夫變換

(70,200)

計算機視覺——邊緣檢測與霍夫變換

(40,60)

嘗試加入滑動條:

計算機視覺——邊緣檢測與霍夫變換
計算機視覺——邊緣檢測與霍夫變換

注意:調用Canny函數的輸入和輸出圖像不能是一個名字,否則就會不斷疊代canny:

計算機視覺——邊緣檢測與霍夫變換

二.霍夫圓檢測

1.實驗目的:實作基于霍夫變換的圖像圓檢測(邊緣檢測可以用opencv的canny函數)。

  1. 實驗原理:在笛卡爾坐标下,圓的表示方程為:(x-a)²+(y-b)²=r²;但在極坐标下,假設已知圓心(x0,y0),那麼圓上的點可以表示為:

對于任意一個圓, 假設中心像素點p(x0, y0)像素點已知, 圓半徑已知,則旋轉360度,由極坐标方程可以得到每個點上的坐标。同樣,如果隻是知道圖像上像素點, 圓半徑,旋轉360°,則會有一個集中的交點,即圓心,也就是說圓點處的坐标值最強,這正是霍夫變換檢測圓的數學原理。

3.經典方法:

3.1對直線來說, 一條直線能由參數極徑極角 (γ, θ) 表示. 而對圓來說, 需要三個參數來表示一個圓。現在原圖像的邊緣圖像的任意點對應的經過這個點的所有可能圓是在三維空間有下面這三個參數來表示了,其對應一條三維空間的曲線. 那麼與二維的霍夫線變換同樣的道理,對于多個邊緣點越多這些點對應的三維空間曲線交于一點那麼他們經過的共同圓上的點就越多。

類似的我們也就可以用同樣的門檻值的方法來判斷一個圓是否被檢測到, 這就是标準霍夫圓變換的原理, 但也正是在三維空間的計算量大大增加的原因, 标準霍夫圓變化很難被應用到實際中。

3.2 opencv中實作的圓檢測算法,一般算法為取參考點,對于邊緣像素點計算梯度角,對每一個梯度角,存儲對應于參考點的距離和角度;算法具有較好的抗幹擾性,但也需要較大的存儲空間和計算量,如下是opencv圓檢測算法的思路:

4.霍夫變換,兩個階段:

(1)檢測圓心:圓心是它所在圓周所有法線的交彙處,是以隻要找到這個交點,即可确定圓心:

對輸入圖像邊緣檢測;

 計算圖形的梯度,并确定圓周線,其中圓周的梯度就是它的法線;

 在二維霍夫空間内繪出所有圖形的梯度直線,坐标點累加和的值越大,則該點上直線相交的次數越多,該點越有可能是圓心;

 在霍夫空間的4鄰域内進行非最大值抑制;

 設定一個門檻值,霍夫空間内累加和大于該門檻值的點就對應于圓心。

(2)測圓半徑的方法是從圓心到圓周上的任意一點的距離相同,首先确定一個門檻值,隻要計算得到相同距離的數量大于該門檻值,就認為該距離就是該圓心所對應的圓半徑:

 計算某一個圓心到所有圓周線的距離,這些距離中就有該圓心所對應的圓的半徑的值,這些半徑值當然是相等的,并且這些圓半徑的數量要遠遠大于其他距離值相等的數量;

 設定兩個門檻值:最大半徑和最小半徑。保留距離在這兩個半徑之間的值,這意味着我們檢測的圓不能太大,也不能太小;

 對保留下來的距離進行排序;

 找到距離相同的那些值,并計算相同值的數量;

 設定一個門檻值,隻有相同值的數量大于該門檻值,就認為該值是該圓心對應的圓半徑;

 對每一個圓心,完成上面的步驟,得到所有的圓半徑。

  1. HoughCircles函數的原型為:

    void HoughCircles(InputArray image,OutputArray circles, int method, double dp, double minDist, double param1=100, double param2=100, int minRadius=0,int maxRadius=0 )

    image為輸入圖像,要求是灰階圖像

    circles為輸出圓向量,每個向量包括三個浮點型的元素——圓心橫坐标,圓心縱坐标和圓半徑

    method為使用霍夫變換圓檢測的算法,Opencv2.4.9隻實作了2-1霍夫變換,它的參數是CV_HOUGH_GRADIENT

    dp為第一階段所使用的霍夫空間的分辨率,dp=1時表示霍夫空間與輸入圖像空間的大小一緻,dp=2時霍夫空間是輸入圖像空間的一半,以此類推

    minDist為圓心之間的最小距離,如果檢測到的兩個圓心之間距離小于該值,則認為它們是同一個圓心

    param1、param2為門檻值

    minRadius和maxRadius為所檢測到的圓半徑的最小值和最大。

    特定參數檢測效果:

    計算機視覺——邊緣檢測與霍夫變換
    計算機視覺——邊緣檢測與霍夫變換

    6.發現的一些問題:

    (1)對一些不是很規整的圖檔是很難找到圓的

    計算機視覺——邊緣檢測與霍夫變換
    (2)依賴于參數的調節,參數不當會出現如下情況
    計算機視覺——邊緣檢測與霍夫變換
    調整後Mindist= edges.rows / 5
計算機視覺——邊緣檢測與霍夫變換
//邊緣檢測
#include<cv.h>
#include<highgui.h>
#include<iostream>
#include<io.h>
#include<opencv2\opencv.hpp>   

using namespace std;
using namespace cv;

Mat img,DstPic, edge, grayImage, edge1;
int min1;
int max1;
static void CANNY(int,void*)
{

	Canny(edge, edge1, min1, max1, 3);
	imshow("canny", edge1);
}
int main()
{
	img = imread("D:\\picture\\circle.jpg");
	imshow("原始圖", img);
	namedWindow("canny", 0);

	//建立與src同類型和同大小的矩陣
	DstPic.create(img.size(), img.type());
	DstPic = Scalar::all(0);
	//将原始圖轉化為灰階圖
	cvtColor(img, grayImage, COLOR_BGR2GRAY);
	//先使用3*3核心來降噪
	blur(grayImage, edge, Size(5, 5));
	//運作canny算子
	min1 = 150; 
	max1 = 230;
	//Canny(edge, edge, 70, 100, 3);
	createTrackbar("minvalue", "canny", &min1, 200, CANNY);
	createTrackbar("maxvalue", "canny", &max1, 300, CANNY);
	CANNY(min1,0);	CANNY(max1, 0);
	waitKey(0);
}
           
//霍夫變換
#include <opencv2/opencv.hpp>  
#include <opencv2/imgproc/imgproc.hpp>  

using namespace cv;
using namespace std;
int main()
{
	Mat edges;  //定義轉化的灰階圖
	namedWindow("【效果圖】", CV_WINDOW_NORMAL);
	while (1)
	{
		Mat frame;
		Mat img = imread("D:\\picture\\circle3.jpg");
		//capture >> frame;
		if (!img.data)
			return -1;
		cvtColor(img, edges, CV_BGR2GRAY);
		//高斯濾波
		GaussianBlur(edges, edges, Size(7, 7), 2, 2);
		vector<Vec3f> circles;
		//霍夫圓
		//HoughCircles(edges, circles, CV_HOUGH_GRADIENT, 1.5, 10, 200, 100, 0, 0);
		HoughCircles(edges, circles, CV_HOUGH_GRADIENT, 1, edges.rows / 5, 150, 70, 0, 0);
		for (size_t i = 0; i < circles.size(); i++)
		{
			Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
			int radius = cvRound(circles[i][2]);
			//繪制圓心  
			circle(img, center, 3, Scalar(0, 255, 0), -1, 8, 0);
			//繪制圓輪廓  
			circle(img, center, radius, Scalar(155, 50, 255), 2, 8, 0);
		}


		imshow("【效果圖】", img);


		waitKey(30);

	}

	return 0;
}
           

繼續閱讀