{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n# How to add new bundles into pyAFQ (SLF 1/2/3 Example)\n\npyAFQ is designed to be customizable and extensible. This example shows how you\ncan customize it to define a new bundle based on a definition of waypoint and\nendpoint ROIs of your design.\n\nIn this case, we add sub-bundles of the superior longitudinal fasciculus,\nbased on work by Sagi et al [1]_.\n\nWe start by importing some of the components that we need for this example and\nfixing the random seed for reproducibility\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import os.path as op\nimport numpy as np\n\nfrom AFQ.api.group import GroupAFQ\nimport AFQ.api.bundle_dict as abd\nimport AFQ.data.fetch as afd\nfrom AFQ.definitions.image import RoiImage\nimport wget\nimport os\nnp.random.seed(1234)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Get dMRI data\nWe will analyze eight subject from the Healthy Brain Network Processed Open\nDiffusion Derivatives dataset (HBN-POD2) [2]_, [3]_. We'll use a fetcher to\nget preprocessed dMRI data for eight of the >2,000 subjects in that study. The\ndata gets organized into a BIDS-compatible format in the `~/AFQ_data/HBN`\nfolder. These 12 subjects have very high quality data.\nThe fether returns this directory as study_dir:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "_, study_dir = afd.fetch_hbn_preproc([\n 'NDARKP893TWU',\n 'NDAREP505XAD',\n 'NDARKT540ZW0',\n 'NDARAG340ERT',\n 'NDAREM757NBG',\n 'NDARLL894HC3',\n 'NDARFY525TL2',\n 'NDARKV461KGZ',\n 'NDARUC851WHU',\n 'NDARMJ333WJM',\n 'NDARJG687YYX',\n 'NDARJA157YB3',\n])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Get ROIs and save to disk\nThe goal of this tutorial is to demonstrate how to segment new pathways based\non ROIs that are saved to disk. In principle, ROIs can be a) files created by\nthe user and saved to the local disk, b) files stored somewhere on the internet\n(as is the case here) or c) Files that are accessed with a fetcher. In this\nexample we download these files from the MATLAB AFQ website, but this code could\nbe commented out and paths could be used to local ROIs on disk\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "roi_urls = ['https://github.com/yeatmanlab/AFQ/raw/c762ca4c393f2105d4f444c44d9e4b4702f0a646/SLF123/ROIs/MFgL.nii.gz',\n 'https://github.com/yeatmanlab/AFQ/raw/c762ca4c393f2105d4f444c44d9e4b4702f0a646/SLF123/ROIs/MFgR.nii.gz',\n 'https://github.com/yeatmanlab/AFQ/raw/c762ca4c393f2105d4f444c44d9e4b4702f0a646/SLF123/ROIs/PaL.nii.gz',\n 'https://github.com/yeatmanlab/AFQ/raw/c762ca4c393f2105d4f444c44d9e4b4702f0a646/SLF123/ROIs/PaR.nii.gz',\n 'https://github.com/yeatmanlab/AFQ/raw/c762ca4c393f2105d4f444c44d9e4b4702f0a646/SLF123/ROIs/PrgL.nii.gz',\n 'https://github.com/yeatmanlab/AFQ/raw/c762ca4c393f2105d4f444c44d9e4b4702f0a646/SLF123/ROIs/PrgR.nii.gz',\n 'https://github.com/yeatmanlab/AFQ/raw/c762ca4c393f2105d4f444c44d9e4b4702f0a646/SLF123/ROIs/SFgL.nii.gz',\n 'https://github.com/yeatmanlab/AFQ/raw/c762ca4c393f2105d4f444c44d9e4b4702f0a646/SLF123/ROIs/SFgR.nii.gz',\n 'https://github.com/yeatmanlab/AFQ/raw/c762ca4c393f2105d4f444c44d9e4b4702f0a646/SLF123/ROIs/SLFt_roi2_L.nii.gz',\n 'https://github.com/yeatmanlab/AFQ/raw/c762ca4c393f2105d4f444c44d9e4b4702f0a646/SLF123/ROIs/SLFt_roi2_R.nii.gz']\n\n# We proceed to download the files. First, we define and create the directory\n# for the template ROIs. In the code below, ``op.expanduser(\"~\")`` expands the\n# user's home directory into the full path and ``op.join`` joins these paths,\n# to make the path `~/AFQ_data/SLF_ROIs/`\n\ntemplate_dir = op.join(\n op.expanduser(\"~\"),\n 'AFQ_data/SLF_ROIs/')\nos.makedirs(template_dir, exist_ok=True)\n\n# The `wget` Python library works like the `wget` unix command and downloads\n# each file into the directory created just above.\n\nfor roi_url in roi_urls:\n wget.download(roi_url, template_dir)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Define custom `BundleDict` object\nA `BundleDict` is a custom object that holds information about \"include\" and\n\"exclude\" ROIs, as well as endpoint ROIs, and whether the bundle crosses the\nmidline. In this case, the ROIs are all defined in the MNI template space that\nis used as the default template space in pyAFQ, but, in principle, other\ntemplate spaces could be used. In this example, we provide paths to the ROIs\nto populate the `BundleDict`, but we could also provide already-loaded nifti\nobjects, as demonstrated in other examples.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "bundles = abd.BundleDict({\n \"L_SLF1\": {\n \"include\": [\n template_dir + 'SFgL.nii.gz',\n template_dir + 'PaL.nii.gz'],\n \"exclude\": [\n template_dir + 'SLFt_roi2_L.nii.gz'],\n\n \"cross_midline\": False,\n\n \"mahal\": {\n \"clean_rounds\": 20,\n \"length_threshold\": 4,\n \"distance_threshold\": 2}\n },\n \"L_SLF2\": {\n \"include\": [\n template_dir + 'MFgL.nii.gz',\n template_dir + 'PaL.nii.gz'],\n \"exclude\": [\n template_dir + 'SLFt_roi2_L.nii.gz'],\n\n \"cross_midline\": False,\n\n \"mahal\": {\n \"clean_rounds\": 20,\n \"length_threshold\": 4,\n \"distance_threshold\": 2}\n },\n \"L_SLF3\": {\n \"include\": [\n template_dir + 'PrgL.nii.gz',\n template_dir + 'PaL.nii.gz'],\n \"exclude\": [\n template_dir + 'SLFt_roi2_L.nii.gz'],\n\n \"cross_midline\": False,\n\n \"mahal\": {\n \"clean_rounds\": 20,\n \"length_threshold\": 4,\n \"distance_threshold\": 2}\n }\n})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Custom bundle definitions such as the SLF or OR, and the standard BundleDict\ncan be combined through addition. To get both the SLF and the standard\nbundles, we would execute the following code::\n\n bundles = bundles + abd.default_bd()\n\nIn this case, we will skip this and generate just the SLF.\n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Define GroupAFQ object\nHBN POD2 have been processed with qsiprep [4]_. This means that a brain mask\nhas already been computed for them.\n\nFor tractography, we use CSD-based probabilistic tractography,\nseeding 200,000 seeds but only within the ROIs\nand not throughout the white matter. This is controlled by passing\n`\"seed_mask\": RoiImage()` in the `tracking_params` dict. The custom bundles\nare passed as `bundle_info=bundles`. The call to `my_afq.export_all()`\ninitiates the pipeline.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "my_afq = GroupAFQ(\n bids_path=study_dir,\n dwi_preproc_pipeline=\"qsiprep\",\n output_dir=op.join(study_dir, \"derivatives\", \"afq_slf\"),\n tracking_params={\"n_seeds\": 200000,\n \"directions\": \"pft\",\n \"odf_model\": \"CSD\",\n \"seed_mask\": RoiImage()},\n bundle_info=bundles)\n\n# If you want to redo different stages you can use the `clobber` method.\n# The options for dependent_on are 'track' (to start over from tractography)\n# or 'recog' to start over from bundle recognition. For example, to redo everying\n# related to bundle recognition: `my_afq.clobber(dependent_on='recog')`.\n# This is useful when changing something about how the bundles are recognized.\n# For example, the cleaning parameters.\n\nmy_afq.clobber(dependent_on='recog')\n\nmy_afq.export_all()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Visualize a montage\nOne way to examine the output of the pyAFQ pipeline is by creating a montage\nof images of a particular bundle across a group of participants. In the montage function\nthe first input refers to a key in the bundlediect and the second gives the layout\nof the figure (eg. 3 rows 4 columns) and finally is the view.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "montage = my_afq.group_montage(\n \"L_SLF1\", (3, 4), \"Sagittal\", \"left\", slice_pos=0.5)\nmontage = my_afq.group_montage(\n \"L_SLF2\", (3, 4), \"Sagittal\", \"left\", slice_pos=0.5)\nmontage = my_afq.group_montage(\n \"L_SLF3\", (3, 4), \"Sagittal\", \"left\", slice_pos=0.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Interactive bundle visualization\nAnother way to examine the outputs is to export the individual bundle\nfigures, which show the streamlines, as well as the ROIs used to define the\nbundle. This is an html file, which contains an interactive figure that can\nbe navigated, zoomed, rotated, etc.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "bundle_html = my_afq.export(\"all_bundles_figure\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## References\n.. [1] Romi Sagi, J.S.H. Taylor, Kyriaki Neophytou, Tamar Cohen,\n Brenda Rapp, Kathleen Rastle, Michal Ben-Shachar.\n White matter associations with spelling performance.\n Brain Struct Funct 229, 2115\u20132135 (2024).\n https://doi.org/10.1007/s00429-024-02775-7\n\n.. [2] Alexander LM, Escalera J, Ai L, et al. An open resource for\n transdiagnostic research in pediatric mental health and learning\n disorders. Sci Data. 2017;4:170181.\n\n.. [3] Richie-Halford A, Cieslak M, Ai L, et al. An analysis-ready and quality\n controlled resource for pediatric brain white-matter research. Scientific\n Data. 2022;9(1):1-27.\n\n.. [4] Cieslak M, Cook PA, He X, et al. QSIPrep: an integrative platform for\n preprocessing and reconstructing diffusion MRI data. Nat Methods.\n 2021;18(7):775-778.\n\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.13" } }, "nbformat": 4, "nbformat_minor": 0 }