From 1880d2f37db3f555a9b32d5606d142e85711f557 Mon Sep 17 00:00:00 2001 From: Ziv Yaniv Date: Sun, 1 Feb 2026 19:51:56 -0500 Subject: [PATCH 1/3] Move Jupyter from classic v6 to modern v7. Move from v6, classic, to v7, modern Jupyter notebooks. This includes updating the interactive matplotlib backend from 'notebook' to 'ipympl'. --- 00_setup.ipynb | 19 ++++++---- 01_spatial_transformations.ipynb | 9 +++-- 02_images_and_resampling.ipynb | 14 +++++-- 03_trust_but_verify.ipynb | 18 ++++----- 04_data_augmentation.ipynb | 11 ++++-- 05_basic_registration.ipynb | 11 ++++-- 06_advanced_registration.ipynb | 10 +++-- 07_registration_application.ipynb | 14 +++---- 08_segmentation_and_shape_analysis.ipynb | 10 ++--- 09_segmentation_evaluation.ipynb | 4 +- 10_results_visualization.ipynb | 33 ++--------------- environment.yml | 21 ++++++----- environment_dev.yml | 47 ++++++++++++------------ gui.py | 5 +++ tests/test_notebooks.py | 1 - utilities.py | 1 + 16 files changed, 111 insertions(+), 117 deletions(-) diff --git a/00_setup.ipynb b/00_setup.ipynb index 4ddba18..543fce1 100644 --- a/00_setup.ipynb +++ b/00_setup.ipynb @@ -13,16 +13,19 @@ "\n", "For additional details see the [Jupyter project documentation](https://jupyter.org/documentation) on Jupyter Notebook or JupyterLab.\n", "\n", - "## Convenience\n", + "## Convenience (notebook v7.x)\n", "By default the contents of the Jupyter notebooks do not occupy the full browser window width. To take advantage of the full window width you can either configure each notebook independently by adding the following into a code cell:\n", + "\n", "```\n", - "from IPython.core.display import display, HTML\n", - "display(HTML(\"\"))\n", + "from IPython.display import display, HTML\n", + "display(HTML(\"\"))\n", "```\n", "Or apply this configuration to all notebooks by adding the following to\n", "the custom.css jupyter configuration file:\n", "```\n", - ".container { width:100% !important; }\n", + ":root {\n", + " --jp-notebook-max-width: 100% !important;\n", + "}\n", "```\n", "On OSX/Linux this file is found in `~/.jupyter/custom/custom.css` on windows it is\n", "found in `C:\\Users\\[your_user_name]\\.jupyter\\custom\\custom.css`.\n", @@ -120,7 +123,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "jp-MarkdownHeadingCollapsed": true + }, "source": [ "

Next »

" ] @@ -142,9 +147,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.13.5" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/01_spatial_transformations.ipynb b/01_spatial_transformations.ipynb index 5492c1f..6da4555 100644 --- a/01_spatial_transformations.ipynb +++ b/01_spatial_transformations.ipynb @@ -296,6 +296,7 @@ " plt.xlim(xlim)\n", " plt.ylim(ylim)\n", " plt.legend(loc=(0.25, 1.01))\n", + " plt.show()\n", "\n", "\n", "# 2D square centered on (0,0)\n", @@ -863,7 +864,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "jp-MarkdownHeadingCollapsed": true + }, "source": [ "

Next »

" ] @@ -885,9 +888,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.13.5" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/02_images_and_resampling.ipynb b/02_images_and_resampling.ipynb index fbc8a37..9801571 100644 --- a/02_images_and_resampling.ipynb +++ b/02_images_and_resampling.ipynb @@ -135,7 +135,10 @@ { "cell_type": "markdown", "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "source": [ "Or, creation from file." @@ -552,7 +555,10 @@ { "cell_type": "markdown", "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "source": [ "Write the volume as a series of JPEGs. The WriteImage function receives a volume and a list of images names and writes the volume according to the z axis. For a displayable result we need to rescale the image intensities (default is [0,255]) since the JPEG format requires a cast to the UInt8 pixel type." @@ -820,9 +826,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.13.5" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/03_trust_but_verify.ipynb b/03_trust_but_verify.ipynb index f18d749..5e08d26 100644 --- a/03_trust_but_verify.ipynb +++ b/03_trust_but_verify.ipynb @@ -70,11 +70,10 @@ "import tempfile\n", "import pickle\n", "\n", - "%matplotlib notebook\n", + "%matplotlib ipympl\n", "import matplotlib.pyplot as plt\n", "import ipywidgets as widgets\n", "\n", - "\n", "# utility method that either downloads data from the Girder repository or\n", "# if already downloaded returns the file name for reading from disk (cached data)\n", "from downloaddata import fetch_data as fdata\n", @@ -498,6 +497,7 @@ " )\n", " self.fig.tight_layout()\n", " self.update_display()\n", + " plt.show()\n", "\n", " def create_ui(self):\n", " # Create the active GUI components. Height and width are specified in 'em' units. This is\n", @@ -677,9 +677,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "with open(faux_image_volume_file_name, \"rb\") as fp:\n", @@ -734,9 +732,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "with open(faux_series_volume_file_name, \"rb\") as fp:\n", @@ -764,7 +760,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "jp-MarkdownHeadingCollapsed": true + }, "source": [ "

Next »

" ] @@ -786,7 +784,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.13.5" } }, "nbformat": 4, diff --git a/04_data_augmentation.ipynb b/04_data_augmentation.ipynb index 2dcbf61..497b868 100644 --- a/04_data_augmentation.ipynb +++ b/04_data_augmentation.ipynb @@ -29,7 +29,7 @@ "import numpy as np\n", "import os\n", "\n", - "%matplotlib notebook\n", + "%matplotlib ipympl\n", "import gui\n", "\n", "from downloaddata import fetch_data as fdata\n", @@ -386,7 +386,10 @@ { "cell_type": "markdown", "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "source": [ "## What about flipping\n", @@ -884,9 +887,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.13.5" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/05_basic_registration.ipynb b/05_basic_registration.ipynb index 2e659cf..7d7718e 100644 --- a/05_basic_registration.ipynb +++ b/05_basic_registration.ipynb @@ -130,7 +130,7 @@ "import SimpleITK as sitk\n", "from downloaddata import fetch_data as fdata\n", "\n", - "%matplotlib notebook\n", + "%matplotlib ipympl\n", "import gui\n", "import registration_gui as rgui\n", "\n", @@ -445,7 +445,10 @@ { "cell_type": "markdown", "metadata": { - "collapsed": true + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "source": [ "## Initialization\n", @@ -757,9 +760,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.13.5" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/06_advanced_registration.ipynb b/06_advanced_registration.ipynb index 7706e5b..d0f8d11 100644 --- a/06_advanced_registration.ipynb +++ b/06_advanced_registration.ipynb @@ -687,7 +687,11 @@ { "cell_type": "markdown", "metadata": { - "collapsed": true + "collapsed": true, + "jp-MarkdownHeadingCollapsed": true, + "jupyter": { + "outputs_hidden": true + } }, "source": [ "

Next »

" @@ -710,9 +714,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.13.5" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/07_registration_application.ipynb b/07_registration_application.ipynb index 066aed5..b2f0d45 100644 --- a/07_registration_application.ipynb +++ b/07_registration_application.ipynb @@ -45,7 +45,7 @@ "import os.path\n", "import copy\n", "\n", - "%matplotlib notebook\n", + "%matplotlib ipympl\n", "import gui\n", "import matplotlib.pyplot as plt\n", "\n", @@ -115,9 +115,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "# The ROI we specify is in a region that is expected to have uniform intensities.\n", @@ -473,9 +471,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "metric_sampling_percentage = 0.2\n", @@ -684,9 +680,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.13.5" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/08_segmentation_and_shape_analysis.ipynb b/08_segmentation_and_shape_analysis.ipynb index 452e962..6eafb85 100644 --- a/08_segmentation_and_shape_analysis.ipynb +++ b/08_segmentation_and_shape_analysis.ipynb @@ -23,7 +23,7 @@ "import SimpleITK as sitk\n", "import pandas as pd\n", "\n", - "%matplotlib notebook\n", + "%matplotlib ipympl\n", "\n", "import matplotlib.pyplot as plt\n", "import gui\n", @@ -432,9 +432,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "bacteria_labels = shape_stats.GetLabels()\n", @@ -505,9 +503,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.13.5" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/09_segmentation_evaluation.ipynb b/09_segmentation_evaluation.ipynb index 7613f1a..ef4a783 100644 --- a/09_segmentation_evaluation.ipynb +++ b/09_segmentation_evaluation.ipynb @@ -459,9 +459,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.13.5" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/10_results_visualization.ipynb b/10_results_visualization.ipynb index 10dabb6..08a2558 100644 --- a/10_results_visualization.ipynb +++ b/10_results_visualization.ipynb @@ -32,7 +32,7 @@ "metadata": {}, "outputs": [], "source": [ - "%matplotlib notebook\n", + "%matplotlib ipympl\n", "\n", "import numpy as np\n", "\n", @@ -678,33 +678,6 @@ " os.path.join(OUTPUT_DIR, \"segmentation_differences_inverted.jpg\"),\n", ")" ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Advanced visualization\n", - "\n", - "For more advanced visualization options we need to turn to other packages beyond SimpleITK. We highly recommend the [itkwidgets package](https://github.com/InsightSoftwareConsortium/itkwidgets) for various 3D visualizations. If you need finer control over the visualization there's always the visualization toolkit ([VTK](https://github.com/Kitware/VTK)). " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import itkwidgets\n", - "\n", - "itkwidgets.compare(\n", - " img,\n", - " segmentation == utilities.popi_lung_label,\n", - " cmap=itkwidgets.cm.BrBG,\n", - " annotations=False,\n", - " ui_collapsed=True,\n", - " link_cmap=False,\n", - ")" - ] } ], "metadata": { @@ -723,9 +696,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.13.5" } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/environment.yml b/environment.yml index 50ae00f..7ca844f 100644 --- a/environment.yml +++ b/environment.yml @@ -4,16 +4,17 @@ channels: - conda-forge dependencies: - - python=3.13 - - jupyter - - matplotlib - - ipywidgets - - numpy - - scipy - - pandas - - multiprocess - - SimpleITK>=2.2.0 - pip - pip: - - itkwidgets + - python=3.13 + - jupyter~=7.0 + - matplotlib + - ipywidgets + - numpy + - scipy + - pandas + - multiprocess + - SimpleITK>=2.2.0 + - ipympl + diff --git a/environment_dev.yml b/environment_dev.yml index 4e19f97..81bf26d 100644 --- a/environment_dev.yml +++ b/environment_dev.yml @@ -1,24 +1,23 @@ -name: sitkpy_dev - -channels: - - conda-forge - -dependencies: - - python=3.13 - - jupyter - - matplotlib - - ipywidgets - - numpy - - scipy - - pandas - - multiprocess - - SimpleITK>=2.2.0 - - pytest - - markdown - - lxml - - pip - - pip: - - black[jupyter] - - pyenchant - - itkwidgets[notebook] - +name: sitkpy_dev + +channels: + - conda-forge + +dependencies: + - pip + - pip: + - python=3.13 + - jupyter~=7.0 + - matplotlib + - ipywidgets + - numpy + - scipy + - pandas + - multiprocess + - SimpleITK>=2.2.0 + - ipympl + - pytest + - markdown + - lxml + - black[jupyter] + - pyenchant diff --git a/gui.py b/gui.py index ae50f23..2f60e1d 100644 --- a/gui.py +++ b/gui.py @@ -79,6 +79,7 @@ def __init__( vmax=self.moving_max_intensity, ) self.update_display() + plt.show() def create_ui(self): # Create the active UI components. Height and width are specified in 'em' units. This is @@ -393,6 +394,7 @@ def __init__(self, image, window_level=None, figure_size=(10, 8)): vmax=self.max_intensity, ) self.update_display() + plt.show() display(ui) def create_ui(self): @@ -653,6 +655,7 @@ def __init__( ) self.update_display() plt.tight_layout() + plt.show() def create_ui(self, shared_slider, wl_range, wl_init): # Create the active UI components. Height and width are specified in 'em' units. This is @@ -832,6 +835,7 @@ def __init__(self, image, window_level=None, figure_size=(10, 8)): vmax=self.max_intensity, ) self.update_display() + plt.show() def create_ui(self): # Create the active UI components. Height and width are specified in 'em' units. This is @@ -1130,6 +1134,7 @@ def __init__( self.fig.canvas.mpl_connect("button_release_event", self.on_release) self.update_display() + plt.show() def create_ui(self): # Create the active UI components. Height and width are specified in 'em' units. This is diff --git a/tests/test_notebooks.py b/tests/test_notebooks.py index 9c989b4..196a594 100755 --- a/tests/test_notebooks.py +++ b/tests/test_notebooks.py @@ -13,7 +13,6 @@ from lxml.html import document_fromstring, etree from urllib.request import urlopen, URLError, Request - """ run all tests: pytest -v --tb=short diff --git a/utilities.py b/utilities.py index 74117ab..ffe6920 100644 --- a/utilities.py +++ b/utilities.py @@ -197,6 +197,7 @@ def display_displacement_scaling_effect( plt.legend(loc=(0.25, 1.01)) plt.xlim((-2.5, 2.5)) plt.ylim((-2.5, 2.5)) + plt.show() def parameter_space_regular_grid_sampling(*transformation_parameters): From 132b18551ab92496d8de387b0044de5b6927be83 Mon Sep 17 00:00:00 2001 From: Ziv Yaniv Date: Sun, 1 Feb 2026 20:07:01 -0500 Subject: [PATCH 2/3] Improve documentation. Correct typo and provide more information on type of binary image in SimpleITK. --- 01_spatial_transformations.ipynb | 2 +- 02_images_and_resampling.ipynb | 2 +- tests/additional_dictionary.txt | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/01_spatial_transformations.ipynb b/01_spatial_transformations.ipynb index 6da4555..6b404da 100644 --- a/01_spatial_transformations.ipynb +++ b/01_spatial_transformations.ipynb @@ -668,7 +668,7 @@ "original_transformed = np.array(bspline.TransformPoint(pnt))\n", "secondary_transformed = np.array(displacement_field_transform.TransformPoint(pnt))\n", "print(\"Original transformation result: {0}\".format(original_transformed))\n", - "print(\"Deformaiton field transformation result: {0}\".format(secondary_transformed))\n", + "print(\"Deformation field transformation result: {0}\".format(secondary_transformed))\n", "print(\n", " \"Difference between transformed points is: {0}\".format(\n", " np.linalg.norm(original_transformed - secondary_transformed)\n", diff --git a/02_images_and_resampling.ipynb b/02_images_and_resampling.ipynb index 9801571..47bbaf1 100644 --- a/02_images_and_resampling.ipynb +++ b/02_images_and_resampling.ipynb @@ -312,7 +312,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Comparative operators (>, >=, <, <=, ==) are also supported, returning binary images." + "Comparative operators (>, >=, <, <=, ==) are also supported, returning binary images with pixel type of sitkUInt8." ] }, { diff --git a/tests/additional_dictionary.txt b/tests/additional_dictionary.txt index 0120b1f..1a42ec3 100644 --- a/tests/additional_dictionary.txt +++ b/tests/additional_dictionary.txt @@ -1,3 +1,4 @@ +jp ANTSNeighborhoodCorrelation API Acknowledgements From 2c2312ab36671f6628d276eb577ff1b4b0961c80 Mon Sep 17 00:00:00 2001 From: Ziv Yaniv Date: Mon, 2 Feb 2026 10:04:33 -0500 Subject: [PATCH 3/3] Omit notebook 02_ from testing due to timeout The 02 notebook fails due to timeout even when timeout is 1800 seconds. This is too long, so ommitting from CI. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9d67a78..b7fe000 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -45,7 +45,7 @@ jobs: matrix: # using macos-15-intel, last available intel architecture. macos-latest is arm64 architecture. os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel] - inputs: ["00_ or 01_ or 02_ or 03_ or 04_ or 05_", "06_ or 07_ or 08_ or 09_ or 10_"] + inputs: ["00_ or 01_ or 03_ or 04_ or 05_", "06_ or 07_ or 08_ or 09_ or 10_"] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v6