OpenCV_霍夫变换_直线检测_HougLines

小灰灰 2022-09-29 14:59 525阅读 0赞

目录

  1. 原理介绍

  2. OpenCV中的 C++ 函数定义

2.1 标准的霍夫线变换 cv::HoughLines

2.2 概率统计霍夫线变换 cv::HoughLinesP

  1. 示例1

  2. 示例2(官方)


1. 原理介绍

霍夫变换在检测各种形状的的技术中非常流行,如果你要检测的形状可以用数学表达式写出,你就可以是使用霍夫变换检测它。及时要检测的形状存在一点破坏或者扭曲也可以使用。我们下面就看看如何使用霍夫变换检测直线。

首先将一条直线用一个点表示,这样用一个点表示直线上所有的点,一开始人们使用斜截式y=kx+q中的(k,q)来表示一条直线。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NoeWpoeXAxMQ_size_16_color_FFFFFF_t_70

变换后的空间成为霍夫空间。即:笛卡尔坐标系中一条直线,对应霍夫空间的一个点。反过来同样成立(霍夫空间的一条直线,对应笛卡尔坐标系的一个点):

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NoeWpoeXAxMQ_size_16_color_FFFFFF_t_70 1

再来看看A、B两个点,对应霍夫空间的情形:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NoeWpoeXAxMQ_size_16_color_FFFFFF_t_70 2

一步步来,再看一下三个点共线的情况:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NoeWpoeXAxMQ_size_16_color_FFFFFF_t_70 3

可以看出如果笛卡尔坐标系的点共线,这些点在霍夫空间对应的直线交于一点:这也是必然,共线只有一种取值可能。如果不止一条直线呢?再看看多个点的情况(有两条直线):

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NoeWpoeXAxMQ_size_16_color_FFFFFF_t_70 4

其实(3,2)与(4,1)也可以组成直线,只不过它有两个点确定,而图中A、B两点是由三条直线汇成,这也是霍夫变换的后处理的基本方式:选择由尽可能多直线汇成的点。看看,霍夫空间:选择由三条交汇直线确定的点(中间图),对应的笛卡尔坐标系的直线(右图)。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NoeWpoeXAxMQ_size_16_color_FFFFFF_t_70 5

到这里问题似乎解决了,已经完成了霍夫变换的求解,但是如果像下图这种情况呢?

但这样做会有一个弊端,那就是当直线趋近于垂直时,斜率a会趋近于无穷大。这一困难的解决方法之一就是使用法线来表示直线。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NoeWpoeXAxMQ_size_16_color_FFFFFF_t_70 6

在极坐标系下,其实是一样的:极坐标的点→霍夫空间的正弦线,这条正弦线上的所有点都过极坐标中的那个点。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NoeWpoeXAxMQ_size_16_color_FFFFFF_t_70 7

简单来说就是每个点在霍夫空间的弦线与其他点的弦线交一次,就会在累加器的ρθ平面中累加一次。下面的动画很好的演示了这个过程。

Hough demo

2. OpenCV中的 C++ 函数定义

源码位置: imgproc.hpp

2.1 标准的霍夫线变换 cv::HoughLines

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

参数:

  • image:第一个参数,InputArray类型的image,输入图像,即源图像,需为8位的单通道二进制图像,可以将任意的源图载入进来后由函数修改成此格式后,再填在这里。
  • lines:第二个参数,InputArray类型的lines,经过调用HoughLines函数后储存了霍夫线变换检测到线条的输出矢量。每一条线由具有两个元素的矢量表示,其中,rho 是离坐标原点((0,0)(也就是图像的左上角)的距离。 theta 是弧度线条旋转角度(0~垂直线,π/2~水平线)。
  • lines:第三个参数,double类型的rho,以像素为单位的距离精度。另一种形容方式是直线搜索时的进步尺寸的单位半径。PS:Latex中/rho就表示 。
  • theta:第四个参数,double类型的theta,以弧度为单位的角度精度。另一种形容方式是直线搜索时的进步尺寸的单位角度。
  • threshold:第五个参数,int类型的,累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。大于阈值threshold的线段才可以被检测通过并返回到结果中。
  • srn:第六个参数,double类型的srn,有默认值0。对于多尺度的霍夫变换,这是第三个参数进步尺寸rho的除数距离。粗略的累加器进步尺寸直接是第三个参数rho,而精确的累加器进步尺寸为rho/srn。
  • stn:第七个参数,double类型的stn,有默认值0,对于多尺度霍夫变换,srn表示第四个参数进步尺寸的单位角度theta的除数距离。且如果srn和stn同时为0,就表示使用经典的霍夫变换。否则,这两个参数应该都为正数。
  • min_theta:第八个参数,double类型的 min_theta,对于标准和多尺度Hough变换,检查线条的最小角度。必须介于0和max_theta之间。
  • max_theta:第九个参数,double类型的 max_theta, 对于标准和多尺度Hough变换,检查线条的最大角度。必须介于min_theta和CV_PI之间.

2.2 概率统计霍夫线变换 cv::HoughLinesP

void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold,double minLineLength=0, double maxLineGap=0 )

参数:

  • image: 边缘检测的输出图像. 它应该是个灰度图 (但事实上是个二值化图) *
  • lines: 储存着检测到的直线的参数对 的容器,也就是线段两个端点的坐标,每一条线由具有四个元素的矢量(x_1,y_1, x_2, y_2) 表示,其中,(x_1, y_1)和(x_2, y_2) 是是每个检测到的线段的结束点。
  • rho :  参数极径 以像素值为单位的分辨率. 我们使用 1 像素。以像素为单位的距离精度。另一种形容方式是直线搜索时的进步尺寸的单位半径。
  • theta: 参数极角 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180);以弧度为单位的角度精度。另一种形容方式是直线搜索时的进步尺寸的单位角度。
  • threshold: 要”检测” 一条直线所需最少的的曲线交点 ;累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。大于阈值threshold的线段才可以被检测通过并返回到结果中。
  • minLinLength: 能组成一条直线的最少点的数量. 点数量不足的直线将被抛弃.线段的最小长度。有默认值0,表示最低线段的长度,比这个设定参数短的线段就不能被显现出来。
  • maxLineGap: 线段上最近两点之间的阈值;有默认值0,允许将同一行点与点之间连接起来的最大的距离。

3. 示例1

20170316154250023

  1. cv::Mat image = cv::imread("../../aTestImage/road2.jpg", 0);//Building
  2. cv::Mat contours;
  3. cv::Canny(image, contours, 125, 350);//主体为白色
  4. std::vector<cv::Vec2f> lines;//二维向量组
  5. cv::HoughLines(contours, lines, 1, PI / 180, 150);//获取hough变换的直线数据存储在lines向量中
  6. std::cout << "lines count:" << lines.size() << std::endl;
  7. cv::Mat result(contours.rows, contours.cols, CV_8U, cv::Scalar(255));//和轮廓图像大小一致的图像
  8. image.copyTo(result);
  9. std::vector <cv::Vec2f>::const_iterator it = lines.begin();
  10. while (it != lines.end())
  11. {
  12. float rho = (*it)[0];//半径
  13. float theta = (*it)[1];//角度
  14. if (theta<PI / 4. || theta>3.*PI / 4.)
  15. {
  16. //垂直线
  17. cv::Point pt1(rho / cos(theta), 0);//与第一行交点
  18. cv::Point pt2((rho - result.rows*sin(theta)) / cos(theta), result.rows);//与最后一行交点
  19. cv::line(result, pt1, pt2, cv::Scalar(255), 1);
  20. }
  21. else
  22. {
  23. //水平线
  24. cv::Point pt1(0, rho / sin(theta));//与第一列交点
  25. cv::Point pt2(result.cols, (rho - result.cols*cos(theta)) / sin(theta));//与最后一列交点
  26. cv::line(result, pt1, pt2, cv::Scalar(255), 1);
  27. }
  28. std::cout << "lines Detail:" <<"("<<rho<<","<<theta <<")"<< std::endl;
  29. ++it;
  30. }
  31. cv::namedWindow("resultL", 1);
  32. cv::imshow("resultL", result);

4. 示例2(官方)

  1. /* This is a standalone program. Pass an image name as the first parameter
  2. of the program. Switch between standard and probabilistic Hough transform
  3. by changing "#if 1" to "#if 0" and back */
  4. #include <cv.h>
  5. #include <highgui.h>
  6. #include <math.h>
  7. using namespace cv;
  8. int main(int argc, char** argv)
  9. {
  10. Mat src, dst, color_dst;
  11. if( argc != 2 || !(src=imread(argv[1], 0)).data)
  12. return -1;
  13. Canny( src, dst, 50, 200, 3 );
  14. cvtColor( dst, color_dst, CV_GRAY2BGR );
  15. #if 0
  16. vector<Vec2f> lines;
  17. HoughLines( dst, lines, 1, CV_PI/180, 100 );
  18. for( size_t i = 0; i < lines.size(); i++ )
  19. {
  20. float rho = lines[i][0];
  21. float theta = lines[i][1];
  22. double a = cos(theta), b = sin(theta);
  23. double x0 = a*rho, y0 = b*rho;
  24. Point pt1(cvRound(x0 + 1000*(-b)),
  25. cvRound(y0 + 1000*(a)));
  26. Point pt2(cvRound(x0 - 1000*(-b)),
  27. cvRound(y0 - 1000*(a)));
  28. line( color_dst, pt1, pt2, Scalar(0,0,255), 3, 8 );
  29. }
  30. #else
  31. vector<Vec4i> lines;
  32. HoughLinesP( dst, lines, 1, CV_PI/180, 80, 30, 10 );
  33. for( size_t i = 0; i < lines.size(); i++ )
  34. {
  35. line( color_dst, Point(lines[i][0], lines[i][1]),
  36. Point(lines[i][2], lines[i][3]), Scalar(0,0,255), 3, 8 );
  37. }
  38. #endif
  39. namedWindow( "Source", 1 );
  40. imshow( "Source", src );
  41. namedWindow( "Detected Lines", 1 );
  42. imshow( "Detected Lines", color_dst );
  43. waitKey(0);
  44. return 0;
  45. }

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NoeWpoeXAxMQ_size_16_color_FFFFFF_t_70 8

参考:OpenCV-Python——第21章:霍夫(Hough)直线变换(直线检测)

发表评论

表情:
评论列表 (有 0 条评论,525人围观)

还没有评论,来说两句吧...

相关阅读