0

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

zma
  • 59
  • 7

1 Answers1

2

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

Prefect
  • 1,719
  • 1
  • 7
  • 16
  • 2
    This only works if the skeleton is more or less a horizontal line. If it bends more than this example the sorting breaks. Ideally one would sort them topologically. Next, distances along the line should probably take into account the actual distance between pixels, counting pixels is a poor approximation to distance. – Cris Luengo Apr 20 '21 at 13:49
  • @CrisLuengo thanks for your effort, however; skeleton may be in any form, horizontal, vertical or other orientation. I agree with you this approach depends on how data structured. I am looking for any orientation and it might be some what circular, oval shape. – zma Apr 24 '21 at 07:35
  • @CrisLuengo I updated my answer. The current approach should respond to any orientation and any structure (including multiple skeleton ends). It is a topological approach as you suggested. But, I do not agree with the actual distance calculation, because, it would not make sense for any curve with a circular shape. – Prefect Apr 24 '21 at 22:37
  • Nice! Regarding distance calculation: I suggest you read here: https://www.crisluengo.net/archives/310/ . Everything I state there for perimeters is actually valid for any line, it doesn’t have to be a closed contour. So you can use that method to estimate length along the skeleton as well. – Cris Luengo Apr 24 '21 at 22:58
  • Thanks @Prefect. How to remove branches in the skeleton? – zma Jun 27 '21 at 01:14