Rodrigo Ce Moretto
Rodrigo Ce Moretto

Reputation: 328

Python Image - Finding largest branch from image skeleton

I have a skeletonized image in the following shape: enter image description here

And I want to extract the "largest branch" from the skeleton: enter image description here

I know that maybe I need to extract the junction point and divide de lines from that point(?), but I have no clue how to do that.

Is there any way to do that with Python Scikit Image or OpenCV?

Upvotes: 3

Views: 6923

Answers (2)

mjkvaak
mjkvaak

Reputation: 549

There is an awesome python package for analyzing skeletons with python called FilFinder ($pip install fil_finder) which solves this problem elegantly. Below is a code adopted from their tutorial.

Generate the skeleton:

import numpy as np
import cv2
import matplotlib.pyplot as plt
from fil_finder import FilFinder2D
import astropy.units as u

skeleton = cv2.imread("./data/XmviQ.png", 0) #in numpy array format

fil = FilFinder2D(skeleton, distance=250 * u.pc, mask=skeleton)
fil.preprocess_image(flatten_percent=85)
fil.create_mask(border_masking=True, verbose=False,
use_existing_mask=True)
fil.medskel(verbose=False)
fil.analyze_skeletons(branch_thresh=40* u.pix, skel_thresh=10 * u.pix, prune_criteria='length')

# Show the longest path
plt.imshow(fil.skeleton, cmap='gray')
plt.contour(fil.skeleton_longpath, colors='r')
plt.axis('off')
plt.show()

Output:

Longest path

In your problem, you're not interested in the longest path along the graph corresponding to the skeleton, but the path of the longest branch. Continuing from the above code block, the below script would do the trick. I added the dataframes to visualize that FilFinder automatically generates a lot of interesting information about the skeleton.

import pandas as pd
plt.imshow(fil.skeleton, cmap='gray')

# this also works for multiple filaments/skeletons in the image: here only one
for idx, filament in enumerate(fil.filaments): 

    data = filament.branch_properties.copy()
    data_df = pd.DataFrame(data)
    data_df['offset_pixels'] = data_df['pixels'].apply(lambda x: x+filament.pixel_extents[0])

    print(f"Filament: {idx}")
    display(data_df.head())

    longest_branch_idx = data_df.length.idxmax()
    longest_branch_pix = data_df.offset_pixels.iloc[longest_branch_idx]

    y,x = longest_branch_pix[:,0],longest_branch_pix[:,1]

    plt.scatter(x,y , color='r')

plt.axis('off')
plt.show()

Output:

Longest branch

Upvotes: 9

Sameh Yassin
Sameh Yassin

Reputation: 464

I believe you can use OpenCV to do the following:

  1. Use HarrisCorner to detect all corners in the image. This will get you the shown three green points (I drew a whole circle to highlight the location).

enter image description here

  1. Add a black pixel at all corners

  2. Get all branches in the picture using findContours. Then check the length of each contour using arcLength and get the longest.

Upvotes: 8

Related Questions