【问题标题】:OpenCV line detection for 45 degree lines用于 45 度线的 OpenCV 线检测
【发布时间】:2019-04-28 20:12:04
【问题描述】:

我有一张图片:

在此图像中,OpenCV Hough 变换无法使用 -45 度大线检测

minLineLength = 150 
maxLineGap = 5 
line_thr = 150 
linesP = cv.HoughLinesP(dst, 1, np.pi / 180, line_thr, None, minLineLength, maxLineGap) 

找到的唯一行是:

我也尝试过使用各种阈值,但我在这里找不到这条线。

如果我像这样手动裁剪图像:

然后我可以清楚地看到 OpenCV Hough 变换找到了正确的线:

我想在非裁剪版本中找到同一行。对非裁剪版本有什么建议吗?

还可能存在根本没有线或线没有一直延伸到 X 轴长度的情况。 例子

【问题讨论】:

  • 如果你知道直线是 45 度角,为什么还要使用霍夫变换?您可以保持简单,只查找 45 度角的线。
  • Cris Luengo,任何想法如何实现,一些代码将不胜感激。我正在寻找大于某些单位的线长度,并且在某些噪声(黑点)之间可能存在。
  • 您可以使用过滤器并定义一个内核,该内核将平滑任何非 45 度方向的内容。这可以通过 Kirsch 算子或 Sobel 算子来完成。很可能有很多方法可以实现这一目标。检查stackoverflow.com/questions/8547127/…

标签: image opencv image-processing opencv3.0 hough-transform


【解决方案1】:

我在终端的命令行中使用 ImageMagick 执行此操作,但您可以使用 OpenCV 应用完全相同的技术。

第 1 步

拍摄图像并将其旋转 45 度,在需要的地方引入黑色像素作为背景:

convert 45.jpg -background black -rotate 45 result.png

第 2 步

现在,在前面的命令的基础上,将每个像素设置为以 1px 宽和 250px 高为中心的框的中位数:

convert 45.jpg -background black -rotate 45 -statistic median 1x250 result.png

第 3 步

现在,再次在上一个命令的基础上,将其旋转 45 度:

convert 45.jpg -background black -rotate 45 -statistic median 1x250 -rotate -45 result.png


所以,综上所述,整个处理是:

convert input.jpg -background black -rotate 45 -statistic median 1x250 -rotate -45 result.png

显然然后将其裁剪回原始大小并与原件并排附加以进行检查:

convert 45.jpg -background black -rotate 45 -statistic median 5x250 -rotate -45 +repage -gravity center -crop 184x866+0+0 result.png
convert 45.jpg result.png +append result.png 


您也可以使用mean 统计加阈值而不是median,因为它比排序找到中位数更快,但它往往会导致拖尾:

convert 45.jpg -background black -rotate 45 -statistic mean 1x250 result.png

您新添加的图像被处理成这个结果:

【讨论】:

    【解决方案2】:

    我实现了一个比我的其他答案稍微简单的算法,但这次是在 Python 中使用 OpenCV

    基本上,它不是取垂直像素列的平均值,而是对列中的像素求和并选择最亮的列。如果我在下面显示填充的、旋转的图像和另一个代表列总和的图像,您应该会看到它是如何工作的:

    #!/usr/bin/env python3
    
    import cv2
    import numpy as np
    
    # Load image as greyscale
    im = cv2.imread('45.jpg',cv2.IMREAD_GRAYSCALE)
    
    # Pad with border so it isn't cropped when rotated
    bw=300
    bordered = cv2.copyMakeBorder(im, top=bw, bottom=bw, left=bw, right=bw, borderType= cv2.BORDER_CONSTANT)
    
    # Rotate -45 degrees
    w, h = bordered.shape
    M = cv2.getRotationMatrix2D((h/2,w/2),-45,1)
    paddedrotated = cv2.warpAffine(bordered,M,(h,w))
    # DEBUG cv2.imwrite('1.tif',paddedrotated)
    
    # Sum the elements of each column and find column with most white pixels
    colsum = np.sum(paddedrotated,axis=0,dtype=np.float)
    col = np.argmax(colsum)
    # DEBUG cv2.imwrite('2.tif',colsum)
    
    # Fill with black except for the line we have located which we make white
    paddedrotated[:,:] = 0
    paddedrotated[:,col] = 255
    
    # Rotate back to straight
    w, h = paddedrotated.shape
    M = cv2.getRotationMatrix2D((h/2,w/2),45,1)
    straight = cv2.warpAffine(paddedrotated,M,(h,w))
    
    # Remove padding and save to disk
    straight = straight[bw:-bw,bw:-bw]
    cv2.imwrite('result.png',straight)
    

    请注意,您实际上不必将图像旋转回直线并将其裁剪回原始大小。你实际上可以在第一行之后停下来:

    col = np.argmax(colsum)
    

    并使用一些基本的三角函数来计算出原始图像中的含义。

    这是输出:


    关键词:线检测、检测线、旋转、填充、边框、投影、投影、图像、图像处理、Python、OpenCV、仿射、霍夫

    【讨论】:

    • 很棒的方法马克,但更通用的解决方案会很有帮助,因为我可以有完全没有线条的图像(只有一些白点),或者线条不沿着 x轴。在原始问题中添加了更多图片。
    • 我的其他答案适用于新图像。这个答案也适用于带有部分线条的新图像。如果您不希望它在图像中找到没有线条的任何内容,则需要考虑最小长度阈值并将其应用于代码为col = np.argmax(colsum)
    【解决方案3】:

    问题显然是您要搜索的行不是一行。它实际上看起来像一列相连的圆圈和盒子。因此,我建议您执行以下操作:

    使用查找轮廓查找图像中的所有轮廓

    img = cv.imread('image.jpg')
    img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    ret, thresh = cv.threshold(img_gray, 127, 255, 0)
    img2, contours, hierarchy = cv.findContours(thresh, CHAIN_APPROX_SIMPLE ,cv.RETR_EXTERNAL)
    

    这将返回许多轮廓,因此使用循环仅保存足够长的轮廓。由于图像大小为 814x1041 像素,我假设轮廓长,如果它至少是图像宽度的 10%,几乎是 100(你显然必须优化这个值)

    long_contours = []
    for contour in contours[i]:
        perimeter = cv2.arcLength(contour,True)
        if (perimeter > 0.1 * 1018)  # 10% of the image width
            long_contours.append(contour)
    

    现在围绕那些可能也是一条线的长轮廓绘制一个旋转的边界矩形。长轮廓如果宽度远大于高度,或者纵横比很大(比如8,还需要优化这个值),则认为是一条线

    for long_contour in long_contours:
        rect = cv2.minAreaRect(long_contour)
        aspec_ratio =  rect.width / rect.height
    
        if aspec_ratio > 8 : 
           box = cv2.boxPoints(rect)
           box = np.int0(box)
           cv2.drawContours(img,[box],0,(255,255,255),cv.FILLED)
    

    最后你应该得到类似的东西。请注意,此处的代码仅供参考。

    【讨论】:

    • 这是一个很好的观察。我希望你没有使用轮廓。这对我来说毫无意义。您可以使用连通分量分析更简单、更有效地完成此操作。轮廓完全被过度使用。仅当您特别需要对象的轮廓时才应提取轮廓。
    • 感谢您的提示。我同意使用连接的组件可能更容易、更有效。使用轮廓没有特别的优势,除了我在许多项目中重复使用它们。我以后会改的。
    【解决方案4】:

    您的原始代码很好。唯一的问题是您的图像包含太多信息,这会弄乱累加器分数。如果将行阈值增加到 255,一切都会好起来的。

    minLineLength = 150 
    maxLineGap = 5
    line_thr = 255
    linesP = cv2.HoughLinesP(dst, 1, np.pi / 180.0, line_thr, None, minLineLength, maxLineGap) 
    

    以下是使用该值的结果。 由于白色像素较大,此处检测到 3 行。

    [  1  41 286 326]
    [  0  42 208 250]
    [  1  42 286 327]
    

    由于与上述相同的原因,在同一区域周围检测到 5 条线。使用形态学操作或距离变换减小像素大小应该可以解决此问题。

    [110 392 121 598]
    [112 393 119 544]
    [141 567 147 416]
    [ 29 263  29 112]
    [  0  93 179 272]
    

    此处未找到行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-25
      • 2023-03-10
      • 2017-10-31
      • 2017-06-28
      • 1970-01-01
      • 2012-11-11
      • 1970-01-01
      相关资源
      最近更新 更多