Python - Reading Multiple Volumes from NIfTI file


some background:
For my Bachelor’s Thesis I’m working on a medical application handling dosimetry images.
For this I have to display them in 3d and 2d.
So far, no problem.

No I got a list of NIfTI files (.nii) with organ segmentations. What this should mean is that there is more than one volume stored in that file.
What I want to achieve is to read these volumes from the file and then draw them on top of each other on my window.

I can draw multiple volumes in one coordinate system, no problem.

I’ve used the vtkNIFTIImageReader to read from the file when displaying only one volume. Is this the wrong class here?

Hope somebody can help me with this problem.
If you need any more information for that I can ask my prof for more information on the files themselves.
Thanks a lot.

The vtkNIFTIImageReader will read all of the volumes that are stored in the file. The output of the reader will be a multi-component vtkImageData. By multi-component, I mean that each voxel will have N components, where N is the vector dimentions (the dim[5] value stored in the header).

If you have a way of dumping the header contents for your file, that would be very useful for figuring out exactly how they are structured. For example, use fslinfo if FSL is installed on your computer. Or, if you don’t have FSL, you can use VTK itself:

header = reader.GetNIFTIHeader()

Anyway, once you’ve called reader.Update() and reader.GetOutput().GetNumberOfScalarCompnents() to ensure that the data has the expected number of components, you can try rendering each component as its own image:

table1 = vtk.vtkLookupTable()
table1.SetRange(0, 255) # set according to the expected value range
table1.SetHueRange(0.0, 0.0) # hue of 0.0 is red
table1.SetSaturationRange(1.0, 1.0) # make color bright as possible
table1.SetValueRange(0.0, 1.0)
table1.SetAlphaRange(1.0, 1.0)

color1 = vtk.vtkImageMapToColors()
color1.SetActiveComponent(0) # for first volume

# then, display the output of color1, or feed it into
# vtkImageBlend to color-blend it with other volumes

The idea is that you can use SetActiveComponent( X ) to choose which component to apply the lookup table to. By using a different lookup table for each component, you can display each component with a different hue.

Also, if these volumes are probability maps (which is common for nifti files produced by FSL), you can call SetAlphaRange(0.0, 1.0) on the lookup table to cause it to map the probability values to opacity values. This is very useful for displaying probability maps on top of an image volume.

In any case, be sure to dump the file header, if you haven’t done so already, in order to see how the dim[] and pixdim[] have been set.

1 Like

Thanks for the quick and quite detailed answer.
Gonna work through this tomorrow.
But this looks like it is definitely gonna get me a lot closer to my goal.

Finally got to implement it today and it works perfectly.

I’m setting the outputFormat of the vtkImageMapToColor to Luminace and the taking the output imageData and storing all of those in a dict.
I then pass on those to vtkMarchingCubes to generate the surfaces in the renderWindow.

There’s a filter called vtkImageExtractComponents for taking just one component out of an image data so that it can be used in other filters like vtkMarchingCubes.

I guess the best thing would be a filter that takes an N-component image and produces N single-component images, but VTK doesn’t have a filter like that.

1 Like

I tried approaching something like that filter with my current solution.
But maybe I can make it more elegant with the vtkImageExtractComponents.
Gonna look into it.

extract = vtk.vtkImageExtractComponents()

        numComponents = self.reader.GetOutput().GetNumberOfScalarComponents()
        for i in range(numComponents):
            imageData = extract.GetOutput()

            colorNamesIndex = i % len(QColor.colorNames())
            color = QColor(QColor.colorNames()[colorNamesIndex])

            self.addSegmentation(True, f"Segmentation {i}", color, i, imageData)

That’s how I got it to work.
I then later just hook up the differen image data to MarchingCubes and it displays as intendet.