OpenCV-霍夫变换与直线、圆检测

霍夫变换

直线检测

任何一条线都可以用(ρ,θ)这两个术语表示。因此,首先创建2D数组或累加器(以保存两个参数的值),并将其初始设置为0。让行表示ρ,列表示θ。阵列的大小取决于所需的精度。假设您希望角度的精度为1度,则需要180列。对于ρ,最大距离可能是图像的对角线长度。因此,以一个像素精度为准,行数可以是图像的对角线长度。

考虑一个100x100的图像,中间有一条水平线。取直线的第一点。您知道它的(x,y)值。现在在线性方程式中,将值θ= 0,1,2,… 180放进去,然后检查得到ρ。对于每对(ρ,θ),在累加器中对应的(ρ,θ)单元格将值增加1。所以现在在累加器中,单元格(50,90)= 1以及其他一些单元格。

现在,对行的第二个点。执行与上述相同的操作。递增(ρ,θ)对应的单元格中的值。这次,单元格(50,90)=2。实际上,您正在对(ρ,θ)值进行投票。您对线路上的每个点都继续执行此过程。在每个点上,单元格(50,90)都会增加或投票,而其他单元格可能会或可能不会投票。这样一来,最后,单元格(50,90)的投票数将最高。因此,如果在累加器中搜索最大票数,则将获得(50,90)值,该值表示该图像中的一条线与原点的距离为50,角度为90度。

标准直线检测

cv2.HoughLines

使用标准Hough变换在二值图像中查找线条

HoughLines(image, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]]) -> lines
  • image 是输入图像,即源图像,必须是 8 位的单通道二值图像。如果是其他类型的图像,在进行霍夫变换之前,需要将其修改为指定格式。
  • rho 为以像素为单位的距离 r 的精度。一般情况下,使用的精度是 1。
  • theta 为角度 θ 的精度。一般情况下,使用的精度是 π/180,表示要搜索所有可能的角度。
  • threshold 是阈值。该值越小,判定出的直线就越多。通过上一节的分析可知,识别直线时,要判定有多少个点位于该直线上。在判定直线是否存在时,对直线所穿过的点的数量进行评估,如果直线所穿过的点的数量小于阈值,则认为这些点恰好(偶然)在算法上构成直线,但是在源图像中该直线并不存在;如果大于阈值,则认为直线存在。所以,如果阈值较小,就会得到较多的直线;阈值较大,就会得到较少的直线。
  • 返回值 lines 中的每个元素都是一对浮点数,表示检测到的直线的参数,即(r,θ),是 numpy.ndarray 类型
示例
def line_detection(image):
    """霍夫变换直线检测"""
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    edges = cv.Canny(gray, 50, 150, apertureSize=3)

    # cv2.HoughLines()返回值就是(ρ,θ)。ρ 的单位是像素,θ 的单位是弧度。
    # 这个函数的第一个参数是一个二值化图像,所以在进行霍夫变换之前要首先进行二值化,或者进行 Canny 边缘检测。
    # 第二和第三个值分别代表 ρ 和 θ 的精确度。第四个参数是阈值,只有累加其中的值高于阈值时才被认为是一条直线,
    # 也可以把它看成能 检测到的直线的最短长度(以像素点为单位)。

    lines = cv.HoughLines(edges, 1, np.pi / 180, 200)
    # print(lines):
    for line in lines:
        # print(type(lines))
        rho, theta = line[0]
        a = np.cos(theta)
        b = np.sin(theta)
        x0 = a * rho
        y0 = b * rho
        x1 = int(x0 + 1000 * (-b))
        y1 = int(y0 + 1000 * (a))
        x2 = int(x0 - 1000 * (-b))
        y2 = int(y0 - 1000 * (a))
        cv.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2)
    cv.imshow("line_detection", image)

结果:
在这里插入图片描述
注意: 当检测不到图片时,lines为None,这时候程序会报错。建议加上if判断。

概率直线检测

概率霍夫变换对基本霍夫变换算法进行了一些修正,是霍夫变换算法的优化。它没有考虑所有的点。相反,它只需要一个足以进行线检测的随机点子集即可。

为了更好地判断直线(线段),概率霍夫变换算法还对选取直线的方法作了两点改进:

  • 所接受直线的最小长度。如果有超过阈值个数的像素点构成了一条直线,但是这条直线很短,那么就不会接受该直线作为判断结果,而认为这条直线仅仅是图像中的若干个像素点恰好随机构成了一种算法上的直线关系而已,实际上原图中并不存在这条直线。

  • 接受直线时允许的最大像素点间距。如果有超过阈值个数的像素点构成了一条直线,但是这组像素点之间的距离都很远,就不会接受该直线作为判断结果,而认为这条直线仅仅是图像中的若干个像素点恰好随机构成了一种算法上的直线关系而已,实际上原始图像中并不存在这条直线。

cv2.HoughLinesP

用概率Hough变换在二值图像中查找线段

HoughLinesP(image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]]) -> lines
  • image 是输入图像,即源图像,必须为 8 位的单通道二值图像。对于其他类型的图像,在进行霍夫变换之前,需要将其修改为这个指定的格式。
  • rho 为以像素为单位的距离 r 的精度。一般情况下,使用的精度是 1。
  • theta 是角度 θ 的精度。一般情况下,使用的精度是 np.pi/180,表示要搜索可能的角度。
  • threshold 是阈值。该值越小,判定出的直线越多;值越大,判定出的直线就越少。
  • minLineLength 用来控制「接受直线的最小长度」的值,默认值为 0。
  • maxLineGap 用来控制接受共线线段之间的最小间隔,即在一条线中两点的最大间隔。
    如果两点间的间隔超过了参数 maxLineGap 的值,就认为这两点不在一条线上。默认值为 0。
  • 返回值 lines 是线的输出向量。每一行由一个4元素向量表示,x1,y1,x2,y2。
示例
def line_detection_possible(image):
    """概率霍夫变换直线检测"""

    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    edges = cv.Canny(gray, 50, 150, apertureSize=3)
    lines = cv.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=100, maxLineGap=10)
    for line in lines:
        x1, y1, x2, y2 = line[0]
        cv.line(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
    cv.imshow('line_detection_possible', image)

结果:
在这里插入图片描述

参考链接:

圆检测

cv2.HoughCircles

使用Hough变换在灰度图像中查找圆

HoughCircles(image, method, dp, minDist[, circles[, param1[, param2[, minRadius[, maxRadius]]]]]) -> circles
  • image:输入图像
  • method:cv2.HOUGH_GRADIENT 也就是霍夫圆检测,梯度法
  • dp:计数器的分辨率图像像素分辨率与参数空间分辨率的比值(官方文档上写的是图像分辨率与累加器分辨率的比值,它把参数空间认为是一个累加器,毕竟里面存储的都是经过的像素点的数量),dp=1,则参数空间与图像像素空间(分辨率)一样大,dp=2,参数空间的分辨率只有像素空间的一半大
  • minDist:圆心之间最小距离,如果距离太小,会产生很多相交的圆,如果距离太大,则会漏掉正确的圆
  • param1:canny检测的双阈值中的高阈值,低阈值是它的一半
  • param2:最小投票数(基于圆心的投票数)
  • minRadius:需要检测院的最小半径
  • maxRadius:需要检测院的最大半径
  • 返回值为圆心坐标与半径

示例

def circle_detection(image):
    """霍夫圆检测"""
    # 均值迁移滤波,因为霍夫圆检测对噪声敏感,sp,sr为空间域核与像素范围域核半径
    dst = cv.pyrMeanShiftFiltering(image, 10, 100)
    gray = cv.cvtColor(dst, cv.COLOR_BGR2GRAY)
    circles = cv.HoughCircles(gray, cv.HOUGH_GRADIENT, 1, 20, param1=50, param2=30, minRadius=0, maxRadius=0)
    circles = np.uint16(np.around(circles))
    # print(circles.shape)
    for i in circles[0, :]:  # draw the outer circle
        cv.circle(image, (i[0], i[1]), i[2], (0, 255, 0), 2)  # 画圆
        cv.circle(image, (i[0], i[1]), 2, (0, 0, 255), 3)  # 画圆心
    cv.imshow('detected circles', image)

结果:
在这里插入图片描述
注意: 霍夫圆检测对噪声敏感,使用霍夫圆检测之前,需要先对图片进行滤波降噪处理。

参考链接:

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咬着棒棒糖闯天下

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值