navis-org/skeletor

Issue when computing skeleton (with non-watertight mesh)

yvanblanchard opened this issue · 6 comments

Hello,

I have a non-watertight mesh (OBJ) and the output skeleton is KO (very few points).

Mesh:
image
image

Here is my input file:
MeshToSkeletonize.zip

Here is the output skeelton file (swc):
image

Moreover, I tried to display the skeleton result, but it failed. It said that trimesh.viewer.windowed requires pyglet<2 (I had version 2.0.7).
Then, Installed pyglet-1.2.4 , but I got another error: (missing clock attribute):
image

Hi! Two separate issues here.

  1. The skeleton:

I quickly loaded your mesh into Blender to look at the structure. Not sure if you're familiar with that software but in the screenshot you can see the vertices and the edges/faces between them. The highlighted on is a face that I selected and then moved a little to the side. As you can see it's disconnected by which I mean your mesh consists of faces that each have their entirely independent set of vertices. This is an issue since skeletor uses the mesh's connectivity to generate skeletons.

Screenshot 2023-05-28 at 13 22 38

Fortunately, we can fix this issue using trimesh:

>>> import trimesh as tm 
>>># Load mesh and forbid trimesh run fixes
>>> m = tm.load('MeshToSkeletonize.obj', process=False)
>>> # Inspect - note the number of vertices
>>> m
<trimesh.Trimesh(vertices.shape=(1635, 3), faces.shape=(545, 3))>

>>> # Check the number of connected components
>>> # (i.e. how many disconnected pieces your mesh consists of)
>>> import networkx as nx 
>>> len(list(nx.connected_components(m.vertex_adjacency_graph)))
545 
>>> # As expected: 545 = the number of faces in your mesh

>>> # Now let's see if we can tell trimesh to merge vertices and generate one continuous mesh 
>>> m.merge_vertices(merge_tex=True, merge_norm=True)
>>> # Note the reduction in vertices:
>>> m
<trimesh.Trimesh(vertices.shape=(473, 3), faces.shape=(545, 3))>

>>> # Check connected components again 
>>> len(list(nx.connected_components(m.vertex_adjacency_graph)))
2
>>> # Looking at your mesh this makes sense since there is a small corner that is 
>>> # entirely disconnected

OK, with the mesh fixed we can try to skeletonise your mesh. I had a really quick crack at it and this is the best I could come up with:

>>> # Let's try skeletonizing the fixed mesh 
>>> import skeletor as sk 
>>> s = sk.skeletonize.by_teasar(m, inv_dist=10)
>>> s 
<Skeleton(vertices=(244, 3), edges=(242, 2), method=teasar)>
Screenshot 2023-05-28 at 13 48 10

As you can see it did something but probably not what you were hoping for. Some notes:

  • skeletor is designed to generate tree-like (acyclic) skeletons (i.e. each vertex has exactly one or no parent) which is why you get the breaks in your honeycomb structure. This is a limitation of the output format (SWC) but in theory I could rewrite the code to optionally not break-up cycles. Alternatively: you could use the skeleton --> mesh vertex map (s.skel_map) to heal the breaks by re-introducing edges according to the original mesh.
  • Your mesh has a fairly low vertex count. That's perfect for visualisation but some of the skeletonization methods (e.g. mesh contraction or the wavefront method) don't work well on low-polygon, geometric meshes.
  1. The visualisation issue: skeletor uses trimesh's visualisation functions and from the backtrace in your screenshot it looks like the issue is with pyglet, not skeletor or trimesh directly. I would recommend you check the issues in the pyglet Github repository and open one there if you don't find a solution to your problem.

Hope this is at least somewhat helpful!

Thank you for your detailed reply .

I followed your advice, and remesh my model for having a much finer mesh, and also check again for disconnected faces (merge_vertices).
image
File:
MeshToSkeletonizeRemeshedFiner2.zip

I also tried your skeleton method (by_teasar, with inv_dist of 10mm).
But it leads to this bad/unexpected result:
Notice that it does not matter is skeleton lines are not fully connected (zero or one parent max), but I need the skeleton to be perfeclty at medial position of 'branches' shapes of course.
image

The TEASAR method doesn't produce centred lines unfortunately.

With the higher res mesh you could try the wavefront method though:

>>> m = tm.load('/Users/philipps/Downloads/MeshToSkeletonizeRemeshedFiner.obj')
>>> m.merge_vertices(merge_tex=True, merge_norm=True, digits_vertex=5)
>>> s = sk.skeletonize.by_wavefront(m, waves=1)
Screenshot 2023-05-28 at 15 54 12

Thank you again.
Result is much better, but still lots of 'artefacts'.
I think I will use another method, maybe with some heuristic..
image

No problem. These are likely small false-positive side branches that you could prune away in post-processing (see screenshot). That said: you will not get a geometrically perfect centreline from any skeletonization method implemented in this library, I'm afraid.

Screenshot 2023-05-28 at 16 07 48