Python图像处理之Hough变换检测直线

 更新时间:2023年07月27日 16:16:55   作者:菜菜的小粉猪  
霍夫变换是一种特征检测(feature extraction),被广泛应用在图像分析,本文将利用Hough变换实现直线检测,感兴趣的小伙伴可以了解一下

一、 前言

霍夫变换是一种特征检测(feature extraction),被广泛应用在图像分析(image analysis)、计算机视觉(computer vision)以及数位影像处理(digital image processing)。由RichardDuda和PeterHart在公元1972年发明,并称之为广义霍夫变换(generalizedHoughtransform),广义霍夫变换和更早前1962年的PaulHough的专利有关。经典的霍夫变换是侦测图片中的直线,之后,霍夫变换不仅能识别直线,也能够识别任何形状,常见的有圆形、椭圆形。1981年,因为DanaH.Ballard的一篇期刊论文"Generalizing the Hough transform to detect arbitrary shapes",让霍夫变换开始流行于计算机视觉界。霍夫变换是用来辨别找出物件中的特征,例如:线条。他的算法流程大致如下,给定一个物件、要辨别的形状的种类,算法会在参数空间(parameter space)中执行投票来决定物体的形状,而这是由累加空间(accumulator space)里的局部最大值(local maximum)来决定。

二、Hough 变换

一条直线可由两个点A=(x1​,y1​)和B=(x2​,y2​)确定(笛卡尔坐标)

另一方面,y=kx+b也可以写成关于(k,q)的函数表达式(霍夫空间):

空间变换过程如下图:

变换后的空间成为霍夫空间。即:笛卡尔坐标系中一条直线,对应霍夫空间的一个点。

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

笛卡尔坐标系中两个点对应霍夫空间两条线:

如果笛卡尔坐标系三个点共线,对应的霍夫空间的三条线相交于一点

霍夫变换的后处理的基本方式:选择由尽可能多直线汇成的点。但是,按照直角坐标系表示的话会出现下图的情况:当图像空间中点共的线垂直于x轴时,斜率无限大,在霍夫空间无法找到交点。因而,人们最终引入了极坐标的表示法。

极坐标下的霍夫直线检测原理与直角坐标系下完全一致,唯一需要重新推导的是与霍夫空间的极坐标参数函数:

化简便可得到:

r=xcosθ+ysinθ

如果对于一个给定点(x0​,y0​),意味着每一对(r,θ)代表一条通过点(xθ​,yθ​)的直线。我们在极坐标对极径极角平面绘出所有通过它的直线, 将得到一条正弦曲线. 例如, 对于给定点(x0​=8和y0​=6) 我们可以绘出下图 (在平面):

极坐标与笛卡尔坐标的转换公式,从极坐标转换(r,θ)在笛卡尔坐标系(x,y):

从笛卡儿坐标转换 (x,y) 到极坐标(r,θ):

在极坐标系下,其实是一样的:极坐标的点→霍夫空间的直线,只不过霍夫空间不再是[k,q]的参数,而是(r,θ)。

三、直线检测

通过上面的介绍可知,画出x−y坐标空间中的点在参数空间中对应的曲线,然后计算参数空间中曲线的交点,就能求得待求的参数。但还有一个问题,当参数空间中的曲线存在多个交点时,如何挑选出最有可能的解呢?

具体计算时,可将参数空间划分为所谓的累加单元A(θ,ρ)。如图下图所示,对于x−y平面的每一个非背景点(xk​,yk​),令 θ等于每个可取的细分值,根据θ=−xk​θ+yk​计算出对应的ρ值,每计算出一组 A(θ,ρ),则令A(θ,ρ)=A(θ,ρ)+1。计算所有结果后,找到A(θ,ρ)的峰值对应的θ和ρ,即可检测直线。(θmin​,θmax​)和(ρmin​,ρmax​)是期望的参数范围:0°≤θ≤180°和−D≤θ≤D, D是图像对角线的长度。 θ和ρ的细分数量决定了检测结果的精度。

投票过程可以观看下面的GIF,

左边上青色的点代表图像上的像素点,黄色的代表对各个点不同角度搜索。右半边是投票盘,颜色越浅代表票数越多。

四、代码实现

1.hough检测

def lines_detector_hough(img,ThetaDim=None, DistStep=None, threshold=None, halfThetaWindowSize=2,
                         halfDistWindowSize=None):
    '''
    :param img: 经过边缘检测得到的二值图
    :param ThetaDim: hough空间中theta轴的刻度数量(将[0,pi)均分为多少份),反应theta轴的粒度,越大粒度越细
    :param DistStep: hough空间中dist轴的划分粒度,即dist轴的最小单位长度
    :param threshold: 投票表决认定存在直线的起始阈值
    :return: 返回检测出的所有直线的参数(theta,dist)和对应的索引值,
    '''
    row,col= edge.shape
    if ThetaDim == None:
        ThetaDim = 90
    if DistStep == None:
        DistStep = 1
    # 计算距离分段数量
    MaxDist = np.sqrt(row ** 2 + col ** 2)
    DistDim = int(np.ceil(MaxDist / DistStep))
    if halfDistWindowSize == None:
        halfDistWindowSize = int(DistDim /50)
    # 建立投票
    accumulator = np.zeros((ThetaDim, DistDim))  # theta的范围是[0,pi). 在这里将[0,pi)进行了线性映射.类似的,也对Dist轴进行了线性映射
    #
    sinTheta = [np.sin(t * np.pi / ThetaDim) for t in range(ThetaDim)]
    cosTheta = [np.cos(t * np.pi / ThetaDim) for t in range(ThetaDim)]
    #计算距离(rho)
    for i in range(row):
        for j in range(col):
            if not edge[i, j] == 0:
                for k in range(ThetaDim):
                    accumulator[k][int(round((i * cosTheta[k] + j * sinTheta[k]) * DistDim / MaxDist))] += 1
    M = accumulator.max()
#---------------------------------------
    #非极大抑制
    if threshold == None:
        threshold = int(M * 1.369/ 10)
    result = np.array(np.where(accumulator > threshold))  # 阈值化
    #获得对应的索引值
    temp = [[], []]
    for i in range(result.shape[1]):
        eight_neiborhood = accumulator[
                           max(0, result[0, i] - halfThetaWindowSize + 1):min(result[0, i] + halfThetaWindowSize,
                                                                              accumulator.shape[0]),
                           max(0, result[1, i] - halfDistWindowSize + 1):min(result[1, i] + halfDistWindowSize,
                                                                             accumulator.shape[1])]
        if (accumulator[result[0, i], result[1, i]] >= eight_neiborhood).all():
            temp[0].append(result[0, i])
            temp[1].append(result[1, i])
    #记录原图所检测的坐标点(x,y)
    result_temp= np.array(temp)
#-------------------------------------------------------------
    result = result_temp.astype(np.float64)
    result[0] = result[0] * np.pi / ThetaDim
    result[1] = result[1] * MaxDist / DistDim
    return result,result_temp

2.画直线代码

def drawLines(lines, edge, color=(255, 0, 0), err=3):
    '''
    :param lines: 检测后的直线参数
    :param edge: 原图
    :param color: 直线的颜色
    :param err:检测的可接受的误差值
    :return: 无
    '''
    if len(edge.shape) == 2:
        result = np.dstack((edge, edge, edge))
    else:
        result = edge
    Cos = np.cos(lines[0])
    Sin = np.sin(lines[0])
    for i in range(edge.shape[0]):
        for j in range(edge.shape[1]):
            e = np.abs(lines[1] - i * Cos - j * Sin)
            if (e < err).any():
                result[i, j] = color
    plt.imshow(result, cmap='gray')
    plt.axis('off')
    plt.show()

3.画hough空间代码

def data_img(data):
    '''
    :param data: 直线上含有的点(x,y)
    :return: 输出hough空间图像
    '''
    fig = plt.figure()  # 新建画布
    ax = axisartist.Subplot(fig, 111)  # 使用axisartist.Subplot方法创建一个绘图区对象ax
    fig.add_axes(ax)
    ax.axis[:].set_visible(False)  # 隐藏原来的实线矩形
    ax.axis["x"] = ax.new_floating_axis(0, 0, axis_direction="bottom")  # 添加x轴
    ax.axis["y"] = ax.new_floating_axis(1, 0, axis_direction="bottom")  # 添加y轴
    ax.axis["x"].set_axisline_style("->", size=1.0)  # 给x坐标轴加箭头
    ax.axis["y"].set_axisline_style("->", size=1.0)  # 给y坐标轴加箭头
    t = np.arange(-np.pi / 2, np.pi / 2, 0.1)
    ax.annotate(text='x', xy=(2 * math.pi, 0), xytext=(2 * math.pi, 0.1))  # 标注x轴
    ax.annotate(text='y', xy=(0, 1.0), xytext=(-0.5, 1.0))  # 标注y轴
    for i in range(data.shape[1]):
        rho = data[0][i] * np.cos(t) + data[1][i] * np.sin(t)
        plt.plot(t, rho)
    plt.show()

4.检测结果

以上就是Python图像处理之Hough变换检测直线的详细内容,更多关于Python检测直线的资料请关注脚本之家其它相关文章!

相关文章

  • Django rest framework工具包简单用法示例

    Django rest framework工具包简单用法示例

    这篇文章主要介绍了Django rest framework工具包简单用法,结合匿名访问控制的具体实例分析了Django rest framework工具包的注册、路由设置、视图、权限控制、配置等相关操作技巧,需要的朋友可以参考下
    2018-07-07
  • Python使用not运算符对布尔值取反的方法

    Python使用not运算符对布尔值取反的方法

    本文详细介绍了Python中布尔值的取反操作,包括基本用法、非布尔值的取反、实际场景示例、注意事项以及自定义对象的布尔转换,通过not运算符,可以高效地进行逻辑反转,需要的朋友可以参考下
    2026-02-02
  • Python实现将Json转换为Xml与Markdown

    Python实现将Json转换为Xml与Markdown

    这篇文章主要为大家详细介绍了如何利用Python实现将Json转换为Xml与Markdown格式,文中的示例代码讲解详细,有需要的小伙伴可以了解一下
    2025-07-07
  • python中Pycharm 输出中文或打印中文乱码现象的解决办法

    python中Pycharm 输出中文或打印中文乱码现象的解决办法

    本篇文章主要介绍了python中Pycharm 输出中文或打印中文乱码现象的解决办法 ,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • 分享11个Python自动化操作Excel的方法

    分享11个Python自动化操作Excel的方法

    这篇文章主要给大家分享的是11个Python自动化操作Excel的方法,自动化办公,我相信很多人都有强烈的需求,今天我教大家如何利用Python自动化操作Excel,需要的朋友可以参考一下
    2022-02-02
  • Python如何快速在PDF中添加文字或者图片水印

    Python如何快速在PDF中添加文字或者图片水印

    在 PDF 文件中添加水印,是保护文档内容的一种常见方式,本文将为大家详细介绍如何使用 Python 快速为 PDF 添加文字或图片水印,希望对大家有所帮助
    2025-11-11
  • 使用Python接入OCR文字识别API的全教程

    使用Python接入OCR文字识别API的全教程

    本文介绍了Python调用OCR API进行文字识别的全过程,包括准备工作、API请求方式、Python接入示例、识别效果示例、常见优化技巧、在线体验与多语言文档等内容,帮助读者快速实现文字识别功能,需要的朋友可以参考下
    2026-04-04
  • Python pip 安装与使用(安装、更新、删除)

    Python pip 安装与使用(安装、更新、删除)

    pip 是 Python 包管理工具,该工具提供了对Python 包的查找、下载、安装、卸载的功能,需要的朋友可以参考下
    2019-10-10
  • Flask配置Cors跨域的实现

    Flask配置Cors跨域的实现

    这篇文章主要介绍了Flask配置Cors跨域的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • Windows下实现pytorch环境搭建

    Windows下实现pytorch环境搭建

    这篇文章主要介绍了Windows下实现pytorch环境搭建,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04

最新评论