Session 9 - fMRI Flashcards
In this session, we will start looking at how to
to manipulate neuroimaging data in Python.
We are going to be using a module called
nibabel to load MRI and fMRI data to Python and save out overlays and statistical maps in the same format - old way of doing it
Going to be using Spyder
which is used for scientific programming in Python and can run python directly from machine (as compared to Colab - running it from cloud) and can produce interactive plots
All of the files for this section can be found in the - (2)
course materials repository.
They are in the s7_fmri directory.
Obtaining the data files needed for this lecture which is found in s7_fMRI directory by running this code:
What does this code mean?
git is accessing remote reposities and borrows data from YNiC (s7_fMRI directory) to local content directory
Output of this code:
After running the code:
We can check the current working directory
List the contents inside the current working directory
List what is inside pin-materials and s7_fMRI
What are these files when listening what is inside s7_fMRI? (last part) - (4)
These are all data files
‘highres.nii.gz’ is a anatomical file - 3D high-resolution structural image (T1) of someone’s brain
‘highres_brain.nii.gz’ - word brain means we have stripped away the skull and just looking at the brain (skull-stripped image of the brain) - using FSL command called BET
There are two functional imaging data files here/datasets which are 3D snapshots of the brain taken every 2 seconds (TR) while its doing a task: 1) filtered_func_data.nii.gz and func_data.nii.gz
What does
‘ls-l’ mean?
Listening contents of files and directories in long format (“l” at bottom)
The ‘Babel’ part of nibabel refers to
the ‘Tower of Babel’.
The nibabel module has
reading and writing routines for various neuroimaging formats - so that we can all speak the same neuroimaging ‘language’ or something.
Most MRI data are taken off scanners in a format known as
DICOM
What does DICOM stand for?
Digital Imaging and Communications in Medicine)
Most MRI data are taken off the scanners in a format known as DICOM - this format is
very complex so scientists almost always convert the DICOM images from the scanner into NIFTI
What does NIFTI stand for?
Neuroimaging Informatics Technology Initiative
Most MRI data are taken off scanners in a format known as DICOM which is complex and scientist always conert to NIFTI and at YNIC this is done
automatically
NIFTI files end in
nii.gz, gz means the file is compressed so does not take up much space on disk
What are the two parts of NIFTI files, and what does each part contain? - (3)
NIFTI files consist of a ‘header,’ which contains information about
the image (e.g., how big the image is, how big the voxels are, many voxels there are in each direction, number of volumes, TR),
and the actual imaging data which is just an array of numbers: usually integers for raw data that has come from scanner; often floating point for processed data
The first dataset we will look at is in the folder is a high-resolution anatomy file called ‘highres.nii.gz’ which contains - (2)
a single T1-weighted scan of a brain
It was acquired slice by slice : 176 slices running >across< the head from Left to Right in steps of 1mm with 256x256 1mm x 1mm voxels in each slice.
Let’s move into the s7_fMRI which contains ‘highres.nii.gz’ directory for rest of tutorial by doing this which means
This code changes the current working directory to ‘/content/pin-material/s7_fmri’
So we are going to examine the ‘highres.nii.gz’ by loading it using nibabel
Thus, we need to import the module first:
This is common practice, analogous to importing numpy as np.
We will now use nibabel to load an fMRI file into Python and examine it of ‘highres.nii.gz’ using nibabel:
Explain this code:
anatImage = nib.load(‘highres.nii.gz’) - (2)
This code utilizes the ‘nibabel’ library to load a structural NIFTI file named ‘highres.nii.gz’.
The function ‘nib.load()’ reads the file and stores its contents in the variable ‘anatImage’, which includes both the header information and the data of the image - two partsof NIFITY
After using nibabel nib.load to load the structural NIFITI file (‘highres.nii.gz’) we can check the type of object returned by file and what type of object is returned which prints out…
this prints out: <class ‘nibabel.nifti1.Nifti1Image’>
We can also make a variable and store header info using anatImage.header and check its type which prints out…
this prints out:
We can also look at the keys of the header using
this print(hdr.leys()) is - (2)
This code prints out the keys present in the header of a NIFTI file.
Think of these keys as analogous to columns in a table. They provide information about various attributes and properties of the NIFTI image data.
The output of this code is
Explain this output:
These are all the stuff you would acquire as you run the MRI scanner such as extent of the data, how big the head was, what is sequence is, how tiny voxels are, coverage of brain, how was imaging planes oriented to acquire data
There are many items in the header but we are interested in
dim and pixdim entries
We can also look at the dim entries in which treating header like a dictionary and requesting ‘dim’ key from it using
print(hdr[‘dim’]) and see as output
What does this mean from print(hdr[‘dim’]) - (3)
When we look at dim, we find an array of integers
The 0th element (3 in this case) refers to how many dimensions of data there are; as this is a structural or anatomical dataset, there are 3-dimensions.
The remaining values tell us the number of voxels in each of the dimensions. As we only have three dimensions, only elements 1 to 3 (inclusive) have any meaning; the rest can be ignored.
When we show pixel dimensions header print(hdr[‘pixdim’]) - explain - (6)
In this code snippet, we’re accessing the ‘pixdim’ header in the NIFTI file, which provides information about the size of each voxel in millimeters.
These numbers are floats because they refer to the size in mm:
The printed array, [1. 1. 1. 1. 2.3 0. 0. 0.], contains float values representing voxel sizes.
Positions 1 to 3 (1.0) are relevant, indicating the voxel sizes in each dimension.
In this example, the numbers are all 1.0 which makes sense because the voxel sizes are 1mm in all dimensions
Additionally, the value in position 4 (2.3) refers to the ‘repetition time’ (TR) of the image, which might be relevant depending on the type of MRI data.
More detail on this output - (2)
We have taken 176 slices in sagittal plane/direction (going through middle of brain)
Each sagittal section is 256 by 256 voxels
Explain this output in more detail - (5)
Position 1: Width of the image in mm.
Position 2: Height of the image in mm.
Position 3: Depth or slice thickness of the image in mm.
For example, if positions 1 to 3 contain [1.0, 1.0, 1.0], it indicates that each voxel (3D pixel) in the image is 1mm x 1mm x 1mm in size.
So, if we’re considering the array [1. 1. 1. 1. 2.3 0. 0. 0.], positions are typically indexed starting from 0.
Next, we can load the actual image data of ‘highres.nii.gz’ using nib.looad instead of its header
Explain this code - (7)
import nibabel as nib
: Importing the Nibabel library, used for reading and working with neuroimaging data.
anatImage = nib.load(‘highres.nii.gz’)`: Loading a structural NIFTI image from a file called ‘highres.nii.gz’.
data = anatImage.get_fdata()
: Extracting the data from the loaded NIFTI image, storing it as floating-point numbers.
print(data.shape)`: Printing the dimensions of the data array, indicating the size of the image - (176 by 256 by 256)
print(data.dtype)`: Printing the data type of the elements in the data array, indicating they are floating-point numbers - float64
print(data[88, 127, 127])
: Printing the value of a voxel located at coordinates (88, 127, 127) within the image. - 274.0 - saying how big the voxel is
print(data[1, 1, 1])`: Printing the value of a voxel located at coordinates (1, 1, 1), typically near the edge of the image (more likely to be small number as most black space near the head) - 4.0
Output of this code
Instead of looking at the value at one voxel, we can instead peek at one slice of data and to do this we need to
import pyplot using import matplotlib.pyplot as plt
Code producing one slice of data instead of one voxel
Explain this code - (4)
import matplotlib.pyplot as plt
: Importing the Matplotlib library for visualization.
aSlice = data[124,:,:]
: Extracting a single sagittal slice of brain data from a 3D volume stored in the variable ‘data’. This slice is located at index 124 (pass midpoint off the edge) along the first axis, which typically represents the sagittal plane (out of 176 slices) if we say index 88 its more or else down the middle
plt.grid(True)
: Configuring the plot to display grid lines for better visualization.
- plt.imshow(aSlice)
: Displaying the extracted sagittal slice using Matplotlib's
imshow()` function, which visualizes a 2D array as an image. In this case, it displays the 2D slice of brain data.
Output of this code:
matplot lib has a function called
plt.imshow() which if you give a two-dimensional slice of data then it will make a picture of it
First of all the output of this is messy as it
t is standing up on its nose and the colour map is weird.