一、簡介
在圖像處理和計算機視覺領域中,如何從當前的圖像中提取所需要的特征信息是圖像識別的關鍵所在。在許多應用場合中需要快速准確地檢測出直線或者圓。其中一種非常有效的解決問題的方法是霍夫(Hough)變換。
二、霍夫線變換
霍夫線變換的原理
以上原理部分,轉自http://blog.csdn.net/poem_qianmo/article/details/26977557
1 #include "opencv2/opencv.hpp" 2 using namespace cv; 3 4 void main() 5 { 6 Mat src=imread("E://1.png"); 7 Mat dst; 8 imshow("src", src); 9 10 Canny(src, src, 50, 200); 11 imshow("Canny", src); 12 //cvtColor(src, dst, CV_BGR2GRAY);//彩色圖轉灰度圖:該語句不能放在canny邊緣檢測后(canny檢測后,圖像變為了灰度圖) 13 cvtColor(src, dst, CV_GRAY2BGR);//灰度圖轉彩色圖 14 15 //進行霍夫線變換 16 vector<Vec2f> lines; //定義矢量結構lines用於存放得到的線段矢量集合 17 HoughLines(src, lines, 1, CV_PI/180, 150);//超過150的線段才被檢測到 18 19 //依次在圖中繪制出每條線段 20 for (size_t i = 0; i < lines.size(); i++) 21 { 22 float rho=lines[i][0],theta=lines[i][1]; 23 Point pt1,pt2; 24 double a=cos(theta),b=sin(theta); 25 double x0=a*rho,y0=b*rho; 26 pt1.x=cvRound(x0+1000*(-b));//cvRound(double value) 函數:對一個double型數字四舍五入,返回一個整數 27 pt1.y=cvRound(y0+1000*(a)); 28 pt2.x = cvRound(x0 - 1000*(-b)); 29 pt2.y = cvRound(y0 - 1000*(a)); 30 line(dst,pt1,pt2,Scalar(55,100,195),2,8); 31 } 32 33 imshow("dst", dst); 34 waitKey(0); 35 destroyAllWindows(); 36 }
三、累計概率霍夫變換
1 #include "opencv2/opencv.hpp" 2 using namespace cv; 3 4 void main() 5 { 6 //HoughLinesP()用法 7 Mat src = imread("E://1.png"); 8 Mat dstImg = src.clone(); 9 imshow("src", src); 10 11 cvtColor(src, src, CV_BGR2GRAY); 12 Canny(src,src, 50, 200); 13 vector<Vec4i> lines; //定義矢量結構lines用於存放得到的線段矢量集合 14 HoughLinesP(src, lines, 1, CV_PI/180, 150, 50, 10); 15 //依次在圖中繪制出每條線段 16 for(size_t i = 0; i<lines.size(); i++) 17 { 18 Vec4i p = lines[i]; 19 line(dstImg, Point(p[0], p[1]), Point(p[2], p[3]), Scalar(0, 255, 0), 2, 8);20 } 21 imshow("dst", dstImg); 22 waitKey(0); 23 destroyAllWindows(); 24 }
四、霍夫圓變換
霍夫圓變換的基本原理和上面講的霍夫線變化大體上是很類似的,只是點對應的二維極徑極角空間被三維的圓心點x, y還有半徑r空間取代。說“大體上類似”的原因是,如果完全用相同的方法的話,累加平面會被三維的累加容器所代替:在這三維中,一維是x,一維是y,另外一維是圓的半徑r。這就意味着需要大量的內存而且執行效率會很低,速度會很慢。
對直線來說, 一條直線能由參數極徑極角表示. 而對圓來說, 我們需要三個參數來表示一個圓, 也就是:
這里的(Xcenter,Ycenter)表示圓心的位置 (下圖中的綠點) ,而 r 表示半徑, 這樣我們就能唯一的定義一個圓了, 見下圖:
在OpenCV中,我們一般通過一個叫做“霍夫梯度法”的方法來解決圓變換的問題。
- 第一個參數,InputArray類型的image,輸入圖像,即源圖像,需為8位的灰度單通道圖像。
- 第二個參數,InputArray類型的circles,經過調用HoughCircles函數后此參數存儲了檢測到的圓的輸出矢量,每個矢量由包含了3個元素的浮點矢量(x, y, radius)表示。
- 第三個參數,int類型的method,即使用的檢測方法,目前OpenCV中就霍夫梯度法一種可以使用,它的標識符為CV_HOUGH_GRADIENT,在此參數處填這個標識符即可。
- 第四個參數,double類型的dp,用來檢測圓心的累加器圖像的分辨率於輸入圖像之比的倒數,且此參數允許創建一個比輸入圖像分辨率低的累加器。上述文字不好理解的話,來看例子吧。例如,如果dp= 1時,累加器和輸入圖像具有相同的分辨率。如果dp=2,累加器便有輸入圖像一半那么大的寬度和高度。
- 第五個參數,double類型的minDist,為霍夫變換檢測到的圓的圓心之間的最小距離,即讓我們的算法能明顯區分的兩個不同圓之間的最小距離。這個參數如果太小的話,多個相鄰的圓可能被錯誤地檢測成了一個重合的圓。反之,這個參數設置太大的話,某些圓就不能被檢測出來了。
- 第六個參數,double類型的param1,有默認值100。它是第三個參數method設置的檢測方法的對應的參數。對當前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示傳遞給canny邊緣檢測算子的高閾值,而低閾值為高閾值的一半。
- 第七個參數,double類型的param2,也有默認值100。它是第三個參數method設置的檢測方法的對應的參數。對當前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示在檢測階段圓心的累加器閾值。它越小的話,就可以檢測到更多根本不存在的圓,而它越大的話,能通過檢測的圓就更加接近完美的圓形了。
- 第八個參數,int類型的minRadius,有默認值0,表示圓半徑的最小值。
- 第九個參數,int類型的maxRadius,也有默認值0,表示圓半徑的最大值。
需要注意的是,使用此函數可以很容易地檢測出圓的圓心,但是它可能找不到合適的圓半徑。我們可以通過第八個參數minRadius和第九個參數maxRadius指定最小和最大的圓半徑,來輔助圓檢測的效果。或者,我們可以直接忽略返回半徑,因為它們都有着默認值0,單單用HoughCircles函數檢測出來的圓心,然后用額外的一些步驟來進一步確定半徑。
1 #include "opencv2/opencv.hpp" 2 using namespace cv; 3 4 void main() 5 { 6 Mat src = imread("E://C.jpg"); 7 Mat dst = src.clone(); 8 imshow("src", src); 9 10 cvtColor(src, src, CV_BGR2GRAY); 11 GaussianBlur(src,src,Size(9,9),2); 12 13 vector<Vec3f> circles; 14 HoughCircles(src, circles, CV_HOUGH_GRADIENT,1.5, 10, 200, 100); 15 for(size_t i = 0; i<circles.size(); i++) 16 { 17 Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); 18 int radius = cvRound(circles[i][2]); 19 circle(dst, center, 3, Scalar(0, 0, 255), -1, 8,0);//設置為-1時,畫實心圓 20 circle(dst, center, radius, Scalar(0, 255, 0), 3, 8,0);//畫空心圓 21 } 22 imshow("dst", dst); 23 waitKey(0); 24 }
五、其他