霍夫变换——Opencv直线检测和圆形检测

        霍夫变换是在图像处理中进行直线、圆形检测和拟合一种非常重要的手段。无论是对直线还是圆形进行检测和拟合,霍夫变换的中心思想就是将图像像素坐标转换到参数坐标。

关于直线的霍夫变换原理,下面一张图描述得非常清楚:

        通过将像素坐标空间转换到参数空间,在像素坐标空间中的每一个点在坐标空间中都会变成一条直线,而所有这些直线都会倾向于相交于同一个点,这一点的坐标所代表的参数就是拟合出的直线的参数。霍夫变换检测圆形的原理和直线的也相似,不同之处就在于,在像素坐标空间中,表示一条直线的方程需要两个参数,而表示一个圆的方程需要三个参数(圆心的横坐标、圆心的纵坐标、半径),但是往往增加一个参数所带来的 计算开销增加q非常巨大的,这也是为什么霍夫变换检测圆形在对实时性要求别叫高的工程项目中使用的非常少。

鉴于Opencv中有关于霍夫变化可以直接调用的接口,该文下面就主要介绍这些接口函数的调用方法和参数的意义,常用的接口函数包括:HoughLines()、HoughLinesP()、HoughCircles()

  • HoughLines()
void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )

参数说明:

  1. image: 8位单通道二进制图像,可以载入任意图像由函数修改成此格式
  2. lines: 输出直线矢量,每一条线有两个元素的矢量(ρ , θ)表示,ρ是离坐标原点(0, 0)也就是图像左上角的距离,θ是弧度线条旋转角度(0度表示垂直线,π/2度表示水平线)。
  3. rho: 以像素为单位的距离精度,其它表述还有是直线搜索时的进步尺寸的单位半径
  4. theta: 以弧度为单位的角度精度。其它表述还有是直线搜索时的进步尺寸的单位角度。
  5. threshold: 累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。大于阈值threshold的直线才可以被检测通过并返回到结果中。
  6. srn: 对于多尺度的霍夫变换,这是第三个参数进步尺寸rho的除数距离。粗略的累加器进步尺寸直接是第三个参数rho, 而精确的累加器进步尺寸为rho/srn。有默认值0
  7. stn:对于多尺度霍夫变换,stn表示第四个参数进步尺寸的单位角度theta的除数距离。且如果srn和stn同时为0, 就表示使用经典的霍夫变换,扶着这两个参数都应该为正数。经典霍夫变换和多尺度霍夫变换就是在这进行区分的。有默认值0

代码演示:

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;

int main(){
    Mat image;
    image=imread("5.jpg");
    Mat image_copy;
    image_copy=image.clone();
    imshow("1",image);
    cvtColor(image,image,CV_RGB2GRAY);//灰度化
    GaussianBlur(image,image,Size(9,9),2,2);//高斯模糊
    Canny(image,image,30,60);//Canny边缘检测
    vector<Vec2f> lines;
    HoughLines(image, lines, 1, CV_PI/180,230, 0, 0);//霍夫变换
    for(size_t i = 0; i < lines.size(); i++)//显示检测到的直线
    {
        float rho = lines[i][0];
        float theta = lines[i][1];
        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));
        line(image_copy, pt1, pt2, Scalar(0, 0, 255), 2);
    }
    imshow("2",image_copy);
    waitKey(0);
    return 0;
}

        在进行直线检测之前,我们先将RGB图转换成为灰度图像,然后进行高斯模糊,通过Canny边缘检测算法得到图像的二值边缘图像,将二值边缘图像作为HoughLines()函数的图像输入,输入的vector容器中包含的就是检测到的直线的信息。效果如下图所示:


  • HoughLinesP()
        我们可以发现HoughLinesP函数比HoughLines()函数多了一个P,区别就在于:在opencv中可以用HoughLines()函数来调用标准霍夫变换(SHT)和多尺度霍夫变换(MSHT),而HoughLinesP()函数用于调用累计概率霍夫变换PPHT,累计概率霍夫变换执行效率会比前者更高。
void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )  

参数说明:

  1. image: 8位单通道二进制图像,可以载入任意图像由函数修改成此格式
  2. lines: 线段输出矢量,每一条之先都由四个矢量元素(x1, y1, x2, y2)表示,其中(x1, y1)和(x2, y2)分别是检测到直线线段的两个端点。
  3. rho: 以像素为单位的直线搜寻步长
  4. theta: 以弧度为单位的直线搜寻步长
  5. threshold: 累加器阈值参数,只有大于threshold的直线结果才能被检测通过并返回到结果中
  6. minLineLength: 线段长度最小值,如果线段长度小于这个值则线段结果不显示,有默认值0
  7. maxLineGap: 允许点连接到相同直线的中间距离的最大值,如果超过这个最大值则点不会被划分到该直线。有默认值0

代码演示:

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;

int main(){
    Mat image;
    image=imread("5.jpg");
    Mat image_copy;
    image_copy=image.clone();
    imshow("1",image);
    cvtColor(image,image,CV_RGB2GRAY);
    blur(image,image,Size(5,5));
    Canny(image,image,30,60);
    vector<Vec4i> Lines;
    HoughLinesP(image, Lines, 1, CV_PI / 180,40,40,5);//霍夫直线检测
    //在图中绘出线段
    for(int i=0;i<Lines.size();i++)
        line(image_copy,Point(Lines[i][0],Lines[i][1]),Point(Lines[i][2],Lines[i][3]),Scalar(0,255,0),2);
    imshow("2",image_copy);
    waitKey(0);
    return 0;
}

        HoughLinesP()函数保存直线信息的方式和HoughLines()函数的方式也不一样,HoughLines()函数保存的信息是截距和斜率,而HoughLinesP()函数保存的是在直线上的两点(两点确定一条直线)。示例代码中设置的参数限制了线段的最小长度和艰巨最大值,可见效果如下:


  • HoughCircles()
        霍夫圆变换的基本原理和霍夫线变换类似,值时点对应的二维极径极角空间被三维的圆心点x, y还有半径r空间取代。对直线来说,一条直线能由参数极径极角(r, θ),而对圆来说,我们需要三个参数来表示一个圆,现在原图像的边缘图像的任意点对应的经过这个点的所有可能圆是在三维空间由下面这三个参数来表示了,对应一条三维空间的曲线。 
void cv::HoughCircles   (   InputArray      image,
        OutputArray     circles,
        int     method,
        double      dp,
        double      minDist,
        double      param1 = 100,
        double      param2 = 100,
        int     minRadius = 0,
        int     maxRadius = 0 
    )   

参数说明:

  1. image: 输入图像为8位单通道灰度图
  2. circles: 检测到圆的输出矢量,每个矢量都是三个浮点元素构成(x,y,radius)
  3. method: 检测方法,可以查看HoughModes查看具体细节,但目前只有HOUGH_GRADIENT(霍夫梯度)一种方法可以使用。
  4. dp: 用来检测圆心的累加器图像的分辨率与输入图像之比的倒数,且此参数允许创建一个比输入图像分辨率低的累加器。例如,如果dp=1,累加器和输入图像具有相同的分辨率,如果dp=2累加器便有输入图像一半那么大的宽度和高度。
  5. minDist: 霍夫检测到的圆的圆心之间的最小距离,即让算法能明显区分的两个不同圆之间的最小距离。这个参数如果太小的话,多个相邻的圆可能被错误的检测成了一个重合的圆。繁殖这个参数设置太大,某些圆就不能被检测出来了。也就是超过这个距离就是两个圆,否则是一个圆。
  6. param1: 指定检测方法的第一个参数,当前可用方法是HOUGH_GRADIENT,它表示传递给Canny边缘检测算子的高阈值(低阈值是高阈值的一半),有默认值100
  7. param2: 指定检测方法的第二个参数,对于HOUGH_GRADIENT方法,它表示在检测阶段圆心的累加器阈值。它越小就越可以检测到更多根本不存在的圆,而它越大的话能通过检测的圆就更加接近完美的圆形了。有默认值100
  8. minRadius: 圆的最小半径
  9. maxRadius: 圆的最大半径

代码演示:

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;

int main(){
    Mat image;
    image=imread("4.jpg");
    Mat image_copy;
    image_copy=image.clone();
    imshow("1",image);
    cvtColor(image,image,CV_RGB2GRAY);
    blur(image,image,Size(5,5));
    Canny(image,image,30,60);
    vector<Vec3f> circles;
    HoughCircles(image, circles, CV_HOUGH_GRADIENT, 1, image.rows/8, 100, 100, 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(image_copy, center, 3, Scalar(0, 255, 0), -1, 8, 0);
        circle(image_copy, center, radius, Scalar(0, 255, 0), 3, 8, 0);
    }

    imshow("2",image_copy);
    waitKey(0);
    return 0;
}

效果图:


原图

霍夫圆变换检测

总结:

        霍夫变换是在二值边缘图像的基础上进行计算的,这也就涉及到一个问题:二值边缘图像的像素数量将会直接影响霍夫变换的速度,也就是说二值边缘图像中的边缘像素点越少,则霍夫变换的计算开销越小,像素点越多则计算开销越大,而且这种开销的增加不是呈线性的。因此,在对实时性要求较高的工程中,如果使用到了霍夫变换,那么就要严格控制输入的二值边缘图像的边缘像素的数量,该文中的边缘检测使用的都是Canny算法,Canny的参数以及Hough变换函数的参数需要在效果和速度两者权衡的情况下设置得当。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值