Session 9 - fMRI Flashcards

1
Q

In this session, we will start looking at how to

A

to manipulate neuroimaging data in Python.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

We are going to be using a module called

A

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Going to be using Spyder

A

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

All of the files for this section can be found in the - (2)

A

course materials repository.

They are in the s7_fmri directory.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Obtaining the data files needed for this lecture which is found in s7_fMRI directory by running this code:

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

What does this code mean?

A

git is accessing remote reposities and borrows data from YNiC (s7_fMRI directory) to local content directory

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Output of this code:

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

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

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

What are these files when listening what is inside s7_fMRI? (last part) - (4)

A

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

What does

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

‘ls-l’ mean?

A

Listening contents of files and directories in long format (“l” at bottom)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

The ‘Babel’ part of nibabel refers to

A

the ‘Tower of Babel’.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

The nibabel module has

A

reading and writing routines for various neuroimaging formats - so that we can all speak the same neuroimaging ‘language’ or something.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Most MRI data are taken off scanners in a format known as

A

DICOM

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

What does DICOM stand for?

A

Digital Imaging and Communications in Medicine)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Most MRI data are taken off the scanners in a format known as DICOM - this format is

A

very complex so scientists almost always convert the DICOM images from the scanner into NIFTI

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

What does NIFTI stand for?

A

Neuroimaging Informatics Technology Initiative

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

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

A

automatically

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

NIFTI files end in

A

nii.gz, gz means the file is compressed so does not take up much space on disk

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

What are the two parts of NIFTI files, and what does each part contain? - (3)

A

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

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

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

Let’s move into the s7_fMRI which contains ‘highres.nii.gz’ directory for rest of tutorial by doing this which means

A

This code changes the current working directory to ‘/content/pin-material/s7_fmri’

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

So we are going to examine the ‘highres.nii.gz’ by loading it using nibabel

Thus, we need to import the module first:

A

This is common practice, analogous to importing numpy as np.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

We will now use nibabel to load an fMRI file into Python and examine it of ‘highres.nii.gz’ using nibabel:

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Q

Explain this code:

anatImage = nib.load(‘highres.nii.gz’) - (2)

A

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
26
Q

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…

A

this prints out: <class ‘nibabel.nifti1.Nifti1Image’>

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
27
Q

We can also make a variable and store header info using anatImage.header and check its type which prints out…

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
28
Q

this prints out:

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
29
Q

We can also look at the keys of the header using

this print(hdr.leys()) is - (2)

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
30
Q

The output of this code is

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
31
Q

Explain this output:

A

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
32
Q

There are many items in the header but we are interested in

A

dim and pixdim entries

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
33
Q

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

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
34
Q

What does this mean from print(hdr[‘dim’]) - (3)

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
35
Q

When we show pixel dimensions header print(hdr[‘pixdim’]) - explain - (6)

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
36
Q

More detail on this output - (2)

A

We have taken 176 slices in sagittal plane/direction (going through middle of brain)

Each sagittal section is 256 by 256 voxels

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
37
Q

Explain this output in more detail - (5)

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
38
Q

Next, we can load the actual image data of ‘highres.nii.gz’ using nib.looad instead of its header

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
39
Q

Explain this code - (7)

A

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
40
Q

Output of this code

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
41
Q

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

A

import pyplot using import matplotlib.pyplot as plt

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
42
Q

Code producing one slice of data instead of one voxel

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
43
Q

Explain this code - (4)

A

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.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
44
Q

Output of this code:

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
45
Q

matplot lib has a function called

A

plt.imshow() which if you give a two-dimensional slice of data then it will make a picture of it

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
46
Q

First of all the output of this is messy as it

A

t is standing up on its nose and the colour map is weird.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
47
Q

We can then load the data again

A
48
Q

To change the colour map of the output - (2)

A

We can do this

plt.imshow(slc, cmap=’gray’) - # change colour map to grey

49
Q

What does this code mean in more detail? - (6)

A

take 124 elemens through one dimension, : take every row and column through 124 slices of the head

We pick slice 124

The dimensions here (see Advanced material below) are

L-R, P-A and I-S

Slice 124 from the first dimension is more or less through the middle of the right hemisphere

The remaining dimensions are P-A and I-S

50
Q

What does this output display - white stuff and grey stuff - (2)

A
  • white stuff is fat
  • grey stuff is neurons - convey communication between different parts of grey matter
51
Q

Highlight positions on the head

A
52
Q

We would like to flip the image the right way around - (3)

A

plt.show() then using tranpose function which takes a data set and spins it around the axis - it swaprs the dimensions so swaps vertical and horizontal axis

It would need anterior and posterior running along x axis - be upside down

so to fix then take that slice and resample in opposite direction [::-1,:] - pick all rows and columns but pick them in reverse order

53
Q

Flipping image on right way around - first tranpose

What does this code do first? - (7)

A

It selects a single slice (slc) from the 3D brain data (data), specifically the slice at index 124 along the first axis.

It plots the selected slice using plt.imshow() with a grayscale colormap (cmap=’gray’).

It adds a title ‘Original’ to the plot.

It displays the plot using plt.show().

After plotting the original slice, the code recognizes that the image orientation needs adjustment for proper display.

It transposes the dimensions of the slice (slc.transpose(1, 0)) to swap the axes, changing the orientation from [I-S, P-A] to [P-A, I-S].

It plots the transposed slice using plt.imshow() with the same grayscale colormap.

54
Q

After tranposing we need to reverse the image

Explain the reverse part - (7)

A

Remember how we can use the -1 operator to reverse the order in which we choose numbers from a list? - resample to opposite direction so all row and columns to minus 1

list[<start>:<stop>:<step>]</step></stop></start>

if you leave out an argument it just assumes ‘the start’ or ‘the end’ or ‘1’

So slc[::-1,:] means ‘I want the 2D matrix from slc but give me the first dimension (the rows) stepping ‘backwards’ in steps of 1,

and then leave the other dimension (the columns) unchanged’

his operation effectively flips the image vertically, changing the orientation from inferior-superior (I-S) to superior-inferior (S-I).

remeber dimensions are : # L-R, P-A and I-S

55
Q

Explain rest of this code: after tranposing we want to we would like to have the axes labelled correctly in mm.
# So we need to know how big
# the image is on each side. That is found from the header:
# pixelSize * numberOfPixels - (8)

A

ap_npix = anatData.header[‘dim’][2]`: Retrieves the number of pixels along the anterior-posterior (A-P) direction from the image header.

  • si_npix = anatData.header['dim'][3]: Retrieves the number of pixels along the inferior-superior (I-S) direction from the image header.
  • ap_pixdim = anatData.header['pixdim'][2]: Retrieves the pixel dimensions (in millimeters) along the A-P direction from the image header.
  • si_pixdim = anatData.header['pixdim'][3]: Retrieves the pixel dimensions (in millimeters) along the I-S direction from the image header.
  • Setting Image Extent:
    • extent = (0, si_npix*si_pixdim, 0, ap_npix*ap_pixdim): Calculates the extent parameter for the image plot, which defines the spatial dimensions in millimeters. It specifies the extent of the image along the x-axis (I-S) and y-axis (A-P) based on the number of pixels and pixel dimensions.
  • Plotting the Image:
    • plt.imshow(slc, cmap='gray', extent=extent): Plots the image (slc) using Matplotlib’s imshow() function with a grayscale colormap (cmap='gray') and the calculated extent parameter. This ensures correct scaling and spatial representation of the image in millimeters.
    • plt.title('Final image'): Sets the title of the plot to ‘Final image’ using plt.title().
    • plt.show(): Displays the final image plot.
56
Q

Output after tranposing, reversing and setting image extent:

A
57
Q

The dimensions of our image are: [L-R, P-A, I-S]. We have taken a single slice in the left-right direction which means that we are left with data which is [P-A, I-S].

We are using the matplotlib imshow() routine whch

A

This takes data as if it were a photograph, i.e. the bottom left hand entry in our data matrix will be at the bottom left of the displayed image.

58
Q

More detail what does transpose do and reverse? - (3)

A

At present, we have [P-A, I-S], so we start by using the .transpose() function to reverse the orders of the axes, ending up with [I-S, P-A].

This looks nearly right, except that we want our data in row 0 to be the most superior data and the data in the last row (255) to be the most inferior: i.e. our I-S axis needs to be S-I.

To do this, we just reverse the data in that dimension using the ::-1 syntax`(see below).

59
Q

What does plt.show() and extent() do near end of code? - (7)

A

Now that we have re-ordered our data, we can pass it to imshow().

In the imshow() call, we use an extra argument which we have not seen before: extent.

This allows us to set the pixel dimensions for the image.

This is not actually that important in this case because the MRI image pixel size is 1x1mm, so it makes no difference.

If, however, your image was 1.13x1mm or similar, you would end up with a distorted image if you do not make this correction.

What we do is to multiply the number of pixels in each dimension by the size of the pixels in that dimension.

This makes matplotlib scale the image properly.

60
Q

What does red plot do near end of code?

A

To show off, we also show that we can plot points on the same plot - so, we add a red point at a specific point on the image; we could also add text, arrows and anything else which we can do in matplotlib.

61
Q

hat you have there is a ‘sagittal’ slice. There are two other types of slice you can make: ‘axial’ and ‘coronal’. - (2) which are..

A

Axial slices are parallel to the ground if you are standing up

Coronal slices are vertical (if you are standing up) and run across the brain from left to right (so, for example, there is a coronal slice at the front of the head that has both of your eyes and your nose and teeth in it - we call this ‘Scary Slice’)

62
Q

Explain this part of the code that tranposes and reverses brain image NIFTI file that it got to right orientation - (18)

A
  • Loading Data:
    • import os: Imports the os module to interact with the operating system.
    • import nibabel as nib: Imports the nibabel library for working with neuroimaging data.
    • import matplotlib.pyplot as plt: Imports the matplotlib library for plotting.
    • os.chdir('/content/pin-material/s7_fmri'): Changes the current working directory to ‘/content/pin-material/s7_fmri’.
    • s = nib.load('highres.nii.gz'): Loads the structural NIFTI file named ‘highres.nii.gz’.
    • data = s.get_fdata(): Loads the image data from the NIFTI file as floating point numbers.
  • Plotting Original Slice:
    • slc = data[:, :, 100]: The syntax [:, :, 100] selects all rows and columns (full 2D slice) at the specific index 100 along the third axis (I-S direction) of the data volume.
    • plt.imshow(slc, cmap='gray'): Plots the selected slice with a grayscale colormap.
    • plt.title('Original'): Sets the title of the plot to ‘Original’.
    • plt.show(): Displays the plot.
  • Adjusting Orientation:
    • slc = slc.transpose(1, 0): Transposes the dimensions of the slice, effectively swapping the axes from inferior-superior (I-S) to posterior-anterior (P-A).
      • This operation changes the orientation of the slice by exchanging the axes, which can be useful for adjusting the display orientation.
    • slc = slc[::-1, :]:
      • By reversing the rows, the orientation of the slice is corrected, ensuring proper anatomical orientation. effectively reverses the order of rows in the 2D array slc, flipping the slice vertically. This operation ensures that the slice is properly oriented along the inferior-superior (I-S) axis,
  • Plotting Final Slice:
    • plt.imshow(slc, cmap='gray'): Plots the adjusted slice with a grayscale colormap.
    • plt.title('Final'): Sets the title of the plot to ‘Final’.
    • plt.grid(True): Adds grid lines to the plot for better visualization.
    • plt.show(): Displays the final plot.
63
Q

Regardless of the selected slice (slc = data[:, 124, :], slc = data[124,:,:], or slc = data[:,:,124]), the following steps adjust its orientation - (2)

A

Transpose Operation: slc = slc.transpose(1, 0)
- Swaps the axes of the 2D slice, effectively rotating it.

2. Reverse Operation: `slc = slc[::-1, :]`
   - Flips the slice vertically by reversing the order of its rows.
64
Q

When slc =data[:, 124, :], what happens when transposing and reversing the NIFIT image? - (3)

A

slc = data[:, 124, :]: This selects a single slice from the MRI volume. The colon : indicates that all elements are selected along the first and third dimensions (likely the left-right and inferior-superior directions), while selects one slice at index 124 along the second dimension (likely the anterior-posterior direction).

slc = slc.transpose(1, 0): This transposes the selected slice. Therefore, [::-1, :] effectively reverses the order along the second axis (P-A) while keeping all elements along the first axis (L-R) and third axis (I-S) unchanged

slc = slc[::-1, :]: This reverses the order of rows in the slice, flipping it vertically. The [::-1, :] syntax means reversing the order along the first axis (anterior-posterior) while leaving the second axis (left-right) unchanged. This step ensures proper anatomical orientation, aligning the slice from inferior to superior. GOES P-A THEN A-P

65
Q

When
slc = data[:, :, 124], what happens when transposing and reversing the NIFIT image? - (3)

A

slc = data[:, :, 124]: This selects a single axial slice from the MRI volume. The colon : indicates that all elements are selected along the first and second dimensions (likely the left-right and anterior-posterior directions), while selects on slice at index 124 along the third dimension (likely the inferior-superior direction).

slc = slc.transpose(1, 0): This transposes the selected slice. The axes are swapped, effectively rotating the slice. Therefore, [::-1, :] effectively reverses the order along the second axis (P-A) while keeping all elements along the first axis (L-R) and third axis (I-S) unchanged.

slc = slc[::-1, :]: This reverses the order of rows in the slice, flipping it vertically. The [::-1, :] syntax means reversing the order along the first axis (anterior-posterior). This step ensures proper anatomical orientation, aligning the slice from inferior to superior.

66
Q

Transposing and reversing seems like a lot of work so it gives you a shortcut - (3)

A

e.g, using these member functions

Get the data and the qform matrix
data = s.get_fdata()
qform = s.get_qform()

s.orthoview()

67
Q

Explain this code - (6)

A

The code imports the necessary libraries: nibabel (as nib) and matplotlib.pyplot (as plt).

It then loads a structural NIFTI file named ‘highres.nii.gz’ using the ‘nib.load()’ function and stores it in the variable ‘s’.

The ‘s.get_fdata()’ function retrieves the image data from the NIFTI file and stores it in the variable ‘data’.
in floating-point format

Additionally, the ‘s.get_qform()’ function retrieves the affine transformation matrix (qform matrix) associated with the NIFTI file and stores it in the variable ‘qform’.

This matrix contains information about the spatial orientation and voxel dimensions.

Finally, the ‘s.orthoview()’ function is called, which automatically produces three views of the data, labeling them as anterior, posterior, superior, and inferior.

68
Q

Output of this code:

A
69
Q

To run this code of spyder have to change

s = nib.load(‘highres.nii.gz’) which works in Colab

to

A

s = nib.load(‘C:\Users\gs1211 (whatever usename you have)\pin-material\s7_fmri\highres.nii.gz’)

70
Q

In this output - (2)

A

labels left on left and right is on right

However, in FSL this is flipped other way as in radiological convention so be careful looking at fMRI should be labelled

71
Q

In the notebook this will just print out three static views of the data. That’s fine but we can do better. If you run it in Spyder it will bring up a complete volume browser that you - (2)

A

you can click around in spyder by simply uploading same code you did in Colab and changing

s = nib.load(‘highres.nii.gz’) which works in Colab to
s = nib.load(‘C:\Users\gs1211 (whatever usename you have)\pin-material\s7_fmri\highres.nii.gz’)

72
Q

How to tell spyder to install nibabel? - (2)

A

In the Spyder Console (Bottom Right) type:

pip install nibabel

73
Q

How to check if sypder installed nibabel? - (3)

A

You can check that it has worked by typing (on the console again)

import nibabel as nib

There should be no complaints!

74
Q

How to tell spyder we want interactive graphics? - (5)

A

Go into the Preferences window like this

Tools->Preferences

From the iPython Console settings:

Select Graphics -> Graphics Backend -> Qt5

And hit ‘Apply’

75
Q

Quite often you may perform some analysis on MRI/fMRI data and then want to save it out in a

A

new file

76
Q

Quite often you may perform some analysis on MRI/fMRI data and then want to save it out in a new file.
For instance, - (3)

A

you might want to create a ‘mask’ for a specific area based on some criteria.

This is a bit more complex than writing things out in normal files as you must make sure that the header information is correct.

The easiest way is to start from the type of file that you are analysing.

77
Q

What is the process involved in saving new NIFTI images in MRI or fMRI analysis? - (5)

A

When working with MRI or fMRI data, you might need to perform analyses and then save the results in a new NIFTI file.

This process involves ensuring that the header information is correct to maintain data integrity.

One common use case is creating a mask for specific areas based on certain criteria.

For example, you might want to create a mask where only certain voxels are set to a value like 255.0, while others remain at 0.0.

This mask can then be used for further analysis.

78
Q

If spyder crashes press or any changes you make to Python pipline then

A

restart the kernel

79
Q

Explain this code -(5)

producing mask

A

This code snippet performs several operations on a structural NIFTI file to create a mask and save it. Let’s break down each step:

  1. Loading the NIFTI File:
    • The code starts by loading a structural NIFTI file named ‘highres.nii.gz’ using the nib.load() function from the nibabel library. This file contains MRI data.
  2. Extracting Structural Data:
    • Once the file is loaded, the structural data is extracted using the get_fdata() method, and it’s stored in a variable named data.
    • The print(data.shape) statement displays the shape of the extracted data. This provides information about the dimensions of the MRI data. - (176, 256, 256)
  3. Modifying Data to Create Mask:
    • Next, a specific region within the data is modified to create a mask. The code sets voxels in a box-shaped region (with indices ranging from 100 to 169 along each axis) to the value 255. This operation effectively creates a mask where the selected region is marked with 255, indicating the presence of the mask. - #setting all one partof the data to be the same value of ‘255’ - inserting bick of 255 values inside someone’s head
    • This process allows for the creation of a mask that can be used to isolate specific areas of interest within the MRI data.
  4. Creating a New NIFTI Image in memory:
    • After modifying the data to create the mask, a new NIFTI image is created in memory using the modified data. This is achieved using the Nifti1Image constructor from nibabel.
    • The constructor requires three arguments: the modified data (mask), the affine transformation (s.affine), and the header information (s.header) obtained from the original NIFTI file.
  5. Writing/Saving to the Overlay:
    • Finally, the newly created NIFTI image (mask) is saved as ‘my_overlay.nii.gz’ using the nib.save() function. This function takes two arguments: the new image object (new_image) and the desired filename (‘my_overlay.nii.gz’). - #takes 2 arguements, dataset you made and name it
80
Q

What mask does this code produce? - (2)

A

The code defines a box-shaped region within the MRI data, with boundaries set along the left-right (L-R), posterior-anterior (P-A), and inferior-superior (I-S) axes, corresponding to the X, Y, and Z dimensions, respectively.

Within this defined region, the voxel intensities are set to 255, effectively creating a binary mask where this specific region is highlighted, while the rest of the voxels remain unchanged.

81
Q

What is output of this code? (spyder)

A

change to be smaller e.g., 100:110 to all

82
Q

So far, we have only loaded in structural MRI data into Python. But we can also load

A

functional data

83
Q

In s7 directory we glone from pin-materials there is a

A

n fMRI time-series in the file func_data.nii.gz.

84
Q

Explain this code which loads the fMRI data, extracts header information, and prints some details about the data

A
85
Q

What is a purpose of producing a mask?

A

Masking out parts of brain (ROIs) and extract data from that or sometimes ROIs to mark parts of brain as not interesting so analysis does not include them - e.g., don’t include damaged brain parts from stroke patients

86
Q

Explain this code that loads the fMRI data, extracts header information, and prints some details about the data - (6)

A
  • The code begins by importing necessary libraries: nibabel for working with NIFTI files, matplotlib.pyplot for plotting, and numpy for numerical operations.
  • It loads fMRI data from a NIFTI file named ‘func_data.nii.gz’ using the nibabel.load() function.
  • The header and data are then extracted from the loaded file using the .header and .get_fdata() methods, respectively.
  • The header contains essential information about the data, such as its dimensions and pixel dimensions, which are printed using the hdr[‘dim’] and hdr[‘pixdim’] statements.
  • The size and type of the data array are printed using the data.shape and type(data) statements, respectively.
  • This provides an overview of the fMRI data, including its dimensions and voxel size, facilitating further analysis or visualization.
87
Q

Explain the output from this code of loading and extracting info of fMRI data - (6)

A

The first line [ 4 80 80 64 160 0 0 0] represents the dimensions of the data. Each number corresponds to a specific dimension:

The first number (4) indicates that the data has four dimensions - we have four-dimensional data. This makes sense as we now also have a time dimension.

The subsequent numbers (80, 80, 64, 160) represent the size of each dimension: 80 voxels along the first dimension, 80 voxels along the second dimension, 64 voxels along the third dimension, and 160 time points along the fourth dimension.

Looking at the next four numbers, we find that each of our images is (80, 80, 64) and that we have 160 of them (so 160 time-points).

The pixel dimensions tell us that the voxel size in this case is 3x3x3; i.e. we have a 3x3mm acquisition with 3mm slices. The next number in the pixdim array is the TR: in this case that is 2s. - blocks of brain collected every 2s and dimensins of block 3,3,3mm

The third line (80, 80, 64, 160) - this shape specification indicates that the data array comprises 80 voxels in the x-direction, 80 voxels in the y-direction, 64 voxels in the z-direction, and 160 time points, forming a four-dimensional array.

88
Q

We can add f.orthoview()
at end to make it interactive:

A
89
Q

Extending this code to produce graphs of voxel from two different positions - explain the code - (8) with output

A

This code plots the time-series for two voxel positions using data obtained from fMRI.

It uses the ‘ggplot’ style for visualization and plots the signals from two different voxels in blue and red, respectively.

It also adds labels for the x-axis and y-axis, as well as a legend to distinguish between the two voxel positions.

Because we now have data available as a numpy array, we can plot it.

This time we choose two (very non-accidentally chosen) voxels and plot the timeseries for each of them:

The first thing to note is that this is the raw image-space data from the scanner; it has not been pre-processed in any way.

The second thing is that it looks like we have almost periodic responses in each of these voxels. In Voxel 1 (plotted in blue) we have 10 cycles through the experiment (32 seconds per cycle).

In Voxel 2 (plotted in red) we have 16 cycles through the experiment (20 seconds per cycle). It is almost as though the person was doing two different tasks or having different parts of their brain stimulated at different frequencies.

90
Q

What is the goal of manual fMRI analysis using numpy?

A

The goal is to replicate part of a first-level FSL analysis, inspired by old-school electrophysiology methods.

91
Q

What technique is used to identify voxel responses at stimulus frequencies?

A

Fourier Transform (FT) is used to break down voxel time series into frequency components.

92
Q

How does Fourier Transform (FT) help in fMRI analysis?

A

FT allows us to examine voxel responses at specific frequencies, indicating neural activity synchronized with experimental stimuli.

93
Q

What does “np.fft” stand for in numpy?

A

np.fft” stands for NumPy’s Fast Fourier Transform, used for efficient Fourier analysis of voxel time series data.

94
Q

What is the significance of bright patches in frequency volumes obtained from Fourier Transform?

A

Bright patches indicate voxels that are active at specific frequencies, suggesting neural responses to experimental stimuli.

95
Q

Here is the simplest possible fMRI analysis you could ever do - (4)

A

Load the data (4D)

Compute the FT of the data across time for every voxel (4D). This gives us a data volume at every frequency. The amplitude of each voxel in a volume ‘f’ tells us how much that voxel was responding at frequency ‘f’.

Take a look at the volumes for frequencies ‘10’ and ‘16’ times per experiment.

Bright patches of voxels in those volume are ‘active’ at those frequencies.

96
Q

Explain this code that plots three different voxels - (5)

A

Imports necessary libraries: nibabel for working with NIFTI files, matplotlib.pyplot for plotting, and numpy for numerical operations.

Loads the fMRI data from the file named ‘filtered_func_data.nii.gz’.

Extracts header information (hdr), voxel data (data), and the qform matrix (qform) from the loaded NIFTI file.

Prints some header information, including the dimensions (hdr[‘dim’]), pixel dimensions (hdr[‘pixdim’]), and shape of the data array (data.shape).

Plots the time-series data for three specific voxels (p1, p2, and p3) on the same graph p1: represents voxel data at position (29, 29, 40), p2 represents voxel data at position (50, 29, 40)., p3 represents voxel data at position (46, 9, 30).

97
Q

What is the qform matrix? - (2)

A

The qform matrix, extracted from a NIFTI file, represents the transformation matrix that maps voxel coordinates to the scanner’s spatial coordinate system.

It defines the orientation, position, and scaling of the image data in physical space.

98
Q

What do the coordinates represent in the context of the code snippet p1=data[29, 29, 40, :], p2=data[50, 29, 40, :], and p3=data[46, 9, 30]? - (4)

A

In the context of fMRI data, the coordinates represent the spatial location of specific voxels within the brain volume.

Each set of coordinates specifies the position of a voxel along the x, y, and z axes, respectively.

For example, in p1=data[29, 29, 40, :], the voxel is located at coordinates (29, 29, 40), indicating its position along the x-axis (29 units), y-axis (29 units), and z-axis (40 units).

Similarly, p2=data[50, 29, 40, :] and p3=data[46, 9, 30] represent voxels located at different spatial coordinates within the fMRI volume.

99
Q

Output of this code

A

Those are the two voxels we looked at before plus another one that doesn’t seem to be active.

100
Q

We can then produce fourier transform of each voxel using this code

Explain it - (6)

A
  1. fp1=abs(np.fft.fft(p1)), fp2=abs(np.fft.fft(p2)), and fp3=abs(np.fft.fft(p3)): These lines apply the Fourier Transform to each voxel time series (p1, p2, and p3) to compute the amplitude of each frequency component.
  2. plt.figure(figsize=(15,5)): Creates a new figure for plotting with a specific size.
  3. plt.bar(np.arange(29)+.8,fp1[1:30],width=0.2), plt.bar(np.arange(29)+1,fp2[1:30],width=0.2), plt.bar(np.arange(29)+1.2,fp3[1:30],width=0.2): These lines generate bar charts for each voxel’s Fourier transform. They plot the amplitudes of the Fourier transform components (from frequencies 1 to 29) along the x-axis, with slight adjustments to the bar positions to avoid overlap.
  4. plt.xlabel('Frequency (cycles/expt)'), plt.ylabel('Signal amp (au)'): Sets labels for the x-axis and y-axis of the plot.
  5. plt.legend(['Voxel 1', 'Voxel 2','Voxel 3']): Adds a legend to the plot indicating which voxel each set of bars represents.
  6. plt.show(): Displays the plot.
101
Q

What does output show of fourier transform of each voxel? - (3)

A

voxel 1 (red) clearly responding at 10 cycles per second - has 1 input

voxel 3 (lilac) responding at 10 and 16 cycles per second - has 2 inputs

voxe 2 (blue) that is weak has some power at every frequency - call white noise

102
Q

The grande finale is that we would like to see

A

where these are in the brain and if they are part of larger groups of similar voxels. To do this we just compute the FFT on all the voxels at once and make nice response maps at all the different frequencies!

103
Q

explain in more detail what line of code does - (5)

plt.bar(np.arange(29)+1.2,fp3[1:30],width=0.2)

A

np.arange(29): Generates an array of integers from 0 to 28, representing the indices of the frequencies.

+1.2: Adds a small offset to the x-axis values, ensuring that the bars for each frequency are evenly spaced.

fp3[1:30]: Extracts the Fourier transform magnitudes for frequencies 1 to 29 (indexing is zero-based in Python).

width=0.2: Sets the width of the bars to 0.2 units.

Together, this line positions and plots the bars representing the Fourier transform magnitudes of the third voxel’s time-series data, ensuring proper spacing and visualization of the frequency spectrum.

104
Q

Explain this code- (7)

A

Fourier Transform (FT) Calculation: The code begins by computing the Fourier transform of the entire fMRI data array (data) using NumPy’s Fast Fourier Transform (FFT) function (np.fft.fft()). The FFT is applied along the first dimension, which represents time. This step decomposes the time series data into its frequency components, providing insights into how different frequencies contribute to the signal.

Magnitude Extraction: After computing the FFT, the code uses the abs() function to extract the magnitude of the resulting complex numbers. This is necessary because the FFT output includes both real and imaginary components. By taking the absolute value, we obtain the magnitude of each frequency component, which represents the strength or amplitude of the signal at that frequency.

Frequency Response Extraction: Next, the code extracts the frequency responses at 10 and 16 times per experiment from the magnitude data (fData). These frequencies are of interest because they may correspond to specific experimental conditions or stimuli. The extracted responses are stored in arrays response10 and response16, respectively, creating 3D maps of response magnitudes across all voxels in the fMRI data.

Slice Selection: To visualize the frequency responses, the code selects specific slices (s10 and s16) from the response maps. These slices represent responses at particular voxels within the brain. However, since the orientation of the data may not be optimal for visualization, the code applies a transpose operation and reverses the order of elements along the second axis to ensure correct orientation ([:, :, ::-1].transpose()).

Plotting: The selected response slices (s10 and s16) are plotted side by side using Matplotlib’s imshow() function. Each subplot displays the response map at either 10 or 16 cycles per second. The ‘jet’ color map is utilized to visualize the intensity of responses, with warmer colors indicating higher response magnitudes. Additionally, titles are added to each subplot to denote the corresponding frequency.

Save Output: Finally, the code saves the generated response maps as NIFTI files (10cycles.nii.gz and 16cycles.nii.gz). These files can be further analyzed or visualized using neuroimaging software.

List Files: As a final step, the code executes a command (!ls -lat) to list the files in the current directory, providing an overview of the saved output files.

105
Q

What does plt.subplot(1,2,1) mean? - (2)

A

The code utilizes Matplotlib’s subplot() function to organize the visualization of the frequency response maps.

By specifying (1,2,1) as the argument for the first subplot, it instructs Matplotlib to create a grid of subplots with one row and two columns and select the first subplot for the current plot.

106
Q

What does response10=fData[:,:,:,10] do in code?

A

In the given code snippet, response10[29, :, ::-1] reverses the order of elements along the last axis of response10, ensuring proper orientation of the data for visualization

107
Q

What is output

A
108
Q

I have loaded in anantomical nifti file using nibabel like this:

What does the ‘dim’ key in the header represent?

A

The 3D or 4D dimensions of the image in voxels (and TRs if appropriate)

109
Q

Q2:
Similarly, there is another field called ‘pixdim’. What is that?

A

The size of each voxel in mm

110
Q

Q3:
I have loaded in two files from a function dataset. myData_r.nii.gz and myData_f.nii.gz . They are exactly the same dimensions but when I plot the same pixel location from each file in turn the timeseries is subtly different - one seems to be smaller and smoother than the other. What might ‘r’ and ‘f’ mean in the headers?

A

‘Raw’ and ‘Filtered’ - this is how we refered to them in the lecture the filtered image has probably been smoothed in time or space or both.

111
Q

Q4:
I run an fMRI experiment in which I flash two checkerboards on and then off at different frequencies. One checkerboard is in the left visual field, the other is in the right visual field.

In the left visual field I flash at 10 cycles per experiment. In the right visual field I flash at 7 cycles per experiment.

a) What would I expect to see in the FT of a voxel from left hemisphere V1?

b) Some voxels in the brain seem to have Fourier Transform peaks at both 7 and 10 cycles per experiment. What must be true about at least some of the receptive fields in those voxels? - (2)

A

a) A peak at 7 cycles per experiment - in the Fourier Transform (FT) of a voxel from the left hemisphere V1, you would expect to see a peak at 7 cycles per experiment. This corresponds to the frequency of the checkerboard stimuli presented in the right visual field, as the left visual field is represented in the right hemisphere.

b) They must have inputs from both visual fields

112
Q

5:
I have a nifti file and I want to plot two different voxels of data from it. However although the data loads, I only see one line. What is wrong with this code and how can I fix it?

A

You are plotting p1 twice. Change the second ‘plt.plot’ statement to use p2 not p1

113
Q

The provided code snippet, using slc.transpose(1, 0), only transposes the - (3)

A

In this case, it swaps the first and second dimensions, effectively exchanging the P-A (posterior-anterior) and I-S (inferior-superior) axes.

So, if the original orientation of the slice was (L-R, P-A, I-S), it would become (L-R, I-S, P-A) after this operation.

does not affect L-R

114
Q

What does slc.tranpose(0,1) do? - (2)

A

it swaps the first and second dimensions, effectively exchanging the L-R (left-right) and P-A (posterior-anterior) axes.

So, if the original orientation of the slice was (L-R, P-A, I-S), it would become (P-A, L-R, I-S) after this operation.

115
Q

User
so slc is always [::-1,:] regardles if slice is L-R, P-A or I-S?

A

he slicing operation slc[::-1, :] is independent of the orientation of the slice. It always reverses the order of rows in the 2D array slc, regardless of whether the slice represents left-right (L-R), posterior-anterior (P-A), or inferior-superior (I-S) orientation.