【问题标题】:How to detect lines in OpenCV?如何检测 OpenCV 中的线条?
【发布时间】:2018-01-01 12:30:21
【问题描述】:

我正在尝试检测停车线,如下所示。

我希望得到的是交叉线上的清晰线条和 (x,y) 位置。但是,结果并不乐观。

我猜主要有两个原因:

  1. 有些线条非常破损或缺失。就连人眼也能清晰 识别它们。即使 HoughLine 可以帮助连接一些缺失的 线,因为 HoughLine 有时会连接不必要的线 一起,我宁愿手动做。

  2. 有一些重复的行。

工作的一般管道如下所示:

1。选择一些特定的颜色(白色或黄色)

import cv2
import numpy as np
import matplotlib
from matplotlib.pyplot import imshow
from matplotlib import pyplot as plt

# white color mask
img = cv2.imread(filein)
#converted = convert_hls(img)
image = cv2.cvtColor(img,cv2.COLOR_BGR2HLS)
lower = np.uint8([0, 200, 0])
upper = np.uint8([255, 255, 255])
white_mask = cv2.inRange(image, lower, upper)
# yellow color mask
lower = np.uint8([10, 0,   100])
upper = np.uint8([40, 255, 255])
yellow_mask = cv2.inRange(image, lower, upper)
# combine the mask
mask = cv2.bitwise_or(white_mask, yellow_mask)
result = img.copy()
cv2.imshow("mask",mask) 

2。重复膨胀和腐蚀,直到图像不能改变(reference

height,width = mask.shape
skel = np.zeros([height,width],dtype=np.uint8)      #[height,width,3]
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3,3))
temp_nonzero = np.count_nonzero(mask)
while(np.count_nonzero(mask) != 0 ):
    eroded = cv2.erode(mask,kernel)
    cv2.imshow("eroded",eroded)   
    temp = cv2.dilate(eroded,kernel)
    cv2.imshow("dilate",temp)
    temp = cv2.subtract(mask,temp)
    skel = cv2.bitwise_or(skel,temp)
    mask = eroded.copy()
 
cv2.imshow("skel",skel)
#cv2.waitKey(0)

3。应用 canny 过滤线条并使用 HoughLinesP 获取线条

edges = cv2.Canny(skel, 50, 150)
cv2.imshow("edges",edges)
lines = cv2.HoughLinesP(edges,1,np.pi/180,40,minLineLength=30,maxLineGap=30)
i = 0
for x1,y1,x2,y2 in lines[0]:
    i+=1
    cv2.line(result,(x1,y1),(x2,y2),(255,0,0),1)
print i

cv2.imshow("res",result)
cv2.waitKey(0)

我想知道为什么在选择某种颜色的第一步之后,线条会断裂并带有噪音。我认为在这一步中我们应该做一些事情来使虚线成为一条完整的、噪音较小的线。然后尝试应用一些东西来做 Canny 和 Hough 线。有什么想法吗?

【问题讨论】:

  • 不需要检测边缘,可以直接在二值图像上使用HoughLinesP
  • 哟,检查我更新的答案。我认为这有点符合您对交叉路口检测的要求。
  • 在主代码中,您只为黄线输入了 RGB 代码。此外,如果您将其编辑为白色,则可以得到结果。

标签: python opencv image-processing


【解决方案1】:

这是我的管道,也许它可以给你一些帮助。

首先,得到灰度图,处理GaussianBlur。

img = cv2.imread('src.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

kernel_size = 5
blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)

二、处理边缘检测使用Canny。

low_threshold = 50
high_threshold = 150
edges = cv2.Canny(blur_gray, low_threshold, high_threshold)

然后,使用 HoughLinesP 获取线条。您可以调整参数以获得更好的性能。

rho = 1  # distance resolution in pixels of the Hough grid
theta = np.pi / 180  # angular resolution in radians of the Hough grid
threshold = 15  # minimum number of votes (intersections in Hough grid cell)
min_line_length = 50  # minimum number of pixels making up a line
max_line_gap = 20  # maximum gap in pixels between connectable line segments
line_image = np.copy(img) * 0  # creating a blank to draw lines on

# Run Hough on edge detected image
# Output "lines" is an array containing endpoints of detected line segments
lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),
                    min_line_length, max_line_gap)

for line in lines:
    for x1,y1,x2,y2 in line:
    cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),5)

最后,在你的 srcImage 上画线。

# Draw the lines on the  image
lines_edges = cv2.addWeighted(img, 0.8, line_image, 1, 0)

这是我最后的表演。

最终图像:

【讨论】:

    【解决方案2】:

    我不确定您到底在问什么,因为您的帖子中没有问题。

    检测线段的一种很好且稳健的技术是 LSD(线段检测器),从 openCV 3 开始在 openCV 中可用。

    这里有一些简单的基本 C++ 代码,可以很容易地转换成 python:

    int main(int argc, char* argv[])
    {
        cv::Mat input = cv::imread("C:/StackOverflow/Input/parking.png");
        cv::Mat gray;
        cv::cvtColor(input, gray, CV_BGR2GRAY);
    
    
        cv::Ptr<cv::LineSegmentDetector> det;
        det = cv::createLineSegmentDetector();
    
    
    
        cv::Mat lines;
        det->detect(gray, lines);
    
        det->drawSegments(input, lines);
    
        cv::imshow("input", input);
        cv::waitKey(0);
        return 0;
    }
    

    给出这个结果:

    哪个看起来比您的图像更适合进一步处理(没有行重复等)

    【讨论】:

    • 过境点?也许您可以按角度和距离对线段进行分组(可能还包括 houghLines - 但然后再次处理分组的线段),如果您知道停车位标记的一般外观,您可以执行透视校正,这将使其更容易比较和分组线段。一般来说,应该有一些“格式塔理论”的方法,但 afaik 的研究还没有那么远......
    • 然而,LSDDetector 在 Python OpenCV 中已被弃用
    • 尚未测试,但 EDLines 可能也不错:github.com/CihanTopal/ED_Lib
    【解决方案3】:

    对于您的问题的第一部分,这里有一些很好的答案,但至于第二部分(查找线交叉点),我没有看到很多。

    我建议你看看Bentley-Ottmann 算法。

    算法herehere有一些python实现。

    编辑:使用 VeraPoseidon 的 Houghlines 实现和此处链接的第二个库,我设法获得了以下交叉点检测结果。感谢 Vera 和图书馆作者的出色工作。绿色方块代表检测到的交叉点。有一些错误,但这对我来说似乎是一个非常好的起点。似乎您实际想要检测交叉路口的大多数位置都检测到了多个交叉路口,因此您可能会在图像上运行一个适当大小的窗口,以查找多个交叉路口并将真正的交叉路口视为该窗口激活的交叉路口。

    这是我用来产生该结果的代码:

    import cv2
    import numpy as np
    import isect_segments_bentley_ottmann.poly_point_isect as bot
    
    
    img = cv2.imread('parking.png')
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    
    kernel_size = 5
    blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)
    
    low_threshold = 50
    high_threshold = 150
    edges = cv2.Canny(blur_gray, low_threshold, high_threshold)
    
    rho = 1  # distance resolution in pixels of the Hough grid
    theta = np.pi / 180  # angular resolution in radians of the Hough grid
    threshold = 15  # minimum number of votes (intersections in Hough grid cell)
    min_line_length = 50  # minimum number of pixels making up a line
    max_line_gap = 20  # maximum gap in pixels between connectable line segments
    line_image = np.copy(img) * 0  # creating a blank to draw lines on
    
    # Run Hough on edge detected image
    # Output "lines" is an array containing endpoints of detected line segments
    lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),
                        min_line_length, max_line_gap)
    print(lines)
    points = []
    for line in lines:
        for x1, y1, x2, y2 in line:
            points.append(((x1 + 0.0, y1 + 0.0), (x2 + 0.0, y2 + 0.0)))
            cv2.line(line_image, (x1, y1), (x2, y2), (255, 0, 0), 5)
    
    lines_edges = cv2.addWeighted(img, 0.8, line_image, 1, 0)
    print(lines_edges.shape)
    #cv2.imwrite('line_parking.png', lines_edges)
    
    print points
    intersections = bot.isect_segments(points)
    print intersections
    
    for inter in intersections:
        a, b = inter
        for i in range(3):
            for j in range(3):
                lines_edges[int(b) + i, int(a) + j] = [0, 255, 0]
    
    cv2.imwrite('line_parking.png', lines_edges)
    

    您可以使用类似以下代码块的策略来删除小区域内的多个交叉点:

    for idx, inter in enumerate(intersections):
        a, b = inter
        match = 0
        for other_inter in intersections[idx:]:
            if other_inter == inter:
                continue
            c, d = other_inter
            if abs(c-a) < 15 and abs(d-b) < 15:
                match = 1
                intersections[idx] = ((c+a)/2, (d+b)/2)
                intersections.remove(other_inter)
    
        if match == 0:
            intersections.remove(inter)
    

    输出图片:

    不过,您必须使用窗口功能。

    【讨论】:

    • 如何在节点js中做同样的过程
    • 如何导入 isect_segments_bentley_ottmann ?
    • 您可以下载此处链接的库并将其添加到您的项目中。从那里引用它。
    • 嘿嘿,删除重复交集的代码会删除最后一个交集,因为它会将交集与自身进行比较。在图片中,您可以看到最右下角的十字路口不存在。可通过if other_inter == inter: continue Cheers 修复。
    • 好收获!确保我将其编辑到正确的位置。
    【解决方案4】:

    如果调整 maxLineGap 或侵蚀内核的大小会发生什么。或者,您可以找到线之间的距离。您将不得不通过成对的行说 ax1,ay1 到 ax2,ay2 c.f. bx1,by1 到 bx2,by2 您可以找到与 a 成直角的梯度(线的梯度为 -1)与线 b 相交的点。基础学校几何和联立方程,例如:

    x = (ay1 - by1) / ((by2 - by1) / (bx2 - bx1) + (ax2 - ax1) / (ay2 - ay1))
    # then
    y = by1 + x * (by2 - by1) / (bx2 - bx1)
    

    并将 x,y 与 ax1,ay1 进行比较

    PS 您可能需要检查 ax1,ay1 和 bx1,by1 之间的距离,因为您的某些线看起来是其他线的延续,而这些线可能会被最近点技术消除。

    【讨论】:

      【解决方案5】:

      我是初学者。 我得到了一些可能对这个问题有帮助的东西。

      一种检测图像中线条的简单方法。

      output

      以下是在 google colab 中执行的代码

      import cv2
      import numpy as np
      from google.colab.patches import cv2_imshow
      !wget  https://i.stack.imgur.com/sDQLM.png
      #read image 
      image = cv2.imread( "/content/sDQLM.png")
      
      #convert to gray
      gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
      
      #performing binary thresholding
      kernel_size = 3
      ret,thresh = cv2.threshold(gray,200,255,cv2.THRESH_BINARY)  
      
      #finding contours 
      cnts = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
      cnts = cnts[0] if len(cnts) == 2 else cnts[1]
      
      #drawing Contours
      radius =2
      color = (30,255,50)
      cv2.drawContours(image, cnts, -1,color , radius)
      # cv2.imshow(image) commented as colab don't support cv2.imshow()
      cv2_imshow(image)
      # cv2.waitKey()
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-06-22
        • 2021-03-11
        • 2018-10-21
        • 1970-01-01
        • 2021-05-21
        • 1970-01-01
        • 2020-10-13
        相关资源
        最近更新 更多