Search code examples
pythonopencvimage-processingcomputer-visionmedial-axis

Is there any method to find medial axis distance at middle, and fifth pixel of end points of skeleton medial axis?


I want to find medial axis distance transform at middle, fifth pixel at each end point. my input image and desired point are:

Input Image Input Image Desired point on skeleton enter image description here

My code as follows:

skeleton, distance = medial_axis(cimg, return_distance=True)
                med_dist = distance * skeleton
                width = med_dist*2
                skeld=width[skeleton]
                dwidth=skeld[skeld.shape[0]//2]

But it doesn't give correct result


Solution

  • EDIT 0: If you are saying, the skeleton can be any oriented, let's get complicated :). Let me start with a similar problem from my past. There, I needed an approach to track down the pixels between given two points at a skeleton. Please check the accepted answer at the question, and, keep this approach in your mind, because I will use it for your problem too.

    Here are the steps that I followed for your problem.

    1. Get the skeleton image
    2. Get the tips (i.e. start, end points) of the skeleton by using the number of adjacent pixels around the tips
    3. Draw the path between the start and end points by BFS
    4. Get the desired indices (fifth pixel of end points) from the drawn path
    import numpy as np
    import cv2
    import os
    import matplotlib.pyplot as plt
    import sys
    from collections import deque
    from skimage.morphology import medial_axis
    from itertools import combinations
    
    
    img = cv2.imread('curvy_1.png',0)/255
    skel = medial_axis(img, return_distance=False) # skeleton
    
    
    img_conv = cv2.filter2D(skel.astype(np.uint8),-1,np.ones((3,3))) #
    img_conv = img_conv*skel
    img_tips = img_conv == 2
    tips = np.array(np.nonzero(img_tips)).T
    tip_combs = combinations(tips, 2) # get all the combinations of the tips in case the skeleton are branched
    

    Here are the found tips. tips of the skeleton

    # BFS
    def findPathBwTwoPoints(img_binary,points):
        '''
        img_binary: skeleton image
        points: (y_start_point,x_start_point),(y_end_point,x_end_point)
        '''
        height, width = img_binary.shape
    
        # The start and end point you're looking at
        # start, end = (31, 14), (34, 51)
    
        start,end = points
    
        # print(start,end)
    
        # All 8 directions
        delta = [(-1, -1), (-1, 0), (-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1)]
    
        # Store the results of the BFS as the shortest distance to start
        grid = [[sys.maxsize for _ in range(width)] for _ in range(height)]
        grid[start[0]][start[1]] = 0
    
        # The actual BFS algorithm
        bfs = deque([start])
        found = False
        while len(bfs) > 0:
            y, x = bfs.popleft()
            # print(y,x)
            # We've reached the end!
            if (y, x) == end:
                found = True
                break
    
            # Look all 8 directions for a good path
            for dy, dx in delta:
                yy, xx = y + dy, x + dx
                # If the next position hasn't already been looked at and it's white
                if 0 <= yy < height and 0 <= xx < width and grid[y][x] + 1 < grid[yy][xx] and img_binary[yy][xx] != 0:
                    grid[yy][xx] = grid[y][x] + 1
                    bfs.append((yy, xx))
    
        if found:
            # Now rebuild the path from the end to beginning
            path = []
            y, x = end
            while grid[y][x] != 0:
                for dy, dx in delta:
                    yy, xx = y + dy, x + dx
                    if 0 <= yy < height and 0 <= xx < width and grid[yy][xx] == grid[y][x] - 1:
                        path.append([yy, xx])
                        y, x = yy, xx
    
            return np.array(path)
        else:
            # print(f'No path found between {start} and {end}')
            return 0
    

    Let's get the path between the found tips by using BFS.

    for tip_comb in list(tip_combs):
    
        start, end = tuple(tip_comb[0]), tuple(tip_comb[1])
    
        paths = findPathBwTwoPoints(skel,points=[start,end]) # this will return the path between the start and end points
    
        # ready to get the indices you are asking for
        first_fifth = paths[4]
        last_fifth = paths[-5]
        middle = paths[int(len(paths)/2)]
    
    
        fig,ax = plt.subplots(1)
        ax.imshow(skel,'gray')
        ax.scatter( [first_fifth[1],last_fifth[1],middle[1]],
                    [first_fifth[0],last_fifth[0],middle[0]],s=10,c='r')
    
    plt.show()
    

    indices on the skeleton

    Here are a few more example output from my approach.

    curvy_2

    In case your skeleton is branched, this approach will give you the indices for all the combinations between the tips.

    curvy_3_0 curvy_3_1 curvy_3_2