How to read a Cifti file header with nibabel

Hi everyone,

I am trying to understand the Cifti structure. I read this tutorial Jupyter Notebook Viewer but it did not answer all my questions. Maybe someone could point me another tutorial to handle cifti files with nibabel ?

I am trying to get access to my cifti file header.
I used the following code :

cifti = nb.load(fname)
cifti_data = cifti.get_fdata(dtype=np.float32)
cifti_hdr = cifti.header
nifti_hdr = cifti.nifti_header

I can use the print(nifti_hdr) to read the nifti header but print(cifti_hdr) just gives me this object <nibabel.cifti2.cifti2.Cifti2Header object at 0x7faeae8c4850> that I am not able to read.

The information that I am trying to find is how to interpret the 2D matrix that I get from the .get_data() function. Right now I have a cifti file, I can load it with nibabel, get a 2D matrix but I don’t know to what it corresponds. I am sure that there is a nibabel cifti file tutorial to explain how to handle this but I am not able to find it…

Thanks a lot for your help !

Best,

Charles

1 Like

What you found is probably the most comprehensive guide to CIFTI-2 in Python that I’ve seen (full disclosure: I wrote it). I would strongly suggest using the cifti_axes API, which you can get with:

cifti_axes = [cifti_hdr.get_axis(i) for i in range(cifti.ndim)]

Very likely you have a BrainModel axis and a Series axis (usually (Series, BrainModel)). The series is the time axis indicating that the rows of your get_fdata() array are time points, and the brain model axis shows the grayordinates (see Glasser et al, 2016).

Unfortunately, we don’t have great representations of the axes like print(nifti_hdr). The reason that works nicely is that NIfTI headers are a set of named fields with pretty straightforward interpretations. CIFTI headers are XML, and therefore flexible enough to not have a clean representation. You can serialize to XML (print(cifti_hdr.to_xml)) but that’s not much more helpful.

The BrainModelAxis class has an iter_structures() method that can be useful:

>>> axes = [cii.header.get_axis(i) for i in range(cii.ndim)]
>>> axes
[<nibabel.cifti2.cifti2_axes.SeriesAxis at 0x7fc1a7612e10>,
 <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7f26f60>]
>>> list(axes[1].iter_structures())
[('CIFTI_STRUCTURE_CORTEX_LEFT',
  slice(0, 29696, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a781aef0>),
 ('CIFTI_STRUCTURE_CORTEX_RIGHT',
  slice(29696, 59412, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7ecddd8>),
 ('CIFTI_STRUCTURE_ACCUMBENS_LEFT',
  slice(59412, 59547, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7ecd908>),
 ('CIFTI_STRUCTURE_ACCUMBENS_RIGHT',
  slice(59547, 59687, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7ecdf60>),
 ('CIFTI_STRUCTURE_AMYGDALA_LEFT',
  slice(59687, 60002, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7ecdba8>),
 ('CIFTI_STRUCTURE_AMYGDALA_RIGHT',
  slice(60002, 60334, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7bc9d68>),
 ('CIFTI_STRUCTURE_BRAIN_STEM',
  slice(60334, 63806, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7bc9c18>),
 ('CIFTI_STRUCTURE_CAUDATE_LEFT',
  slice(63806, 64534, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7bc9048>),
 ('CIFTI_STRUCTURE_CAUDATE_RIGHT',
  slice(64534, 65289, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7bc9dd8>),
 ('CIFTI_STRUCTURE_CEREBELLUM_LEFT',
  slice(65289, 73998, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7bc9e80>),
 ('CIFTI_STRUCTURE_CEREBELLUM_RIGHT',
  slice(73998, 83142, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7bc9e48>),
 ('CIFTI_STRUCTURE_DIENCEPHALON_VENTRAL_LEFT',
  slice(83142, 83848, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7bc9c50>),
 ('CIFTI_STRUCTURE_DIENCEPHALON_VENTRAL_RIGHT',
  slice(83848, 84560, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7bc9240>),
 ('CIFTI_STRUCTURE_HIPPOCAMPUS_LEFT',
  slice(84560, 85324, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7bc9e10>),
 ('CIFTI_STRUCTURE_HIPPOCAMPUS_RIGHT',
  slice(85324, 86119, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7ec8198>),
 ('CIFTI_STRUCTURE_PALLIDUM_LEFT',
  slice(86119, 86416, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7ec8a90>),
 ('CIFTI_STRUCTURE_PALLIDUM_RIGHT',
  slice(86416, 86676, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7ec85f8>),
 ('CIFTI_STRUCTURE_PUTAMEN_LEFT',
  slice(86676, 87736, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7ec8208>),
 ('CIFTI_STRUCTURE_PUTAMEN_RIGHT',
  slice(87736, 88746, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7ec8c50>),
 ('CIFTI_STRUCTURE_THALAMUS_LEFT',
  slice(88746, 90034, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7ec8be0>),
 ('CIFTI_STRUCTURE_THALAMUS_RIGHT',
  slice(90034, None, None),
  <nibabel.cifti2.cifti2_axes.BrainModelAxis at 0x7fc1a7ecdb38>)]
4 Likes

Thank you so much this is very clear and it works, and thank you for writing this tutorial which is very useful.

Just a very additional question :

load data

cifti = nb.load(fname)
cifti_data = cifti.get_fdata(dtype=np.float32)
cifti_hdr = cifti.header
nifti_hdr = cifti.nifti_header

save the data

img = cifti2.Cifti2Image(cifti_data, header=cifti_hdr)
img.to_filename(’/path/test.dtseries.nii’)

Would those two last lines be the ideal way to save my file for an I/O workflow ?

I think that should work. You can also pass nifti_header=nifti_hdr, but CIFTI goes out of its way not to use much of the NIfTI header, so it probably doesn’t make a difference.

1 Like