目的:
瞭解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函數)。
- 實驗原理:在笛卡爾座標下,圓的表示方程爲:(x-a)²+(y-b)²=r²;但在極座標下,假設已知圓心(x0,y0),那麼圓上的點可以表示爲:
對於任意一個圓, 假設中心像素點p(x0, y0)像素點已知, 圓半徑已知,則旋轉360度,由極座標方程可以得到每個點上的座標。同樣,如果只是知道圖像上像素點, 圓半徑,旋轉360°,則會有一個集中的交點,即圓心,也就是說圓點處的座標值最強,這正是霍夫變換檢測圓的數學原理。
3.經典方法:
3.1對直線來說, 一條直線能由參數極徑極角 (γ, θ) 表示. 而對圓來說, 需要三個參數來表示一個圓。現在原圖像的邊緣圖像的任意點對應的經過這個點的所有可能圓是在三維空間有下面這三個參數來表示了,其對應一條三維空間的曲線. 那麼與二維的霍夫線變換同樣的道理,對於多個邊緣點越多這些點對應的三維空間曲線交於一點那麼他們經過的共同圓上的點就越多。
類似的我們也就可以用同樣的閾值的方法來判斷一個圓是否被檢測到, 這就是標準霍夫圓變換的原理, 但也正是在三維空間的計算量大大增加的原因, 標準霍夫圓變化很難被應用到實際中。
3.2 opencv中實現的圓檢測算法,一般算法爲取參考點,對於邊緣像素點計算梯度角,對每一個梯度角,存儲對應於參考點的距離和角度;算法具有較好的抗干擾性,但也需要較大的存儲空間和計算量,如下是opencv圓檢測算法的思路:
4.霍夫變換,兩個階段:
(1)檢測圓心:圓心是它所在圓周所有法線的交匯處,因此只要找到這個交點,即可確定圓心:
對輸入圖像邊緣檢測;
計算圖形的梯度,並確定圓周線,其中圓周的梯度就是它的法線;
在二維霍夫空間內繪出所有圖形的梯度直線,座標點累加和的值越大,則該點上直線相交的次數越多,該點越有可能是圓心;
在霍夫空間的4鄰域內進行非最大值抑制;
設定一個閾值,霍夫空間內累加和大於該閾值的點就對應於圓心。
(2)測圓半徑的方法是從圓心到圓周上的任意一點的距離相同,首先確定一個閾值,只要計算得到相同距離的數量大於該閾值,就認爲該距離就是該圓心所對應的圓半徑:
計算某一個圓心到所有圓周線的距離,這些距離中就有該圓心所對應的圓的半徑的值,這些半徑值當然是相等的,並且這些圓半徑的數量要遠遠大於其他距離值相等的數量;
設定兩個閾值:最大半徑和最小半徑。保留距離在這兩個半徑之間的值,這意味着我們檢測的圓不能太大,也不能太小;
對保留下來的距離進行排序;
找到距離相同的那些值,並計算相同值的數量;
設定一個閾值,只有相同值的數量大於該閾值,就認爲該值是該圓心對應的圓半徑;
對每一個圓心,完成上面的步驟,得到所有的圓半徑。
- 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;
}