jni / skan

Python module to analyse skeleton (thin object) images
https://skeleton-analysis.org
BSD 3-Clause "New" or "Revised" License
118 stars 38 forks source link

Feature request: Add function to prune skeleton #97

Open jni opened 3 years ago

jni commented 3 years ago

Skeletonization algorithms often produce small spurious branches. skan should be able to prune those branches and produce a revised skeleton. Additionally, one might want to prune branches based on other criteria, such as curvature. This can be tricky to do correctly, however. I sketched some possible approaches in an email to @jdhenshaw, which I'll reproduce here for reference:

The short answer is that it should be possible to do some naive pruning (1), but to do it right will require some tricky logic (2).

(1) If you look at the documentation for the Skeleton class, (https://jni.github.io/skan/api/skan.csr.html#skan.csr.Skeleton), you can see that there's a method "path_lengths" to get all the path lengths. You can then do:

my_paths = skel.paths[skel.path_lengths() > min_length]

Possibly you can even do skel.paths = my_paths but I have no idea if other things will break then...!

You can then draw these back on the image with the clunkily-named skan.draw.overlay_skeleton_2d_class (https://jni.github.io/skan/api/skan.draw.html#skan.draw.overlay_skeleton_2d_class).

In all these cases you can look at the source code so you can customise to your heart's content.

But,

(2) this won't take into account some nuances like:

So, ideally, but also expensively, what I would consider the "proper" way to proceed is:

There's probably a way to make this much faster. In broad strokes, you could build the junction-junction graph in networkx from the table returned by summarize (the American spelling function is the preferred one now, see https://github.com/jni/skan/issues/71 for more details). (nx graphs are much more mutable than the more compact and faster csr matrices used in skan.) Once you have the graph, you can do the procedure above on the graph: remove the shortest edge, fuse any nodes that only have two nodes, and repeat, until you have your final graph. Finally, create a csr graph from the networkx graph and the original skeleton csr graph (which contains the pixel positions). Once you have that, it should be feasible to draw back onto an image.

ns-rse commented 1 year ago

I've been documenting my work on this in #206 (see also ns-rs/iterative-pruning branch on my fork) but have only just come across this issue so referencing it here.