Knowing the slice direction from a nifti image

I wrote the following Python function to calculate the slice gap from Nifti Image obtained opened using Nibabel. The key idea is to fetch the slice direction, which makes it then easy to calculate it. However, an assumption is that there is no rotation in the slicing direction was made. Is that a safe assumption? If not, how can the code be modified to be more accurate with minimum set of assumptions?

    def calculate_slice_gap(self, img):
         #img is nibabel nifti object
        # Get the transformation matrix
        transformation_matrix = img.affine


        slice_direction = None

        # Find the axis corresponding to the slice direction
        for i in range(3):
            if np.abs(transformation_matrix[2, i]) == 1:
                slice_direction = i
                break

        if slice_direction is None:
            raise Exception("the slice direction is not found")

        # Get the voxel dimensions in the transformed space
        voxel_dimensions = np.abs(np.diagonal(transformation_matrix[:3, :3]))
        slice_dim = img.header["dim"][slice_direction]  # Number of slices
        # Get the slice positions in the transformed space
        slice_positions = img.header["slice_start"] + slice_dim * voxel_dimensions[slice_direction]

        # Calculate the differences between adjacent slice positions
        slice_gaps = np.diff(slice_positions)

        # Check if the slice gaps are uniform or vary across slices
        if np.allclose(slice_gaps, slice_gaps[0]):
            # If slice gaps are uniform, return the common slice gap value
            slice_gap = slice_gaps[0]
        else:
            # If slice gaps vary, raise an error or handle the situation as desired
            raise ValueError("Slice gaps are not uniform.")

        return slice_gap

I think it might be worth trying to describe what problem you are trying to solve. For MRI scans we tend to think of three spatial values in the slice direction: the SliceThickness, the gap between slices and the SpacingBetweenSlices. So the values are:
SpacingBetweenSlices = SliceGap + SliceThickness
Thicker SpacingBetweenSlices means poorer spatial resolution, larger SliceThickness means more signal (hydrogen) and larger SliceGap reduce interference between slices for some sequences.

Unfortunately, the NIFTI header only stores the distance between slice centers. Further, the NIfTI format requires that all slices in a 3D volume are equidistantly spaced. This is unlike other formats, for example it is common for DICOM CT scans of the head to have thinner slices near the life supporting brain stem and thicker slices more superiorly. This requires DICOM to NIfTI tools to interpolate these images - here is an example.

In sum, the NIfTI header is unable to encode slice gaps and it is unable to encode variable spacing between slices in a volume. To determine the distance between slices for a NIfTI volume:

import nibabel as nib
fnm = 'ax_asc_35sl_6.nii'
img = nib.load(fnm)
print(img.header['pixdim'][3])

If you have a JSON BIDS NIfTI sidecar created by dcm2niix you can read the SliceThickness, SpacingBetweenSlices and infer the slice gap:

	"SliceThickness": 3,
	"SpacingBetweenSlices": 3.6,
2 Likes