Reputation: 328
I have a skeletonized image in the following shape:
And I want to extract the "largest branch" from the skeleton:
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
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:
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:
Upvotes: 9
Reputation: 464
I believe you can use OpenCV to do the following:
Add a black pixel at all corners
Get all branches in the picture using findContours. Then check the length of each contour using arcLength and get the longest.
Upvotes: 8