diff --git a/applications/fslpy/fslpy.ipynb b/applications/fslpy/fslpy.ipynb index a8813b7ca7b8d9e0b8777fa558e804b0ba987d7b..953821cae8945e072bfaad021d6ef03dc635cdc1 100644 --- a/applications/fslpy/fslpy.ipynb +++ b/applications/fslpy/fslpy.ipynb @@ -2,15 +2,16 @@ "cells": [ { "cell_type": "markdown", + "id": "d9547ad5", "metadata": {}, "source": [ "# `fslpy`\n", "\n", "\n", - "> *Note*: This practical assumes that you have FSL 6.0.4 or newer installed.\n", + "> *Note*: This practical assumes that you have FSL 6.0.5.1 or newer installed.\n", "\n", "\n", - "[`fslpy`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/) is a\n", + "[`fslpy`](https://open.win.ox.ac.uk/pages/fsl/fslpy/) is a\n", "Python library which is built into FSL, and contains a range of functionality\n", "for working with FSL and with neuroimaging data from Python.\n", "\n", @@ -34,7 +35,6 @@ " * [Describing your data](#describing-your-data)\n", " * [Using the `FileTree`](#using-the-filetree)\n", " * [Building a processing pipeline with `FileTree`](#building-a-processing-pipeline-with-filetree)\n", - " * [The `FileTreeQuery`](#the-filetreequery)\n", "* [Calling shell commands](#calling-shell-commands)\n", " * [The `runfsl` function](#the-runfsl-function)\n", " * [Submitting to the cluster](#submitting-to-the-cluster)\n", @@ -56,6 +56,7 @@ { "cell_type": "code", "execution_count": null, + "id": "0a8807ba", "metadata": {}, "outputs": [], "source": [ @@ -72,6 +73,7 @@ }, { "cell_type": "markdown", + "id": "fb1c18e4", "metadata": {}, "source": [ "And a little function that we can use to generate a simple orthographic plot:" @@ -80,6 +82,7 @@ { "cell_type": "code", "execution_count": null, + "id": "733fa41c", "metadata": {}, "outputs": [], "source": [ @@ -138,6 +141,7 @@ }, { "cell_type": "markdown", + "id": "a375c62b", "metadata": {}, "source": [ "And another function which uses FSLeyes for more complex plots:" @@ -146,32 +150,26 @@ { "cell_type": "code", "execution_count": null, + "id": "2fa4e153", "metadata": {}, "outputs": [], "source": [ "def render(cmdline):\n", "\n", - " import shlex\n", " import IPython.display as display\n", "\n", " prefix = '-of screenshot.png -hl -c 2 '\n", "\n", - " try:\n", - " from fsleyes.render import main\n", - " main(shlex.split(prefix + cmdline))\n", - "\n", - " except (ImportError, AttributeError):\n", - " # fall-back for macOS - we have to run\n", - " # FSLeyes render in a separate process\n", - " from fsl.utils.run import runfsl\n", - " prefix = 'render ' + prefix\n", - " runfsl(prefix + cmdline, env={})\n", + " from fsl.utils.run import runfsl\n", + " prefix = 'render ' + prefix\n", + " runfsl(prefix + cmdline, env={})\n", "\n", " return display.Image('screenshot.png')" ] }, { "cell_type": "markdown", + "id": "3667cc28", "metadata": {}, "source": [ "\n", @@ -179,7 +177,7 @@ "\n", "\n", "The\n", - "[`fsl.data.image`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.image.html#fsl.data.image.Image)\n", + "[`fsl.data.image`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.image.html#fsl.data.image.Image)\n", "module provides the `Image` class, which sits on top of `nibabel` and contains\n", "some handy functionality if you need to work with coordinate transformations,\n", "or do some FSL-specific processing. The `Image` class provides features such\n", @@ -195,7 +193,7 @@ "> example, when you create an `Image` object, the default behaviour is to load\n", "> the image data into memory. This is configurable however; take a look at\n", "> [the\n", - "> documentation](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.image.html#fsl.data.image.Image)\n", + "> documentation](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.image.html#fsl.data.image.Image)\n", "> to explore all of the options.\n", "\n", "\n", @@ -213,6 +211,7 @@ { "cell_type": "code", "execution_count": null, + "id": "649bcad8", "metadata": {}, "outputs": [], "source": [ @@ -229,6 +228,7 @@ }, { "cell_type": "markdown", + "id": "68003fb0", "metadata": {}, "source": [ "You can create an `Image` from an existing `nibabel` image:" @@ -237,6 +237,7 @@ { "cell_type": "code", "execution_count": null, + "id": "863c9948", "metadata": {}, "outputs": [], "source": [ @@ -249,6 +250,7 @@ }, { "cell_type": "markdown", + "id": "086a20bf", "metadata": {}, "source": [ "Or you can create an `Image` from a `numpy` array:" @@ -257,6 +259,7 @@ { "cell_type": "code", "execution_count": null, + "id": "ba0f297d", "metadata": {}, "outputs": [], "source": [ @@ -267,6 +270,7 @@ }, { "cell_type": "markdown", + "id": "a3e4a949", "metadata": {}, "source": [ "If you have generated some data from another `Image` (or from a\n", @@ -277,6 +281,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5566318e", "metadata": {}, "outputs": [], "source": [ @@ -285,6 +290,7 @@ }, { "cell_type": "markdown", + "id": "8f13c01b", "metadata": {}, "source": [ "You can save an image to file via the `save` method:" @@ -293,6 +299,7 @@ { "cell_type": "code", "execution_count": null, + "id": "7245b478", "metadata": {}, "outputs": [], "source": [ @@ -302,6 +309,7 @@ }, { "cell_type": "markdown", + "id": "3f50bba4", "metadata": {}, "source": [ "`Image` objects have all of the attributes you might expect:" @@ -310,6 +318,7 @@ { "cell_type": "code", "execution_count": null, + "id": "997ba5ec", "metadata": {}, "outputs": [], "source": [ @@ -328,6 +337,7 @@ }, { "cell_type": "markdown", + "id": "100f5f17", "metadata": {}, "source": [ "and a number of useful methods:" @@ -336,6 +346,7 @@ { "cell_type": "code", "execution_count": null, + "id": "bfa9c7cc", "metadata": {}, "outputs": [], "source": [ @@ -349,6 +360,7 @@ }, { "cell_type": "markdown", + "id": "f26eb209", "metadata": {}, "source": [ "An `Image` object is a high-level wrapper around a `nibabel` image object -\n", @@ -359,6 +371,7 @@ { "cell_type": "code", "execution_count": null, + "id": "f297fc8a", "metadata": {}, "outputs": [], "source": [ @@ -368,6 +381,7 @@ }, { "cell_type": "markdown", + "id": "b165916c", "metadata": {}, "source": [ "\n", @@ -380,6 +394,7 @@ { "cell_type": "code", "execution_count": null, + "id": "be17fb15", "metadata": {}, "outputs": [], "source": [ @@ -390,6 +405,7 @@ }, { "cell_type": "markdown", + "id": "6bc1e464", "metadata": {}, "source": [ "> Note that `Image.data` will give you the data in its underlying type, unlike\n", @@ -402,6 +418,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4ece60d9", "metadata": {}, "outputs": [], "source": [ @@ -411,6 +428,7 @@ }, { "cell_type": "markdown", + "id": "d4128213", "metadata": {}, "source": [ "Doing so has some advantages that may or may not be useful, depending on your\n", @@ -428,45 +446,45 @@ "\n", "\n", "The\n", - "[`fsl.data`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.html#module-fsl.data)\n", + "[`fsl.data`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.html#module-fsl.data)\n", "package has a number of other classes for working with different types of FSL\n", "and neuroimaging data. Most of these are higher-level wrappers around the\n", "corresponding `nibabel` types:\n", "\n", "* The\n", - " [`fsl.data.bitmap.Bitmap`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.bitmap.html)\n", + " [`fsl.data.bitmap.Bitmap`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.bitmap.html#fsl.data.bitmap.Bitmap)\n", " class can be used to load a bitmap image (e.g. `jpg`, `png`, etc) and\n", " convert it to a NIfTI image.\n", "* The\n", - " [`fsl.data.dicom.DicomImage`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.dicom.html)\n", + " [`fsl.data.dicom.DicomImage`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.dicom.html#fsl.data.dicom.DicomImage)\n", " class uses `dcm2niix` to load NIfTI images contained within a DICOM\n", " directory*.\n", "* The\n", - " [`fsl.data.mghimage.MGHImage`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.mghimage.html)\n", + " [`fsl.data.mghimage.MGHImage`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.mghimage.html#fsl.data.mghimage.MGHImage)\n", " class can be used to load `.mgh`/`.mgz` images (they are converted into\n", " NIfTI images).\n", "* The\n", - " [`fsl.data.dtifit`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.dtifit.html)\n", + " [`fsl.data.dtifit`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.dtifit.html)\n", " module contains functions for loading and working with the output of the\n", " FSL `dtifit` tool.\n", "* The\n", - " [`fsl.data.featanalysis`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.featanalysis.html),\n", - " [`fsl.data.featimage`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.featimage.html),\n", + " [`fsl.data.featanalysis`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.featanalysis.html),\n", + " [`fsl.data.featimage`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.featimage.html),\n", " and\n", - " [`fsl.data.featdesign`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.featdesign.html)\n", + " [`fsl.data.featdesign`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.featdesign.html)\n", " modules contain classes and functions for loading data from FEAT\n", " directories.\n", "* Similarly, the\n", - " [`fsl.data.melodicanalysis`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.melodicanalysis.html)\n", + " [`fsl.data.melodicanalysis`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.melodicanalysis.html)\n", " and\n", - " [`fsl.data.melodicimage`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.melodicimage.html)\n", + " [`fsl.data.melodicimage`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.melodicimage.html)\n", " modules contain classes and functions for loading data from MELODIC\n", " directories.\n", "* The\n", - " [`fsl.data.gifti`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.gifti.html),\n", - " [`fsl.data.freesurfer`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.freesurfer.html),\n", + " [`fsl.data.gifti`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.gifti.html),\n", + " [`fsl.data.freesurfer`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.freesurfer.html),\n", " and\n", - " [`fsl.data.vtk`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.vtk.html)\n", + " [`fsl.data.vtk`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.vtk.html)\n", " modules contain functionality form loading surface data from GIfTI,\n", " freesurfer, and ASCII VTK files respectively.\n", "\n", @@ -489,6 +507,7 @@ { "cell_type": "code", "execution_count": null, + "id": "988a63e9", "metadata": {}, "outputs": [], "source": [ @@ -508,17 +527,19 @@ }, { "cell_type": "markdown", + "id": "65121fc2", "metadata": {}, "source": [ "The code above is a bit fiddly, so instead of figuring it out, you can just\n", "use the\n", - "[`affine.transform`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.transform.affine.html#fsl.transform.affine.transform)\n", + "[`affine.transform`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.transform.affine.html#fsl.transform.affine.transform)\n", "function:" ] }, { "cell_type": "code", "execution_count": null, + "id": "67da0015", "metadata": {}, "outputs": [], "source": [ @@ -537,6 +558,7 @@ }, { "cell_type": "markdown", + "id": "bd76e384", "metadata": {}, "source": [ "> The `Image.getAffine` method can give you transformation matrices\n", @@ -563,6 +585,7 @@ { "cell_type": "code", "execution_count": null, + "id": "667ca11f", "metadata": {}, "outputs": [], "source": [ @@ -581,6 +604,7 @@ }, { "cell_type": "markdown", + "id": "c0fdb45f", "metadata": {}, "source": [ "Now that we've got the voxel coordinates in functional space, we need to\n", @@ -591,6 +615,7 @@ { "cell_type": "code", "execution_count": null, + "id": "0c8a57db", "metadata": {}, "outputs": [], "source": [ @@ -599,6 +624,7 @@ }, { "cell_type": "markdown", + "id": "49ee9de3", "metadata": {}, "source": [ "But ... wait a minute ... this is a FLIRT matrix! We can't just plug voxel\n", @@ -612,6 +638,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8acb014f", "metadata": {}, "outputs": [], "source": [ @@ -621,6 +648,7 @@ }, { "cell_type": "markdown", + "id": "baff5147", "metadata": {}, "source": [ "Now we can use them to get affines which convert between all of the different\n", @@ -635,6 +663,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8b693da2", "metadata": {}, "outputs": [], "source": [ @@ -644,6 +673,7 @@ }, { "cell_type": "markdown", + "id": "86d75acf", "metadata": {}, "source": [ "Combining two affines into one is just a simple dot-product. There is a\n", @@ -653,6 +683,7 @@ { "cell_type": "code", "execution_count": null, + "id": "12beef51", "metadata": {}, "outputs": [], "source": [ @@ -667,10 +698,11 @@ }, { "cell_type": "markdown", + "id": "94b36d03", "metadata": {}, "source": [ "> In the next section we will use the\n", - "> [`fsl.transform.flirt.fromFlirt`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.transform.flirt.html#fsl.transform.flirt.fromFlirt)\n", + "> [`fsl.transform.flirt.fromFlirt`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.transform.flirt.html#fsl.transform.flirt.fromFlirt)\n", "> function, which does all of the above for us.\n", "\n", "\n", @@ -681,6 +713,7 @@ { "cell_type": "code", "execution_count": null, + "id": "dc193ebb", "metadata": {}, "outputs": [], "source": [ @@ -693,6 +726,7 @@ }, { "cell_type": "markdown", + "id": "60a3b4b7", "metadata": {}, "source": [ "Note that in the above example we are only applying a linear transformation\n", @@ -711,9 +745,9 @@ "this, we're going to resample our functional image into MNI space, so we can\n", "overlay it on the MNI template. This can be done using some handy functions\n", "from the\n", - "[`fsl.transform.flirt`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.transform.flirt.html)\n", + "[`fsl.transform.flirt`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.transform.flirt.html#)\n", "and\n", - "[`fsl.utils.image.resample`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.image.resample.html)\n", + "[`fsl.utils.image.resample`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.utils.image.resample.html)\n", "modules.\n", "\n", "\n", @@ -723,6 +757,7 @@ { "cell_type": "code", "execution_count": null, + "id": "3fc656bc", "metadata": {}, "outputs": [], "source": [ @@ -733,17 +768,21 @@ }, { "cell_type": "markdown", + "id": "03919e5c", "metadata": {}, "source": [ "Now we'll load the `example_func2standard` FLIRT matrix, and adjust it so that\n", "it transforms from functional *world* coordinates into standard *world*\n", - "coordinates - this is what is expected by the `resampleToReference` function,\n", - "used below:" + "coordinates, as this is what is expected by the `resampleToReference`\n", + "function, used below. We can use the\n", + "[`fromFlirt`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.transform.flirt.html#fsl.transform.flirt.fromFlirt)\n", + "function to do this for us:" ] }, { "cell_type": "code", "execution_count": null, + "id": "17c25065", "metadata": {}, "outputs": [], "source": [ @@ -755,6 +794,7 @@ }, { "cell_type": "markdown", + "id": "ea3917c9", "metadata": {}, "source": [ "Now we can use `resampleToReference` to resample our functional data into\n", @@ -766,6 +806,7 @@ { "cell_type": "code", "execution_count": null, + "id": "222a079c", "metadata": {}, "outputs": [], "source": [ @@ -777,6 +818,7 @@ }, { "cell_type": "markdown", + "id": "ac2f748d", "metadata": {}, "source": [ "Now that we have our t-statistic image in MNI152 space, we can plot it in\n", @@ -786,6 +828,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a7fe6c8b", "metadata": {}, "outputs": [], "source": [ @@ -801,6 +844,7 @@ }, { "cell_type": "markdown", + "id": "aef19d0e", "metadata": {}, "source": [ "In the example above, we resampled some data from functional space into\n", @@ -810,9 +854,9 @@ "\n", "\n", "The\n", - "[`fsl.transform.fnirt`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.transform.fnirt.html#fsl.transform.fnirt.fromFnirt)\n", + "[`fsl.transform.fnirt`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.transform.fnirt.html)\n", "and\n", - "[`fsl.transform.nonlinear`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.transform.nonlinear.html)\n", + "[`fsl.transform.nonlinear`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.transform.nonlinear.html)\n", "modules contain classes and functions for working with FNIRT-style warp fields\n", "(modules for working with lions, tigers, and bears are still under\n", "development).\n", @@ -829,6 +873,7 @@ { "cell_type": "code", "execution_count": null, + "id": "16c1b772", "metadata": {}, "outputs": [], "source": [ @@ -840,6 +885,7 @@ }, { "cell_type": "markdown", + "id": "c5e907ee", "metadata": {}, "source": [ "Now, let's say we have obtained our seed location in MNI152 coordinates. Let's\n", @@ -849,6 +895,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8b7aa6be", "metadata": {}, "outputs": [], "source": [ @@ -859,6 +906,7 @@ }, { "cell_type": "markdown", + "id": "56296715", "metadata": {}, "source": [ "Now we'll load the FNIRT warp field, which encodes a nonlinear transformation\n", @@ -871,6 +919,7 @@ { "cell_type": "code", "execution_count": null, + "id": "84a63882", "metadata": {}, "outputs": [], "source": [ @@ -883,6 +932,7 @@ }, { "cell_type": "markdown", + "id": "901c7356", "metadata": {}, "source": [ "We'll also load our FLIRT functional to structural transformation, adjust it\n", @@ -894,6 +944,7 @@ { "cell_type": "code", "execution_count": null, + "id": "17b25fa6", "metadata": {}, "outputs": [], "source": [ @@ -905,16 +956,19 @@ }, { "cell_type": "markdown", + "id": "ce56d4e3", "metadata": {}, "source": [ "Now we can transform our seed coordinates from MNI152 space into functional\n", "space in two stages. First, we'll use our deformation field to transform from\n", - "MNI152 space into structural space:" + "MNI152 space into structural space, then we'll use our inverted FLIRT affine\n", + "to transform from structural space into functional space:" ] }, { "cell_type": "code", "execution_count": null, + "id": "448033ac", "metadata": {}, "outputs": [], "source": [ @@ -928,12 +982,13 @@ }, { "cell_type": "markdown", + "id": "c9636bb0", "metadata": {}, "source": [ "> FNIRT warp fields kind of work backwards - we can use them to transform\n", "> reference coordinates into source coordinates, but would need to invert the\n", "> warp field using `invwarp` if we wanted to transform from source coordinates\n", - "> into referemce coordinates.\n", + "> into reference coordinates.\n", "\n", "\n", "Of course, we can also use our deformation field to resample an image from\n", @@ -945,6 +1000,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a38b874a", "metadata": {}, "outputs": [], "source": [ @@ -962,6 +1018,7 @@ }, { "cell_type": "markdown", + "id": "f27e844c", "metadata": {}, "source": [ "The `premat` option to `applyDeformation` can be used to specify our linear\n", @@ -972,6 +1029,7 @@ { "cell_type": "code", "execution_count": null, + "id": "81b3d22b", "metadata": {}, "outputs": [], "source": [ @@ -984,12 +1042,13 @@ }, { "cell_type": "markdown", + "id": "6da604af", "metadata": {}, "source": [ "There are a few other useful functions tucked away in the\n", - "[`fsl.utils.image`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.image.html)\n", + "[`fsl.utils.image`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.utils.image.html)\n", "and\n", - "[`fsl.transform`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.transform.html)\n", + "[`fsl.transform`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.transform.html)\n", "packages, with more to be added in the future.\n", "\n", "\n", @@ -998,7 +1057,7 @@ "\n", "\n", "The\n", - "[fsl.wrappers](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.wrappers.html)\n", + "[`fsl.wrappers`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.wrappers.html)\n", "package is the home of \"wrapper\" functions for a range of FSL tools. You can\n", "use them to call an FSL tool from Python code, without having to worry about\n", "constructing a command-line, or saving/loading input/output images.\n", @@ -1015,6 +1074,7 @@ { "cell_type": "code", "execution_count": null, + "id": "08d8e2a2", "metadata": {}, "outputs": [], "source": [ @@ -1027,6 +1087,7 @@ }, { "cell_type": "markdown", + "id": "00eb0f6c", "metadata": {}, "source": [ "The `fsl.wrappers` functions strive to provide an interface which is as close\n", @@ -1059,6 +1120,7 @@ { "cell_type": "code", "execution_count": null, + "id": "383795d5", "metadata": {}, "outputs": [], "source": [ @@ -1074,6 +1136,7 @@ }, { "cell_type": "markdown", + "id": "e03ababe", "metadata": {}, "source": [ "> Some FSL commands accept arguments which cannot be used as Python\n", @@ -1106,6 +1169,7 @@ { "cell_type": "code", "execution_count": null, + "id": "af58a85d", "metadata": {}, "outputs": [], "source": [ @@ -1121,6 +1185,7 @@ }, { "cell_type": "markdown", + "id": "ea0678c8", "metadata": {}, "source": [ "\n", @@ -1134,6 +1199,7 @@ { "cell_type": "code", "execution_count": null, + "id": "6cf63c78", "metadata": {}, "outputs": [], "source": [ @@ -1152,6 +1218,7 @@ }, { "cell_type": "markdown", + "id": "48111f56", "metadata": {}, "source": [ "You can use the `LOAD` symbol for any output argument - any output files which\n", @@ -1161,6 +1228,7 @@ { "cell_type": "code", "execution_count": null, + "id": "ca37247b", "metadata": {}, "outputs": [], "source": [ @@ -1187,6 +1255,7 @@ }, { "cell_type": "markdown", + "id": "a3534694", "metadata": {}, "source": [ "For tools like `bet` and `fast`, which expect an output *prefix* or\n", @@ -1197,6 +1266,7 @@ { "cell_type": "code", "execution_count": null, + "id": "d07d92d6", "metadata": {}, "outputs": [], "source": [ @@ -1210,6 +1280,7 @@ }, { "cell_type": "markdown", + "id": "f68f04fc", "metadata": {}, "source": [ "\n", @@ -1232,6 +1303,7 @@ { "cell_type": "code", "execution_count": null, + "id": "958a31a2", "metadata": {}, "outputs": [], "source": [ @@ -1245,6 +1317,7 @@ }, { "cell_type": "markdown", + "id": "2f6f5e4a", "metadata": {}, "source": [ "Of course, you can also use the `fslmaths` wrapper with in-memory images:" @@ -1253,6 +1326,7 @@ { "cell_type": "code", "execution_count": null, + "id": "75ada533", "metadata": {}, "outputs": [], "source": [ @@ -1269,6 +1343,7 @@ }, { "cell_type": "markdown", + "id": "9de21eab", "metadata": {}, "source": [ "\n", @@ -1276,15 +1351,15 @@ "\n", "\n", "The\n", - "[`fsl.utils.filetree`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.filetree.html)\n", + "[`file_tree`](https://git.fmrib.ox.ac.uk/ndcn0236/file-tree)\n", "library provides functionality which allows you to work with *structured data\n", - "directories*, such as HCP or BIDS datasets. You can use `filetree` for both\n", + "directories*, such as HCP or BIDS datasets. You can use `file_tree` for both\n", "reading and for creating datasets.\n", "\n", "\n", - "This practical gives a very brief introduction to the `filetree` library -\n", + "This practical gives a very brief introduction to the `file_tree` library -\n", "refer to the [full\n", - "documentation](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.filetree.html)\n", + "documentation](https://open.win.ox.ac.uk/pages/ndcn0236/file-tree/)\n", "to get a feel for how powerful it can be.\n", "\n", "\n", @@ -1292,7 +1367,7 @@ "### Describing your data\n", "\n", "\n", - "To introduce `filetree`, we'll begin with a small example. Imagine that we\n", + "To introduce `file_tree`, we'll begin with a small example. Imagine that we\n", "have a dataset which looks like this:\n", "\n", "\n", @@ -1325,6 +1400,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5381d777", "metadata": {}, "outputs": [], "source": [ @@ -1343,15 +1419,17 @@ }, { "cell_type": "markdown", + "id": "8c317dfc", "metadata": {}, "source": [ - "To use `filetree` with this dataset, we must first describe its structure - we\n", - "do this by creating a `.tree` file:" + "To use `file_tree` with this dataset, we must first describe its structure -\n", + "we do this by creating a `.tree` file:" ] }, { "cell_type": "code", "execution_count": null, + "id": "da3970ca", "metadata": {}, "outputs": [], "source": [ @@ -1364,6 +1442,7 @@ }, { "cell_type": "markdown", + "id": "9018dc9e", "metadata": {}, "source": [ "A `.tree` file is simply a description of the structure of your data\n", @@ -1383,10 +1462,11 @@ { "cell_type": "code", "execution_count": null, + "id": "53118eba", "metadata": {}, "outputs": [], "source": [ - "from fsl.utils.filetree import FileTree\n", + "from file_tree import FileTree\n", "\n", "# Create a FileTree, giving\n", "# it our tree specification,\n", @@ -1396,28 +1476,40 @@ }, { "cell_type": "markdown", + "id": "96cc4535", "metadata": {}, "source": [ - "We can list all of the T1 images via the `FileTree.get_all` method. The\n", - "`glob_vars='all'` option tells the `FileTree` to fill in the `T1w` template\n", - "with all possible combinations of variables. The `FileTree.extract_variables`\n", - "method accepts a file path, and gives you back the variable values contained\n", - "within:" + "We can list all of the T1 images via the `FileTree.get_mult_glob` method. This\n", + "method returns a [multi-dimensional\n", + "array](https://xarray.pydata.org/en/stable/index.html) of file names, where\n", + "each dimension corresponds to one of the variables in your `.tree` file\n", + "(`subject` and `session` in this example):" ] }, { "cell_type": "code", "execution_count": null, + "id": "89325712", "metadata": {}, "outputs": [], "source": [ - "for t1file in tree.get_all('T1w', glob_vars='all'):\n", - " fvars = tree.extract_variables('T1w', t1file)\n", - " print(t1file, fvars)" + "import itertools as it\n", + "\n", + "t1files = tree.get_mult_glob('T1w')\n", + "subjects = t1files.coords['subject'].data\n", + "sessions = t1files.coords['session'].data\n", + "\n", + "print('Subjects:', subjects)\n", + "print('Sessions:', sessions)\n", + "\n", + "for subject, session in it.product(subjects, sessions):\n", + " t1file = t1files.loc[subject, session].item()\n", + " print(subject, session, t1file)" ] }, { "cell_type": "markdown", + "id": "b3080ecd", "metadata": {}, "source": [ "The `FileTree.update` method allows you to \"fill in\" variable values; it\n", @@ -1428,17 +1520,19 @@ { "cell_type": "code", "execution_count": null, + "id": "a45ea015", "metadata": {}, "outputs": [], "source": [ - "treeA = tree.update(subject='A')\n", - "for t1file in treeA.get_all('T1w', glob_vars='all'):\n", - " fvars = treeA.extract_variables('T1w', t1file)\n", - " print(t1file, fvars)" + "treeA = tree.update(subject='A')\n", + "t1files = treeA.get_mult_glob('T1w')\n", + "for t1file in t1files:\n", + " print(t1file.session.item(), t1file.item())" ] }, { "cell_type": "markdown", + "id": "44ca52c4", "metadata": {}, "source": [ "\n", @@ -1452,6 +1546,7 @@ { "cell_type": "code", "execution_count": null, + "id": "a33ed1ed", "metadata": {}, "outputs": [], "source": [ @@ -1466,89 +1561,44 @@ }, { "cell_type": "markdown", + "id": "fff7b7cc", "metadata": {}, "source": [ "Now we can use the `FileTree` to generate the relevant file names for us,\n", - "which we can then pass on to BET. Here we'll use the `FileTree.get_all_trees`\n", - "method to create a sub-tree for each subject and each session:" + "which we can then pass on to BET. Here we'll use the `FileTree.update_glob`\n", + "method, which tells the `FileTree` to scan the directory to identify all\n", + "`T1w` images. Then the `FileTree.iter` method allows us to create a sub-tree\n", + "for all combinations of `subject` and `session`:" ] }, { "cell_type": "code", "execution_count": null, + "id": "79767584", "metadata": {}, "outputs": [], "source": [ "from fsl.wrappers import bet\n", "tree = FileTree.read('mydata.tree', 'mydata')\n", - "for subtree in tree.get_all_trees('T1w', glob_vars='all'):\n", + "\n", + "tree.update_glob(\"T1w\", inplace=True)\n", + "\n", + "for subtree in tree.iter('T1w'):\n", " t1file = subtree.get('T1w')\n", " t1brain = subtree.get('T1w_brain')\n", - " print('Running BET: {} -> {} ...'.format(t1file, t1brain))\n", + " print(f'Running BET: {t1file} -> {t1brain} ...')\n", " bet(t1file, t1brain, mask=True)\n", "print('Done!')\n", "\n", "example = tree.update(subject='A', session='1')\n", "render('{} {} -ot mask -o -w 2 -mc 0 1 0'.format(\n", " example.get('T1w'),\n", - " example.get('T1w_brain_mask')))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### The `FileTreeQuery`\n", - "\n", - "\n", - "The `filetree` module contains another class called the\n", - "[`FileTreeQuery`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.filetree.query.html),\n", - "which provides an interface that is more convenient if you are reading data\n", - "from large datasets with many different file types and variables.\n", - "\n", - "\n", - "When you create a `FileTreeQuery`, it scans the entire data directory and\n", - "identifies all of the values that are present for each variable defined in the\n", - "`.tree` file:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from fsl.utils.filetree import FileTreeQuery\n", - "tree = FileTree.read('mydata.tree', 'mydata')\n", - "query = FileTreeQuery(tree)\n", - "print('T1w variables:', query.variables('T1w'))\n", - "print('T2w variables:', query.variables('T2w'))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `FileTreeQuery.query` method will return the paths to all existing files\n", - "which match a set of variable values:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print('All files for subject A')\n", - "for template in query.templates:\n", - " print(' {} files:'.format(template))\n", - " for match in query.query(template, subject='A'):\n", - " print(' ', match.filename)" + " example.get('T1w_brain_mask')))" ] }, { "cell_type": "markdown", + "id": "6bb919b6", "metadata": {}, "source": [ "\n", @@ -1556,19 +1606,20 @@ "\n", "\n", "The\n", - "[`fsl.utils.run`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.run.html)\n", + "[`fsl.utils.run`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.utils.run.html)\n", "module provides the `run` and `runfsl` functions, which are wrappers around\n", "the built-in [`subprocess`\n", "library](https://docs.python.org/3/library/subprocess.html).\n", "\n", "\n", "The default behaviour of `run` is to return the standard output of the\n", - "command:" + "command as a string, while also printing it normally:" ] }, { "cell_type": "code", "execution_count": null, + "id": "4acfb19a", "metadata": {}, "outputs": [], "source": [ @@ -1583,6 +1634,7 @@ }, { "cell_type": "markdown", + "id": "01f20517", "metadata": {}, "source": [ "But you can control what `run` returns, depending on your needs. Let's create\n", @@ -1592,6 +1644,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5abe68ea", "metadata": {}, "outputs": [], "source": [ @@ -1607,6 +1660,7 @@ }, { "cell_type": "markdown", + "id": "7bac50e6", "metadata": {}, "source": [ "And let's not forget to make it executable:" @@ -1615,6 +1669,7 @@ { "cell_type": "code", "execution_count": null, + "id": "68c2870c", "metadata": {}, "outputs": [], "source": [ @@ -1623,6 +1678,7 @@ }, { "cell_type": "markdown", + "id": "ac9875e2", "metadata": {}, "source": [ "You can use the `stdout`, `stderr` and `exitcode` arguments to control the\n", @@ -1632,6 +1688,7 @@ { "cell_type": "code", "execution_count": null, + "id": "3f9f6b9b", "metadata": {}, "outputs": [], "source": [ @@ -1658,6 +1715,7 @@ }, { "cell_type": "markdown", + "id": "57728f5a", "metadata": {}, "source": [ "So if only one of `stdout`, `stderr`, or `exitcode` is `True`, `run` will only\n", @@ -1672,6 +1730,7 @@ { "cell_type": "code", "execution_count": null, + "id": "e8e0fcd9", "metadata": {}, "outputs": [], "source": [ @@ -1680,6 +1739,7 @@ }, { "cell_type": "markdown", + "id": "44a3a3cd", "metadata": {}, "source": [ "\n", @@ -1694,6 +1754,7 @@ { "cell_type": "code", "execution_count": null, + "id": "48c662a4", "metadata": {}, "outputs": [], "source": [ @@ -1710,6 +1771,7 @@ }, { "cell_type": "markdown", + "id": "69321596", "metadata": {}, "source": [ "\n", @@ -1721,11 +1783,12 @@ "command.\n", "\n", "\n", - "> Cluster submission is handled by the\n", - "> [`fsl.utils.fslsub`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.fslsub.html)\n", - "> module - it contains lower level functions for managing and querying jobs\n", - "> that have been submitted to the cluster. The functions defined in this\n", - "> module can be used directly if you have more complicated requirements.\n", + "> Cluster submission is handled by\n", + "> [`fsl_sub`](https://git.fmrib.ox.ac.uk/fsl/fsl_sub) - in addition to\n", + "> providing the `fsl_sub` command, it contains Python functions for managing\n", + "> and querying jobs that have been submitted to the cluster. The functions\n", + "> defined in the `fsl_sub` library can be used directly if you have more\n", + "> complicated requirements.\n", "\n", "\n", "The semantics of the `run` and `runfsl` functions are slightly different when\n", @@ -1737,6 +1800,7 @@ { "cell_type": "code", "execution_count": null, + "id": "f8ce946d", "metadata": {}, "outputs": [], "source": [ @@ -1746,6 +1810,7 @@ }, { "cell_type": "markdown", + "id": "3838f821", "metadata": {}, "source": [ "Once the job finishes, we should be able to read the usual `.o` and `.e`\n", @@ -1755,6 +1820,7 @@ { "cell_type": "code", "execution_count": null, + "id": "0c1b4c6f", "metadata": {}, "outputs": [], "source": [ @@ -1765,6 +1831,7 @@ }, { "cell_type": "markdown", + "id": "626dff1a", "metadata": {}, "source": [ "All of the `fsl.wrappers` functions also accept the `submit` argument:" @@ -1773,6 +1840,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2ae56b20", "metadata": {}, "outputs": [], "source": [ @@ -1782,6 +1850,7 @@ }, { "cell_type": "markdown", + "id": "a2af7f93", "metadata": {}, "source": [ "> But an error will occur if you try to pass in-memory images, or `LOAD` any\n", @@ -1795,6 +1864,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4e4f16dc", "metadata": {}, "outputs": [], "source": [ @@ -1808,25 +1878,33 @@ }, { "cell_type": "markdown", + "id": "d583e7a6", "metadata": {}, "source": [ "When you use `submit=True`, you can also specify cluster submission options -\n", "you can include any arguments that are accepted by the\n", - "[`fslsub.submit`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.fslsub.html#fsl.utils.fslsub.submit)\n", - "function" + "[`fsl_sub`](https://git.fmrib.ox.ac.uk/fsl/fsl_sub) command - for example, if\n", + "you call:\n", + "\n", + "> `runfsl('command', submit=True, queue='short.q', jobhold=)`\n", + "\n", + "this will be translated into a command-line call:\n", + "\n", + "> `fsl_sub command --queue=short.q --jobhold=`" ] }, { "cell_type": "code", "execution_count": null, + "id": "5b8cfcb4", "metadata": {}, "outputs": [], "source": [ "jobs = []\n", "jobs.append(runfsl('robustfov -i bighead -r bighead_cropped', submit=True, queue='short.q'))\n", - "jobs.append(runfsl('bet bighead_cropped bighead_brain', submit=True, queue='short.q', wait_for=jobs[-1]))\n", - "jobs.append(runfsl('fslroi bighead_brain bighead_slices 0 -1 111 3 0 -1', submit=True, queue='short.q', wait_for=jobs[-1]))\n", - "jobs.append(runfsl('fast -o bighead_fast bighead_slices', submit=True, queue='short.q', wait_for=jobs[-1]))\n", + "jobs.append(runfsl('bet bighead_cropped bighead_brain', submit=True, queue='short.q', jobhold=jobs[-1]))\n", + "jobs.append(runfsl('fslroi bighead_brain bighead_slices 0 -1 111 3 0 -1', submit=True, queue='short.q', jobhold=jobs[-1]))\n", + "jobs.append(runfsl('fast -o bighead_fast bighead_slices', submit=True, queue='short.q', jobhold=jobs[-1]))\n", "print('Waiting for', jobs, '...')\n", "hold(jobs)\n", "\n", @@ -1838,49 +1916,57 @@ }, { "cell_type": "markdown", + "id": "02ef68d6", "metadata": {}, "source": [ + "> *Note:* If you are using a version of FSL older than 6.0.5.1, you may need\n", + "> to change `jobhold` to `wait_for` in the code block above.\n", + "\n", + "\n", "\n", "### Redirecting output\n", "\n", "\n", - "The `log` option, accepted by both `run` and `fslrun`, allows for more\n", + "The `log` option, accepted by both `run` and `runfsl`, allows for more\n", "fine-grained control over what is done with the standard output and error\n", "streams.\n", "\n", "\n", - "You can use `'tee'` to redirect the standard output and error streams of the\n", - "command to the standard output and error streams of the calling command (your\n", - "python script):" + "The default behaviour of `run`/`runfsl` is to redirect the standard output and\n", + "error streams of the command to the standard output and error streams of the\n", + "calling command (your python script). However you can disable this via the\n", + "`tee` option:" ] }, { "cell_type": "code", "execution_count": null, + "id": "d2626c3a", "metadata": {}, "outputs": [], "source": [ - "print('Teeing:')\n", - "_ = run('./mycmd 0', log={'tee' : True})" + "_ = run('./mycmd 0', log={'tee' : False})" ] }, { "cell_type": "markdown", + "id": "08b26a21", "metadata": {}, "source": [ - "Or you can use `'stdout'` and `'stderr'` to redirect the standard output and\n", - "error streams of the command to files:" + "You can also use `'stdout'` and `'stderr'` to redirect the standard output and\n", + "error streams of the command to separate files:" ] }, { "cell_type": "code", "execution_count": null, + "id": "97e3464c", "metadata": {}, "outputs": [], "source": [ "with open('stdout.log', 'wt') as o, \\\n", " open('stderr.log', 'wt') as e:\n", - " run('./mycmd 0', log={'stdout' : o, 'stderr' : e})\n", + " run('./mycmd 0', log={'tee' : False, 'stdout' : o, 'stderr' : e})\n", "print('\\nRedirected stdout:')\n", "!cat stdout.log\n", "print('\\nRedirected stderr:')\n", @@ -1889,6 +1975,7 @@ }, { "cell_type": "markdown", + "id": "059b146a", "metadata": {}, "source": [ "Finally, you can use `'cmd'` to log the command itself to a file (useful for\n", @@ -1898,12 +1985,13 @@ { "cell_type": "code", "execution_count": null, + "id": "d4b264af", "metadata": {}, "outputs": [], "source": [ "with open('commands.log', 'wt') as cmdlog:\n", - " run('./mycmd 0', log={'cmd' : cmdlog})\n", - " run('wc -l fslpy.md', log={'cmd' : cmdlog})\n", + " run('./mycmd 0', log={'tee' : False, 'cmd' : cmdlog})\n", + " run('wc -l fslpy.md', log={'tee' : False, 'cmd' : cmdlog})\n", "\n", "print('\\nCommand log:')\n", "!cat commands.log" @@ -1911,6 +1999,7 @@ }, { "cell_type": "markdown", + "id": "128cbef5", "metadata": {}, "source": [ "\n", @@ -1918,7 +2007,7 @@ "\n", "\n", "The\n", - "[`fsl.data.atlases`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.atlases.html)\n", + "[`fsl.data.atlases`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.atlases.html)\n", "module provides access to all of the atlas images that are stored in the\n", "`$FSLDIR/data/atlases/` directory of a standard FSL installation. It can be\n", "used to load and query probabilistic and label-based atlases.\n", @@ -1930,6 +2019,7 @@ { "cell_type": "code", "execution_count": null, + "id": "b97c19c5", "metadata": {}, "outputs": [], "source": [ @@ -1939,6 +2029,7 @@ }, { "cell_type": "markdown", + "id": "1bfdf4a4", "metadata": {}, "source": [ "\n", @@ -1951,6 +2042,7 @@ { "cell_type": "code", "execution_count": null, + "id": "193049ea", "metadata": {}, "outputs": [], "source": [ @@ -1960,6 +2052,7 @@ }, { "cell_type": "markdown", + "id": "4163e9c1", "metadata": {}, "source": [ "`listAtlases` returns a list of `AtlasDescription` objects, each of which\n", @@ -1971,6 +2064,7 @@ { "cell_type": "code", "execution_count": null, + "id": "83d75569", "metadata": {}, "outputs": [], "source": [ @@ -1983,6 +2077,7 @@ }, { "cell_type": "markdown", + "id": "01311333", "metadata": {}, "source": [ "Each `AtlasDescription` maintains a list of `AtlasLabel` objects, each of\n", @@ -1993,6 +2088,7 @@ { "cell_type": "code", "execution_count": null, + "id": "6914ad40", "metadata": {}, "outputs": [], "source": [ @@ -2002,6 +2098,7 @@ }, { "cell_type": "markdown", + "id": "0630b809", "metadata": {}, "source": [ "Or you can retrieve a specific label using the `find` method:" @@ -2010,6 +2107,7 @@ { "cell_type": "code", "execution_count": null, + "id": "afb6cd3f", "metadata": {}, "outputs": [], "source": [ @@ -2022,6 +2120,7 @@ }, { "cell_type": "markdown", + "id": "4778d6e0", "metadata": {}, "source": [ "\n", @@ -2034,6 +2133,7 @@ { "cell_type": "code", "execution_count": null, + "id": "65e7bb65", "metadata": {}, "outputs": [], "source": [ @@ -2061,6 +2161,7 @@ }, { "cell_type": "markdown", + "id": "548fdc41", "metadata": {}, "source": [ "\n", @@ -2074,6 +2175,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5e6104d3", "metadata": {}, "outputs": [], "source": [ @@ -2089,6 +2191,7 @@ }, { "cell_type": "markdown", + "id": "ff93344d", "metadata": {}, "source": [ "Calling `get` on a `ProbabilisticAtlas` will return a probability image:" @@ -2097,6 +2200,7 @@ { "cell_type": "code", "execution_count": null, + "id": "46250e83", "metadata": {}, "outputs": [], "source": [ @@ -2112,6 +2216,7 @@ }, { "cell_type": "markdown", + "id": "befac68b", "metadata": {}, "source": [ "The `get` method can be used to retrieve an image for a region by:\n", @@ -2128,6 +2233,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2e86834e", "metadata": {}, "outputs": [], "source": [ @@ -2135,7 +2241,7 @@ "# voxel or world coordinates\n", "val = lblatlas.label((25, 52, 43), voxel=True)\n", "lbl = lblatlas.find(value=val)\n", - "print('Region at voxel [25, 52, 43]: {} [{}]'.format(val, lbl.name))\n", + "print(f'Region at voxel [25, 52, 43]: {val} [{lbl.name}]')\n", "\n", "\n", "# or a 3D weighted or binary mask\n", @@ -2147,11 +2253,15 @@ "print('Labels in mask:')\n", "for lbl, prop in zip(lbls, props):\n", " lblname = lblatlas.find(value=lbl).name\n", - " print(' {} [{}]: {:0.2f}%'.format(lbl, lblname, prop))" + " print(f' {lbl} [{lblname}]: {prop:0.2f}%')\n", + "\n", + "fig = ortho(std2mm.data, (45, 54, 45), cmap=plt.cm.gray)\n", + "fig = ortho(mask.data, (45, 54, 45), cmap=plt.cm.winter, fig=fig, alpha=0.5)" ] }, { "cell_type": "markdown", + "id": "523d6346", "metadata": {}, "source": [ "`ProbabilisticAtlas` objects have an analogous method called `values`:" @@ -2160,6 +2270,7 @@ { "cell_type": "code", "execution_count": null, + "id": "daf900b4", "metadata": {}, "outputs": [], "source": [ @@ -2168,18 +2279,18 @@ "for idx, val in enumerate(vals):\n", " if val > 0:\n", " lbl = probatlas.find(index=idx)\n", - " print(' {} [{}]: {:0.2f}%'.format(lbl.value, lbl.name, val))\n", + " print(f' {lbl.value} [{lbl.name}]: {val:0.2f}%')\n", "\n", "print('Average proportions of regions within mask:')\n", "vals = probatlas.values(mask)\n", "for idx, val in enumerate(vals):\n", " if val > 0:\n", " lbl = probatlas.find(index=idx)\n", - " print(' {} [{}]: {:0.2f}%'.format(lbl.value, lbl.name, val))" + " print(f' {lbl.value} [{lbl.name}]: {val:0.2f}%')" ] } ], "metadata": {}, "nbformat": 4, - "nbformat_minor": 4 + "nbformat_minor": 5 } diff --git a/applications/fslpy/fslpy.md b/applications/fslpy/fslpy.md index 3cb5abef7975dc1fe159041e11e3c42900c93e70..01692da4d22a1bcd9455401c63b6d3b10b28a48d 100644 --- a/applications/fslpy/fslpy.md +++ b/applications/fslpy/fslpy.md @@ -1,10 +1,10 @@ # `fslpy` -> *Note*: This practical assumes that you have FSL 6.0.4 or newer installed. +> *Note*: This practical assumes that you have FSL 6.0.5.1 or newer installed. -[`fslpy`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/) is a +[`fslpy`](https://open.win.ox.ac.uk/pages/fsl/fslpy/) is a Python library which is built into FSL, and contains a range of functionality for working with FSL and with neuroimaging data from Python. @@ -28,7 +28,6 @@ perform analyses and image processing in conjunction with FSL. * [Describing your data](#describing-your-data) * [Using the `FileTree`](#using-the-filetree) * [Building a processing pipeline with `FileTree`](#building-a-processing-pipeline-with-filetree) - * [The `FileTreeQuery`](#the-filetreequery) * [Calling shell commands](#calling-shell-commands) * [The `runfsl` function](#the-runfsl-function) * [Submitting to the cluster](#submitting-to-the-cluster) @@ -124,21 +123,13 @@ And another function which uses FSLeyes for more complex plots: ``` def render(cmdline): - import shlex import IPython.display as display prefix = '-of screenshot.png -hl -c 2 ' - try: - from fsleyes.render import main - main(shlex.split(prefix + cmdline)) - - except (ImportError, AttributeError): - # fall-back for macOS - we have to run - # FSLeyes render in a separate process - from fsl.utils.run import runfsl - prefix = 'render ' + prefix - runfsl(prefix + cmdline, env={}) + from fsl.utils.run import runfsl + prefix = 'render ' + prefix + runfsl(prefix + cmdline, env={}) return display.Image('screenshot.png') ``` @@ -149,7 +140,7 @@ def render(cmdline): The -[`fsl.data.image`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.image.html#fsl.data.image.Image) +[`fsl.data.image`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.image.html#fsl.data.image.Image) module provides the `Image` class, which sits on top of `nibabel` and contains some handy functionality if you need to work with coordinate transformations, or do some FSL-specific processing. The `Image` class provides features such @@ -165,7 +156,7 @@ as: > example, when you create an `Image` object, the default behaviour is to load > the image data into memory. This is configurable however; take a look at > [the -> documentation](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.image.html#fsl.data.image.Image) +> documentation](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.image.html#fsl.data.image.Image) > to explore all of the options. @@ -317,45 +308,45 @@ use-case: The -[`fsl.data`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.html#module-fsl.data) +[`fsl.data`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.html#module-fsl.data) package has a number of other classes for working with different types of FSL and neuroimaging data. Most of these are higher-level wrappers around the corresponding `nibabel` types: * The - [`fsl.data.bitmap.Bitmap`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.bitmap.html) + [`fsl.data.bitmap.Bitmap`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.bitmap.html#fsl.data.bitmap.Bitmap) class can be used to load a bitmap image (e.g. `jpg`, `png`, etc) and convert it to a NIfTI image. * The - [`fsl.data.dicom.DicomImage`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.dicom.html) + [`fsl.data.dicom.DicomImage`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.dicom.html#fsl.data.dicom.DicomImage) class uses `dcm2niix` to load NIfTI images contained within a DICOM directory*. * The - [`fsl.data.mghimage.MGHImage`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.mghimage.html) + [`fsl.data.mghimage.MGHImage`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.mghimage.html#fsl.data.mghimage.MGHImage) class can be used to load `.mgh`/`.mgz` images (they are converted into NIfTI images). * The - [`fsl.data.dtifit`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.dtifit.html) + [`fsl.data.dtifit`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.dtifit.html) module contains functions for loading and working with the output of the FSL `dtifit` tool. * The - [`fsl.data.featanalysis`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.featanalysis.html), - [`fsl.data.featimage`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.featimage.html), + [`fsl.data.featanalysis`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.featanalysis.html), + [`fsl.data.featimage`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.featimage.html), and - [`fsl.data.featdesign`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.featdesign.html) + [`fsl.data.featdesign`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.featdesign.html) modules contain classes and functions for loading data from FEAT directories. * Similarly, the - [`fsl.data.melodicanalysis`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.melodicanalysis.html) + [`fsl.data.melodicanalysis`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.melodicanalysis.html) and - [`fsl.data.melodicimage`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.melodicimage.html) + [`fsl.data.melodicimage`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.melodicimage.html) modules contain classes and functions for loading data from MELODIC directories. * The - [`fsl.data.gifti`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.gifti.html), - [`fsl.data.freesurfer`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.freesurfer.html), + [`fsl.data.gifti`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.gifti.html), + [`fsl.data.freesurfer`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.freesurfer.html), and - [`fsl.data.vtk`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.vtk.html) + [`fsl.data.vtk`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.vtk.html) modules contain functionality form loading surface data from GIfTI, freesurfer, and ASCII VTK files respectively. @@ -393,7 +384,7 @@ voxcoords = (np.dot(world2vox[:3, :3], mnicoords.T)).T + world2vox[:3, 3] The code above is a bit fiddly, so instead of figuring it out, you can just use the -[`affine.transform`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.transform.affine.html#fsl.transform.affine.transform) +[`affine.transform`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.transform.affine.html#fsl.transform.affine.transform) function: @@ -502,7 +493,7 @@ print(funcvox2mni) ``` > In the next section we will use the -> [`fsl.transform.flirt.fromFlirt`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.transform.flirt.html#fsl.transform.flirt.fromFlirt) +> [`fsl.transform.flirt.fromFlirt`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.transform.flirt.html#fsl.transform.flirt.fromFlirt) > function, which does all of the above for us. @@ -535,9 +526,9 @@ at some images. Let's display our peak activation location in MNI space. To do this, we're going to resample our functional image into MNI space, so we can overlay it on the MNI template. This can be done using some handy functions from the -[`fsl.transform.flirt`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.transform.flirt.html) +[`fsl.transform.flirt`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.transform.flirt.html#) and -[`fsl.utils.image.resample`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.image.resample.html) +[`fsl.utils.image.resample`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.utils.image.resample.html) modules. @@ -553,8 +544,10 @@ std = Image(op.expandvars(op.join('$FSLDIR', 'data', 'standard', 'MNI152_T1_ Now we'll load the `example_func2standard` FLIRT matrix, and adjust it so that it transforms from functional *world* coordinates into standard *world* -coordinates - this is what is expected by the `resampleToReference` function, -used below: +coordinates, as this is what is expected by the `resampleToReference` +function, used below. We can use the +[`fromFlirt`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.transform.flirt.html#fsl.transform.flirt.fromFlirt) +function to do this for us: ``` @@ -601,9 +594,9 @@ real world is full of lions and tigers and bears and warp fields. The -[`fsl.transform.fnirt`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.transform.fnirt.html#fsl.transform.fnirt.fromFnirt) +[`fsl.transform.fnirt`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.transform.fnirt.html) and -[`fsl.transform.nonlinear`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.transform.nonlinear.html) +[`fsl.transform.nonlinear`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.transform.nonlinear.html) modules contain classes and functions for working with FNIRT-style warp fields (modules for working with lions, tigers, and bears are still under development). @@ -667,7 +660,8 @@ struc2func = invert(func2struc) Now we can transform our seed coordinates from MNI152 space into functional space in two stages. First, we'll use our deformation field to transform from -MNI152 space into structural space: +MNI152 space into structural space, then we'll use our inverted FLIRT affine +to transform from structural space into functional space: ``` @@ -683,7 +677,7 @@ ortho(func.data, seedfunc, cursor=True) > FNIRT warp fields kind of work backwards - we can use them to transform > reference coordinates into source coordinates, but would need to invert the > warp field using `invwarp` if we wanted to transform from source coordinates -> into referemce coordinates. +> into reference coordinates. Of course, we can also use our deformation field to resample an image from @@ -721,9 +715,9 @@ fig = ortho(tstatmni, [45, 54, 45], fig=fig) There are a few other useful functions tucked away in the -[`fsl.utils.image`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.image.html) +[`fsl.utils.image`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.utils.image.html) and -[`fsl.transform`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.transform.html) +[`fsl.transform`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.transform.html) packages, with more to be added in the future. @@ -732,7 +726,7 @@ packages, with more to be added in the future. The -[fsl.wrappers](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.wrappers.html) +[`fsl.wrappers`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.wrappers.html) package is the home of "wrapper" functions for a range of FSL tools. You can use them to call an FSL tool from Python code, without having to worry about constructing a command-line, or saving/loading input/output images. @@ -946,15 +940,15 @@ fig = ortho(erodedbrain.data, (80, 112, 85), cmap=plt.cm.inferno, fig=fig) The -[`fsl.utils.filetree`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.filetree.html) +[`file_tree`](https://git.fmrib.ox.ac.uk/ndcn0236/file-tree) library provides functionality which allows you to work with *structured data -directories*, such as HCP or BIDS datasets. You can use `filetree` for both +directories*, such as HCP or BIDS datasets. You can use `file_tree` for both reading and for creating datasets. -This practical gives a very brief introduction to the `filetree` library - +This practical gives a very brief introduction to the `file_tree` library - refer to the [full -documentation](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.filetree.html) +documentation](https://open.win.ox.ac.uk/pages/ndcn0236/file-tree/) to get a feel for how powerful it can be. @@ -962,7 +956,7 @@ to get a feel for how powerful it can be. ### Describing your data -To introduce `filetree`, we'll begin with a small example. Imagine that we +To introduce `file_tree`, we'll begin with a small example. Imagine that we have a dataset which looks like this: @@ -1007,8 +1001,8 @@ done ``` -To use `filetree` with this dataset, we must first describe its structure - we -do this by creating a `.tree` file: +To use `file_tree` with this dataset, we must first describe its structure - +we do this by creating a `.tree` file: ``` @@ -1035,7 +1029,7 @@ Now that we have a `.tree` file which describe our data, we can create a ``` -from fsl.utils.filetree import FileTree +from file_tree import FileTree # Create a FileTree, giving # it our tree specification, @@ -1043,17 +1037,25 @@ from fsl.utils.filetree import FileTree tree = FileTree.read('mydata.tree', 'mydata') ``` -We can list all of the T1 images via the `FileTree.get_all` method. The -`glob_vars='all'` option tells the `FileTree` to fill in the `T1w` template -with all possible combinations of variables. The `FileTree.extract_variables` -method accepts a file path, and gives you back the variable values contained -within: - +We can list all of the T1 images via the `FileTree.get_mult_glob` method. This +method returns a [multi-dimensional +array](https://xarray.pydata.org/en/stable/index.html) of file names, where +each dimension corresponds to one of the variables in your `.tree` file +(`subject` and `session` in this example): ``` -for t1file in tree.get_all('T1w', glob_vars='all'): - fvars = tree.extract_variables('T1w', t1file) - print(t1file, fvars) +import itertools as it + +t1files = tree.get_mult_glob('T1w') +subjects = t1files.coords['subject'].data +sessions = t1files.coords['session'].data + +print('Subjects:', subjects) +print('Sessions:', sessions) + +for subject, session in it.product(subjects, sessions): + t1file = t1files.loc[subject, session].item() + print(subject, session, t1file) ``` @@ -1063,10 +1065,10 @@ data set: ``` -treeA = tree.update(subject='A') -for t1file in treeA.get_all('T1w', glob_vars='all'): - fvars = treeA.extract_variables('T1w', t1file) - print(t1file, fvars) +treeA = tree.update(subject='A') +t1files = treeA.get_mult_glob('T1w') +for t1file in t1files: + print(t1file.session.item(), t1file.item()) ``` @@ -1090,17 +1092,21 @@ sub_{subject} Now we can use the `FileTree` to generate the relevant file names for us, -which we can then pass on to BET. Here we'll use the `FileTree.get_all_trees` -method to create a sub-tree for each subject and each session: - +which we can then pass on to BET. Here we'll use the `FileTree.update_glob` +method, which tells the `FileTree` to scan the directory to identify all +`T1w` images. Then the `FileTree.iter` method allows us to create a sub-tree +for all combinations of `subject` and `session`: ``` from fsl.wrappers import bet tree = FileTree.read('mydata.tree', 'mydata') -for subtree in tree.get_all_trees('T1w', glob_vars='all'): + +tree.update_glob("T1w", inplace=True) + +for subtree in tree.iter('T1w'): t1file = subtree.get('T1w') t1brain = subtree.get('T1w_brain') - print('Running BET: {} -> {} ...'.format(t1file, t1brain)) + print(f'Running BET: {t1file} -> {t1brain} ...') bet(t1file, t1brain, mask=True) print('Done!') @@ -1108,44 +1114,6 @@ example = tree.update(subject='A', session='1') render('{} {} -ot mask -o -w 2 -mc 0 1 0'.format( example.get('T1w'), example.get('T1w_brain_mask'))) - -``` - - - -### The `FileTreeQuery` - - -The `filetree` module contains another class called the -[`FileTreeQuery`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.filetree.query.html), -which provides an interface that is more convenient if you are reading data -from large datasets with many different file types and variables. - - -When you create a `FileTreeQuery`, it scans the entire data directory and -identifies all of the values that are present for each variable defined in the -`.tree` file: - - -``` -from fsl.utils.filetree import FileTreeQuery -tree = FileTree.read('mydata.tree', 'mydata') -query = FileTreeQuery(tree) -print('T1w variables:', query.variables('T1w')) -print('T2w variables:', query.variables('T2w')) -``` - - -The `FileTreeQuery.query` method will return the paths to all existing files -which match a set of variable values: - - -``` -print('All files for subject A') -for template in query.templates: - print(' {} files:'.format(template)) - for match in query.query(template, subject='A'): - print(' ', match.filename) ``` @@ -1154,14 +1122,14 @@ for template in query.templates: The -[`fsl.utils.run`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.run.html) +[`fsl.utils.run`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.utils.run.html) module provides the `run` and `runfsl` functions, which are wrappers around the built-in [`subprocess` library](https://docs.python.org/3/library/subprocess.html). The default behaviour of `run` is to return the standard output of the -command: +command as a string, while also printing it normally: ``` @@ -1271,11 +1239,12 @@ you to submit jobs to be executed on the cluster via the FSL `fsl_sub` command. -> Cluster submission is handled by the -> [`fsl.utils.fslsub`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.fslsub.html) -> module - it contains lower level functions for managing and querying jobs -> that have been submitted to the cluster. The functions defined in this -> module can be used directly if you have more complicated requirements. +> Cluster submission is handled by +> [`fsl_sub`](https://git.fmrib.ox.ac.uk/fsl/fsl_sub) - in addition to +> providing the `fsl_sub` command, it contains Python functions for managing +> and querying jobs that have been submitted to the cluster. The functions +> defined in the `fsl_sub` library can be used directly if you have more +> complicated requirements. The semantics of the `run` and `runfsl` functions are slightly different when @@ -1330,16 +1299,21 @@ render('bighead bighead_brain -cm hot') When you use `submit=True`, you can also specify cluster submission options - you can include any arguments that are accepted by the -[`fslsub.submit`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.fslsub.html#fsl.utils.fslsub.submit) -function +[`fsl_sub`](https://git.fmrib.ox.ac.uk/fsl/fsl_sub) command - for example, if +you call: + +> `runfsl('command', submit=True, queue='short.q', jobhold=)` +this will be translated into a command-line call: + +> `fsl_sub command --queue=short.q --jobhold=` ``` jobs = [] jobs.append(runfsl('robustfov -i bighead -r bighead_cropped', submit=True, queue='short.q')) -jobs.append(runfsl('bet bighead_cropped bighead_brain', submit=True, queue='short.q', wait_for=jobs[-1])) -jobs.append(runfsl('fslroi bighead_brain bighead_slices 0 -1 111 3 0 -1', submit=True, queue='short.q', wait_for=jobs[-1])) -jobs.append(runfsl('fast -o bighead_fast bighead_slices', submit=True, queue='short.q', wait_for=jobs[-1])) +jobs.append(runfsl('bet bighead_cropped bighead_brain', submit=True, queue='short.q', jobhold=jobs[-1])) +jobs.append(runfsl('fslroi bighead_brain bighead_slices 0 -1 111 3 0 -1', submit=True, queue='short.q', jobhold=jobs[-1])) +jobs.append(runfsl('fast -o bighead_fast bighead_slices', submit=True, queue='short.q', jobhold=jobs[-1])) print('Waiting for', jobs, '...') hold(jobs) @@ -1349,35 +1323,38 @@ render('-vl 80 112 91 -xh -zh -hc ' 'bighead_fast_seg.nii.gz -ot label -o') ``` +> *Note:* If you are using a version of FSL older than 6.0.5.1, you may need +> to change `jobhold` to `wait_for` in the code block above. + ### Redirecting output -The `log` option, accepted by both `run` and `fslrun`, allows for more +The `log` option, accepted by both `run` and `runfsl`, allows for more fine-grained control over what is done with the standard output and error streams. -You can use `'tee'` to redirect the standard output and error streams of the -command to the standard output and error streams of the calling command (your -python script): +The default behaviour of `run`/`runfsl` is to redirect the standard output and +error streams of the command to the standard output and error streams of the +calling command (your python script). However you can disable this via the +`tee` option: ``` -print('Teeing:') -_ = run('./mycmd 0', log={'tee' : True}) +_ = run('./mycmd 0', log={'tee' : False}) ``` -Or you can use `'stdout'` and `'stderr'` to redirect the standard output and -error streams of the command to files: +You can also use `'stdout'` and `'stderr'` to redirect the standard output and +error streams of the command to separate files: ``` with open('stdout.log', 'wt') as o, \ open('stderr.log', 'wt') as e: - run('./mycmd 0', log={'stdout' : o, 'stderr' : e}) + run('./mycmd 0', log={'tee' : False, 'stdout' : o, 'stderr' : e}) print('\nRedirected stdout:') !cat stdout.log print('\nRedirected stderr:') @@ -1391,8 +1368,8 @@ pipeline logging): ``` with open('commands.log', 'wt') as cmdlog: - run('./mycmd 0', log={'cmd' : cmdlog}) - run('wc -l fslpy.md', log={'cmd' : cmdlog}) + run('./mycmd 0', log={'tee' : False, 'cmd' : cmdlog}) + run('wc -l fslpy.md', log={'tee' : False, 'cmd' : cmdlog}) print('\nCommand log:') !cat commands.log @@ -1404,7 +1381,7 @@ print('\nCommand log:') The -[`fsl.data.atlases`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.data.atlases.html) +[`fsl.data.atlases`](https://open.win.ox.ac.uk/pages/fsl/fslpy/fsl.data.atlases.html) module provides access to all of the atlas images that are stored in the `$FSLDIR/data/atlases/` directory of a standard FSL installation. It can be used to load and query probabilistic and label-based atlases. @@ -1552,7 +1529,7 @@ interrogate the atlas at specific locations: # voxel or world coordinates val = lblatlas.label((25, 52, 43), voxel=True) lbl = lblatlas.find(value=val) -print('Region at voxel [25, 52, 43]: {} [{}]'.format(val, lbl.name)) +print(f'Region at voxel [25, 52, 43]: {val} [{lbl.name}]') # or a 3D weighted or binary mask @@ -1564,7 +1541,10 @@ lbls, props = lblatlas.label(mask) print('Labels in mask:') for lbl, prop in zip(lbls, props): lblname = lblatlas.find(value=lbl).name - print(' {} [{}]: {:0.2f}%'.format(lbl, lblname, prop)) + print(f' {lbl} [{lblname}]: {prop:0.2f}%') + +fig = ortho(std2mm.data, (45, 54, 45), cmap=plt.cm.gray) +fig = ortho(mask.data, (45, 54, 45), cmap=plt.cm.winter, fig=fig, alpha=0.5) ``` @@ -1577,12 +1557,12 @@ print('Regions at voxel [25, 52, 43]:') for idx, val in enumerate(vals): if val > 0: lbl = probatlas.find(index=idx) - print(' {} [{}]: {:0.2f}%'.format(lbl.value, lbl.name, val)) + print(f' {lbl.value} [{lbl.name}]: {val:0.2f}%') print('Average proportions of regions within mask:') vals = probatlas.values(mask) for idx, val in enumerate(vals): if val > 0: lbl = probatlas.find(index=idx) - print(' {} [{}]: {:0.2f}%'.format(lbl.value, lbl.name, val)) + print(f' {lbl.value} [{lbl.name}]: {val:0.2f}%') ```