convert .nii to a mesh

Dear Community,

I am trying to convert a niftii file into a mesh ( and later save it as .obj) and I want to color this latter using the pixel intesity from the nifti volume. Is it possible using vtk and python ?

Hello,

VTK has the vtkOBJExporter class (https://vtk.org/doc/nightly/html/classvtkOBJExporter.html) that saves the entire scene contained in a vtkRenderWindow as a .OBJ file. As far as I know, there is no data object-to-obj direct conversion. In other words, it exports the scene as rendered and not as data. I mean, if your volume is being rendered as a slice, there will be only a flat panel with a texture applied to it in the OBJ file (vtkOBJExporter also generates a complimentary .mtl file with material description when necessary). There is a small Python example on how to use vtkOBJExporter here: https://discourse.vtk.org/t/how-can-i-export-tubes-to-obj/6015/12

Now, if you aim at converting all the voxels of the input volume into grid cells of a mesh described as OBJ format, I believe you need to study the OBJ file format and write the code to generate the file explicitly.

take care,

Paulo

Dear Paulo,

Thank you for your answer. It really helped.

My main problem is to color my mesh using the pixel intesity from my niftii volume. Is there a way to do it ?

thank you

What class are you using to represent the image? vtkImage?

You can use vtkProbeFilter for this.

Usually this coloring does not lead to meaningful result, as the mesh points are on the boundary of the segmented object. You end up just amplifying any segmentation imperfections.

What do you expect from this visualization? Would you like to display the volume similarly to volume rendering (raycasting), but with using just surface rendering, so that you can use simple web or mixed reality viewers for volume visualization?

Hello,
I did some investigations, and I know that my nifti volume have a range from 0 to 4 and each range is an index for colorification. I think it will be easier for me to color my mesh using thouse indexes.

Here is the code that I used, but it is displaying only white color during the visualization.

import vtk
import nibabel as nib
nifti_path = '/path/to/niftivolume'

reader = vtk.vtkNIFTIImageReader()
reader.SetFileName(nifti_path)
reader.Update()

image_data = reader.GetOutput()



# Load the Nifti file
img = nib.load('/path/to/niftivolume')
# Get the data array
data = img.get_fdata()


# Extract iso-surface mesh using vtkFlyingEdges3D
iso_value = 0.5
flying_edges = vtk.vtkFlyingEdges3D()
flying_edges.SetInputData(image_data)
flying_edges.SetValue(0, iso_value)
flying_edges.Update()

mesh = flying_edges.GetOutput()


# Create a color array to store the index values for each vertex
color_array = vtk.vtkUnsignedCharArray()
color_array.SetName("Colors")
color_array.SetNumberOfComponents(4)
color_array.SetNumberOfTuples(mesh.GetNumberOfPoints())

# Create a lookup table to map index values to colors
lookup_table = vtk.vtkLookupTable()
lookup_table.SetNumberOfTableValues(4)
lookup_table.SetTableValue(0, 1, 0, 0, 1)  # Red for index 0
lookup_table.SetTableValue(1, 0, 1, 0, 1)  # Green for index 2
lookup_table.SetTableValue(2, 0, 0, 1, 1)  # Blue for index 3
lookup_table.SetTableValue(3, 1, 1, 0, 1)  # Yellow for index 4
# Loop through the voxels in the Nifti volume and extract the index values
# Map the index values to colors using the lookup table
for i in range(image_data.GetDimensions()[0]):
    for j in range(image_data.GetDimensions()[1]):
        for k in range(image_data.GetDimensions()[2]):
            index_value = image_data.GetScalarComponentAsDouble(i, j, k, 0)
            # color = [0, 0, 0, 255]
            if index_value == 0.0 : 
                color = [255, 255, 255, 255]
            if index_value == 2.0 : 
                color = [255, 0, 0, 255]
            if index_value == 3.0 : 
                color = [0, 255, 0, 255]
            if index_value == 4.0 : 
                color = [0, 0, 255, 255]
            
            # lookup_table.GetColor(index_value, color)
            color_array.InsertTuple(image_data.ComputePointId((i, j, k)), color)

# Set the color array as the point data for the mesh
mesh.GetPointData().SetScalars(color_array)

I also double checked the indexes contained in my nifti volume :

import nibabel as nib
import numpy as np

# Load the Nifti file
img = nib.load('/path/to/nifti')

# Get the data array
data = img.get_fdata()

# Print the unique values in the data array
print(np.unique(data))

the output was :

[0. 2. 3. 4.]

Do you know what I got wrong ?
Thanks

There a couple of potential issues:

  1. You have only specified to extract a single isosurface, you need to extract 4
  2. I’m not sure if ComputeScalars is enabled by default
  3. Consider using discrete flying edges for label image
1 Like