AtlasReader

atlasreader is a Python interface that generates coordinate tables and region labels from statistical MRI images.

This notebook will show you how to install AtlasReader, how to run the toolbox and what the different parameters allow you to do.

Installing AtlasReader

Provided you have pip at your disposal, installing AtlasReader is as simple as this:

pip install atlasreader

If you want to install AtlasReader directly from the source code, use the following code:

git clone https://github.com/miykael/atlasreader.git
cd atlasreader
python setup.py install

Using AtlasReader

License terms of AtlasReader

Before showcasing the toolbox, we want to point to the particular license issue that you need to consider when you are using AtlasReader.

AtlasReader itself is licensed under the BSD-3 license, but the included atlases that are used to lookup the coordinate labels are under their own license terms. For this reason, we ask you to inform yourself about the atlas specific license terms under https://github.com/miykael/atlasreader/tree/master/atlasreader/data and to cite the corresponding papers should you use AtlasReader in a publication.

We've included the following information message after every first module import, to make sure that users are aware about this issue:

The Python package you are importing, AtlasReader, is licensed under the
BSD-3 license; however, the atlases it uses are separately licensed under more
restrictive frameworks.
By using AtlasReader, you agree to abide by the license terms of the
individual atlases. Information on these terms can be found online at:
https://github.com/miykael/atlasreader/tree/master/atlasreader/data


Citing atlasreader

If you use AtlasReader in your publication, please cite the following paper:

Notter M. P., Gale D., Herholz P., Markello R. D., Notter-Bielser M.-L., & Whitaker K. (2019). AtlasReader: A Python package to generate coordinate tables, region labels, and informative figures from statistical MRI images. *Journal of Open Source Software, 4(34), 1257*, [https://doi.org/10.21105/joss.01257](https://doi.org/10.21105/joss.01257)

Prepare an example file

AtlasReader can either be run through the command line interface, or directly within Python. To showcase both of those examples, let's first import relevant plotting functions for this notebook.

In [1]:
%matplotlib inline
from nilearn.plotting import plot_glass_brain
from IPython.display import Image

Next, let's grab an example stat map. We'll use nilearn to grab a motor task stat map from neurovault and to threshold it at a value of 3 to create some clusters. Note that you'll need the most up to date version of nilearn in order to run fetch_neurovault_motor_task().

In [2]:
# Get a stat map from neurovault using nilearn
from nilearn.datasets import fetch_neurovault_motor_task
motor_images = fetch_neurovault_motor_task()
stat_img = motor_images.images[0]
In [3]:
# Threshold image at a value of 3 to create clusters
from nilearn.image import threshold_img
stat_img = threshold_img(stat_img, threshold=3)
In [4]:
# Save the thresholded image into a NIfTI file
file_name = 'stat_img.nii.gz'
stat_img.to_filename(file_name)

And what does our example data look like?

In [6]:
plot_glass_brain(file_name, black_bg=True, colorbar=True, plot_abs=False,
                 display_mode='lyrz', title='Finger tapping task');

Ok, so we can see that there are a few particular clusters, if we threshold the data at a value of 3. But how big are they and in which regions are they?

To answer these questions, you can use AtlasReader. It gives you the opportunity to better understand where the peaks of those clusters are, over which regions the clusters extend, and much more. So, how is it done?

Calling AtlasReader from Python

If you want to run AtlasReader directly in Python, just import the create_output() function, and you're good to go:

In [7]:
from atlasreader import create_output
The Python package you are importing, AtlasReader, is licensed under the
BSD-3 license; however, the atlases it uses are separately licensed under more
restrictive frameworks.
By using AtlasReader, you agree to abide by the license terms of the
individual atlases. Information on these terms can be found online at:
https://github.com/miykael/atlasreader/tree/master/atlasreader/data

Great, so now we can run AtlasReader. The only thing that we have to provide is our stat map (either the file_name or the image object) and the cluster extent, which specifies the minimum number of contiguous voxels required for a cluster to be considered for the analysis. For this example, let's set the cluster_extent to 5.

In [8]:
create_output(file_name, cluster_extent=5, direction='both')

After execution, we get four different kind of files:

  • An overview figure that shows the results within the whole brain at once
  • For each cluster, an informative figure showing the sagittal, coronal and trasnveral plane centered at this center of the cluster
  • A csv file containing relevant information about the peak of each cluster
  • A csv file containing relevant information about each cluster

So, let's take a closer look at those outputs:

Overview figure

The overview figure shows all clusters throught the brain, plotted on a glass brain plot. The name of file is the name of provided statistical image, but with the file ending .png

In [9]:
Image('stat_img.png')
Out[9]:

Informative cluster figure

AtlasReader creates for each cluster an informative figure, showing the cluster center in the three anatomical planes: sagittal, coronal and trasnveral. The name of those files all end in _clusterXY.png, i.e. _cluster01.png, _cluster02.png, ...:

In [10]:
Image('stat_img_cluster01.png')
Out[10]:
In [11]:
Image('stat_img_cluster03.png')
Out[11]:

Peak table

The csv file ending with _peaks.csv contains the location of each cluster's peak, it's signal value at this location, the cluster extend (in mm, not in number of voxels) and the atlas correspondence of the peak:

In [12]:
import pandas as pd
pd.read_csv('stat_img_peaks.csv')
Out[12]:
cluster_id peak_x peak_y peak_z peak_value volume_mm aal desikan_killiany harvard_oxford
0 1 42 -25 55 7.94135 60399 Postcentral_R Right-Cerebral-White-Matter 48% Right_Postcentral_Gyrus; 12% Right_Precent...
1 2 -36 -25 55 -7.94144 19386 Postcentral_L Left-Cerebral-White-Matter 30% Left_Postcentral_Gyrus; 30% Left_Precentra...
2 3 -15 -52 -26 7.94135 10260 no_label Left-Cerebellum-Cortex 0% no_label
3 4 18 -55 -23 -7.94144 8964 Cerebelum_6_R Right-Cerebellum-Cortex 0% no_label
4 5 -36 -19 19 -6.21808 1215 Insula_L Unknown 37% Left_Central_Opercular_Cortex; 37% Left_In...
5 6 -6 -19 49 -5.03538 1215 Cingulate_Mid_L ctx-lh-paracentral 50% Left_Precentral_Gyrus; 9% Left_Juxtapositi...
6 7 -30 -10 -2 -4.65454 378 Putamen_L Left-Putamen 98% Left_Putamen
7 8 -15 -55 16 -3.57240 351 Precuneus_L Left-Cerebral-White-Matter 35% Left_Precuneous_Cortex
8 9 -66 -25 31 3.33892 351 SupraMarginal_L ctx-lh-supramarginal 42% Left_Supramarginal_Gyrus_anterior_division...
9 10 -21 41 46 -3.41921 162 Frontal_Sup_2_L Unknown 51% Left_Frontal_Pole; 19% Left_Superior_Front...

Cluster table

The csv file ending with _clusters.csv contains the location of each cluster's peak, the mean value within the cluster, the cluster extend (in mm, not in number of voxels), as well as the membership of each cluster, given a particular atlas.

In [13]:
pd.read_csv('stat_img_clusters.csv')
Out[13]:
cluster_id peak_x peak_y peak_z cluster_mean volume_mm aal desikan_killiany harvard_oxford
0 1 42 -25 55 5.71861 60399 28.39% Postcentral_R; 14.75% Precentral_R; 9.1... 31.47% Unknown; 27.09% Right-Cerebral-White-Ma... 28.07% Right_Postcentral_Gyrus; 19.18% Right_P...
1 2 -36 -25 55 -5.92288 19386 60.17% Postcentral_L; 26.32% Precentral_L; 6.2... 47.49% Left-Cerebral-White-Matter; 18.80% ctx-... 60.58% Left_Postcentral_Gyrus; 35.38% Left_Pre...
2 3 -15 -52 -26 5.28665 10260 43.68% Cerebelum_6_L; 32.63% Cerebelum_4_5_L; ... 76.05% Left-Cerebellum-Cortex; 19.21% Left-Cer... 78.16% no_label; 10.53% Left_Lingual_Gyrus; 5....
3 4 18 -55 -23 -4.93921 8964 32.53% Cerebelum_4_5_R; 31.02% Cerebelum_6_R; ... 75.60% Right-Cerebellum-Cortex; 12.95% Right-C... 81.93% no_label; 15.96% Right_Lingual_Gyrus
4 5 -36 -19 19 -4.30705 1215 71.11% Rolandic_Oper_L; 28.89% Insula_L 46.67% Unknown; 28.89% ctx-lh-supramarginal; 1... 57.78% Left_Central_Opercular_Cortex; 24.44% L...
5 6 -6 -19 49 -3.76954 1215 51.11% Cingulate_Mid_L; 37.78% Supp_Motor_Area... 40.00% ctx-lh-paracentral; 31.11% Unknown; 15.... 68.89% Left_Precentral_Gyrus; 20.00% Left_Juxt...
6 7 -30 -10 -2 -3.67586 378 92.86% Putamen_L; 7.14% no_label 100.00% Left-Putamen 100.00% Left_Putamen
7 8 -15 -55 16 -3.21501 351 84.62% Precuneus_L; 7.69% Cuneus_L; 7.69% Calc... 38.46% ctx-lh-precuneus; 38.46% Left-Cerebral-... 100.00% Left_Precuneous_Cortex
8 9 -66 -25 31 3.10147 351 84.62% SupraMarginal_L; 15.38% no_label 76.92% ctx-lh-supramarginal; 23.08% Left-Cereb... 69.23% Left_Supramarginal_Gyrus_anterior_divis...
9 10 -21 41 46 -3.18072 162 100.00% Frontal_Sup_2_L 66.67% Unknown; 16.67% ctx-lh-superiorfrontal;... 66.67% Left_Frontal_Pole; 33.33% Left_Superior...

For example, in the csv table shown above, we know that 60.17% of the second cluster is in the left postcentral and 26.32% is in the left precentral area, according to the AAL atlas.

Additional parameters

As mentioned before, atlasreader.create_output has many additional parameters that allow you to change the way the clusters are generated and what kind of outputs are generated. So, let's have a closer look:

  • filename: Niimg_like
    A 3D statistical image.
  • cluster_extent: int
    Minimum number of contiguous voxels required to consider a cluster in filename
  • atlas: str or list, optional
    Name of atlas(es) to consider for cluster analysis. Default: 'default'
  • voxel_thresh: float, optional Threshold to apply to stat_img. Use direction to specify the directionality of the threshold. If a negative number is provided a percentile threshold is used instead, where the percentile is determined by the equation 100 - voxel_thresh. Default: 1.96
  • direction: str, optional Specifies the direction in which voxel_thresh should be applied. Possible values are 'both', 'pos' or 'neg'. Default: 'both'
  • prob_thresh: int, optional
    Probability (percentage) threshold to apply to atlas, if it is probabilistic. Default: 5
  • min_distance: float, optional
    Specifies the minimum distance (in mm) required between sub-peaks in a cluster. If None, sub-peaks will not be examined and only the primary cluster peak will be reported. Default: None
  • outdir: str or None, optional
    Path to desired output directory. If None, generated files will be saved to the same folder as filename. Default: None
  • glass_plot_kws: dict or None, optional
    Additional keyword arguments to pass to nilearn.plotting.plot_glass_brain.
  • stat_plot_kws: dict or None, optional
    Additional keyword arguments to pass to nilearn.plotting.plot_stat_map.

The first two, we've already used in the previous example, but let's take a closer look at the others.

atlas and voxel_thresh

First, let's change the atlas and the voxel_thresh parameters:

In [14]:
create_output(file_name, cluster_extent=5,
              atlas=['destrieux', 'marsatlas'],
              voxel_thresh=6)

As you can see, the usage of a higher voxel threshold created smaller clusters and caused the clusters with below threshold voxels to disapear.

In [15]:
Image('stat_img.png')
Out[15]:

Additionally, the csv files now contain the information about the destrieux and marsatlas.

In [16]:
pd.read_csv('stat_img_clusters.csv')
Out[16]:
cluster_id peak_x peak_y peak_z cluster_mean volume_mm destrieux marsatlas
0 1 42 -25 58 7.62272 22869 33.06% Right-Cerebral-White-Matter; 31.17% Unk... 65.41% no_label; 13.34% Right_Dorsolateral_Som...
1 2 -36 -25 55 -7.61916 9639 49.86% Left-Cerebral-White-Matter; 18.77% Unkn... 66.67% no_label; 13.73% Left_Dorsolateral_Soma...
2 3 -15 -52 -26 7.39954 3429 82.68% Left-Cerebellum-Cortex; 17.32% Left-Cer... 100.00% no_label
3 4 45 -19 16 7.30833 2754 44.12% Unknown; 17.65% ctx_rh_G_and_S_subcentr... 53.92% no_label; 34.31% Right_Ventral_Inferior...
4 5 18 -55 -23 -7.25811 2133 94.94% Right-Cerebellum-Cortex; 5.06% Right-Ce... 100.00% no_label
5 6 6 -10 52 6.78709 1053 38.46% ctx_rh_G_front_sup; 23.08% Unknown; 20.... 35.90% no_label; 30.77% Right_Dorsomedial_Moto...
6 7 33 -7 -2 6.74001 243 100.00% Right-Putamen 100.00% Right_Puttamen

Note: The three atlases from the previous example, aal, desikan_killiany, harvard_oxford are the default atlases, which can either be specified with atlas='default', atlas=['aal', 'desikan_killiany', 'harvard_oxford'] or by just omitting the atlas parameter.

If a negative number is provided for voxel_thresh, a percentile threshold is used instead, where the percentile is determined by the equation 100 - voxel_thresh. So, let's threshold the data at 99%.

In [17]:
create_output(file_name, cluster_extent=5,
              voxel_thresh=-1)
In [18]:
Image('stat_img.png')
Out[18]:

Note: This percentage term is misleading if the statistical images contains negative values. For example, 99% in the above example, is the 99 percentile of all values between -7.9 to 7.9.

In [19]:
pd.read_csv('stat_img_clusters.csv')
Out[19]:
cluster_id peak_x peak_y peak_z cluster_mean volume_mm aal desikan_killiany harvard_oxford
0 1 42 -25 58 7.19965 28215 50.91% Postcentral_R; 28.33% Precentral_R; 6.8... 34.16% Right-Cerebral-White-Matter; 29.67% Unk... 48.33% Right_Postcentral_Gyrus; 29.47% Right_P...
1 2 -36 -25 55 -7.13494 12285 64.40% Postcentral_L; 32.53% Precentral_L 48.79% Left-Cerebral-White-Matter; 18.02% ctx-... 63.30% Left_Postcentral_Gyrus; 36.48% Left_Pre...
2 3 -15 -52 -26 6.70928 5265 43.59% Cerebelum_6_L; 38.97% Cerebelum_4_5_L; ... 79.49% Left-Cerebellum-Cortex; 20.00% Left-Cer... 88.72% no_label; 5.13% Left_Lingual_Gyrus
3 4 45 -19 16 6.55843 4509 63.47% Rolandic_Oper_R; 14.97% Insula_R; 8.98%... 44.31% Unknown; 29.34% ctx-rh-supramarginal; 1... 37.72% Right_Parietal_Operculum_Cortex; 37.13%...
4 5 18 -55 -23 -6.41416 3834 48.59% Cerebelum_4_5_R; 39.44% Cerebelum_6_R 82.39% Right-Cerebellum-Cortex; 9.15% Right-Ce... 87.32% no_label; 11.97% Right_Lingual_Gyrus
5 6 6 -10 52 5.88783 2970 64.55% Supp_Motor_Area_R; 35.45% Cingulate_Mid_R 38.18% ctx-rh-paracentral; 33.64% Unknown; 17.... 60.91% Right_Juxtapositional_Lobule_Cortex_(fo...
6 7 33 -7 -2 6.20878 405 86.67% Putamen_R; 6.67% Pallidum_R; 6.67% no_l... 100.00% Right-Putamen 80.00% Right_Putamen; 20.00% Right_Insular_Cortex
7 8 -36 -19 19 -5.61900 297 63.64% Rolandic_Oper_L; 36.36% Insula_L 45.45% Unknown; 27.27% ctx-lh-postcentral; 18.... 90.91% Left_Central_Opercular_Cortex; 9.09% Le...
8 9 6 -70 -38 -5.08795 162 100.00% Vermis_8 100.00% Right-Cerebellum-Cortex 100.00% no_label

direction

The direction parameter allows you to specify the directionality of the thresholding. By default, the directionality is 'both', which means that the voxel_thresh value is applied to positive and negative values. Changing direction to 'pos' or 'neg' allows you to apply the threshold only to one direction and ignore everything else in the other direction.

In [20]:
create_output(file_name, cluster_extent=5, atlas=['aal'],
              voxel_thresh=4, direction='both')
In [21]:
Image('stat_img.png')
Out[21]:
In [22]:
create_output(file_name, cluster_extent=5, atlas=['aal'],
              voxel_thresh=4, direction='pos')
In [23]:
Image('stat_img.png')
Out[23]:
In [24]:
create_output(file_name, cluster_extent=5, atlas=['aal'],
              voxel_thresh=4, direction='neg')
In [25]:
Image('stat_img.png')
Out[25]:

This thresholding directionality is of course also preserved in the tables.

In [26]:
pd.read_csv('stat_img_clusters.csv')
Out[26]:
cluster_id peak_x peak_y peak_z cluster_mean volume_mm aal
0 1 -36 -25 55 -6.63604 15012 63.67% Postcentral_L; 30.40% Precentral_L
1 2 18 -55 -23 -5.90086 5184 43.23% Cerebelum_4_5_R; 38.54% Cerebelum_6_R; ...
2 3 -36 -19 19 -5.00687 648 70.83% Rolandic_Oper_L; 29.17% Insula_L
3 4 6 -70 -38 -4.60103 459 47.06% Vermis_8; 41.18% Cerebelum_8_R; 11.76% ...
4 5 -6 -19 49 -4.41695 351 53.85% Cingulate_Mid_L; 46.15% Supp_Motor_Area_L

prob_thresh

The prob_thresh parameter can be used to restrict the information in the cluster table to a given probability value. This will only influence the information in the table, but won't change the cluster size or any other outcome.

So let's take again an example from before and set the probability threshold to 33%

In [27]:
create_output(file_name, cluster_extent=5,
              prob_thresh=33.0)

As you can see, the cluster table has now fewer entries in the atlas column, as atlas corresponances below 33% where omitted. Be carefull, if this threshold is set to high, no atlas corresponance will survive. In which case the entry is replaces by NaN.

In [28]:
pd.read_csv('stat_img_clusters.csv')
Out[28]:
cluster_id peak_x peak_y peak_z cluster_mean volume_mm aal desikan_killiany harvard_oxford
0 1 42 -25 55 5.71861 60399 NaN NaN NaN
1 2 -36 -25 55 -5.92288 19386 60.17% Postcentral_L 47.49% Left-Cerebral-White-Matter 60.58% Left_Postcentral_Gyrus; 35.24% Left_Pre...
2 3 -15 -52 -26 5.28665 10260 43.68% Cerebelum_6_L 76.05% Left-Cerebellum-Cortex 78.16% no_label
3 4 18 -55 -23 -4.93921 8964 NaN 75.60% Right-Cerebellum-Cortex 81.93% no_label
4 5 -36 -19 19 -4.30705 1215 71.11% Rolandic_Oper_L 46.67% Unknown 57.78% Left_Central_Opercular_Cortex
5 6 -6 -19 49 -3.76954 1215 51.11% Cingulate_Mid_L; 37.78% Supp_Motor_Area_L 40.00% ctx-lh-paracentral 68.89% Left_Precentral_Gyrus
6 7 -30 -10 -2 -3.67586 378 92.86% Putamen_L 100.00% Left-Putamen 100.00% Left_Putamen
7 8 -15 -55 16 -3.21501 351 84.62% Precuneus_L 38.46% ctx-lh-precuneus; 38.46% Left-Cerebral-... 100.00% Left_Precuneous_Cortex
8 9 -66 -25 31 3.10147 351 84.62% SupraMarginal_L 76.92% ctx-lh-supramarginal 69.23% Left_Supramarginal_Gyrus_anterior_division
9 10 -21 41 46 -3.18072 162 100.00% Frontal_Sup_2_L 66.67% Unknown 66.67% Left_Frontal_Pole; 33.33% Left_Superior...

min_distance

By default, AtlasReader only shows information of the main peak in the peak table. But some clusters might be rather big with many different peaks. If you also want to extract peak information of other peaks within a given cluster, you can use the min_distance paramter. This parameter specifies the minimum distance required between sub-peaks in a cluster.

To better illustrate this example, let's focus only on the big four cluster in our dataset (i.e. we will set cluster_extent=100).

In [29]:
create_output(file_name, cluster_extent=100,
              atlas='aal',
              min_distance=10)
In [30]:
Image('stat_img.png')
Out[30]: