霍夫直线变换和霍夫圆变换的原理和实现

一、霍夫直线的原理

(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;
}

猜你喜欢

转载自blog.csdn.net/u013263891/article/details/82867251