FilFinder
FilFinder copied to clipboard
Ignoring looped filaments
There are 2 kinds of problems with looped filaments introduced in filfinder2D:
- Looped filament without any end gives: UnboundLocalError: local variable 'max_path_length' referenced before assignment;
- Looped filament with any end attached, even insignificant by length(at figure), is ignored, when providing longest path: the longest path of the insignificant branch is provided instead of longer looped filbranch.
How to avoid losing information contained with loops when, e.g., extracting longest paths?
OS: Linux, Ubuntu 18.04 Environment: Pycharm 2020.3.3 PlantCV Version 3.11.0]
I'm working on this currently. To deal with the first max_path_length error resulting from looped filaments, I added a check before running "analyze_skeletons":
# define two functions to identify junctions and endpoints:
def find_junctions(skel):
"""Finds pixels with exactly three neighbors."""
kernel = np.array([
[1, 1, 1],
[1, 10, 1],
[1, 1, 1]
])
neighbors_count = convolve(skel.astype(int), kernel, mode='constant', cval=0)
return (neighbors_count - 10 == 3) & skel
def find_endpoints(skel):
# Define a kernel that counts the number of neighbors
kernel = np.array([
[1, 1, 1],
[1, 10, 1],
[1, 1, 1]
])
neighbors_count = convolve(skel.astype(int), kernel, mode='constant', cval=0)
# Endpoints are skeleton points with only one neighbor
return (neighbors_count == 11) & skel
def make_skeletons(binary_image):
"""
This function uses the FilFinder package to find and prune skeletons of images.
Args:
image: 2D numpy array or list of points that make up polygon mask
Returns:
skeleton: 2D numpy array with skeletons
skeleton_longpath: 2D numpy array with skeletons that have been pruned to a single line
"""
# Load in skeleton class for skeleton pruning
fil = FilFinder2D(binary_image, beamwidth=0 * u.pix, mask=binary_image)
# This is a necessary step for the fil object. It does nothing.
fil.preprocess_image(skip_flatten=True)
# This makes the skeleton
fil.medskel(verbose=False)
# find junctions and endpoints
junctions = np.asarray(np.nonzero(find_junctions(fil.skeleton))).shape[1]
endpoints = np.asarray(np.nonzero(find_endpoints(fil.skeleton))).shape[1]
print(f'Number of junctions is {junctions}')
print(f'Number of endpoints is {endpoints}')
print('-----------------------------------------------')
if junctions == 0 and endpoints == 0:
return fil.skeleton, fil.skeleton
else:
# This prunes the skeleton
fil.analyze_skeletons(branch_thresh=BRANCH_THRESH * u.pix, prune_criteria='length',
skel_thresh=MIN_CAP_LEN * u.pix)
return fil.skeleton, fil.skeleton_longpath
A circle with have zero endpoints and zero junctions, so if I don't find them then I don't run the pruning algorithm and just return the circle.