CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
adasegroup

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: adasegroup/NEUROML2022
Path: blob/main/seminar4/Introduction.ipynb
Views: 63
Kernel: Python 3

fMRI data preprocessing - Introduction

Open In Colab

fMRI scans are saved in dicom format. For scientific analysis of brain images the nifty format (.nii files) are often used. The conversion from dicom to nifty can be done with dcm2niix

Many file are generated during fMRI sessions. These can arranged in many ways, thus a standard is needed how to arrange them. Commonly used standard is Brain Imaging Data Structure (BIDS).

You can use HeuDiConv or Dcm2Bids to automate the conversion from dicom to BIDS.

DICOM TO BIDS

Let's download the data we will be working with. We will download data through DataLad. It's destibuted data managements system, its provide data storage and version control.

http://www.datalad.org/

about the data, that we working with: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3641991/

%%bash datalad get -J 20 -d /data/ds000114 \ /data/ds000114/derivatives/fmriprep/sub-*/anat/*preproc.nii.gz \ /data/ds000114/sub-*/ses-test/func/*fingerfootlips*
from utils import list_files # The data is already in BIDS format # The subjects peformed 5 tasks. We will focus on fingerfootlips task list_files('/data/ds000114/sub-01/ses-retest')

With nibabel we can load a file and inspect its properties.

import nibabel from nilearn import plotting import numpy as np import warnings warnings.filterwarnings('ignore') anat = nibabel.load('/data/ds000114/derivatives/fmriprep/sub-01/anat/sub-01_t1w_preproc.nii.gz') fmri = nibabel.load('/data/ds000114/sub-01/ses-test/func/sub-01_ses-test_task-fingerfootlips_bold.nii.gz') print(f'Anatomical dimensionality is {anat.ndim} and fmri is {fmri.ndim}') #The anatomical image have higher resolution then the fmri print(f'Anatomical voxelization: {anat.shape}\nfMRI voxelization: {fmri.shape}') #the data can be accessed as print(f'\nAnatomical volume affine:\n{anat.affine}\nfMRI affine:\n{fmri.affine}') data = np.array(anat.dataobj)
fmri.orthoview()

Lets stop on SliceTiming key, unlike a photograph, in which the entire picture is taken in a single moment, an fMRI volume is acquired in slices. Each of these slices takes time to acquire - from tens to hundreds of milliseconds.

The two most commonly used methods for creating volumes are sequential and interleaved slice acquisition. Sequential slice acquisition acquires each adjacent slice consecutively, either bottom-to-top or top-to-bottom. Interleaved slice acquisition acquires every other slice, and then fills in the gaps on the second pass. Both of these methods are illustrated in the video below.

https://www.brainvoyager.com/bv/doc/UsersGuide/Preprocessing/SliceScanTimeCorrection.html

SliceTiming

import json #metadata is located in json files with open('/data/ds000114/task-fingerfootlips_bold.json', 'rt') as fp: task_info = json.load(fp) task_info
len(task_info['SliceTiming'])

Introduction Nipype

Why nipype?

Nipype allows to build preprocessing pipelines from different softwares, and it is computationally efficient. There are some helpful ready to use pipleines written with Nipype like fmriprep. To use fmriprep the data have to be in valid BIDS format. The user have to supply only the path to the data setup the parametars.

In Nipype, interfaces are python modules that allow you to use various external packages (e.g. FSL, SPM or FreeSurfer), even if they themselves are written in another programming language than python. Such an interface knows what sort of options an external program has and how to execute it.

Nipype architecture

In Nipype, a node is an object that executes a certain function. This function can be anything from a Nipype interface to a user-specified function or an external script. Each node consists of a name, an interface category and at least one input field, and at least one output field.

Once you connect multiple nodes to each other, you create a directed graph. In Nipype we call such graphs either workflows or pipelines. Directed connections can only be established from an output field of a node to an input field of another node.

from nipype import Node, Function, Workflow from IPython.display import Image, clear_output def multiply(a, b): return a * b #Create a Node that multiplies 2 numbers mul = Node(Function(input_names=['a', 'b'], output_names=['multiply_result'], function=multiply), name='a_x_b') mul.inputs.a = 2 mul.inputs.b = 3 result = mul.run() result.outputs
#Create a Node that adds 2 numbers def add(a, b): return a + b adder = Node(Function(input_names=['a', 'b'], output_names=['add'], function=add), name='a_plus_b') adder.inputs.b = 10 #Create a workflow wf = Workflow('hello') # connect the nodes wf.connect(mul, 'multiply_result', adder, 'a') #visualize the graph wf.write_graph(graph2use='flat', format='png', simple_form=True) clear_output() Image(filename='graph_detailed.png')
#run the graph eg = wf.run() clear_output()#don't print the pipeline steps during exection #check the results eg = list(eg.nodes()) nodes_outputs = [node.result.outputs for node in eg] nodes_outputs