OpenCV标准霍夫直线检测详解

Posted OpenCV学堂

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV标准霍夫直线检测详解相关的知识,希望对你有一定的参考价值。

霍夫直线检测

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

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

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

OpenCV标准霍夫直线检测详解

OpenCV标准霍夫直线检测详解

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

OpenCV标准霍夫直线检测详解

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

OpenCV标准霍夫直线检测详解

就说明这些点具有相同的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, 0255, 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(00255), 1, LINE_AA);
}
imshow("contours", src);

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

OpenCV标准霍夫直线检测详解

使用霍夫直线变换

假设有如下图像:

OpenCV标准霍夫直线检测详解

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

  • 左侧倾斜直线

  • 右侧倾斜直线

  • 水平或者垂直线

  • 长度最大直线

代码实现如下

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

    // 绘制直线
    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(02550), 1, LINE_AA);
            putText(image, "max-line", Point((pt1.x + pt2.x) / 2, (pt1.y + pt2.y) / 2), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(00255), 28);
            continue;
        }
        if (rho > 0) { // 右倾
            line(image, pt1, pt2, Scalar(00255), 1, LINE_AA);
            if (angle == 90) { // 水平线
                line(image, pt1, pt2, Scalar(2552550), 1, LINE_AA);
            }
            if (angle <= 1) { // 垂直线
                line(image, pt1, pt2, Scalar(0255255), 1, LINE_AA);
            }
        }
        else { // 左倾
            line(image, pt1, pt2, Scalar(25500), 1, LINE_AA);
        }

    }
    imshow("result", image);
}

运行结果如下:

OpenCV标准霍夫直线检测详解

欢迎扫码加入【OpenCV研习社】

- 学习OpenCV+tensorflow开发技术
- 与更多伙伴相互交流、一起学习进步
- 每周一到每周五分享知识点学习(音频+文字+源码)
- 系统化学习知识点,从易到难、由浅入深
- 直接向老师提问、每天答疑辅导


推荐阅读












关注【OpenCV学堂】

长按或者扫码即可关注


以上是关于OpenCV标准霍夫直线检测详解的主要内容,如果未能解决你的问题,请参考以下文章

pyhton—opencv直线检测(HoughLines)找到最长的一条线

利用OpenCV的霍夫变换线检测函数HoughLines()得到直线的ρ和θ值后绘制直线的原理详解

opencv学习笔记霍夫变换——直线检测

opencv学习-霍夫变换-直线检测

OpenCV ⚠️高手勿入! 半小时学会基本操作 13⚠️ 直线检测

OpenCV 霍夫线检测练习直线绘制代码调整