在 python opencv 中查找骨架化图像的交点

Finding intersections of a skeletonised image in python opencv

我有一个骨架图像(如下所示)。

我想得到直线的交点。我在下面尝试了以下方法,skeleton 是一个 openCV 图像,算法 returns 是一个坐标列表:

def getSkeletonIntersection(skeleton):
    image = skeleton.copy();
    image = image/255;
    intersections = list();
    for y in range(1,len(image)-1):
        for x in range(1,len(image[y])-1):
            if image[y][x] == 1:
                neighbourCount = 0;
                neighbours = neighbourCoords(x,y);
                for n in neighbours:
                    if (image[n[1]][n[0]] == 1):
                        neighbourCount += 1;
                if(neighbourCount > 2):
                    print(neighbourCount,x,y);
                    intersections.append((x,y));
    return intersections;

它找到有两个以上相邻像素的白色像素的坐标。我认为这只会 return 个角落,但事实并非如此 - 它 return 还有很多点。

这是在图像上标记了检测到的点的输出。这是因为它检测到下面显示的一些不是交叉点的示例。

0 0 0    1 1 0    0 1 1
1 1 1    0 1 0    1 1 0
0 0 1    0 0 1    0 0 0

还有更多例子。我应该看看另一种检测交叉点的方法吗?感谢所有输入和想法,谢谢。

我不确定 OpenCV 功能,但您也许应该尝试使用 Hit and Miss 形态学 here.

阅读 Line Junctions 并查看您需要测试的 12 个模板:

如果对于给定的像素,而不是计算 8 个邻域的总数(= 连通性为 8 的邻域),您可以计算 8 个邻域的数量 而不是4-邻居彼此

因此在您的误报示例中

0 0 0    1 1 0    0 1 1
1 1 1    0 1 0    1 1 0
0 0 1    0 0 1    0 0 0

对于每种情况,你有 3 个邻居,但每次都有 2 个是 4-连通的。 (在下一个片段中标记为“2”的像素)

0 0 0    2 2 0    0 2 2
1 1 2    0 1 0    1 1 0
0 0 2    0 0 1    0 0 0

如果您只考虑其中一个作为您的计数(而不是现在在您的代码中同时考虑它们),您确实只有 2 个 newly-defined "neighbors" 并且考虑的点不是考虑交叉路口。 其他的"real intersections"还是会保留,比如下面的

0 1 0    0 1 0    0 1 0
1 1 1    0 1 0    1 1 0
0 0 0    1 0 1    0 0 1

还有 3 个 newly-defined 邻居。

我没有检查过你的图像是否完美,但我已经为这个问题实现了类似的东西......

我最近收到一封电子邮件,询问我对问题的最终解决方案。它张贴在下面,以便它可以通知其他人。我并没有声称这段代码特别快或特别稳定——只是它对我有用!该功能还包括过滤检测到的重复项和交叉点靠得太近,表明它们不是真正的交叉点,而是从骨架化过程中引入了噪声。

def neighbours(x,y,image):
    """Return 8-neighbours of image point P1(x,y), in a clockwise order"""
    img = image
    x_1, y_1, x1, y1 = x-1, y-1, x+1, y+1;
    return [ img[x_1][y], img[x_1][y1], img[x][y1], img[x1][y1], img[x1][y], img[x1][y_1], img[x][y_1], img[x_1][y_1] ]   


def getSkeletonIntersection(skeleton):
    """ Given a skeletonised image, it will give the coordinates of the intersections of the skeleton.

    Keyword arguments:
    skeleton -- the skeletonised image to detect the intersections of

    Returns: 
    List of 2-tuples (x,y) containing the intersection coordinates
    """
    # A biiiiiig list of valid intersections             2 3 4
    # These are in the format shown to the right         1 C 5
    #                                                    8 7 6 
    validIntersection = [[0,1,0,1,0,0,1,0],[0,0,1,0,1,0,0,1],[1,0,0,1,0,1,0,0],
                         [0,1,0,0,1,0,1,0],[0,0,1,0,0,1,0,1],[1,0,0,1,0,0,1,0],
                         [0,1,0,0,1,0,0,1],[1,0,1,0,0,1,0,0],[0,1,0,0,0,1,0,1],
                         [0,1,0,1,0,0,0,1],[0,1,0,1,0,1,0,0],[0,0,0,1,0,1,0,1],
                         [1,0,1,0,0,0,1,0],[1,0,1,0,1,0,0,0],[0,0,1,0,1,0,1,0],
                         [1,0,0,0,1,0,1,0],[1,0,0,1,1,1,0,0],[0,0,1,0,0,1,1,1],
                         [1,1,0,0,1,0,0,1],[0,1,1,1,0,0,1,0],[1,0,1,1,0,0,1,0],
                         [1,0,1,0,0,1,1,0],[1,0,1,1,0,1,1,0],[0,1,1,0,1,0,1,1],
                         [1,1,0,1,1,0,1,0],[1,1,0,0,1,0,1,0],[0,1,1,0,1,0,1,0],
                         [0,0,1,0,1,0,1,1],[1,0,0,1,1,0,1,0],[1,0,1,0,1,1,0,1],
                         [1,0,1,0,1,1,0,0],[1,0,1,0,1,0,0,1],[0,1,0,0,1,0,1,1],
                         [0,1,1,0,1,0,0,1],[1,1,0,1,0,0,1,0],[0,1,0,1,1,0,1,0],
                         [0,0,1,0,1,1,0,1],[1,0,1,0,0,1,0,1],[1,0,0,1,0,1,1,0],
                         [1,0,1,1,0,1,0,0]];
    image = skeleton.copy();
    image = image/255;
    intersections = list();
    for x in range(1,len(image)-1):
        for y in range(1,len(image[x])-1):
            # If we have a white pixel
            if image[x][y] == 1:
                neighbours = neighbours(x,y,image);
                valid = True;
                if neighbours in validIntersection:
                    intersections.append((y,x));
    # Filter intersections to make sure we don't count them twice or ones that are very close together
    for point1 in intersections:
        for point2 in intersections:
            if (((point1[0] - point2[0])**2 + (point1[1] - point2[1])**2) < 10**2) and (point1 != point2):
                intersections.remove(point2);
    # Remove duplicates
    intersections = list(set(intersections));
    return intersections;

这在 github here 上也可用。

这是我的解决方案:


    # Functions to generate kernels of curve intersection 

    def generate_nonadjacent_combination(input_list,take_n):
        """ 
        It generates combinations of m taken n at a time where there is no adjacent n.
        INPUT:
            input_list = (iterable) List of elements you want to extract the combination 
            take_n =     (integer) Number of elements that you are going to take at a time in
                         each combination
        OUTPUT:
            all_comb =   (np.array) with all the combinations
        """
        all_comb = []
        for comb in itertools.combinations(input_list, take_n):
            comb = np.array(comb)
            d = np.diff(comb)
            fd = np.diff(np.flip(comb))
            if len(d[d==1]) == 0 and comb[-1] - comb[0] != 7:
                all_comb.append(comb)        
                print(comb)
        return all_comb


    def populate_intersection_kernel(combinations):
        """
        Maps the numbers from 0-7 into the 8 pixels surrounding the center pixel in
        a 9 x 9 matrix clockwisely i.e. up_pixel = 0, right_pixel = 2, etc. And 
        generates a kernel that represents a line intersection, where the center 
        pixel is occupied and 3 or 4 pixels of the border are ocuppied too.
        INPUT:
            combinations = (np.array) matrix where every row is a vector of combinations
        OUTPUT:
            kernels =      (List) list of 9 x 9 kernels/masks. each element is a mask.
        """
        n = len(combinations[0])
        template = np.array((
                [-1, -1, -1],
                [-1, 1, -1],
                [-1, -1, -1]), dtype="int")
        match = [(0,1),(0,2),(1,2),(2,2),(2,1),(2,0),(1,0),(0,0)]
        kernels = []
        for n in combinations:
            tmp = np.copy(template)
            for m in n:
                tmp[match[m][0],match[m][1]] = 1
            kernels.append(tmp)
        return kernels


    def give_intersection_kernels():
        """
        Generates all the intersection kernels in a 9x9 matrix.
        INPUT:
            None
        OUTPUT:
            kernels =      (List) list of 9 x 9 kernels/masks. each element is a mask.
        """
        input_list = np.arange(8)
        taken_n = [4,3]
        kernels = []
        for taken in taken_n:
            comb = generate_nonadjacent_combination(input_list,taken)
            tmp_ker = populate_intersection_kernel(comb)
            kernels.extend(tmp_ker)
        return kernels


    # Find the curve intersections
    def find_line_intersection(input_image, show=0):
        """
        Applies morphologyEx with parameter HitsMiss to look for all the curve 
        intersection kernels generated with give_intersection_kernels() function.
        INPUT:
            input_image =  (np.array dtype=np.uint8) binarized m x n image matrix
        OUTPUT:
            output_image = (np.array dtype=np.uint8) image where the nonzero pixels 
                           are the line intersection.
        """
        kernel = np.array(give_intersection_kernels())
        output_image = np.zeros(input_image.shape)
        for i in np.arange(len(kernel)):
            out = cv2.morphologyEx(input_image, cv2.MORPH_HITMISS, kernel[i,:,:])
            output_image = output_image + out
        if show == 1:
            show_image = np.reshape(np.repeat(input_image, 3, axis=1),(input_image.shape[0],input_image.shape[1],3))*255
            show_image[:,:,1] = show_image[:,:,1] -  output_image *255
            show_image[:,:,2] = show_image[:,:,2] -  output_image *255
            plt.imshow(show_image)
        return output_image

    #  finding corners
    def find_endoflines(input_image, show=0):
        """
        """
        kernel_0 = np.array((
                [-1, -1, -1],
                [-1, 1, -1],
                [-1, 1, -1]), dtype="int")

        kernel_1 = np.array((
                [-1, -1, -1],
                [-1, 1, -1],
                [1,-1, -1]), dtype="int")

        kernel_2 = np.array((
                [-1, -1, -1],
                [1, 1, -1],
                [-1,-1, -1]), dtype="int")

        kernel_3 = np.array((
                [1, -1, -1],
                [-1, 1, -1],
                [-1,-1, -1]), dtype="int")

        kernel_4 = np.array((
                [-1, 1, -1],
                [-1, 1, -1],
                [-1,-1, -1]), dtype="int")

        kernel_5 = np.array((
                [-1, -1, 1],
                [-1, 1, -1],
                [-1,-1, -1]), dtype="int")

        kernel_6 = np.array((
                [-1, -1, -1],
                [-1, 1, 1],
                [-1,-1, -1]), dtype="int")

        kernel_7 = np.array((
                [-1, -1, -1],
                [-1, 1, -1],
                [-1,-1, 1]), dtype="int")

        kernel = np.array((kernel_0,kernel_1,kernel_2,kernel_3,kernel_4,kernel_5,kernel_6, kernel_7))
        output_image = np.zeros(input_image.shape)
        for i in np.arange(8):
            out = cv2.morphologyEx(input_image, cv2.MORPH_HITMISS, kernel[i,:,:])
            output_image = output_image + out

        if show == 1:
            show_image = np.reshape(np.repeat(input_image, 3, axis=1),(input_image.shape[0],input_image.shape[1],3))*255
            show_image[:,:,1] = show_image[:,:,1] -  output_image *255
            show_image[:,:,2] = show_image[:,:,2] -  output_image *255
            plt.imshow(show_image)    

        return output_image#, np.where(output_image == 1)

    # 0- Find end of lines
    input_image = img.astype(np.uint8) # must be blaack and white thin network image
    eol_img = find_endoflines(input_image, 0)

    # 1- Find curve Intersections
    lint_img = find_line_intersection(input_image, 0)

    # 2- Put together all the nodes
    nodes = eol_img + lint_img
    plt.imshow(nodes)