如何在HoughLinesP之后合并行?

问题描述:

我的任务是找到坐标线(startX,startY,endX,endY)和矩形(4行)。下面是输入文件:enter image description here如何在HoughLinesP之后合并行?

我用下面的代码:

img = cv2.imread(image_src) 
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 
ret, thresh1 = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) 

edges = cv2.Canny(thresh1,50,150,apertureSize = 3) 

minLineLength = 100 
maxLineGap = 10 
lines = cv2.HoughLinesP(edges,1,np.pi/180,10,minLineLength,maxLineGap) 
print(len(lines)) 
for line in lines: 
    cv2.line(img,(line[0][0],line[0][1]),(line[0][2],line[0][3]),(0,0,255),6) 

我得到下一个结果: enter image description here enter image description here enter image description here

从最后一个图像,你可以看到小大量红线。

问题:

  1. 什么是合并小行的最好方法?
  2. 为什么有很多 小部分未被HoughLinesP检测到?
+2

我可以看到的一个问题是,你用不正确的参数调用'HoughLinesP'。看到[这个答案](https://*.com/questions/36452491/opencv-python-houghlinesp-strange-results)的解释。 –

+1

谷歌“合并线段”结果列表中的第一个:http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.217.9430&rep=rep1&type=pdf –

+1

你不需要做Canny edge检测。这会给你箭头的轮廓,而不是你已经拥有的箭头本身。稍微增加'rho'参数,因为它会使可允许的单行的宽度变大。当你说你想要一条线的坐标时,你想要什么?终点,或一行中每个像素的坐标? –

我终于完成了管道:

  1. 固定不正确的参数(如由丹建议)
  2. 发展出自己的“合流管线段”算法。 I implemented TAVARES and PADILHA algorithm(Andrew建议)时,我的结果很糟糕。
  3. 我跳过谨慎又得到了更好的结果(如由亚历山大的建议)

请找到的代码和结果:

def get_lines(lines_in): 
    if cv2.__version__ < '3.0': 
     return lines_in[0] 
    return [l[0] for l in lines_in] 


def process_lines(image_src): 
    img = mpimg.imread(image_src) 
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 

    ret, thresh1 = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) 

    thresh1 = cv2.bitwise_not(thresh1) 

    edges = cv2.Canny(thresh1, threshold1=50, threshold2=200, apertureSize = 3) 

    lines = cv2.HoughLinesP(thresh1, rho=1, theta=np.pi/180, threshold=50, 
          minLineLength=50, maxLineGap=30) 

    # l[0] - line; l[1] - angle 
    for line in get_lines(lines): 
     leftx, boty, rightx, topy = line 
     cv2.line(img, (leftx, boty), (rightx,topy), (0,0,255), 6) 

    # merge lines 

    #------------------ 
    # prepare 
    _lines = [] 
    for _line in get_lines(lines): 
     _lines.append([(_line[0], _line[1]),(_line[2], _line[3])]) 

    # sort 
    _lines_x = [] 
    _lines_y = [] 
    for line_i in _lines: 
     orientation_i = math.atan2((line_i[0][1]-line_i[1][1]),(line_i[0][0]-line_i[1][0])) 
     if (abs(math.degrees(orientation_i)) > 45) and abs(math.degrees(orientation_i)) < (90+45): 
      _lines_y.append(line_i) 
     else: 
      _lines_x.append(line_i) 

    _lines_x = sorted(_lines_x, key=lambda _line: _line[0][0]) 
    _lines_y = sorted(_lines_y, key=lambda _line: _line[0][1]) 

    merged_lines_x = merge_lines_pipeline_2(_lines_x) 
    merged_lines_y = merge_lines_pipeline_2(_lines_y) 

    merged_lines_all = [] 
    merged_lines_all.extend(merged_lines_x) 
    merged_lines_all.extend(merged_lines_y) 
    print("process groups lines", len(_lines), len(merged_lines_all)) 
    img_merged_lines = mpimg.imread(image_src) 
    for line in merged_lines_all: 
     cv2.line(img_merged_lines, (line[0][0], line[0][1]), (line[1][0],line[1][1]), (0,0,255), 6) 


    cv2.imwrite('prediction/lines_gray.jpg',gray) 
    cv2.imwrite('prediction/lines_thresh.jpg',thresh1) 
    cv2.imwrite('prediction/lines_edges.jpg',edges) 
    cv2.imwrite('prediction/lines_lines.jpg',img) 
    cv2.imwrite('prediction/merged_lines.jpg',img_merged_lines) 

    return merged_lines_all 

def merge_lines_pipeline_2(lines): 
    super_lines_final = [] 
    super_lines = [] 
    min_distance_to_merge = 30 
    min_angle_to_merge = 30 

    for line in lines: 
     create_new_group = True 
     group_updated = False 

     for group in super_lines: 
      for line2 in group: 
       if get_distance(line2, line) < min_distance_to_merge: 
        # check the angle between lines  
        orientation_i = math.atan2((line[0][1]-line[1][1]),(line[0][0]-line[1][0])) 
        orientation_j = math.atan2((line2[0][1]-line2[1][1]),(line2[0][0]-line2[1][0])) 

        if int(abs(abs(math.degrees(orientation_i)) - abs(math.degrees(orientation_j)))) < min_angle_to_merge: 
         #print("angles", orientation_i, orientation_j) 
         #print(int(abs(orientation_i - orientation_j))) 
         group.append(line) 

         create_new_group = False 
         group_updated = True 
         break 

      if group_updated: 
       break 

     if (create_new_group): 
      new_group = [] 
      new_group.append(line) 

      for idx, line2 in enumerate(lines): 
       # check the distance between lines 
       if get_distance(line2, line) < min_distance_to_merge: 
        # check the angle between lines  
        orientation_i = math.atan2((line[0][1]-line[1][1]),(line[0][0]-line[1][0])) 
        orientation_j = math.atan2((line2[0][1]-line2[1][1]),(line2[0][0]-line2[1][0])) 

        if int(abs(abs(math.degrees(orientation_i)) - abs(math.degrees(orientation_j)))) < min_angle_to_merge: 
         #print("angles", orientation_i, orientation_j) 
         #print(int(abs(orientation_i - orientation_j))) 

         new_group.append(line2) 

         # remove line from lines list 
         #lines[idx] = False 
      # append new group 
      super_lines.append(new_group) 


    for group in super_lines: 
     super_lines_final.append(merge_lines_segments1(group)) 

    return super_lines_final 

def merge_lines_segments1(lines, use_log=False): 
    if(len(lines) == 1): 
     return lines[0] 

    line_i = lines[0] 

    # orientation 
    orientation_i = math.atan2((line_i[0][1]-line_i[1][1]),(line_i[0][0]-line_i[1][0])) 

    points = [] 
    for line in lines: 
     points.append(line[0]) 
     points.append(line[1]) 

    if (abs(math.degrees(orientation_i)) > 45) and abs(math.degrees(orientation_i)) < (90+45): 

     #sort by y 
     points = sorted(points, key=lambda point: point[1]) 

     if use_log: 
      print("use y") 
    else: 

     #sort by x 
     points = sorted(points, key=lambda point: point[0]) 

     if use_log: 
      print("use x") 

    return [points[0], points[len(points)-1]] 

# https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.cdist.html 
# https://*.com/questions/32702075/what-would-be-the-fastest-way-to-find-the-maximum-of-all-possible-distances-betw 
def lines_close(line1, line2): 
    dist1 = math.hypot(line1[0][0] - line2[0][0], line1[0][0] - line2[0][1]) 
    dist2 = math.hypot(line1[0][2] - line2[0][0], line1[0][3] - line2[0][1]) 
    dist3 = math.hypot(line1[0][0] - line2[0][2], line1[0][0] - line2[0][3]) 
    dist4 = math.hypot(line1[0][2] - line2[0][2], line1[0][3] - line2[0][3]) 

    if (min(dist1,dist2,dist3,dist4) < 100): 
     return True 
    else: 
     return False 

def lineMagnitude (x1, y1, x2, y2): 
    lineMagnitude = math.sqrt(math.pow((x2 - x1), 2)+ math.pow((y2 - y1), 2)) 
    return lineMagnitude 

#Calc minimum distance from a point and a line segment (i.e. consecutive vertices in a polyline). 
# https://nodedangles.wordpress.com/2010/05/16/measuring-distance-from-a-point-to-a-line-segment/ 
# http://paulbourke.net/geometry/pointlineplane/ 
def DistancePointLine(px, py, x1, y1, x2, y2): 
    #http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/source.vba 
    LineMag = lineMagnitude(x1, y1, x2, y2) 

    if LineMag < 0.00000001: 
     DistancePointLine = 9999 
     return DistancePointLine 

    u1 = (((px - x1) * (x2 - x1)) + ((py - y1) * (y2 - y1))) 
    u = u1/(LineMag * LineMag) 

    if (u < 0.00001) or (u > 1): 
     #// closest point does not fall within the line segment, take the shorter distance 
     #// to an endpoint 
     ix = lineMagnitude(px, py, x1, y1) 
     iy = lineMagnitude(px, py, x2, y2) 
     if ix > iy: 
      DistancePointLine = iy 
     else: 
      DistancePointLine = ix 
    else: 
     # Intersecting point is on the line, use the formula 
     ix = x1 + u * (x2 - x1) 
     iy = y1 + u * (y2 - y1) 
     DistancePointLine = lineMagnitude(px, py, ix, iy) 

    return DistancePointLine 

def get_distance(line1, line2): 
    dist1 = DistancePointLine(line1[0][0], line1[0][1], 
           line2[0][0], line2[0][1], line2[1][0], line2[1][1]) 
    dist2 = DistancePointLine(line1[1][0], line1[1][1], 
           line2[0][0], line2[0][1], line2[1][0], line2[1][1]) 
    dist3 = DistancePointLine(line2[0][0], line2[0][1], 
           line1[0][0], line1[0][1], line1[1][0], line1[1][1]) 
    dist4 = DistancePointLine(line2[1][0], line2[1][1], 
           line1[0][0], line1[0][1], line1[1][0], line1[1][1]) 


    return min(dist1,dist2,dist3,dist4) 

enter image description here

仍然有572线。我的“合并线段”后,我们只有89行 enter image description here