From f4fb40da1ad4d4308aa1ef4a82dae765d777808f Mon Sep 17 00:00:00 2001 From: Oscar Esteban Date: Fri, 26 Apr 2024 12:31:23 +0200 Subject: [PATCH] fix: various fixes --- docs/notebook/02-data.ipynb | 5 +- docs/notebook/03-models.ipynb | 71 ++++++++++++++++++++++++----- docs/notebook/04-registration.ipynb | 46 +++++++++++++++++-- docs/tutorial/data.md | 7 +-- docs/tutorial/intro.md | 2 + docs/tutorial/models.md | 18 ++++---- docs/tutorial/registration.md | 7 +-- 7 files changed, 123 insertions(+), 33 deletions(-) diff --git a/docs/notebook/02-data.ipynb b/docs/notebook/02-data.ipynb index aac8189..5655354 100644 --- a/docs/notebook/02-data.ipynb +++ b/docs/notebook/02-data.ipynb @@ -451,10 +451,11 @@ "metadata": {}, "outputs": [], "source": [ + "from eddymotion.data.splitting import lovo_split as logo_split\n", "from eddymotion.viz import plot_dwi\n", "\n", - "data_train, data_test = dmri_dataset.logo_split(10)\n", - "plot_dwi(data_test[0], dmri_dataset.affine, gradient=data_test[1]);" + "data_train, data_test = logo_split(dmri_dataset, 10)\n", + "plot_dwi(np.squeeze(data_test[0]), dmri_dataset.affine, gradient=data_test[1]);" ] }, { diff --git a/docs/notebook/03-models.ipynb b/docs/notebook/03-models.ipynb index afe79c3..4e6850a 100644 --- a/docs/notebook/03-models.ipynb +++ b/docs/notebook/03-models.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "73bc033f", "metadata": {}, "source": [ "# Diffusion modeling" @@ -10,6 +11,7 @@ { "cell_type": "code", "execution_count": null, + "id": "86034e8b", "metadata": { "tags": [ "hide-cell" @@ -18,12 +20,14 @@ "outputs": [], "source": [ "import warnings\n", + "import numpy as np\n", "\n", "warnings.filterwarnings(\"ignore\")" ] }, { "cell_type": "markdown", + "id": "16e127b6", "metadata": {}, "source": [ "The proposed method requires inferring a motion-less, reference DW map for a given diffusion orientation for which we want to estimate the misalignment.\n", @@ -46,16 +50,19 @@ { "cell_type": "code", "execution_count": null, + "id": "3225815e", "metadata": {}, "outputs": [], "source": [ - "from eddymotion.dmri import DWI\n", + "from eddymotion.data.dmri import DWI\n", + "from eddymotion.data.splitting import lovo_split as logo_split\n", "from eddymotion.viz import plot_dwi\n", "dmri_dataset = DWI.from_filename(\"../../data/dwi.h5\")" ] }, { "cell_type": "markdown", + "id": "1a677d9e", "metadata": {}, "source": [ "## Implementing a trivial model\n", @@ -73,6 +80,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2875c19b", "metadata": {}, "outputs": [], "source": [ @@ -106,6 +114,7 @@ }, { "cell_type": "markdown", + "id": "4ddb8436", "metadata": {}, "source": [ "The model can easily be initialized as follows (assuming we still have our dataset loaded):" @@ -114,6 +123,7 @@ { "cell_type": "code", "execution_count": null, + "id": "19da1464", "metadata": {}, "outputs": [], "source": [ @@ -125,6 +135,7 @@ }, { "cell_type": "markdown", + "id": "52034adb", "metadata": {}, "source": [ "Then, at each iteration of our estimation strategy, we will fit this model to the data, after holding one particular direction (`data_test`) out, using the `logo_split` method of the dataset. In every iteration, this finds the b=0 volumes in the data and averages their values in every voxel:" @@ -133,15 +144,17 @@ { "cell_type": "code", "execution_count": null, + "id": "6a65d321", "metadata": {}, "outputs": [], "source": [ - "data_train, data_test = dmri_dataset.logo_split(10)\n", + "data_train, data_test = logo_split(dmri_dataset, 10)\n", "model.fit(data_train[0])" ] }, { "cell_type": "markdown", + "id": "dc3f7299", "metadata": {}, "source": [ "Finally, we can generate our registration reference with the `predict()` method:" @@ -150,6 +163,7 @@ { "cell_type": "code", "execution_count": null, + "id": "5229302c", "metadata": {}, "outputs": [], "source": [ @@ -159,6 +173,7 @@ }, { "cell_type": "markdown", + "id": "dce0260f", "metadata": {}, "source": [ "As expected, the *b=0* doesn't look very much like the particular left-out direction, but it is a start!" @@ -167,14 +182,16 @@ { "cell_type": "code", "execution_count": null, + "id": "9b6dd61a", "metadata": {}, "outputs": [], "source": [ - "plot_dwi(data_test[0], dmri_dataset.affine, gradient=data_test[1]);" + "plot_dwi(np.squeeze(data_test[0]), dmri_dataset.affine, gradient=data_test[1]);" ] }, { "cell_type": "markdown", + "id": "231a0729", "metadata": {}, "source": [ "## Implementing a *regression to the mean* model\n", @@ -187,6 +204,11 @@ ] }, { + "cell_type": "code", + "execution_count": null, + "id": "95130912", + "metadata": {}, + "outputs": [], "source": [ "class AverageDWModel:\n", " \"\"\"A trivial model that returns an average map.\"\"\"\n", @@ -204,14 +226,11 @@ " def predict(self, gradient, **kwargs):\n", " \"\"\"Return the average map.\"\"\"\n", " return self._data" - ], - "cell_type": "code", - "metadata": {}, - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", + "id": "893ae6de", "metadata": {}, "source": [ "**Exercise**\n", @@ -224,6 +243,7 @@ { "cell_type": "code", "execution_count": null, + "id": "77c74c0e", "metadata": { "tags": [ "hide-cell" @@ -234,6 +254,7 @@ }, { "cell_type": "markdown", + "id": "63a5bc6b", "metadata": {}, "source": [ "## Investigating the tensor model\n", @@ -245,6 +266,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8dda846f", "metadata": { "tags": [ "remove-cell" @@ -267,11 +289,12 @@ "\n", "dmri_dataset = DWI.from_filename(datapath)\n", "datapath.unlink()\n", - "data_train, data_test = dmri_dataset.logo_split(88, with_b0=True)" + "data_train, data_test = logo_split(dmri_dataset, 88, with_b0=True)" ] }, { "cell_type": "markdown", + "id": "f9b5a9b0", "metadata": {}, "source": [ "### The model factory\n", @@ -283,6 +306,7 @@ { "cell_type": "code", "execution_count": null, + "id": "3a957d5e", "metadata": {}, "outputs": [], "source": [ @@ -299,6 +323,7 @@ }, { "cell_type": "markdown", + "id": "3157aa22", "metadata": {}, "source": [ "### Leveraging the `fit()` / `predict()` API\n", @@ -309,6 +334,7 @@ { "cell_type": "code", "execution_count": null, + "id": "615b8a23", "metadata": {}, "outputs": [], "source": [ @@ -318,6 +344,7 @@ }, { "cell_type": "markdown", + "id": "1428316d", "metadata": {}, "source": [ "Now, the predicted map for the particular ***b*** gradient looks much closer to the original:" @@ -326,6 +353,7 @@ { "cell_type": "code", "execution_count": null, + "id": "2d1c921a", "metadata": {}, "outputs": [], "source": [ @@ -334,6 +362,7 @@ }, { "cell_type": "markdown", + "id": "17ac6e64", "metadata": {}, "source": [ "Here's the original DW map, for reference:" @@ -342,14 +371,16 @@ { "cell_type": "code", "execution_count": null, + "id": "41078b66", "metadata": {}, "outputs": [], "source": [ - "plot_dwi(data_test[0], dmri_dataset.affine, gradient=data_test[1]);" + "plot_dwi(np.squeeze(data_test[0]), dmri_dataset.affine, gradient=data_test[1]);" ] }, { "cell_type": "markdown", + "id": "d1603ab8", "metadata": {}, "source": [ "**Exercise**\n", @@ -361,6 +392,7 @@ { "cell_type": "code", "execution_count": null, + "id": "25b9ff4d", "metadata": { "tags": [ "hide-cell" @@ -371,6 +403,7 @@ }, { "cell_type": "markdown", + "id": "40c6b7ec", "metadata": {}, "source": [ "**Exercise**\n", @@ -383,12 +416,14 @@ { "cell_type": "code", "execution_count": null, + "id": "267008fe", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", + "id": "9b947d74", "metadata": {}, "source": [ "## Next steps: image registration\n", @@ -399,11 +434,23 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" } }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/docs/notebook/04-registration.ipynb b/docs/notebook/04-registration.ipynb index 047183c..9325b43 100644 --- a/docs/notebook/04-registration.ipynb +++ b/docs/notebook/04-registration.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "d5cde43a", "metadata": {}, "source": [ "# Image registration (spatial alignment)" @@ -10,6 +11,7 @@ { "cell_type": "code", "execution_count": null, + "id": "795729f6", "metadata": { "tags": [ "remove-cell" @@ -24,7 +26,8 @@ "import numpy as np\n", "import nibabel as nb\n", "\n", - "from eddymotion.dmri import DWI\n", + "from eddymotion.data.dmri import DWI\n", + "from eddymotion.data.splitting import lovo_split as logo_split\n", "from eddymotion.viz import plot_dwi\n", "from eddymotion.estimator import _advanced_clip\n", "\n", @@ -56,6 +59,7 @@ }, { "cell_type": "markdown", + "id": "e94a2727", "metadata": {}, "source": [ "At this point of the tutorial we have covered two of the three initial requirements:\n", @@ -147,12 +151,13 @@ { "cell_type": "code", "execution_count": null, + "id": "1af4766c", "metadata": {}, "outputs": [], "source": [ "from eddymotion.model import ModelFactory\n", "\n", - "data_train, data_test = dmri_dataset.logo_split(7, with_b0=True)\n", + "data_train, data_test = logo_split(dmri_dataset, 7, with_b0=True)\n", "\n", "model = ModelFactory.init(\n", " gtab=data_train[1],\n", @@ -165,6 +170,7 @@ }, { "cell_type": "markdown", + "id": "098e67d5", "metadata": {}, "source": [ "Since we are using the command-line interface of ANTs, the software must be installed in the computer and the input data is provided via files in the filesystem.\n", @@ -174,6 +180,7 @@ { "cell_type": "code", "execution_count": null, + "id": "18c4df34", "metadata": {}, "outputs": [], "source": [ @@ -193,6 +200,7 @@ }, { "cell_type": "markdown", + "id": "0e1f3e8e", "metadata": {}, "source": [ "We can now visualize our reference (the prediction) and the actual DW map.\n", @@ -202,10 +210,11 @@ { "cell_type": "code", "execution_count": null, + "id": "a8768046", "metadata": {}, "outputs": [], "source": [ - "from niworkflows.viz.notebook import display\n", + "from nireports.reportlets.notebook import display\n", "\n", "display(\n", " fixed_path,\n", @@ -217,6 +226,7 @@ }, { "cell_type": "markdown", + "id": "0adb200c", "metadata": {}, "source": [ "Let's configure ANTs via NiPype:" @@ -225,6 +235,7 @@ { "cell_type": "code", "execution_count": null, + "id": "17d86a04", "metadata": {}, "outputs": [], "source": [ @@ -238,6 +249,7 @@ { "cell_type": "code", "execution_count": null, + "id": "34c6537e", "metadata": {}, "outputs": [], "source": [ @@ -260,6 +272,7 @@ }, { "cell_type": "markdown", + "id": "a7f05826", "metadata": {}, "source": [ "which will run the following command-line:" @@ -268,6 +281,7 @@ { "cell_type": "code", "execution_count": null, + "id": "1f326a92", "metadata": {}, "outputs": [], "source": [ @@ -276,6 +290,7 @@ }, { "cell_type": "markdown", + "id": "4c9247b3", "metadata": {}, "source": [ "Nipype interfaces can be submitted for execution with the `run()` method:" @@ -284,6 +299,7 @@ { "cell_type": "code", "execution_count": null, + "id": "1758610e", "metadata": {}, "outputs": [], "source": [ @@ -292,6 +308,7 @@ }, { "cell_type": "markdown", + "id": "3b297ba2", "metadata": {}, "source": [ "If everything worked out, we can now retrieve the aligned file with the output `result.outputs.warped_image`.\n", @@ -301,6 +318,7 @@ { "cell_type": "code", "execution_count": null, + "id": "666b4795", "metadata": {}, "outputs": [], "source": [ @@ -314,6 +332,7 @@ }, { "cell_type": "markdown", + "id": "45c27711", "metadata": {}, "source": [ "## Resampling an image\n", @@ -333,6 +352,7 @@ { "cell_type": "code", "execution_count": null, + "id": "8bad9b5a", "metadata": {}, "outputs": [], "source": [ @@ -345,6 +365,7 @@ }, { "cell_type": "markdown", + "id": "b57b6a98", "metadata": {}, "source": [ "Resampling an image requires two pieces of information: the *reference* image (which provides the new grid where we want to have the data) and the *moving* image which contains the actual data we are interested in:" @@ -353,6 +374,7 @@ { "cell_type": "code", "execution_count": null, + "id": "19796849", "metadata": {}, "outputs": [], "source": [ @@ -370,6 +392,7 @@ }, { "cell_type": "markdown", + "id": "b1a1d770", "metadata": {}, "source": [ "**Exercise**\n", @@ -382,6 +405,7 @@ { "cell_type": "code", "execution_count": null, + "id": "9827cd66", "metadata": { "tags": [ "hide-cell" @@ -393,11 +417,23 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" } }, "nbformat": 4, "nbformat_minor": 5 -} \ No newline at end of file +} diff --git a/docs/tutorial/data.md b/docs/tutorial/data.md index 16d0c02..ce4d005 100644 --- a/docs/tutorial/data.md +++ b/docs/tutorial/data.md @@ -120,7 +120,7 @@ Let's get some insights from it: ```{code-cell} python # import the class from the library -from eddymotion.dmri import DWI +from eddymotion.data.dmri import DWI # load the sample file dmri_dataset = DWI.from_filename("../../data/dwi.h5") @@ -301,10 +301,11 @@ def logo_split(self, index, with_b0=False): This function is contained in the `DWI` class shown earlier and will allow us to easily partition the dataset as follows: ```{code-cell} python +from eddymotion.data.splitting import lovo_split as logo_split from eddymotion.viz import plot_dwi -data_train, data_test = dmri_dataset.logo_split(10) -plot_dwi(data_test[0], dmri_dataset.affine, gradient=data_test[1]); +data_train, data_test = logo_split(dmri_dataset, 10) +plot_dwi(np.squeeze(data_test[0]), dmri_dataset.affine, gradient=data_test[1]); ``` `data_train` is a tuple containing all diffusion-weighted volumes and the corresponding gradient table, excluding the left-out, which is stored in `data_test` (the 11th gradient indexed by `10`, in this example). diff --git a/docs/tutorial/intro.md b/docs/tutorial/intro.md index 29f65c4..190c1cb 100644 --- a/docs/tutorial/intro.md +++ b/docs/tutorial/intro.md @@ -94,6 +94,8 @@ Briefly anticipate further requirements that are important, but do not alter the - Parallelism: simulation and registration are CPU-intensive processes - for the runtime to be in a manageable scale, we'll need to leverage parallelism. ``` +![eddymotion](https://raw.githubusercontent.com/nipreps/eddymotion/507fc9bab86696d5330fd6a86c3870968243aea8/docs/_static/eddymotion-flowchart.svg) + ### Step 2: Sketch out an API (Application Programming Interface) Plan how the new software will expose the implementation downstream. Assuming our DWI data is encapsulated in an object (holding not just the data array, but also metadata such as the gradient table) diff --git a/docs/tutorial/models.md b/docs/tutorial/models.md index 20393bf..81debf1 100644 --- a/docs/tutorial/models.md +++ b/docs/tutorial/models.md @@ -16,6 +16,7 @@ kernelspec: :tags: [remove-cell] import warnings +import numpy as np warnings.filterwarnings("ignore") ``` @@ -37,7 +38,8 @@ We must reload the dataset again to use it in this notebook. ``` ```{code-cell} python -from eddymotion.dmri import DWI +from eddymotion.data.dmri import DWI +from eddymotion.data.splitting import lovo_split as logo_split from eddymotion.viz import plot_dwi dmri_dataset = DWI.from_filename("../../data/dwi.h5") ``` @@ -94,8 +96,8 @@ model = TrivialB0Model( Then, at each iteration of our estimation strategy, we will fit this model to the data, after holding one particular direction (`data_test`) out, using the `logo_split` method of the dataset. In every iteration, this finds the b=0 volumes in the data and averages their values in every voxel: ```{code-cell} python -data_train, data_test = dmri_dataset.logo_split(10) -model.fit(data_train[0]) +data_train, data_test = logo_split(dmri_dataset, 10) +model.fit(np.squeeze(data_train[0])) ``` Finally, we can generate our registration reference with the `predict()` method: @@ -108,7 +110,7 @@ plot_dwi(predicted, dmri_dataset.affine, gradient=data_test[1]); As expected, the *b=0* doesn't look very much like the particular left-out direction, but it is a start! ```{code-cell} python -plot_dwi(data_test[0], dmri_dataset.affine, gradient=data_test[1]); +plot_dwi(np.squeeze(data_test[0]), dmri_dataset.affine, gradient=data_test[1]); ``` ## Implementing a *regression to the mean* model @@ -174,7 +176,7 @@ model = AverageDWModel( model.fit(data_train[0]) predicted = model.predict(data_test[1]) plot_dwi(predicted, dmri_dataset.affine, gradient=data_test[1]); -plot_dwi(data_test[0], dmri_dataset.affine, gradient=data_test[1]); +plot_dwi(np.squeeze(data_test[0]), dmri_dataset.affine, gradient=data_test[1]); ``` ## Investigating the tensor model @@ -200,7 +202,7 @@ if datapath.stat().st_size == 0: dmri_dataset = DWI.from_filename(datapath) datapath.unlink() -data_train, data_test = dmri_dataset.logo_split(88, with_b0=True) +data_train, data_test = logo_split(dmri_dataset, 88, with_b0=True) ``` ### The model factory @@ -238,7 +240,7 @@ plot_dwi(predicted, dmri_dataset.affine, gradient=data_test[1], black_bg=True); Here's the original DW map, for reference: ```{code-cell} python -plot_dwi(data_test[0], dmri_dataset.affine, gradient=data_test[1]); +plot_dwi(np.squeeze(data_test[0]), dmri_dataset.affine, gradient=data_test[1]); ``` ```{admonition} Exercise @@ -263,7 +265,7 @@ Once the model has been initialized, we can easily generate a new prediction. model.fit(data_train[0]) predicted = model.predict(data_test[1]) plot_dwi(predicted, dmri_dataset.affine, gradient=data_test[1], black_bg=True); -plot_dwi(data_test[0], dmri_dataset.affine, gradient=data_test[1]); +plot_dwi(np.squeeze(data_test[0]), dmri_dataset.affine, gradient=data_test[1]); ``` ## Next steps: image registration diff --git a/docs/tutorial/registration.md b/docs/tutorial/registration.md index aa5d183..b39ee9e 100644 --- a/docs/tutorial/registration.md +++ b/docs/tutorial/registration.md @@ -23,7 +23,8 @@ from pathlib import Path import numpy as np import nibabel as nb -from eddymotion.dmri import DWI +from eddymotion.data.dmri import DWI +from eddymotion.data.splitting import lovo_split as logo_split from eddymotion.viz import plot_dwi from eddymotion.estimator import _advanced_clip @@ -143,7 +144,7 @@ For this example, we have selected the 8th DW map (`index=7`) because ```{code-cell} python from eddymotion.model import ModelFactory -data_train, data_test = dmri_dataset.logo_split(7, with_b0=True) +data_train, data_test = logo_split(dmri_dataset, 7, with_b0=True) model = ModelFactory.init( gtab=data_train[1], @@ -178,7 +179,7 @@ Please notice the subtle *nodding* of the head, perhaps more apparent when looki ```{code-cell} python :tags: [remove-output] -from niworkflows.viz.notebook import display +from nireports.reportlets.notebook import display display( fixed_path,