OpenCV标准霍夫直线检测详解

点击上方“小白学视觉”,选择加"星标"或“置顶”
重磅干货,第一时间送达
本文转载自:OpenCV学堂
 
 

霍夫直线检测

对于图像来说可以从笛卡尔坐标系统转换到霍夫空间,对于一条直线来说

  • 在笛卡尔坐标系统中表示一条直线有两个参数斜率k与截距b

  • 在霍夫空间中表示一条直线也有两个参数到原点的距离d与角度theta

b035bc78743c1fac2cfe42c8da44304f.png

3af1788c6facc9e7a61f0bc382a179b1.png

对于给定任意theta值,都有一个r与之对应,对于点x0=8, y0=6,在霍夫空间有如下的曲线:

4a363c1e96e84f0907692cc6feeb1c05.png

当有很多点在霍夫空间的曲线相交于一点时候

178a57cbdc943e896652bbe739fe41ca.png

就说明这些点具有相同的theta与r,即它们都属于同一条直线,而参数theta与r就是该直线在霍夫空间的直线参数方程。

OpenCV中标准霍夫直线检测源码部分:

// stage 1. fill accumulator
for( i = 0; i < height; i++ )
    for( j = 0; j < width; j++ )
    {
        if( image[i * step + j] != 0 )
            for(int n = 0; n < numangle; n++ )
            {
                int r = cvRound( j * tabCos[n] + i * tabSin[n] );
                r += (numrho - 1) / 2;
                accum[(n+1) * (numrho+2) + r+1]++;
            }
    }

// stage 2. find local maximums
findLocalMaximums( numrho, numangle, threshold, accum, _sort_buf );

// stage 3. sort the detected lines by accumulator value
std::sort(_sort_buf.begin(), _sort_buf.end(), hough_cmp_gt(accum));

// stage 4. store the first min(total,linesMax) lines to the output buffer
linesMax = std::min(linesMax, (int)_sort_buf.size());
double scale = 1./(numrho+2);

lines.create(linesMax, 1, type);
Mat _lines = lines.getMat();
for( i = 0; i < linesMax; i++ )
{
    LinePolar line;
    int idx = _sort_buf[i];
    int n = cvFloor(idx*scale) - 1;
    int r = idx - (n+1)*(numrho+2) - 1;
    line.rho = (r - (numrho - 1)*0.5f) * rho;
    line.angle = static_cast<float>(min_theta) + n * theta;
    if (type == CV_32FC2)
    {
        _lines.at<Vec2f>(i) = Vec2f(line.rho, line.angle);
    }
    else
    {
        CV_DbgAssert(type == CV_32FC3);
        _lines.at<Vec3f>(i) = Vec3f(line.rho, line.angle, (float)accum[idx]);
    }
}

相关API使用

OpenCV中关于霍夫直线检测有两个API,一个被称为标准霍夫变换直线检测,另外一个叫霍夫直线检测。二者之间的区别在于,前者会直接输出theta与r,还有累加和,后者会直接输出相关线段的平面坐标。

void cv::HoughLines(
    InputArray image, // 输入参数
    OutputArray lines, // 输出结果vector<Vec2f>, vector<Vec3f>
    double rho, // 距离步长d=1, 是指该直线到原点距离,对于屏幕坐标是左上角点
    double theta, // 角度步长1°
    int threshold, // 阈值,是指累加数目
    double srn = 0, // 多尺度检测需要,默认为0
    double stn = 0, // 多尺度检测需要,默认为0
    double min_theta = 0, // 直线旋转角度
    double max_theta = CV_PI // 直线旋转角度
)

对输出的结果是Vec2f的话为(r, theta)如果是Vec3f的话为(r, theta, votes)

当r值大于零的时候表示直线在X轴下方有垂直距离
当r值小于零的时候表示直线在X轴上方有垂直距离

首先对输入图像进行二值化

// 二值化
Mat dst, gray, binary;
cvtColor(src, gray, COLOR_BGR2GRAY);
threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
imshow("binary", binary);

标准霍夫直线检测代码如下

// 绘制直线
Point pt1, pt2;
for (size_t i = 0; i < lines.size(); i++)
{
    float rho = lines[i][0]; // 距离
    float theta = lines[i][1]; // 角度
    float votes = lines[i][2]; // 累加
    printf("rho %.2f, theta : %.2f, votes : %.2f \n", rho, theta, votes);
    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(src, pt1, pt2, Scalar(0, 0, 255), 1, LINE_AA);
}
imshow("contours", src);

对上述的代码解释,其中x0与y0是直线上的点,1000是表示对改点延长到直线上距离,上述代码计算公式很多人不理解,特手绘白纸一张如下:

58740c223a903c7119b15633b484ee14.png

使用霍夫直线变换

假设有如下图像:

78e6c14d78330b17b38e67d799f95596.png

通过标准霍夫直线变换实现如下直线分类与最大直线提取

  • 左侧倾斜直线

  • 右侧倾斜直线

  • 水平或者垂直线

  • 长度最大直线

代码实现如下

void hough_lines_demo(Mat &image, Mat &binary) {
    // 标准霍夫直线检测
    vector<Vec3f> lines;
    HoughLines(binary, lines, 1, CV_PI / 180, 100, 0, 0);

    // 绘制直线
    Point pt1, pt2;
    for (size_t i = 0; i < lines.size(); i++)
    {
        float rho = lines[i][0]; // 距离
        float theta = lines[i][1]; // 角度
        float votes = lines[i][2]; // 累加
        printf("rho %.2f, theta : %.2f, votes : %.2f \n", rho, theta, votes);
        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));
        int angle = round((theta / CV_PI) * 180);
        printf("angle : %d\n", angle);
        if (i == 0) {
            line(image, pt1, pt2, Scalar(0, 255, 0), 1, LINE_AA);
            putText(image, "max-line", Point((pt1.x + pt2.x) / 2, (pt1.y + pt2.y) / 2), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255), 2, 8);
            continue;
        }
        if (rho > 0) { // 右倾
            line(image, pt1, pt2, Scalar(0, 0, 255), 1, LINE_AA);
            if (angle == 90) { // 水平线
                line(image, pt1, pt2, Scalar(255, 255, 0), 1, LINE_AA);
            }
            if (angle <= 1) { // 垂直线
                line(image, pt1, pt2, Scalar(0, 255, 255), 1, LINE_AA);
            }
        }
        else { // 左倾
            line(image, pt1, pt2, Scalar(255, 0, 0), 1, LINE_AA);
        }

    }
    imshow("result", image);
}

运行结果如下:

3524f367c5f2ece2624119f20e7f67b7.png

下载1:OpenCV-Contrib扩展模块中文版教程

在「小白学视觉」公众号后台回复:扩展模块中文教程,即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。


下载2:Python视觉实战项目52讲
在「小白学视觉」公众号后台回复:Python视觉实战项目,即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。


下载3:OpenCV实战项目20讲
在「小白学视觉」公众号后台回复:OpenCV实战项目20讲,即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。


交流群

欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器、自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~
  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值