如何在 HoughLinesP 之后合并行?

问题描述

我的任务是找到线(startX、startY、endX、endY)和矩形(4 线)的坐标.这是输入文件:

我使用下一个代码:

img = cv2.imread(image_src)灰色 = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)ret, thresh1 = cv2.threshold(灰色,127,255,cv2.THRESH_BINARY)边缘 = cv2.Canny(thresh1,50,150,apertureSize = 3)minLineLength = 100最大线间隙 = 10线 = cv2.HoughLinesP(edges,1,np.pi/180,10,minLineLength,maxLineGap)打印(长度(行))对于行中的行:cv2.line(img,(line[0][0],line[0][1]),(line[0][2],line[0][3]),(0,0,255),6)

我得到下一个结果:

从最后一张图片中,您可以看到大量的小红线.

问题:

  1. 合并小线条的最佳方法是什么?
  2. 为什么有很多HoughLinesP 检测不到的小部分?

解决方案

我终于完成了流水线:

  1. 修正了不正确的参数(Dan 建议)
  2. 开发了我自己的合并线段"算法.

    还有 572 行.在我的合并线段"之后,我们只有 89 行

    My task is to find coordinates of lines (startX, startY, endX, endY) and rectangles (4 lines). Here is input file:

    I use the next code:

    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)
    

    I get the next results:

    From the last image you can see big amount of small red lines.

    Questions:

    1. What is the best way to merge small lines?
    2. Why there are a lot of small portions that are not detected by HoughLinesP?

    解决方案

    I have finally completed the pipeline:

    1. fixed incorrect parameters (as were suggested by Dan)
    2. developed my own 'merging line segments' algorithm. I had bad results when I implemented TAVARES and PADILHA algorithm (as were suggested by Andrew).
    3. I have skipped Canny and got better results (as were suggested by Alexander)

    Please find the code and results:

    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://stackoverflow.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)
    

    There are still 572 lines. After my "merging line segments" we have only 89 lines

相关文章