3D model from image slices

Hi,
I have a large set of slices of a bone sample processed into masks. Is there a way to combine these images into a 3D model such as an stl file?
I’d rather keep this within my python code, I’m sure there is software that could do this quite easily.

1 Like

If you want to stick with python, this should work:

  1. Load 2D masks into a single 3D numpy array
  2. Use scikit-image’s implementation of the marching cubes algorithm to extract the surface
  3. Save as STL.

If you’re stuck, I have code (that I’m happy to share) that does this, but saves to .obj. Looks like it’s easy to convert though.

3 Likes

Great, Thanks. I’ll give this a try

Thanks for your help, this seems to be working!
I have one problem though, I had this working great using:

verts, faces, normals, values = marching_cubes_lewiner(all_trabecular_slices)
mlab.triangular_mesh([vert[0] for vert in verts],
[vert[1] for vert in verts],
[vert[2] for vert in verts],
faces)
mlab.savefig(“Output\Trabecular_Mask\trabecular_model.obj”)

My models were as expected but now they seem to be saving as just the outlines:
image
image

Even though the saved image slices look like this still:trabecular_mask_25.tiff (207.2 KB)

Do you have any idea why this might be? I only changed the method for building my 3D array, I can’t see why the obj file has changed.

What do you mean by “saving as just the outlines”? The marching cubes algorithm will only extract the surface of the object, or do you mean something else?

I mean that the obj files are as pictured, it just seems to be creating a model with the outlines of the slices. Before the models were being produced as a solid object. For example the first “rainbow” coloured image in my post was coming out with the inside filled before, I’m not sure what could have changed.

I’m still not 100% on clear on what your images look like, could you upload an image stack, and I’ll see if I can reproduce the problem.

Do you mean that it has worked on some images, and not on others? The only thing I can think of is if the binary mask touches the edge of the image, then maybe that edge isn’t being detected by the marching cubes algorithm.

Trabecular_Mask.rar (51.2 KB)
I have attached a small sample of the processed images I’m working with.
I’d like a 3D model of all the white objects “stacked”. So for example, if my shape had been a circle, I’d like to produce a solid cylinder.
I actually had it working but slightly changed the code that produced the 3D array of all my images and now it seems to be only making a 3D model of just the outlines of all the images whereas I’d like a solid shape. The 3D array is in exactly the same format though which is why I’m confused.

So the marching cubes algorithm only extracts the surface. To get “solid” object, I’m not sure if:
a) You can just change the settings on the software used to visualise/print the object
or b) You need to convert the image in a completely different way.

Is it possible for me to have a look at your code that you mentioned please?

I’m not sure how easy it will be to understand, but the surface extraction is here:

and the saving of the surface to .obj is here:

Hi, the first code link isn’t available now, would you please update it again, thanks!

Have you solved your problem now? I encountered a very similar problem as you.

My code has been moved here:

Yes, I used Adam’s solution to process the masks then load the 2D masks into one 3D array and then used Scikit-image marching cubes to extract the vertices and faces. I then used trimesh to turn this into a surface mesh and export as an STL. Using something similar to this:

surf_mesh = trimesh.Trimesh(verts, faces, validate=True)
surf_mesh.export('outputdirectory\\meshname.stl')

The key to this for me though was to pad the ends of the 3D array. For certain shapes, the marching cubes “missed” the ends of the sample as you can see above (in the multi coloured images). I just had to pad both ends of the array with multiple “layers” of empty arrays. Just kept padding until it worked, for my specific case it took 16 empty arrays either side of the masks.

Turning the masks into a tetrahedral volume mesh rather than a surface mesh was another battle entirely but let me know if you need any help with that.