OpenCV学习 day10 霍夫变换

霍夫变换-直线检测  Hough Line Transform

 

 

 对图像上每一个像素点x,y,变换到霍夫空间,根据不同的角度θ可以绘制出一条曲线,不同位置的x,y可以绘制出多条曲线,通过这些曲线的交点所对应的r和θ可以还原出直线的位置。

 

对于任意一条直线上的所有点来说
变换到极坐标中,从[0~360]空间,可以得到r的大小
属于同一条直线上点在极坐标空(r, θ)必然在一个点上有最强的信号出现,根据此反算到平面坐标中就可以得到直线上各点的像素坐标。从而得到直线

  θ是直线的法向方向与 x 轴的夹角,r是原点到直线的距离

 

 API

 

标准的霍夫变换 cv::HoughLines从平面坐标转换到霍夫空间,最终输出是(θ, rθ)   表示极坐标空间,不建议使用

cv::HoughLines(
InputArray src, // 输入图像,必须8-bit的灰度图像
OutputArray lines, // 输出的极坐标来表示直线
double rho, // 生成极坐标时候的像素扫描步长 一般取1
double theta, //生成极坐标时候的角度步长,一般取值CV_PI/180
int threshold, // 阈值,只有获得足够多交点的极坐标点才被看成是直线
double srn=0;// 是否应用多尺度的霍夫变换,如果不是设置0表示经典霍夫变换
double min_theta=0; // 表示角度扫描范围 0 ~180之间, 默认即可
double max_theta=CV_PI
) 

 

霍夫变换直线概率 cv::HoughLinesP最终输出是直线的两个点(x0, y0, x1, y1)

cv::HoughLinesP(
InputArray src, // 输入图像,必须8-bit的灰度图像
OutputArray lines, // 输出的极坐标来表示直线
double rho, // 生成极坐标时候的像素扫描步长
double theta, //生成极坐标时候的角度步长,一般取值CV_PI/180
int threshold, // 阈值,只有获得足够多交点的极坐标点才被看成是直线
double minLineLength=0;// 最小直线长度
double maxLineGap=0;// 最大间隔
)

 

代码演示:

int Hough_Line(Mat src) {

    Mat edge_src, dst;
    
    Canny(src, edge_src, 150, 200);
    cvtColor(edge_src, dst, COLOR_GRAY2BGR);
    imshow("canny result", dst);
    
    vector<Vec4f> plines;  //定义一个直线
    HoughLinesP(edge_src, plines, 1, CV_PI / 180.0, 10, 0, 10);  //plines中存储多条直线

    //遍历每条直线
    for (size_t i = 0; i < plines.size(); i++) {
        Vec4f hline = plines[i]; 
        //绘制 (x0,y0,x1,y1)
        line(dst, Point(hline[0], hline[1]), Point(hline[2], hline[3]), Scalar(0, 0, 255), 3, LINE_AA);
    }
    imshow("final result", dst);

    waitKey(0);
    return 0;
}

 

 

 

霍夫变换-圆检测

其实检测圆形和检测直线的原理差别不大,只不过直线是在二维空间,因为y=kx+b,只有k和b两个自由度。而圆形的一般性方程表示为(x-a)²+(y-b)²=r²。那么就有三个自由度圆心坐标a,b,和半径r。这就意味着需要更多的计算量,而OpenCV中提供的cvHoughCircle()函数里面可以设定半径r的取值范围,相当于有一个先验设定,在每一个r来说,在二维空间内寻找a和b就可以了,能够减少计算量。

 使用(a,b,r)来确定一个圆心为(a,b)半径为 r 的圆。

经过一个点可以作出无数个圆,假设某个点平面坐标为(xi, yi),使用的参数为(ai, bi, ri)则经过此点的圆的表达式为(xi - ai)2 + (yi - bi)= ri2

对于点(xj, yj),必定存在(aj, bj, rj)使得 近似计算中 ai = aj, bi = bj, ri = rj,即两个点在同一个圆上;

同理如果三个点在同一个圆上,则也必须存在 ai = aj = ak = a, bi=bj=bk = b, ri=rj=rk = r 的情况。此时的这个ai, bi, ri就是我们要找的圆的坐标

 

 

参考:https://blog.csdn.net/shenziheng1/article/details/75307410

 

因为霍夫圆检测对噪声比较敏感,所以首先要对图像做中值滤波
基于效率考虑,Opencv中实现的霍夫变换圆检测是基于图像梯度的实现,分为两步:
1. 检测边缘,发现可能的圆心
2. 基于第一步的基础上从候选圆心开始计算最佳半径大小

 

API

HoughCircles(
InputArray image, // 输入图像 ,必须是8位的单通道灰度图像
OutputArray circles, // 输出结果,发现的圆信息
int method, // 方法 - HOUGH_GRADIENT
double dp, // dp = 1 尺度 dip
double mindist, // 10 最短距离-可以分辨是两个圆的,否则认为是同心圆- src.rows/8 越小同心圆越多
double param1, // canny edge detection low threshold
double param2, // 中心点累加器阈值 – 候选圆心 过多少个值确定是圆心
int minradius, // 最小半径
int maxradius//最大半径 
)

 

演示代码

int Hough_Circle(Mat src) {

    Mat mid_src, dst;

    //中值滤波
    medianBlur(src, mid_src, 5);
    cvtColor(mid_src, mid_src, COLOR_BGR2GRAY);

    //霍夫圆检测
    vector<Vec3f> pcircles;
    HoughCircles(mid_src, pcircles, HOUGH_GRADIENT, 1, 10, 100.0, 30, 3, 50);
    src.copyTo(dst);
    for (size_t i = 0; i < pcircles.size(); i++) {
        Vec3f circl = pcircles[i]; //(a, b, r)
        circle(dst, Point(circl[0], circl[1]), circl[2], Scalar(0, 0, 255), 2, LINE_AA);
        circle(dst, Point(circl[0], circl[1]), 2, Scalar(0, 255, 0), 2, LINE_AA);  //标注圆心

    }
    imshow("final result", dst);

    waitKey(0);
    return 0;
}

 

 

 

posted @ 2020-05-27 11:27  xyfun72  阅读(181)  评论(0编辑  收藏  举报