diff --git a/advanced_topics/08_fslpy.ipynb b/advanced_topics/08_fslpy.ipynb index a7c6a282e816bef52cfec7b524d025797408bdcd..637779b92680b92ca997778801e22efb77eefb64 100644 --- a/advanced_topics/08_fslpy.ipynb +++ b/advanced_topics/08_fslpy.ipynb @@ -34,7 +34,8 @@ " * [The `fslmaths` wrapper](#the-fslmaths-wrapper)\n", "* [The `filetree`](#the-filetree)\n", "* [Calling shell commands](#calling-shell-commands)\n", - " * [`runfsl` and `submit`](#runfsl-and-submit)\n", + " * [The `runfsl` function](#the-runfsl-function)\n", + " * [Submitting to the cluster](#submitting-to-the-cluster)\n", " * [Redirecting output](#redirecting-output)\n", "* [FSL atlases](#fsl-atlases)\n", " * [Querying atlases](#querying-atlases)\n", @@ -711,6 +712,10 @@ "constructing a command-line, or saving/loading input/output images.\n", "\n", "\n", + "> The `fsl.wrappers` functions also allow you to submit jobs to be run on the\n", + "> cluster - this is described [below](#submitting-to-the-cluster).\n", + "\n", + "\n", "You can use the FSL wrapper functions with file names, similar to calling the\n", "corresponding tool via the command-line:" ] @@ -732,7 +737,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The `fsl.wrapper` functions strive to provide an interface which is as close\n", + "The `fsl.wrappers` functions strive to provide an interface which is as close\n", "as possible to the command-line tool - most functions use positional arguments\n", "for required options, and keyword arguments for all other options, with\n", "argument names equivalent to command line option names. For example, the usage\n", @@ -793,9 +798,9 @@ "\n", "\n", "It can be quite awkward to combine image processing with FSL tools and image\n", - "processing in Python. The `fsl.wrapper` package tries to make this a little\n", + "processing in Python. The `fsl.wrappers` package tries to make this a little\n", "easier for you - if you are working with image data in Python, you can pass\n", - "`Image` or `nibabel` objects directly into `fsl.wrapper` functions - they will\n", + "`Image` or `nibabel` objects directly into `fsl.wrappers` functions - they will\n", "be automatically saved to temporary files and passed to the underlying FSL\n", "command:" ] @@ -824,8 +829,8 @@ "### Loading outputs into Python\n", "\n", "\n", - "By using the special `fsl.wrappers.LOAD` symbol, you can have any output\n", - "files produced by the tool automatically loaded in too:" + "By using the special `fsl.wrappers.LOAD` symbol, you can also have any output\n", + "files produced by the tool automatically loaded:" ] }, { @@ -877,9 +882,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "For tools like `bet`, which expect an output *prefix* or *basename*, you can\n", - "just set the prefix to `LOAD` - all output files with that prefix will be\n", - "available in the returned dictionary:" + "For tools like `bet` and `fast`, which expect an output *prefix* or\n", + "*basename*, you can just set the prefix to `LOAD` - all output files with that\n", + "prefix will be available in the returned dictionary:" ] }, { @@ -906,7 +911,7 @@ "### The `fslmaths` wrapper\n", "\n", "\n", - "*Most* of the `fsl.wrapper` functions aim to provide an interface which is as\n", + "*Most* of the `fsl.wrappers` functions aim to provide an interface which is as\n", "close as possible to the underlying FSL tool. Ideally, if you read the\n", "command-line help for a tool, you should be able to figure out how to use the\n", "corresponding wrapper function. The wrapper for the `fslmaths` command is a\n", @@ -964,6 +969,8 @@ "## The `filetree`\n", "\n", "\n", + "todo\n", + "\n", "\n", "<a class=\"anchor\" id=\"calling-shell-commands\"></a>\n", "## Calling shell commands\n", @@ -1096,8 +1103,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "<a class=\"anchor\" id=\"runfsl-and-submit\"></a>\n", - "### `runfsl` and `submit`\n", + "<a class=\"anchor\" id=\"the-runfsl-function\"></a>\n", + "### The `runfsl` function\n", "\n", "\n", "The `runfsl` function is a wrapper around `run` which simply makes sure that\n", @@ -1112,8 +1119,125 @@ "outputs": [], "source": [ "from fsl.utils.run import runfsl\n", - "runfsl('fslroi 08_fslpy/bighead_cropped bighead_slices 0 -1 0 -1 90 5')\n", - "runfsl('fast -o bighead_fast bighead_slices')" + "runfsl('bet 08_fslpy/bighead_cropped bighead_cropped_brain')\n", + "runfsl('fslroi bighead_cropped_brain bighead_slices 0 -1 0 -1 90 3')\n", + "runfsl('fast -o bighead_fast bighead_slices')\n", + "\n", + "render('-vl 80 112 91 -xh -yh '\n", + " '08_fslpy/bighead_cropped '\n", + " 'bighead_slices.nii.gz -cm brain_colours_1hot -b 30 '\n", + " 'bighead_fast_seg.nii.gz -ot label -o')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "<a class=\"anchor\" id=\"submitting-to-the-cluster\"></a>\n", + "### Submitting to the cluster\n", + "\n", + "\n", + "Both the `run` and `runfsl` accept an argument called `submit`, which allows\n", + "you to submit jobs to be executed on the cluster via the FSL `fsl_sub`\n", + "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", + "\n", + "\n", + "The semantics of the `run` and `runfsl` functions are slightly different when\n", + "you use the `submit` option - when you submit a job, the `run`/`runfsl` will\n", + "return immediately, and will return a string containing the job ID:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "jobid = run('ls', submit=True)\n", + "print('Job ID:', jobid)\n", + "stdout = f'ls.o{jobid}'\n", + "print('Job output')\n", + "print(open(stdout).read())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All of the `fsl.wrappers` functions also accept the `submit` argument:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "jobid = bet('08_fslpy/bighead', 'bighead_brain', submit=True)\n", + "print('Job ID:', jobid)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> But an error will occur if you try to pass in-memory images, or `LOAD` any\n", + "> outputs when you call a wrapper function with `submit=True`.\n", + "\n", + "\n", + "After submitting a job, you can use the `wait` function to wait until a job\n", + "has completed:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from fsl.utils.run import wait\n", + "jobid = bet('08_fslpy/bighead', 'bighead_brain', submit=True)\n", + "print('Job ID:', jobid)\n", + "wait(jobid)\n", + "print('Done!')\n", + "render('08_fslpy/bighead bighead_brain -cm hot')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When you submit a job, instead of passing `submit=True`, you can pass in a\n", + "dict which contains cluster submission options - you can include any arguments\n", + "to the\n", + "[`fslsub.submit`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.fslsub.html#fsl.utils.fslsub.submit)\n", + "function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "jid = runfsl('robustfov -i 08_fslpy/bighead -r bighead_cropped', submit=dict(queue='short.q'))\n", + "jid = runfsl('bet bighead_cropped bighead_brain', submit=dict(queue='short.q', wait_for=jid))\n", + "jid = runfsl('fslroi bighead_brain bighead_slices 0 -1 111 3 0 -1', submit=dict(queue='short.q', wait_for=jid))\n", + "jid = runfsl('fast -o bighead_fast bighead_slices', submit=dict(queue='short.q', wait_for=jid))\n", + "\n", + "wait(jid)\n", + "\n", + "render('-vl 80 112 91 -xh -zh -hc '\n", + " 'bighead_brain '\n", + " 'bighead_slices.nii.gz -cm brain_colours_1hot -b 30 '\n", + " 'bighead_fast_seg.nii.gz -ot label -o')" ] }, { diff --git a/advanced_topics/08_fslpy.md b/advanced_topics/08_fslpy.md index 19733172eed90c63455b00a330fe63777ad77675..d776ce75eb6ff2aa1c1270123de0b4235671ede5 100644 --- a/advanced_topics/08_fslpy.md +++ b/advanced_topics/08_fslpy.md @@ -28,7 +28,8 @@ perform analyses and image processing in conjunction with FSL. * [The `fslmaths` wrapper](#the-fslmaths-wrapper) * [The `filetree`](#the-filetree) * [Calling shell commands](#calling-shell-commands) - * [`runfsl` and `submit`](#runfsl-and-submit) + * [The `runfsl` function](#the-runfsl-function) + * [Submitting to the cluster](#submitting-to-the-cluster) * [Redirecting output](#redirecting-output) * [FSL atlases](#fsl-atlases) * [Querying atlases](#querying-atlases) @@ -536,6 +537,10 @@ 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. +> The `fsl.wrappers` functions also allow you to submit jobs to be run on the +> cluster - this is described [below](#submitting-to-the-cluster). + + You can use the FSL wrapper functions with file names, similar to calling the corresponding tool via the command-line: @@ -549,7 +554,7 @@ render('08_fslpy/bighead bighead_cropped -cm blue') ``` -The `fsl.wrapper` functions strive to provide an interface which is as close +The `fsl.wrappers` functions strive to provide an interface which is as close as possible to the command-line tool - most functions use positional arguments for required options, and keyword arguments for all other options, with argument names equivalent to command line option names. For example, the usage @@ -602,9 +607,9 @@ render('bighead_cropped -b 40 ' It can be quite awkward to combine image processing with FSL tools and image -processing in Python. The `fsl.wrapper` package tries to make this a little +processing in Python. The `fsl.wrappers` package tries to make this a little easier for you - if you are working with image data in Python, you can pass -`Image` or `nibabel` objects directly into `fsl.wrapper` functions - they will +`Image` or `nibabel` objects directly into `fsl.wrappers` functions - they will be automatically saved to temporary files and passed to the underlying FSL command: @@ -625,8 +630,8 @@ fig = ortho(betted .data, (80, 112, 85), cmap=plt.cm.inferno, fig=fig) ### Loading outputs into Python -By using the special `fsl.wrappers.LOAD` symbol, you can have any output -files produced by the tool automatically loaded in too: +By using the special `fsl.wrappers.LOAD` symbol, you can also have any output +files produced by the tool automatically loaded: ``` @@ -662,9 +667,9 @@ fig = ortho(aligned.data, (45, 54, 45), cmap=plt.cm.inferno, fig=fig) ``` -For tools like `bet`, which expect an output *prefix* or *basename*, you can -just set the prefix to `LOAD` - all output files with that prefix will be -available in the returned dictionary: +For tools like `bet` and `fast`, which expect an output *prefix* or +*basename*, you can just set the prefix to `LOAD` - all output files with that +prefix will be available in the returned dictionary: ``` @@ -683,7 +688,7 @@ fig = ortho(betted['output_mask'].data, (80, 112, 85), cmap=plt.cm.summer, fig= ### The `fslmaths` wrapper -*Most* of the `fsl.wrapper` functions aim to provide an interface which is as +*Most* of the `fsl.wrappers` functions aim to provide an interface which is as close as possible to the underlying FSL tool. Ideally, if you read the command-line help for a tool, you should be able to figure out how to use the corresponding wrapper function. The wrapper for the `fslmaths` command is a @@ -817,8 +822,8 @@ run("./mycmd 99") ``` -<a class="anchor" id="runfsl-and-submit"></a> -### `runfsl` and `submit` +<a class="anchor" id="the-runfsl-function"></a> +### The `runfsl` function The `runfsl` function is a wrapper around `run` which simply makes sure that @@ -828,11 +833,95 @@ same usage as the `run` function: ``` from fsl.utils.run import runfsl -runfsl('fslroi 08_fslpy/bighead_cropped bighead_slices 0 -1 0 -1 90 5') +runfsl('bet 08_fslpy/bighead_cropped bighead_cropped_brain') +runfsl('fslroi bighead_cropped_brain bighead_slices 0 -1 0 -1 90 3') runfsl('fast -o bighead_fast bighead_slices') + +render('-vl 80 112 91 -xh -yh ' + '08_fslpy/bighead_cropped ' + 'bighead_slices.nii.gz -cm brain_colours_1hot -b 30 ' + 'bighead_fast_seg.nii.gz -ot label -o') ``` +<a class="anchor" id="submitting-to-the-cluster"></a> +### Submitting to the cluster + + +Both the `run` and `runfsl` accept an argument called `submit`, which allows +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. + + +The semantics of the `run` and `runfsl` functions are slightly different when +you use the `submit` option - when you submit a job, the `run`/`runfsl` will +return immediately, and will return a string containing the job ID: + + +``` +jobid = run('ls', submit=True) +print('Job ID:', jobid) +stdout = f'ls.o{jobid}' +print('Job output') +print(open(stdout).read()) +``` + + +All of the `fsl.wrappers` functions also accept the `submit` argument: + + +``` +jobid = bet('08_fslpy/bighead', 'bighead_brain', submit=True) +print('Job ID:', jobid) +``` + + +> But an error will occur if you try to pass in-memory images, or `LOAD` any +> outputs when you call a wrapper function with `submit=True`. + + +After submitting a job, you can use the `wait` function to wait until a job +has completed: + + +``` +from fsl.utils.run import wait +jobid = bet('08_fslpy/bighead', 'bighead_brain', submit=True) +print('Job ID:', jobid) +wait(jobid) +print('Done!') +render('08_fslpy/bighead bighead_brain -cm hot') +``` + + +When you submit a job, instead of passing `submit=True`, you can pass in a +dict which contains cluster submission options - you can include any arguments +to the +[`fslsub.submit`](https://users.fmrib.ox.ac.uk/~paulmc/fsleyes/fslpy/latest/fsl.utils.fslsub.html#fsl.utils.fslsub.submit) +function: + + +``` +jid = runfsl('robustfov -i 08_fslpy/bighead -r bighead_cropped', submit=dict(queue='short.q')) +jid = runfsl('bet bighead_cropped bighead_brain', submit=dict(queue='short.q', wait_for=jid)) +jid = runfsl('fslroi bighead_brain bighead_slices 0 -1 111 3 0 -1', submit=dict(queue='short.q', wait_for=jid)) +jid = runfsl('fast -o bighead_fast bighead_slices', submit=dict(queue='short.q', wait_for=jid)) + +wait(jid) + +render('-vl 80 112 91 -xh -zh -hc ' + 'bighead_brain ' + 'bighead_slices.nii.gz -cm brain_colours_1hot -b 30 ' + 'bighead_fast_seg.nii.gz -ot label -o') +``` + <a class="anchor" id="redirecting-output"></a> ### Redirecting output