zoukankan      html  css  js  c++  java
  • OpenCVPython系列之霍夫线变换

    本次我们来看OpenCV中的霍夫线变换,它可以用于检测图像中的直线进而标注出来。

    基本原理

    一条直线可由两个点A=(X1,Y1)和B=(X2,Y2)确定(笛卡尔坐标):

    image.png

    另一方面,image.png也可以写成关于(k,q)的函数表达式(霍夫空间):

    image.png

    对应的变换可以通过图形直观表示:

    image.png

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

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

    image.png

    再来看看A、B两个点,对应霍夫空间的情形:

    image.png

    一步步来,再看一下三个点共线的情况:

    image.png

    可以看出如果笛卡尔坐标系的点共线,这些点在霍夫空间对应的直线交于一点:这也是必然,共线只有一种取值可能。

    如果不止一条直线呢?再看看多个点的情况(有两条直线):

    image.png

    其实(3,2)与(4,1)也可以组成直线,只不过它有两个点确定,而图中A、B两点是由三条直线汇成,这也是霍夫变换的后处理的基本方式选择由尽可能多直线汇成的点

    看看,霍夫空间:选择由三条交汇直线确定的点(中间图),对应的笛卡尔坐标系的直线(右图)。

    image.png

    到这里问题似乎解决了,已经完成了霍夫变换的求解,但是如果像下图这种情况呢?

    image.png

    k=∞是不方便表示的,而且q怎么取值呢,这样不是办法。因此考虑将笛卡尔坐标系换为:极坐标表示

    image.png

    在极坐标系下,其实是一样的:极坐标的点→霍夫空间的直线,只不过霍夫空间不再是[k,q]的参数,而是image.png的参数,给出对比图:

    image.png

    我们来看霍夫变换的算法步骤:

    image.png

    OpenCV中的霍夫变换

    在OpenCV中,我们可以使用相应的函数API,先来看函数原型:
    lines=cv.HoughLines(image, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]])

    第一个参数,输入图像应该是一个二值图像,因此在应用hough变换之前应用阈值或使用Canny边缘检测.

    第二和第三个参数分别是ρ和θ的精度.

    第四个参数是阈值,这意味着它应该被视为一条直线.

    记住,线条的数量取决于直线上的点的数量,所以它表示应该检测到的最小长度。

    我们来看代码:

    def Hough(img):
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        edges = cv2.Canny(gray, 50, 150, apertureSize=3)
    
        lines = cv2.HoughLines(edges, 1, np.pi / 180, 90)
        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(img, (x1, y1), (x2, y2), (0, 0, 255), 1)
    
        cv2.imshow('show', img)
        cv2.waitKey()

    image.png

    可以看到,线条被检测出来,不过精度并不好,接下来我们介绍另一种直线检测。

    概率霍夫线变换

    上面介绍的标准霍夫变换,其本质上就是把图像中的边缘像素映射到它的霍夫空间,比如一共有M个边缘像素,则所有的边缘像素都需要进行映射,其运算量和所需内存都会很大。

    如果只处理图像的m(m<M)个边缘像素点,则这m个边缘像素点的选取是具有一定概率的,因此该方法就是概率霍夫变换。该方法有一个重要的特点就是能够检测出线段,即能够检测出图像中直线的两个端点,从而定位图像中的直线。

    下面是概率霍夫变换的简易步骤:

    1. 随机抽取图像中的一个边缘像素点,如果已经被标定为是某一条直线上的点,则继续在剩下的边缘点中随机抽取一个边缘点,直到所有边缘点都抽取完为止;

    2. 对该点进行霍夫变换,并进行累加计算;

    3. 选取在霍夫空间内累加值最大的点,如果该点的值大于阈值,则进行步骤4,否则回到步骤1;

    4. 对于累加值大于阈值的点,从该点出发,沿着图像中的直线的方向位移,从而找到直线的两个端点;

    5. 计算直线的长度,如果大于某个阈值,则被认为是直线并输出。

    OpenCV提供了函数API:

    lines=cv.HoughLinesP(image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]])

    其参数跟上面的一样,这里不再过多赘述,函数cv2.HoughLinesP()是一种概率直线检测,我们知道,原理上讲hough变换是一个耗时耗力的算法,尤其是每一个点计算,即使经过了canny转换了有的时候点的个数依然是庞大的,这个时候我们采取一种概率挑选机制,不是所有的点都计算,而是随机的选取一些个点来计算,相当于降采样了。这样的话我们的阈值设置上也要降低一些。在参数输入输出上,输入不过多了两个参数:minLineLengh(线的最短长度,比这个短的都被忽略)和MaxLineCap(两条直线之间的最大间隔,小于此值,认为是一条直线)。输出上也变了,不再是直线参数的,这个函数输出的直接就是直线点的坐标位置,这样可以省去一系列for循环中的由参数空间到图像的实际坐标点的转换。

    我们来看代码:

    def HoughP(img):
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        edges = cv2.Canny(gray, 50, 150, apertureSize=3)
        lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=90, maxLineGap=10)
        for line in lines:
            x1, y1, x2, y2 = line[0]
            cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 1)
    
        cv2.imshow('show', img)
        cv2.waitKey()

    image.png

    可以看到,这个直线检测是比之前的要好很多的,我们将在下一次中介绍霍夫圆变换,用途更为广泛。

  • 相关阅读:
    wireshark如何抓取本机包
    模拟post请求方法
    Spring Boot中使用RabbitMQ
    Dubbo注册中心的四种配置方式详解
    spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情
    zookeeper 大量连接断开重连原因排查
    分布式一致性协议之:Gossip(八卦)算法
    MongoDB分析工具之一:explain()语句分析工具
    MongoDB分析工具之二:MongoDB分析器Profile
    MySQL安装
  • 原文地址:https://www.cnblogs.com/wuyuan2011woaini/p/15659862.html
Copyright © 2011-2022 走看看