imgproc模塊--霍夫圓變換

1.目的
(1)如何使用openCV的HoughCircles在圖像中檢測圓區域

2.原理
[1]標準霍夫變換
霍夫圓變換可以根據霍夫線變換來實現 ,通過極座標來表示圓(a,b)表示圓心,R表示半徑,則圓表示爲:
x = a + Rcosθ
y = b + Rsinθ
θ的值爲0-360
一開始我們假設R是已知的,那麼我們就可以把x,y空間的公式變換爲關於a、b空間的公式:
a = x1 – Rcosθ
b = y1 – Rsinθ
x1、y1爲x、y空間中的每一個非零像素點,如此一來,後面的變換就可以跟霍夫線變換一樣操作了。

具體操作步驟爲:
①、讀取一副圖像
②、檢測邊緣,產生一副二值圖像
③、對於每個非零像素轉換到ab空間中
④、對於每個ab空間中的點,進行累加
⑤、提取數量最多的點作爲圓點
PS:在上面提到R是假設已知的,那麼當R是未知的情況怎麼辦?其實很簡單,就是將R設置爲1、2、3……這樣子已知下去,慢慢試,再對R和圓心數量進行閾值就可以了。

[2]霍夫梯度法
霍夫梯度法的原理是這樣的。
【1】 首先對圖像應用邊緣檢測,比如用canny邊緣檢測。
【2】 然後,對邊緣圖像中的每一個非零點,考慮其局部梯度,即用Sobel()函數計算x和y方向的Sobel一階導數得到梯度。
【3】 利用得到的梯度,由斜率指定的直線上的每一個點都在累加器中被累加,這裏的斜率是從一個指定的最小值到指定的最大值的距離。
【4】 同時,標記邊緣圖像中每一個非0像素的位置。
【5】 然後從二維累加器中這些點中選擇候選的中心,這些中心都大於給定閾值並且大於其所有近鄰。這些候選的中心按照累加值降序排列,以便於最支持像素的中心首先出現。
【6】 接下來對每一箇中心,考慮所有的非0像素。
【7】 這些像素按照其與中心的距離排序。從到最大半徑的最小距離算起,選擇非0像素最支持的一條半徑。
【8】如果一箇中心收到邊緣圖像非0像素最充分的支持,並且到前期被選擇的中心有足夠的距離,那麼它就會被保留下來。
這個實現可以使算法執行起來更高效,或許更加重要的是,能夠幫助解決三維累加器中 會產生許多噪聲並且使得結果不穩定的 稀疏分佈問題。

霍夫梯度法的缺點:
<1> 在霍夫梯度法中,我們使用Sobel導數來計算局部梯度,那麼隨之而來的假設是,其可以視作等同於一條局部切線,並這個不是一個數值穩定的做法。在大多數情況下,這樣做會得到正確的結果,但或許會在輸出中產生一些噪聲。

<2> 在邊緣圖像中的整個非0像素集被看做每個中心的候選部分。因此,如果把累加器的閾值設置偏低,算法將要消耗比較長的時間。

<3> 因爲中心是按照其關聯的累加器值的升序排列的,並且如果新的中心過於接近之前 已經接受的中心的話,就不會被保留下來。且當有許多同心圓或者是近似的同心圓時,霍夫梯度法的傾向是保留最大的一個圓。可以說這是一種比較極端的做法,因 爲在這裏默認Sobel導數會產生噪聲,若是對於無窮分辨率的平滑圖像而言的話,這纔是必須的。 (實際上可以設置min_dist來解決該問題)

PS:openCV中實現的是霍夫梯度法

3.部分代碼解釋
(1)HoughCircles

    /*
    HoughCircles參數解釋
    edge:通過邊緣檢測獲得的二值圖像,一般可以使用canny邊緣檢測算子
    circles:檢測到的圓的參數(圓心Point([0],[1]),半徑radius([2]))
    CV_HOUGH_GRADIENT:霍夫梯度法
    dp = 1:累加器圖像的反比分辨率
    min_dist = src_gray.rows/8: 檢測到圓心之間的最小距離
    param_1 = 200: Canny邊緣函數的高閾值
    param_2 = thresh:圓心檢測閾值
    min_radius:最小能夠檢測的圓大小
    max_radius:最大能夠檢測的圓大小
    ps:min_radius, max_radius默認值爲0,當max_radius設置爲0時候,可以檢測任意大小的圓
    */
    HoughCircles(edge, circles, CV_HOUGH_GRADIENT, 1, edge.rows/8, 200, thresh, 0, edge.rows/2);

4.完整代碼
(1)CommonInclude.h

#ifndef COMMON_INCLUDE
#define COMMON_INCLUDE
#include<iostream>
#include<vector>
using namespace std;
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace cv;
#endif

(2)HoughCircles.cpp

#include"CommonInclude.h"

int thresh = 50;
int max_thresh = 200;
Mat src, gray, edge, dst;

char windowName[] = "HoughCircles";

void HoughCirclesTrans(int, void*){
    vector<Vec3f> circles;
    /*
    HoughCircles參數解釋
    edge:通過邊緣檢測獲得的二值圖像,一般可以使用canny邊緣檢測算子
    circles:檢測到的圓的參數(圓心Point([0],[1]),半徑radius([2]))
    CV_HOUGH_GRADIENT:霍夫梯度法
    dp = 1:累加器圖像的反比分辨率
    min_dist = src_gray.rows/8: 檢測到圓心之間的最小距離
    param_1 = 200: Canny邊緣函數的高閾值
    param_2 = thresh:圓心檢測閾值
    min_radius:最小能夠檢測的圓大小
    max_radius:最大能夠檢測的圓大小
    ps:min_radius, max_radius默認值爲0,當max_radius設置爲0時候,可以檢測任意大小的圓
    */
    HoughCircles(edge, circles, CV_HOUGH_GRADIENT, 1, edge.rows/8, 200, thresh, 0, edge.rows/2);
    Point center;
    float radius;
    cvtColor(edge, dst, CV_GRAY2BGR);
    for(int i=0; i<circles.size(); i++){
        center = Point(circles[i][0], circles[i][1]);
        radius = circles[i][2];
        circle(dst, center, 2, Scalar(255,0,0), 2, 8);
        circle(dst, center, radius, Scalar(0,0,255), 2, 8);
    }
    imshow(windowName, dst);
}

int main(int argc, char** argv){
    if(argc<2){
        cout << "more parameters are required!!!" << endl;
        return(-1);
    }
    src = imread(argv[1]);
    if(!src.data){
        cout << "error to read image!!!" << endl;
        return(-1);
    }
    namedWindow(windowName, CV_WINDOW_AUTOSIZE);
    //高斯平滑,去除噪聲
    GaussianBlur(src, src, Size(5,5), 0, 0);
    //轉換成灰度圖像
    cvtColor(src, gray, CV_BGR2GRAY);
    imshow("gray", gray);
    //Canny邊緣檢測
    Canny(gray, edge, 50, 200, 3);
    createTrackbar("Thresh", windowName,
            &thresh, max_thresh,
            HoughCirclesTrans);
    HoughCirclesTrans(0,0);
    waitKey(0);
    return(0);
}

參考文獻
1.http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/imgtrans/hough_circle/hough_circle.html#hough-circle

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