Ndmg Tutorial: Running Inside Python

This tutorial provides a basic overview of how to run ndmg manually within Python.
We begin by checking for dependencies, then we set our input parameters, then we smiply run the pipeline.

Running the pipeline is quite simple: call ndmg_dwi_pipeline.ndmg_dwi_worker with the correct arguments.

Note that, although you can run the pipeline in Python, the absolute easiest way (outside Gigantum) is to run the pipeline from the command line once all dependencies are installed using the following command:
ndmg_bids </absolute/input/dir> </absolute/output/dir>.
This will run a single session from the input directory, and output the results into your output directory.

But for now, let's look at running in Python --
Let's begin!


In [11]:
import os
import os.path as op
import glob
import shutil
import warnings
import subprocess
from pathlib import Path

from ndmg.scripts import ndmg_dwi_pipeline
from ndmg.scripts.ndmg_bids import get_atlas
from ndmg.utils import cloud_utils

Check for dependencies, Set Directories

The below code is a simple check that makes sure AFNI and FSL are installed.
We also set the input, data, and atlas paths.

Make sure that AFNI and FSL are installed


In [12]:
# FSL
try:
    print(f"Your fsl directory is located here: {os.environ['FSLDIR']}")
except KeyError:
    raise AssertionError("You do not have FSL installed! See installation instructions here: https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FslInstallation")
    
# AFNI
try:
    print(f"Your AFNI directory is located here: {subprocess.check_output('which afni', shell=True, universal_newlines=True)}")
except subprocess.CalledProcessError:
    raise AssertionError("You do not have AFNI installed! See installation instructions here: https://afni.nimh.nih.gov/pub/dist/doc/htmldoc/background_install/main_toc.html")


Your fsl directory is located here: /usr/local/fsl
Your AFNI directory is located here: /Users/alex/abin/afni

Set Input, Output, and Atlas Locations

Here, you set:

  1. the input_dir - this is where your input data lives.
  2. the out_dir - this is where your output data will go.

In [21]:
# get atlases
ndmg_dir = Path.home() / ".ndmg"
atlas_dir = ndmg_dir / "ndmg_atlases"
get_atlas(str(atlas_dir), "2mm")

# These
input_dir = ndmg_dir / "input"
out_dir = ndmg_dir / "output"

print(f"Your input and output directory will be : {input_dir} and {out_dir}")

assert op.exists(input_dir), f"You must have an input directory with data. Your input directory is located here: {input_dir}"


Your input and output directory will be : /Users/alex/.ndmg/input and /Users/alex/.ndmg/output

Choose input parameters

Naming Conventions

Here, we define input variables to the pipeline. To run the ndmg pipeline, you need four files:

  1. a t1w - this is a high-resolution anatomical image.
  2. a dwi - the diffusion image.
  3. bvecs - this is a text file that defines the gradient vectors created by a DWI scan.
  4. bvals - this is a text file that defines magnitudes for the gradient vectors created by a DWI scan.

The naming convention is in the BIDs spec.


In [23]:
# Specify base directory and paths to input files (dwi, bvecs, bvals, and t1w required)
subject_id = 'sub-0025864'

# Define the location of our input files.
t1w = str(input_dir / f"{subject_id}/ses-1/anat/{subject_id}_ses-1_T1w.nii.gz")
dwi = str(input_dir / f"{subject_id}/ses-1/dwi/{subject_id}_ses-1_dwi.nii.gz")
bvecs = str(input_dir / f"{subject_id}/ses-1/dwi/{subject_id}_ses-1_dwi.bvec")
bvals = str(input_dir / f"{subject_id}/ses-1/dwi/{subject_id}_ses-1_dwi.bval")

print(f"Your anatomical image location: {t1w}")
print(f"Your dwi image location: {dwi}")
print(f"Your bvector location: {bvecs}")
print(f"Your bvalue location: {bvals}")


Your anatomical image location: /Users/alex/.ndmg/input/sub-0025864/ses-1/anat/sub-0025864_ses-1_T1w.nii.gz
Your dwi image location: /Users/alex/.ndmg/input/sub-0025864/ses-1/dwi/sub-0025864_ses-1_dwi.nii.gz
Your bvector location: /Users/alex/.ndmg/input/sub-0025864/ses-1/dwi/sub-0025864_ses-1_dwi.bvec
Your bvalue location: /Users/alex/.ndmg/input/sub-0025864/ses-1/dwi/sub-0025864_ses-1_dwi.bval

Parameter Choices and Output Directory

Here, we choose the parameters to run the pipeline with. If you are inexperienced with diffusion MRI theory, feel free to just use the default parameters.

  • atlases = ['desikan', 'CPAC200', 'DKT', 'HarvardOxfordcort', 'HarvardOxfordsub', 'JHU', 'Schaefer2018-200', 'Talairach', 'aal', 'brodmann', 'glasser', 'yeo-7-liberal', 'yeo-17-liberal'] : The atlas that defines the node location of the graph you create.
  • mod_types = ['det', 'prob'] : Deterministic or probablistic tractography.
  • track_types = ['local', 'particle'] : Local or particle tracking.
  • mods = ['csa', 'csd'] : Constant Solid Angle or Constrained Spherical Deconvolution.
  • regs = ['native', 'native_dsn', 'mni'] : Registration style. If native, do all registration in each scan's space; if mni, register scans to the MNI atlas; if native_dsn, do registration in native space, and then fit the streamlines to MNI space.
  • vox_size = ['1mm', '2mm'] : Whether our voxels are 1mm or 2mm.
  • seeds = int : Seeding density for tractography. More seeds generally results in a better graph, but at a much higher computational cost.

In [24]:
# Use the default parameters.
atlas = 'desikan'
mod_type = 'prob'
track_type = 'local'
mod_func = 'csd'
reg_style = 'native'
vox_size = '2mm'
seeds = 1

Get masks and labels

The pipeline needs these two variables as input.
Running the pipeline via ndmg_bids does this for you.


In [25]:
# Auto-set paths to neuroparc files
mask = str(atlas_dir / "atlases/mask/MNI152NLin6_res-2x2x2_T1w_descr-brainmask.nii.gz")
labels = [str(i) for i in (atlas_dir / "atlases/label/Human/").glob(f"*{atlas}*2x2x2.nii.gz")]

print(f"mask location : {mask}")
print(f"atlas location : {labels}")


mask location : /Users/alex/.ndmg/ndmg_atlases/atlases/mask/MNI152NLin6_res-2x2x2_T1w_descr-brainmask.nii.gz
atlas location : ['/Users/alex/.ndmg/ndmg_atlases/atlases/label/Human/desikan_space-MNI152NLin6_res-2x2x2.nii.gz']

Run the pipeline!


In [28]:
ndmg_dwi_pipeline.ndmg_dwi_worker(dwi=dwi, bvals=bvals, bvecs=bvecs, t1w=t1w, atlas=atlas, mask=mask, labels=labels, outdir=str(out_dir), vox_size=vox_size, mod_type=mod_type, track_type=track_type, mod_func=mod_func, seeds=seeds, reg_style=reg_style, clean=False, skipeddy=True, skipreg=True)


dwi = /Users/alex/.ndmg/input/sub-0025864/ses-1/dwi/sub-0025864_ses-1_dwi.nii.gz
bvals = /Users/alex/.ndmg/input/sub-0025864/ses-1/dwi/sub-0025864_ses-1_dwi.bval
bvecs = /Users/alex/.ndmg/input/sub-0025864/ses-1/dwi/sub-0025864_ses-1_dwi.bvec
t1w = /Users/alex/.ndmg/input/sub-0025864/ses-1/anat/sub-0025864_ses-1_T1w.nii.gz
atlas = desikan
mask = /Users/alex/.ndmg/ndmg_atlases/atlases/mask/MNI152NLin6_res-2x2x2_T1w_descr-brainmask.nii.gz
labels = ['/Users/alex/.ndmg/ndmg_atlases/atlases/label/Human/desikan_space-MNI152NLin6_res-2x2x2.nii.gz']
outdir = /Users/alex/.ndmg/output
vox_size = 2mm
mod_type = prob
track_type = local
mod_func = csd
seeds = 1
reg_style = native
clean = False
skip eddy = True
skip registration = True
Output directory: /Users/alex/.ndmg/output
Adding directory tree...
Connectomes downsampled to given labels: /Users/alex/.ndmg/output/dwi/roi-connectomes/desikan_space-MNI152NLin6_res-2x2x2/sub-0025864_ses-1_dwi_desikan_space-MNI152NLin6_res-2x2x2_measure-spatial-ds_adj.csv
Performing eddy correction...
eddy_correct /Users/alex/.ndmg/input/sub-0025864/ses-1/dwi/sub-0025864_ses-1_dwi.nii.gz /Users/alex/.ndmg/output/dwi/preproc/eddy_corrected_data.nii.gz 0
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-28-f45eb063a997> in <module>
----> 1 ndmg_dwi_pipeline.ndmg_dwi_worker(dwi=dwi, bvals=bvals, bvecs=bvecs, t1w=t1w, atlas=atlas, mask=mask, labels=labels, outdir=str(out_dir), vox_size=vox_size, mod_type=mod_type, track_type=track_type, mod_func=mod_func, seeds=seeds, reg_style=reg_style, clean=False, skipeddy=True, skipreg=True)

~/Dropbox/NeuroData/ndmg-top/ndmg/ndmg/scripts/ndmg_dwi_pipeline.py in ndmg_dwi_worker(dwi, bvals, bvecs, t1w, atlas, mask, labels, outdir, vox_size, mod_type, track_type, mod_func, seeds, reg_style, clean, skipeddy, skipreg, buck, remo, push, creds, debug, modif)
    242         cmd = "eddy_correct " + dwi + " " + dwi_prep + " 0"
    243         print(cmd)
--> 244         sts = Popen(cmd, shell=True).wait()
    245         print(sts)
    246         ts = time.time()

/usr/local/Caskroom/miniconda/base/envs/ndmg/lib/python3.6/subprocess.py in wait(self, timeout, endtime)
   1475                         if self.returncode is not None:
   1476                             break  # Another thread waited.
-> 1477                         (pid, sts) = self._try_wait(0)
   1478                         # Check the pid and loop as waitpid has been known to
   1479                         # return 0 even without WNOHANG in odd situations.

/usr/local/Caskroom/miniconda/base/envs/ndmg/lib/python3.6/subprocess.py in _try_wait(self, wait_flags)
   1422             """All callers to this function MUST hold self._waitpid_lock."""
   1423             try:
-> 1424                 (pid, sts) = os.waitpid(self.pid, wait_flags)
   1425             except ChildProcessError:
   1426                 # This happens if SIGCLD is set to be ignored or waiting

KeyboardInterrupt: 

Try It Yourself : Command Line

ndmg runs best as a standalone program on the command line.

The simplest form of the command, given that you have input data, pass an output folder, and have all dependencies installed, is the following:

ndmg_bids </absolute/input/dir> </absolute/output/dir>

Here, we'll show you how to set this up yourself.

Setup: Running Locally

  1. install FSL
  2. install AFNI
  3. git clone https://github.com/neurodata/ndmg.git
  4. cd ndmg
  5. pip install -r requirements.txt
  6. pip install .

Running Locally

Most Basic

This will run the first session from your input dataset, and put the results into the output dataset. We still recommend the --atlas flag so that graphs don't get generated on all possible atlases.

ndmg_bids --atlas desikan </absolute/input/dir> </absolute/output/dir>

Specifying Participant and Session

You can also specify a particular participant and session. (This is extremely useful for setting up batch scripts to run large datasets).

ndmg_bids --atlas desikan --participant_label <label> --session_label <number> </absolute/input/dir> </absolute/output/dir>

Different Registration Styles, Diffusion Models, Tractography Styles

You can use:

  • the --sp flag to set the registration space;
  • the --mf flag to set the diffusion model; and
  • the --mod flag to set deterministic / probablistic tracking;
ndmg_bids --atlas desikan --sp <space> --mf <model> --mod <tracking style> </absolute/input/dir> </absolute/output/dir>

Setup: Running in Docker

If you're having problems installing the program locally, it's often easier to use Docker.

  1. install docker
  2. docker pull neurodata/ndmg_dev:latest

Running in Docker

Option A (Docker executable approach):

Once you've downloaded the docker image, you can: Attach your local input and output folders with -v,
Run the image,
and input your participant and session labels into the container.

docker run -ti --rm --privileged -e DISPLAY=$DISPLAY -v <absolute/path/to/input/data>:/input -v <absolute/path/to/output/data>:/outputs neurodata/ndmg_dev:latest --participant_label <label> --session_label <number> --atlas desikan /input /output

Option B (Inside Docker container):

You can also enter the container yourself and then run ndmg from inside the container.

docker run -ti --rm --privileged --entrypoint /bin/bash -e DISPLAY=$DISPLAY -v <absolute/path/to/input/data>/input -v <absolute/path/to/output/data>/output ndmg_dev:latest

ndmg_bids --participant_label <label> --session_label <number> --atlas desikan /input /output