计算机视觉-OpenCV-Android---基本特征检测之-霍夫直线检测-详析

114 阅读7分钟

####霍夫直线检测的作用——计算得到输入图像(一般是二值化的边缘检测结果图像)中包含的所有直线的数目与位置

  • 在取得图像边缘的基础上, 对一些特定的几何形状边缘,如直线、圆,通过图像霍夫变换把图像从平面坐标空间变换到霍夫坐标空间, 就可以通过求取霍夫空间局部极大值方法(其实就是霍夫空间中的曲线交集点), 得到极坐标空间对应参数方程直线的两个参数(r,θ), 从而计算得到边缘图像中所有直线(基于平面坐标)的数目与位置

假设有一条直线如下图:

(红色部分是计算过程,递等到右下角的结果,待会儿要用)

笛卡儿平面坐标系统中的斜率参数与截距参数为(k,b)

若变换到极坐标空间则变成求取另外两个参数(r,θ)r 和 θ 之间的关系可以表示为: (公式的来源运算过程见上图)


对于```每个平面空间的像素点坐标(x,y)```, 随着```角度θ```的取值不同,都会```得到r值```, **(%+++%要点.B)而对于```任意一条直线```来说,在```极坐标空间```它的```(r,θ)```都是```固定不变的```,** 则对于```边缘图像```的```每个平面空间坐标点```可绘制```极坐标的曲线```如图所示:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9b90a4a8d1cc408d829a4f2302e265a5~tplv-k3u1fbpfcp-zoom-1.image) >- 上图中,
左侧是```一个```平面空间的```像素点```, 基于公式```r = x * cosθ + y * sinθ```, 通过给定不同的```θ```值,得到唯一对应```r```值, 无数个```(r,θ)```数对构成的```一道极坐标曲线```;
右侧是```三个```平面空间的```像素点```, 基于公式```r = x * cosθ + y * sinθ```, 通过给定不同的```θ```值,得到唯一对应```r```值, 无数个```(r,θ)```数对构成的```三道极坐标曲线```;
>- 无论截图的左侧还是右侧,都是所谓 ```霍夫空间的一部分```,所谓**```霍夫空间```**,如下图:![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2f857eda97fd4022b12cfacacc37d41e~tplv-k3u1fbpfcp-zoom-1.image)**[图片参考于此博文](https://www.cnblogs.com/cheermyang/p/5348820.html)**
>--- >#####霍夫空间 概念详析 >**```霍夫空间```**就是一个基于(r,θ)两个参数坐标轴的数据空间, ```数量级规模```是可以是```一个边缘图像```的```像素点数量```;
并且这个空间包括了**```这样的一系列曲线 :```** 一个**```边缘图像```**的**```所有(all & each,假设为 N 个)像素点(x,y)```**, 基于**公式```r = x * cosθ + y * sinθ```,** 通过给定不同的**```θ```**值,得到唯一对应**```r```**值, 无数个**```(r,θ)```**数对构成对应上 **```N个 像素点```**的 **``` N 道 极坐标曲线(霍夫空间的曲线)```**; >--- >#####霍夫直线检测 的 知识要点 >- **(要点.A)** 输入的边缘图像中的每一个像素点``` 一 一 对 应```一条```霍夫空间(or 极坐标参数)曲线```;
>- **(要点.B) 而对于```边缘图像中的 任意一条直线```来说,在```极坐标空间```它的```(r,θ)```都是```固定不变的```,**
>- **(由上可得 要点.C)** **霍夫空间中的一个```交集点(若干曲线的交点(r,θ))``` 就是一条```直线```(点的参数(r,θ)可变换成直线);** >- 而重叠在这个```交集点```上的```霍夫(极坐标)曲线集```, 其实就是```该交集点```代表的```(存在边缘图像中 的 对应的)直线``` ```所包含的(像素)点集```;
>- **(要点.D)** ```交集点```上```累积的曲线```越```多```; ```对应(平面坐标系的边缘图像上的)直线```所包含的```像素点集```就越```多```; 也即```对应直线```的```长度```越```大```; >--- >#####霍夫直线检测 从二值化.边缘检测.结果图像到检测绘制出直线 的大概步骤 >![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bbc67768944f492eb4876b1fcea4b821~tplv-k3u1fbpfcp-zoom-1.image)

以上引用框中的内容是个人的梳理总结,下面继续读书笔记的内容。

  • 由在平面空间同属于一条直线的像素点绘制出来的曲线必然会相交于一点(上方截图的b)右侧所示的曲线),
  • 而这个点正是存在边缘对象中的对应的直线极坐标空间中的参数方程的参数, 这样就在极坐标空间找到了直线的参数方程, 反变换回到平面坐标空间就可以求得直线的两个参数(k,b), 得到直线位置, 而它们在极坐标的交点就是直线在霍夫空间的表达, 直线越,其在霍夫空间这个点的累积值就越,相对的灰度值也就越(亮)

OpenCV关于霍夫直线变换提供了两个相关API函数, 一个是在霍夫空间求取直线两个极坐标的参数, 需要开发者自己转换到平面坐标空间计算直线; 另外一个则会直接返回平面空间直线/线段的两个点坐标信息

返回极坐标参数的API函数如下:

  • HoughLines(Mat image, Mat lines, double rho, double theta, int threshold) image:表示输入图像,8位单通道图像,一般为二值图像。 lines:表示输出的每个直线的极坐标参数方程的两个参数。 rho:表示极坐标空间r值每次的步长,一般设置为1theta:表示角度θ,每次移动即可。 threshold:表示霍夫空间中该点的累积数, 该累积数越大,则得到的直线可能就越长, 取值范围通常为30~50,单位是像素, 假设为30的话,则表示大于30个像素长度的线段才会被检测到。
  • threshold解释中所述的累积数可以看做我们数据处理中的投票机制票数大于threshold交集点 (即累积的曲线数大于threshold交集点), 才认定是有效直线, 才能被函数检测到提取出来用于返回/变换绘制成直线;

使用该API实现直线检测:

private void houghLinesDemo(Mat src, Mat dst) {
  Mat edges = new Mat();
  Imgproc.Canny(src, edges, 50, 150, 3, true);
  Mat lines = new Mat();
  Imgproc.HoughLines(edges, lines, 1,Math.PI/180.0, 200);
  Mat out = Mat.zeros(src.size(), src.type());
  float[] data = new float[2];
  for(int i=0; i<lines.rows(); i++) { 
      lines.get(i, 0, data);
      float rho = data[0], theta = data[1];
      double a = Math.cos(theta), b = Math.sin(theta);
      double x0 = a*rho, y0 = b*rho;

      Point pt1 = new Point();
      Point pt2 = new Point();
      pt1.x = Math.round(x0 + 100*(-b));//!!!!!!!!!!!!!!!!!
      pt1.y = Math.round(y0 + 100*(a));
      pt2.x = Math.round(x0 - 100*(-b));
      pt2.y = Math.round(y0 - 100*(a));

      Imgproc.line(out, pt1, pt2, new Scalar(0,0,255), 3, Imgproc.LINE_AA, 0);
  }
  out.copyTo(dst);
  out.release();
  edges.release();
}

关于pt1.x = Math.round(x0 + 100*(-b));这一行代码,

以上的这个API函数需要对得到的每对极坐标参数(r,θ)计算, 使其变换平面空间x0 = r * cosθ ; y0 = r * sinθ), 接着通过对x0y0添加偏移量并进行计算,得到直线的两个点; 然后绘制直线

另外一个API函数则比较简单, 它省去了开发者自己把极坐标变换为直线坐标的过程直接返回每个线段/直线对应的两个点坐标, 其API函数与参数的解释具体如下:

  • HoughLinesP(Mat image, Mat lines, double rho, double theta, int threshold, double minLineLength, double maxLineGap) image:表示输入图像,8位单通道图像,一般为二值图像。 lines表示输出的每个直线最终要绘制用两个 平面坐标系参数 rho:表示极坐标空间r值每次的步长,一般设置为1。 theta:表示角度θ,每次移动1°即可。 threshold:表示极坐标中该点的累积数,该累积数越大,则得到的直线可能就越长,取值范围通常为30~50,单位是像素,假设取值为30,则表示大于30个像素长度的线段才会被检测到。 minLineLength:表示可以检测的最小线段长度,根据实际需要进行设置。 maxLineGap:表示线段之间的最大间隔像素,假设5表示小于5个像素的两个相邻线段可以连接起来。

使用该API实现图像直线检测:

private void houghLinePDemo(Mat src, Mat dst) {
  Mat edges = new Mat();
  Imgproc.Canny(src, edges, 50, 150, 3, true);
  Mat lines = new Mat();
  Imgproc.HoughLinesP(edges, lines, 1, Math.PI/180.0, 100, 50, 10);
  Mat out = Mat.zeros(src.size(), src.type());
  for(int i=0; i<lines.rows(); i++) {
      int[] oneline = new int[4];
      lines.get(i, 0, oneline);
      Imgproc.line(out, new Point(oneline[0], oneline[1]),
          new Point(oneline[2], oneline[3]),
          new Scalar(0, 0, 255), 2, 8, 0);
  }
  out.copyTo(dst);
  // 释放内存
  out.release();
  edges.release();
}
  • 这里需要注意的是, 图像二值化边缘检测算法输出结果质量在**很大程度上影响 霍夫直线变换结果**, 同时在使用HoughLinesP的时候,最后两个参数设置也会影响霍夫直线检测的结果。




--- ##### 参考材料