{ "cells": [ { "cell_type": "markdown", "id": "bfb40f2d", "metadata": {}, "source": [ "# Getting started with pyAFQ - ParticipantAFQ" ] }, { "cell_type": "code", "execution_count": null, "id": "1bcf36c9", "metadata": {}, "outputs": [], "source": [ "import os\n", "import os.path as op\n", "\n", "import matplotlib.pyplot as plt\n", "import nibabel as nib\n", "import plotly\n", "\n", "from AFQ.api.participant import ParticipantAFQ\n", "import AFQ.data.fetch as afd\n", "import AFQ.definitions.image as afm" ] }, { "cell_type": "markdown", "id": "f762e5e4", "metadata": {}, "source": [ "## Example data\n", "\n", "The following call downloads a a single subject's data from the Healthy Brain\n", "Network Processed Open Diffusion Derivatives dataset (HBN-POD2) [^1], [^2]\n", "and organizes it in BIDS in the user's home directory under:\n", "\n", "`~/AFQ_data/HBN/`\n", "\n", "The data is also placed in a derivatives directory, signifying that it has\n", "already undergone the required preprocessing necessary for pyAFQ to run." ] }, { "cell_type": "code", "execution_count": null, "id": "8016f8b1", "metadata": {}, "outputs": [], "source": [ "afd.fetch_hbn_preproc([\"NDARAA948VFH\"])" ] }, { "cell_type": "markdown", "id": "3b44b64a", "metadata": {}, "source": [ "## Defining data files\n", "\n", "If your data is not in BIDS format, you can still use pyAFQ. If you have BIDS\n", "compliant dataset, you can use `GroupAFQ` instead ([plot_001_group_afq_api](plot_001_group_afq_api)).\n", "Otherwise, You will need to define the data files that you want to use. In\n", "this case, we will define the data files for the subject we downloaded above.\n", "The data files are located in the `~/AFQ_data/HBN/derivatives/qsiprep`\n", "directory, and are organized into a BIDS compliant directory structure. The\n", "data files are located in the `dwi` directories." ] }, { "cell_type": "code", "execution_count": null, "id": "f2cbf251", "metadata": {}, "outputs": [], "source": [ "sub_dir = op.join(afd.afq_home, \"HBN\", \"derivatives\", \"qsiprep\",\n", " \"sub-NDARAA948VFH\")\n", "dwi_data_file = op.join(sub_dir, \"ses-HBNsiteRU\", \"dwi\", (\n", " \"sub-NDARAA948VFH_\"\n", " \"ses-HBNsiteRU_\"\n", " \"acq-64dir_space-T1w_desc-preproc_dwi.nii.gz\"))\n", "bval_file = op.join(sub_dir, \"ses-HBNsiteRU\", \"dwi\", (\n", " \"sub-NDARAA948VFH_\"\n", " \"ses-HBNsiteRU_\"\n", " \"acq-64dir_space-T1w_desc-preproc_dwi.bval\"))\n", "bvec_file = op.join(sub_dir, \"ses-HBNsiteRU\", \"dwi\", (\n", " \"sub-NDARAA948VFH_\"\n", " \"ses-HBNsiteRU_\"\n", " \"acq-64dir_space-T1w_desc-preproc_dwi.bvec\"))\n", "t1_file = op.join(sub_dir, \"anat\",\n", " \"sub-NDARAA948VFH_desc-preproc_T1w.nii.gz\")\n", "\n", "# You will also need to define the output directory where you want to store the\n", "# results. The output directory needs to exist before exporting ParticipantAFQ\n", "# results.\n", "\n", "output_dir = op.join(afd.afq_home, \"HBN\",\n", " \"derivatives\", \"afq\", \"sub-NDARAA948VFH\",\n", " \"ses-HBNsiteRU\", \"dwi\")\n", "os.makedirs(output_dir, exist_ok=True)" ] }, { "cell_type": "markdown", "id": "700749eb", "metadata": {}, "source": [ "## Set tractography parameters (optional)\n", "We make create a `tracking_params` variable, which we will pass to the\n", "ParticipantAFQ object which specifies that we want 100,000 seeds randomly\n", "distributed in the white matter. We only do this to make this example faster\n", "and consume less space; normally, we use more seeds." ] }, { "cell_type": "code", "execution_count": null, "id": "60e57202", "metadata": {}, "outputs": [], "source": [ "tracking_params = dict(n_seeds=1e5,\n", " random_seeds=True,\n", " rng_seed=2025,\n", " trx=True)" ] }, { "cell_type": "markdown", "id": "6d3434c5", "metadata": {}, "source": [ "## Define PVE images (optional)\n", "To improve segmentation and tractography results, we can provide\n", "partial volume estimate (PVE) images for the cerebrospinal fluid (CSF),\n", "gray matter (GM), and white matter (WM). Here, we define these images\n", "using the AFQ.definitions.image.PVEImages class, which takes as input\n", "three AFQ.definitions.image.ImageFile objects, one for each tissue type.\n", "One can also provide a single PVE image with all three tissue types\n", "using the AFQ.definitions.image.PVEImage class. Finally, by default,\n", "if no PVE images are provided, pyAFQ will use SynthSeg2 to compute\n", "these images." ] }, { "cell_type": "code", "execution_count": null, "id": "1222a6b2", "metadata": {}, "outputs": [], "source": [ "pve = afm.PVEImages(\n", " afm.ImageFile(\n", " path=op.join(sub_dir, \"anat\", \n", " \"sub-NDARAA948VFH_label-CSF_probseg.nii.gz\")),\n", " afm.ImageFile(\n", " path=op.join(sub_dir, \"anat\", \n", " \"sub-NDARAA948VFH_label-GM_probseg.nii.gz\")),\n", " afm.ImageFile(\n", " path=op.join(sub_dir, \"anat\", \n", " \"sub-NDARAA948VFH_label-WM_probseg.nii.gz\")))" ] }, { "cell_type": "markdown", "id": "c3ccbc2f", "metadata": {}, "source": [ "## Brain Mask Definition (optional)\n", "\n", "By default, pyAFQ will compute a brain mask from the T1. However,\n", "this requires onnxruntime to be installed. If you do not have onnxruntime\n", "installed, or if you want to use a different brain mask, you can specify\n", "it here." ] }, { "cell_type": "code", "execution_count": null, "id": "cd200c54", "metadata": {}, "outputs": [], "source": [ "brain_mask_definition = afm.ImageFile(\n", " path=op.join(sub_dir, \"anat\", \"sub-NDARAA948VFH_desc-brain_mask.nii.gz\"))" ] }, { "cell_type": "markdown", "id": "9b5d05c0", "metadata": {}, "source": [ "## Initialize a ParticipantAFQ object:\n", "\n", "Creates a ParticipantAFQ object, that encapsulates tractometry. This object\n", "can be used to manage the entire [tractometry pipeline](/explanations/index), including:\n", "\n", "- Tractography\n", "- Registration\n", "- Segmentation\n", "- Cleaning\n", "- Profiling\n", "- Visualization\n", "\n", "To initialize the object, we will pass in the diffusion data files and specify\n", "the output directory where we want to store the results. We will also\n", "pass in the tracking parameters we defined above." ] }, { "cell_type": "code", "execution_count": null, "id": "c015bae4", "metadata": {}, "outputs": [], "source": [ "myafq = ParticipantAFQ(\n", " dwi_data_file=dwi_data_file,\n", " bval_file=bval_file,\n", " bvec_file=bvec_file,\n", " t1_file=t1_file,\n", " output_dir=output_dir,\n", " tracking_params=tracking_params,\n", " pve=pve,\n", " brain_mask_definition=brain_mask_definition,\n", ")" ] }, { "cell_type": "markdown", "id": "2271676f", "metadata": {}, "source": [ "## Calculating DTI FA (Diffusion Tensor Imaging Fractional Anisotropy)\n", "\n", "The ParticipantAFQ object has a method called `export`, which allows the user\n", "to calculate various derived quantities from the data.\n", "\n", "For example, FA can be computed using the DTI model, by explicitly\n", "calling `myafq.export(\"dti_fa\")`. This triggers the computation of DTI\n", "parameters, and stores the results in the AFQ derivatives directory.\n", "In addition, it calculates the FA from these parameters and stores it in a\n", "different file in the same directory.\n", "\n", ":::{note}\n", "The AFQ API computes quantities lazily. This means that DTI parameters\n", "are not computed until they are required. This means that the first\n", "line below is the one that requires time.\n", ":::\n", "\n", "The result of the call to `export` is the filename of the corresponding FA\n", "files." ] }, { "cell_type": "code", "execution_count": null, "id": "ff0c45a5", "metadata": {}, "outputs": [], "source": [ "FA_fname = myafq.export(\"dti_fa\")" ] }, { "cell_type": "markdown", "id": "efd5ac63", "metadata": {}, "source": [ "We will then use `nibabel` to load the deriviative file and retrieve the\n", "data array." ] }, { "cell_type": "code", "execution_count": null, "id": "2339af28", "metadata": {}, "outputs": [], "source": [ "FA_img = nib.load(FA_fname)\n", "FA = FA_img.get_fdata()" ] }, { "cell_type": "markdown", "id": "13ba920c", "metadata": {}, "source": [ "## Visualize the result with Matplotlib\n", "\n", "At this point `FA` is an array, and we can use standard Python tools to\n", "visualize it or perform additional computations with it.\n", "\n", "In this case we are going to take an axial slice halfway through the\n", "FA data array and plot using a sequential color map.\n", "\n", ":::{note}\n", "The data array is structured as a xyz coordinate system.\n", ":::" ] }, { "cell_type": "code", "execution_count": null, "id": "e9616e40", "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(1)\n", "ax.matshow(FA[:, :, FA.shape[-1] // 2], cmap=\"viridis\")\n", "ax.axis(\"off\")\n", "plt.savefig(\"FA.png\")" ] }, { "cell_type": "code", "execution_count": null, "id": "0e757f54", "metadata": { "tags": [ "remove-input" ] }, "outputs": [], "source": [ "from AFQ.utils.docs import embed_html, embed_image\n", "\n", "embed_image(\"FA.png\")" ] }, { "cell_type": "markdown", "id": "59167ca3", "metadata": {}, "source": [ "## Recognizing the bundles and calculating tract profiles:\n", "\n", "Typically, users of pyAFQ are interested in calculating not only an overall\n", "map of the FA, but also the major white matter pathways (or bundles) and\n", "tract profiles of tissue properties along their length. To trigger the\n", "pyAFQ pipeline that calculates the profiles, users can call the\n", "`export(\"profiles\")` method:\n", "\n", ":::{note}\n", "Running the code below triggers the full pipeline of operations\n", "leading to the computation of the tract profiles. Therefore, it\n", "takes a little while to run (about 40 minutes, typically).\n", ":::" ] }, { "cell_type": "code", "execution_count": null, "id": "9dd5104b", "metadata": {}, "outputs": [], "source": [ "myafq.export(\"profiles\")" ] }, { "cell_type": "markdown", "id": "52dd0ca9", "metadata": {}, "source": [ "## Visualizing the bundles and calculating tract profiles:\n", "\n", "The pyAFQ API provides several ways to visualize bundles and profiles.\n", "\n", "First, we will run a function that exports an html file that contains\n", "an interactive visualization of the bundles that are segmented.\n", "\n", ":::{note}\n", "By default we resample a 100 points within a bundle, however to reduce\n", "processing time we will only resample 50 points.\n", ":::\n", "\n", "Once it is done running, it should pop a browser window open and let you\n", "interact with the bundles.\n", "\n", ":::{note}\n", "You can hide or show a bundle by clicking the legend, or select a\n", "single bundle by double clicking the legend. The interactive\n", "visualization will also allow you to pan, zoom, and rotate.\n", ":::" ] }, { "cell_type": "code", "execution_count": null, "id": "fbec6383", "metadata": { "tags": [ "remove-execution" ] }, "outputs": [], "source": [ "bundle_html = myafq.export(\"all_bundles_figure\")\n", "plotly.io.show(bundle_html[0])" ] }, { "cell_type": "code", "execution_count": null, "id": "1a7769f0", "metadata": { "tags": [ "remove-input" ] }, "outputs": [], "source": [ "bundle_html = myafq.export(\"all_bundles_figure\")\n", "embed_html(bundle_html[1])" ] }, { "cell_type": "markdown", "id": "87b6d04d", "metadata": {}, "source": [ "We can also visualize the tract profiles in all of the bundles. These\n", "plots show both FA (left) and MD (right) laid out anatomically.\n", "To make this plot, it is required that you install with\n", "`pip install pyAFQ[plot]` so that you have the necessary dependencies." ] }, { "cell_type": "code", "execution_count": null, "id": "714ea07f", "metadata": {}, "outputs": [], "source": [ "fig_files = myafq.export(\"tract_profile_plots\")" ] }, { "cell_type": "code", "execution_count": null, "id": "af405af7", "metadata": { "tags": [ "remove-input" ] }, "outputs": [], "source": [ "embed_image(fig_files[0] + \".png\")" ] }, { "cell_type": "markdown", "id": "4de008c1", "metadata": {}, "source": [ "## Exporting citations\n", "Finally, we can export the citations for the some of methods used in this\n", "analysis. These are not guaranteed to be comprehensive, but they\n", "should be a good starting point." ] }, { "cell_type": "code", "execution_count": null, "id": "163fba9b", "metadata": {}, "outputs": [], "source": [ "myafq.export(\"citations\")" ] }, { "cell_type": "markdown", "id": "1156b01b", "metadata": {}, "source": [ "## References\n", "\n", "[^1]: 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", "[^2]: 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", "[^3]: 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", ":::{only} html\n", "{download}`Download as Jupyter Notebook `\n", ":::" ] } ], "metadata": { "file_format": "mystnb", "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 5 }