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

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

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

假设有一条直线如下图:


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

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

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



对于每个平面空间的像素点坐标(x,y)
随着角度θ的取值不同,都会得到r值
(%+++%要点.B)而对于任意一条直线来说,在极坐标空间它的(r,θ)都是固定不变的
则对于边缘图像每个平面空间坐标点可绘制极坐标的曲线如图所示:

  • 上图中,

    左侧是一个平面空间的像素点
    基于公式r = x * cosθ + y * sinθ
    通过给定不同的θ值,得到唯一对应r值,
    无数个(r,θ)数对构成的一道极坐标曲线

    右侧是三个平面空间的像素点
    基于公式r = x * cosθ + y * sinθ
    通过给定不同的θ值,得到唯一对应r值,
    无数个(r,θ)数对构成的三道极坐标曲线

  • 无论截图的左侧还是右侧,都是所谓 霍夫空间的一部分,所谓霍夫空间,如下图:
    图片参考于此博文

霍夫空间 概念详析

霍夫空间就是一个基于(r,θ)两个参数坐标轴的数据空间,
数量级规模是可以是一个边缘图像像素点数量

并且这个空间包括了这样的一系列曲线 :
一个边缘图像所有(all & each,假设为 N 个)像素点(x,y)
基于公式r = x * cosθ + y * sinθ
通过给定不同的θ值,得到唯一对应r值,
无数个(r,θ)数对构成对应上 N个 像素点N 道 极坐标曲线(霍夫空间的曲线)


霍夫直线检测 的 知识要点
  • (要点.A)
    输入的边缘图像中的每一个像素点一 一 对 应一条霍夫空间(or 极坐标参数)曲线

  • (要点.B)
    而对于边缘图像中的 任意一条直线来说,在极坐标空间它的(r,θ)都是固定不变的


  • (由上可得 要点.C)
    霍夫空间中的一个交集点(若干曲线的交点(r,θ))
    就是一条直线(点的参数(r,θ)可变换成直线);
  • 而重叠在这个交集点上的霍夫(极坐标)曲线集
    其实就是该交集点代表的(存在边缘图像中 的 对应的)直线 所包含的(像素)点集

  • (要点.D)
    交集点累积的曲线
    对应(平面坐标系的边缘图像上的)直线所包含的像素点集就越
    也即对应直线长度

霍夫直线检测 从二值化.边缘检测.结果图像到检测绘制出直线 的大概步骤

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

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

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

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

  • HoughLines(Mat image, Mat lines, double rho, double theta, int threshold)
    image:表示输入图像,8位单通道图像,一般为二值图像。
    lines:表示输出的每个直线的极坐标参数方程的两个参数。
    rho:表示极坐标空间r值每次的步长,一般设置为1
    theta:表示角度θ,每次移动即可。
    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的时候,最后两个参数设置也会影响霍夫直线检测的结果。







参考材料
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容