霍夫直线检测算法c语言,计算机视觉 OpenCV Android | 基本特征检测之 霍夫直线检测 详析...

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

在取得图像边缘的基础上,

对一些特定的几何形状边缘,如直线、圆,通过图像霍夫变换把图像从平面坐标空间变换到霍夫坐标空间,

就可以通过求取霍夫空间的局部极大值方法(其实就是霍夫空间中的曲线交集点),

得到极坐标空间对应参数方程中直线的两个参数(r,θ),

从而计算得到边缘图像中的所有直线(基于平面坐标)的数目与位置。

假设有一条直线如下图:

ae5b49b4e5a2

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

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

若变换到极坐标空间则变成求取另外两个参数(r,θ),r 和 θ之间的关系可以表示为:

(公式的来源运算过程见上图)

ae5b49b4e5a2

对于每个平面空间的像素点坐标(x,y),

随着角度θ的取值不同,都会得到r值,

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

则对于边缘图像的每个平面空间坐标点可绘制极坐标的曲线如图所示:

ae5b49b4e5a2

上图中,

左侧是一个平面空间的像素点,

基于公式r = x * cosθ + y * sinθ,

通过给定不同的θ值,得到唯一对应r值,

无数个(r,θ)数对构成的一道极坐标曲线;

右侧是三个平面空间的像素点,

基于公式r = x * cosθ + y * sinθ,

通过给定不同的θ值,得到唯一对应r值,

无数个(r,θ)数对构成的三道极坐标曲线;

无论截图的左侧还是右侧,都是所谓 霍夫空间的一部分,所谓霍夫空间,如下图:

ae5b49b4e5a2

霍夫空间 概念详析

霍夫空间就是一个基于(r,θ)两个参数坐标轴的数据空间,

数量级规模是可以是一个边缘图像的像素点数量;

并且这个空间包括了这样的一系列曲线 :

一个边缘图像的所有(all & each,假设为 N 个)像素点(x,y),

基于公式r = x * cosθ + y * sinθ,

通过给定不同的θ值,得到唯一对应r值,

无数个(r,θ)数对构成对应上 N个 像素点的 N 道 极坐标曲线(霍夫空间的曲线);

霍夫直线检测 的 知识要点

(要点.A)

输入的边缘图像中的每一个像素点一 一 对 应一条霍夫空间(or 极坐标参数)曲线;

(要点.B)

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

(由上可得 要点.C)

霍夫空间中的一个交集点(若干曲线的交点(r,θ))

就是一条直线(点的参数(r,θ)可变换成直线);

而重叠在这个交集点上的霍夫(极坐标)曲线集,

其实就是该交集点代表的(存在边缘图像中 的 对应的)直线 所包含的(像素)点集;

(要点.D)

交集点上累积的曲线越多;

对应(平面坐标系的边缘图像上的)直线所包含的像素点集就越多;

也即对应直线的长度越大;

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

ae5b49b4e5a2

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

由在平面空间同属于一条直线的像素点绘制出来的曲线必然会相交于一点(上方截图的b)右侧所示的曲线),

而这个点正是存在边缘对象中的对应的直线在极坐标空间中的参数方程的参数,

这样就在极坐标空间找到了直线的参数方程,

反变换回到平面坐标空间就可以求得直线的两个参数(k,b),

得到直线位置,

而它们在极坐标的交点就是直线在霍夫空间的表达,

直线越长,其在霍夫空间这个点的累积值就越高,相对的灰度值也就越(亮)。

OpenCV关于霍夫直线变换提供了两个相关API函数,

一个是在霍夫空间求取直线两个极坐标的参数,

需要开发者自己转换到平面坐标空间计算直线;

另外一个则会直接返回平面空间直线/线段的两个点坐标信息。

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

HoughLines(Mat image, Mat lines, double rho, double theta, int threshold)

image:表示输入图像,8位单通道图像,一般为二值图像。

lines:表示输出的每个直线的极坐标参数方程的两个参数。

rho:表示极坐标空间r值每次的步长,一般设置为1。

theta:表示角度θ,每次移动1°即可。

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.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));这一行代码,

关于参数100的意义,可参考

ae5b49b4e5a2

在实际效果图中(下方结果图源程序用的原图是lena),这个100偏移量,决定的就是这些生成直线的长度:

ae5b49b4e5a2

以上的这个API函数需要对得到的每对极坐标参数(r,θ)做计算,

使其变换到平面空间(x0 = r * cosθ ; y0 = r * sinθ),

接着通过对x0和y0添加偏移量并进行计算,得到直线的两个点;

然后绘制直线。

另外一个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

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的时候,最后两个参数的设置也会影响霍夫直线检测的结果。

参考材料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
import cv2 as cv import numpy as np #直线检测 #使用霍夫直线变换做直线检测,前提条件:边缘检测已经完成 #标准霍夫线变换 def line_detection(image): gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY) edges = cv.Canny(gray, 50, 150) #apertureSize参数默认其实就是3 cv.imshow("edges", edges) #cv.HoughLines参数设置:参数1,灰度图像;参数二,以像素为单位的距离精度(一般都是1,进度高,但是速度会慢一点) #参数三,以弧度为单位的角度精度(一般是1rad);参数四,阈值,大于阈值threshold的线段才可以被检测通过并返回到结果中 #该函数返回值为rho与theta lines = cv.HoughLines(edges, 1, np.pi/180, 200) for line in lines: rho, theta = line[0] #line[0]存储的是点到直线的极径和极角,其中极角是弧度表示的。 a = np.cos(theta) #theta是弧度 b = np.sin(theta) x0 = a * rho #代表x = r * cos(theta) y0 = b * rho #代表y = r * sin(theta) x1 = int(x0 + 1000 * (-b)) #计算直线起点横坐标 y1 = int(y0 + 1000 * a) #计算起始起点纵坐标 x2 = int(x0 - 1000 * (-b)) #计算直线终点横坐标 y2 = int(y0 - 1000 * a) #计算直线终点纵坐标 注:这里的数值1000给出了画出的线段长度范围大小,数值越小,画出的线段越短,数值越大,画出的线段越长 cv.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2) #点的坐标必须是元组,不能是列表。 cv.imshow("image-lines", image) #统计概率霍夫线变换 def line_detect_possible_demo(image): gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY) edges = cv.Canny(gray, 50, 150, apertureSize=3) # apertureSize参数默认其实就是3 lines = cv.HoughLinesP(edges, 1, np.pi / 180, 60, minLineLength=60, maxLineGap=5) for line in lines: x1, y1, x2, y2 = line[0] cv.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2) cv.imshow("line_detect_possible_demo",image) src = cv.imread("E:/opencv/picture/track.jpg") print(src.shape) cv.namedWindow('input_image', cv.WINDOW_AUTOSIZE) cv.imshow('input_image', src) line_detection(src) src = cv.imread("E:/opencv/picture/track.jpg") #调用上一个函数后,会把传入的src数组改变,所以调用下一个函数时,要重新读取图片 line_detect_possible_demo(src) cv.waitKey(0) cv.destroyAllWindows() 霍夫检测直线原理: 关于hough变换,核心以及难点就是关于就是有原始空间到参数空间的变换上。以直线检测为例,假设有一条直线L,原点到该直线的垂直距离为p,垂线与x轴夹角为θθ,那么这条直线是唯一的,且直线的方程为 ρ=xcosθ+ysinθρ=xcosθ+ysinθ, 如下图所
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值