Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何在HoughLinesP之后合并线?_Python_Opencv_Computer Vision_Hough Transform_Cv2 - Fatal编程技术网

Python 如何在HoughLinesP之后合并线?

Python 如何在HoughLinesP之后合并线?,python,opencv,computer-vision,hough-transform,cv2,Python,Opencv,Computer Vision,Hough Transform,Cv2,我的任务是找到直线(startX、startY、endX、endY)和矩形(4条直线)的坐标。以下是输入文件: 我使用下一个代码: 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)

我的任务是找到直线(startX、startY、endX、endY)和矩形(4条直线)的坐标。以下是输入文件:

我使用下一个代码:

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)
我得到下一个结果:

从上一张图片中,你可以看到大量的小红线

问题:

  • 合并小线条的最佳方法是什么
  • 为什么有很多 HoughLinesP未检测到的小部分

  • 我终于完成了管道:

  • 修正了不正确的参数(如Dan所建议的)
  • 开发了我自己的“合并线段”算法。正如安德鲁所建议的那样,我的成绩很差
  • 我跳过了Canny,得到了更好的结果(正如Alexander所建议的那样)
  • 请查找代码和结果:

    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)
    
    def get_行(行中):
    如果cv2.版本<'3.0':
    [0]中的返回行\u
    返回[l[0]表示行中的l\u in]
    def处理线(图像src):
    img=mpimg.imread(图像\u src)
    灰色=cv2.CVT颜色(img,cv2.COLOR\U BGR2GRAY)
    ret,thresh1=cv2.threshold(灰色,127255,cv2.THRESH_二进制)
    thresh1=cv2。按位_非(thresh1)
    边缘=cv2.Canny(阈值1,阈值1=50,阈值2=200,光圈大小=3)
    lines=cv2.HoughLinesP(阈值1,ρ=1,θ=np.pi/180,阈值=50,
    minLineLength=50,maxLineGap=30)
    #l[0]-线;l[1]-角度
    对于get_行中的行(行):
    leftx,boty,rightx,topy=直线
    cv2.线(img,(左X,底部),(右X,顶部),(0,0255),6)
    #合并行
    #------------------
    #预备
    _行=[]
    对于get_行(行)中的_行:
    _行。追加([([行[0],[行[1]),([行[2],[行[3]))
    #分类
    _行_x=[]
    _行_y=[]
    对于第i行中的第i行:
    方向i=math.atan2((行i[0][1]-行i[1][1]),(行i[0][0]-行i[1][0]))
    如果(abs(数学度数(方向i))>45)和abs(数学度数(方向i))<(90+45):
    _第y行追加(第i行)
    其他:
    _行\u x.追加(行\u i)
    _行\u x=已排序(\u行\u x,key=lambda\u行:\u行[0][0])
    _行\u y=已排序(\u行\u y,key=lambda\u行:\u行[0][1])
    合并的管线=合并管线2(管线)
    合并的线\u y=合并线\u管道\u 2(\u线\u y)
    合并的\u行\u全部=[]
    合并线\u全部。扩展(合并线\u x)
    合并线\u全部。扩展(合并线\u y)
    打印(“处理组行”、len(_行)、len(合并的_行和全部))
    img\u merged\u line=mpimg.imread(image\u src)
    对于合并的\u行\u全部中的行:
    cv2.行(img_合并_行,(行[0][0],行[0][1]),(行[1][0],行[1][1]),(0,0255),6)
    cv2.imwrite('prediction/lines_gray.jpg',gray)
    cv2.imwrite('prediction/lines\u thresh.jpg',thresh1)
    cv2.imwrite('prediction/lines\u edges.jpg',edges)
    cv2.imwrite('prediction/lines\u lines.jpg',img)
    cv2.imwrite('prediction/merged_line.jpg',img_merged_line)
    返回合并的\u行\u全部
    def合并管路管路2(管路):
    超级线路\最终=[]
    超级线=[]
    最小距离合并=30
    最小角度到合并=30
    对于行中的行:
    创建新组=真
    组更新=错误
    对于super_行中的组:
    对于组中的第2行:
    如果获取距离(第2行,第2行)45)和abs(数学度数(方向i))<(90+45):
    #按y排序
    点=已排序(点,键=lambda点:点[1])
    如果使用日志:
    打印(“使用y”)
    其他:
    #按x排序
    点=已排序(点,键=lambda点:点[0])
    如果使用日志:
    打印(“使用x”)
    返回[点[0],点[len(点)-1]]
    # https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.cdist.ht
    
    class HoughBundler:
        '''Clasterize and merge each cluster of cv2.HoughLinesP() output
        a = HoughBundler()
        foo = a.process_lines(houghP_lines, binary_image)
        '''
    
        def get_orientation(self, line):
            '''get orientation of a line, using its length
            https://en.wikipedia.org/wiki/Atan2
            '''
            orientation = math.atan2(abs((line[0] - line[2])), abs((line[1] - line[3])))
            return math.degrees(orientation)
    
        def checker(self, line_new, groups, min_distance_to_merge, min_angle_to_merge):
            '''Check if line have enough distance and angle to be count as similar
            '''
            for group in groups:
                # walk through existing line groups
                for line_old in group:
                    # check distance
                    if self.get_distance(line_old, line_new) < min_distance_to_merge:
                        # check the angle between lines
                        orientation_new = self.get_orientation(line_new)
                        orientation_old = self.get_orientation(line_old)
                        # if all is ok -- line is similar to others in group
                        if abs(orientation_new - orientation_old) < min_angle_to_merge:
                            group.append(line_new)
                            return False
            # if it is totally different line
            return True
    
        def DistancePointLine(self, point, line):
            """Get distance between point and line
            http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/source.vba
            """
            px, py = point
            x1, y1, x2, y2 = line
    
            def lineMagnitude(x1, y1, x2, y2):
                'Get line (aka vector) length'
                lineMagnitude = math.sqrt(math.pow((x2 - x1), 2) + math.pow((y2 - y1), 2))
                return lineMagnitude
    
            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(self, a_line, b_line):
            """Get all possible distances between each dot of two lines and second line
            return the shortest
            """
            dist1 = self.DistancePointLine(a_line[:2], b_line)
            dist2 = self.DistancePointLine(a_line[2:], b_line)
            dist3 = self.DistancePointLine(b_line[:2], a_line)
            dist4 = self.DistancePointLine(b_line[2:], a_line)
    
            return min(dist1, dist2, dist3, dist4)
    
        def merge_lines_pipeline_2(self, lines):
            'Clusterize (group) lines'
            groups = []  # all lines groups are here
            # Parameters to play with
            min_distance_to_merge = 30
            min_angle_to_merge = 30
            # first line will create new group every time
            groups.append([lines[0]])
            # if line is different from existing gropus, create a new group
            for line_new in lines[1:]:
                if self.checker(line_new, groups, min_distance_to_merge, min_angle_to_merge):
                    groups.append([line_new])
    
            return groups
    
        def merge_lines_segments1(self, lines):
            """Sort lines cluster and return first and last coordinates
            """
            orientation = self.get_orientation(lines[0])
    
            # special case
            if(len(lines) == 1):
                return [lines[0][:2], lines[0][2:]]
    
            # [[1,2,3,4],[]] to [[1,2],[3,4],[],[]]
            points = []
            for line in lines:
                points.append(line[:2])
                points.append(line[2:])
            # if vertical
            if 45 < orientation < 135:
                #sort by y
                points = sorted(points, key=lambda point: point[1])
            else:
                #sort by x
                points = sorted(points, key=lambda point: point[0])
    
            # return first and last point in sorted group
            # [[x,y],[x,y]]
            return [points[0], points[-1]]
    
        def process_lines(self, lines, img):
            '''Main function for lines from cv.HoughLinesP() output merging
            for OpenCV 3
            lines -- cv.HoughLinesP() output
            img -- binary image
            '''
            lines_x = []
            lines_y = []
            # for every line of cv2.HoughLinesP()
            for line_i in [l[0] for l in lines]:
                    orientation = self.get_orientation(line_i)
                    # if vertical
                    if 45 < orientation < 135:
                        lines_y.append(line_i)
                    else:
                        lines_x.append(line_i)
    
            lines_y = sorted(lines_y, key=lambda line: line[1])
            lines_x = sorted(lines_x, key=lambda line: line[0])
            merged_lines_all = []
    
            # for each cluster in vertical and horizantal lines leave only one line
            for i in [lines_x, lines_y]:
                    if len(i) > 0:
                        groups = self.merge_lines_pipeline_2(i)
                        merged_lines = []
                        for group in groups:
                            merged_lines.append(self.merge_lines_segments1(group))
    
                        merged_lines_all.extend(merged_lines)
    
            return merged_lines_all
    
    def distance_to_line(self, point, line):
        """Get distance between point and line
        https://stackoverflow.com/questions/40970478/python-3-5-2-distance-from-a-point-to-a-line
        """
        px, py = point
        x1, y1, x2, y2 = line
        x_diff = x2 - x1
        y_diff = y2 - y1
        num = abs(y_diff * px - x_diff * py + x2 * y1 - y2 * x1)
        den = math.sqrt(y_diff**2 + x_diff**2)
        return num / den
    
    def get_distance(self, a_line, b_line):
        """Get all possible distances between each dot of two lines and second line
        return the shortest
        """
        dist1 = self.distance_to_line(a_line[:2], b_line)
        dist2 = self.distance_to_line(a_line[2:], b_line)
        dist3 = self.distance_to_line(b_line[:2], a_line)
        dist4 = self.distance_to_line(b_line[2:], a_line)
    
        return min(dist1, dist2, dist3, dist4)
    
    import numpy as np
    import math
    
    class Line:
        def __init__(self, x1, y1, x2, y2):
            if x1 < x2:
                self.x1 = x1
                self.x2 = x2
                self.y1 = y1
                self.y2 = y2
            else:
                self.x1 = x2
                self.x2 = x1
                self.y1 = y2
                self.y2 = y1
            dx = self.x2 - self.x1
            if dx == 0:
                dx = 0.000000000000000001
            dy = self.y2 - self.y1
            m = dy / dx
            self.theta = np.arctan(m)
            if self.theta < 0:
                self.theta = 2 * np.pi + self.theta
            self.rho = np.abs(m * self.x1 - self.y1) / np.sqrt(1 + m * m)
            self.length = math.sqrt(dx * dx + dy * dy)
    
        def point1(self):
            return self.x1, self.y1
    
        def point2(self):
            return self.x2, self.y2
    
    
    class LineMerger:
        def __init__(self):
            self.THETA_THRESHOLD = np.pi / 36
            self.MAX_DISTANCE = 5
            pass
    
        def merge_lines(self, line1, line2):
            theta_r = (line1.theta * line1.length + line2.theta * line2.length) / (line1.length + line2.length)
            if np.average([abs(theta_r - line1.theta), abs(theta_r - line2.theta)]) < self.THETA_THRESHOLD:
                # get gradients
                m = math.tan(theta_r)
                if m == 0:
                    m = 0.00000000000001
                md = 1 / m
                # find center of gravity
                cx = ((line1.x1 + line1.x2) * line1.length + (line2.x1 + line2.x2) * line2.length) * 0.5 / (
                        line1.length + line2.length)
                cy = ((line1.y1 + line1.y2) * line1.length + (line2.y1 + line2.y2) * line2.length) * 0.5 / (
                        line1.length + line2.length)
                # find projection points
                # x, y, d
                r0 = self.get_projection_point(line1.point1(), (cx, cy), m, md)
                r1 = self.get_projection_point(line1.point2(), (cx, cy), m, md)
                r2 = self.get_projection_point(line2.point1(), (cx, cy), m, md)
                r3 = self.get_projection_point(line2.point2(), (cx, cy), m, md)
                l0 = self.get_distance(r0[:2], r2[:2])
                l1 = self.get_distance(r0[:2], r3[:2])
                l2 = self.get_distance(r1[:2], r2[:2])
                l3 = self.get_distance(r1[:2], r3[:2])
                l4 = line1.length
                l5 = line2.length
                max_len_index = np.argmax([l0, l1, l2, l3, l4, l5])
                max_len = np.max([l0, l1, l2, l3, l4, l5])
                if max_len - (line1.length + line2.length) < self.MAX_DISTANCE:
                    point1 = None
                    point2 = None
                    if max_len_index == 0:
                        point1, point2 = r0[:2], r2[:2]
                    elif max_len_index == 1:
                        point1, point2 = r0[:2], r3[:2]
                    elif max_len_index == 2:
                        point1, point2 = r1[:2], r2[:2]
                    elif max_len_index == 3:
                        point1, point2 = r1[:2], r3[:2]
                    elif max_len_index == 4:
                        point1, point2 = r0[:2], r1[:2]
                    elif max_len_index == 5:
                        point1, point2 = r2[:2], r3[:2]
                    if point1 and point2:
                        x1, y1 = point1
                        x2, y2 = point2
                        return int(x1), int(y1), int(x2), int(y2)
            return None
    
        @staticmethod
        def get_projection_point(external_point, center_point, m, md):
            x0, y0 = external_point
            cx, cy = center_point
            c = cy - m * cx
            cd = y0 + md * x0
            mm1 = (m * m + 1)
            x = m * (cd - c) / mm1
            y = (m * m * cd + c) / mm1
            xd = x - x0
            yd = y - y0
            d = math.sqrt(xd * xd + yd * yd)
            return x, y, d
    
        @staticmethod
        def get_distance(point1, point2):
            x1, y1 = point1
            x2, y2 = point2
            dx = x1 - x2
            dy = y1 - y2
            return math.sqrt(dx * dx + dy * dy)
    
    def convert_lines(lines_p) -> [Line]:
        lines = []
        for line in lines_p:
            ln = line[0]
            lines.append(Line(ln[0], ln[1], ln[2], ln[3]))
        return lines
    
    import numpy as np
    def check_overlap(line1, line2):
        combination = np.array([line1,
                                line2,
                                [line1[0], line1[1], line2[0], line2[1]],
                                [line1[0], line1[1], line2[2], line2[3]],
                                [line1[2], line1[3], line2[0], line2[1]],
                                [line1[2], line1[3], line2[2], line2[3]]])
        distance = np.sqrt((combination[:,0] - combination[:,2])**2 + (combination[:,1] - combination[:,3])**2)
        max = np.amax(distance)
        overlap = distance[0] + distance[1] - max 
        endpoint = combination[np.argmax(distance)]
        return (overlap >= 0), endpoint #replace 0 with the value of distance between 2 collinear lines
    
    def mergeLine(line_list):
        #convert (x1, y1, x2, y2) formm to (r, alpha) form
        A = line_list[:,1] - line_list[:,3]
        B = line_list[:,2] - line_list[:,0]
        C = line_list[:,0]*line_list[:,3] - line_list[:,2]*line_list[:,1]
        r = np.divide(np.abs(C), np.sqrt(A*A+B*B))
        alpha = (np.arctan2(-B,-A) + math.pi) % (2*math.pi) - math.pi
        r_alpha = np.column_stack((r, alpha))
    
        #prepare some variables to keep track of lines looping
        r_bin_size = 10 #maximum distance to treat 2 lines as one
        alpha_bin_size = 0.15 #maximum angle (radian) to treat 2 lines as one
        merged = np.zeros(len(r_alpha), dtype=np.uint8)
        line_group = np.empty((0,4), dtype=np.int32)
        group_count = 0
    
        for line_index in range(len(r_alpha)): 
            if merged[line_index] == 0: #if line hasn't been merged yet
                merged[line_index] = 1
                line_group = np.append(line_group, [line_list[line_index]], axis=0)
                for line_index2 in range(line_index+1,len(r_alpha)):
                    if merged[line_index2] == 0:
                        #calculate the differences between 2 lines by r and alpha
                        dr = abs(r_alpha[line_index,0] - r_alpha[line_index2,0])
                        dalpha = abs(r_alpha[line_index,1] - r_alpha[line_index2,1])
                        if (dr<r_bin_size) and (dalpha<alpha_bin_size): #if they are close, they are the same line, so check if they are overlap
                            overlap, endpoints = check_overlap(line_group[group_count], line_list[line_index2])
                            if overlap:
                                line_group[group_count] = endpoints
                                merged[line_index2] = 1
                group_count += 1
        return line_group