1. 程式人生 > >霍夫直線變換和霍夫圓變換的原理和實現

霍夫直線變換和霍夫圓變換的原理和實現

一、霍夫直線的原理

(1)本部分大部分學習來自《OpenCV3程式設計入門》,另外有一些自己的理解

如上圖所以,將一條直線由截距是表示為在極座標系下

化簡為

(2)對於一個點(x0,y0)來說,可以通過這個點的一族直線統一定義為

每一對  代表一條通過點(x0,y0)的直線。

(3)如果對於一個給定點(x0,y0),我們在極座標對極徑極角平面繪製出所有通過它的直線,將會得到一條正弦曲線。例如x0=8,y0=6的曲線如下所示:

上圖是r>0 theta(0,2*PI)

(4)對影象中所有的點進行上述操作,如果兩個點在一條直線上,那麼兩條正弦曲線將會交於一點

上圖所有的三個點在一條直線上

(5)一條直線能夠通過在平面theta-r尋找交於一點的曲線數量來檢測, 越多的 曲線交於一點就意味著這個交點表示的直線由更多的點組成,通過設定交於一點的曲線數的閾值來決定是否檢測到一條直線。

(6)霍夫變換即追蹤影象中每個點對應曲線間的交點,如果 交點數量超過了閾值,那麼可以認為這個交點所代表的引數為原影象中的一條直線。

二、霍夫直線的程式碼

#include<opencv2/opencv.hpp>
#include<iostream>

int main(){
    cv::Mat src=cv::imread("../2.jpg");
    cv::Mat dst,mid;
    cv::Canny(src,mid,50,200,3);
    cv::cvtColor(mid,dst,cv::COLOR_GRAY2BGR);

    std::vector<cv::Vec2f>lines;
    cv::HoughLines(mid,lines,1,CV_PI/180,100,0,0);

    for(size_t t=0;t<lines.size();t++){
        float rho=lines[t][0],theta=lines[t][1];
        cv::Point pt1,pt2;
        double a=cos(theta),b=sin(theta);
        double x0=a*rho,y0=b*rho;
        pt1.x=cvRound(x0+1000*(-b));
        pt1.y=cvRound(y0+1000*a);
        pt2.x=cvRound(x0-1000*(-b));
        pt2.y=cvRound(y0-1000*(a));

        cv::line(dst,pt1,pt2,cv::Scalar(55,10,195),1,cv::LINE_AA);
    };
    cv::imshow("src",src);

    cv::imshow("mid",mid);

    cv::imshow("dst",dst);

    cv::waitKey(0);
    return 0;
}

三、霍夫圓的原理

     霍夫變換檢測圓形的原理跟檢測直線的原理是一樣的。圓的表示式為  (x-a)2+(y-b)2=r2 , 把問題轉換成在求解經過畫素點最多的 (a,b,r) 引數對。這裡會發現(a,b,r)的引數空間特別大,計算量特別大。我們一般使用霍夫梯度法來解決圓的變換。

如上圖所示,如果我們對一個圓求梯度,那麼圓上所有的點的梯度的方向均朝向圓心

基於此有如下原理:

(1)首先對影象應用邊緣檢測,比如用canny邊緣檢測

(2)使用sobel運算元計算所有畫素的梯度

(3)遍歷canny之後的所有非0的畫素點,沿著梯度方向畫線,每個點有是一個累加器,有一個線經過該點,累加器加1,對所有累加器進行排序,根據閾值找到所有可能的圓心

(4)計算canny影象中所有的非0畫素點距離圓心的距離,距離從小到大排序,選取合適的半徑

(5)對選取的半徑設定累加器,對於滿足半徑r的累加器+1

(6)統計所有可能的半徑

四、霍夫圓的程式碼

#include<iostream>
#include<opencv2/opencv.hpp>

int main(){
    cv::Mat src=cv::imread("../3.png");
    cv::Mat gray;
    cv::namedWindow("src",cv::WINDOW_AUTOSIZE);
    cv::imshow("src",src);

    cv::cvtColor(src,gray,cv::COLOR_BGR2GRAY);
    cv::namedWindow("gray",cv::WINDOW_AUTOSIZE);
    cv::imshow("gray",gray);

    std::vector<cv::Vec3f>circles;
    cv::HoughCircles(gray,circles,cv::HOUGH_GRADIENT,2,10,200,100,85,0);

    for(size_t t=0;t<circles.size();t++){
        cv::Point center(circles[t][0],circles[t][1]);
        int r=circles[t][2];

        cv::circle(src,center,2,cv::Scalar(0,0,255),-1,8);
        cv::circle(src,center,r,cv::Scalar(0,255,0),3,8,0);
    }

    cv::namedWindow("效果",cv::WINDOW_AUTOSIZE);
    cv::imshow("效果",src);

    cv::waitKey(0);
    return 0;
}