Python OpenCV HoughLinesP无法检测行

时间:2015-11-21 00:15:26

标签: python opencv computer-vision houghlinesp

我正在使用OpenCV HoughlinesP来查找水平和垂直线条。它大部分时间都没有找到任何行。即使它找到了一条线,它甚至都不接近实际图像。

import cv2
import numpy as np

img = cv2.imread('image_with_edges.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)


flag,b = cv2.threshold(gray,0,255,cv2.THRESH_OTSU)

element = cv2.getStructuringElement(cv2.MORPH_CROSS,(1,1))
cv2.erode(b,element)

edges = cv2.Canny(b,10,100,apertureSize = 3)

lines = cv2.HoughLinesP(edges,1,np.pi/2,275, minLineLength = 100, maxLineGap = 200)[0].tolist()

for x1,y1,x2,y2 in lines:
   for index, (x3,y3,x4,y4) in enumerate(lines):

    if y1==y2 and y3==y4: # Horizontal Lines
        diff = abs(y1-y3)
    elif x1==x2 and x3==x4: # Vertical Lines
        diff = abs(x1-x3)
    else:
        diff = 0

    if diff < 10 and diff is not 0:
        del lines[index]

    gridsize = (len(lines) - 2) / 2

   cv2.line(img,(x1,y1),(x2,y2),(0,0,255),2)
   cv2.imwrite('houghlines3.jpg',img)

输入图片: input image

输出图像:(见红线): enter image description here

@ljetibo试试这个: c_6.jpg

1 个答案:

答案 0 :(得分:11)

这里有点不对,所以我只是从头开始。

好的,打开图像后首先要做的是阈值处理。我强烈建议您再次查看tresholding上的OpenCV手册以及treshold methods的确切含义。

手册提到

  

cv2.threshold(src,thresh,maxval,type [,dst])→retval,dst

     

特殊值THRESH_OTSU可以与上述之一组合   值。在这种情况下,该函数确定最佳阈值   使用Otsu算法的值并使用它而不是指定的   打谷。

我知道它有点令人困惑,因为你不会将 THRESH_OTSU与其他任何方法(THRESH_BINARY等......)结合起来,不幸的是,手册可能就像那样。这种方法实际上做的是假设有一个&#34;前景&#34;和&#34;背景&#34;遵循双模式直方图然后应用THRESH_BINARY我相信。

想象一下,好像你在中午拍摄了一座大教堂或一座高楼的照片。在阳光明媚的日子,天空将非常明亮和蓝色,大教堂/建筑将会更加暗。这意味着属于天空的像素组将具有高亮度值,即将位于直方图的右侧,并且属于教堂的像素将更暗,即位于中间和左侧的像素。直方图。

Otsu使用它来尝试猜测正确的&#34;截止&#34;点,称为打谷。为了您的形象大津的阿尔格。假设地图一侧的所有白色都是背景,而地图本身就是前景。因此,阈值处理后的图像如下所示:

Image after OP's thresholding

在此之后,不难猜出出了什么问题。但是,让我们继续吧,我相信,你想要实现的是这样的事情:

flag,b = cv2.threshold(gray,160,255,cv2.THRESH_BINARY)

Image with manually guessed threshold.

然后你继续,并试图侵蚀图像。我不确定你为什么这样做,你的意图是&#34;大胆&#34;这些线条,或者是你打算消除噪音。在任何情况下,你从未将腐蚀的结果分配给某些东西。 Numpy数组,这是图像的表示方式,是可变的,但它不是语法的工作方式:

cv2.erode(src, kernel, [optionalOptions] ) → dst

所以你必须写:

b = cv2.erode(b,element)

好的,现在对于元素以及侵蚀是如何起作用的。侵蚀在图像上拖动内核。内核是一个简单的矩阵,其中有1&0; s和0&#39; s。该矩阵的一个元素,通常是中心元素,称为锚。锚是在操作结束时将被替换的元素。当你创建

cv2.getStructuringElement(cv2.MORPH_CROSS, (1, 1))

您创建的实际上是1x1矩阵(1列,1行)。这使侵蚀完全无用。

侵蚀是什么,首先从原始图像中检索像素亮度的所有值,其中与图像片段重叠的核心元素具有&#34; 1&#34;。然后它找到检索到的像素的最小值,并用该值替换锚点。

在您的情况下,这意味着您将[1]矩阵拖到图像上,比较源图像像素亮度是否大于,等于或小于自身,然后将其替换为自身。

如果您打算删除&#34; noise&#34;,那么在图像上使用矩形内核可能会更好。可以这样想,&#34;噪音&#34;那是&#34;不适合&#34;与周围的环境。因此,如果您将中心像素与其周围环境进行比较,并且发现它不适合,那么它很可能是噪音。

此外,我已经说它用内核检索到的最小值替换了锚点。在数值上,最小值为0,这恰好是图像中黑色的表示方式。这意味着,在您的白色图像占主导地位的情况下,侵蚀会“膨胀”#34;黑色像素。如果它们在内核的范围内,则侵蚀将用具有0值黑色像素的255值白色像素替换。在任何情况下,它都不应该是形状(1,1)。

>>> cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
array([[0, 1, 0],
       [1, 1, 1],
       [0, 1, 0]], dtype=uint8)

如果我们用3x3矩形内核侵蚀第二个图像,我们会得到下面的图像。

Eroded threshed image.

好的,现在我们已经解决了这个问题,接下来你要做的就是使用Canny边缘检测找到边缘。你从中获得的图像是:

Canny edges

好的,现在我们寻找完全垂直和完全水平线。当然,除了图像左侧的子午线之外没有这样的线条(这就是所谓的吗?)和你做完右后的最终图像是这样的:

enter image description here

既然你从来没有描述过你的确切想法,我最好的猜测是你需要平行线和经线,你会在规模较小的地图上获得更多的运气,因为那些线条不是开头的,他们是曲线。另外,有没有特定的理由去做概率霍夫? &#34;常规&#34;霍夫不够吗?

很抱歉这个帖子太长了,希望有点帮助。

此处的文字被添加为11月24日OP的澄清请求。因为没有办法让答案符合char限制评论。

我建议OP提出一个更具体的问题来检测曲线因为你正在处理曲线op,而不是水平和垂直线。< / p>

有几种方法可以检测曲线,但没有一种方法很容易。按照从最简单到最难实施的顺序:

  1. 使用RANSAC算法。制定一个描述长期性质的公式。和拉特。线条取决于相关地图。即当您在赤道附近时,纬度曲线几乎是地图上的完美​​直线,赤道是完美的直线,但是当您在高纬度地区时,它将非常弯曲,类似于圆弧段(靠近两极)。 SciPy已经将RANSAC作为一个类实现了你所要做的就是找到并以编程方式定义你想要尝试适应曲线的模型。当然,这是一个永远有用的4dummies文本here。这是最简单的,因为你所要做的就是数学。
  2. 有点难做的是创建一个矩形网格,然后尝试使用cv findHomography将网格扭曲到图像上的位置。对于您可以对网格进行的各种几何变换,您可以查看OpenCv manual。这是一种黑客攻击的方法,可能比1更糟糕。因为它取决于你可以重新创建一个网格,其中有足够的细节和对象,可以识别图像上的结构。试图扭曲它。这个要求你做类似的数学运算1.只需要一些编码就可以从几个不同的函数中组成最终解决方案。
  3. 实际上这样做。有一些数学上简洁的方法可以将曲线描述为曲线上的切线列表。您可以尝试将一堆较短的HoughLine拟合到图像或图像片段,然后尝试对所有找到的线进行分组,并通过假设它们与曲线相切来确定它们是否真的遵循所需形状的曲线或者他们是随机的。有关此问题,请参阅this paper。在所有方法中,这个方法最难,因为它需要相当多的独奏编码和一些关于该方法的数学。
  4. 可能有更简单的方法,我以前从未真正处理过曲线检测。也许有一些技巧可以让你更轻松,我不知道。如果你问一个新的问题,那个尚未被关闭的问题你可能会有更多人注意到它。请确保就您感兴趣的确切主题提出完整而完整的问题。人们通常花费大量时间撰写如此广泛的主题。

    为了向您展示如何使用Hough变换,请查看下面的内容:

    import cv2
    import numpy as np
    
    def draw_lines(hough, image, nlines):
       n_x, n_y=image.shape
       #convert to color image so that you can see the lines
       draw_im = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
    
       for (rho, theta) in hough[0][:nlines]:
          try:
             x0 = np.cos(theta)*rho
             y0 = np.sin(theta)*rho
             pt1 = ( int(x0 + (n_x+n_y)*(-np.sin(theta))),
                     int(y0 + (n_x+n_y)*np.cos(theta)) )
             pt2 = ( int(x0 - (n_x+n_y)*(-np.sin(theta))),
                     int(y0 - (n_x+n_y)*np.cos(theta)) )
             alph = np.arctan( (pt2[1]-pt1[1])/( pt2[0]-pt1[0]) )
             alphdeg = alph*180/np.pi
             #OpenCv uses weird angle system, see: http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_houghlines/py_houghlines.html
             if abs( np.cos( alph - 180 )) > 0.8: #0.995:
                cv2.line(draw_im, pt1, pt2, (255,0,0), 2)
             if rho>0 and abs( np.cos( alphdeg - 90)) > 0.7:
                cv2.line(draw_im, pt1, pt2, (0,0,255), 2)    
          except:
             pass
       cv2.imwrite("/home/dino/Desktop/3HoughLines.png", draw_im,
                 [cv2.IMWRITE_PNG_COMPRESSION, 12])   
    
    img = cv2.imread('a.jpg')
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    
    flag,b = cv2.threshold(gray,160,255,cv2.THRESH_BINARY)
    cv2.imwrite("1tresh.jpg", b)
    
    element = np.ones((3,3))
    b = cv2.erode(b,element)
    cv2.imwrite("2erodedtresh.jpg", b)
    
    edges = cv2.Canny(b,10,100,apertureSize = 3)
    cv2.imwrite("3Canny.jpg", edges)
    
    hough = cv2.HoughLines(edges, 1, np.pi/180, 200)   
    draw_lines(hough, b, 100)
    

    从下图中可以看出,直线只是经度。纬度不是那么直,因此对于每个纬度,您有几条检测到的线条就像线上的切线一样。蓝色绘制的线条由if abs( np.cos( alph - 180 )) > 0.8:绘制,而红色绘制的线条由rho>0 and abs( np.cos( alphdeg - 90)) > 0.7条件绘制。将原始图像与绘制在其上的线条的图像进行比较时要特别注意。相似之处是不可思议的(嘿,得到它?)但是因为它们并不是很多,所以它看起来像垃圾。 (特别是检测到的最高纬度线看起来像它也是#34;角度&#34;但实际上这些线在其最厚点上与纬度线形成完美切线,就像霍夫算法所要求的那样)。承认使用线检测算法检测曲线存在限制

    Best possible detected lines.