Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vascular structure with different radii #45

Open
koenterheegde0507 opened this issue Mar 30, 2024 · 15 comments
Open

Vascular structure with different radii #45

koenterheegde0507 opened this issue Mar 30, 2024 · 15 comments

Comments

@koenterheegde0507
Copy link

Hey,

I'm testing out your algorithms on a vascular structure with an aneurysm. All methods I have tried out right now seem to work quite well on narrow vascular structures, but when the radius becomes bigger it seems to go wrong. See this:
image

Might this be due to a not correct mesh? I do use fix_mesh and contract. When using a smaller value than 0.1 for epsilon in the contract function, most of the times the contraction stops prematurely.

Complete code:

import numpy as np
import pyvista as pv
import tkinter as tk 
from tkinter import filedialog
import os
import skeletor as sk
import trimesh as tm
import open3d as o3d
import networkx as nx

root = tk.Tk()
root.withdraw()
root.title('Pick patient data')
file_path = filedialog.askdirectory()


aorta_mesh = tm.load(f"{file_path}\Segment_1.stl")
aorta_mesh_simplified = aorta_mesh.simplify_quadric_decimation(len(aorta_mesh.faces)/10)
fixed2 = sk.pre.fix_mesh(aorta_mesh_simplified, fix_normals=True, remove_disconnected=5, inplace=True)
contracted_mesh = sk.pre.contract(fixed2,epsilon=0.1)

skel = sk.skeletonize.by_wavefront(contracted_mesh, waves=50, step_size=1)
#contracted_mesh = sk.pre.contract(aorta_mesh,epsilon=0.2)
skel2 = sk.skeletonize.by_teasar(contracted_mesh,inv_dist=10)

skel3 = sk.skeletonize.by_tangent_ball(contracted_mesh)
skel4 = sk.skeletonize.by_vertex_clusters(contracted_mesh,sampling_dist=1,cluster_pos='median')

big_plotter = pv.Plotter(shape=(2,2))
big_plotter.subplot(0,0)
big_plotter.add_points(np.array(skel.vertices),color='black')
big_plotter.add_mesh(aorta_mesh_simplified,opacity=0.5)
big_plotter.add_text('Wavefront',position='upper_right',font_size=10)

big_plotter.subplot(0,1)
big_plotter.add_points(np.array(skel2.vertices),color='black')
big_plotter.add_mesh(aorta_mesh_simplified,opacity=0.5)
big_plotter.add_text('TEASAR',position='upper_right',font_size=10)

big_plotter.subplot(1,0)
big_plotter.add_points(np.array(skel3.vertices),color='black')
big_plotter.add_mesh(aorta_mesh_simplified,opacity=0.5)
big_plotter.add_text('Tangent Ball',position='upper_right',font_size=10)

big_plotter.subplot(1,1)
big_plotter.add_points(np.array(skel4.vertices),color='black')
big_plotter.add_mesh(aorta_mesh_simplified,opacity=0.5)
big_plotter.add_text('Vertex Cluster',position='upper_right',font_size=10)

big_plotter.show()
@schlegelp
Copy link
Collaborator

Can you share the mesh from your examples please?

@koenterheegde0507
Copy link
Author

Sharing a .STL is not supported :(

@schlegelp
Copy link
Collaborator

Try zipping before upload.

@koenterheegde0507
Copy link
Author

Could I send it to you personally? Its personal data so would rather not share it in here

@schlegelp
Copy link
Collaborator

Yes, my email is in the setup.py.

@koenterheegde0507
Copy link
Author

I've sent the email with the STL in it

@schlegelp
Copy link
Collaborator

schlegelp commented Mar 30, 2024

I'd use the wavefront method without prior contraction here:

import trimesh as tm 
import skeletor as sk 

m = tm.load('Segment_1.stl')
s = sk.skeletonize.by_wavefront(m)
Screenshot 2024-03-30 at 14 41 24

This screenshot shows the actual skeleton which doesn't look half bad. You might have noticed though that the bulbous region the skeleton seems to have a decent midline but also various spiky "offshoots". On closer inspection, these consist of single nodes - which in my experience are often due to imperfections in the mesh.

I used Blender to quickly re-mesh the mesh (via the modifier of the same name) and then also ran a function to remove these bristles:

m2 = tm.load('Segment_1_remesh.stl')
s2 = sk.skeletonize.by_wavefront(m2)
s2_clean = sk.post.postprocessing.remove_hairs(s2, los_only=False)
Screenshot 2024-03-30 at 16 19 52

As you can see it already looks much better. In general I find it's often better to think about how to clean up the initial skeleton than trying to optimise the skeletonisation.

As a final hint: the wavefront method is somewhat sensitive to where the wave starts. Ideally it would start at the tip of one of the vasculatures but by default a semi-random vertex on the mesh is chosen. You can define the start point using the using the origins parameter:

# Here, I randomly picked the 50,000th vertex as starting point but that already made the skeleton better off the bat
s2 = sk.skeletonize.by_wavefront(m2, waves=1, origins=[50000])

@schlegelp
Copy link
Collaborator

Oh forgot to say: I had to make a minor tweak to the remove_hairs function. To run the code above, you will have to re-install skeletor from Github.

@koenterheegde0507
Copy link
Author

When will the new function be available using pip install instead of cloning the repository?

@schlegelp
Copy link
Collaborator

I released skeletor 1.3.0 this morning. Note though that the remove_hairs function was renamed to remove_bristles.

@koenterheegde0507
Copy link
Author

I've used pip uninstall skeletor and the reinstalled it, but the functionality doesn't seem to be there yet?

@koenterheegde0507
Copy link
Author

This only seems to work on the wavefront method, I still get strange skeletons for the other methods when using the remove_bristles function. However, I do not remesh it as I don't want to use external software.

@schlegelp
Copy link
Collaborator

Can you share examples (code + image) for these "strange" skeletons?

@schlegelp
Copy link
Collaborator

For what it's worth: I had a bit of a play with your mesh myself and I'd say the wavefront method is your best bet.

TEASAR is problematic when the object has both small and large radii which is the case for your mesh: too small inv_dist and you get extraneous branches on the bulbous parts, too large inv_dist and you loose details in the fine branches. Plus the skeleton is on the mesh surface - not sure if you need midline traces?

Vertex cluster and edge collapse require a contracted mesh which for some reason doesn't seem to work well with your mesh.

Tangent ball doesn't do well on the bulbous part - looks like it may be thrown off by the small vessel attached to the surface.

@schlegelp
Copy link
Collaborator

I've used pip uninstall skeletor and the reinstalled it, but the functionality doesn't seem to be there yet?

Sorry, I just realised that the Github Actions workflow that was supposed to push version 1.3.0 to PyPI had failed. The new version is now definitely on PyPI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants