Update 4/6/2022: Check out our ciftiTools R package, available via CRAN and Github!
install.packages(“ciftiTools”) #install from CRAN
library(devtools); install_github(“mandymejia/ciftiTools”) #install from GitHub
We also have a paper about it out in NeuroImage:
Pham, D., Muschelli, J., & Mejia, A. (2022). ciftiTools: A package for reading, writing, visualizing, and manipulating CIFTI files in R. NeuroImage, 118877. https://doi.org/10.1016/j.neuroimage.2022.118877
[Update: Here is another nice intro to CIFTI files, by Jo Etzel at WashU.]
My research group recently began working with the Human Connectome Project (HCP) dataset, a large database of resting-state fMRI, task fMRI and other brain imaging and demographic data for 500+ subjects. The HCP is pretty unique in that it combines a large number of subjects with long scans and a standardized scanning protocol. This distinguishes it from other large neuroimaging datasets, such as ABIDE, ADHD-200 or the 1000 Functional Connectomes Project, which are grassroots compilations of data collected across multiple sites with different scanning equipment, acquisition protocols, preprocessing pipelines and quality control procedures. Most of the resting-state scans in those datasets are 5-10 minutes long, whereas in the HCP there are 60 minutes of resting-state data collected for each subject.
Our group and many others are interested in using the HCP to develop and evaluate different ways of analyzing brain imaging data. However, some aspects of the HCP are new to many researchers, and it’s not always obvious from an outside perspective how to work with and interpret the data. One example of this is data stored in “grayordinates” using the CIFTI file format. While the HCP also contains data in volumetric space (in NIFTI file format), working with the grayordinates is appealing for a number of reasons that I won’t elaborate on here, except to say that the idea is to isolate the gray matter, which consists of cortical, subcortical and cerebellar areas. However, working with CIFTI files can be daunting for a new user, and the documentation is not always very user-friendly.
I recently started working with these files, so based on my experience here is a basic introduction to navigating, reading and writing CIFTI files with MATLAB. This explanation is based on my limited experience, but it will hopefully be helpful for other novice users. Please feel free to make additions or corrections in the comments!
Part 1: Navigating CIFTI files
The first thing to know about CIFTI files is that they contain several parts, similar to a structure in MATLAB or a list in R. In that sense, they are very different from NIFTI files, which are essentially just 3D or 4D arrays of intensities. Furthermore, there are several types of CIFTI files, including times series (*.dtseries.nii), parcellations (*.dlabel.nii), and scalar images (*.dscalar.nii), and the parts or “fields” contained in each type of CIFTI file are different (though they have several commonalities).
Consider a CIFTI file representing time series data. The time series themselves are stored in a VxT matrix, where V is the number of grayordinate “voxels” and T is the number of time points in the series. (Technically “voxels” only refer to volumetric space, but it’s convenient to have a single term for both surface and volumetric elements… please forgive the abuse of notation.) The total number of voxels in a CIFTI file is around 90,000, including about 60,000 surface voxels (about 30,000 per hemisphere) and 30,000 subcortical and cerebellar voxels. The surface voxels are a 2D representation of the cortical gray matter, while the subcortical voxels are still in 3D/volumetric space. Each surface grayordinate is essentially an average of several cortical gray matter voxels in the original volumetric scan. So what else does a CIFTI file contain? Common to all types of CIFTI files are the following fields:
- brainstructure: a vector of length V with a numerical indicator (1-21) for the major brain structure that each voxel forms part of
- brainstructurelabel: a vector of length 21 with the name of each major brain structure (e.g. CORTEX_LEFT, CORTEX_RIGHT, CAUDATE_LEFT, CAUDATE_RIGHT, CEREBELLUM_LEFT, CEREBELLUM_RIGHT, etc.)
- pos: a Vx3 matrix of the x-y-z coordinates of each voxel. However, only the subcortical and cerebellar voxels have coordinates specified; for surface voxels, the values are missing.
In addition, each type of CIFTI file contains additional information.
- A time series (*.dtseries.nii) CIFTI file contains time (1xT) and dtseries (VxT), where time is the timing (in seconds) of each time point (0, 0.72, 1.44, …) and dtseries is the time series of each voxel.
- A parcellation (*.dlabel.nii) CIFTI file contains some parcellation field myfield (Vx1) and myfieldlabel (1xQ), where Q is the number of parcels. For example, in the HCP groupICA parcellation files (melodic_IC_ftb.dlabel.nii), the parcellation field is named “indexmax“, and Q is the number of ICA components. (As a side note, I believe “indexmax” refers to how spatial components from a groupICA were used to create a parcellation, namely by identifying the component with the maximum z-score at each voxel.) In the FreeSurfer anatomical parcellation files ending in aparc.32k_fs_LR.dlabel.nii, theparcellation field is named “x100307_aparc”, and Q=70.
- A scalar (*.dscalar.nii) CIFTI file contains one or more fields field1 (Vx1), field2 (Vx1), etc., each representing a scalar image of some sort. For example, in the HCP groupICA spatial maps are represented as this type of file, with the fields x1,….,xQ representing the Q spatial maps. Structural images for each subject are also represented as .dscalar files. For example, the files ending in MyelinMap_BC.32k_fs_LR.dscalar.nii contain the field myelinmap_bc, which contain the estimated myelin content of each of the roughly 60,000 surface voxels.
You might have noticed that I never mentioned where CIFTI files store the location of each surface voxel. That’s because they don’t! This is because the surface can be “inflated” to varying degrees, from very “wiggly” (showing full anatomical detail) to completely flattened (showing no anatomical detail). Depending on the degree of inflation, the 3-dimensional location of each surface voxel changes! For a given degree of inflation, the x-y-z location of each voxel are stored as a different type of file, called a GIFTI. I’ll mention below how to read and navigate these files in MATLAB too.
Part 2: Reading CIFTI files with MATLAB
I use the FieldTrip toolbox, which includes the function ft_read_cifti. Once you have the toolbox downloaded and have added to the path, reading a CIFTI file is as simple as mycifti = ft_read_cifti(‘fname.dsomething.nii’). mycifti is then a MATLAB structure containing fields such as the ones described above. Each field can be accessed or altered with the “.” operator, i.e. mycifti.fieldname.
As mentioned above, CIFTI files do not contain the x-y-z coordinates of the surface voxels. These are instead contained in GIFTI files with names ending in “surf.gii”. In the HCP dataset, the level of inflation is indicated in the file name and includes “flat”, “inflated”, “mid thickness”, and “very_inflated”. The left- and right-hemispheres are stored in separate GIFTI files. The GIFTI toolbox can be used to read GIFTI files into MATLAB. After installing and adding the toolbox, mygifti = gifti(‘fname.surf.gii’) results in a MATLAB structure with several fields, including vertices, a VL x 3 matrix containing the x-y-z coordinates of each voxel, where VL is around 30,000, the number of surface voxels in one hemisphere of the brain. To map the voxels in a GIFTI file to the corresponding voxels in a CIFTI file, reference the brainstructure field: all voxels with brainstructure==1 (CORTEX_LEFT) map to those in a GIFTI file for the left hemisphere; all voxels with brainstructure==2 (CORTEX_RIGHT) map to those in a GIFTI file for the right hemisphere.
Part 3: Writing CIFTI files with MATLAB
I have found this to be the most challenging aspect of working with CIFTI files, in part because the documentation for the ft_write_cifti function seems to be somewhat limited. Furthermore, since there are different types of CIFTI files, you cannot simply run ft_write_cifti(‘fname’, mycifti), even if mycifti is simply the result of mycifti = ft_read_cifti(‘fname.d*.nii’). Instead, you must provide some additional information in the form of “optional” key-value pairs. Also, in general I’ve found that you must respect the original file type. For example, to save a parcellation you should start by reading in a .dlabel file; to save a static image you should start by reading in a .dscalar.nii file; etc. Here are a few examples.
- ft_write_cifti(‘fname’, mycifti, ‘parameter’, ‘dtseries’) saves the object mycifti, which has a dtseries field containing the functional data, as the CIFTI file fname.dtseries.nii.
- ft_write_cifti(‘fname’, mycifti, ‘parameter’, ‘x1’, ‘parameter’, ‘x2’) saves the object mycifti, which has fields x1 and x2 containing two static images, as the CIFTI file fname.dscalar.nii.
- ft_write_cifti(‘fname’, ‘mycifti’, ‘parcellation’, ‘parcels’, ‘parameter’, ‘parcels’) or ft_write_cifti(‘fname’, ‘mycifti’, ‘parameter’, ‘parcels’) saves the object mycifti, which has a field parcels containing the labels of each voxel, as the CIFTI file fname.dscalar.nii. (I haven’t figured out how to save a .dlabel file. Running ft_write_cifti(‘fname’, ‘mycifti’, ‘parcellation’, ‘parcels’) throws an error that indicates that ‘parameter’ is required. Specifying ‘parameter’ and ‘parcellation’ or just ‘parameter’ results in a .dscalar file. If anyone knows how to do this, I’d love to know!)
One aspect of working with CIFTI files that I have conspiciously ignored is visualization. This is obviously a very important aspect of working with imaging data, but for now let’s just say it’s next on my to-do list. Luckily another member of our group, Mary Beth Nebel, has started working with the Connectome Workbench, and has produced some beautiful figures, including the ones below. I hope to pick her brain soon and report back!
Scan-rescan reliability by ICA parcel (Q=100):
Group ICA parcellations:
I’ve been struggling with ft_write_cifti for a while now and your article is so helpful! Thank you!
I’m very glad to hear it was helpful! I’ve actually found the cifti-matlab function to be a little easier to use than fieldtrip, in case you’re having any issues.
Very helpful indeed. Thank you!
I’m so glad to hear it!
[…] A layman’s guide to working with CIFTI files […]
Great job and crystal clear introduction, Mandy! Keep your awesome work!
Thumbs up and thank you!
Great introduction. Do you know if there is an easy way to make statistical comparisons between CIFTI files? For example, I would like to create a whole-brain (surface “voxels” + subcortical / cerebellar voxels) significance map, corrected for multiple comparisons, which illustrates the differences in resting-state connectivity between two voxels.
You would think such a command would be included in the “-cifti-math” command in the HCP Connectome Workbench. Basically, making a simple contrast map between two cifti files is what I would like to do.
Anyway, great article and thank you for posting.
Hi Michael, thanks for the nice comments. I don’t believe that the connectome workbench has statistical analysis capabilities, and I’m not sure if any other software packages are doing that for CIFTI yet. You should be able to read in the data to MATLAB using ft_read_cifti or ciftiopen, then use existing matlab packages (such as SPM or Canlab tools) to do a simple command line analysis. The main difference is that the data is now in matrix form without spatial information, rather than in array/volumetric form, so whatever software you use should be designed to accept that format. That also means that if you want to do any smoothing, you’ll need to do it beforehand using the workbench commands (e.g. cifti-smoothing). The help files are pretty good, but the hcp-users mailing list archive has a ton of Q&A with examples that can be a good supplement to the official documentation. Hope that helps!
Thank you very much Mandy. Your post provided me a gentle introduction to process Human Connectome data. Regarding the generation of ‘dlabel.nii’, I think it’s not supported by ft_write_cifti function (checked the source code of the function). Later I came across the following discussion on how to generate dlabel file:
https://www.mail-archive.com/hcp-users@humanconnectome.org/msg05229.html
Using the Matlab functions provided by Human Connectome Project(ciftiopen.m and ciftisave.m; see the following link), I am able to create the dlabel file.
https://wiki.humanconnectome.org/display/PublicData/HCP+Users+FAQ#HCPUsersFAQ-2.HowdoyougetCIFTIfilesintoMATLAB?
Thanks for sharing, Jithin! I don’t think I was using the ciftiopen/ciftisave functions when I wrote the post, but they are definitely a good resource in addition to or in place of the cifti-matlab or fieldtrip toolboxes. I do find them easier in particular for writing out CIFTI files. Unfortunately I don’t think ciftiread loads in any meta data (like the brain structure each grayordinate belongs to), so I often still need to use cifti-matlab for that.
I’m japanese student and not good at english. So, I’m sorry that I may make some mistakes in this comment.
Thank you very much for great explanation about CIFTI. However, I have a question. By using FieldTrip to a CIFTI file(*.dtseries.nii), I got a 91282×1200 matrix. According to your explanation, 91282 rows include both surface voxels and subcortical and cerebellar voxels. My question is about this surface voxels. Now I want to know which surface voxel value in CIFTI matches surface voxel in GIFTI(surf.gii). I understand that surface voxel locations are not stored in CIFTI file.
Hi Shoma, thank you for your comment. You are correct, CIFTI files contain no information about surface voxel locations. That is because the surface can be inflated to different levels, such as the subject’s white matter surface or the template inflated surface. The location information about the surface is located in the GIFTI files that are matched to the CIFTI file. These GIFTI files should have the same number of surface vertices, so the rows of the CIFTI matrix should be able to be matched up one-to-one with the rows of the “vertices” matrix in the GIFTI file. Hope this helps!
Thank you very much Ms. Mandy. I have understood that it is very simple. Your reply is very helpful for my graduation research. Thank you.
I just ran across this by chance, and to set the record straight, CIFTI can also exclude vertices (in particular, ones that are inside the medial wall), and the standard 91282 grayordinates files use this exclusion. Each hemisphere has only about 30K of the 32K vertices stored in these cifti files. You can use wb_command -cifti-export-dense-mapping to output text files that match up the cifti indices to the correct vertices.