pyAFQ: Automated Fiber Quantification in Python¶

The core of the tractometry.org ecosystem is the pyAFQ software library. This library uses inputs from various other programs to perform the delineation of major white matter pathways and to quantify white matter tissue properties along their lengths - tract profiles. The final output of the program includes a tabular summary with these tract profiles for each subject in the dataset, and one merged table that includes the tract profiles for all of the subjects.

Along the way, the program produces many different intermediate data, including tractography results, maps of tissue properties, and visualizations, which can be used for quality assurance of the data and processing.

There are two ways to use pyAFQ: through a command line interface, and by writing Python code. Here, we will focus on the latter, using pyAFQ's Python Application Programming Interface (API).

Setup¶

We start with a few setup steps. The pyAFQ software downloads templates that are required for anatomical delineation of tracts into a designated directory on the user's machine. For the purpose of these examples, we will set the path to this directory to /data/tractometry. You will see a similar setup step at the top of many of the notebooks here, and it always does the same thing.

In [1]:
import os.path as op
from paths import afq_home

Next, we import the GroupAFQ class from the pyAFQ api module. We also import plotly, which we will use for visualization.

In [2]:
from AFQ.api.group import GroupAFQ
import plotly

Set up tracking parameters¶

Before we initialize the GroupAFQ object, we create a tracking_params variable, which we will pass to the GroupAFQ object to specify the tractography part of the pipeline. Here, we specify that we want 100,000 seeds randomly distributed in the white matter. We also set num_chunks to True. This will tell pyAFQ to use the ray software library to parallelize the tracking across all cores. This can be removed to process in serial, or set to use a particular distribution of work by setting num_chunks to an integer number.

In [3]:
tracking_params = dict(n_seeds=100000,
                       random_seeds=True,
                       rng_seed=2022,
                       num_chunks=True)

Initialize an AFQ object¶

Next, we initialize the GroupAFQ object. Upon initialization, the object does some validation steps, but no major computation is triggered. Instead, computation is deferred until the export method is called.

In [4]:
myafq = GroupAFQ(
    bids_path=op.join(afq_home, 'stanford_hardi'),
    preproc_pipeline='vistasoft',
    tracking_params=tracking_params,
    viz_backend_spec='plotly_no_gif')
INFO:AFQ:No seed mask given, using FA (or first scalar if none are FA)thresholded to 0.2
INFO:AFQ:No stop mask given, using FA (or first scalar if none are FA)thresholded to 0.2

Recognize the tracts and calculating tract profiles:¶

The myafq class instance now has all the information that it needs in order to do all kinds of computations on the data. Typically, we will want to calculate the tract profiles. To trigger the pyAFQ pipeline that calculates the profiles, users can call the export('profiles') method.

For the purpose of this demonstration, the pipeline has been run in advance and cached in the data folder (under /data). In usual practice, the following line of code would trigger the full computational pipeline, which takes about 10 minutes to run and requires about 6GB RAM. However, the caching behavior is typical to pyAFQ. Once derivatives are computed, they are cached such that they can be reused.

In [5]:
myafq.export('profiles')
Out[5]:
{'01': '../data_/tractometry/stanford_hardi/derivatives/afq/sub-01/ses-01/dwi/sub-01_ses-01_desc-profiles_tractography.csv'}

After this line of code has finished running, a CSV file that contains the tract profiles should be saved into the afq folder within the derivatives folder of the dataset. At this point, we can export a visualization that shows all of the tracts and tract profiles of FA by calling export again. The call to plotly then renders the html of the figure into the notebook.

In [6]:
bundle_html = myafq.export("all_bundles_figure")
plotly.io.show(bundle_html["01"][0])
INFO:AFQ:Forceps Major and Callosum Occipital bundles are co-located, and AFQ assigns each streamline to only one bundle. Only Callosum Occipital will be used.
INFO:AFQ:Forceps Minor and Callosum Orbital bundles are co-located, and AFQ assigns each streamline to only one bundle. Only Callosum Orbital will be used.
INFO:AFQ:Forceps Major and Callosum Occipital bundles are co-located, and AFQ assigns each streamline to only one bundle. Only Callosum Occipital will be used.
INFO:AFQ:Forceps Minor and Callosum Orbital bundles are co-located, and AFQ assigns each streamline to only one bundle. Only Callosum Orbital will be used.
INFO:AFQ:Loading Volume...
INFO:AFQ:Loading Volume...
INFO:AFQ:Loading Stateful Tractogram...
/Users/john/pyAFQ/AFQ/utils/streamlines.py:93: UserWarning:

Pass ['to_space'] as keyword args. From version 2.0.0 passing these as positional arguments will result in an error. 

INFO:AFQ:Generating colorful lines from tractography...
WARNING:AFQ:Failed to write HTML file: ../data_/tractometry/stanford_hardi/derivatives/afq/sub-01/ses-01.html

Tract profile data for all subjects in a particular dataset can be accessed as a Pandas dataframe, using the combine_profiles method.

In [7]:
profiles_df = myafq.combine_profiles()
WARNING:AFQ:Unable to update combined tract profile. This is likely due to file permissions.
In [ ]: