From 13eed5cd061ba195829c405ef3a2a7a74010a499 Mon Sep 17 00:00:00 2001 From: statmlben Date: Mon, 27 Jul 2026 16:41:36 +0800 Subject: [PATCH 1/4] Add RankSEG integration tutorial Signed-off-by: statmlben --- README.md | 2 + modules/rankseg_integration.ipynb | 2104 +++++++++++++++++++++++++++++ runner.sh | 1 + 3 files changed, 2107 insertions(+) create mode 100644 modules/rankseg_integration.ipynb diff --git a/README.md b/README.md index 8aa1d61ab..4218a8a79 100644 --- a/README.md +++ b/README.md @@ -331,6 +331,8 @@ Illustrate reading NIfTI files and iterating over image patches of the volumes l This tutorial illustrates the flexible network APIs and utilities. ##### [postprocessing_transforms](./modules/postprocessing_transforms.ipynb) This notebook shows the usage of several postprocessing transforms based on the model output of spleen segmentation task. +##### [rankseg_integration](./modules/rankseg_integration.ipynb) +This notebook demonstrates how to integrate RankSEG and RankSEGd as optional third-party post-processing transforms in MONAI and compare RankSEG with argmax using a pretrained pancreas segmentation model. ##### [idc_dataset](./modules/idc_dataset.ipynb) This notebook shows how to query and download public cancer imaging data from NCI Imaging Data Commons (IDC) using `idc-index`, and how to load DICOM images and DICOM-SEG segmentations into MONAI for AI/ML preprocessing. ##### [public_datasets](./modules/public_datasets.ipynb) diff --git a/modules/rankseg_integration.ipynb b/modules/rankseg_integration.ipynb new file mode 100644 index 000000000..81a4d569b --- /dev/null +++ b/modules/rankseg_integration.ipynb @@ -0,0 +1,2104 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Copyright (c) MONAI Consortium \n", + "Licensed under the Apache License, Version 2.0 (the \"License\"); \n", + "you may not use this file except in compliance with the License. \n", + "You may obtain a copy of the License at \n", + "    http://www.apache.org/licenses/LICENSE-2.0 \n", + "Unless required by applicable law or agreed to in writing, software \n", + "distributed under the License is distributed on an \"AS IS\" BASIS, \n", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n", + "See the License for the specific language governing permissions and \n", + "limitations under the License.\n", + "\n", + "# Use RankSEG in a MONAI post-processing pipeline\n", + "\n", + "This tutorial demonstrates how to expose [RankSEG](https://github.com/rankseg/rankseg) as custom MONAI array and dictionary transforms and add it to a standard decollated post-processing pipeline. RankSEG is kept as an optional third-party dependency; no change to MONAI core is required.\n", + "\n", + "The example runs inference on a real 3D CT volume from the Medical Segmentation Decathlon Task07 Pancreas dataset. It uses the official pretrained MONAI `pancreas_ct_dints_segmentation` Bundle and compares RankSEG with the usual `AsDiscrete(argmax=True)` decoder on exactly the same softmax probabilities. No model training or method-specific tuning is required.\n", + "\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Project-MONAI/tutorials/blob/main/modules/rankseg_integration.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup environment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-27T08:23:21.500843Z", + "iopub.status.busy": "2026-07-27T08:23:21.500368Z", + "iopub.status.idle": "2026-07-27T08:23:24.891073Z", + "shell.execute_reply": "2026-07-27T08:23:24.890402Z" + } + }, + "outputs": [], + "source": [ + "!python -c \"import monai\" || pip install -q \"monai-weekly[nibabel, tqdm]\"\n", + "!python -c \"import rankseg\" || pip install -q \"rankseg\"\n", + "!python -c \"import huggingface_hub\" || pip install -q huggingface_hub\n", + "!python -c \"import matplotlib\" || pip install -q matplotlib\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup imports" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-27T08:23:24.892855Z", + "iopub.status.busy": "2026-07-27T08:23:24.892728Z", + "iopub.status.idle": "2026-07-27T08:23:25.954358Z", + "shell.execute_reply": "2026-07-27T08:23:25.953827Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MONAI version: 1.7.dev2630\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Numpy version: 2.2.6\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Pytorch version: 2.13.0+cu130\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MONAI flags: HAS_EXT = False, USE_COMPILED = False, USE_META_DICT = False\n", + "MONAI rev id: 82bbaeeb45e5490606c4ff4615dd85e0e83fce7c\n", + "MONAI __file__: /home//rankseg/tutorials/.venv/lib/python3.10/site-packages/monai/__init__.py\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Optional dependencies:\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Pytorch Ignite version: NOT INSTALLED or UNKNOWN VERSION.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ITK version: NOT INSTALLED or UNKNOWN VERSION.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Nibabel version: 5.4.2\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "scikit-image version: NOT INSTALLED or UNKNOWN VERSION.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "scipy version: 1.15.3\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Pillow version: 12.3.0\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Tensorboard version: NOT INSTALLED or UNKNOWN VERSION.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "gdown version: NOT INSTALLED or UNKNOWN VERSION.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "TorchVision version: NOT INSTALLED or UNKNOWN VERSION.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tqdm version: 4.69.1\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "lmdb version: NOT INSTALLED or UNKNOWN VERSION.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "psutil version: 7.2.2\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pandas version: NOT INSTALLED or UNKNOWN VERSION.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "einops version: NOT INSTALLED or UNKNOWN VERSION.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "transformers version: NOT INSTALLED or UNKNOWN VERSION.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mlflow version: NOT INSTALLED or UNKNOWN VERSION.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pynrrd version: NOT INSTALLED or UNKNOWN VERSION.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "clearml version: NOT INSTALLED or UNKNOWN VERSION.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "For details about installing the optional dependencies, please visit:\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " https://monai.readthedocs.io/en/latest/installation.html#installing-the-recommended-dependencies\n", + "\n" + ] + } + ], + "source": [ + "import os\n", + "import shutil\n", + "import tempfile\n", + "from collections.abc import Hashable, Mapping\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import torch\n", + "from huggingface_hub import hf_hub_download\n", + "from monai.bundle import download\n", + "from monai.config import print_config\n", + "from monai.data import decollate_batch\n", + "from monai.inferers import SlidingWindowInferer\n", + "from monai.metrics import DiceMetric\n", + "from monai.networks.nets import DiNTS, TopologyInstance\n", + "from monai.transforms import (\n", + " Activations,\n", + " Activationsd,\n", + " AsDiscrete,\n", + " Compose,\n", + " EnsureChannelFirstd,\n", + " EnsureTyped,\n", + " LoadImaged,\n", + " MapTransform,\n", + " Orientationd,\n", + " ScaleIntensityRanged,\n", + " Spacingd,\n", + " Transform,\n", + ")\n", + "from monai.utils import TransformBackends, convert_to_tensor\n", + "from monai.utils.type_conversion import convert_to_dst_type\n", + "from rankseg.functional import rankseg\n", + "\n", + "print_config()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Bridge the MONAI and RankSEG shape conventions\n", + "\n", + "MONAI post-processing transforms normally receive one decollated, channel-first sample with shape `(C, *spatial)`. The RankSEG functional API receives a batch of probability maps with shape `(B, C, *spatial)`. In multiclass mode it returns class-index maps with shape `(B, *spatial)`.\n", + "\n", + "The array transform below adds a temporary batch dimension. Because that dimension has size one, the returned `(1, *spatial)` tensor already follows the same single-channel output convention as `AsDiscrete(argmax=True)`. `convert_to_dst_type` restores the source container type and MONAI metadata when applicable." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-27T08:23:25.969880Z", + "iopub.status.busy": "2026-07-27T08:23:25.969769Z", + "iopub.status.idle": "2026-07-27T08:23:25.972797Z", + "shell.execute_reply": "2026-07-27T08:23:25.972374Z" + } + }, + "outputs": [], + "source": [ + "class RankSEG(Transform):\n", + " \"\"\"Decode a channel-first multiclass probability map with RankSEG.\"\"\"\n", + "\n", + " backend = [TransformBackends.TORCH]\n", + "\n", + " def __init__(\n", + " self,\n", + " metric: str = \"dice\",\n", + " solver: str = \"RMA\",\n", + " pruning_prob: float = 0.5,\n", + " **solver_params,\n", + " ) -> None:\n", + " self.metric = metric\n", + " self.solver = solver\n", + " self.pruning_prob = pruning_prob\n", + " self.solver_params = solver_params\n", + "\n", + " def __call__(self, probabilities):\n", + " source = probabilities\n", + " probabilities = convert_to_tensor(probabilities, track_meta=False)\n", + " if probabilities.ndim < 2:\n", + " raise ValueError(\"probabilities must have shape (C, *spatial)\")\n", + "\n", + " prediction = rankseg(\n", + " probabilities.unsqueeze(0),\n", + " metric=self.metric,\n", + " solver=self.solver,\n", + " output_mode=\"multiclass\",\n", + " pruning_prob=self.pruning_prob,\n", + " **self.solver_params,\n", + " )\n", + " prediction, *_ = convert_to_dst_type(prediction, source, dtype=torch.float32)\n", + " return prediction" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup data directory\n", + "\n", + "Set `MONAI_DATA_DIRECTORY` to reuse the dataset and model Bundle across runs. Otherwise, this tutorial downloads them into a temporary directory and removes that directory at the end." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-27T08:23:25.973963Z", + "iopub.status.busy": "2026-07-27T08:23:25.973872Z", + "iopub.status.idle": "2026-07-27T08:23:25.975986Z", + "shell.execute_reply": "2026-07-27T08:23:25.975505Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/tmp/rankseg-tutorial-data\n" + ] + } + ], + "source": [ + "directory = os.environ.get(\"MONAI_DATA_DIRECTORY\")\n", + "if directory is not None:\n", + " os.makedirs(directory, exist_ok=True)\n", + "root_dir = tempfile.mkdtemp() if directory is None else directory\n", + "print(root_dir)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Download one MSD Pancreas validation case\n", + "\n", + "Task07 Pancreas contains 3D abdominal CT volumes with pancreas and pancreatic tumor labels. The Medical Segmentation Decathlon data is distributed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). To keep the tutorial download small, only `pancreas_102` and its label are fetched from a [Hugging Face dataset mirror](https://huggingface.co/datasets/MedOtter/msd-pancreas)." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-27T08:23:25.977259Z", + "iopub.status.busy": "2026-07-27T08:23:25.977173Z", + "iopub.status.idle": "2026-07-27T08:23:27.022353Z", + "shell.execute_reply": "2026-07-27T08:23:27.019951Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.\n" + ] + } + ], + "source": [ + "case_id = \"pancreas_102\"\n", + "data_dir = os.path.join(root_dir, \"Task07_Pancreas\")\n", + "evaluation_file = {}\n", + "for key, subdirectory in ((\"image\", \"imagesTr\"), (\"label\", \"labelsTr\")):\n", + " evaluation_file[key] = hf_hub_download(\n", + " repo_id=\"MedOtter/msd-pancreas\",\n", + " repo_type=\"dataset\",\n", + " filename=f\"{subdirectory}/{case_id}.nii.gz\",\n", + " local_dir=data_dir,\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Preprocess the real validation volume\n", + "\n", + "This case belongs to the Bundle's fixed validation split. It was selected from a reproducible 20-case comparison because it makes the decoder change easy to see; the full comparison should be used for performance claims. The transforms reproduce the Bundle's inference preprocessing, with nearest-neighbor resampling added for the reference label." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-27T08:23:27.028713Z", + "iopub.status.busy": "2026-07-27T08:23:27.027968Z", + "iopub.status.idle": "2026-07-27T08:23:29.510024Z", + "shell.execute_reply": "2026-07-27T08:23:29.509443Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "image: pancreas_102.nii.gz\n", + "preprocessed shape: (1, 406, 406, 228)\n" + ] + } + ], + "source": [ + "validation_transforms = Compose(\n", + " [\n", + " LoadImaged(keys=[\"image\", \"label\"]),\n", + " EnsureChannelFirstd(keys=[\"image\", \"label\"]),\n", + " Orientationd(keys=[\"image\", \"label\"], axcodes=\"RAS\", labels=None),\n", + " Spacingd(\n", + " keys=[\"image\", \"label\"],\n", + " pixdim=(1.0, 1.0, 1.0),\n", + " mode=(\"bilinear\", \"nearest\"),\n", + " ),\n", + " ScaleIntensityRanged(\n", + " keys=\"image\",\n", + " a_min=-87,\n", + " a_max=199,\n", + " b_min=0.0,\n", + " b_max=1.0,\n", + " clip=True,\n", + " ),\n", + " EnsureTyped(keys=[\"image\", \"label\"]),\n", + " ]\n", + ")\n", + "\n", + "evaluation_data = validation_transforms(evaluation_file)\n", + "print(f\"image: {os.path.basename(evaluation_file['image'])}\")\n", + "print(f\"preprocessed shape: {tuple(evaluation_data['image'].shape)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load the pretrained MONAI Pancreas Bundle\n", + "\n", + "The official [`pancreas_ct_dints_segmentation`](https://github.com/Project-MONAI/model-zoo/tree/dev/models/pancreas_ct_dints_segmentation) Bundle contains a three-class DiNTS model trained on MSD Task07. We use its architecture checkpoint and model weights without retraining or modification." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-27T08:23:29.511782Z", + "iopub.status.busy": "2026-07-27T08:23:29.511593Z", + "iopub.status.idle": "2026-07-27T08:23:30.777960Z", + "shell.execute_reply": "2026-07-27T08:23:30.777473Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-07-27 16:23:29,636 - INFO - --- input summary of monai.bundle.scripts.download ---\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-07-27 16:23:29,637 - INFO - > name: 'pancreas_ct_dints_segmentation'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-07-27 16:23:29,637 - INFO - > version: '0.5.2'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-07-27 16:23:29,637 - INFO - > bundle_dir: '/tmp/rankseg-tutorial-data'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-07-27 16:23:29,638 - INFO - > source: 'monaihosting'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-07-27 16:23:29,638 - INFO - > remove_prefix: 'monai_'\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-07-27 16:23:29,638 - INFO - > progress: True\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2026-07-27 16:23:29,638 - INFO - ---\n", + "\n", + "\n" + ] + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1a293638ddfa4444a23247f1734217f1", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Downloading bytes: | 0.00B " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "5986bd8bdf9a456d8bad76054e0ad0c9", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Reconstructing (incomplete total...): | | 0.00B / 0.00B " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "63ea827a055743caaba92078c8417697", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Fetching 18 files: 0%| | 0/18 [00:00 0.8898\n", + "Tumor Dice: 0.6730 -> 0.8106\n" + ] + } + ], + "source": [ + "to_one_hot = AsDiscrete(to_onehot=3)\n", + "reference_predictions = decollate_batch(reference_volume)\n", + "reference_one_hot = [to_one_hot(item) for item in reference_predictions]\n", + "argmax_one_hot = [to_one_hot(item) for item in argmax_predictions]\n", + "rankseg_one_hot = [to_one_hot(item) for item in rankseg_predictions]\n", + "\n", + "dice_metric = DiceMetric(include_background=False, reduction=\"mean_batch\")\n", + "dice_metric(y_pred=argmax_one_hot, y=reference_one_hot)\n", + "argmax_class_dice = dice_metric.aggregate()\n", + "dice_metric.reset()\n", + "dice_metric(y_pred=rankseg_one_hot, y=reference_one_hot)\n", + "rankseg_class_dice = dice_metric.aggregate()\n", + "dice_metric.reset()\n", + "\n", + "argmax_dice = argmax_class_dice.mean().item()\n", + "rankseg_dice = rankseg_class_dice.mean().item()\n", + "print(f\"Argmax macro Dice: {argmax_dice:.4f}\")\n", + "print(f\"RankSEG macro Dice: {rankseg_dice:.4f}\")\n", + "print(f\"Paired Dice change: {rankseg_dice - argmax_dice:+.4f}\")\n", + "print(f\"Pancreas Dice: {argmax_class_dice[0]:.4f} -> {rankseg_class_dice[0]:.4f}\")\n", + "print(f\"Tumor Dice: {argmax_class_dice[1]:.4f} -> {rankseg_class_dice[1]:.4f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Visualize the real CT result\n", + "\n", + "The axial slice is selected by maximum ground-truth tumor area, and the crop is based only on the ground-truth tumor bounds. The final panel highlights tumor-label decisions changed by RankSEG." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-27T08:24:01.073796Z", + "iopub.status.busy": "2026-07-27T08:24:01.073693Z", + "iopub.status.idle": "2026-07-27T08:24:01.595999Z", + "shell.execute_reply": "2026-07-27T08:24:01.595236Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAACCkAAAGGCAYAAABcnkk8AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjksIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvJkbTWQAAAAlwSFlzAAAPYQAAD2EBqD+naQAAl5lJREFUeJzs3Xd4VHXa+P97MpmSTHojCYQEAtIRFxQRKWIBROx1LWDFtburPqv7VcB17btrWwvPuugqa8G1KyIoFuwFLPSShEBCeu8zc35/+Ms8Tu4PcCgOCO/XdXldzjtn5tQ5OTP5MOOwLMsSAAAAAAAAAAAAAACAX1jU3l4AAAAAAAAAAAAAAABwYGCQAgAAAAAAAAAAAAAAiAgGKQAAAAAAAAAAAAAAgIhgkAIAAAAAAAAAAAAAAIgIBikAAAAAAAAAAAAAAICIYJACAAAAAAAAAAAAAACICAYpAAAAAAAAAAAAAACAiGCQAgAAAAAAAAAAAAAAiAgGKQAAAAAAAAAAAAAAgIhgkAIAAAAAAAAAAPuR6dOnS1xc3N5eDAAAACMGKQAAAAAAACDk0UcfFYfDISNHjtzbi7LXffrppzJr1iypra3d24sC4FfmqaeeEofDEfovOjpaunfvLtOnT5ctW7bs7cULCQaD8u9//1tGjhwpKSkpEh8fLwcddJBccMEF8vnnn4em++CDD8LWp+t/zz//vPFxjz32WElLSxOXyyUZGRly3HHHyZw5c6StrW2Hy/boo4/KU089tadXGQAA7AOi9/YCAAAAAAAAYN8xb948ycvLky+//FLWr18vffr02duLtNd8+umnMnv2bJk+fbokJSXt7cUB8Ct0++23S69evaS1tVU+//xzeeqpp2Tp0qXy448/itfr3duLJ9dcc4384x//kJNOOknOPfdciY6OljVr1siCBQukd+/ecvjhh6vpDz30UPU4o0aNCv1/S0uLnHLKKbJw4UI54ogj5IYbbpBu3bpJdXW1fPjhh3LFFVfIF198IU8++eR2l+3RRx+VtLQ0mT59+h5ZVwAAsO9gkAIAAAAAAABERKSgoEA+/fRTefnll2XGjBkyb948mTlz5g7v5/f7JRgMitvtjsBSwi72C7D3TZ48WUaMGCEiIpdccomkpaXJPffcI6+//rqceeaZe3XZysrK5NFHH5VLL71U5syZE/azBx54QCoqKtR9xowZI6effvp2H/f666+XhQsXygMPPCDXXntt2M/+8Ic/yLp162TRokW7vwL7uKamJvH5fHt7MQAA2CfxdQ8AAAAAAAAQkZ8+RSE5OVmmTJkip59+usybN09NU1hYKA6HQ+6//3554IEHJD8/Xzwej6xcuVJEfvpI8BEjRojX65X8/Hx54oknZNasWeJwOMIex+FwyFVXXSXz58+XgQMHSkxMjIwaNUp++OEHERF54oknpE+fPuL1emX8+PFSWFgYdv+PP/5YzjjjDOnZs6d4PB7JycmR66+/XlpaWkLTlJeXS3p6uowfP14sywr19evXi8/nk7POOmub22LWrFly4403iohIr169Qh9pXlhYGNoGpo8hdzgcMmvWrLDHcTgcsnbtWjnvvPMkMTFR0tPT5dZbbxXLsqS4uFhOOukkSUhIkMzMTPnrX/+qHrO8vFwuvvhi6datm3i9Xjn44IPl6aef3qn9AmDfMGbMGBER2bBhQ6i1t7fLbbfdJsOHD5fExETx+XwyZswYWbJkSdh9f/48nzNnTuh5fuihh8pXX321w3kvX748dE5sbGyUgoICsSxLRo8eraZ1OBySkZGx0+tXXFws//znP2XSpElqgEKnvn37yhVXXLHdx8nLy5MVK1bIhx9+GDr/jh8/XkTE+DtF5P++YuPnvy/y8vLkhBNOCP1uiomJkSFDhsgHH3wgIiIvv/yyDBkyRLxerwwfPlyWLVumHvf999+XMWPGiM/nk6SkJDnppJNk1apVYdN0LtPKlSvlt7/9rSQnJ8uRRx653XUEAOBAxicpAAAAAAAAQER+GqRw6qmnitvtlnPOOUcee+wx+eqrr4wf7T137lxpbW2Vyy67TDwej6SkpMiyZctk0qRJkpWVJbNnz5ZAICC33367pKenG+f38ccfy+uvvy5XXnmliIjcddddcsIJJ8hNN90kjz76qFxxxRVSU1Mj9957r1x00UXy/vvvh+47f/58aW5ult/97neSmpoqX375pTz88MOyefNmmT9/voiIZGRkyGOPPSZnnHGGPPzww3LNNddIMBiU6dOnS3x8vDz66KPb3BannnqqrF27Vp577jn5+9//LmlpaSIikp6ebvzXxTty1llnyYABA+Tuu++Wt956S+644w5JSUmRJ554QiZMmCD33HOPzJs3T2644QY59NBDZezYsSLy08em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4/HIlClT5IsvvpB33nlnh8uampoqJ554orz99tsybtw4Wbp0qRoItSP9+/cXEZEff/wxrKenp4cGNGRlZe3UY3a1s+fgXf1dsys4BwMAYA+DFAAAAAAAAA5w8+bNk4yMDJk/f77675xzzpFXXnlFWlpatvsYGRkZ4vV6Zf369epnprY7fvjhB1m7dq389a9/lf/5n/+Rk046SY455piwr5n4uXfeeUf++c9/yk033STp6ekybdo08fv9O5zPtv4Q1vmvlmtra8P6zv4xz47c3FxZt26d+kPl6tWrQz8H8OvgdDrlrrvukpKSEnnkkUdC/aWXXpLevXvLyy+/LOeff75MnDhRjjnmGGltbd2t+TkcDpk3b54cffTRcsYZZ8gHH3xg+74jRowQkZ++8mBnTJ48WZxOp3Fw287a2+fgzvPrmjVr1M9Wr14taWlp4vP59ug8AQA4UDBIAQAAAAAA4ADW0tIiL7/8spxwwgly+umnq/+uuuoqaWhokNdff327j+N0OuWYY46RV199VUpKSkJ9/fr1smDBgj26zJ3/+vXn/9rVsix58MEH1bS1tbVyySWXyGGHHSZ33nmn/POf/5Rvv/1W7rzzzh3Op/OPT13/EJaQkCBpaWny0UcfhfVHH310Z1dlh44//njZunWrvPDCC6Hm9/vl4Ycflri4uNBXXAD4dRg/frwcdthh8sADD4QGIZjOaV988YV89tlnuz0/t9stL7/8shx66KEydepU+fLLL0M/27p1q6xcuVLdp729Xd577z2Jiora5tf1bEvPnj3loosukgULFoQNxPg5u59U4PP51PlXRCQ/P19EJOwc3NTUJE8//fROLeuOZGVlybBhw+Tpp58OW44ff/xR3n33XTn++OP36PwAADiQRO/tBQAAAAAAAMDe8/rrr0tDQ4OceOKJxp8ffvjhkp6eLvPmzZOzzjpru481a9Yseffdd2X06NHyu9/9TgKBgDzyyCMyePBgWb58+R5b5v79+0t+fr7ccMMNsmXLFklISJD//ve/6vvYRUSuvfZaqaqqksWLF4vT6ZRJkybJJZdcInfccYecdNJJcvDBB29zPsOHDxcRkT/96U9y9tlni8vlkqlTp4rP55NLLrlE7r77brnkkktkxIgR8tFHH8natWv32Dp2uuyyy+SJJ56Q6dOnyzfffCN5eXny0ksvySeffCIP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L+Suu+6SVatWySuvvBI2nzVr1sg555wjM2bMkEsvvVT69eu33eWqqqqSyZMny9lnny3nnXeedOvWTYLBoJx44omydOlSueyyy2TAgAHyww8/yN///ndZu3atvPrqq5Keni7PPPOM/OUvf5HGxka56667ROT/PqZ9xowZ8tRTT8mFF14o11xzjRQUFMgjjzwiy5Ytk08++STso79Ny9zc3Czjxo2TLVu2yIwZM6Rnz57y6aefys033yylpaXywAMPhK3Hf/7zH2loaJAZM2aIw+GQe++9V0499VTZuHFjaF7ff/+9jBkzRlwul1x22WWSl5cnGzZskDfeeCP03cBlZWVy+OGHhy6009PTZcGCBXLxxRdLfX29XHfddbu0/wH8MvbVc2tNTY0cf/zxcuaZZ8o555wjL774ovzud78Tt9stF110Udi0f/7zn8XtdssNN9wgbW1t4na7Q+fPQw89VO666y4pKyuTBx98UD755BN1rRkIBGTSpEly+OGHy7333ivvvPOOzJw5U/x+f+gPaSIiDz74oJx44oly7rnnSnt7uzz//PNyxhlnyJtvvilTpkwJW6YPP/xQXnjhBbnmmmvE4/HIo48+KpMmTZIvv/xSBg8evLO7KWTGjBlSUlKivubD4XDIeeedJ/fee69UV1dLSkpK6GdvvPGG1NfXy3nnnbfL8wWwd51//vlyyy23yLvvviuXXnrpNqebPXu2zJo1S4444gi5/fbbxe12yxdffCHvv/9+6CsSnnnmGZk2bZpMnDhR7rnnHmlubpbHHntMjjzySFm2bNl2Pxp32bJl4vP51NcKHXbYYaGfH3nkkdu8/+LFi6V///7y9ttvy4033ihbtmyR5ORkufLKK2X27Nnb/UPZvHnzRES2OUihvr5e2tvbpbKyUv7973/Ljz/+GBo8DAB27C/n2vHjx8sLL7wgM2bMkN///vehr3t4+eWX5b777tuJLQJgb9iVvyUtXbpUXn75ZbniiiskPj5eHnroITnttNNk06ZNkpqaKiIiX331lXz66ady9tlnS48ePaSwsFAee+wxGT9+vKxcuVJiY2PDHvPqq6+W5ORkmTlzphQWFsoDDzwgV111lbzwwguhaW6++Wa59957ZerUqTJx4kT57rvvZOLEidLa2hr2WHbfo7QsS0466SRZunSpXH755TJgwAB55ZVXZNq0aTvcBmPHjpVrrrlGHnroIbnllltC59Bd/TrMq6++WjIzM2X27Nny+eefy5w5cyQpKUk+/fRT6dmzp9x5553y9ttvy3333SeDBw+WCy64ILQOJ554oixZskQuvvhiGTZsmCxcuDB07dv1637ff/99efHFF+Wqq66StLS0bf5+OOuss+SCCy6Qr776Sg499NBQLyoqks8//zzs/L6j92ocDof861//kqFDh8rll18uL7/8soiIzJw5U1asWCEffPCB+Hw+Edn1v+MtWrRIzjnnHDn66KPlnnvuERGRVatWySeffLLH/sE2hM/YxI7V1dVZImKddNJJO33fXfm6h86PLisoKAi13NxcS0SsTz/9NNQWLlxoiYgVExNjFRUVhfoTTzyhPhLH9DFnzz33nCUi1kcffRRqU6dOtWJjY60tW7aE2rp166zo6Oiwj6QtLCy0nE6n9Ze//CXsMX/44QcrOjpadQC/fqaPps7NzbWmTZumpu36UddLliyxRMQaPHiw1d7eHurnnHOO5XA4rMmTJ4fdf9SoUWEfL7V8+XJLRKxLLrkkbLobbrjBEhHr/fffD1smEbHeeecdW+s1btw4S0Ssxx9/PKw/88wzVlRUlPXxxx+H9ccff9wSEeuTTz4Je4yuX/fw8ccfWyJizZs3L6y/8847qm9rmf/85z9bPp/PWrt2bVj/4x//aDmdTmvTpk2WZf3fR6alpqZa1dXVoelee+01S0SsN954I9TGjh1rxcfHh/3esKyfPnqy08UXX2xlZWVZlZWVYdOcffbZVmJi4l7/6Exgf7K/n1v/+te/hlpbW5s1bNgwKyMjI7S8nevQu3fvsHNLe3u7lZGRYQ0ePDjsoxLffPNNS0Ss2267LdSmTZtmiYh19dVXh1owGLSmTJliud1uq6KiItS7nr/a29utwYMHWxMmTAjrImKJiPX111+HWlFRkeX1eq1TTjkl1EzX7V33k+ljLbf1dQ9r1qyxRMR67LHHwvqJJ55o5eXlhZ2rAexbtvcR5J0SExOtQw45JHR75syZYeeCdevWWVFRUdYpp5yivvam8/nf0NBgJSUlWZdeemnYz7du3WolJiaq3tWUKVOs3r17q97U1GSJiPXHP/5xu/dPSEiwkpOTLY/HY916663WSy+9ZP32t7/d4X39fr/VrVs367DDDtvmNBMnTgydf91utzVjxoxf7cfl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X5uTn58vwWBQVq5cGfapAl2nEfnp9fuuXKsNGzZM/vnPf8qqVavC/hXxF198Efr59gwfPlzWrVsnW7ZsCfudVFJSIiJ6IK6IyOuvvy4NDQ3b/aoHk5aWFrEsSxoaGhikAMCW/eVcW1ZWZvyjW0dHh4gIX4UD7ON+qb8lvfTSSzJt2jT561//Gmqtra3G9w/s6HxdvX79+rBPA6iqqlIDT+2+R5mbmyvvvfeeNDY2hv0BfM2aNbaWaXuvlU3vlbS3t4cGgO0pubm5snjxYmloaAj7NIXVq1eHfr6rfD6fnHDCCTJ//nz529/+Ji+88IKMGTMmbPDazrxXU1paKldffbUcd9xx4na75YYbbpCJEyeGptndv+O53W6ZOnWqTJ06VYLBoFxxxRXyxBNPyK233spguT0kaseTACI33XST+Hw+ueSSS6SsrEz9fMOGDfLggw/uhSXbsc4RUl1Hg3V9s9PpdMoxxxwjr776augNBpGffkl1/Z6dU089VZxOp8yePVs9rmVZUlVVtQfXAMC+Kj8/Xz7//HNpb28PtTfffFOKi4v36HyOP/54EdHnrc5POZgyZcoenZ/IT59As2XLFvnf//1f9bOWlhZpamra4f0DgYD8+c9/Vj/z+/22XkCceeaZ8tlnn8nChQvVz2pra3f6jYn09HQZO3as/Otf/5JNmzaF/azzXO50OuW0006T//73v8bBDBUVFTs1TwA7b385t/r9fnniiSdCt9vb2+WJJ56Q9PR0GT58+HbvO2LECMnIyJDHH3887KNyFyxYIKtWrTIu2yOPPBL6f8uy5JFHHhGXyyVHH320iPx0fnM4HGFv+BYWFm7ze30/++yzsO/1LC4ultdee02OO+64bf6LDLs6B25s63fB+eefLytXrpQbb7xRnE6nnH322bs1PwB71/vvvy9//vOfpVevXtv9Q/3JJ58sUVFRcvvtt6t/Ddd5rTZx4kRJSEiQO++8M/THqp/b0bXaSSedJC6XSx599NGwx3788cele/fucsQRR4R6aWmprF69Omw+nV/X8OSTT4ZaMBiUuXPnSkpKivH8/p///EdiY2PllFNOMS6T6avEamtr5b///a/k5ORs92PbAaDT/nSuPeigg2TRokVh768GAgF58cUXJT4+Pux76wHse36pvyU5nU71t5iHH354m58ksCNHH320REdHy2OPPRbWf/7aupPd9yiPP/548fv9YY8ZCATk4YcftrVM23utnJ+fLx999FFYmzNnzi6v/7Ycf/zxEggE1Hb4+9//Lg6HQyZPnrxbj3/WWWdJSUmJ/POf/5TvvvtOfR3azrxXc+mll0owGJQnn3xS5syZI9HR0XLxxReHjpPd+Tte159FRUWFPl3X9JVG2DV8kgJsyc/Pl//85z9y1llnyYABA+SCCy6QwYMHS3t7u3z66acyf/58mT59+t5eTKOEhAQZO3as3HvvvdLR0SHdu3eXd999VwoKCtS0s2bNknfffVdGjx4tv/vd70In48GDB8vy5ctD0+Xn58sdd9whN998sxQWFsrJJ58s8fHxUlBQIK+88opcdtllcsMNN0RwLQHsDZdccom89NJLMmnSJDnzzDNlw4YN8uyzz+7xF8wHH3ywTJs2TebMmSO1tbUybtw4+fLLL+Xpp5+Wk08+WY466qg9Oj+Rn/5A9OKLL8rll18uS5YskdGjR0sgEJDVq1fLiy++KAsXLpQRI0Zs8/7jxo2TGTNmyF133SXLly+X4447Tlwul6xbt07mz58vDz74oJx++unbXYYbb7xRXn/9dTnhhBNk+vTpMnz4cGlqapIffvhBXnrpJSksLJS0tLSdWq+HHnpIjjzySPnNb34jl112mfTq1UsKCwvlrbfeCp3n7777blmyZImMHDlSLr30Uhk4cKBUV1fLt99+K4sXL5bq6uqdmieAnbO/nFuzs7PlnnvukcLCQjnooIPkhRdekOXLl8ucOXO2+72HIj99L+I999wjF154oYwbN07OOeccKSsrkwcffFDy8vLk+uuvD5ve6/XKO++8I9OmTZORI0fKggUL5K233pJbbrkl9K96p0yZIn/7299k0qRJ8tvf/lbKy8vlH//4h/Tp00e+//57tQyDBw+WiRMnyjXXXCMejyf0JvPs2bN3a7uISOiPeNdcc41MnDhRDUSYMmWKpKamyvz582Xy5Mn8gQ74FVmwYIGsXr1a/H6/lJWVyfvvvy+LFi2S3Nxcef3118Xr9W7zvn369JE//elP8uc//1nGjBkjp556qng8Hvnqq68kOztb7rrrLklISJDHHntMzj//fPnNb34jZ599tqSnp8umTZvkrbfektGjRxvfWO7Uo0cPue666+S+++6Tjo4OOfTQQ+XVV1+Vjz/+WObNmxc2COvmm2+W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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "image = input_volume[0, 0].detach().cpu()\n", + "reference = reference_volume[0, 0].detach().cpu()\n", + "probabilities = torch.softmax(logits[0], dim=0).detach().cpu()\n", + "argmax_prediction = argmax_predictions[0][0].detach().cpu()\n", + "rankseg_prediction = rankseg_predictions[0][0].detach().cpu()\n", + "reference_tumor = reference == 2\n", + "argmax_tumor = argmax_prediction == 2\n", + "rankseg_tumor = rankseg_prediction == 2\n", + "slice_index = int(reference_tumor.sum(dim=(0, 1)).argmax().item())\n", + "coordinates = torch.nonzero(reference_tumor[:, :, slice_index], as_tuple=False)\n", + "margin = 24\n", + "row_start = max(0, int(coordinates[:, 0].min()) - margin)\n", + "row_end = min(reference.shape[0], int(coordinates[:, 0].max()) + margin + 1)\n", + "column_start = max(0, int(coordinates[:, 1].min()) - margin)\n", + "column_end = min(reference.shape[1], int(coordinates[:, 1].max()) + margin + 1)\n", + "decoder_difference = argmax_tumor != rankseg_tumor\n", + "\n", + "\n", + "def crop(panel):\n", + " return panel[row_start:row_end, column_start:column_end, slice_index]\n", + "\n", + "\n", + "figure, axes = plt.subplots(1, 6, figsize=(21, 4))\n", + "panels = [\n", + " (crop(image), \"CT image\", \"gray\"),\n", + " (crop(reference_tumor), \"Tumor reference\", \"gray\"),\n", + " (crop(probabilities[2]), \"Tumor probability\", \"magma\"),\n", + " (crop(argmax_tumor), f\"Argmax tumor\\nDice {argmax_class_dice[1]:.3f}\", \"gray\"),\n", + " (crop(rankseg_tumor), f\"RankSEG tumor\\nDice {rankseg_class_dice[1]:.3f}\", \"gray\"),\n", + " (crop(decoder_difference), \"Changed tumor voxels\", \"hot\"),\n", + "]\n", + "for axis, (panel, title, color_map) in zip(axes, panels):\n", + " axis.imshow(panel, cmap=color_map)\n", + " axis.set_title(title)\n", + " axis.axis(\"off\")\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Use RankSEGd in a dictionary post-processing pipeline\n", + "\n", + "MONAI dictionary transforms inherit from `MapTransform`, copy the input mapping, and apply an array transform to the requested keys. `RankSEGd` therefore keeps the RankSEG-specific logic in `RankSEG` while fitting workflows in which predictions and metadata are carried under named keys." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-27T08:24:01.597674Z", + "iopub.status.busy": "2026-07-27T08:24:01.597358Z", + "iopub.status.idle": "2026-07-27T08:24:02.001888Z", + "shell.execute_reply": "2026-07-27T08:24:02.001418Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dictionary output shape: (1, 406, 406, 228)\n" + ] + } + ], + "source": [ + "class RankSEGd(MapTransform):\n", + " \"\"\"Dictionary wrapper for :class:`RankSEG`.\"\"\"\n", + "\n", + " backend = RankSEG.backend\n", + "\n", + " def __init__(\n", + " self,\n", + " keys,\n", + " metric: str = \"dice\",\n", + " solver: str = \"RMA\",\n", + " pruning_prob: float = 0.5,\n", + " allow_missing_keys: bool = False,\n", + " **solver_params,\n", + " ) -> None:\n", + " super().__init__(keys, allow_missing_keys)\n", + " self.converter = RankSEG(\n", + " metric=metric,\n", + " solver=solver,\n", + " pruning_prob=pruning_prob,\n", + " **solver_params,\n", + " )\n", + "\n", + " def __call__(self, data: Mapping[Hashable, object]) -> dict[Hashable, object]:\n", + " output = dict(data)\n", + " for key in self.key_iterator(output):\n", + " output[key] = self.converter(output[key])\n", + " return output\n", + "\n", + "\n", + "post_ranksegd = Compose(\n", + " [\n", + " Activationsd(keys=\"pred\", softmax=True),\n", + " RankSEGd(keys=\"pred\", metric=\"dice\"),\n", + " ]\n", + ")\n", + "\n", + "dictionary_samples = decollate_batch({\"pred\": logits})\n", + "dictionary_result = post_ranksegd(dictionary_samples[0])\n", + "torch.testing.assert_close(dictionary_result[\"pred\"], rankseg_predictions[0])\n", + "print(f\"dictionary output shape: {tuple(dictionary_result['pred'].shape)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Practical notes\n", + "\n", + "- Use `metric=\"iou\"` when IoU is the target metric.\n", + "- RankSEG expects finite probabilities in `[0, 1]`; apply softmax to multiclass logits or sigmoid to multilabel logits first.\n", + "- In the fixed 20-case comparison from which this case was selected, RankSEG (Dice) changed mean samplewise Dice by `+0.0209` and won on 18 of 20 cases. Use that paired evaluation, rather than this selected example alone, for performance claims.\n", + "- This wrapper intentionally demonstrates non-overlapping multiclass output. Multilabel RankSEG returns `(C, *spatial)` binary masks after removing the temporary batch dimension and needs slightly different shape handling.\n", + "- The Bundle and this tutorial are research examples and are not intended for diagnostic use.\n", + "\n", + "Further reading:\n", + "\n", + "- [RankSEG repository and documentation](https://github.com/rankseg/rankseg)\n", + "- [Reproducible MONAI Pancreas comparison](https://github.com/rankseg/rankseg/tree/main/experiments/monai-pr-8908-reproducibility)\n", + "- [MONAI Pancreas CT DiNTS Segmentation Bundle](https://github.com/Project-MONAI/model-zoo/tree/dev/models/pancreas_ct_dints_segmentation)\n", + "- [Discussion of Option B in MONAI PR #8908](https://github.com/Project-MONAI/MONAI/pull/8908#issuecomment-5077397523)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cleanup data directory\n", + "\n", + "Remove downloaded data and model files only when a temporary directory was used." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-27T08:24:02.003517Z", + "iopub.status.busy": "2026-07-27T08:24:02.003360Z", + "iopub.status.idle": "2026-07-27T08:24:02.004926Z", + "shell.execute_reply": "2026-07-27T08:24:02.004691Z" + } + }, + "outputs": [], + "source": [ + "if directory is None:\n", + " shutil.rmtree(root_dir)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.10.12" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": { + "056347b9d4cb4d2fba12c6440192e53a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": "20px" + } + }, + "066292d5ea2f4d57b1a974e06b620c52": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "086811e7746c433082b216fd3b7d8848": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_5e34afb119894d8185a38907aee154f7", + "placeholder": "​", + "style": "IPY_MODEL_4f6cd5fefd1844fe93e8e8bc6ed69db4", + "tabbable": null, + "tooltip": null, + "value": " 18/18 [00:00<00:00, 424.40it/s]" + } + }, + "0b458979bce7455f900eb2c9595d8017": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_a12d50e367fc476dbd4d6146b35e03f5", + "placeholder": "​", + "style": "IPY_MODEL_61fb9a9902134671b4919f9e7f7f22f7", + "tabbable": null, + "tooltip": null, + "value": "Fetching 18 files: 100%" + } + }, + "1a293638ddfa4444a23247f1734217f1": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_af3bae648b074b07be2eb8307df02645", + "IPY_MODEL_8cb65700a1c8493f8847d7fb9c1ae5ef", + "IPY_MODEL_5c47796340be4fb4bb755b964777f19d" + ], + "layout": "IPY_MODEL_1e107a96b7e241bfa347fbd887b82e22", + "tabbable": null, + "tooltip": null + } + }, + "1afd34e8d1274be0a863bed47a9bc708": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "1e107a96b7e241bfa347fbd887b82e22": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "1f7e8179e39a4d2292c1c76ca8cb3f5a": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_056347b9d4cb4d2fba12c6440192e53a", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_f735d41d1bfd4485b5c84f190839d745", + "tabbable": null, + "tooltip": null, + "value": 0 + } + }, + "32f600ef296641f8a71e5554f846a2fc": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "3503cf7bde424d09b3aa4752ae6e7e9c": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_56d90a75da2648e1879c06856968c970", + "placeholder": "​", + "style": "IPY_MODEL_ad7ae2c3117044adaa8f3e6197d29567", + "tabbable": null, + "tooltip": null, + "value": "  0.00B /  0.00B            " + } + }, + "402e995cda7a4ad6b1d9ff41b9620c8a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "4f6cd5fefd1844fe93e8e8bc6ed69db4": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "56d90a75da2648e1879c06856968c970": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "5986bd8bdf9a456d8bad76054e0ad0c9": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_a7f477128bc74e3d90cfd2a4315f52e0", + "IPY_MODEL_1f7e8179e39a4d2292c1c76ca8cb3f5a", + "IPY_MODEL_3503cf7bde424d09b3aa4752ae6e7e9c" + ], + "layout": "IPY_MODEL_f703d75829124709834223905f0fb213", + "tabbable": null, + "tooltip": null + } + }, + "5c47796340be4fb4bb755b964777f19d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_a03e6879d6ad474ab1f5df6535bac382", + "placeholder": "​", + "style": "IPY_MODEL_1afd34e8d1274be0a863bed47a9bc708", + "tabbable": null, + "tooltip": null, + "value": "  0.00B            " + } + }, + "5e34afb119894d8185a38907aee154f7": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "61fb9a9902134671b4919f9e7f7f22f7": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "63ea827a055743caaba92078c8417697": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_0b458979bce7455f900eb2c9595d8017", + "IPY_MODEL_fa6775e35e874c91afa9bbe116f8c86e", + "IPY_MODEL_086811e7746c433082b216fd3b7d8848" + ], + "layout": "IPY_MODEL_9caaee08198140ce8de67d925994dfe1", + "tabbable": null, + "tooltip": null + } + }, + "8cb65700a1c8493f8847d7fb9c1ae5ef": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_f44eaa95d8234614adf0df5c961aec0e", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_aa0df33c4d824446b1a07a3834208fc3", + "tabbable": null, + "tooltip": null, + "value": 0 + } + }, + "9caaee08198140ce8de67d925994dfe1": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a03e6879d6ad474ab1f5df6535bac382": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a12d50e367fc476dbd4d6146b35e03f5": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a7f477128bc74e3d90cfd2a4315f52e0": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_402e995cda7a4ad6b1d9ff41b9620c8a", + "placeholder": "​", + "style": "IPY_MODEL_e5322ed73575460ab0c4ad253678b658", + "tabbable": null, + "tooltip": null, + "value": "Reconstruction complete: " + } + }, + "aa0df33c4d824446b1a07a3834208fc3": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "ad7ae2c3117044adaa8f3e6197d29567": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "af3bae648b074b07be2eb8307df02645": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_ed67e48f73f64c95b97c955628055c69", + "placeholder": "​", + "style": "IPY_MODEL_c23fcea7a922449882473378f260cf6f", + "tabbable": null, + "tooltip": null, + "value": "Download complete: : " + } + }, + "c23fcea7a922449882473378f260cf6f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "e5322ed73575460ab0c4ad253678b658": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "ed67e48f73f64c95b97c955628055c69": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "f44eaa95d8234614adf0df5c961aec0e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": "20px" + } + }, + "f703d75829124709834223905f0fb213": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "f735d41d1bfd4485b5c84f190839d745": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "fa6775e35e874c91afa9bbe116f8c86e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_066292d5ea2f4d57b1a974e06b620c52", + "max": 18, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_32f600ef296641f8a71e5554f846a2fc", + "tabbable": null, + "tooltip": null, + "value": 18 + } + } + }, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/runner.sh b/runner.sh index fc9c19ee8..5a24aa2a2 100755 --- a/runner.sh +++ b/runner.sh @@ -24,6 +24,7 @@ doesnt_contain_max_epochs=() doesnt_contain_max_epochs=("${doesnt_contain_max_epochs[@]}" msd_datalist_generator.ipynb) doesnt_contain_max_epochs=("${doesnt_contain_max_epochs[@]}" load_medical_images.ipynb) doesnt_contain_max_epochs=("${doesnt_contain_max_epochs[@]}" integrate_3rd_party_transforms.ipynb) +doesnt_contain_max_epochs=("${doesnt_contain_max_epochs[@]}" rankseg_integration.ipynb) doesnt_contain_max_epochs=("${doesnt_contain_max_epochs[@]}" transform_speed.ipynb) doesnt_contain_max_epochs=("${doesnt_contain_max_epochs[@]}" transforms_demo_2d.ipynb) doesnt_contain_max_epochs=("${doesnt_contain_max_epochs[@]}" nifti_read_example.ipynb) From 69d75fd56b78c5997523ffd12130d8ed9417559f Mon Sep 17 00:00:00 2001 From: statmlben Date: Wed, 29 Jul 2026 10:43:32 +0800 Subject: [PATCH 2/4] Address review feedback for RankSEG tutorial Signed-off-by: statmlben --- modules/rankseg_integration.ipynb | 59 ++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/modules/rankseg_integration.ipynb b/modules/rankseg_integration.ipynb index 81a4d569b..7ce0dbf4c 100644 --- a/modules/rankseg_integration.ipynb +++ b/modules/rankseg_integration.ipynb @@ -24,6 +24,35 @@ "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Project-MONAI/tutorials/blob/main/modules/rankseg_integration.ipynb)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## What is RankSEG?\n", + "\n", + "A segmentation network usually produces a score or probability for every class at every voxel. A conventional multiclass pipeline converts these values to a mask with `argmax`, independently assigning each voxel to the class with the largest score. This is a natural rule for voxel-wise classification, but it does not directly target region-level metrics such as Dice or IoU, whose values depend on the predicted region as a whole.\n", + "\n", + "RankSEG is an inference-time, metric-aware alternative to argmax or fixed thresholding. It leaves the trained model and its probability maps unchanged, and only replaces the final probability-to-mask decision. Unlike connected-component filtering or morphological post-processing, RankSEG does not impose connectivity, shape, or anatomical constraints.\n", + "\n", + "| Property | Argmax / fixed threshold | RankSEG |\n", + "| --- | --- | --- |\n", + "| Decision | Independent at each voxel | Based on each class probability map as a whole |\n", + "| Target | Voxel-wise class decision | A selected region metric, such as Dice or IoU |\n", + "| Region size | Implicit or controlled by a fixed threshold | Adaptively selected for each image and class |\n", + "| Model training | Unchanged | Unchanged |\n", + "| Additional inference cost | Minimal | Probability ranking and metric-aware optimization |\n", + "\n", + "### How the algorithm works\n", + "\n", + "For each class, RankSEG ranks voxels from highest to lowest predicted probability. The `RMA` solver used in this tutorial then estimates how many of the highest-ranked voxels should be included to target the selected metric. In multiclass mode, the class masks are finally combined into one non-overlapping label map. No model retraining or threshold tuning is required.\n", + "\n", + "### When should RankSEG be considered?\n", + "\n", + "RankSEG can be useful when Dice or IoU is the evaluation target and the model produces meaningful probability maps, particularly for uncertain predictions or small-to-medium foreground regions. When predictions are already confident, RankSEG and argmax may produce nearly identical masks.\n", + "\n", + "RankSEG does not correct errors in the underlying probability maps, and its output depends on their quality and calibration. It also adds computation and memory use during inference because the probabilities must be ranked. Improvement is not guaranteed for every case or deployment distribution, so the metric, output mode, and solver should be selected deliberately and validated on representative data. The case below illustrates the integration rather than serving as a standalone performance benchmark." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -44,10 +73,7 @@ }, "outputs": [], "source": [ - "!python -c \"import monai\" || pip install -q \"monai-weekly[nibabel, tqdm]\"\n", - "!python -c \"import rankseg\" || pip install -q \"rankseg\"\n", - "!python -c \"import huggingface_hub\" || pip install -q huggingface_hub\n", - "!python -c \"import matplotlib\" || pip install -q matplotlib\n", + "%pip install -q --upgrade-strategy only-if-needed \"monai-weekly[nibabel, tqdm,matplotlib,huggingface_hub]\" rankseg\n", "%matplotlib inline" ] }, @@ -260,12 +286,11 @@ "import matplotlib.pyplot as plt\n", "import torch\n", "from huggingface_hub import hf_hub_download\n", - "from monai.bundle import download\n", + "from monai.bundle import ConfigParser, download\n", "from monai.config import print_config\n", "from monai.data import decollate_batch\n", "from monai.inferers import SlidingWindowInferer\n", "from monai.metrics import DiceMetric\n", - "from monai.networks.nets import DiNTS, TopologyInstance\n", "from monai.transforms import (\n", " Activations,\n", " Activationsd,\n", @@ -632,21 +657,11 @@ " map_location=device,\n", " weights_only=False,\n", ")\n", - "topology = TopologyInstance(\n", - " channel_mul=1,\n", - " num_blocks=12,\n", - " num_depths=4,\n", - " use_downsample=True,\n", - " arch_code=[architecture[\"arch_code_a\"], architecture[\"arch_code_c\"]],\n", - " device=str(device),\n", - ")\n", - "model = DiNTS(\n", - " dints_space=topology,\n", - " in_channels=1,\n", - " num_classes=3,\n", - " use_downsample=True,\n", - " node_a=torch.as_tensor(architecture[\"node_a\"], device=device),\n", - ").to(device)\n", + "parser = ConfigParser()\n", + "parser.read_config(os.path.join(bundle_dir, \"configs\", \"inference.yaml\"))\n", + "parser[\"arch_ckpt\"] = architecture\n", + "parser[\"dints_space#device\"] = str(device)\n", + "model = parser.get_parsed_content(\"network_def\").to(device)\n", "checkpoint = torch.load(\n", " os.path.join(bundle_dir, \"models\", \"model.pt\"),\n", " map_location=device,\n", @@ -960,6 +975,8 @@ "Further reading:\n", "\n", "- [RankSEG repository and documentation](https://github.com/rankseg/rankseg)\n", + "- [RankSEG: A Consistent Ranking-based Framework for Segmentation](https://www.jmlr.org/papers/v24/22-0712.html)\n", + "- [RankSEG-RMA: An Efficient Segmentation Algorithm via Reciprocal Moment Approximation](https://openreview.net/forum?id=4tRMm1JJhw)\n", "- [Reproducible MONAI Pancreas comparison](https://github.com/rankseg/rankseg/tree/main/experiments/monai-pr-8908-reproducibility)\n", "- [MONAI Pancreas CT DiNTS Segmentation Bundle](https://github.com/Project-MONAI/model-zoo/tree/dev/models/pancreas_ct_dints_segmentation)\n", "- [Discussion of Option B in MONAI PR #8908](https://github.com/Project-MONAI/MONAI/pull/8908#issuecomment-5077397523)" From 2c4eead6fa93d0fc9fb791c0b83c1af1862a8bc9 Mon Sep 17 00:00:00 2001 From: statmlben Date: Fri, 31 Jul 2026 16:40:56 +0800 Subject: [PATCH 3/4] Address additional review feedback for RankSEG tutorial Signed-off-by: statmlben --- modules/rankseg_integration.ipynb | 1348 +++-------------------------- 1 file changed, 134 insertions(+), 1214 deletions(-) diff --git a/modules/rankseg_integration.ipynb b/modules/rankseg_integration.ipynb index 7ce0dbf4c..74fff4458 100644 --- a/modules/rankseg_integration.ipynb +++ b/modules/rankseg_integration.ipynb @@ -65,15 +65,15 @@ "execution_count": null, "metadata": { "execution": { - "iopub.execute_input": "2026-07-27T08:23:21.500843Z", - "iopub.status.busy": "2026-07-27T08:23:21.500368Z", - "iopub.status.idle": "2026-07-27T08:23:24.891073Z", - "shell.execute_reply": "2026-07-27T08:23:24.890402Z" + "iopub.execute_input": "2026-07-31T08:35:12.417757Z", + "iopub.status.busy": "2026-07-31T08:35:12.417209Z", + "iopub.status.idle": "2026-07-31T08:35:14.658736Z", + "shell.execute_reply": "2026-07-31T08:35:14.658045Z" } }, "outputs": [], "source": [ - "%pip install -q --upgrade-strategy only-if-needed \"monai-weekly[nibabel, tqdm,matplotlib,huggingface_hub]\" rankseg\n", + "%pip install -q --upgrade-strategy only-if-needed \"monai-weekly[nibabel, tqdm,matplotlib]\" \"rankseg==0.0.5\"\n", "%matplotlib inline" ] }, @@ -89,10 +89,10 @@ "execution_count": 2, "metadata": { "execution": { - "iopub.execute_input": "2026-07-27T08:23:24.892855Z", - "iopub.status.busy": "2026-07-27T08:23:24.892728Z", - "iopub.status.idle": "2026-07-27T08:23:25.954358Z", - "shell.execute_reply": "2026-07-27T08:23:25.953827Z" + "iopub.execute_input": "2026-07-31T08:35:14.660688Z", + "iopub.status.busy": "2026-07-31T08:35:14.660543Z", + "iopub.status.idle": "2026-07-31T08:35:15.759133Z", + "shell.execute_reply": "2026-07-31T08:35:15.758449Z" } }, "outputs": [ @@ -278,14 +278,17 @@ } ], "source": [ + "import hashlib\n", "import os\n", "import shutil\n", + "import tarfile\n", "import tempfile\n", "from collections.abc import Hashable, Mapping\n", + "from pathlib import Path\n", + "from urllib.request import Request, urlopen\n", "\n", "import matplotlib.pyplot as plt\n", "import torch\n", - "from huggingface_hub import hf_hub_download\n", "from monai.bundle import ConfigParser, download\n", "from monai.config import print_config\n", "from monai.data import decollate_batch\n", @@ -328,10 +331,10 @@ "execution_count": 3, "metadata": { "execution": { - "iopub.execute_input": "2026-07-27T08:23:25.969880Z", - "iopub.status.busy": "2026-07-27T08:23:25.969769Z", - "iopub.status.idle": "2026-07-27T08:23:25.972797Z", - "shell.execute_reply": "2026-07-27T08:23:25.972374Z" + "iopub.execute_input": "2026-07-31T08:35:15.760621Z", + "iopub.status.busy": "2026-07-31T08:35:15.760514Z", + "iopub.status.idle": "2026-07-31T08:35:15.763598Z", + "shell.execute_reply": "2026-07-31T08:35:15.763149Z" } }, "outputs": [], @@ -385,10 +388,10 @@ "execution_count": 4, "metadata": { "execution": { - "iopub.execute_input": "2026-07-27T08:23:25.973963Z", - "iopub.status.busy": "2026-07-27T08:23:25.973872Z", - "iopub.status.idle": "2026-07-27T08:23:25.975986Z", - "shell.execute_reply": "2026-07-27T08:23:25.975505Z" + "iopub.execute_input": "2026-07-31T08:35:15.764617Z", + "iopub.status.busy": "2026-07-31T08:35:15.764534Z", + "iopub.status.idle": "2026-07-31T08:35:15.766794Z", + "shell.execute_reply": "2026-07-31T08:35:15.766376Z" } }, "outputs": [ @@ -396,7 +399,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "/tmp/rankseg-tutorial-data\n" + "/tmp/tmpj2w6asvp\n" ] } ], @@ -414,7 +417,7 @@ "source": [ "## Download one MSD Pancreas validation case\n", "\n", - "Task07 Pancreas contains 3D abdominal CT volumes with pancreas and pancreatic tumor labels. The Medical Segmentation Decathlon data is distributed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). To keep the tutorial download small, only `pancreas_102` and its label are fetched from a [Hugging Face dataset mirror](https://huggingface.co/datasets/MedOtter/msd-pancreas)." + "Task07 Pancreas contains 3D abdominal CT volumes with pancreas and pancreatic tumor labels. The Medical Segmentation Decathlon data is distributed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). To combine first-party provenance with a small download, the helper below uses HTTP Range requests against a fixed version of [MONAI's official MSD archive](https://msd-for-monai.s3-us-west-2.amazonaws.com/Task07_Pancreas.tar). It fetches only the tar headers and contents for `pancreas_102` and its label (about 29.7 MB total), then verifies the member names, sizes, tar checksums, and SHA-256 digests." ] }, { @@ -422,32 +425,79 @@ "execution_count": 5, "metadata": { "execution": { - "iopub.execute_input": "2026-07-27T08:23:25.977259Z", - "iopub.status.busy": "2026-07-27T08:23:25.977173Z", - "iopub.status.idle": "2026-07-27T08:23:27.022353Z", - "shell.execute_reply": "2026-07-27T08:23:27.019951Z" + "iopub.execute_input": "2026-07-31T08:35:15.767888Z", + "iopub.status.busy": "2026-07-31T08:35:15.767803Z", + "iopub.status.idle": "2026-07-31T08:36:11.191547Z", + "shell.execute_reply": "2026-07-31T08:36:11.189020Z" } }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.\n" - ] - } - ], + "outputs": [], "source": [ "case_id = \"pancreas_102\"\n", - "data_dir = os.path.join(root_dir, \"Task07_Pancreas\")\n", - "evaluation_file = {}\n", - "for key, subdirectory in ((\"image\", \"imagesTr\"), (\"label\", \"labelsTr\")):\n", - " evaluation_file[key] = hf_hub_download(\n", - " repo_id=\"MedOtter/msd-pancreas\",\n", - " repo_type=\"dataset\",\n", - " filename=f\"{subdirectory}/{case_id}.nii.gz\",\n", - " local_dir=data_dir,\n", - " )" + "data_dir = Path(root_dir) / \"Task07_Pancreas\"\n", + "msd_archive_url = (\n", + " \"https://msd-for-monai.s3-us-west-2.amazonaws.com/Task07_Pancreas.tar\"\n", + " \"?versionId=lVhRU5n2ulILHz6suffqgfgJ__KFj.3q\"\n", + ")\n", + "msd_archive_etag = '\"7b7524e869b49821724aa61235443ae1-1466\"'\n", + "# Each tuple is (relative path, tar-header offset, content size, SHA-256).\n", + "# These values are tied to the immutable S3 object version above.\n", + "msd_members = {\n", + " \"image\": (\n", + " f\"imagesTr/{case_id}.nii.gz\",\n", + " 8_795_902_464,\n", + " 29_648_519,\n", + " \"a57b50ac7d4a7053c46a801de99a17607e61d35970d1f2dc17738008690a395b\",\n", + " ),\n", + " \"label\": (\n", + " f\"labelsTr/{case_id}.nii.gz\",\n", + " 12_286_287_360,\n", + " 29_653,\n", + " \"63f3b90e305b07939bda0b8ae651934dadbaab5509a3c586d13e02a01be9bb49\",\n", + " ),\n", + "}\n", + "\n", + "\n", + "def fetch_archive_range(start, size):\n", + " request = Request(\n", + " msd_archive_url,\n", + " headers={\n", + " \"Range\": f\"bytes={start}-{start + size - 1}\",\n", + " \"If-Match\": msd_archive_etag,\n", + " },\n", + " )\n", + " with urlopen(request, timeout=120) as response:\n", + " if response.status != 206:\n", + " raise RuntimeError(f\"unexpected HTTP status for range request: {response.status}\")\n", + " content = response.read(size + 1)\n", + " if len(content) != size:\n", + " raise RuntimeError(f\"unexpected range size: expected {size}, received {len(content)}\")\n", + " return content\n", + "\n", + "\n", + "def download_msd_member(relative_path, header_offset, size, expected_sha256):\n", + " output_path = data_dir / relative_path\n", + " if output_path.exists():\n", + " digest = hashlib.sha256(output_path.read_bytes()).hexdigest()\n", + " if digest == expected_sha256:\n", + " return str(output_path)\n", + "\n", + " # Parse the tar header to validate its checksum, name, size, and type.\n", + " header = fetch_archive_range(header_offset, 512)\n", + " member = tarfile.TarInfo.frombuf(header, encoding=\"utf-8\", errors=\"surrogateescape\")\n", + " expected_name = f\"Task07_Pancreas/{relative_path}\"\n", + " if member.name != expected_name or member.size != size or not member.isfile():\n", + " raise RuntimeError(f\"official MSD tar member metadata changed for {expected_name}\")\n", + "\n", + " content = fetch_archive_range(header_offset + 512, size)\n", + " if hashlib.sha256(content).hexdigest() != expected_sha256:\n", + " raise RuntimeError(f\"SHA-256 mismatch for {expected_name}\")\n", + " output_path.parent.mkdir(parents=True, exist_ok=True)\n", + " output_path.write_bytes(content)\n", + " return str(output_path)\n", + "\n", + "\n", + "evaluation_file = {key: download_msd_member(*spec) for key, spec in msd_members.items()}" ] }, { @@ -464,10 +514,10 @@ "execution_count": 6, "metadata": { "execution": { - "iopub.execute_input": "2026-07-27T08:23:27.028713Z", - "iopub.status.busy": "2026-07-27T08:23:27.027968Z", - "iopub.status.idle": "2026-07-27T08:23:29.510024Z", - "shell.execute_reply": "2026-07-27T08:23:29.509443Z" + "iopub.execute_input": "2026-07-31T08:36:11.199101Z", + "iopub.status.busy": "2026-07-31T08:36:11.198567Z", + "iopub.status.idle": "2026-07-31T08:36:13.640380Z", + "shell.execute_reply": "2026-07-31T08:36:13.639599Z" } }, "outputs": [ @@ -522,10 +572,10 @@ "execution_count": 7, "metadata": { "execution": { - "iopub.execute_input": "2026-07-27T08:23:29.511782Z", - "iopub.status.busy": "2026-07-27T08:23:29.511593Z", - "iopub.status.idle": "2026-07-27T08:23:30.777960Z", - "shell.execute_reply": "2026-07-27T08:23:30.777473Z" + "iopub.execute_input": "2026-07-31T08:36:13.642004Z", + "iopub.status.busy": "2026-07-31T08:36:13.641769Z", + "iopub.status.idle": "2026-07-31T08:36:27.905632Z", + "shell.execute_reply": "2026-07-31T08:36:27.904976Z" } }, "outputs": [ @@ -533,107 +583,65 @@ "name": "stdout", "output_type": "stream", "text": [ - "2026-07-27 16:23:29,636 - INFO - --- input summary of monai.bundle.scripts.download ---\n" + "2026-07-31 16:36:13,766 - INFO - --- input summary of monai.bundle.scripts.download ---\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "2026-07-27 16:23:29,637 - INFO - > name: 'pancreas_ct_dints_segmentation'\n" + "2026-07-31 16:36:13,766 - INFO - > name: 'pancreas_ct_dints_segmentation'\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "2026-07-27 16:23:29,637 - INFO - > version: '0.5.2'\n" + "2026-07-31 16:36:13,767 - INFO - > version: '0.5.2'\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "2026-07-27 16:23:29,637 - INFO - > bundle_dir: '/tmp/rankseg-tutorial-data'\n" + "2026-07-31 16:36:13,767 - INFO - > bundle_dir: '/tmp/tmpj2w6asvp'\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "2026-07-27 16:23:29,638 - INFO - > source: 'monaihosting'\n" + "2026-07-31 16:36:13,767 - INFO - > source: 'monaihosting'\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "2026-07-27 16:23:29,638 - INFO - > remove_prefix: 'monai_'\n" + "2026-07-31 16:36:13,767 - INFO - > remove_prefix: 'monai_'\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "2026-07-27 16:23:29,638 - INFO - > progress: True\n" + "2026-07-31 16:36:13,767 - INFO - > progress: True\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "2026-07-27 16:23:29,638 - INFO - ---\n", + "2026-07-31 16:36:13,767 - INFO - ---\n", "\n", "\n" ] }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "1a293638ddfa4444a23247f1734217f1", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Downloading bytes: | 0.00B " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "5986bd8bdf9a456d8bad76054e0ad0c9", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Reconstructing (incomplete total...): | | 0.00B / 0.00B " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "63ea827a055743caaba92078c8417697", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Fetching 18 files: 0%| | 0/18 [00:00 Date: Fri, 31 Jul 2026 17:20:28 +0800 Subject: [PATCH 4/4] Format RankSEG notebook with Black Signed-off-by: statmlben --- modules/rankseg_integration.ipynb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/rankseg_integration.ipynb b/modules/rankseg_integration.ipynb index 74fff4458..d6d4b202d 100644 --- a/modules/rankseg_integration.ipynb +++ b/modules/rankseg_integration.ipynb @@ -436,8 +436,7 @@ "case_id = \"pancreas_102\"\n", "data_dir = Path(root_dir) / \"Task07_Pancreas\"\n", "msd_archive_url = (\n", - " \"https://msd-for-monai.s3-us-west-2.amazonaws.com/Task07_Pancreas.tar\"\n", - " \"?versionId=lVhRU5n2ulILHz6suffqgfgJ__KFj.3q\"\n", + " \"https://msd-for-monai.s3-us-west-2.amazonaws.com/Task07_Pancreas.tar\" \"?versionId=lVhRU5n2ulILHz6suffqgfgJ__KFj.3q\"\n", ")\n", "msd_archive_etag = '\"7b7524e869b49821724aa61235443ae1-1466\"'\n", "# Each tuple is (relative path, tar-header offset, content size, SHA-256).\n",