Python OpenCV中的霍夫线变换(Hough Line Transform)详解

文章目录

  • 1、功能描述
  • 2、原理分析
  • 3、代码实现
  • 4、效果展示
  • 5、完整代码
  • 6、涉及到的库函数
  • cv2.HoughLines
  • cv2.HoughLinesP
  • 7、参考

  • 更多有趣的代码示例,可参考【Programming】


    1、功能描述

    基于 opencv-python 使用 hough 变换(cv2.HoughLines、cv2.HoughLinesP),来检测出来图片中的直线

    2、原理分析

    参考 Hough变化

    图像空间中的每条直线在参数空间中都对应着单个点;在图像空间中直线上的任何像素在参数空间中对应的直线(曲线)相交于同一个点。因此,通过Hough变换寻找图像中的直线就是寻找参数空间中大量直线(曲线)相交的一点。

    3、代码实现

    导入必要的库函数

    import sys
    import math
    import cv2 as cv
    import numpy as np
    

    写好程序入口

    if __name__ == "__main__":
        main(sys.argv[1:], save=True)
    

    读入图片,转成灰度图

    def main(argv, save=False):
        default_file = '1.jpg'
        filename = argv[0] if len(argv) > 0 else default_file
    
        # Loads an image
        src = cv.imread(cv.samples.findFile(filename))
        gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
    

    确认图片是否载入成功

        # Check if image is loaded fine
        if src is None:
            print('Error opening image!')
            print('Usage: hough_lines.py [image_name -- default ' + default_file + '] \n')
            return -1
    

    canny 变化准备提取边缘(潜在直线区域)

        dst = cv.Canny(gray, 50, 200, None, 3)
        if save:
            cv.imwrite("canny.jpg", dst)
    

    复制图片,以备绘制两种 hough 算法的结果

        cdst = np.copy(src)
        cdstP = np.copy(src)
    

    调用 cv2.HoughLines,求出图片中的直线,并绘制出来

        lines = cv.HoughLines(dst, 1, np.pi / 180, 150, None, 0, 0)
    
        if lines is not None:
            for i in range(0, len(lines)):
                rho = lines[i][0][0]
                theta = lines[i][0][1]
                a = math.cos(theta)
                b = math.sin(theta)
                x0 = a * rho
                y0 = b * rho
                pt1 = (int(x0 + 1000 * (-b)), int(y0 + 1000 * (a)))
                pt2 = (int(x0 - 1000 * (-b)), int(y0 - 1000 * (a)))
    
                cv.line(cdst, pt1, pt2, (0, 0, 255), 1, cv.LINE_AA)
    

    调用 cv2.HoughLinesP,求出图片中的直线,并绘制出来

        linesP = cv.HoughLinesP(dst, 1, np.pi / 180, 50, None, 50, 10)
    
        if linesP is not None:
            for i in range(0, len(linesP)):
                l = linesP[i][0]
                cv.line(cdstP, (l[0], l[1]), (l[2], l[3]), (0, 0, 255), 3, cv.LINE_AA)
    

    绘制的时候会更宽一些,cv.line(cdstP, (l[0], l[1]), (l[2], l[3]), (0, 0, 255), 3, cv.LINE_AA) 配置为了 3

    显示原图及两种方法绘制出来直线的效果,保存结果,退出显示,退出主程序

        cv.imshow("Source", src)
        cv.imshow("Detected Lines (in red) - Standard Hough Line Transform", cdst)
        cv.imshow("Detected Lines (in red) - Probabilistic Line Transform", cdstP)
    
        if save:
            cv.imwrite("cdst.jpg", cdst)
            cv.imwrite("cdstP.jpg", cdstP)
    
        cv.waitKey()
        return 0
    

    4、效果展示

    会依赖于 canny 算子的

    5、完整代码

    """
    @file hough_lines.py
    @brief This program demonstrates line finding with the Hough transform
    """
    import sys
    import math
    import cv2 as cv
    import numpy as np
    
    
    def main(argv, save=False):
        default_file = '2.jpg'
        filename = argv[0] if len(argv) > 0 else default_file
    
        # Loads an image
        src = cv.imread(cv.samples.findFile(filename))
        gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
    
        # Check if image is loaded fine
        if src is None:
            print('Error opening image!')
            print('Usage: hough_lines.py [image_name -- default ' + default_file + '] \n')
            return -1
    
        dst = cv.Canny(gray, 50, 200, None, 3)
        if save:
            cv.imwrite("canny.jpg", dst)
    
        cdst = np.copy(src)
        cdstP = np.copy(src)
    
        lines = cv.HoughLines(dst, 1, np.pi / 180, 150, None, 0, 0)
    
        if lines is not None:
            for i in range(0, len(lines)):
                rho = lines[i][0][0]
                theta = lines[i][0][1]
                a = math.cos(theta)
                b = math.sin(theta)
                x0 = a * rho
                y0 = b * rho
                pt1 = (int(x0 + 1000 * (-b)), int(y0 + 1000 * (a)))
                pt2 = (int(x0 - 1000 * (-b)), int(y0 - 1000 * (a)))
    
                cv.line(cdst, pt1, pt2, (0, 0, 255), 1, cv.LINE_AA)
    
        linesP = cv.HoughLinesP(dst, 1, np.pi / 180, 50, None, 50, 10)
    
        if linesP is not None:
            for i in range(0, len(linesP)):
                l = linesP[i][0]
                cv.line(cdstP, (l[0], l[1]), (l[2], l[3]), (0, 0, 255), 3, cv.LINE_AA)
    
        cv.imshow("Source", src)
        cv.imshow("Detected Lines (in red) - Standard Hough Line Transform", cdst)
        cv.imshow("Detected Lines (in red) - Probabilistic Line Transform", cdstP)
    
        if save:
            cv.imwrite("cdst.jpg", cdst)
            cv.imwrite("cdstP.jpg", cdstP)
    
        cv.waitKey()
        return 0
    
    
    if __name__ == "__main__":
        main(sys.argv[1:], save=True)
    

    6、涉及到的库函数

    cv2.HoughLines

    cv2.HoughLines 是 OpenCV 中用于霍夫直线变换的函数,它能够在图像中检测直线。

    一、函数原型

    lines = cv2.HoughLines(image, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]])
    

    二、参数说明

  • image:输入图像,必须是 8 位的单通道二值图像。通常,在应用霍夫变换之前,会对图像进行阈值处理或使用 Canny 边缘检测来获得二值图像。
  • rho:距离的精度(以像素为单位)。它决定了霍夫变换中累加器的分辨率。通常设置为 1。
  • theta:角度的精度,以弧度为单位。它决定了搜索直线的角度间隔。通常使用 π/180 来表示搜索所有可能的角度。
  • threshold:阈值。只有那些获得足够交点数(即边缘点数)的候选直线才会被返回。值越小,检测出的直线越多。
  • lines(可选):用于存储检测到的直线的数组。如果不提供,OpenCV 会自动分配内存。
  • srn 和 stn(可选):对于多尺度的霍夫变换,它们分别表示在 r 和 θ 方向上的尺度变化因子。默认情况下,这些参数不被使用。
  • min_theta 和 max_theta(可选):限制搜索直线的最小和最大角度。默认情况下,这些参数不被使用。
  • 三、返回值

  • lines:一个数组,其中每个元素都代表一条检测到的直线。每条直线由其极坐标 (ρ, θ) 表示,其中 ρ 以像素为单位,θ 以弧度为单位。
  • 四、使用示例

    import cv2
    import numpy as np
     
    # 读取图像
    image = cv2.imread('example_image.jpg')
    # 转换为灰度图像
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # 使用 Canny 边缘检测
    edges = cv2.Canny(gray, 50, 150)
     
    # 使用霍夫变换检测直线
    lines = cv2.HoughLines(edges, 1, np.pi / 180, 150)
     
    # 绘制检测到的直线
    if lines is not None:
        for line in 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))
            cv2.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2)
     
    # 显示结果图像
    cv2.imshow('Detected Lines', image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    五、注意事项

  • 在使用霍夫变换之前,确保输入图像是二值图像。
  • 调整 rho、theta 和 threshold 参数以获得最佳的直线检测结果。
  • 霍夫变换对于噪声和图像中的不连续直线可能不够鲁棒,因此在实际应用中可能需要结合其他图像处理方法来提高准确性。
  • cv2.HoughLinesP

    cv2.HoughLinesP 是 OpenCV 中用于概率霍夫线变换(Probabilistic Hough Transform)的函数,相比于标准的霍夫线变换(cv2.HoughLines),它能够在保持检测效果的同时显著减少计算量

    一、函数原型

    lines = cv2.HoughLinesP(image, rho, theta, threshold, minLineLength, maxLineGap)
    

    二、参数说明

  • image:输入图像,必须是 8 位的单通道二值图像。在调用 cv2.HoughLinesP 之前,通常会对图像进行边缘检测(如使用 Canny 算法)。
  • rho:距离分辨率,以像素为单位。它决定了累加器中的步长,即能够检测到的直线的最小间隔。
  • theta:角度分辨率,以弧度为单位。它决定了搜索直线的角度步长。
  • threshold:累加器阈值。只有那些获得足够交点数(即边缘点数)的候选直线才会被返回。阈值越高,检测到的直线越少,但通常更准确。
  • minLineLength:最小线段长度。只有长度大于此值的线段才会被返回。这有助于过滤掉短小的噪声线段。
  • maxLineGap:线段上允许的最大间隔。在一条直线上,如果两个点之间的距离超过此值,则它们将被视为两条不同的线段。这个参数有助于连接由于噪声或图像中的小间隙而断开的线段。
  • 三、返回值

  • lines:一个数组,其中每个元素都是一条检测到的线段。每条线段由其端点坐标 (x1, y1, x2, y2) 表示。
  • 四、使用示例

    import cv2
    import numpy as np
     
    # 读取图像
    image = cv2.imread('example_image.jpg')
    # 转换为灰度图像
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # 使用 Canny 边缘检测
    edges = cv2.Canny(gray, 50, 150)
     
    # 使用概率霍夫变换检测线段
    lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=100, maxLineGap=10)
     
    # 绘制检测到的线段
    if lines is not None:
        for line in lines:
            x1, y1, x2, y2 = line[0]
            cv2.line(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
     
    # 显示结果图像
    cv2.imshow('Detected Lines', image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    五、注意事项

  • 在调用 cv2.HoughLinesP 之前,确保输入图像是二值图像。
  • 调整 rho、theta、threshold、minLineLength 和 maxLineGap 参数以获得最佳的线段检测结果。
  • 概率霍夫变换比标准霍夫变换更快,因为它通过随机选择边缘点来减少计算量,并且只检查那些最有可能形成直线的点。
  • 如果图像中的线段非常复杂或噪声很多,可能需要结合其他图像处理方法(如预处理步骤中的滤波或形态学操作)来提高线段检测的准确性。
  • 7、参考

  • https://docs.opencv.org/5.x/d9/db0/tutorial_hough_lines.html

  • 更多有趣的代码示例,可参考【Programming】

    作者:bryant_meng

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python OpenCV中的霍夫线变换(Hough Line Transform)详解

    发表回复