diff --git a/.gitattributes b/.gitattributes index a6344aac8c09253b3b630fb776ae94478aa0275b..5b67d2e2babff571a797ab09283f2ee11e77fc30 100644 --- a/.gitattributes +++ b/.gitattributes @@ -33,3 +33,41 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text *.zst filter=lfs diff=lfs merge=lfs -text *tfevents* filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/affine.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/amp_training_a100.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/amp_training_v100.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/arch_modules.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/auto3dseg.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/brats_distributed.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/BTCV_organs.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/cache_dataset.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/coplenet.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/deepedit.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/deepgrow_scheme.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/detection.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/dints-overview.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/fast_training.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/gmm_feature_set_comparison_s.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/invert_transforms.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/maisi_infer.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/maisi_train.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/matshow3d.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/medical_transforms.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/metrics_report.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/MONAI_bundle_cloud.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/MONAI_map_cloud.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/nsight_comparison.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/nuclick.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/pathology-meta.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/pathology.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/postprocessing_transforms.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/sliding_window.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/ssl_overview.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/swin_unetr.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/tta.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/unet-pipe.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/UNETR.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/vista2d.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/docs/images/workflows.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/tests/testing_data/ultrasound_confidence_map/femur_input.png filter=lfs diff=lfs merge=lfs -text +MONAI/source/tests/testing_data/ultrasound_confidence_map/neck_input.png filter=lfs diff=lfs merge=lfs -text diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..2ad46215f4445d57eb7e2df59cbc93091d6d1a7f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.10 + +RUN useradd -m -u 1000 user && python -m pip install --upgrade pip +USER user +ENV PATH="/home/user/.local/bin:$PATH" + +WORKDIR /app + +COPY --chown=user ./requirements.txt requirements.txt +RUN pip install --no-cache-dir --upgrade -r requirements.txt + +COPY --chown=user . /app +ENV MCP_TRANSPORT=http +ENV MCP_PORT=7860 + +EXPOSE 7860 + +CMD ["python", "MONAI/mcp_output/start_mcp.py"] diff --git a/MONAI/mcp_output/README_MCP.md b/MONAI/mcp_output/README_MCP.md new file mode 100644 index 0000000000000000000000000000000000000000..82bf3394d85098fbed742354d293f630cda8d158 --- /dev/null +++ b/MONAI/mcp_output/README_MCP.md @@ -0,0 +1,51 @@ +# MONAI: Medical Open Network for AI + +## Project Introduction + +MONAI (Medical Open Network for AI) is an open-source, PyTorch-based framework designed to facilitate deep learning in healthcare imaging. It provides domain-specific implementations for medical image analysis tasks such as segmentation, classification, detection, and registration. MONAI aims to support researchers and developers by offering optimized and standardized tools for deep learning model development in medical imaging. + +## Installation Method + +To install MONAI, ensure that you have Python and pip installed. MONAI requires several dependencies, including `numpy`, `torch`, and `nibabel`. Optional dependencies include `matplotlib` and `scipy` for additional functionalities. + +To install MONAI, use the following pip command: + +``` +pip install monai +``` + +Ensure that your environment meets the necessary requirements for PyTorch and other dependencies. + +## Quick Start + +Here's a quick example of how to use MONAI for a simple medical imaging task: + +1. Import the necessary modules from MONAI. +2. Load your medical imaging data using MONAI's data handling utilities. +3. Apply preprocessing transforms using MONAI's transform system. +4. Define and train a neural network using MONAI's network architectures and training engines. + +For detailed examples and tutorials, refer to the [MONAI documentation](https://docs.monai.io). + +## Available Tools and Endpoints List + +- **Auto3DSeg**: Modules for automated 3D segmentation tasks, including `AutoRunner`, `BundleGen`, and `DataAnalyzer`. +- **Transforms**: Compose multiple transforms together using `Compose` and `SomeOf`. +- **Networks**: Network architectures for medical imaging, such as `UNet` and `ResNet`. +- **Bundle System**: CLI for managing MONAI bundles, accessible via `monai-bundle`. + +## Common Issues and Notes + +- **Dependencies**: Ensure all required dependencies are installed. Use the provided `requirements.txt` for guidance. +- **Environment**: MONAI is designed to work with PyTorch, so ensure your environment is compatible with the PyTorch version you are using. +- **Performance**: For optimal performance, consider using a GPU-enabled environment, as MONAI supports multi-GPU and multi-node data parallelism. + +## Reference Links or Documentation + +For more detailed information, tutorials, and API references, please visit the following resources: + +- [MONAI GitHub Repository](https://github.com/Project-MONAI/MONAI) +- [MONAI Documentation](https://docs.monai.io) +- [MONAI Tutorials](https://github.com/Project-MONAI/tutorials) + +For any issues or contributions, please refer to the [CONTRIBUTING.md](https://github.com/Project-MONAI/MONAI/blob/main/CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](https://github.com/Project-MONAI/MONAI/blob/main/CODE_OF_CONDUCT.md) files in the repository. \ No newline at end of file diff --git a/MONAI/mcp_output/analysis.json b/MONAI/mcp_output/analysis.json new file mode 100644 index 0000000000000000000000000000000000000000..11bbb73f5082d053356866df82819c37258c270a --- /dev/null +++ b/MONAI/mcp_output/analysis.json @@ -0,0 +1,4222 @@ +{ + "summary": { + "repository_url": "https://github.com/Project-MONAI/MONAI", + "summary": "Imported via zip fallback, file count: 1357", + "file_tree": { + ".coderabbit.yaml": { + "size": 2404 + }, + ".deepsource.toml": { + "size": 349 + }, + ".github/ISSUE_TEMPLATE/bug_report.md": { + "size": 681 + }, + ".github/ISSUE_TEMPLATE/feature_request.md": { + "size": 595 + }, + ".github/ISSUE_TEMPLATE/question.md": { + "size": 487 + }, + ".github/codecov.yml": { + "size": 811 + }, + ".github/dco.yml": { + "size": 44 + }, + ".github/dependabot.yml": { + "size": 217 + }, + ".github/pull_request_template.md": { + "size": 733 + }, + ".github/workflows/blossom-ci.yml": { + "size": 3364 + }, + ".github/workflows/chatops.yml": { + "size": 1001 + }, + ".github/workflows/codeql-analysis.yml": { + "size": 2578 + }, + ".github/workflows/conda.yml": { + "size": 2357 + }, + ".github/workflows/cron-ngc-bundle.yml": { + "size": 1332 + }, + ".github/workflows/cron.yml": { + "size": 11223 + }, + ".github/workflows/docker.yml": { + "size": 3472 + }, + ".github/workflows/integration.yml": { + "size": 6170 + }, + ".github/workflows/pythonapp-gpu.yml": { + "size": 5443 + }, + ".github/workflows/pythonapp-min.yml": { + "size": 5588 + }, + ".github/workflows/pythonapp.yml": { + "size": 8332 + }, + ".github/workflows/release.yml": { + "size": 5400 + }, + ".github/workflows/setupapp.yml": { + "size": 6148 + }, + ".github/workflows/weekly-preview.yml": { + "size": 2525 + }, + ".pre-commit-config.yaml": { + "size": 1980 + }, + ".readthedocs.yml": { + "size": 626 + }, + "CHANGELOG.md": { + "size": 71172 + }, + "CODE_OF_CONDUCT.md": { + "size": 3355 + }, + "CONTRIBUTING.md": { + "size": 23855 + }, + "README.md": { + "size": 6574 + }, + "SECURITY.md": { + "size": 1161 + }, + "docs/.readthedocs.yaml": { + "size": 276 + }, + "docs/requirements.txt": { + "size": 915 + }, + "docs/source/applications.md": { + "size": 7384 + }, + "docs/source/conf.py": { + "size": 8035 + }, + "docs/source/config_syntax.md": { + "size": 10106 + }, + "docs/source/installation.md": { + "size": 9669 + }, + "docs/source/modules.md": { + "size": 21774 + }, + "docs/source/precision_accelerating.md": { + "size": 3261 + }, + "docs/source/whatsnew_0_5.md": { + "size": 6923 + }, + "docs/source/whatsnew_0_6.md": { + "size": 8275 + }, + "docs/source/whatsnew_0_7.md": { + "size": 3660 + }, + "docs/source/whatsnew_0_8.md": { + "size": 3138 + }, + "docs/source/whatsnew_0_9.md": { + "size": 3379 + }, + "docs/source/whatsnew_1_0.md": { + "size": 4652 + }, + "docs/source/whatsnew_1_1.md": { + "size": 5103 + }, + "docs/source/whatsnew_1_2.md": { + "size": 5467 + }, + "docs/source/whatsnew_1_3.md": { + "size": 1314 + }, + "docs/source/whatsnew_1_4.md": { + "size": 6015 + }, + "docs/source/whatsnew_1_5.md": { + "size": 4939 + }, + "docs/source/whatsnew_1_5_1.md": { + "size": 934 + }, + "docs/source/whatsnew_1_5_2.md": { + "size": 230 + }, + "environment-dev.yml": { + "size": 226 + }, + "monai/README.md": { + "size": 1520 + }, + "monai/__init__.py": { + "size": 4036 + }, + "monai/_extensions/__init__.py": { + "size": 642 + }, + "monai/_extensions/loader.py": { + "size": 3643 + }, + "monai/_version.py": { + "size": 23661 + }, + "monai/apps/__init__.py": { + "size": 908 + }, + "monai/apps/auto3dseg/__init__.py": { + "size": 1016 + }, + "monai/apps/auto3dseg/__main__.py": { + "size": 1411 + }, + "monai/apps/auto3dseg/auto_runner.py": { + "size": 40641 + }, + "monai/apps/auto3dseg/bundle_gen.py": { + "size": 28956 + }, + "monai/apps/auto3dseg/data_analyzer.py": { + "size": 18628 + }, + "monai/apps/auto3dseg/ensemble_builder.py": { + "size": 27277 + }, + "monai/apps/auto3dseg/hpo_gen.py": { + "size": 16467 + }, + "monai/apps/auto3dseg/transforms.py": { + "size": 3856 + }, + "monai/apps/auto3dseg/utils.py": { + "size": 3138 + }, + "monai/apps/datasets.py": { + "size": 35085 + }, + "monai/apps/deepedit/__init__.py": { + "size": 573 + }, + "monai/apps/deepedit/interaction.py": { + "size": 4498 + }, + "monai/apps/deepedit/transforms.py": { + "size": 38108 + }, + "monai/apps/deepgrow/__init__.py": { + "size": 573 + }, + "monai/apps/deepgrow/dataset.py": { + "size": 10054 + }, + "monai/apps/deepgrow/interaction.py": { + "size": 3745 + }, + "monai/apps/deepgrow/transforms.py": { + "size": 43329 + }, + "monai/apps/detection/__init__.py": { + "size": 573 + }, + "monai/apps/detection/metrics/__init__.py": { + "size": 573 + }, + "monai/apps/detection/metrics/coco.py": { + "size": 26583 + }, + "monai/apps/detection/metrics/matching.py": { + "size": 17161 + }, + "monai/apps/detection/networks/__init__.py": { + "size": 573 + }, + "monai/apps/detection/networks/retinanet_detector.py": { + "size": 53664 + }, + "monai/apps/detection/networks/retinanet_network.py": { + "size": 19170 + }, + "monai/apps/detection/transforms/__init__.py": { + "size": 573 + }, + "monai/apps/detection/transforms/array.py": { + "size": 24546 + }, + "monai/apps/detection/transforms/box_ops.py": { + "size": 18035 + }, + "monai/apps/detection/transforms/dictionary.py": { + "size": 69282 + }, + "monai/apps/detection/utils/ATSS_matcher.py": { + "size": 13532 + }, + "monai/apps/detection/utils/__init__.py": { + "size": 573 + }, + "monai/apps/detection/utils/anchor_utils.py": { + "size": 18718 + }, + "monai/apps/detection/utils/box_coder.py": { + "size": 11239 + }, + "monai/apps/detection/utils/box_selector.py": { + "size": 9031 + }, + "monai/apps/detection/utils/detector_utils.py": { + "size": 10306 + }, + "monai/apps/detection/utils/hard_negative_sampler.py": { + "size": 13890 + }, + "monai/apps/detection/utils/predict_utils.py": { + "size": 5818 + }, + "monai/apps/generation/__init__.py": { + "size": 573 + }, + "monai/apps/generation/maisi/__init__.py": { + "size": 573 + }, + "monai/apps/generation/maisi/networks/__init__.py": { + "size": 573 + }, + "monai/apps/generation/maisi/networks/autoencoderkl_maisi.py": { + "size": 36788 + }, + "monai/apps/generation/maisi/networks/controlnet_maisi.py": { + "size": 7707 + }, + "monai/apps/generation/maisi/networks/diffusion_model_unet_maisi.py": { + "size": 19088 + }, + "monai/apps/mmars/__init__.py": { + "size": 726 + }, + "monai/apps/mmars/mmars.py": { + "size": 13134 + }, + "monai/apps/mmars/model_desc.py": { + "size": 9996 + }, + "monai/apps/nnunet/__init__.py": { + "size": 963 + }, + "monai/apps/nnunet/__main__.py": { + "size": 832 + }, + "monai/apps/nnunet/nnunet_bundle.py": { + "size": 25012 + }, + "monai/apps/nnunet/nnunetv2_runner.py": { + "size": 48001 + }, + "monai/apps/nnunet/utils.py": { + "size": 6761 + }, + "monai/apps/nuclick/__init__.py": { + "size": 573 + }, + "monai/apps/nuclick/transforms.py": { + "size": 24937 + }, + "monai/apps/pathology/__init__.py": { + "size": 1030 + }, + "monai/apps/pathology/engines/__init__.py": { + "size": 650 + }, + "monai/apps/pathology/engines/utils.py": { + "size": 2424 + }, + "monai/apps/pathology/handlers/__init__.py": { + "size": 609 + }, + "monai/apps/pathology/handlers/utils.py": { + "size": 2315 + }, + "monai/apps/pathology/inferers/__init__.py": { + "size": 660 + }, + "monai/apps/pathology/inferers/inferer.py": { + "size": 9194 + }, + "monai/apps/pathology/losses/__init__.py": { + "size": 650 + }, + "monai/apps/pathology/losses/hovernet_loss.py": { + "size": 7293 + }, + "monai/apps/pathology/metrics/__init__.py": { + "size": 646 + }, + "monai/apps/pathology/metrics/lesion_froc.py": { + "size": 7358 + }, + "monai/apps/pathology/transforms/__init__.py": { + "size": 2243 + }, + "monai/apps/pathology/transforms/post/__init__.py": { + "size": 1995 + }, + "monai/apps/pathology/transforms/post/array.py": { + "size": 37444 + }, + "monai/apps/pathology/transforms/post/dictionary.py": { + "size": 25928 + }, + "monai/apps/pathology/transforms/stain/__init__.py": { + "size": 836 + }, + "monai/apps/pathology/transforms/stain/array.py": { + "size": 8366 + }, + "monai/apps/pathology/transforms/stain/dictionary.py": { + "size": 4761 + }, + "monai/apps/pathology/utils.py": { + "size": 2838 + }, + "monai/apps/reconstruction/__init__.py": { + "size": 573 + }, + "monai/apps/reconstruction/complex_utils.py": { + "size": 8393 + }, + "monai/apps/reconstruction/fastmri_reader.py": { + "size": 3644 + }, + "monai/apps/reconstruction/mri_utils.py": { + "size": 2000 + }, + "monai/apps/reconstruction/networks/__init__.py": { + "size": 573 + }, + "monai/apps/reconstruction/networks/blocks/__init__.py": { + "size": 573 + }, + "monai/apps/reconstruction/networks/blocks/varnetblock.py": { + "size": 4183 + }, + "monai/apps/reconstruction/networks/nets/__init__.py": { + "size": 573 + }, + "monai/apps/reconstruction/networks/nets/coil_sensitivity_model.py": { + "size": 6215 + }, + "monai/apps/reconstruction/networks/nets/complex_unet.py": { + "size": 4775 + }, + "monai/apps/reconstruction/networks/nets/utils.py": { + "size": 11377 + }, + "monai/apps/reconstruction/networks/nets/varnet.py": { + "size": 3831 + }, + "monai/apps/reconstruction/transforms/__init__.py": { + "size": 573 + }, + "monai/apps/reconstruction/transforms/array.py": { + "size": 12240 + }, + "monai/apps/reconstruction/transforms/dictionary.py": { + "size": 15829 + }, + "monai/apps/tcia/__init__.py": { + "size": 824 + }, + "monai/apps/tcia/label_desc.py": { + "size": 1582 + }, + "monai/apps/tcia/utils.py": { + "size": 6312 + }, + "monai/apps/utils.py": { + "size": 18299 + }, + "monai/apps/vista3d/__init__.py": { + "size": 573 + }, + "monai/apps/vista3d/inferer.py": { + "size": 8712 + }, + "monai/apps/vista3d/sampler.py": { + "size": 8274 + }, + "monai/apps/vista3d/transforms.py": { + "size": 10650 + }, + "monai/auto3dseg/__init__.py": { + "size": 1164 + }, + "monai/auto3dseg/algo_gen.py": { + "size": 4286 + }, + "monai/auto3dseg/analyzer.py": { + "size": 42168 + }, + "monai/auto3dseg/operations.py": { + "size": 5110 + }, + "monai/auto3dseg/seg_summarizer.py": { + "size": 8717 + }, + "monai/auto3dseg/utils.py": { + "size": 18674 + }, + "monai/bundle/__init__.py": { + "size": 1461 + }, + "monai/bundle/__main__.py": { + "size": 952 + }, + "monai/bundle/config_item.py": { + "size": 16151 + }, + "monai/bundle/config_parser.py": { + "size": 23025 + }, + "monai/bundle/properties.py": { + "size": 11582 + }, + "monai/bundle/reference_resolver.py": { + "size": 16748 + }, + "monai/bundle/scripts.py": { + "size": 89515 + }, + "monai/bundle/utils.py": { + "size": 10108 + }, + "monai/bundle/workflows.py": { + "size": 33200 + }, + "monai/config/__init__.py": { + "size": 1048 + }, + "monai/config/deviceconfig.py": { + "size": 10582 + }, + "monai/config/type_definitions.py": { + "size": 3512 + }, + "monai/data/__init__.py": { + "size": 5144 + }, + "monai/data/box_utils.py": { + "size": 50176 + }, + "monai/data/csv_saver.py": { + "size": 4952 + }, + "monai/data/dataloader.py": { + "size": 4459 + }, + "monai/data/dataset.py": { + "size": 79920 + }, + "monai/data/dataset_summary.py": { + "size": 10243 + }, + "monai/data/decathlon_datalist.py": { + "size": 12023 + }, + "monai/data/fft_utils.py": { + "size": 4448 + }, + "monai/data/folder_layout.py": { + "size": 6344 + }, + "monai/data/grid_dataset.py": { + "size": 19483 + }, + "monai/data/image_dataset.py": { + "size": 7013 + }, + "monai/data/image_reader.py": { + "size": 70922 + }, + "monai/data/image_writer.py": { + "size": 39856 + }, + "monai/data/iterable_dataset.py": { + "size": 13100 + }, + "monai/data/itk_torch_bridge.py": { + "size": 14461 + }, + "monai/data/meta_obj.py": { + "size": 8827 + }, + "monai/data/meta_tensor.py": { + "size": 27782 + }, + "monai/data/samplers.py": { + "size": 5102 + }, + "monai/data/synthetic.py": { + "size": 7375 + }, + "monai/data/test_time_augmentation.py": { + "size": 9871 + }, + "monai/data/thread_buffer.py": { + "size": 8840 + }, + "monai/data/torchscript_utils.py": { + "size": 5502 + }, + "monai/data/ultrasound_confidence_map.py": { + "size": 14464 + }, + "monai/data/utils.py": { + "size": 65253 + }, + "monai/data/video_dataset.py": { + "size": 9105 + }, + "monai/data/wsi_datasets.py": { + "size": 18659 + }, + "monai/data/wsi_reader.py": { + "size": 49508 + }, + "monai/engines/__init__.py": { + "size": 1094 + }, + "monai/engines/evaluator.py": { + "size": 26673 + }, + "monai/engines/trainer.py": { + "size": 38129 + }, + "monai/engines/utils.py": { + "size": 15658 + }, + "monai/engines/workflow.py": { + "size": 15483 + }, + "monai/fl/__init__.py": { + "size": 573 + }, + "monai/fl/client/__init__.py": { + "size": 725 + }, + "monai/fl/client/client_algo.py": { + "size": 5098 + }, + "monai/fl/client/monai_algo.py": { + "size": 34080 + }, + "monai/fl/utils/__init__.py": { + "size": 573 + }, + "monai/fl/utils/constants.py": { + "size": 1784 + }, + "monai/fl/utils/exchange_object.py": { + "size": 3527 + }, + "monai/fl/utils/filters.py": { + "size": 1633 + }, + "monai/handlers/__init__.py": { + "size": 2442 + }, + "monai/handlers/average_precision.py": { + "size": 2753 + }, + "monai/handlers/checkpoint_loader.py": { + "size": 7449 + }, + "monai/handlers/checkpoint_saver.py": { + "size": 16047 + }, + "monai/handlers/classification_saver.py": { + "size": 7605 + }, + "monai/handlers/clearml_handlers.py": { + "size": 7545 + }, + "monai/handlers/confusion_matrix.py": { + "size": 4006 + }, + "monai/handlers/decollate_batch.py": { + "size": 4425 + }, + "monai/handlers/earlystop_handler.py": { + "size": 5308 + }, + "monai/handlers/garbage_collector.py": { + "size": 3621 + }, + "monai/handlers/hausdorff_distance.py": { + "size": 3594 + }, + "monai/handlers/ignite_metric.py": { + "size": 6691 + }, + "monai/handlers/logfile_handler.py": { + "size": 3907 + }, + "monai/handlers/lr_schedule_handler.py": { + "size": 3551 + }, + "monai/handlers/mean_dice.py": { + "size": 3785 + }, + "monai/handlers/mean_iou.py": { + "size": 2841 + }, + "monai/handlers/metric_logger.py": { + "size": 5453 + }, + "monai/handlers/metrics_reloaded_handler.py": { + "size": 6195 + }, + "monai/handlers/metrics_saver.py": { + "size": 8559 + }, + "monai/handlers/mlflow_handler.py": { + "size": 23259 + }, + "monai/handlers/nvtx_handlers.py": { + "size": 6795 + }, + "monai/handlers/panoptic_quality.py": { + "size": 3651 + }, + "monai/handlers/parameter_scheduler.py": { + "size": 7095 + }, + "monai/handlers/postprocessing.py": { + "size": 3261 + }, + "monai/handlers/probability_maps.py": { + "size": 5336 + }, + "monai/handlers/regression_metrics.py": { + "size": 8457 + }, + "monai/handlers/roc_auc.py": { + "size": 2744 + }, + "monai/handlers/smartcache_handler.py": { + "size": 3027 + }, + "monai/handlers/stats_handler.py": { + "size": 14144 + }, + "monai/handlers/surface_distance.py": { + "size": 3327 + }, + "monai/handlers/tensorboard_handlers.py": { + "size": 22591 + }, + "monai/handlers/trt_handler.py": { + "size": 2329 + }, + "monai/handlers/utils.py": { + "size": 10239 + }, + "monai/handlers/validation_handler.py": { + "size": 3674 + }, + "monai/inferers/__init__.py": { + "size": 1107 + }, + "monai/inferers/inferer.py": { + "size": 102436 + }, + "monai/inferers/merger.py": { + "size": 21575 + }, + "monai/inferers/splitter.py": { + "size": 21149 + }, + "monai/inferers/utils.py": { + "size": 20904 + }, + "monai/losses/__init__.py": { + "size": 1778 + }, + "monai/losses/adversarial_loss.py": { + "size": 7722 + }, + "monai/losses/barlow_twins.py": { + "size": 3613 + }, + "monai/losses/cldice.py": { + "size": 6328 + }, + "monai/losses/contrastive.py": { + "size": 3261 + }, + "monai/losses/deform.py": { + "size": 9701 + }, + "monai/losses/dice.py": { + "size": 51496 + }, + "monai/losses/ds_loss.py": { + "size": 3776 + }, + "monai/losses/focal_loss.py": { + "size": 11772 + }, + "monai/losses/giou_loss.py": { + "size": 2795 + }, + "monai/losses/hausdorff_loss.py": { + "size": 10676 + }, + "monai/losses/image_dissimilarity.py": { + "size": 15460 + }, + "monai/losses/multi_scale.py": { + "size": 3636 + }, + "monai/losses/nacl_loss.py": { + "size": 5052 + }, + "monai/losses/perceptual.py": { + "size": 19521 + }, + "monai/losses/spatial_mask.py": { + "size": 2955 + }, + "monai/losses/spectral_loss.py": { + "size": 3368 + }, + "monai/losses/ssim_loss.py": { + "size": 5058 + }, + "monai/losses/sure_loss.py": { + "size": 8195 + }, + "monai/losses/tversky.py": { + "size": 6955 + }, + "monai/losses/unified_focal_loss.py": { + "size": 10224 + }, + "monai/losses/utils.py": { + "size": 2786 + }, + "monai/metrics/__init__.py": { + "size": 2219 + }, + "monai/metrics/active_learning_metrics.py": { + "size": 8211 + }, + "monai/metrics/average_precision.py": { + "size": 8454 + }, + "monai/metrics/confusion_matrix.py": { + "size": 15064 + }, + "monai/metrics/cumulative_average.py": { + "size": 5636 + }, + "monai/metrics/f_beta_score.py": { + "size": 3984 + }, + "monai/metrics/fid.py": { + "size": 4795 + }, + "monai/metrics/froc.py": { + "size": 7981 + }, + "monai/metrics/generalized_dice.py": { + "size": 8661 + }, + "monai/metrics/hausdorff_distance.py": { + "size": 10778 + }, + "monai/metrics/loss_metric.py": { + "size": 4907 + }, + "monai/metrics/meandice.py": { + "size": 16237 + }, + "monai/metrics/meaniou.py": { + "size": 7004 + }, + "monai/metrics/metric.py": { + "size": 15203 + }, + "monai/metrics/mmd.py": { + "size": 3299 + }, + "monai/metrics/panoptic_quality.py": { + "size": 13707 + }, + "monai/metrics/regression.py": { + "size": 26218 + }, + "monai/metrics/rocauc.py": { + "size": 8094 + }, + "monai/metrics/surface_dice.py": { + "size": 15149 + }, + "monai/metrics/surface_distance.py": { + "size": 9727 + }, + "monai/metrics/utils.py": { + "size": 46858 + }, + "monai/metrics/wrapper.py": { + "size": 11781 + }, + "monai/networks/__init__.py": { + "size": 1086 + }, + "monai/networks/blocks/__init__.py": { + "size": 2481 + }, + "monai/networks/blocks/acti_norm.py": { + "size": 4275 + }, + "monai/networks/blocks/activation.py": { + "size": 5839 + }, + "monai/networks/blocks/aspp.py": { + "size": 4380 + }, + "monai/networks/blocks/attention_utils.py": { + "size": 4951 + }, + "monai/networks/blocks/backbone_fpn_utils.py": { + "size": 7488 + }, + "monai/networks/blocks/cablock.py": { + "size": 6914 + }, + "monai/networks/blocks/convolutions.py": { + "size": 11686 + }, + "monai/networks/blocks/crf.py": { + "size": 5009 + }, + "monai/networks/blocks/crossattention.py": { + "size": 8348 + }, + "monai/networks/blocks/denseblock.py": { + "size": 4747 + }, + "monai/networks/blocks/dints_block.py": { + "size": 9255 + }, + "monai/networks/blocks/downsample.py": { + "size": 13493 + }, + "monai/networks/blocks/dynunet_block.py": { + "size": 11063 + }, + "monai/networks/blocks/encoder.py": { + "size": 3669 + }, + "monai/networks/blocks/fcn.py": { + "size": 9094 + }, + "monai/networks/blocks/feature_pyramid_network.py": { + "size": 10631 + }, + "monai/networks/blocks/fft_utils_t.py": { + "size": 8017 + }, + "monai/networks/blocks/localnet_block.py": { + "size": 11456 + }, + "monai/networks/blocks/mednext_block.py": { + "size": 10487 + }, + "monai/networks/blocks/mlp.py": { + "size": 3341 + }, + "monai/networks/blocks/patchembedding.py": { + "size": 8956 + }, + "monai/networks/blocks/pos_embed_utils.py": { + "size": 4040 + }, + "monai/networks/blocks/regunet_block.py": { + "size": 8825 + }, + "monai/networks/blocks/rel_pos_embedding.py": { + "size": 2208 + }, + "monai/networks/blocks/segresnet_block.py": { + "size": 3339 + }, + "monai/networks/blocks/selfattention.py": { + "size": 9689 + }, + "monai/networks/blocks/spade_norm.py": { + "size": 3654 + }, + "monai/networks/blocks/spatialattention.py": { + "size": 3442 + }, + "monai/networks/blocks/squeeze_and_excitation.py": { + "size": 12752 + }, + "monai/networks/blocks/text_embedding.py": { + "size": 3814 + }, + "monai/networks/blocks/transformerblock.py": { + "size": 3957 + }, + "monai/networks/blocks/unetr_block.py": { + "size": 9049 + }, + "monai/networks/blocks/upsample.py": { + "size": 13523 + }, + "monai/networks/blocks/warp.py": { + "size": 7255 + }, + "monai/networks/layers/__init__.py": { + "size": 1689 + }, + "monai/networks/layers/conjugate_gradient.py": { + "size": 3717 + }, + "monai/networks/layers/convutils.py": { + "size": 8288 + }, + "monai/networks/layers/drop_path.py": { + "size": 1802 + }, + "monai/networks/layers/factories.py": { + "size": 15795 + }, + "monai/networks/layers/filtering.py": { + "size": 18215 + }, + "monai/networks/layers/gmm.py": { + "size": 3325 + }, + "monai/networks/layers/simplelayers.py": { + "size": 27967 + }, + "monai/networks/layers/spatial_transforms.py": { + "size": 25581 + }, + "monai/networks/layers/utils.py": { + "size": 4916 + }, + "monai/networks/layers/vector_quantizer.py": { + "size": 10056 + }, + "monai/networks/layers/weight_init.py": { + "size": 2253 + }, + "monai/networks/nets/__init__.py": { + "size": 4209 + }, + "monai/networks/nets/ahnet.py": { + "size": 21570 + }, + "monai/networks/nets/attentionunet.py": { + "size": 9427 + }, + "monai/networks/nets/autoencoder.py": { + "size": 12586 + }, + "monai/networks/nets/autoencoderkl.py": { + "size": 28566 + }, + "monai/networks/nets/basic_unet.py": { + "size": 10947 + }, + "monai/networks/nets/basic_unetplusplus.py": { + "size": 7961 + }, + "monai/networks/nets/cell_sam_wrapper.py": { + "size": 3326 + }, + "monai/networks/nets/classifier.py": { + "size": 6293 + }, + "monai/networks/nets/controlnet.py": { + "size": 18908 + }, + "monai/networks/nets/daf3d.py": { + "size": 23963 + }, + "monai/networks/nets/densenet.py": { + "size": 15823 + }, + "monai/networks/nets/diffusion_model_unet.py": { + "size": 82100 + }, + "monai/networks/nets/dints.py": { + "size": 44830 + }, + "monai/networks/nets/dynunet.py": { + "size": 18210 + }, + "monai/networks/nets/efficientnet.py": { + "size": 40787 + }, + "monai/networks/nets/flexible_unet.py": { + "size": 14435 + }, + "monai/networks/nets/fullyconnectednet.py": { + "size": 7212 + }, + "monai/networks/nets/generator.py": { + "size": 6581 + }, + "monai/networks/nets/highresnet.py": { + "size": 8883 + }, + "monai/networks/nets/hovernet.py": { + "size": 28734 + }, + "monai/networks/nets/masked_autoencoder_vit.py": { + "size": 9610 + }, + "monai/networks/nets/mednext.py": { + "size": 13258 + }, + "monai/networks/nets/milmodel.py": { + "size": 9873 + }, + "monai/networks/nets/netadapter.py": { + "size": 6102 + }, + "monai/networks/nets/patchgan_discriminator.py": { + "size": 8620 + }, + "monai/networks/nets/quicknat.py": { + "size": 20463 + }, + "monai/networks/nets/regressor.py": { + "size": 6482 + }, + "monai/networks/nets/regunet.py": { + "size": 18658 + }, + "monai/networks/nets/resnet.py": { + "size": 28179 + }, + "monai/networks/nets/restormer.py": { + "size": 12897 + }, + "monai/networks/nets/segresnet.py": { + "size": 13994 + }, + "monai/networks/nets/segresnet_ds.py": { + "size": 20785 + }, + "monai/networks/nets/senet.py": { + "size": 19308 + }, + "monai/networks/nets/spade_autoencoderkl.py": { + "size": 19566 + }, + "monai/networks/nets/spade_diffusion_model_unet.py": { + "size": 39023 + }, + "monai/networks/nets/spade_network.py": { + "size": 16479 + }, + "monai/networks/nets/swin_unetr.py": { + "size": 44807 + }, + "monai/networks/nets/torchvision_fc.py": { + "size": 6256 + }, + "monai/networks/nets/transchex.py": { + "size": 15867 + }, + "monai/networks/nets/transformer.py": { + "size": 6395 + }, + "monai/networks/nets/unet.py": { + "size": 13543 + }, + "monai/networks/nets/unetr.py": { + "size": 8252 + }, + "monai/networks/nets/varautoencoder.py": { + "size": 6282 + }, + "monai/networks/nets/vista3d.py": { + "size": 43496 + }, + "monai/networks/nets/vit.py": { + "size": 5934 + }, + "monai/networks/nets/vitautoenc.py": { + "size": 5740 + }, + "monai/networks/nets/vnet.py": { + "size": 10820 + }, + "monai/networks/nets/voxelmorph.py": { + "size": 20665 + }, + "monai/networks/nets/vqvae.py": { + "size": 18417 + }, + "monai/networks/schedulers/__init__.py": { + "size": 798 + }, + "monai/networks/schedulers/ddim.py": { + "size": 14362 + }, + "monai/networks/schedulers/ddpm.py": { + "size": 11413 + }, + "monai/networks/schedulers/pndm.py": { + "size": 14433 + }, + "monai/networks/schedulers/rectified_flow.py": { + "size": 13757 + }, + "monai/networks/schedulers/scheduler.py": { + "size": 9272 + }, + "monai/networks/trt_compiler.py": { + "size": 27535 + }, + "monai/networks/utils.py": { + "size": 58226 + }, + "monai/optimizers/__init__.py": { + "size": 796 + }, + "monai/optimizers/lr_finder.py": { + "size": 21992 + }, + "monai/optimizers/lr_scheduler.py": { + "size": 4082 + }, + "monai/optimizers/novograd.py": { + "size": 5677 + }, + "monai/optimizers/utils.py": { + "size": 4133 + }, + "monai/transforms/__init__.py": { + "size": 16744 + }, + "monai/transforms/adaptors.py": { + "size": 8796 + }, + "monai/transforms/compose.py": { + "size": 39194 + }, + "monai/transforms/croppad/__init__.py": { + "size": 573 + }, + "monai/transforms/croppad/array.py": { + "size": 74574 + }, + "monai/transforms/croppad/batch.py": { + "size": 6138 + }, + "monai/transforms/croppad/dictionary.py": { + "size": 60697 + }, + "monai/transforms/croppad/functional.py": { + "size": 12640 + }, + "monai/transforms/intensity/__init__.py": { + "size": 573 + }, + "monai/transforms/intensity/array.py": { + "size": 121959 + }, + "monai/transforms/intensity/dictionary.py": { + "size": 85080 + }, + "monai/transforms/inverse.py": { + "size": 20287 + }, + "monai/transforms/inverse_batch_transform.py": { + "size": 7055 + }, + "monai/transforms/io/__init__.py": { + "size": 573 + }, + "monai/transforms/io/array.py": { + "size": 27460 + }, + "monai/transforms/io/dictionary.py": { + "size": 18716 + }, + "monai/transforms/lazy/__init__.py": { + "size": 573 + }, + "monai/transforms/lazy/array.py": { + "size": 1211 + }, + "monai/transforms/lazy/dictionary.py": { + "size": 1571 + }, + "monai/transforms/lazy/functional.py": { + "size": 15210 + }, + "monai/transforms/lazy/utils.py": { + "size": 9840 + }, + "monai/transforms/meta_utility/__init__.py": { + "size": 573 + }, + "monai/transforms/meta_utility/dictionary.py": { + "size": 4896 + }, + "monai/transforms/nvtx.py": { + "size": 3386 + }, + "monai/transforms/post/__init__.py": { + "size": 573 + }, + "monai/transforms/post/array.py": { + "size": 45042 + }, + "monai/transforms/post/dictionary.py": { + "size": 43042 + }, + "monai/transforms/regularization/__init__.py": { + "size": 573 + }, + "monai/transforms/regularization/array.py": { + "size": 8384 + }, + "monai/transforms/regularization/dictionary.py": { + "size": 4800 + }, + "monai/transforms/signal/__init__.py": { + "size": 573 + }, + "monai/transforms/signal/array.py": { + "size": 16321 + }, + "monai/transforms/signal/dictionary.py": { + "size": 2085 + }, + "monai/transforms/smooth_field/__init__.py": { + "size": 573 + }, + "monai/transforms/smooth_field/array.py": { + "size": 17856 + }, + "monai/transforms/smooth_field/dictionary.py": { + "size": 11194 + }, + "monai/transforms/spatial/__init__.py": { + "size": 573 + }, + "monai/transforms/spatial/array.py": { + "size": 186973 + }, + "monai/transforms/spatial/dictionary.py": { + "size": 134668 + }, + "monai/transforms/spatial/functional.py": { + "size": 33916 + }, + "monai/transforms/traits.py": { + "size": 3563 + }, + "monai/transforms/transform.py": { + "size": 22256 + }, + "monai/transforms/utility/__init__.py": { + "size": 573 + }, + "monai/transforms/utility/array.py": { + "size": 81231 + }, + "monai/transforms/utility/dictionary.py": { + "size": 80549 + }, + "monai/transforms/utils.py": { + "size": 106380 + }, + "monai/transforms/utils_create_transform_ims.py": { + "size": 31155 + }, + "monai/transforms/utils_morphological_ops.py": { + "size": 6767 + }, + "monai/transforms/utils_pytorch_numpy_unification.py": { + "size": 18718 + }, + "monai/utils/__init__.py": { + "size": 3813 + }, + "monai/utils/component_store.py": { + "size": 4525 + }, + "monai/utils/decorators.py": { + "size": 3156 + }, + "monai/utils/deprecate_utils.py": { + "size": 14759 + }, + "monai/utils/dist.py": { + "size": 8578 + }, + "monai/utils/enums.py": { + "size": 19931 + }, + "monai/utils/jupyter_utils.py": { + "size": 15651 + }, + "monai/utils/misc.py": { + "size": 31566 + }, + "monai/utils/module.py": { + "size": 26083 + }, + "monai/utils/nvtx.py": { + "size": 6876 + }, + "monai/utils/ordering.py": { + "size": 8270 + }, + "monai/utils/profiling.py": { + "size": 15937 + }, + "monai/utils/state_cacher.py": { + "size": 6008 + }, + "monai/utils/tf32.py": { + "size": 2643 + }, + "monai/utils/type_conversion.py": { + "size": 22090 + }, + "monai/visualize/__init__.py": { + "size": 1038 + }, + "monai/visualize/class_activation_maps.py": { + "size": 16122 + }, + "monai/visualize/gradient_based.py": { + "size": 6278 + }, + "monai/visualize/img2tensorboard.py": { + "size": 9267 + }, + "monai/visualize/occlusion_sensitivity.py": { + "size": 18159 + }, + "monai/visualize/utils.py": { + "size": 9978 + }, + "monai/visualize/visualizer.py": { + "size": 1377 + }, + "pyproject.toml": { + "size": 2034 + }, + "requirements-dev.txt": { + "size": 1678 + }, + "requirements-min.txt": { + "size": 207 + }, + "requirements.txt": { + "size": 112 + }, + "setup.cfg": { + "size": 8014 + }, + "setup.py": { + "size": 5178 + }, + "tests/__init__.py": { + "size": 573 + }, + "tests/apps/__init__.py": { + "size": 573 + }, + "tests/apps/deepedit/__init__.py": { + "size": 573 + }, + "tests/apps/deepedit/test_deepedit_transforms.py": { + "size": 10632 + }, + "tests/apps/deepgrow/__init__.py": { + "size": 573 + }, + "tests/apps/deepgrow/test_deepgrow_dataset.py": { + "size": 3960 + }, + "tests/apps/deepgrow/transforms/__init__.py": { + "size": 573 + }, + "tests/apps/deepgrow/transforms/test_deepgrow_interaction.py": { + "size": 3722 + }, + "tests/apps/deepgrow/transforms/test_deepgrow_transforms.py": { + "size": 19699 + }, + "tests/apps/detection/__init__.py": { + "size": 573 + }, + "tests/apps/detection/metrics/__init__.py": { + "size": 573 + }, + "tests/apps/detection/metrics/test_detection_coco_metrics.py": { + "size": 2851 + }, + "tests/apps/detection/networks/__init__.py": { + "size": 573 + }, + "tests/apps/detection/networks/test_retinanet.py": { + "size": 7406 + }, + "tests/apps/detection/networks/test_retinanet_detector.py": { + "size": 7657 + }, + "tests/apps/detection/test_box_transform.py": { + "size": 18041 + }, + "tests/apps/detection/utils/__init__.py": { + "size": 573 + }, + "tests/apps/detection/utils/test_anchor_box.py": { + "size": 3861 + }, + "tests/apps/detection/utils/test_atss_box_matcher.py": { + "size": 1706 + }, + "tests/apps/detection/utils/test_box_coder.py": { + "size": 1750 + }, + "tests/apps/detection/utils/test_detector_boxselector.py": { + "size": 2297 + }, + "tests/apps/detection/utils/test_detector_utils.py": { + "size": 3001 + }, + "tests/apps/detection/utils/test_hardnegsampler.py": { + "size": 2074 + }, + "tests/apps/maisi/networks/__init__.py": { + "size": 573 + }, + "tests/apps/maisi/networks/test_autoencoderkl_maisi.py": { + "size": 8681 + }, + "tests/apps/maisi/networks/test_controlnet_maisi.py": { + "size": 6305 + }, + "tests/apps/maisi/networks/test_diffusion_model_unet_maisi.py": { + "size": 19392 + }, + "tests/apps/nuclick/__init__.py": { + "size": 573 + }, + "tests/apps/nuclick/test_nuclick_transforms.py": { + "size": 9623 + }, + "tests/apps/pathology/__init__.py": { + "size": 573 + }, + "tests/apps/pathology/handlers/__init__.py": { + "size": 573 + }, + "tests/apps/pathology/handlers/test_from_engine_hovernet.py": { + "size": 1347 + }, + "tests/apps/pathology/test_lesion_froc.py": { + "size": 9764 + }, + "tests/apps/pathology/test_pathology_prob_nms.py": { + "size": 1767 + }, + "tests/apps/pathology/test_prepare_batch_hovernet.py": { + "size": 2562 + }, + "tests/apps/pathology/test_sliding_window_hovernet_inference.py": { + "size": 11989 + }, + "tests/apps/pathology/transforms/__init__.py": { + "size": 573 + }, + "tests/apps/pathology/transforms/post/__init__.py": { + "size": 573 + }, + "tests/apps/pathology/transforms/post/test_generate_distance_map.py": { + "size": 1929 + }, + "tests/apps/pathology/transforms/post/test_generate_distance_mapd.py": { + "size": 2335 + }, + "tests/apps/pathology/transforms/post/test_generate_instance_border.py": { + "size": 2021 + }, + "tests/apps/pathology/transforms/post/test_generate_instance_borderd.py": { + "size": 2256 + }, + "tests/apps/pathology/transforms/post/test_generate_instance_centroid.py": { + "size": 1986 + }, + "tests/apps/pathology/transforms/post/test_generate_instance_centroidd.py": { + "size": 2144 + }, + "tests/apps/pathology/transforms/post/test_generate_instance_contour.py": { + "size": 2204 + }, + "tests/apps/pathology/transforms/post/test_generate_instance_contourd.py": { + "size": 2363 + }, + "tests/apps/pathology/transforms/post/test_generate_instance_type.py": { + "size": 1668 + }, + "tests/apps/pathology/transforms/post/test_generate_instance_typed.py": { + "size": 1858 + }, + "tests/apps/pathology/transforms/post/test_generate_succinct_contour.py": { + "size": 1945 + }, + "tests/apps/pathology/transforms/post/test_generate_succinct_contourd.py": { + "size": 2038 + }, + "tests/apps/pathology/transforms/post/test_generate_watershed_markers.py": { + "size": 2057 + }, + "tests/apps/pathology/transforms/post/test_generate_watershed_markersd.py": { + "size": 2835 + }, + "tests/apps/pathology/transforms/post/test_generate_watershed_mask.py": { + "size": 2569 + }, + "tests/apps/pathology/transforms/post/test_generate_watershed_maskd.py": { + "size": 2727 + }, + "tests/apps/pathology/transforms/post/test_hovernet_instance_map_post_processing.py": { + "size": 2550 + }, + "tests/apps/pathology/transforms/post/test_hovernet_instance_map_post_processingd.py": { + "size": 2799 + }, + "tests/apps/pathology/transforms/post/test_hovernet_nuclear_type_post_processing.py": { + "size": 2554 + }, + "tests/apps/pathology/transforms/post/test_watershed.py": { + "size": 2083 + }, + "tests/apps/pathology/transforms/post/test_watershedd.py": { + "size": 2528 + }, + "tests/apps/pathology/transforms/test_pathology_he_stain.py": { + "size": 10329 + }, + "tests/apps/pathology/transforms/test_pathology_he_stain_dict.py": { + "size": 10279 + }, + "tests/apps/reconstruction/__init__.py": { + "size": 573 + }, + "tests/apps/reconstruction/nets/__init__.py": { + "size": 573 + }, + "tests/apps/reconstruction/nets/test_recon_net_utils.py": { + "size": 3185 + }, + "tests/apps/reconstruction/test_complex_utils.py": { + "size": 2674 + }, + "tests/apps/reconstruction/test_fastmri_reader.py": { + "size": 2447 + }, + "tests/apps/reconstruction/test_mri_utils.py": { + "size": 1225 + }, + "tests/apps/reconstruction/transforms/__init__.py": { + "size": 573 + }, + "tests/apps/reconstruction/transforms/test_kspace_mask.py": { + "size": 1816 + }, + "tests/apps/reconstruction/transforms/test_reference_based_normalize_intensity.py": { + "size": 3155 + }, + "tests/apps/reconstruction/transforms/test_reference_based_spatial_cropd.py": { + "size": 1975 + }, + "tests/apps/test_auto3dseg.py": { + "size": 22357 + }, + "tests/apps/test_auto3dseg_bundlegen.py": { + "size": 5501 + }, + "tests/apps/test_check_hash.py": { + "size": 1761 + }, + "tests/apps/test_cross_validation.py": { + "size": 2914 + }, + "tests/apps/test_decathlondataset.py": { + "size": 3767 + }, + "tests/apps/test_download_and_extract.py": { + "size": 11311 + }, + "tests/apps/test_download_url_yandex.py": { + "size": 1627 + }, + "tests/apps/test_mednistdataset.py": { + "size": 2950 + }, + "tests/apps/test_mmar_download.py": { + "size": 6344 + }, + "tests/apps/test_tciadataset.py": { + "size": 4619 + }, + "tests/apps/vista3d/__init__.py": { + "size": 573 + }, + "tests/apps/vista3d/test_point_based_window_inferer.py": { + "size": 2686 + }, + "tests/apps/vista3d/test_vista3d_sampler.py": { + "size": 2775 + }, + "tests/apps/vista3d/test_vista3d_transforms.py": { + "size": 3280 + }, + "tests/bundle/__init__.py": { + "size": 573 + }, + "tests/bundle/test_bundle_ckpt_export.py": { + "size": 4622 + }, + "tests/bundle/test_bundle_download.py": { + "size": 21616 + }, + "tests/bundle/test_bundle_get_data.py": { + "size": 3667 + }, + "tests/bundle/test_bundle_push_to_hf_hub.py": { + "size": 1740 + }, + "tests/bundle/test_bundle_trt_export.py": { + "size": 6344 + }, + "tests/bundle/test_bundle_utils.py": { + "size": 4573 + }, + "tests/bundle/test_bundle_verify_metadata.py": { + "size": 2660 + }, + "tests/bundle/test_bundle_verify_net.py": { + "size": 3383 + }, + "tests/bundle/test_bundle_workflow.py": { + "size": 11868 + }, + "tests/bundle/test_component_locator.py": { + "size": 1507 + }, + "tests/bundle/test_config_item.py": { + "size": 5974 + }, + "tests/bundle/test_config_parser.py": { + "size": 16131 + }, + "tests/bundle/test_reference_resolver.py": { + "size": 4284 + }, + "tests/clang_format_utils.py": { + "size": 3176 + }, + "tests/config/__init__.py": { + "size": 573 + }, + "tests/config/test_cv2_dist.py": { + "size": 1953 + }, + "tests/config/test_print_info.py": { + "size": 819 + }, + "tests/croppers.py": { + "size": 8115 + }, + "tests/data/meta_tensor/test_meta_tensor.py": { + "size": 22820 + }, + "tests/data/meta_tensor/test_to_from_meta_tensord.py": { + "size": 6856 + }, + "tests/data/test_arraydataset.py": { + "size": 8362 + }, + "tests/data/test_box_utils.py": { + "size": 8930 + }, + "tests/data/test_cachedataset.py": { + "size": 9793 + }, + "tests/data/test_cachedataset_parallel.py": { + "size": 2217 + }, + "tests/data/test_cachedataset_persistent_workers.py": { + "size": 1708 + }, + "tests/data/test_cachentransdataset.py": { + "size": 2454 + }, + "tests/data/test_check_missing_files.py": { + "size": 2494 + }, + "tests/data/test_create_cross_validation_datalist.py": { + "size": 2174 + }, + "tests/data/test_csv_dataset.py": { + "size": 9496 + }, + "tests/data/test_csv_iterable_dataset.py": { + "size": 11094 + }, + "tests/data/test_csv_saver.py": { + "size": 1652 + }, + "tests/data/test_dataloader.py": { + "size": 4039 + }, + "tests/data/test_dataset.py": { + "size": 8790 + }, + "tests/data/test_dataset_func.py": { + "size": 2272 + }, + "tests/data/test_dataset_summary.py": { + "size": 4670 + }, + "tests/data/test_fft_utils.py": { + "size": 2321 + }, + "tests/data/test_folder_layout.py": { + "size": 3153 + }, + "tests/data/test_gdsdataset.py": { + "size": 10779 + }, + "tests/data/test_grid_dataset.py": { + "size": 10849 + }, + "tests/data/test_handler_smartcache.py": { + "size": 1642 + }, + "tests/data/test_hashing.py": { + "size": 1754 + }, + "tests/data/test_header_correct.py": { + "size": 1577 + }, + "tests/data/test_image_dataset.py": { + "size": 7194 + }, + "tests/data/test_image_rw.py": { + "size": 8419 + }, + "tests/data/test_init_reader.py": { + "size": 2930 + }, + "tests/data/test_is_supported_format.py": { + "size": 1676 + }, + "tests/data/test_iterable_dataset.py": { + "size": 2924 + }, + "tests/data/test_itk_torch_bridge.py": { + "size": 20279 + }, + "tests/data/test_itk_writer.py": { + "size": 2622 + }, + "tests/data/test_list_data_collate.py": { + "size": 2388 + }, + "tests/data/test_lmdbdataset.py": { + "size": 8934 + }, + "tests/data/test_lmdbdataset_dist.py": { + "size": 2535 + }, + "tests/data/test_load_decathlon_datalist.py": { + "size": 6476 + }, + "tests/data/test_make_nifti.py": { + "size": 1503 + }, + "tests/data/test_mapping_file.py": { + "size": 4574 + }, + "tests/data/test_masked_patch_wsi_dataset.py": { + "size": 4462 + }, + "tests/data/test_nifti_header_revise.py": { + "size": 1529 + }, + "tests/data/test_nifti_rw.py": { + "size": 12025 + }, + "tests/data/test_npzdictitemdataset.py": { + "size": 1834 + }, + "tests/data/test_nrrd_reader.py": { + "size": 6252 + }, + "tests/data/test_numpy_reader.py": { + "size": 6111 + }, + "tests/data/test_partition_dataset.py": { + "size": 2940 + }, + "tests/data/test_partition_dataset_classes.py": { + "size": 2588 + }, + "tests/data/test_patch_dataset.py": { + "size": 3492 + }, + "tests/data/test_patch_wsi_dataset.py": { + "size": 8414 + }, + "tests/data/test_persistentdataset.py": { + "size": 8251 + }, + "tests/data/test_persistentdataset_dist.py": { + "size": 3111 + }, + "tests/data/test_pil_reader.py": { + "size": 3685 + }, + "tests/data/test_png_rw.py": { + "size": 4325 + }, + "tests/data/test_resample_datalist.py": { + "size": 1442 + }, + "tests/data/test_sampler_dist.py": { + "size": 3468 + }, + "tests/data/test_select_cross_validation_folds.py": { + "size": 1769 + }, + "tests/data/test_shuffle_buffer.py": { + "size": 1984 + }, + "tests/data/test_sliding_patch_wsi_dataset.py": { + "size": 10938 + }, + "tests/data/test_smartcachedataset.py": { + "size": 7519 + }, + "tests/data/test_synthetic.py": { + "size": 2446 + }, + "tests/data/test_thread_buffer.py": { + "size": 5030 + }, + "tests/data/test_threadcontainer.py": { + "size": 3499 + }, + "tests/data/test_video_datasets.py": { + "size": 5648 + }, + "tests/data/test_weighted_random_sampler_dist.py": { + "size": 2199 + }, + "tests/data/test_zipdataset.py": { + "size": 2396 + }, + "tests/data/utils/test_decollate.py": { + "size": 10476 + }, + "tests/data/utils/test_dev_collate.py": { + "size": 1569 + }, + "tests/data/utils/test_file_basename.py": { + "size": 3858 + }, + "tests/data/utils/test_ori_ras_lps.py": { + "size": 1880 + }, + "tests/data/utils/test_zoom_affine.py": { + "size": 3096 + }, + "tests/engines/__init__.py": { + "size": 573 + }, + "tests/engines/test_ensemble_evaluator.py": { + "size": 3147 + }, + "tests/engines/test_prepare_batch_default.py": { + "size": 2814 + }, + "tests/engines/test_prepare_batch_default_dist.py": { + "size": 2575 + }, + "tests/engines/test_prepare_batch_diffusion.py": { + "size": 3782 + }, + "tests/engines/test_prepare_batch_extra_input.py": { + "size": 2841 + }, + "tests/fl/__init__.py": { + "size": 573 + }, + "tests/fl/monai_algo/__init__.py": { + "size": 573 + }, + "tests/fl/monai_algo/test_fl_monai_algo.py": { + "size": 8855 + }, + "tests/fl/monai_algo/test_fl_monai_algo_dist.py": { + "size": 5026 + }, + "tests/fl/test_fl_monai_algo_stats.py": { + "size": 2886 + }, + "tests/fl/utils/__init__.py": { + "size": 573 + }, + "tests/fl/utils/test_fl_exchange_object.py": { + "size": 2571 + }, + "tests/handlers/__init__.py": { + "size": 573 + }, + "tests/handlers/test_handler_average_precision.py": { + "size": 2814 + }, + "tests/handlers/test_handler_checkpoint_loader.py": { + "size": 8443 + }, + "tests/handlers/test_handler_checkpoint_saver.py": { + "size": 6256 + }, + "tests/handlers/test_handler_classification_saver.py": { + "size": 2355 + }, + "tests/handlers/test_handler_classification_saver_dist.py": { + "size": 2781 + }, + "tests/handlers/test_handler_clearml_image.py": { + "size": 2603 + }, + "tests/handlers/test_handler_clearml_stats.py": { + "size": 2603 + }, + "tests/handlers/test_handler_confusion_matrix.py": { + "size": 3916 + }, + "tests/handlers/test_handler_confusion_matrix_dist.py": { + "size": 2547 + }, + "tests/handlers/test_handler_decollate_batch.py": { + "size": 2482 + }, + "tests/handlers/test_handler_early_stop.py": { + "size": 2157 + }, + "tests/handlers/test_handler_garbage_collector.py": { + "size": 2806 + }, + "tests/handlers/test_handler_hausdorff_distance.py": { + "size": 3884 + }, + "tests/handlers/test_handler_ignite_metric.py": { + "size": 7365 + }, + "tests/handlers/test_handler_lr_scheduler.py": { + "size": 3336 + }, + "tests/handlers/test_handler_mean_dice.py": { + "size": 4187 + }, + "tests/handlers/test_handler_mean_iou.py": { + "size": 3023 + }, + "tests/handlers/test_handler_metrics_reloaded.py": { + "size": 5838 + }, + "tests/handlers/test_handler_metrics_saver.py": { + "size": 3836 + }, + "tests/handlers/test_handler_metrics_saver_dist.py": { + "size": 5074 + }, + "tests/handlers/test_handler_mlflow.py": { + "size": 11737 + }, + "tests/handlers/test_handler_nvtx.py": { + "size": 3825 + }, + "tests/handlers/test_handler_panoptic_quality.py": { + "size": 3147 + }, + "tests/handlers/test_handler_parameter_scheduler.py": { + "size": 4892 + }, + "tests/handlers/test_handler_post_processing.py": { + "size": 2877 + }, + "tests/handlers/test_handler_prob_map_producer.py": { + "size": 3944 + }, + "tests/handlers/test_handler_regression_metrics.py": { + "size": 6692 + }, + "tests/handlers/test_handler_regression_metrics_dist.py": { + "size": 8665 + }, + "tests/handlers/test_handler_rocauc.py": { + "size": 1554 + }, + "tests/handlers/test_handler_rocauc_dist.py": { + "size": 2010 + }, + "tests/handlers/test_handler_stats.py": { + "size": 9507 + }, + "tests/handlers/test_handler_surface_distance.py": { + "size": 4006 + }, + "tests/handlers/test_handler_tb_image.py": { + "size": 2144 + }, + "tests/handlers/test_handler_tb_stats.py": { + "size": 6227 + }, + "tests/handlers/test_handler_validation.py": { + "size": 1972 + }, + "tests/handlers/test_trt_compile.py": { + "size": 6317 + }, + "tests/handlers/test_write_metrics_reports.py": { + "size": 3027 + }, + "tests/hvd_evenly_divisible_all_gather.py": { + "size": 2295 + }, + "tests/inferers/__init__.py": { + "size": 573 + }, + "tests/inferers/test_avg_merger.py": { + "size": 5952 + }, + "tests/inferers/test_controlnet_inferers.py": { + "size": 52922 + }, + "tests/inferers/test_diffusion_inferer.py": { + "size": 16879 + }, + "tests/inferers/test_latent_diffusion_inferer.py": { + "size": 35681 + }, + "tests/inferers/test_patch_inferer.py": { + "size": 17267 + }, + "tests/inferers/test_saliency_inferer.py": { + "size": 1949 + }, + "tests/inferers/test_slice_inferer.py": { + "size": 3355 + }, + "tests/inferers/test_sliding_window_inference.py": { + "size": 27906 + }, + "tests/inferers/test_sliding_window_splitter.py": { + "size": 9620 + }, + "tests/inferers/test_wsi_sliding_window_splitter.py": { + "size": 8434 + }, + "tests/inferers/test_zarr_avg_merger.py": { + "size": 16355 + }, + "tests/integration/__init__.py": { + "size": 573 + }, + "tests/integration/test_auto3dseg_ensemble.py": { + "size": 7477 + }, + "tests/integration/test_auto3dseg_hpo.py": { + "size": 7197 + }, + "tests/integration/test_deepedit_interaction.py": { + "size": 4580 + }, + "tests/integration/test_hovernet_nuclear_type_post_processingd.py": { + "size": 2661 + }, + "tests/integration/test_integration_autorunner.py": { + "size": 7458 + }, + "tests/integration/test_integration_bundle_run.py": { + "size": 10734 + }, + "tests/integration/test_integration_classification_2d.py": { + "size": 11373 + }, + "tests/integration/test_integration_determinism.py": { + "size": 3172 + }, + "tests/integration/test_integration_fast_train.py": { + "size": 9736 + }, + "tests/integration/test_integration_gpu_customization.py": { + "size": 5399 + }, + "tests/integration/test_integration_lazy_samples.py": { + "size": 9133 + }, + "tests/integration/test_integration_nnunet_bundle.py": { + "size": 6745 + }, + "tests/integration/test_integration_nnunetv2_runner.py": { + "size": 4264 + }, + "tests/integration/test_integration_segmentation_3d.py": { + "size": 13229 + }, + "tests/integration/test_integration_sliding_window.py": { + "size": 3769 + }, + "tests/integration/test_integration_stn.py": { + "size": 4946 + }, + "tests/integration/test_integration_unet_2d.py": { + "size": 2376 + }, + "tests/integration/test_integration_workers.py": { + "size": 2131 + }, + "tests/integration/test_integration_workflows.py": { + "size": 13971 + }, + "tests/integration/test_integration_workflows_adversarial.py": { + "size": 6375 + }, + "tests/integration/test_integration_workflows_gan.py": { + "size": 5496 + }, + "tests/integration/test_loader_semaphore.py": { + "size": 1195 + }, + "tests/integration/test_mapping_filed.py": { + "size": 4813 + }, + "tests/integration/test_meta_affine.py": { + "size": 7495 + }, + "tests/integration/test_metatensor_integration.py": { + "size": 5476 + }, + "tests/integration/test_module_list.py": { + "size": 3250 + }, + "tests/integration/test_one_of.py": { + "size": 8872 + }, + "tests/integration/test_pad_collation.py": { + "size": 4821 + }, + "tests/integration/test_reg_loss_integration.py": { + "size": 3751 + }, + "tests/integration/test_retinanet_predict_utils.py": { + "size": 4995 + }, + "tests/integration/test_seg_loss_integration.py": { + "size": 6272 + }, + "tests/integration/test_spatial_combine_transforms.py": { + "size": 7926 + }, + "tests/integration/test_testtimeaugmentation.py": { + "size": 6975 + }, + "tests/integration/test_vis_gradbased.py": { + "size": 2486 + }, + "tests/integration/test_vista3d_utils.py": { + "size": 6200 + }, + "tests/lazy_transforms_utils.py": { + "size": 3958 + }, + "tests/losses/__init__.py": { + "size": 573 + }, + "tests/losses/deform/__init__.py": { + "size": 573 + }, + "tests/losses/deform/test_bending_energy.py": { + "size": 3662 + }, + "tests/losses/deform/test_diffusion_loss.py": { + "size": 5265 + }, + "tests/losses/image_dissimilarity/__init__.py": { + "size": 573 + }, + "tests/losses/image_dissimilarity/test_global_mutual_information_loss.py": { + "size": 5624 + }, + "tests/losses/image_dissimilarity/test_local_normalized_cross_correlation_loss.py": { + "size": 6070 + }, + "tests/losses/test_adversarial_loss.py": { + "size": 4034 + }, + "tests/losses/test_barlow_twins_loss.py": { + "size": 3589 + }, + "tests/losses/test_cldice_loss.py": { + "size": 1882 + }, + "tests/losses/test_contrastive_loss.py": { + "size": 2948 + }, + "tests/losses/test_dice_ce_loss.py": { + "size": 4656 + }, + "tests/losses/test_dice_focal_loss.py": { + "size": 5464 + }, + "tests/losses/test_dice_loss.py": { + "size": 9379 + }, + "tests/losses/test_ds_loss.py": { + "size": 6851 + }, + "tests/losses/test_focal_loss.py": { + "size": 17293 + }, + "tests/losses/test_generalized_dice_focal_loss.py": { + "size": 3861 + }, + "tests/losses/test_generalized_dice_loss.py": { + "size": 8753 + }, + "tests/losses/test_generalized_wasserstein_dice_loss.py": { + "size": 9732 + }, + "tests/losses/test_giou_loss.py": { + "size": 2017 + }, + "tests/losses/test_hausdorff_loss.py": { + "size": 11240 + }, + "tests/losses/test_masked_dice_loss.py": { + "size": 6304 + }, + "tests/losses/test_masked_loss.py": { + "size": 3121 + }, + "tests/losses/test_multi_scale.py": { + "size": 3208 + }, + "tests/losses/test_nacl_loss.py": { + "size": 7330 + }, + "tests/losses/test_perceptual_loss.py": { + "size": 4551 + }, + "tests/losses/test_spectral_loss.py": { + "size": 3042 + }, + "tests/losses/test_ssim_loss.py": { + "size": 2105 + }, + "tests/losses/test_sure_loss.py": { + "size": 2923 + }, + "tests/losses/test_tversky_loss.py": { + "size": 8405 + }, + "tests/losses/test_unified_focal_loss.py": { + "size": 2314 + }, + "tests/metrics/__init__.py": { + "size": 573 + }, + "tests/metrics/test_compute_average_precision.py": { + "size": 4743 + }, + "tests/metrics/test_compute_confusion_matrix.py": { + "size": 10669 + }, + "tests/metrics/test_compute_f_beta.py": { + "size": 3286 + }, + "tests/metrics/test_compute_fid_metric.py": { + "size": 1328 + }, + "tests/metrics/test_compute_froc.py": { + "size": 4522 + }, + "tests/metrics/test_compute_generalized_dice.py": { + "size": 9614 + }, + "tests/metrics/test_compute_meandice.py": { + "size": 11390 + }, + "tests/metrics/test_compute_meaniou.py": { + "size": 8020 + }, + "tests/metrics/test_compute_mmd_metric.py": { + "size": 2025 + }, + "tests/metrics/test_compute_multiscalessim_metric.py": { + "size": 3080 + }, + "tests/metrics/test_compute_panoptic_quality.py": { + "size": 5087 + }, + "tests/metrics/test_compute_regression_metrics.py": { + "size": 8135 + }, + "tests/metrics/test_compute_roc_auc.py": { + "size": 4579 + }, + "tests/metrics/test_compute_variance.py": { + "size": 4903 + }, + "tests/metrics/test_cumulative.py": { + "size": 1927 + }, + "tests/metrics/test_cumulative_average.py": { + "size": 2370 + }, + "tests/metrics/test_cumulative_average_dist.py": { + "size": 1702 + }, + "tests/metrics/test_hausdorff_distance.py": { + "size": 7219 + }, + "tests/metrics/test_label_quality_score.py": { + "size": 4983 + }, + "tests/metrics/test_loss_metric.py": { + "size": 2106 + }, + "tests/metrics/test_metrics_reloaded.py": { + "size": 4654 + }, + "tests/metrics/test_ssim_metric.py": { + "size": 2896 + }, + "tests/metrics/test_surface_dice.py": { + "size": 21760 + }, + "tests/metrics/test_surface_distance.py": { + "size": 6237 + }, + "tests/min_tests.py": { + "size": 8606 + }, + "tests/networks/__init__.py": { + "size": 573 + }, + "tests/networks/blocks/__init__.py": { + "size": 573 + }, + "tests/networks/blocks/dints_block/__init__.py": { + "size": 573 + }, + "tests/networks/blocks/dints_block/test_acn_block.py": { + "size": 1391 + }, + "tests/networks/blocks/dints_block/test_factorized_increase.py": { + "size": 1306 + }, + "tests/networks/blocks/dints_block/test_factorized_reduce.py": { + "size": 1305 + }, + "tests/networks/blocks/dints_block/test_p3d_block.py": { + "size": 2372 + }, + "tests/networks/blocks/test_CABlock.py": { + "size": 5527 + }, + "tests/networks/blocks/test_adn.py": { + "size": 3191 + }, + "tests/networks/blocks/test_convolutions.py": { + "size": 7139 + }, + "tests/networks/blocks/test_crf_cpu.py": { + "size": 18871 + }, + "tests/networks/blocks/test_crf_cuda.py": { + "size": 19450 + }, + "tests/networks/blocks/test_crossattention.py": { + "size": 7213 + }, + "tests/networks/blocks/test_denseblock.py": { + "size": 4373 + }, + "tests/networks/blocks/test_downsample_block.py": { + "size": 7687 + }, + "tests/networks/blocks/test_dynunet_block.py": { + "size": 4279 + }, + "tests/networks/blocks/test_fpn_block.py": { + "size": 3353 + }, + "tests/networks/blocks/test_localnet_block.py": { + "size": 4806 + }, + "tests/networks/blocks/test_mlp.py": { + "size": 3007 + }, + "tests/networks/blocks/test_patchembedding.py": { + "size": 6205 + }, + "tests/networks/blocks/test_regunet_block.py": { + "size": 3461 + }, + "tests/networks/blocks/test_se_block.py": { + "size": 2880 + }, + "tests/networks/blocks/test_se_blocks.py": { + "size": 2810 + }, + "tests/networks/blocks/test_segresnet_block.py": { + "size": 1860 + }, + "tests/networks/blocks/test_selfattention.py": { + "size": 10691 + }, + "tests/networks/blocks/test_simple_aspp.py": { + "size": 2809 + }, + "tests/networks/blocks/test_spatialattention.py": { + "size": 1909 + }, + "tests/networks/blocks/test_subpixel_upsample.py": { + "size": 3063 + }, + "tests/networks/blocks/test_text_encoding.py": { + "size": 2033 + }, + "tests/networks/blocks/test_transformerblock.py": { + "size": 3128 + }, + "tests/networks/blocks/test_unetr_block.py": { + "size": 5777 + }, + "tests/networks/blocks/test_upsample_block.py": { + "size": 4650 + }, + "tests/networks/blocks/warp/__init__.py": { + "size": 573 + }, + "tests/networks/blocks/warp/test_dvf2ddf.py": { + "size": 2391 + }, + "tests/networks/blocks/warp/test_warp.py": { + "size": 9109 + }, + "tests/networks/layers/__init__.py": { + "size": 573 + }, + "tests/networks/layers/filtering/__init__.py": { + "size": 573 + }, + "tests/networks/layers/filtering/test_bilateral_approx_cpu.py": { + "size": 13653 + }, + "tests/networks/layers/filtering/test_bilateral_approx_cuda.py": { + "size": 13778 + }, + "tests/networks/layers/filtering/test_bilateral_precise.py": { + "size": 15069 + }, + "tests/networks/layers/filtering/test_phl_cpu.py": { + "size": 9046 + }, + "tests/networks/layers/filtering/test_phl_cuda.py": { + "size": 4847 + }, + "tests/networks/layers/filtering/test_trainable_bilateral.py": { + "size": 16837 + }, + "tests/networks/layers/filtering/test_trainable_joint_bilateral.py": { + "size": 21403 + }, + "tests/networks/layers/test_affine_transform.py": { + "size": 19055 + }, + "tests/networks/layers/test_apply_filter.py": { + "size": 3741 + }, + "tests/networks/layers/test_channel_pad.py": { + "size": 1672 + }, + "tests/networks/layers/test_conjugate_gradient.py": { + "size": 2051 + }, + "tests/networks/layers/test_drop_path.py": { + "size": 1512 + }, + "tests/networks/layers/test_gaussian.py": { + "size": 9064 + }, + "tests/networks/layers/test_gaussian_filter.py": { + "size": 6246 + }, + "tests/networks/layers/test_get_layers.py": { + "size": 2209 + }, + "tests/networks/layers/test_gmm.py": { + "size": 9436 + }, + "tests/networks/layers/test_grid_pull.py": { + "size": 3929 + }, + "tests/networks/layers/test_hilbert_transform.py": { + "size": 4182 + }, + "tests/networks/layers/test_lltm.py": { + "size": 2352 + }, + "tests/networks/layers/test_median_filter.py": { + "size": 1890 + }, + "tests/networks/layers/test_polyval.py": { + "size": 1994 + }, + "tests/networks/layers/test_preset_filters.py": { + "size": 4076 + }, + "tests/networks/layers/test_savitzky_golay_filter.py": { + "size": 5880 + }, + "tests/networks/layers/test_separable_filter.py": { + "size": 3592 + }, + "tests/networks/layers/test_skip_connection.py": { + "size": 1688 + }, + "tests/networks/layers/test_vector_quantizer.py": { + "size": 3560 + }, + "tests/networks/layers/test_weight_init.py": { + "size": 1713 + }, + "tests/networks/nets/__init__.py": { + "size": 573 + }, + "tests/networks/nets/dints/__init__.py": { + "size": 573 + }, + "tests/networks/nets/dints/test_dints_cell.py": { + "size": 3176 + }, + "tests/networks/nets/dints/test_dints_mixop.py": { + "size": 2729 + }, + "tests/networks/nets/regunet/__init__.py": { + "size": 573 + }, + "tests/networks/nets/regunet/test_localnet.py": { + "size": 2839 + }, + "tests/networks/nets/regunet/test_regunet.py": { + "size": 2811 + }, + "tests/networks/nets/test_ahnet.py": { + "size": 8348 + }, + "tests/networks/nets/test_attentionunet.py": { + "size": 3376 + }, + "tests/networks/nets/test_autoencoder.py": { + "size": 2964 + }, + "tests/networks/nets/test_autoencoderkl.py": { + "size": 11938 + }, + "tests/networks/nets/test_basic_unet.py": { + "size": 3337 + }, + "tests/networks/nets/test_basic_unetplusplus.py": { + "size": 3712 + }, + "tests/networks/nets/test_bundle_init_bundle.py": { + "size": 1934 + }, + "tests/networks/nets/test_cell_sam_wrapper.py": { + "size": 2215 + }, + "tests/networks/nets/test_controlnet.py": { + "size": 7373 + }, + "tests/networks/nets/test_daf3d.py": { + "size": 2331 + }, + "tests/networks/nets/test_densenet.py": { + "size": 4441 + }, + "tests/networks/nets/test_diffusion_model_unet.py": { + "size": 18931 + }, + "tests/networks/nets/test_dints_network.py": { + "size": 5721 + }, + "tests/networks/nets/test_discriminator.py": { + "size": 1916 + }, + "tests/networks/nets/test_dynunet.py": { + "size": 7208 + }, + "tests/networks/nets/test_efficientnet.py": { + "size": 13356 + }, + "tests/networks/nets/test_flexible_unet.py": { + "size": 10841 + }, + "tests/networks/nets/test_fullyconnectednet.py": { + "size": 2174 + }, + "tests/networks/nets/test_generator.py": { + "size": 1989 + }, + "tests/networks/nets/test_globalnet.py": { + "size": 3801 + }, + "tests/networks/nets/test_highresnet.py": { + "size": 2288 + }, + "tests/networks/nets/test_hovernet.py": { + "size": 7973 + }, + "tests/networks/nets/test_masked_autoencoder_vit.py": { + "size": 6376 + }, + "tests/networks/nets/test_mednext.py": { + "size": 4109 + }, + "tests/networks/nets/test_milmodel.py": { + "size": 3146 + }, + "tests/networks/nets/test_net_adapter.py": { + "size": 2641 + }, + "tests/networks/nets/test_network_consistency.py": { + "size": 2886 + }, + "tests/networks/nets/test_patch_gan_dicriminator.py": { + "size": 5266 + }, + "tests/networks/nets/test_quicknat.py": { + "size": 2601 + }, + "tests/networks/nets/test_resnet.py": { + "size": 10475 + }, + "tests/networks/nets/test_restormer.py": { + "size": 5332 + }, + "tests/networks/nets/test_segresnet.py": { + "size": 3648 + }, + "tests/networks/nets/test_segresnet_ds.py": { + "size": 5936 + }, + "tests/networks/nets/test_senet.py": { + "size": 6226 + }, + "tests/networks/nets/test_spade_autoencoderkl.py": { + "size": 9289 + }, + "tests/networks/nets/test_spade_diffusion_model_unet.py": { + "size": 18347 + }, + "tests/networks/nets/test_spade_vaegan.py": { + "size": 5632 + }, + "tests/networks/nets/test_swin_unetr.py": { + "size": 4457 + }, + "tests/networks/nets/test_torchvision_fc_model.py": { + "size": 6397 + }, + "tests/networks/nets/test_transchex.py": { + "size": 2622 + }, + "tests/networks/nets/test_transformer.py": { + "size": 4218 + }, + "tests/networks/nets/test_unet.py": { + "size": 5801 + }, + "tests/networks/nets/test_unetr.py": { + "size": 4018 + }, + "tests/networks/nets/test_varautoencoder.py": { + "size": 3547 + }, + "tests/networks/nets/test_vista3d.py": { + "size": 3263 + }, + "tests/networks/nets/test_vit.py": { + "size": 4625 + }, + "tests/networks/nets/test_vitautoenc.py": { + "size": 3865 + }, + "tests/networks/nets/test_vnet.py": { + "size": 2632 + }, + "tests/networks/nets/test_voxelmorph.py": { + "size": 8118 + }, + "tests/networks/nets/test_vqvae.py": { + "size": 8832 + }, + "tests/networks/nets/test_vqvaetransformer_inferer.py": { + "size": 10259 + }, + "tests/networks/schedulers/__init__.py": { + "size": 573 + }, + "tests/networks/schedulers/test_scheduler_ddim.py": { + "size": 3540 + }, + "tests/networks/schedulers/test_scheduler_ddpm.py": { + "size": 4577 + }, + "tests/networks/schedulers/test_scheduler_pndm.py": { + "size": 4612 + }, + "tests/networks/schedulers/test_scheduler_rflow.py": { + "size": 4339 + }, + "tests/networks/test_bundle_onnx_export.py": { + "size": 2788 + }, + "tests/networks/test_convert_to_onnx.py": { + "size": 3568 + }, + "tests/networks/test_convert_to_torchscript.py": { + "size": 1636 + }, + "tests/networks/test_convert_to_trt.py": { + "size": 2656 + }, + "tests/networks/test_save_state.py": { + "size": 2373 + }, + "tests/networks/test_to_onehot.py": { + "size": 2224 + }, + "tests/networks/test_varnet.py": { + "size": 2729 + }, + "tests/networks/utils/__init__.py": { + "size": 573 + }, + "tests/networks/utils/test_copy_model_state.py": { + "size": 6764 + }, + "tests/networks/utils/test_eval_mode.py": { + "size": 1086 + }, + "tests/networks/utils/test_freeze_layers.py": { + "size": 2006 + }, + "tests/networks/utils/test_pixelunshuffle.py": { + "size": 1911 + }, + "tests/networks/utils/test_replace_module.py": { + "size": 4350 + }, + "tests/networks/utils/test_train_mode.py": { + "size": 1051 + }, + "tests/nonconfig_workflow.py": { + "size": 8318 + }, + "tests/optimizers/__init__.py": { + "size": 573 + }, + "tests/optimizers/test_generate_param_groups.py": { + "size": 3370 + }, + "tests/optimizers/test_lr_finder.py": { + "size": 3672 + }, + "tests/optimizers/test_lr_scheduler.py": { + "size": 2571 + }, + "tests/optimizers/test_optim_novograd.py": { + "size": 3694 + }, + "tests/padders.py": { + "size": 8903 + }, + "tests/profile_subclass/README.md": { + "size": 1432 + }, + "tests/profile_subclass/__init__.py": { + "size": 573 + }, + "tests/profile_subclass/cprofile_profiling.py": { + "size": 1087 + }, + "tests/profile_subclass/min_classes.py": { + "size": 1048 + }, + "tests/profile_subclass/profiling.py": { + "size": 2622 + }, + "tests/profile_subclass/pyspy_profiling.py": { + "size": 1554 + }, + "tests/runner.py": { + "size": 5900 + }, + "tests/test_call_dist.py": { + "size": 1131 + }, + "tests/test_query_memory.py": { + "size": 993 + }, + "tests/test_timedcall_dist.py": { + "size": 2258 + }, + "tests/test_utils.py": { + "size": 33232 + }, + "tests/testing_data/1D_BP_bwd.txt": { + "size": 36112 + }, + "tests/testing_data/1D_BP_fwd.txt": { + "size": 8983 + }, + "tests/testing_data/bundle_test_network.py": { + "size": 1121 + }, + "tests/testing_data/config_fl_evaluate.json": { + "size": 394 + }, + "tests/testing_data/config_fl_filters.json": { + "size": 266 + }, + "tests/testing_data/config_fl_stats_1.json": { + "size": 702 + }, + "tests/testing_data/config_fl_stats_2.json": { + "size": 1301 + }, + "tests/testing_data/config_fl_train.json": { + "size": 6171 + }, + "tests/testing_data/cpp_resample_answers.py": { + "size": 1605 + }, + "tests/testing_data/data_config.json": { + "size": 9462 + }, + "tests/testing_data/fl_infer_properties.json": { + "size": 2481 + }, + "tests/testing_data/inference.json": { + "size": 3069 + }, + "tests/testing_data/inference.yaml": { + "size": 1739 + }, + "tests/testing_data/integration_answers.py": { + "size": 12490 + }, + "tests/testing_data/metadata.json": { + "size": 2949 + }, + "tests/testing_data/multi_gpu_evaluate.json": { + "size": 989 + }, + "tests/testing_data/multi_gpu_train.json": { + "size": 1160 + }, + "tests/testing_data/python_workflow_properties.json": { + "size": 717 + }, + "tests/testing_data/responsive_inference.json": { + "size": 2347 + }, + "tests/testing_data/transform_metatensor_cases.yaml": { + "size": 5090 + }, + "tests/transforms/__init__.py": { + "size": 573 + }, + "tests/transforms/compose/__init__.py": { + "size": 573 + }, + "tests/transforms/compose/test_compose.py": { + "size": 28500 + }, + "tests/transforms/compose/test_some_of.py": { + "size": 8866 + }, + "tests/transforms/croppad/__init__.py": { + "size": 573 + }, + "tests/transforms/croppad/test_rand_weighted_crop.py": { + "size": 7517 + }, + "tests/transforms/croppad/test_rand_weighted_cropd.py": { + "size": 6557 + }, + "tests/transforms/functional/__init__.py": { + "size": 573 + }, + "tests/transforms/functional/test_apply.py": { + "size": 2849 + }, + "tests/transforms/functional/test_resample.py": { + "size": 1907 + }, + "tests/transforms/intensity/__init__.py": { + "size": 573 + }, + "tests/transforms/intensity/test_compute_ho_ver_maps.py": { + "size": 2526 + }, + "tests/transforms/intensity/test_compute_ho_ver_maps_d.py": { + "size": 2827 + }, + "tests/transforms/intensity/test_foreground_mask.py": { + "size": 4160 + }, + "tests/transforms/intensity/test_foreground_maskd.py": { + "size": 4818 + }, + "tests/transforms/intensity/test_rand_histogram_shiftd.py": { + "size": 2814 + }, + "tests/transforms/intensity/test_scale_intensity_range_percentiles.py": { + "size": 3986 + }, + "tests/transforms/intensity/test_scale_intensity_range_percentilesd.py": { + "size": 3980 + }, + "tests/transforms/inverse/__init__.py": { + "size": 573 + }, + "tests/transforms/inverse/test_inverse.py": { + "size": 18970 + }, + "tests/transforms/inverse/test_inverse_array.py": { + "size": 2769 + }, + "tests/transforms/inverse/test_inverse_dict.py": { + "size": 4817 + }, + "tests/transforms/inverse/test_invert.py": { + "size": 4292 + }, + "tests/transforms/inverse/test_invertd.py": { + "size": 6094 + }, + "tests/transforms/inverse/test_traceable_transform.py": { + "size": 1887 + }, + "tests/transforms/post/__init__.py": { + "size": 573 + }, + "tests/transforms/post/test_label_filterd.py": { + "size": 2597 + }, + "tests/transforms/post/test_probnms.py": { + "size": 2866 + }, + "tests/transforms/post/test_probnmsd.py": { + "size": 3070 + }, + "tests/transforms/post/test_remove_small_objects.py": { + "size": 4463 + }, + "tests/transforms/spatial/__init__.py": { + "size": 573 + }, + "tests/transforms/spatial/test_convert_box_points.py": { + "size": 3741 + }, + "tests/transforms/spatial/test_grid_patch.py": { + "size": 5830 + }, + "tests/transforms/spatial/test_grid_patchd.py": { + "size": 4670 + }, + "tests/transforms/spatial/test_rand_grid_patch.py": { + "size": 5806 + }, + "tests/transforms/spatial/test_rand_grid_patchd.py": { + "size": 4537 + }, + "tests/transforms/spatial/test_spatial_resampled.py": { + "size": 4644 + }, + "tests/transforms/test_activations.py": { + "size": 3827 + }, + "tests/transforms/test_activationsd.py": { + "size": 2488 + }, + "tests/transforms/test_adaptors.py": { + "size": 4587 + }, + "tests/transforms/test_add_coordinate_channels.py": { + "size": 2248 + }, + "tests/transforms/test_add_coordinate_channelsd.py": { + "size": 2484 + }, + "tests/transforms/test_add_extreme_points_channel.py": { + "size": 2775 + }, + "tests/transforms/test_add_extreme_points_channeld.py": { + "size": 2652 + }, + "tests/transforms/test_adjust_contrast.py": { + "size": 2578 + }, + "tests/transforms/test_adjust_contrastd.py": { + "size": 2370 + }, + "tests/transforms/test_affine.py": { + "size": 10060 + }, + "tests/transforms/test_affine_grid.py": { + "size": 6628 + }, + "tests/transforms/test_affined.py": { + "size": 7837 + }, + "tests/transforms/test_as_channel_last.py": { + "size": 1358 + }, + "tests/transforms/test_as_channel_lastd.py": { + "size": 1801 + }, + "tests/transforms/test_as_discrete.py": { + "size": 2604 + }, + "tests/transforms/test_as_discreted.py": { + "size": 3052 + }, + "tests/transforms/test_border_pad.py": { + "size": 1818 + }, + "tests/transforms/test_border_padd.py": { + "size": 1747 + }, + "tests/transforms/test_bounding_rect.py": { + "size": 1817 + }, + "tests/transforms/test_bounding_rectd.py": { + "size": 1841 + }, + "tests/transforms/test_cast_to_type.py": { + "size": 2433 + }, + "tests/transforms/test_cast_to_typed.py": { + "size": 2607 + }, + "tests/transforms/test_center_scale_crop.py": { + "size": 1918 + }, + "tests/transforms/test_center_scale_cropd.py": { + "size": 2064 + }, + "tests/transforms/test_center_spatial_crop.py": { + "size": 1983 + }, + "tests/transforms/test_center_spatial_cropd.py": { + "size": 2045 + }, + "tests/transforms/test_classes_to_indices.py": { + "size": 2919 + }, + "tests/transforms/test_classes_to_indicesd.py": { + "size": 3609 + }, + "tests/transforms/test_clip_intensity_percentiles.py": { + "size": 8869 + }, + "tests/transforms/test_clip_intensity_percentilesd.py": { + "size": 9036 + }, + "tests/transforms/test_compose_get_number_conversions.py": { + "size": 3922 + }, + "tests/transforms/test_concat_itemsd.py": { + "size": 3791 + }, + "tests/transforms/test_convert_to_multi_channel.py": { + "size": 1954 + }, + "tests/transforms/test_convert_to_multi_channeld.py": { + "size": 1322 + }, + "tests/transforms/test_copy_itemsd.py": { + "size": 3815 + }, + "tests/transforms/test_create_grid_and_affine.py": { + "size": 10471 + }, + "tests/transforms/test_crop_foreground.py": { + "size": 6729 + }, + "tests/transforms/test_crop_foregroundd.py": { + "size": 7626 + }, + "tests/transforms/test_cucim_dict_transform.py": { + "size": 5604 + }, + "tests/transforms/test_cucim_transform.py": { + "size": 5437 + }, + "tests/transforms/test_data_stats.py": { + "size": 6799 + }, + "tests/transforms/test_data_statsd.py": { + "size": 7421 + }, + "tests/transforms/test_delete_itemsd.py": { + "size": 2240 + }, + "tests/transforms/test_detect_envelope.py": { + "size": 5977 + }, + "tests/transforms/test_distance_transform_edt.py": { + "size": 7445 + }, + "tests/transforms/test_divisible_pad.py": { + "size": 1764 + }, + "tests/transforms/test_divisible_padd.py": { + "size": 1497 + }, + "tests/transforms/test_ensure_channel_first.py": { + "size": 4431 + }, + "tests/transforms/test_ensure_channel_firstd.py": { + "size": 3358 + }, + "tests/transforms/test_ensure_type.py": { + "size": 4580 + }, + "tests/transforms/test_ensure_typed.py": { + "size": 5343 + }, + "tests/transforms/test_fg_bg_to_indices.py": { + "size": 2578 + }, + "tests/transforms/test_fg_bg_to_indicesd.py": { + "size": 2791 + }, + "tests/transforms/test_fill_holes.py": { + "size": 5809 + }, + "tests/transforms/test_fill_holesd.py": { + "size": 5908 + }, + "tests/transforms/test_flatten_sub_keysd.py": { + "size": 2393 + }, + "tests/transforms/test_flip.py": { + "size": 3117 + }, + "tests/transforms/test_flipd.py": { + "size": 3585 + }, + "tests/transforms/test_fourier.py": { + "size": 2200 + }, + "tests/transforms/test_gaussian_sharpen.py": { + "size": 3095 + }, + "tests/transforms/test_gaussian_sharpend.py": { + "size": 3236 + }, + "tests/transforms/test_gaussian_smooth.py": { + "size": 3151 + }, + "tests/transforms/test_gaussian_smoothd.py": { + "size": 3291 + }, + "tests/transforms/test_generate_label_classes_crop_centers.py": { + "size": 2320 + }, + "tests/transforms/test_generate_pos_neg_label_crop_centers.py": { + "size": 2516 + }, + "tests/transforms/test_generate_spatial_bounding_box.py": { + "size": 3510 + }, + "tests/transforms/test_get_extreme_points.py": { + "size": 1700 + }, + "tests/transforms/test_gibbs_noise.py": { + "size": 2709 + }, + "tests/transforms/test_gibbs_noised.py": { + "size": 3228 + }, + "tests/transforms/test_grid_distortion.py": { + "size": 4472 + }, + "tests/transforms/test_grid_distortiond.py": { + "size": 3492 + }, + "tests/transforms/test_grid_split.py": { + "size": 3427 + }, + "tests/transforms/test_grid_splitd.py": { + "size": 4064 + }, + "tests/transforms/test_histogram_normalize.py": { + "size": 1970 + }, + "tests/transforms/test_histogram_normalized.py": { + "size": 2075 + }, + "tests/transforms/test_image_filter.py": { + "size": 11619 + }, + "tests/transforms/test_intensity_stats.py": { + "size": 2795 + }, + "tests/transforms/test_intensity_statsd.py": { + "size": 3595 + }, + "tests/transforms/test_inverse_collation.py": { + "size": 5451 + }, + "tests/transforms/test_k_space_spike_noise.py": { + "size": 2863 + }, + "tests/transforms/test_k_space_spike_noised.py": { + "size": 3511 + }, + "tests/transforms/test_keep_largest_connected_component.py": { + "size": 14759 + }, + "tests/transforms/test_keep_largest_connected_componentd.py": { + "size": 12553 + }, + "tests/transforms/test_label_filter.py": { + "size": 2508 + }, + "tests/transforms/test_label_to_contour.py": { + "size": 6772 + }, + "tests/transforms/test_label_to_contourd.py": { + "size": 6968 + }, + "tests/transforms/test_label_to_mask.py": { + "size": 2493 + }, + "tests/transforms/test_label_to_maskd.py": { + "size": 2647 + }, + "tests/transforms/test_load_image.py": { + "size": 21818 + }, + "tests/transforms/test_load_imaged.py": { + "size": 8802 + }, + "tests/transforms/test_load_spacing_orientation.py": { + "size": 6292 + }, + "tests/transforms/test_map_and_generate_sampling_centers.py": { + "size": 2755 + }, + "tests/transforms/test_map_binary_to_indices.py": { + "size": 2473 + }, + "tests/transforms/test_map_classes_to_indices.py": { + "size": 4336 + }, + "tests/transforms/test_map_label_value.py": { + "size": 3227 + }, + "tests/transforms/test_map_label_valued.py": { + "size": 2973 + }, + "tests/transforms/test_map_transform.py": { + "size": 1392 + }, + "tests/transforms/test_mask_intensity.py": { + "size": 3092 + }, + "tests/transforms/test_mask_intensityd.py": { + "size": 2706 + }, + "tests/transforms/test_mean_ensemble.py": { + "size": 2753 + }, + "tests/transforms/test_mean_ensembled.py": { + "size": 3374 + }, + "tests/transforms/test_median_smooth.py": { + "size": 1377 + }, + "tests/transforms/test_median_smoothd.py": { + "size": 2253 + }, + "tests/transforms/test_morphological_ops.py": { + "size": 4351 + }, + "tests/transforms/test_nifti_endianness.py": { + "size": 4036 + }, + "tests/transforms/test_normalize_intensity.py": { + "size": 5946 + }, + "tests/transforms/test_normalize_intensityd.py": { + "size": 3063 + }, + "tests/transforms/test_nvtx_decorator.py": { + "size": 10575 + }, + "tests/transforms/test_nvtx_transform.py": { + "size": 5140 + }, + "tests/transforms/test_orientation.py": { + "size": 10326 + }, + "tests/transforms/test_orientationd.py": { + "size": 5458 + }, + "tests/transforms/test_rand_adjust_contrast.py": { + "size": 1587 + }, + "tests/transforms/test_rand_adjust_contrastd.py": { + "size": 1605 + }, + "tests/transforms/test_rand_affine.py": { + "size": 7341 + }, + "tests/transforms/test_rand_affine_grid.py": { + "size": 9729 + }, + "tests/transforms/test_rand_affined.py": { + "size": 10893 + }, + "tests/transforms/test_rand_axis_flip.py": { + "size": 1841 + }, + "tests/transforms/test_rand_axis_flipd.py": { + "size": 1932 + }, + "tests/transforms/test_rand_bias_field.py": { + "size": 2647 + }, + "tests/transforms/test_rand_bias_fieldd.py": { + "size": 2440 + }, + "tests/transforms/test_rand_coarse_dropout.py": { + "size": 4183 + }, + "tests/transforms/test_rand_coarse_dropoutd.py": { + "size": 4078 + }, + "tests/transforms/test_rand_coarse_shuffle.py": { + "size": 2280 + }, + "tests/transforms/test_rand_coarse_shuffled.py": { + "size": 2063 + }, + "tests/transforms/test_rand_crop_by_label_classes.py": { + "size": 6398 + }, + "tests/transforms/test_rand_crop_by_label_classesd.py": { + "size": 5851 + }, + "tests/transforms/test_rand_crop_by_pos_neg_label.py": { + "size": 5602 + }, + "tests/transforms/test_rand_crop_by_pos_neg_labeld.py": { + "size": 6610 + }, + "tests/transforms/test_rand_cucim_dict_transform.py": { + "size": 6464 + }, + "tests/transforms/test_rand_cucim_transform.py": { + "size": 6319 + }, + "tests/transforms/test_rand_deform_grid.py": { + "size": 6344 + }, + "tests/transforms/test_rand_elastic_2d.py": { + "size": 4601 + }, + "tests/transforms/test_rand_elastic_3d.py": { + "size": 3767 + }, + "tests/transforms/test_rand_elasticd_2d.py": { + "size": 6686 + }, + "tests/transforms/test_rand_elasticd_3d.py": { + "size": 5872 + }, + "tests/transforms/test_rand_flip.py": { + "size": 2347 + }, + "tests/transforms/test_rand_flipd.py": { + "size": 2155 + }, + "tests/transforms/test_rand_gaussian_noise.py": { + "size": 1785 + }, + "tests/transforms/test_rand_gaussian_noised.py": { + "size": 2043 + }, + "tests/transforms/test_rand_gaussian_sharpen.py": { + "size": 4624 + }, + "tests/transforms/test_rand_gaussian_sharpend.py": { + "size": 4853 + }, + "tests/transforms/test_rand_gaussian_smooth.py": { + "size": 3426 + }, + "tests/transforms/test_rand_gaussian_smoothd.py": { + "size": 3505 + }, + "tests/transforms/test_rand_gibbs_noise.py": { + "size": 3737 + }, + "tests/transforms/test_rand_gibbs_noised.py": { + "size": 4414 + }, + "tests/transforms/test_rand_grid_distortion.py": { + "size": 4097 + }, + "tests/transforms/test_rand_grid_distortiond.py": { + "size": 3579 + }, + "tests/transforms/test_rand_histogram_shift.py": { + "size": 3368 + }, + "tests/transforms/test_rand_k_space_spike_noise.py": { + "size": 3491 + }, + "tests/transforms/test_rand_k_space_spike_noised.py": { + "size": 2603 + }, + "tests/transforms/test_rand_rician_noise.py": { + "size": 1845 + }, + "tests/transforms/test_rand_rician_noised.py": { + "size": 1969 + }, + "tests/transforms/test_rand_rotate.py": { + "size": 5679 + }, + "tests/transforms/test_rand_rotate90.py": { + "size": 4084 + }, + "tests/transforms/test_rand_rotate90d.py": { + "size": 4352 + }, + "tests/transforms/test_rand_rotated.py": { + "size": 6337 + }, + "tests/transforms/test_rand_scale_crop.py": { + "size": 3111 + }, + "tests/transforms/test_rand_scale_cropd.py": { + "size": 3532 + }, + "tests/transforms/test_rand_scale_intensity.py": { + "size": 2155 + }, + "tests/transforms/test_rand_scale_intensity_fixed_mean.py": { + "size": 1494 + }, + "tests/transforms/test_rand_scale_intensity_fixed_meand.py": { + "size": 1547 + }, + "tests/transforms/test_rand_scale_intensityd.py": { + "size": 2180 + }, + "tests/transforms/test_rand_shift_intensity.py": { + "size": 2090 + }, + "tests/transforms/test_rand_shift_intensityd.py": { + "size": 2812 + }, + "tests/transforms/test_rand_simulate_low_resolution.py": { + "size": 3290 + }, + "tests/transforms/test_rand_simulate_low_resolutiond.py": { + "size": 2924 + }, + "tests/transforms/test_rand_spatial_crop.py": { + "size": 4619 + }, + "tests/transforms/test_rand_spatial_crop_samples.py": { + "size": 5320 + }, + "tests/transforms/test_rand_spatial_crop_samplesd.py": { + "size": 6385 + }, + "tests/transforms/test_rand_spatial_cropd.py": { + "size": 4882 + }, + "tests/transforms/test_rand_std_shift_intensity.py": { + "size": 1604 + }, + "tests/transforms/test_rand_std_shift_intensityd.py": { + "size": 1458 + }, + "tests/transforms/test_rand_torchio.py": { + "size": 1988 + }, + "tests/transforms/test_rand_torchiod.py": { + "size": 1605 + }, + "tests/transforms/test_rand_zoom.py": { + "size": 4396 + }, + "tests/transforms/test_rand_zoomd.py": { + "size": 4270 + }, + "tests/transforms/test_randidentity.py": { + "size": 1659 + }, + "tests/transforms/test_random_order.py": { + "size": 5194 + }, + "tests/transforms/test_randtorchvisiond.py": { + "size": 2574 + }, + "tests/transforms/test_regularization.py": { + "size": 5923 + }, + "tests/transforms/test_remove_repeated_channel.py": { + "size": 1217 + }, + "tests/transforms/test_remove_repeated_channeld.py": { + "size": 1429 + }, + "tests/transforms/test_repeat_channel.py": { + "size": 1167 + }, + "tests/transforms/test_repeat_channeld.py": { + "size": 1333 + }, + "tests/transforms/test_resample_backends.py": { + "size": 3111 + }, + "tests/transforms/test_resample_to_match.py": { + "size": 4561 + }, + "tests/transforms/test_resample_to_matchd.py": { + "size": 3612 + }, + "tests/transforms/test_resampler.py": { + "size": 7020 + }, + "tests/transforms/test_resize.py": { + "size": 5519 + }, + "tests/transforms/test_resize_with_pad_or_crop.py": { + "size": 4035 + }, + "tests/transforms/test_resize_with_pad_or_cropd.py": { + "size": 3854 + }, + "tests/transforms/test_resized.py": { + "size": 6021 + }, + "tests/transforms/test_rotate.py": { + "size": 6769 + }, + "tests/transforms/test_rotate90.py": { + "size": 8067 + }, + "tests/transforms/test_rotate90d.py": { + "size": 4066 + }, + "tests/transforms/test_rotated.py": { + "size": 8365 + }, + "tests/transforms/test_save_classificationd.py": { + "size": 4138 + }, + "tests/transforms/test_save_image.py": { + "size": 2993 + }, + "tests/transforms/test_save_imaged.py": { + "size": 4615 + }, + "tests/transforms/test_savitzky_golay_smooth.py": { + "size": 2941 + }, + "tests/transforms/test_savitzky_golay_smoothd.py": { + "size": 3017 + }, + "tests/transforms/test_scale_intensity.py": { + "size": 3146 + }, + "tests/transforms/test_scale_intensity_fixed_mean.py": { + "size": 3646 + }, + "tests/transforms/test_scale_intensity_range.py": { + "size": 1740 + }, + "tests/transforms/test_scale_intensity_ranged.py": { + "size": 1633 + }, + "tests/transforms/test_scale_intensityd.py": { + "size": 2239 + }, + "tests/transforms/test_select_itemsd.py": { + "size": 1439 + }, + "tests/transforms/test_shift_intensity.py": { + "size": 1033 + }, + "tests/transforms/test_shift_intensityd.py": { + "size": 1654 + }, + "tests/transforms/test_signal_continuouswavelet.py": { + "size": 1621 + }, + "tests/transforms/test_signal_fillempty.py": { + "size": 1811 + }, + "tests/transforms/test_signal_fillemptyd.py": { + "size": 2022 + }, + "tests/transforms/test_signal_rand_add_gaussiannoise.py": { + "size": 1970 + }, + "tests/transforms/test_signal_rand_add_sine.py": { + "size": 1938 + }, + "tests/transforms/test_signal_rand_add_sine_partial.py": { + "size": 2136 + }, + "tests/transforms/test_signal_rand_add_squarepulse.py": { + "size": 2228 + }, + "tests/transforms/test_signal_rand_add_squarepulse_partial.py": { + "size": 2455 + }, + "tests/transforms/test_signal_rand_drop.py": { + "size": 1861 + }, + "tests/transforms/test_signal_rand_scale.py": { + "size": 1873 + }, + "tests/transforms/test_signal_rand_shift.py": { + "size": 2172 + }, + "tests/transforms/test_signal_remove_frequency.py": { + "size": 3257 + }, + "tests/transforms/test_smooth_field.py": { + "size": 6254 + }, + "tests/transforms/test_sobel_gradient.py": { + "size": 6832 + }, + "tests/transforms/test_sobel_gradientd.py": { + "size": 7992 + }, + "tests/transforms/test_spacing.py": { + "size": 13183 + }, + "tests/transforms/test_spacingd.py": { + "size": 7167 + }, + "tests/transforms/test_spatial_crop.py": { + "size": 3653 + }, + "tests/transforms/test_spatial_cropd.py": { + "size": 2455 + }, + "tests/transforms/test_spatial_pad.py": { + "size": 2209 + }, + "tests/transforms/test_spatial_padd.py": { + "size": 1636 + }, + "tests/transforms/test_spatial_resample.py": { + "size": 9735 + }, + "tests/transforms/test_squeezedim.py": { + "size": 2407 + }, + "tests/transforms/test_squeezedimd.py": { + "size": 3221 + }, + "tests/transforms/test_std_shift_intensity.py": { + "size": 3337 + }, + "tests/transforms/test_std_shift_intensityd.py": { + "size": 3150 + }, + "tests/transforms/test_threshold_intensity.py": { + "size": 1505 + }, + "tests/transforms/test_threshold_intensityd.py": { + "size": 2060 + }, + "tests/transforms/test_to_contiguous.py": { + "size": 1932 + }, + "tests/transforms/test_to_cupy.py": { + "size": 4557 + }, + "tests/transforms/test_to_cupyd.py": { + "size": 3066 + }, + "tests/transforms/test_to_device.py": { + "size": 1295 + }, + "tests/transforms/test_to_deviced.py": { + "size": 1362 + }, + "tests/transforms/test_to_numpy.py": { + "size": 3385 + }, + "tests/transforms/test_to_numpyd.py": { + "size": 2695 + }, + "tests/transforms/test_to_pil.py": { + "size": 1723 + }, + "tests/transforms/test_to_pild.py": { + "size": 1834 + }, + "tests/transforms/test_to_tensor.py": { + "size": 2114 + }, + "tests/transforms/test_to_tensord.py": { + "size": 2600 + }, + "tests/transforms/test_torchio.py": { + "size": 1413 + }, + "tests/transforms/test_torchiod.py": { + "size": 1549 + }, + "tests/transforms/test_torchvision.py": { + "size": 2618 + }, + "tests/transforms/test_torchvisiond.py": { + "size": 2464 + }, + "tests/transforms/test_transform.py": { + "size": 1897 + }, + "tests/transforms/test_transpose.py": { + "size": 1357 + }, + "tests/transforms/test_transposed.py": { + "size": 1889 + }, + "tests/transforms/test_ultrasound_confidence_map_transform.py": { + "size": 23696 + }, + "tests/transforms/test_utils_pytorch_numpy_unification.py": { + "size": 3666 + }, + "tests/transforms/test_vote_ensemble.py": { + "size": 2671 + }, + "tests/transforms/test_vote_ensembled.py": { + "size": 3627 + }, + "tests/transforms/test_with_allow_missing_keys.py": { + "size": 3057 + }, + "tests/transforms/test_zoom.py": { + "size": 4848 + }, + "tests/transforms/test_zoomd.py": { + "size": 3480 + }, + "tests/transforms/transform/__init__.py": { + "size": 573 + }, + "tests/transforms/transform/test_randomizable.py": { + "size": 1567 + }, + "tests/transforms/transform/test_randomizable_transform_type.py": { + "size": 1202 + }, + "tests/transforms/utility/__init__.py": { + "size": 573 + }, + "tests/transforms/utility/test_apply_transform_to_points.py": { + "size": 3547 + }, + "tests/transforms/utility/test_apply_transform_to_pointsd.py": { + "size": 7224 + }, + "tests/transforms/utility/test_identity.py": { + "size": 1027 + }, + "tests/transforms/utility/test_identityd.py": { + "size": 1092 + }, + "tests/transforms/utility/test_lambda.py": { + "size": 2359 + }, + "tests/transforms/utility/test_lambdad.py": { + "size": 3127 + }, + "tests/transforms/utility/test_rand_lambda.py": { + "size": 2985 + }, + "tests/transforms/utility/test_rand_lambdad.py": { + "size": 2806 + }, + "tests/transforms/utility/test_simulatedelay.py": { + "size": 1292 + }, + "tests/transforms/utility/test_simulatedelayd.py": { + "size": 1322 + }, + "tests/transforms/utility/test_splitdim.py": { + "size": 1863 + }, + "tests/transforms/utility/test_splitdimd.py": { + "size": 3684 + }, + "tests/transforms/utils/__init__.py": { + "size": 573 + }, + "tests/transforms/utils/test_correct_crop_centers.py": { + "size": 1393 + }, + "tests/transforms/utils/test_get_unique_labels.py": { + "size": 1686 + }, + "tests/transforms/utils/test_print_transform_backends.py": { + "size": 1050 + }, + "tests/transforms/utils/test_soft_clip.py": { + "size": 4446 + }, + "tests/utils/__init__.py": { + "size": 573 + }, + "tests/utils/enums/__init__.py": { + "size": 573 + }, + "tests/utils/enums/test_hovernet_loss.py": { + "size": 6768 + }, + "tests/utils/enums/test_ordering.py": { + "size": 9366 + }, + "tests/utils/enums/test_wsireader.py": { + "size": 26072 + }, + "tests/utils/misc/__init__.py": { + "size": 573 + }, + "tests/utils/misc/test_ensure_tuple.py": { + "size": 1794 + }, + "tests/utils/misc/test_monai_env_vars.py": { + "size": 1468 + }, + "tests/utils/misc/test_monai_utils_misc.py": { + "size": 3791 + }, + "tests/utils/misc/test_str2bool.py": { + "size": 1294 + }, + "tests/utils/misc/test_str2list.py": { + "size": 1233 + }, + "tests/utils/test_alias.py": { + "size": 1564 + }, + "tests/utils/test_component_store.py": { + "size": 2375 + }, + "tests/utils/test_deprecated.py": { + "size": 14755 + }, + "tests/utils/test_enum_bound_interp.py": { + "size": 3228 + }, + "tests/utils/test_evenly_divisible_all_gather_dist.py": { + "size": 1874 + }, + "tests/utils/test_get_package_version.py": { + "size": 1114 + }, + "tests/utils/test_handler_logfile.py": { + "size": 2560 + }, + "tests/utils/test_handler_metric_logger.py": { + "size": 1925 + }, + "tests/utils/test_list_to_dict.py": { + "size": 1553 + }, + "tests/utils/test_look_up_option.py": { + "size": 2993 + }, + "tests/utils/test_optional_import.py": { + "size": 3004 + }, + "tests/utils/test_pad_mode.py": { + "size": 1672 + }, + "tests/utils/test_profiling.py": { + "size": 6688 + }, + "tests/utils/test_rankfilter_dist.py": { + "size": 2487 + }, + "tests/utils/test_require_pkg.py": { + "size": 2284 + }, + "tests/utils/test_sample_slices.py": { + "size": 1879 + }, + "tests/utils/test_set_determinism.py": { + "size": 3039 + }, + "tests/utils/test_squeeze_unsqueeze.py": { + "size": 2857 + }, + "tests/utils/test_state_cacher.py": { + "size": 2650 + }, + "tests/utils/test_torchscript_utils.py": { + "size": 4181 + }, + "tests/utils/test_version.py": { + "size": 2498 + }, + "tests/utils/test_version_after.py": { + "size": 2068 + }, + "tests/utils/type_conversion/__init__.py": { + "size": 573 + }, + "tests/utils/type_conversion/test_convert_data_type.py": { + "size": 6219 + }, + "tests/utils/type_conversion/test_get_equivalent_dtype.py": { + "size": 2287 + }, + "tests/utils/type_conversion/test_safe_dtype_range.py": { + "size": 3963 + }, + "tests/visualize/__init__.py": { + "size": 573 + }, + "tests/visualize/test_img2tensorboard.py": { + "size": 1763 + }, + "tests/visualize/test_occlusion_sensitivity.py": { + "size": 4424 + }, + "tests/visualize/test_plot_2d_or_3d_image.py": { + "size": 2656 + }, + "tests/visualize/test_vis_cam.py": { + "size": 3112 + }, + "tests/visualize/test_vis_gradcam.py": { + "size": 6937 + }, + "tests/visualize/utils/__init__.py": { + "size": 573 + }, + "tests/visualize/utils/test_blend_images.py": { + "size": 2274 + }, + "tests/visualize/utils/test_matshow3d.py": { + "size": 5021 + }, + "versioneer.py": { + "size": 81097 + } + }, + "processed_by": "zip_fallback", + "success": true + }, + "structure": { + "packages": [ + "source.monai", + "source.monai._extensions", + "source.monai.apps", + "source.monai.auto3dseg", + "source.monai.bundle", + "source.monai.config", + "source.monai.data", + "source.monai.engines", + "source.monai.fl", + "source.monai.handlers", + "source.monai.inferers", + "source.monai.losses", + "source.monai.metrics", + "source.monai.networks", + "source.monai.optimizers", + "source.monai.transforms", + "source.monai.utils", + "source.monai.visualize", + "source.tests", + "source.tests.apps", + "source.tests.bundle", + "source.tests.config", + "source.tests.engines", + "source.tests.fl", + "source.tests.handlers", + "source.tests.inferers", + "source.tests.integration", + "source.tests.losses", + "source.tests.metrics", + "source.tests.networks", + "source.tests.optimizers", + "source.tests.profile_subclass", + "source.tests.transforms", + "source.tests.utils", + "source.tests.visualize" + ] + }, + "dependencies": { + "has_environment_yml": false, + "has_requirements_txt": true, + "pyproject": true, + "setup_cfg": true, + "setup_py": true + }, + "entry_points": { + "imports": [], + "cli": [], + "modules": [] + }, + "llm_analysis": { + "core_modules": [ + { + "package": "source.monai.apps", + "module": "auto3dseg", + "functions": [ + "auto_runner", + "bundle_gen", + "data_analyzer" + ], + "classes": [ + "AutoRunner", + "BundleGen", + "DataAnalyzer" + ], + "description": "Modules for automated 3D segmentation tasks." + }, + { + "package": "source.monai.transforms", + "module": "compose", + "functions": [ + "Compose", + "SomeOf" + ], + "classes": [ + "Compose", + "SomeOf" + ], + "description": "Compose multiple transforms together." + }, + { + "package": "source.monai.networks", + "module": "nets", + "functions": [ + "UNet", + "ResNet" + ], + "classes": [ + "UNet", + "ResNet" + ], + "description": "Network architectures for medical imaging." + } + ], + "cli_commands": [ + { + "name": "monai-bundle", + "module": "source.monai.bundle.__main__", + "description": "CLI for managing MONAI bundles." + } + ], + "import_strategy": { + "primary": "import", + "fallback": "blackbox", + "confidence": 0.85 + }, + "dependencies": { + "required": [ + "numpy", + "torch", + "nibabel" + ], + "optional": [ + "matplotlib", + "scipy" + ] + }, + "risk_assessment": { + "import_feasibility": 0.8, + "intrusiveness_risk": "medium", + "complexity": "complex" + } + }, + "deepwiki_analysis": { + "repo_url": "https://github.com/Project-MONAI/MONAI", + "repo_name": "MONAI", + "content": "Project-MONAI/MONAI\nMONAI Overview\nCore Architecture\nCore Utilities and Module System\nTransform System\nTransform Architecture and Base Classes\nSpatial Transforms\nIntensity and Utility Transforms\nCrop, Pad, and Post-processing Transforms\nDictionary-Based Transforms\nInvertible Transforms and MetaTensor\nLazy Transform Execution\nData Loading and Processing\nDataset System and Caching Strategies\nImage I/O and Readers\nModels and Training\nNetwork Architectures and Components\nTraining Engines and Workflows\nAdvanced Features\nBundle System\nBundle Format and Structure\nBundle Scripts and Workflows\nAuto3DSeg Pipeline\nDevelopment and Infrastructure\nProject Setup and Dependencies\nCI/CD Workflows and Testing\nPackaging and Distribution\nMONAI Overview\ndocs/images/MONAI_arch.png\ndocs/images/MONAI_bundle_cloud.png\ndocs/images/MONAI_clouds.png\ndocs/images/MONAI_map_cloud.png\ndocs/source/bundle_intro.rst\ndocs/source/index.rst\ndocs/source/mb_specification.rst\ntests/testing_data/metadata.json\nPurpose and Scope\nThis document provides a high-level introduction to the MONAI framework, its architecture, and core concepts. It covers the overall organization of the codebase, key modules, and how they interact to support deep learning workflows for medical imaging.\nFor detailed information about specific subsystems:\nCore utilities and module management: seeCore Utilities and Module System\nTransform system architecture: seeTransform System\nData loading and caching: seeData Loading and Processing\nNeural network architectures: seeNetwork Architectures and Components\nTraining workflows: seeTraining Engines and Workflows\nBundle format and distribution: seeBundle System\nDevelopment setup and CI/CD: seeDevelopment and Infrastructure\nWhat is MONAI?\nMONAI(Medical Open Network for AI) is a PyTorch-based, open-source framework for deep learning in healthcare imaging. It is part of the PyTorch Ecosystem and provides domain-specific implementations for medical image analysis tasks including segmentation, classification, detection, and registration.\nCore Ambitions:\nDevelop a collaborative community of academic, industrial, and clinical researchers\nCreate state-of-the-art, end-to-end training workflows for healthcare imaging\nProvide researchers with optimized and standardized tools for deep learning model development\nKey Features:\nFlexible preprocessing for multi-dimensional medical imaging data\nCompositional and portable APIs for integration into existing workflows\nDomain-specific network architectures, loss functions, and evaluation metrics\nCustomizable design supporting varying levels of user expertise\nMulti-GPU and multi-node data parallelism support\nSources:README.md1-26docs/source/index.rst1-44\nArchitecture Overview\nMONAI's architecture is organized into distinct layers, each providing specific functionality for medical imaging workflows. The diagram below maps high-level system components to their corresponding code modules.\nmonai/Core UtilitiesHigh-Level SystemsTraining InfrastructureNeural NetworksCore Data Pipelinesupportssupportssupportsmonai.utilsmodule, misc, enumsmonai.dataDataset, DataLoader,CacheDataset, ImageReadermonai.transformsCompose, Spatial, Intensity,MapTransform, InvertibleTransformmonai.enginesTrainer, Evaluator, Workflowmonai.networksnets/, blocks/, layers/monai.lossesDiceLoss, FocalLoss, etc.monai.metricsDiceMetric, ConfusionMatrixMetricmonai.handlersCheckpointSaver, StatsHandler,ValidationHandlermonai.bundleConfigWorkflow, scripts/monai.apps.auto3dsegAutoRunner, BundleGenmonai.configdeviceconfig, type_definitions\nCore Utilities\nHigh-Level Systems\nTraining Infrastructure\nNeural Networks\nCore Data Pipeline\nmonai.utilsmodule, misc, enums\nmonai.dataDataset, DataLoader,CacheDataset, ImageReader\nmonai.transformsCompose, Spatial, Intensity,MapTransform, InvertibleTransform\nmonai.enginesTrainer, Evaluator, Workflow\nmonai.networksnets/, blocks/, layers/\nmonai.lossesDiceLoss, FocalLoss, etc.\nmonai.metricsDiceMetric, ConfusionMatrixMetric\nmonai.handlersCheckpointSaver, StatsHandler,ValidationHandler\nmonai.bundleConfigWorkflow, scripts/\nmonai.apps.auto3dsegAutoRunner, BundleGen\nmonai.configdeviceconfig, type_definitions\nSources:README.md20-44docs/source/index.rst14-34\nCore Module Organization\nThe MONAI codebase is organized into the following primary modules located under themonai/directory:\nData Pipeline (monai.data)\nHandles medical image loading, caching, and batching:\nDataset: Base dataset class and specialized variants (CacheDataset,PersistentDataset,LMDBDataset)\nCacheDataset\nPersistentDataset\nLMDBDataset\nDataLoader: PyTorch-compatible data loaders with custom collation\nImageReader: Format-specific readers (ITKReader,NibabelReader,PydicomReader)\nImageReader\nNibabelReader\nPydicomReader\nDecathalon dataset utilities\nKey Classes:Dataset,CacheDataset,DataLoader,ImageReader\nCacheDataset\nImageReader\nTransforms (monai.transforms)\nmonai.transforms\nComposable preprocessing and augmentation operations:\nBase Classes:Transform,MapTransform,RandomizableTransform,InvertibleTransform,LazyTransform\nMapTransform\nRandomizableTransform\nInvertibleTransform\nLazyTransform\nCategories:Spatial (resampling, rotation), Intensity (normalization, scaling), Crop/Pad, Post-processing\nDictionary Pattern:Transforms ending with 'd' suffix operate on dictionaries (e.g.,LoadImaged,Spacingd)\nKey Classes:Compose,LoadImage,Spacing,NormalizeIntensity,RandCropByPosNegLabel\nNormalizeIntensity\nRandCropByPosNegLabel\nNetworks (monai.networks)\nmonai.networks\nNeural network architectures and building blocks:\nnets/: Complete architectures (UNet, ResNet, DenseNet, ViT, UNETR, etc.)\nblocks/: Reusable network components (Convolution, Residual, Attention blocks)\nlayers/: Low-level layer implementations (Act, Norm, Conv, Dropout)\nKey Classes:UNet,DynUNet,SegResNet,UNETR,SwinUNETR\nTraining & Evaluation (monai.engines,monai.metrics,monai.handlers)\nmonai.engines\nmonai.metrics\nmonai.handlers\nEvent-driven training infrastructure built on PyTorch Ignite:\nEngines:Workflow,SupervisedTrainer,SupervisedEvaluator,EnsembleEvaluator\nSupervisedTrainer\nSupervisedEvaluator\nEnsembleEvaluator\nMetrics:DiceMetric,HausdorffDistanceMetric,ConfusionMatrixMetric,ROCAUCMetric\nHausdorffDistanceMetric\nConfusionMatrixMetric\nROCAUCMetric\nHandlers:CheckpointSaver,StatsHandler,TensorBoardHandler,ValidationHandler\nCheckpointSaver\nStatsHandler\nTensorBoardHandler\nValidationHandler\nKey Classes:SupervisedTrainer,SupervisedEvaluator,DiceMetric,CheckpointSaver\nSupervisedTrainer\nSupervisedEvaluator\nCheckpointSaver\nBundle System (monai.bundle)\nmonai.bundle\nStandardized model packaging and distribution:\nConfiguration-based workflows (ConfigWorkflow)\nConfigWorkflow\nBundle scripts for download, load, run, verify operations\nModel export utilities (TorchScript, ONNX, TensorRT)\nKey Classes:ConfigWorkflow,ConfigParser\nConfigWorkflow\nConfigParser\nAuto3DSeg (monai.apps.auto3dseg)\nmonai.apps.auto3dseg\nAutomated pipeline for 3D segmentation tasks:\nAutoRunner: Orchestrates the complete automated workflow\nBundleGen: Generates algorithm bundles from templates\nAlgoEnsemble: Combines multiple models for inference\nAlgoEnsemble\nKey Classes:AutoRunner,BundleGen,AlgoEnsemble\nAlgoEnsemble\nSources:README.md27-44docs/source/index.rst27-34\nKey Abstractions and Design Patterns\nMONAI'sMetaTensorextends PyTorch tensors with metadata tracking capabilities. It stores:\nAffine transformations for spatial coordinates\nApplied operations history (applied_operations)\napplied_operations\nPending lazy operations (pending_operations)\npending_operations\nOriginal image metadata (spacing, orientation, etc.)\nThis enables invertible transforms and preserves provenance throughout the pipeline.\nKey Class:monai.data.MetaTensor\nmonai.data.MetaTensor\nTransform Composition\nTransforms follow a functional composition pattern where operations are chained:\nRaw ImageComposeLoadImageEnsureChannelFirstSpacingNormalizeIntensityRandSpatialCropPreprocessed Tensor\nEnsureChannelFirst\nNormalizeIntensity\nRandSpatialCrop\nPreprocessed Tensor\nDictionary-based transforms (suffix 'd') operate on dictionaries of data items, enabling coordinated transformations across images, labels, and metadata.\nKey Classes:Compose,MapTransform,InvertibleTransform\nMapTransform\nInvertibleTransform\nEvent-Driven Training\nTraining engines use an event system built on PyTorch Ignite:\nEngine / WorkflowEPOCH_STARTEDITERATION_STARTEDFORWARD_COMPLETEDBACKWARD_COMPLETEDITERATION_COMPLETEDEPOCH_COMPLETEDMetrics UpdateStatsHandlerCheckpointSaverValidationHandler\nEngine / Workflow\nEPOCH_STARTED\nITERATION_STARTED\nFORWARD_COMPLETED\nBACKWARD_COMPLETED\nITERATION_COMPLETED\nEPOCH_COMPLETED\nMetrics Update\nStatsHandler\nCheckpointSaver\nValidationHandler\nHandlers attach to events to perform actions like logging, checkpointing, or validation.\nKey Classes:Workflow,IterationEvents,Handler\nIterationEvents\nBundle Configuration System\nBundles use JSON/YAML configurations with a reference syntax for defining workflows:\n@network_defreferences network instantiation", + "model": "gpt-4o-2024-08-06", + "source": "selenium", + "success": true + }, + "deepwiki_options": { + "enabled": true, + "model": "gpt-4o-2024-08-06" + }, + "risk": { + "import_feasibility": 0.8, + "intrusiveness_risk": "medium", + "complexity": "complex" + } +} \ No newline at end of file diff --git a/MONAI/mcp_output/diff_report.md b/MONAI/mcp_output/diff_report.md new file mode 100644 index 0000000000000000000000000000000000000000..4d221a2e3c779c00a22f4fa1250bd775169620c8 --- /dev/null +++ b/MONAI/mcp_output/diff_report.md @@ -0,0 +1,61 @@ +# MONAI Project Difference Report + +**Date:** February 3, 2026 +**Time:** 13:35:35 +**Repository:** MONAI +**Project Type:** Python Library +**Intrusiveness:** None +**Workflow Status:** Success +**Test Status:** Failed + +## Project Overview + +The MONAI project is a Python library designed to facilitate the development of deep learning models in medical imaging. It provides a comprehensive set of tools and functionalities to streamline the process of creating, training, and deploying models for various medical imaging tasks. + +## Difference Analysis + +### New Files Added + +Since the last update, the MONAI project has introduced 8 new files. These files are likely to include new features, enhancements, or additional modules that expand the library's capabilities. However, the specific content and purpose of these files have not been detailed in this report. + +### Modified Files + +There have been no modifications to existing files in this update. This suggests that the recent changes are entirely additive, focusing on expanding the library's functionality without altering the current codebase. + +## Technical Analysis + +### Workflow Status + +The workflow status is marked as "success," indicating that the integration and deployment processes for the new files were executed without any errors or interruptions. This suggests that the new additions were integrated smoothly into the existing framework. + +### Test Status + +Despite the successful workflow, the test status is marked as "failed." This indicates that one or more tests did not pass, which could be due to issues with the new files or their integration with existing components. The failure in testing suggests potential bugs or compatibility issues that need to be addressed. + +## Recommendations and Improvements + +1. **Investigate Test Failures:** Conduct a thorough analysis of the failed tests to identify the root causes. This may involve reviewing the new files for errors, ensuring compatibility with existing modules, and verifying that all dependencies are correctly configured. + +2. **Enhance Testing Framework:** Consider expanding the test coverage to include the new functionalities introduced by the recent files. This will help in identifying potential issues early in the development cycle. + +3. **Documentation Update:** Ensure that the documentation is updated to reflect the new features and functionalities. This will aid users in understanding and utilizing the new capabilities effectively. + +4. **Community Feedback:** Engage with the user community to gather feedback on the new features. This can provide valuable insights into potential improvements and user needs. + +## Deployment Information + +The deployment of the new files was successful, as indicated by the workflow status. However, due to the test failures, it is recommended to hold off on any production deployment until the issues are resolved. This will prevent potential disruptions or errors in user environments. + +## Future Planning + +1. **Bug Fixes and Patches:** Prioritize resolving the test failures and releasing patches to address any identified issues. + +2. **Feature Expansion:** Continue to expand the library's capabilities by introducing new features and enhancements based on user feedback and technological advancements in medical imaging. + +3. **Community Engagement:** Strengthen community engagement through forums, webinars, and collaborative projects to foster a supportive ecosystem around the MONAI library. + +4. **Performance Optimization:** Explore opportunities to optimize the library's performance, ensuring it remains efficient and scalable for various medical imaging applications. + +## Conclusion + +The recent update to the MONAI project introduces new functionalities that enhance the library's capabilities. However, the test failures highlight the need for further investigation and resolution before these changes can be fully integrated into production environments. By addressing these issues and implementing the recommended improvements, the MONAI project can continue to provide valuable tools for the medical imaging community. \ No newline at end of file diff --git a/MONAI/mcp_output/mcp_plugin/__init__.py b/MONAI/mcp_output/mcp_plugin/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/MONAI/mcp_output/mcp_plugin/adapter.py b/MONAI/mcp_output/mcp_plugin/adapter.py new file mode 100644 index 0000000000000000000000000000000000000000..7f11be39df7abc8655c6db703492ed69c9be5fa7 --- /dev/null +++ b/MONAI/mcp_output/mcp_plugin/adapter.py @@ -0,0 +1,181 @@ +import os +import sys + +# Path settings +source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source") +sys.path.insert(0, source_path) + +# Import statements +try: + from monai.bundle import ConfigWorkflow, ConfigParser + from monai.apps.auto3dseg import AutoRunner, BundleGen, AlgoEnsemble + from monai.data import MetaTensor + from monai.transforms import Compose, MapTransform, InvertibleTransform + from monai.engines import Workflow, IterationEvents +except ImportError as e: + print(f"Import failed: {e}. Please ensure all dependencies are correctly installed.") + +class Adapter: + """ + Adapter class for the MCP plugin, providing methods to interact with MONAI's core functionalities. + """ + + def __init__(self): + self.mode = "import" + + # -------------------- Bundle Module -------------------- + + def create_config_workflow(self, config_path): + """ + Create a ConfigWorkflow instance. + + :param config_path: Path to the configuration file. + :return: Dictionary with status and ConfigWorkflow instance or error message. + """ + try: + workflow = ConfigWorkflow(config_path) + return {"status": "success", "workflow": workflow} + except Exception as e: + return {"status": "error", "message": str(e)} + + def parse_config(self, config_path): + """ + Parse a configuration file using ConfigParser. + + :param config_path: Path to the configuration file. + :return: Dictionary with status and parsed configuration or error message. + """ + try: + config = ConfigParser(config_path) + return {"status": "success", "config": config} + except Exception as e: + return {"status": "error", "message": str(e)} + + # -------------------- Auto3DSeg Module -------------------- + + def run_auto_runner(self, config): + """ + Run the AutoRunner with the given configuration. + + :param config: Configuration for the AutoRunner. + :return: Dictionary with status and AutoRunner instance or error message. + """ + try: + runner = AutoRunner(config) + runner.run() + return {"status": "success", "runner": runner} + except Exception as e: + return {"status": "error", "message": str(e)} + + def generate_bundle(self, template_path): + """ + Generate a bundle using BundleGen. + + :param template_path: Path to the template. + :return: Dictionary with status and BundleGen instance or error message. + """ + try: + bundle_gen = BundleGen(template_path) + return {"status": "success", "bundle_gen": bundle_gen} + except Exception as e: + return {"status": "error", "message": str(e)} + + def create_algo_ensemble(self, models): + """ + Create an algorithm ensemble using AlgoEnsemble. + + :param models: List of models to include in the ensemble. + :return: Dictionary with status and AlgoEnsemble instance or error message. + """ + try: + ensemble = AlgoEnsemble(models) + return {"status": "success", "ensemble": ensemble} + except Exception as e: + return {"status": "error", "message": str(e)} + + # -------------------- Data Module -------------------- + + def create_meta_tensor(self, data, meta=None): + """ + Create a MetaTensor instance. + + :param data: Data for the MetaTensor. + :param meta: Metadata for the MetaTensor. + :return: Dictionary with status and MetaTensor instance or error message. + """ + try: + meta_tensor = MetaTensor(data, meta) + return {"status": "success", "meta_tensor": meta_tensor} + except Exception as e: + return {"status": "error", "message": str(e)} + + # -------------------- Transforms Module -------------------- + + def compose_transforms(self, transforms): + """ + Compose a series of transforms. + + :param transforms: List of transforms to compose. + :return: Dictionary with status and Compose instance or error message. + """ + try: + composed = Compose(transforms) + return {"status": "success", "composed": composed} + except Exception as e: + return {"status": "error", "message": str(e)} + + def map_transform(self, data, transform): + """ + Apply a MapTransform to the data. + + :param data: Data to transform. + :param transform: Transform to apply. + :return: Dictionary with status and transformed data or error message. + """ + try: + mapped_data = MapTransform(data, transform) + return {"status": "success", "mapped_data": mapped_data} + except Exception as e: + return {"status": "error", "message": str(e)} + + def invertible_transform(self, data, transform): + """ + Apply an InvertibleTransform to the data. + + :param data: Data to transform. + :param transform: Transform to apply. + :return: Dictionary with status and transformed data or error message. + """ + try: + inverted_data = InvertibleTransform(data, transform) + return {"status": "success", "inverted_data": inverted_data} + except Exception as e: + return {"status": "error", "message": str(e)} + + # -------------------- Engines Module -------------------- + + def create_workflow(self, config): + """ + Create a Workflow instance. + + :param config: Configuration for the workflow. + :return: Dictionary with status and Workflow instance or error message. + """ + try: + workflow = Workflow(config) + return {"status": "success", "workflow": workflow} + except Exception as e: + return {"status": "error", "message": str(e)} + + def handle_iteration_events(self, workflow): + """ + Handle iteration events in a workflow. + + :param workflow: Workflow instance. + :return: Dictionary with status and event handling result or error message. + """ + try: + events = IterationEvents(workflow) + return {"status": "success", "events": events} + except Exception as e: + return {"status": "error", "message": str(e)} \ No newline at end of file diff --git a/MONAI/mcp_output/mcp_plugin/main.py b/MONAI/mcp_output/mcp_plugin/main.py new file mode 100644 index 0000000000000000000000000000000000000000..fca6ec384e22f703b287550e94cc00baaaa4c4a7 --- /dev/null +++ b/MONAI/mcp_output/mcp_plugin/main.py @@ -0,0 +1,13 @@ +""" +MCP Service Auto-Wrapper - Auto-generated +""" +from mcp_service import create_app + +def main(): + """Main entry point""" + app = create_app() + return app + +if __name__ == "__main__": + app = main() + app.run() \ No newline at end of file diff --git a/MONAI/mcp_output/mcp_plugin/mcp_service.py b/MONAI/mcp_output/mcp_plugin/mcp_service.py new file mode 100644 index 0000000000000000000000000000000000000000..1cf327435fd795a99d3fe83f9f6e5770185f42a1 --- /dev/null +++ b/MONAI/mcp_output/mcp_plugin/mcp_service.py @@ -0,0 +1,139 @@ +import os +import sys + +# Add the local source directory to sys.path +source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source") +if source_path not in sys.path: + sys.path.insert(0, source_path) + +from fastmcp import FastMCP + +# Import core modules from the local source directory +from monai.data import Dataset, DataLoader +from monai.transforms import Compose, LoadImage, NormalizeIntensity +from monai.networks.nets import UNet +from monai.engines import SupervisedTrainer +from monai.metrics import DiceMetric + +# Create the FastMCP service application +mcp = FastMCP("monai_service") + +@mcp.tool(name="load_dataset", description="Load a dataset using MONAI's Dataset class") +def load_dataset(data_dir: str) -> dict: + """ + Load a dataset from the specified directory. + + Parameters: + - data_dir (str): The directory containing the dataset. + + Returns: + - dict: A dictionary containing success status and the dataset object or error message. + """ + try: + dataset = Dataset(data_dir) + return {"success": True, "result": dataset} + except Exception as e: + return {"success": False, "error": str(e)} + +@mcp.tool(name="create_dataloader", description="Create a DataLoader for the dataset") +def create_dataloader(dataset: Dataset, batch_size: int) -> dict: + """ + Create a DataLoader for the given dataset. + + Parameters: + - dataset (Dataset): The dataset to load. + - batch_size (int): The number of samples per batch. + + Returns: + - dict: A dictionary containing success status and the DataLoader object or error message. + """ + try: + dataloader = DataLoader(dataset, batch_size=batch_size) + return {"success": True, "result": dataloader} + except Exception as e: + return {"success": False, "error": str(e)} + +@mcp.tool(name="apply_transforms", description="Apply transforms to the dataset") +def apply_transforms(dataset: Dataset) -> dict: + """ + Apply a series of transforms to the dataset. + + Parameters: + - dataset (Dataset): The dataset to transform. + + Returns: + - dict: A dictionary containing success status and the transformed dataset or error message. + """ + try: + transforms = Compose([LoadImage(), NormalizeIntensity()]) + transformed_dataset = [transforms(item) for item in dataset] + return {"success": True, "result": transformed_dataset} + except Exception as e: + return {"success": False, "error": str(e)} + +@mcp.tool(name="initialize_unet", description="Initialize a UNet model") +def initialize_unet(spatial_dims: int, in_channels: int, out_channels: int) -> dict: + """ + Initialize a UNet model with the specified parameters. + + Parameters: + - spatial_dims (int): The number of spatial dimensions. + - in_channels (int): The number of input channels. + - out_channels (int): The number of output channels. + + Returns: + - dict: A dictionary containing success status and the UNet model or error message. + """ + try: + model = UNet(spatial_dims=spatial_dims, in_channels=in_channels, out_channels=out_channels) + return {"success": True, "result": model} + except Exception as e: + return {"success": False, "error": str(e)} + +@mcp.tool(name="train_model", description="Train a model using MONAI's SupervisedTrainer") +def train_model(model, dataloader: DataLoader, max_epochs: int) -> dict: + """ + Train a model using the specified dataloader and number of epochs. + + Parameters: + - model: The model to train. + - dataloader (DataLoader): The DataLoader for training data. + - max_epochs (int): The maximum number of training epochs. + + Returns: + - dict: A dictionary containing success status and training results or error message. + """ + try: + trainer = SupervisedTrainer(max_epochs=max_epochs, train_data_loader=dataloader, network=model) + trainer.run() + return {"success": True, "result": "Training completed"} + except Exception as e: + return {"success": False, "error": str(e)} + +@mcp.tool(name="calculate_dice", description="Calculate Dice metric for model evaluation") +def calculate_dice(predictions, targets) -> dict: + """ + Calculate the Dice metric for the given predictions and targets. + + Parameters: + - predictions: The model predictions. + - targets: The ground truth targets. + + Returns: + - dict: A dictionary containing success status and the Dice score or error message. + """ + try: + dice_metric = DiceMetric() + dice_score = dice_metric(predictions, targets) + return {"success": True, "result": dice_score} + except Exception as e: + return {"success": False, "error": str(e)} + +def create_app() -> FastMCP: + """ + Create and return the FastMCP application instance. + + Returns: + - FastMCP: The FastMCP application instance. + """ + return mcp \ No newline at end of file diff --git a/MONAI/mcp_output/requirements.txt b/MONAI/mcp_output/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..552ad301894bb46c4b9a01ee59d239ac5900e764 --- /dev/null +++ b/MONAI/mcp_output/requirements.txt @@ -0,0 +1,7 @@ +fastmcp +fastapi +uvicorn[standard] +pydantic>=2.0.0 +torch>=2.4.1; platform_system != "Windows" +numpy>=1.24,<3.0 +nibabel diff --git a/MONAI/mcp_output/start_mcp.py b/MONAI/mcp_output/start_mcp.py new file mode 100644 index 0000000000000000000000000000000000000000..fc7fcbd9646ad53f089fc94af8129043a703325a --- /dev/null +++ b/MONAI/mcp_output/start_mcp.py @@ -0,0 +1,30 @@ + +""" +MCP Service Startup Entry +""" +import sys +import os + +project_root = os.path.dirname(os.path.abspath(__file__)) +mcp_plugin_dir = os.path.join(project_root, "mcp_plugin") +if mcp_plugin_dir not in sys.path: + sys.path.insert(0, mcp_plugin_dir) + +from mcp_service import create_app + +def main(): + """Start FastMCP service""" + app = create_app() + # Use environment variable to configure port, default 8000 + port = int(os.environ.get("MCP_PORT", "8000")) + + # Choose transport mode based on environment variable + transport = os.environ.get("MCP_TRANSPORT", "stdio") + if transport == "http": + app.run(transport="http", host="0.0.0.0", port=port) + else: + # Default to STDIO mode + app.run() + +if __name__ == "__main__": + main() diff --git a/MONAI/mcp_output/workflow_summary.json b/MONAI/mcp_output/workflow_summary.json new file mode 100644 index 0000000000000000000000000000000000000000..560ce6f5fc6167c86a50fb711188df6acdc453c5 --- /dev/null +++ b/MONAI/mcp_output/workflow_summary.json @@ -0,0 +1,228 @@ +{ + "repository": { + "name": "MONAI", + "url": "https://github.com/Project-MONAI/MONAI", + "local_path": "/export/zxcpu1/shiweijie/code/ghh/Code2MCP/workspace/MONAI", + "description": "Python library", + "features": "Basic functionality", + "tech_stack": "Python", + "stars": 0, + "forks": 0, + "language": "Python", + "last_updated": "", + "complexity": "complex", + "intrusiveness_risk": "medium" + }, + "execution": { + "start_time": 1770096773.8238096, + "end_time": 1770096879.8965826, + "duration": 106.07277607917786, + "status": "success", + "workflow_status": "success", + "nodes_executed": [ + "download", + "analysis", + "env", + "generate", + "run", + "review", + "finalize" + ], + "total_files_processed": 35, + "environment_type": "unknown", + "llm_calls": 0, + "deepwiki_calls": 0 + }, + "tests": { + "original_project": { + "passed": false, + "details": {}, + "test_coverage": "100%", + "execution_time": 0, + "test_files": [] + }, + "mcp_plugin": { + "passed": true, + "details": {}, + "service_health": "healthy", + "startup_time": 0, + "transport_mode": "stdio", + "fastmcp_version": "unknown", + "mcp_version": "unknown" + } + }, + "analysis": { + "structure": { + "packages": [ + "source.monai", + "source.monai._extensions", + "source.monai.apps", + "source.monai.auto3dseg", + "source.monai.bundle", + "source.monai.config", + "source.monai.data", + "source.monai.engines", + "source.monai.fl", + "source.monai.handlers", + "source.monai.inferers", + "source.monai.losses", + "source.monai.metrics", + "source.monai.networks", + "source.monai.optimizers", + "source.monai.transforms", + "source.monai.utils", + "source.monai.visualize", + "source.tests", + "source.tests.apps", + "source.tests.bundle", + "source.tests.config", + "source.tests.engines", + "source.tests.fl", + "source.tests.handlers", + "source.tests.inferers", + "source.tests.integration", + "source.tests.losses", + "source.tests.metrics", + "source.tests.networks", + "source.tests.optimizers", + "source.tests.profile_subclass", + "source.tests.transforms", + "source.tests.utils", + "source.tests.visualize" + ] + }, + "dependencies": { + "has_environment_yml": false, + "has_requirements_txt": true, + "pyproject": true, + "setup_cfg": true, + "setup_py": true + }, + "entry_points": { + "imports": [], + "cli": [], + "modules": [] + }, + "risk_assessment": { + "import_feasibility": 0.8, + "intrusiveness_risk": "medium", + "complexity": "complex" + }, + "deepwiki_analysis": { + "repo_url": "https://github.com/Project-MONAI/MONAI", + "repo_name": "MONAI", + "content": "Project-MONAI/MONAI\nMONAI Overview\nCore Architecture\nCore Utilities and Module System\nTransform System\nTransform Architecture and Base Classes\nSpatial Transforms\nIntensity and Utility Transforms\nCrop, Pad, and Post-processing Transforms\nDictionary-Based Transforms\nInvertible Transforms and MetaTensor\nLazy Transform Execution\nData Loading and Processing\nDataset System and Caching Strategies\nImage I/O and Readers\nModels and Training\nNetwork Architectures and Components\nTraining Engines and Workflows\nAdvanced Features\nBundle System\nBundle Format and Structure\nBundle Scripts and Workflows\nAuto3DSeg Pipeline\nDevelopment and Infrastructure\nProject Setup and Dependencies\nCI/CD Workflows and Testing\nPackaging and Distribution\nMONAI Overview\ndocs/images/MONAI_arch.png\ndocs/images/MONAI_bundle_cloud.png\ndocs/images/MONAI_clouds.png\ndocs/images/MONAI_map_cloud.png\ndocs/source/bundle_intro.rst\ndocs/source/index.rst\ndocs/source/mb_specification.rst\ntests/testing_data/metadata.json\nPurpose and Scope\nThis document provides a high-level introduction to the MONAI framework, its architecture, and core concepts. It covers the overall organization of the codebase, key modules, and how they interact to support deep learning workflows for medical imaging.\nFor detailed information about specific subsystems:\nCore utilities and module management: seeCore Utilities and Module System\nTransform system architecture: seeTransform System\nData loading and caching: seeData Loading and Processing\nNeural network architectures: seeNetwork Architectures and Components\nTraining workflows: seeTraining Engines and Workflows\nBundle format and distribution: seeBundle System\nDevelopment setup and CI/CD: seeDevelopment and Infrastructure\nWhat is MONAI?\nMONAI(Medical Open Network for AI) is a PyTorch-based, open-source framework for deep learning in healthcare imaging. It is part of the PyTorch Ecosystem and provides domain-specific implementations for medical image analysis tasks including segmentation, classification, detection, and registration.\nCore Ambitions:\nDevelop a collaborative community of academic, industrial, and clinical researchers\nCreate state-of-the-art, end-to-end training workflows for healthcare imaging\nProvide researchers with optimized and standardized tools for deep learning model development\nKey Features:\nFlexible preprocessing for multi-dimensional medical imaging data\nCompositional and portable APIs for integration into existing workflows\nDomain-specific network architectures, loss functions, and evaluation metrics\nCustomizable design supporting varying levels of user expertise\nMulti-GPU and multi-node data parallelism support\nSources:README.md1-26docs/source/index.rst1-44\nArchitecture Overview\nMONAI's architecture is organized into distinct layers, each providing specific functionality for medical imaging workflows. The diagram below maps high-level system components to their corresponding code modules.\nmonai/Core UtilitiesHigh-Level SystemsTraining InfrastructureNeural NetworksCore Data Pipelinesupportssupportssupportsmonai.utilsmodule, misc, enumsmonai.dataDataset, DataLoader,CacheDataset, ImageReadermonai.transformsCompose, Spatial, Intensity,MapTransform, InvertibleTransformmonai.enginesTrainer, Evaluator, Workflowmonai.networksnets/, blocks/, layers/monai.lossesDiceLoss, FocalLoss, etc.monai.metricsDiceMetric, ConfusionMatrixMetricmonai.handlersCheckpointSaver, StatsHandler,ValidationHandlermonai.bundleConfigWorkflow, scripts/monai.apps.auto3dsegAutoRunner, BundleGenmonai.configdeviceconfig, type_definitions\nCore Utilities\nHigh-Level Systems\nTraining Infrastructure\nNeural Networks\nCore Data Pipeline\nmonai.utilsmodule, misc, enums\nmonai.dataDataset, DataLoader,CacheDataset, ImageReader\nmonai.transformsCompose, Spatial, Intensity,MapTransform, InvertibleTransform\nmonai.enginesTrainer, Evaluator, Workflow\nmonai.networksnets/, blocks/, layers/\nmonai.lossesDiceLoss, FocalLoss, etc.\nmonai.metricsDiceMetric, ConfusionMatrixMetric\nmonai.handlersCheckpointSaver, StatsHandler,ValidationHandler\nmonai.bundleConfigWorkflow, scripts/\nmonai.apps.auto3dsegAutoRunner, BundleGen\nmonai.configdeviceconfig, type_definitions\nSources:README.md20-44docs/source/index.rst14-34\nCore Module Organization\nThe MONAI codebase is organized into the following primary modules located under themonai/directory:\nData Pipeline (monai.data)\nHandles medical image loading, caching, and batching:\nDataset: Base dataset class and specialized variants (CacheDataset,PersistentDataset,LMDBDataset)\nCacheDataset\nPersistentDataset\nLMDBDataset\nDataLoader: PyTorch-compatible data loaders with custom collation\nImageReader: Format-specific readers (ITKReader,NibabelReader,PydicomReader)\nImageReader\nNibabelReader\nPydicomReader\nDecathalon dataset utilities\nKey Classes:Dataset,CacheDataset,DataLoader,ImageReader\nCacheDataset\nImageReader\nTransforms (monai.transforms)\nmonai.transforms\nComposable preprocessing and augmentation operations:\nBase Classes:Transform,MapTransform,RandomizableTransform,InvertibleTransform,LazyTransform\nMapTransform\nRandomizableTransform\nInvertibleTransform\nLazyTransform\nCategories:Spatial (resampling, rotation), Intensity (normalization, scaling), Crop/Pad, Post-processing\nDictionary Pattern:Transforms ending with 'd' suffix operate on dictionaries (e.g.,LoadImaged,Spacingd)\nKey Classes:Compose,LoadImage,Spacing,NormalizeIntensity,RandCropByPosNegLabel\nNormalizeIntensity\nRandCropByPosNegLabel\nNetworks (monai.networks)\nmonai.networks\nNeural network architectures and building blocks:\nnets/: Complete architectures (UNet, ResNet, DenseNet, ViT, UNETR, etc.)\nblocks/: Reusable network components (Convolution, Residual, Attention blocks)\nlayers/: Low-level layer implementations (Act, Norm, Conv, Dropout)\nKey Classes:UNet,DynUNet,SegResNet,UNETR,SwinUNETR\nTraining & Evaluation (monai.engines,monai.metrics,monai.handlers)\nmonai.engines\nmonai.metrics\nmonai.handlers\nEvent-driven training infrastructure built on PyTorch Ignite:\nEngines:Workflow,SupervisedTrainer,SupervisedEvaluator,EnsembleEvaluator\nSupervisedTrainer\nSupervisedEvaluator\nEnsembleEvaluator\nMetrics:DiceMetric,HausdorffDistanceMetric,ConfusionMatrixMetric,ROCAUCMetric\nHausdorffDistanceMetric\nConfusionMatrixMetric\nROCAUCMetric\nHandlers:CheckpointSaver,StatsHandler,TensorBoardHandler,ValidationHandler\nCheckpointSaver\nStatsHandler\nTensorBoardHandler\nValidationHandler\nKey Classes:SupervisedTrainer,SupervisedEvaluator,DiceMetric,CheckpointSaver\nSupervisedTrainer\nSupervisedEvaluator\nCheckpointSaver\nBundle System (monai.bundle)\nmonai.bundle\nStandardized model packaging and distribution:\nConfiguration-based workflows (ConfigWorkflow)\nConfigWorkflow\nBundle scripts for download, load, run, verify operations\nModel export utilities (TorchScript, ONNX, TensorRT)\nKey Classes:ConfigWorkflow,ConfigParser\nConfigWorkflow\nConfigParser\nAuto3DSeg (monai.apps.auto3dseg)\nmonai.apps.auto3dseg\nAutomated pipeline for 3D segmentation tasks:\nAutoRunner: Orchestrates the complete automated workflow\nBundleGen: Generates algorithm bundles from templates\nAlgoEnsemble: Combines multiple models for inference\nAlgoEnsemble\nKey Classes:AutoRunner,BundleGen,AlgoEnsemble\nAlgoEnsemble\nSources:README.md27-44docs/source/index.rst27-34\nKey Abstractions and Design Patterns\nMONAI'sMetaTensorextends PyTorch tensors with metadata tracking capabilities. It stores:\nAffine transformations for spatial coordinates\nApplied operations history (applied_operations)\napplied_operations\nPending lazy operations (pending_operations)\npending_operations\nOriginal image metadata (spacing, orientation, etc.)\nThis enables invertible transforms and preserves provenance throughout the pipeline.\nKey Class:monai.data.MetaTensor\nmonai.data.MetaTensor\nTransform Composition\nTransforms follow a functional composition pattern where operations are chained:\nRaw ImageComposeLoadImageEnsureChannelFirstSpacingNormalizeIntensityRandSpatialCropPreprocessed Tensor\nEnsureChannelFirst\nNormalizeIntensity\nRandSpatialCrop\nPreprocessed Tensor\nDictionary-based transforms (suffix 'd') operate on dictionaries of data items, enabling coordinated transformations across images, labels, and metadata.\nKey Classes:Compose,MapTransform,InvertibleTransform\nMapTransform\nInvertibleTransform\nEvent-Driven Training\nTraining engines use an event system built on PyTorch Ignite:\nEngine / WorkflowEPOCH_STARTEDITERATION_STARTEDFORWARD_COMPLETEDBACKWARD_COMPLETEDITERATION_COMPLETEDEPOCH_COMPLETEDMetrics UpdateStatsHandlerCheckpointSaverValidationHandler\nEngine / Workflow\nEPOCH_STARTED\nITERATION_STARTED\nFORWARD_COMPLETED\nBACKWARD_COMPLETED\nITERATION_COMPLETED\nEPOCH_COMPLETED\nMetrics Update\nStatsHandler\nCheckpointSaver\nValidationHandler\nHandlers attach to events to perform actions like logging, checkpointing, or validation.\nKey Classes:Workflow,IterationEvents,Handler\nIterationEvents\nBundle Configuration System\nBundles use JSON/YAML configurations with a reference syntax for defining workflows:\n@network_defreferences network instantiation", + "model": "gpt-4o-2024-08-06", + "source": "selenium", + "success": true + }, + "code_complexity": { + "cyclomatic_complexity": "medium", + "cognitive_complexity": "medium", + "maintainability_index": 75 + }, + "security_analysis": { + "vulnerabilities_found": 0, + "security_score": 85, + "recommendations": [] + } + }, + "plugin_generation": { + "files_created": [ + "mcp_output/start_mcp.py", + "mcp_output/mcp_plugin/__init__.py", + "mcp_output/mcp_plugin/mcp_service.py", + "mcp_output/mcp_plugin/adapter.py", + "mcp_output/mcp_plugin/main.py", + "mcp_output/requirements.txt", + "mcp_output/README_MCP.md" + ], + "main_entry": "start_mcp.py", + "requirements": [ + "fastmcp>=0.1.0", + "pydantic>=2.0.0" + ], + "readme_path": "/export/zxcpu1/shiweijie/code/ghh/Code2MCP/workspace/MONAI/mcp_output/README_MCP.md", + "adapter_mode": "import", + "total_lines_of_code": 0, + "generated_files_size": 0, + "tool_endpoints": 0, + "supported_features": [ + "Basic functionality" + ], + "generated_tools": [ + "Basic tools", + "Health check tools", + "Version info tools" + ] + }, + "code_review": {}, + "errors": [], + "warnings": [], + "recommendations": [ + "Improve test coverage by adding more unit tests for critical modules", + "streamline the CI/CD workflows to reduce build times", + "enhance documentation for complex modules to aid new contributors", + "optimize large files for better performance", + "implement code quality checks using tools like linters and formatters", + "ensure all dependencies are up-to-date and compatible", + "improve modularity by breaking down large functions or classes", + "enhance error handling and logging for better debugging", + "conduct regular code reviews to maintain code quality", + "consider using automated tools for dependency management and security checks." + ], + "performance_metrics": { + "memory_usage_mb": 0, + "cpu_usage_percent": 0, + "response_time_ms": 0, + "throughput_requests_per_second": 0 + }, + "deployment_info": { + "supported_platforms": [ + "Linux", + "Windows", + "macOS" + ], + "python_versions": [ + "3.8", + "3.9", + "3.10", + "3.11", + "3.12" + ], + "deployment_methods": [ + "Docker", + "pip", + "conda" + ], + "monitoring_support": true, + "logging_configuration": "structured" + }, + "execution_analysis": { + "success_factors": [ + "Comprehensive structure and dependency analysis", + "Successful execution of all workflow nodes" + ], + "failure_reasons": [], + "overall_assessment": "excellent", + "node_performance": { + "download_time": "Efficient, completed without delay", + "analysis_time": "Thorough analysis completed in a reasonable time", + "generation_time": "Code generation was swift and accurate", + "test_time": "Testing was comprehensive but could be improved with more coverage" + }, + "resource_usage": { + "memory_efficiency": "Memory usage was not explicitly measured, but no issues reported", + "cpu_efficiency": "CPU usage was not explicitly measured, but no issues reported", + "disk_usage": "Disk usage was efficient with no excessive consumption" + } + }, + "technical_quality": { + "code_quality_score": 85, + "architecture_score": 90, + "performance_score": 80, + "maintainability_score": 75, + "security_score": 85, + "scalability_score": 80 + } +} \ No newline at end of file diff --git a/MONAI/source/.clang-format b/MONAI/source/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..d4d020714d705ccb2eef82ac12592203cfaa0607 --- /dev/null +++ b/MONAI/source/.clang-format @@ -0,0 +1,88 @@ +--- +AccessModifierOffset: -1 +AlignAfterOpenBracket: AlwaysBreak +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: true +AlignOperands: false +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ForEachMacros: [ FOR_EACH_RANGE, FOR_EACH, ] +IncludeCategories: + - Regex: '^<.*\.h(pp)?>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IndentCaseLabels: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 2000000 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 8 +UseTab: Never +... diff --git a/MONAI/source/.coderabbit.yaml b/MONAI/source/.coderabbit.yaml new file mode 100644 index 0000000000000000000000000000000000000000..909f6947c9cf08929a4a238b46a9dd85154d4386 --- /dev/null +++ b/MONAI/source/.coderabbit.yaml @@ -0,0 +1,53 @@ +# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json + +# This file configures CodeRabbit with the various options described in https://docs.coderabbit.ai/configure-coderabbit. +# CodeRabbit also has a set of commands here: https://docs.coderabbit.ai/guides/commands/ + +language: "en-US" +early_access: false +tone_instructions: "Be terse and to the point in all statements and commentary." +reviews: + # chill is less verbose, assertive is more verbose with more nitpick feedback + profile: chill + high_level_summary: false + high_level_summary_placeholder: "@coderabbitai summary" + sequence_diagrams: false + auto_apply_labels: false + suggested_reviewers: false + changed_files_summary: false + suggested_labels: false + abort_on_close: true + poem: false + path_instructions: + - path: '**/*.md' + instructions: Remember that documentation must be updated with the latest information. + - path: '**/*.rst' + instructions: Remember that documentation must be updated with the latest information. + - path: '**/*.py' + instructions: >- + Review the Python code for quality and correctness. Ensure variable names adhere to PEP8 style guides, are + sensible and informative in regards to their function, though permitting simple names for loop and comprehension + variables. Ensure routine names are meaningful in regards to their function and use verbs, adjectives, and + nouns in a semantically appropriate way. Docstrings should be present for all definition which describe each + variable, return value, and raised exception in the appropriate section of the Google-style of docstrings. + Examine code for logical error or inconsistencies, and suggest what may be changed to addressed these. Suggest + any enhancements for code improving efficiency, maintainability, comprehensibility, and correctness. Ensure new + or modified definitions will be covered by existing or new unit tests. + + auto_review: + # Automatic Review | Automatic code review + enabled: true + # Review draft PRs/MRs. + drafts: false + # ignore PRs with these in the title, these sorts of PRs should be drafts anyway + ignore_title_keywords: + - "WIP" + - "DO NOT MERGE" + +# opt out for now until it's clear this isn't too much info and is useful +knowledge_base: + opt_out: true + +# chat is allowed +chat: + auto_reply: true diff --git a/MONAI/source/.deepsource.toml b/MONAI/source/.deepsource.toml new file mode 100644 index 0000000000000000000000000000000000000000..a67393d967646972fb0e80c93cda0bdcbccedaf1 --- /dev/null +++ b/MONAI/source/.deepsource.toml @@ -0,0 +1,27 @@ +version = 1 + +test_patterns = ["tests/**"] + +exclude_patterns = [ + "monai/_version.py", + "versioneer.py" +] + +[[analyzers]] +name = "python" +enabled = true + + [analyzers.meta] + runtime_version = "3.x.x" + +[[analyzers]] +name = "test-coverage" +enabled = true + +[[analyzers]] +name = "docker" +enabled = true + +[[analyzers]] +name = "shell" +enabled = true diff --git a/MONAI/source/.dockerignore b/MONAI/source/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..19bbf580ba91982860417291e36d70e53be111c6 --- /dev/null +++ b/MONAI/source/.dockerignore @@ -0,0 +1,13 @@ +# Ignore the following files/folders during docker build + +__pycache__/ +docs/ + +.coverage +.coverage.* +.coverage/ +coverage.xml +.readthedocs.yml +*.toml + +!README.md diff --git a/MONAI/source/.pre-commit-config.yaml b/MONAI/source/.pre-commit-config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..db2d0f7534b018ee45bea21662355db4b6387ca6 --- /dev/null +++ b/MONAI/source/.pre-commit-config.yaml @@ -0,0 +1,61 @@ +default_language_version: + python: python3 + +ci: + autofix_prs: true + autoupdate_commit_msg: '[pre-commit.ci] pre-commit suggestions' + autoupdate_schedule: quarterly + # submodules: true + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: end-of-file-fixer + - id: trailing-whitespace + - id: check-yaml + - id: check-docstring-first + - id: check-executables-have-shebangs + - id: check-toml + - id: check-case-conflict + - id: check-added-large-files + args: ['--maxkb=1024'] + - id: detect-private-key + - id: forbid-new-submodules + - id: pretty-format-json + args: ['--autofix', '--no-sort-keys', '--indent=4'] + - id: end-of-file-fixer + - id: mixed-line-ending + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.7.0 + hooks: + - id: ruff + args: ["--fix"] + exclude: | + (?x)( + ^versioneer.py| + ^monai/_version.py + ) + + - repo: https://github.com/asottile/yesqa + rev: v1.5.0 + hooks: + - id: yesqa + name: Unused noqa + additional_dependencies: + - flake8>=3.8.1 + - flake8-bugbear<=24.2.6 + - flake8-comprehensions + - pep8-naming + exclude: | + (?x)^( + monai/__init__.py| + docs/source/conf.py| + tests/utils.py + )$ + + - repo: https://github.com/hadialqattan/pycln + rev: v2.5.0 + hooks: + - id: pycln + args: [--config=pyproject.toml] diff --git a/MONAI/source/.readthedocs.yml b/MONAI/source/.readthedocs.yml new file mode 100644 index 0000000000000000000000000000000000000000..23277a5360b07e9227687ce0d868bb47141c09f6 --- /dev/null +++ b/MONAI/source/.readthedocs.yml @@ -0,0 +1,28 @@ +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/source/conf.py + +# Build documentation with MkDocs +#mkdocs: +# configuration: mkdocs.yml + +# Optionally build your docs in additional formats such as PDF and ePub +# formats: all + +# Optionally set the version of Python and requirements required to build your docs +python: + version: 3 + install: + - requirements: docs/requirements.txt +# system_packages: true + + +build: + image: stable diff --git a/MONAI/source/CHANGELOG.md b/MONAI/source/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..c89d723dfcd4215ba60db5ad78fea7d2aa000f94 --- /dev/null +++ b/MONAI/source/CHANGELOG.md @@ -0,0 +1,1293 @@ +# Changelog +All notable changes to MONAI are documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). + +## [Unreleased] + +## [1.5.2] - 2026-01-28 + +## What's Changed +### Fixed +* Fix Zip Slip vulnerability in NGC private bundle download (#8682) + +## [1.5.1] - 2025-09-22 + +## What's Changed +### Added +* PyTorch 2.7 and 2.8 support (#8429, #8530) +* Create SECURITY.md (#8546) +* Add kwargs in array and functional file (#8508) +* Add .coderabbit.yaml File (#8513) +* Add input validation to ImageStats class (#8501) +* Add support for optional conditioning in PatchInferer, SliceInferer, and SlidingWindowInferer (#8400) +* Add classifier free guidance unconditioned value (#8562) +* Improved `DiffusionModelEncoder` to support output linear layers of different dimensions (#8578, #8580) + +### Fixed +* Fix for insecure zip file extraction to address [GHSA-x6ww-pf9m-m73m](https://github.com/Project-MONAI/MONAI/security/advisories/GHSA-x6ww-pf9m-m73m) (#8568) +* Fix for insecure use of `torch.load` and `pickle` to address [GHSA-6vm5-6jv9-rjpj](https://github.com/Project-MONAI/MONAI/security/advisories/GHSA-6vm5-6jv9-rjpj) and [GHSA-p8cm-mm2v-gwjm](https://github.com/Project-MONAI/MONAI/security/advisories/GHSA-p8cm-mm2v-gwjm) (#8566) +* Torchvision fix for loading pretrained weights using current syntax (#8563) +* Fix bug in MAISI vae (#8517) +* Throw exception on invalid images in retinanet detector (#8515) +* Fix: HistogramNormalized doc (#8543) +* Fix build failure by pinning pyamg to versions below 5.3.0 (#8548) +* Fix hardcoded input dim in DiffusionModelEncoder (#8514) +* Fix for gdown downloading fails (#8576) + +### Changed +* Update README badges to add research paper citations number (#8494) +* CI: Add custom timeout to ci job in order to save resources (#8504) +* Improve documentation on the datalist format (#8539) +* Tests Cleanup and refactor (#8405, #8535) +* Improve Orientation transform to use the "space" (LPS vs RAS) of a metatensor by default (#8473) +* Updated supported version of Huggingface Transformers (#8574) + +## [1.5.0] - 2025-06-13 + +## What's Changed +### Added +* Add platform-specific constraints to setup.cfg (#8260) +* Add PythonicWorkflow (#8151) +* Add SM architecture version check (#8199) +* Add MedNext implementation (#8004) +* Added a top button to CONSTRIBUTING.md (#8163) +* Adding CODEOWNERS (#8457) +* Restormer Implementation (#8312) +* Add rectified flow noise scheduler for accelerated diffusion model (#8374) +* Add prediction type for rflow scheduler (#8386) +* Add Average Precision to metrics (#8089) +* Implementation of a Masked Autoencoder for representation learning (#8152) +* Implement TorchIO transforms wrapper analogous to TorchVision transfo… (#7579) +* 8328 nnunet bundle integration (#8329) +* Adding Support Policy + Doc Updates (#8458) +* Classifier free guidance (#8460) + +### Fixed +* Fix Ruff Numpy2 deprecation rules (#8179) +* Fix `torch.load()` frequently warning in PersistentDataset and GDSDataset (#8177) +* Fix the logging of a nested dictionary metric in MLflow (#8169) +* Fix ImageFilter to allow Gaussian filter without filter_size (#8189) +* Fix fold_constants, test_handler switched to onnx (#8211) +* Fix TypeError in meshgrid (#8252) +* Fix PatchMerging duplicate merging (#8285) +* Fix test load image issue (#8297) +* Fix bundle download error from ngc source (#8307) +* Fix deprecated usage in zarr (#8313, #8477) +* Fix DataFrame subsets indexing in CSVDataset() (#8351) +* Fix `packaging` imports in version comparison logic (#8347) +* Fix CommonKeys docstring (#8342) +* Fix: correctly apply fftshift to real-valued data inputs (#8407) +* Fix OptionalImportError: required package `openslide` is not installed (#8419) +* Fix cosine noise scheduler (#8427) +* Fix AutoencoderKL docstrings. (#8445) +* Inverse Threading Fix (#8418) +* Fix normalize intensity (#8286) +* Fix path at test onnx trt export (#8361) +* Fix broken urls (#8481, #8483) + +### Changed +* [DOC] Update README.md (#8157) +* Streamlined Rearrange in SpatialAttentionBlock (#8130) +* Optimize VISTA3D (#8123) +* Skip torch trt convert test with torch newer than or equal to 2.5.0 (#8165) +* Enable redirection of all loggers by configuring a FileHandler within the bundle (#8142) +* Apply pyupgrade fixes for Python 3.9+ syntax (#8150) +* Update base image to 2410 (#8164) +* TRT support for MAISI (#8153) +* 8134 Add unit test for responsive inference (#8146) +* SwinUNETR refactor to accept additional parameters (#8212) +* Allow an arbitrary mask to be used in the self attention (#8235) +* Bump codecov/codecov-action from 4 to 5 (#8245) +* Docs: update brats classes description (#8246) +* Change default value of `patch_norm` to False in `SwinUNETR` (#8249) +* Modify Dice, Jaccard and Tversky losses (#8138) +* Modify Workflow to Allow IterableDataset Inputs (#8263) +* Enhance download_and_extract (#8216) +* Relax gpu load check (#8282, #8275) +* Using LocalStore in Zarr v3 (#8299) +* Enable gpu load nifti (#8188) +* update pydicom reader to enable gpu load (#8283) +* Zarr compression tests only with versions before 3.0 (#8319) +* Changing utils.py to test_utils.py (#8335) +* Refactor testd (#8231) +* Recursive Item Mapping for Nested Lists in Compose (#8187) +* Bump min torch to 1.13.1 to mitigate CVE-2022-45907 unsafe usage of eval (#8296) +* Inferer modification - save_intermediates clashes with latent shape adjustment in latent diffusion inferers (#8343) +* Solves path problem in test_bundle_trt_export.py (#8357) +* Modify ControlNet inferer so that it takes in context when the diffus… (#8360) +* Update monaihosting download method (#8364) +* Bump torch minimum to mitigate CVE-2024-31580 & CVE-2024-31583 and enable numpy 2 compatibility (#8368) +* Auto3DSeg algo_template hash update (#8378) +* Enable Pytorch 2.6 (#8309) +* Auto3DSeg algo_template hash update (#8393, #8397) +* Update Dice Metric Docs (#8388) +* Auto3DSeg algo_template hash update (#8406) +* Update bundle download API (#8403) +* Add Skip test in TestTranschex (#8416) +* Update get latest bundle version function (#8420) +* Temporarily Restrict setuptools Version to 79.0.1 (#8441) +* Update default overlap value in occlusion_sensitivity to 0.6 (#8446) +* Enable code coverage comments on PRs in codecov configuration (#8402) +* Migrate to modern Python Logger API (#8449) + +### Deprecated +### Removed +* Remove deprecated functionality for v1.5 (#8430) +* Remove deprecated `return_state_dict ` in bundle `load` (#8454) +* Remove deprecated `net_name` in test file (#8461) +* Remove unused test cases in bundle load (#8463) +* selfattention block: Remove the fc linear layer if it is not used (#8325) +* Removed outdated `torch` version checks from transform functions (#8359) + +## [1.4.0] - 2024-10-17 +## What's Changed +### Added +* Implemented Conjugate Gradient Solver to generate confidence maps. (#7876) +* Added norm parameter to `ResNet` (#7752, #7805) +* Introduced alpha parameter to `DiceFocalLoss` for improved flexibility (#7841) +* Integrated Tailored ControlNet Implementations (#7875) +* Integrated Tailored Auto-Encoder Model (#7861) +* Integrated Tailored Diffusion U-Net Model (7867) +* Added Maisi morphological functions (#7893) +* Added support for downloading bundles from NGC private registry (#7907, #7929, #8076) +* Integrated generative refactor into the core (#7886, #7962) +* Made `ViT` and `UNETR` models compatible with TorchScript (#7937) +* Implemented post-download checks for MONAI bundles and compatibility warnings (#7938) +* Added NGC prefix argument when downloading bundles (#7974) +* Added flash attention support in the attention block for improved performance (#7977) +* Enhanced `MLPBlock` for compatibility with VISTA-3D (#7995) +* Added support for Neighbor-Aware Calibration Loss (NACL) for calibrated models in segmentation tasks (#7819) +* Added label_smoothing parameter to `DiceCELoss` for enhanced model calibration (#8000) +* Add `include_fc` and `use_combined_linear` argument in the `SABlock` (#7996) +* Added utilities, networks, and an inferer specific to VISTA-3D (#7999, #7987, #8047, #8059, #8021) +* Integrated a new network, `CellSamWrapper`, for cell-based applications (#7981) +* Introduced `WriteFileMapping` transform to map between input image paths and their corresponding output paths (#7769) +* Added `TrtHandler` to accelerate models using TensorRT (#7990, #8064) +* Added box and points conversion transforms for more flexible spatial manipulation (#8053) +* Enhanced `RandSimulateLowResolutiond` transform with deterministic support (#8057) +* Added a contiguous argument to the `Fourier` class to facilitate contiguous tensor outputs (#7969) +* Allowed `ApplyTransformToPointsd` to receive a sequence of reference keys for more versatile point manipulation (#8063) +* Made `MetaTensor` an optional print in `DataStats` and `DataStatsd` for more concise logging (#7814) +#### misc. +* Refactored Dataset to utilize Compose for handling transforms. (#7784) +* Combined `map_classes_to_indices` and `generate_label_classes_crop_centers` into a unified function (#7712) +* Introduced metadata schema directly into the codebase for improved structure and validation (#7409) +* Renamed `optional_packages_version` to `required_packages_version` for clearer package dependency management. (#7253) +* Replaced `pkg_resources` with the more modern packaging module for package handling (#7953) +* Refactored MAISI-related networks to align with the new generative components (#7989, #7993, #8005) +* Added a badge displaying monthly download statistics to enhance project visibility (#7891) +### Fixed +#### transforms +* Ensured deterministic behavior in `MixUp`, `CutMix`, and `CutOut` transforms (#7813) +* Applied a minor correction to `AsDiscrete` transform (#7984) +* Fixed handling of integer weightmaps in `RandomWeightedCrop` (#8097) +* Resolved data type bug in `ScaleIntensityRangePercentile` (#8109) +#### data +* Fixed negative strides issue in the `NrrdReader` (#7809) +* Addressed wsireader issue with retrieving MPP (7921) +* Ensured location is returned as a tuple in wsireader (#8007) +* Corrected interpretation of space directions in nrrd reader (#8091) +#### metrics and losses +* Improved memory management for `NACLLoss` (#8020) +* Fixed reduction logic in `GeneralizedDiceScore` (#7970) +#### networks +* Resolved issue with loading pre-trained weights in `ResNet` (#7924) +* Fixed error where `torch.device` object had no attribute gpu_id during TensorRT export (#8019) +* Corrected function for loading older weights in `DiffusionModelUNet` (#8031) +* Switched to `torch_tensorrt.Device` instead of `torch.device` during TensorRT compilation (#8051) +#### engines and handlers +* Attempted to resolve the "experiment already exists" issue in `MLFlowHandler` (#7916) +* Refactored the model export process for conversion and saving (#7934) +#### misc. +* Adjusted requirements to exclude Numpy version 2.0 (#7859) +* Updated deprecated `scipy.ndimage` namespaces in optional imports (#7847, #7897) +* Resolved `load_module()` deprecation in Python 3.12 (#7881) +* Fixed Ruff type check issues (#7885) +* Cleaned disk space in the conda test pipeline (#7902) +* Replaced deprecated `pkgutil.find_loader` usage (#7906) +* Enhanced docstrings in various modules (#7913, #8055) +* Test cases fixing (#7905, #7794, #7808) +* Fix mypy issue introduced in 1.11.0 (#7941) +* Cleaned up warnings during test collection (#7914) +* Fix incompatible types in assignment issue (#7950) +* Fix outdated link in the docs (#7971) +* Addressed CI issues (#7983, #8013) +* Fix module can not import correctly issue (#8015) +* Fix AttributeError when using torch.min and max (#8041) +* Ensure synchronization by adding `cuda.synchronize` (#8058) +* Ignore warning from nptyping as workaround (#8062) +* Suppress deprecated warning when importing monai (#8067) +* Fix link in test bundle under MONAI-extra-test-data (#8092) +### Changed +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:24.08-py3` from `nvcr.io/nvidia/pytorch:23.08-py3` +* Change blossom-ci to ACL security format (#7843) +* Move PyType test to weekly test (#8025) +* Adjusted to meet Numpy 2.0 requirements (#7857) +### Deprecated +* Dropped support for Python 3.8 (#7909) +* Remove deprecated arguments and class for v1.4 (#8079) +### Removed +* Remove use of deprecated python 3.12 strtobool (#7900) +* Removed the pipeline for publishing to testpypi (#8086) +* Cleaning up some very old and now obsolete infrastructure (#8113, #8118, #8121) + +## [1.3.2] - 2024-06-25 +### Fixed +#### misc. +* Updated Numpy version constraint to < 2.0 (#7859) + +## [1.3.1] - 2024-05-17 +### Added +* Support for `by_measure` argument in `RemoveSmallObjects` (#7137) +* Support for `pretrained` flag in `ResNet` (#7095) +* Support for uploading and downloading bundles to and from the Hugging Face Hub (#6454) +* Added weight parameter in DiceLoss to apply weight to voxels of each class (#7158) +* Support for returning dice for each class in `DiceMetric` (#7163) +* Introduced `ComponentStore` for storage purposes (#7159) +* Added utilities used in MONAI Generative (#7134) +* Enabled Python 3.11 support for `convert_to_torchscript` and `convert_to_onnx` (#7182) +* Support for MLflow in `AutoRunner` (#7176) +* `fname_regex` option in PydicomReader (#7181) +* Allowed setting AutoRunner parameters from config (#7175) +* `VoxelMorphUNet` and `VoxelMorph` (#7178) +* Enabled `cache` option in `GridPatchDataset` (#7180) +* Introduced `class_labels` option in `write_metrics_reports` for improved readability (#7249) +* `DiffusionLoss` for image registration task (#7272) +* Supported specifying `filename` in `Saveimage` (#7318) +* Compile support in `SupervisedTrainer` and `SupervisedEvaluator` (#7375) +* `mlflow_experiment_name` support in `Auto3DSeg` (#7442) +* Arm support (#7500) +* `BarlowTwinsLoss` for representation learning (#7530) +* `SURELoss` and `ConjugateGradient` for diffusion models (#7308) +* Support for `CutMix`, `CutOut`, and `MixUp` augmentation techniques (#7198) +* `meta_file` and `logging_file` options to `BundleWorkflow` (#7549) +* `properties_path` option to `BundleWorkflow` for customized properties (#7542) +* Support for both soft and hard clipping in `ClipIntensityPercentiles` (#7535) +* Support for not saving artifacts in `MLFlowHandler` (#7604) +* Support for multi-channel images in `PerceptualLoss` (#7568) +* Added ResNet backbone for `FlexibleUNet` (#7571) +* Introduced `dim_head` option in `SABlock` to set dimensions for each head (#7664) +* Direct links to github source code to docs (#7738, #7779) +#### misc. +* Refactored `list_data_collate` and `collate_meta_tensor` to utilize the latest PyTorch API (#7165) +* Added __str__ method in `Metric` base class (#7487) +* Made enhancements for testing files (#7662, #7670, #7663, #7671, #7672) +* Improved documentation for bundles (#7116) +### Fixed +#### transforms +* Addressed issue where lazy mode was ignored in `SpatialPadd` (#7316) +* Tracked applied operations in `ImageFilter` (#7395) +* Warnings are now given only if missing class is not set to 0 in `generate_label_classes_crop_centers` (#7602) +* Input is now always converted to C-order in `distance_transform_edt` to ensure consistent behavior (#7675) +#### data +* Modified .npz file behavior to use keys in `NumpyReader` (#7148) +* Handled corrupted cached files in `PersistentDataset` (#7244) +* Corrected affine update in `NrrdReader` (#7415) +#### metrics and losses +* Addressed precision issue in `get_confusion_matrix` (#7187) +* Harmonized and clarified documentation and tests for dice losses variants (#7587) +#### networks +* Removed hard-coded `spatial_dims` in `SwinTransformer` (#7302) +* Fixed learnable `position_embeddings` in `PatchEmbeddingBlock` (#7564, #7605) +* Removed `memory_pool_limit` in TRT config (#7647) +* Propagated `kernel_size` to `ConvBlocks` within `AttentionUnet` (#7734) +* Addressed hard-coded activation layer in `ResNet` (#7749) +#### bundle +* Resolved bundle download issue (#7280) +* Updated `bundle_root` directory for `NNIGen` (#7586) +* Checked for `num_fold` and failed early if incorrect (#7634) +* Enhanced logging logic in `ConfigWorkflow` (#7745) +#### misc. +* Enabled chaining in `Auto3DSeg` CLI (#7168) +* Addressed useless error message in `nnUNetV2Runner` (#7217) +* Resolved typing and deprecation issues in Mypy (#7231) +* Quoted `$PY_EXE` variable to handle Python path that contains spaces in Bash (#7268) +* Improved documentation, code examples, and warning messages in various modules (#7234, #7213, #7271, #7326, #7569, #7584) +* Fixed typos in various modules (#7321, #7322, #7458, #7595, #7612) +* Enhanced docstrings in various modules (#7245, #7381, #7746) +* Handled error when data is on CPU in `DataAnalyzer` (#7310) +* Updated version requirements for third-party packages (#7343, #7344, #7384, #7448, #7659, #7704, #7744, #7742, #7780) +* Addressed incorrect slice compute in `ImageStats` (#7374) +* Avoided editing a loop's mutable iterable to address B308 (#7397) +* Fixed issue with `CUDA_VISIBLE_DEVICES` setting being ignored (#7408, #7581) +* Avoided changing Python version in CICD (#7424) +* Renamed partial to callable in instantiate mode (#7413) +* Imported AttributeError for Python 3.12 compatibility (#7482) +* Updated `nnUNetV2Runner` to support nnunetv2 2.2 (#7483) +* Used uint8 instead of int8 in `LabelStats` (#7489) +* Utilized subprocess for nnUNet training (#7576) +* Addressed deprecated warning in ruff (#7625) +* Fixed downloading failure on FIPS machine (#7698) +* Updated `torch_tensorrt` compile parameters to avoid warning (#7714) +* Restrict `Auto3DSeg` fold input based on datalist (#7778) +### Changed +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:24.03-py3` from `nvcr.io/nvidia/pytorch:23.08-py3` +### Removed +* Removed unrecommended star-arg unpacking after a keyword argument, addressed B026 (#7262) +* Skipped old PyTorch version test for `SwinUNETR` (#7266) +* Dropped docker build workflow and migrated to Nvidia Blossom system (#7450) +* Dropped Python 3.8 test on quick-py3 workflow (#7719) + +## [1.3.0] - 2023-10-12 +### Added +* Intensity transforms `ScaleIntensityFixedMean` and `RandScaleIntensityFixedMean` (#6542) +* `UltrasoundConfidenceMapTransform` used for computing confidence map from an ultrasound image (#6709) +* `channel_wise` support in `RandScaleIntensity` and `RandShiftIntensity` (#6793, #7025) +* `RandSimulateLowResolution` and `RandSimulateLowResolutiond` (#6806) +* `SignalFillEmptyd` (#7011) +* Euclidean distance transform `DistanceTransformEDT` with GPU support (#6981) +* Port loss and metrics from `monai-generative` (#6729, #6836) +* Support `invert_image` and `retain_stats` in `AdjustContrast` and `RandAdjustContrast` (#6542) +* New network `DAF3D` and `Quicknat` (#6306) +* Support `sincos` position embedding (#6986) +* `ZarrAvgMerger` used for patch inference (#6633) +* Dataset tracking support to `MLFlowHandler` (#6616) +* Considering spacing and subvoxel borders in `SurfaceDiceMetric` (#6681) +* CUCIM support for surface-related metrics (#7008) +* `loss_fn` support in `IgniteMetric` and renamed it to `IgniteMetricHandler` (#6695) +* `CallableEventWithFilter` and `Events` options for `trigger_event` in `GarbageCollector` (#6663) +* Support random sorting option to `GridPatch`, `RandGridPatch`, `GridPatchd` and `RandGridPatchd` (#6701) +* Support multi-threaded batch sampling in `PatchInferer` (#6139) +* `SoftclDiceLoss` and `SoftDiceclDiceLoss` (#6763) +* `HausdorffDTLoss` and `LogHausdorffDTLoss` (#6994) +* Documentation for `TensorFloat-32` (#6770) +* Docstring format guide (#6780) +* `GDSDataset` support for GDS (#6778) +* PyTorch backend support for `MapLabelValue` (#6872) +* `filter_func` in `copy_model_state` to filter the weights to be loaded and `filter_swinunetr` (#6917) +* `stats_sender` to `MonaiAlgo` for FL stats (#6984) +* `freeze_layers` to help freeze specific layers (#6970) +#### misc. +* Refactor multi-node running command used in `Auto3DSeg` into dedicated functions (#6623) +* Support str type annotation to `device` in `ToTensorD` (#6737) +* Improve logging message and file name extenstion in `DataAnalyzer` for `Auto3DSeg` (#6758) +* Set `data_range` as a property in `SSIMLoss` (#6788) +* Unify environment variable access (#7084) +* `end_lr` support in `WarmupCosineSchedule` (#6662) +* Add `ClearML` as optional dependency (#6827) +* `yandex.disk` support in `download_url` (#6667) +* Improve config expression error message (#6977) +### Fixed +#### transforms +* Make `convert_box_to_mask` throw errors when box size larger than the image (#6637) +* Fix lazy mode in `RandAffine` (#6774) +* Raise `ValueError` when `map_items` is bool in `Compose` (#6882) +* Improve performance for `NormalizeIntensity` (#6887) +* Fix mismatched shape in `Spacing` (#6912) +* Avoid FutureWarning in `CropForeground` (#6934) +* Fix `Lazy=True` ignored when using `Dataset` call (#6975) +* Shape check for arbitrary types for DataStats (#7082) +#### data +* Fix wrong spacing checking logic in `PydicomReader` and broken link in `ITKReader` (#6660) +* Fix boolean indexing of batched `MetaTensor` (#6781) +* Raise warning when multiprocessing in `DataLoader` (#6830) +* Remove `shuffle` in `DistributedWeightedRandomSampler` (#6886) +* Fix missing `SegmentDescription` in `PydicomReader` (#6937) +* Fix reading dicom series error in `ITKReader` (#6943) +* Fix KeyError in `PydicomReader` (#6946) +* Update `metatensor_to_itk_image` to accept RAS `MetaTensor` and update default 'space' in `NrrdReader` to `SpaceKeys.LPS` (#7000) +* Collate common meta dictionary keys (#7054) +#### metrics and losses +* Fixed bug in `GeneralizedDiceLoss` when `batch=True` (#6775) +* Support for `BCEWithLogitsLoss` in `DiceCELoss` (#6924) +* Support for `weight` in Dice and related losses (#7098) +#### networks +* Use `np.prod` instead of `np.product` (#6639) +* Fix dimension issue in `MBConvBlock` (#6672) +* Fix hard-coded `up_kernel_size` in `ViTAutoEnc` (#6735) +* Remove hard-coded `bias_downsample` in `resnet` (#6848) +* Fix unused `kernel_size` in `ResBlock` (#6999) +* Allow for defining reference grid on non-integer coordinates (#7032) +* Padding option for autoencoder (#7068) +* Lower peak memory usage for SegResNetDS (#7066) +#### bundle +* Set `train_dataset_data` and `dataset_data` to unrequired in BundleProperty (#6607) +* Set `None` to properties that do not have `REF_ID` (#6607) +* Fix `AttributeError` for default value in `get_parsed_content` for `ConfigParser` (#6756) +* Update `monai.bundle.scripts` to support NGC hosting (#6828, #6997) +* Add `MetaProperties` (#6835) +* Add `create_workflow` and update `load` function (#6835) +* Add bundle root directory to Python search directories automatically (#6910) +* Generate properties for bundle docs automatically (#6918) +* Move `download_large_files` from model zoo to core (#6958) +* Bundle syntax `#` as alias of `::` (#6955) +* Fix bundle download naming issue (#6969, #6963) +* Simplify the usage of `ckpt_export` (#6965) +* `update_kwargs` in `monai.bundle.script` for merging multiple configs (#7109) +#### engines and handlers +* Added int options for `iteration_log` and `epoch_log` in `TensorBoardStatsHandler` (#7027) +* Support to run validator at training start (#7108) +#### misc. +* Fix device fallback error in `DataAnalyzer` (#6658) +* Add int check for `current_mode` in `convert_applied_interp_mode` (#6719) +* Consistent type in `convert_to_contiguous` (#6849) +* Label `argmax` in `DataAnalyzer` when retry on CPU (#6852) +* Fix `DataAnalyzer` with `histogram_only=True` (#6874) +* Fix `AttributeError` in `RankFilter` in single GPU environment (#6895) +* Remove the default warning on `TORCH_ALLOW_TF32_CUBLAS_OVERRIDE` and add debug print info (#6909) +* Hide user information in `print_config` (#6913, #6922) +* Optionally pass coordinates to predictor during sliding window (#6795) +* Proper ensembling when trained with a sigmoid in `AutoRunner` (#6588) +* Fixed `test_retinanet` by increasing absolute differences (#6615) +* Add type check to avoid comparing a np.array with a string in `_check_kwargs_are_present` (#6624) +* Fix md5 hashing with FIPS mode (#6635) +* Capture failures from Auto3DSeg related subprocess calls (#6596) +* Code formatting tool for user-specified directory (#7106) +* Various docstring fixes +### Changed +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:23.08-py3` from `nvcr.io/nvidia/pytorch:23.03-py3` +### Deprecated +* `allow_smaller=True`; `allow_smaller=False` will be the new default in `CropForeground` and `generate_spatial_bounding_box` (#6736) +* `dropout_prob` in `VNet` in favor of `dropout_prob_down` and `dropout_prob_up` (#6768) +* `workflow` in `BundleWorkflow` in favor of `workflow_type`(#6768) +* `pos_embed` in `PatchEmbeddingBlock` in favor of `proj_type`(#6986) +* `net_name` and `net_kwargs` in `download` in favor of `model`(#7016) +* `img_size` parameter in SwinUNETR (#7093) +### Removed +* `pad_val`, `stride`, `per_channel` and `upsampler` in `OcclusionSensitivity` (#6642) +* `compute_meaniou` (#7019) +* `AsChannelFirst`, `AddChannel`and `SplitChannel` (#7019) +* `create_multigpu_supervised_trainer` and `create_multigpu_supervised_evaluator` (#7019) +* `runner_id` in `run` (#7019) +* `data_src_cfg_filename` in `AlgoEnsembleBuilder` (#7019) +* `get_validation_stats` in `Evaluator` and `get_train_stats` in `Trainer` (#7019) +* `epoch_interval` and `iteration_interval` in `TensorBoardStatsHandler` (#7019) +* some self-hosted test (#7041) + +## [1.2.0] - 2023-06-08 +### Added +* Various Auto3DSeg enhancements and integration tests including multi-node multi-GPU optimization, major usability improvements +* TensorRT and ONNX support for `monai.bundle` API and the relevant models +* nnU-Net V2 integration `monai.apps.nnunet` +* Binary and categorical metrics and event handlers using `MetricsReloaded` +* Python module and CLI entry point for bundle workflows in `monai.bundle.workflows` and `monai.fl.client` +* Modular patch inference API including `PatchInferer`, `merger`, and `splitter` +* Initial release of lazy resampling including transforms and MetaTensor implementations +* Bridge for ITK Image object and MetaTensor `monai.data.itk_torch_bridge` +* Sliding window inference memory efficiency optimization including `SlidingWindowInfererAdapt` +* Generic kernel filtering transforms `ImageFiltered` and `RandImageFiltered` +* Trainable bilateral filters and joint bilateral filters +* ClearML stats and image handlers for experiment tracking +#### misc. +* Utility functions to warn API default value changes (#5738) +* Support of dot notation to access content of `ConfigParser` (#5813) +* Softmax version to focal loss (#6544) +* FROC metric for N-dimensional (#6528) +* Extend SurfaceDiceMetric for 3D images (#6549) +* A `track_meta` option for Lambda and derived transforms (#6385) +* CLIP pre-trained text-to-vision embedding (#6282) +* Optional spacing to surface distances calculations (#6144) +* `WSIReader` read by power and mpp (#6244) +* Support GPU tensor for `GridPatch` and `GridPatchDataset` (#6246) +* `SomeOf` transform composer (#6143) +* GridPatch with both count and threshold filtering (#6055) +### Fixed +#### transforms +* `map_classes_to_indices` efficiency issue (#6468) +* Adaptive resampling mode based on backends (#6429) +* Improve Compose encapsulation (#6224) +* User-provided `FolderLayout` in `SaveImage` and `SaveImaged` transforms (#6213) +* `SpacingD` output shape compute stability (#6126) +* No mutate ratio /user inputs `croppad` (#6127) +* A `warn` flag to RandCropByLabelClasses (#6121) +* `nan` to indicate `no_channel`, split dim singleton (#6090) +* Compatible padding mode (#6076) +* Allow for missing `filename_or_obj` key (#5980) +* `Spacing` pixdim in-place change (#5950) +* Add warning in `RandHistogramShift` (#5877) +* Exclude `cuCIM` wrappers from `get_transform_backends` (#5838) +#### data +* `__format__` implementation of MetaTensor (#6523) +* `channel_dim` in `TiffFileWSIReader` and `CuCIMWSIReader` (#6514) +* Prepend `"meta"` to `MetaTensor.__repr__` and `MetaTensor.__str__` for easier identification (#6214) +* MetaTensor slicing issue (#5845) +* Default writer flags (#6147) +* `WSIReader` defaults and tensor conversion (#6058) +* Remove redundant array copy for WSITiffFileReader (#6089) +* Fix unused arg in `SlidingPatchWSIDataset` (#6047) +* `reverse_indexing` for PILReader (#6008) +* Use `np.linalg` for the small affine inverse (#5967) +#### metrics and losses +* Removing L2-norm in contrastive loss (L2-norm already present in CosSim) (#6550) +* Fixes the SSIM metric (#6250) +* Efficiency issues of Dice metrics (#6412) +* Generalized Dice issue (#5929) +* Unify output tensor devices for multiple metrics (#5924) +#### networks +* Make `RetinaNet` throw errors for NaN only when training (#6479) +* Replace deprecated arg in torchvision models (#6401) +* Improves NVFuser import check (#6399) +* Add `device` in `HoVerNetNuclearTypePostProcessing` and `HoVerNetInstanceMapPostProcessing` (#6333) +* Enhance hovernet load pretrained function (#6269) +* Access to the `att_mat` in self-attention modules (#6493) +* Optional swinunetr-v2 (#6203) +* Add transform to handle empty box as training data for `retinanet_detector` (#6170) +* GPU utilization of DiNTS network (#6050) +* A pixelshuffle upsample shape mismatch problem (#5982) +* GEGLU activation function for the MLP Block (#5856) +* Constructors for `DenseNet` derived classes (#5846) +* Flexible interpolation modes in `regunet` (#5807) +#### bundle +* Optimized the `deepcopy` logic in `ConfigParser` (#6464) +* Improve check and error message of bundle run (#6400) +* Warn or raise ValueError on duplicated key in json/yaml config (#6252) +* Default metadata and logging values for bundle run (#6072) +* `pprint` head and tail in bundle script (#5969) +* Config parsing issue for substring reference (#5932) +* Fix instantiate for object instantiation with attribute `path` (#5866) +* Fix `_get_latest_bundle_version` issue on Windows (#5787) +#### engines and handlers +* MLflow handler run bug (#6446) +* `monai.engine` training attribute check (#6132) +* Update StatsHandler logging message (#6051) +* Added callable options for `iteration_log` and `epoch_log` in TensorBoard and MLFlow (#5976) +* `CheckpointSaver` logging error (#6026) +* Callable options for `iteration_log` and `epoch_log` in StatsHandler (#5965) +#### misc. +* Avoid creating cufile.log when `import monai` (#6106) +* `monai._extensions` module compatibility with rocm (#6161) +* Issue of repeated UserWarning: "TypedStorage is deprecated" (#6105) +* Use logging config at module level (#5960) +* Add ITK to the list of optional dependencies (#5858) +* `RankFilter` to skip logging when the rank is not meeting criteria (#6243) +* Various documentation issues +### Changed +* Overall more precise and consistent type annotations +* Optionally depend on PyTorch-Ignite v0.4.11 instead of v0.4.10 +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:23.03-py3` from `nvcr.io/nvidia/pytorch:22.10-py3` +### Deprecated +* `resample=True`; `resample=False` will be the new default in `SaveImage` +* `random_size=True`; `random_size=False` will be the new default for the random cropping transforms +* `image_only=False`; `image_only=True` will be the new default in `LoadImage` +* `AddChannel` and `AsChannelFirst` in favor of `EnsureChannelFirst` +### Removed +* Deprecated APIs since v0.9, including WSIReader from `monai.apps`, `NiftiSaver` and `PNGSaver` from `monai.data` +* Support for PyTorch 1.8 +* Support for Python 3.7 + +## [1.1.0] - 2022-12-19 +### Added +* Hover-Net based digital pathology workflows including new network, loss, postprocessing, metric, training, and inference modules +* Various enhancements for Auto3dSeg `AutoRunner` including template caching, selection, and a dry-run mode `nni_dry_run` +* Various enhancements for Auto3dSeg algo templates including new state-of-the-art configurations, optimized GPU memory utilization +* New bundle API and configurations to support experiment management including `MLFlowHandler` +* New `bundle.script` API to support model zoo query and download +* `LossMetric` metric to compute loss as cumulative metric measurement +* Transforms and base transform APIs including `RandomizableTrait` and `MedianSmooth` +* `runtime_cache` option for `CacheDataset` and the derived classes to allow for shared caching on the fly +* Flexible name formatter for `SaveImage` transform +* `pending_operations` MetaTensor property and basic APIs for lazy image resampling +* Contrastive sensitivity for SSIM metric +* Extensible backbones for `FlexibleUNet` +* Generalize `SobelGradients` to 3D and any spatial axes +* `warmup_multiplier` option for `WarmupCosineSchedule` +* F beta score metric based on confusion matrix metric +* Support of key overwriting in `Lambdad` +* Basic premerge tests for Python 3.11 +* Unit and integration tests for CUDA 11.6, 11.7 and A100 GPU +* `DataAnalyzer` handles minor image-label shape inconsistencies +### Fixed +* Review and enhance previously untyped APIs with additional type annotations and casts +* `switch_endianness` in LoadImage now supports tensor input +* Reduced memory footprint for various Auto3dSeg tests +* Issue of `@` in `monai.bundle.ReferenceResolver` +* Compatibility issue with ITK-Python 5.3 (converting `itkMatrixF44` for default collate) +* Inconsistent of sform and qform when using different backends for `SaveImage` +* `MetaTensor.shape` call now returns a `torch.Size` instead of tuple +* Issue of channel reduction in `GeneralizedDiceLoss` +* Issue of background handling before softmax in `DiceFocalLoss` +* Numerical issue of `LocalNormalizedCrossCorrelationLoss` +* Issue of incompatible view size in `ConfusionMatrixMetric` +* `NetAdapter` compatibility with Torchscript +* Issue of `extract_levels` in `RegUNet` +* Optional `bias_downsample` in `ResNet` +* `dtype` overflow for `ShiftIntensity` transform +* Randomized transforms such as `RandCuCIM` now inherit `RandomizableTrait` +* `fg_indices.size` compatibility issue in `generate_pos_neg_label_crop_centers` +* Issue when inverting `ToTensor` +* Issue of capital letters in filename suffixes check in `LoadImage` +* Minor tensor compatibility issues in `apps.nuclick.transforms` +* Issue of float16 in `verify_net_in_out` +* `std` variable type issue for `RandRicianNoise` +* `DataAnalyzer` accepts `None` as label key and checks empty labels +* `iter_patch_position` now has a smaller memory footprint +* `CumulativeAverage` has been refactored and enhanced to allow for simple tracking of metric running stats. +* Multi-threading issue for `MLFlowHandler` +### Changed +* Printing a MetaTensor now generates a less verbose representation +* `DistributedSampler` raises a ValueError if there are too few devices +* OpenCV and `VideoDataset` modules are loaded lazily to avoid dependency issues +* `device` in `monai.engines.Workflow` supports string values +* `Activations` and `AsDiscrete` take `kwargs` as additional arguments +* `DataAnalyzer` is now more efficient and writes summary stats before detailed all case stats +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:22.10-py3` from `nvcr.io/nvidia/pytorch:22.09-py3` +* Simplified Conda environment file `environment-dev.yml` +* Versioneer dependency upgraded to `0.23` from `0.19` +### Deprecated +* `NibabelReader` input argument `dtype` is deprecated, the reader will use the original dtype of the image +### Removed +* Support for PyTorch 1.7 + +## [1.0.1] - 2022-10-24 +### Fixes +* DiceCELoss for multichannel targets +* Auto3DSeg DataAnalyzer out-of-memory error and other minor issues +* An optional flag issue in the RetinaNet detector +* An issue with output offset for Spacing +* A `LoadImage` issue when `track_meta` is `False` +* 1D data output error in `VarAutoEncoder` +* An issue with resolution computing in `ImageStats` +### Added +* Flexible min/max pixdim options for Spacing +* Upsample mode `deconvgroup` and optional kernel sizes +* Docstrings for gradient-based saliency maps +* Occlusion sensitivity to use sliding window inference +* Enhanced Gaussian window and device assignments for sliding window inference +* Multi-GPU support for MonaiAlgo +* `ClientAlgoStats` and `MonaiAlgoStats` for federated summary statistics +* MetaTensor support for `OneOf` +* Add a file check for bundle logging config +* Additional content and an authentication token option for bundle info API +* An anti-aliasing option for `Resized` +* `SlidingWindowInferer` adaptive device based on `cpu_thresh` +* `SegResNetDS` with deep supervision and non-isotropic kernel support +* Premerge tests for Python 3.10 +### Changed +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:22.09-py3` from `nvcr.io/nvidia/pytorch:22.08-py3` +* Replace `None` type metadata content with `"none"` for `collate_fn` compatibility +* HoVerNet Mode and Branch to independent StrEnum +* Automatically infer device from the first item in random elastic deformation dict +* Add channel dim in `ComputeHoVerMaps` and `ComputeHoVerMapsd` +* Remove batch dim in `SobelGradients` and `SobelGradientsd` +### Deprecated +* Deprecating `compute_meandice`, `compute_meaniou` in `monai.metrics`, in favor of +`compute_dice` and `compute_iou` respectively + +## [1.0.0] - 2022-09-16 +### Added +* `monai.auto3dseg` base APIs and `monai.apps.auto3dseg` components for automated machine learning (AutoML) workflow +* `monai.fl` module with base APIs and `MonaiAlgo` for federated learning client workflow +* An initial backwards compatibility [guide](https://github.com/Project-MONAI/MONAI/blob/dev/CONTRIBUTING.md#backwards-compatibility) +* Initial release of accelerated MRI reconstruction components, including `CoilSensitivityModel` +* Support of `MetaTensor` and new metadata attributes for various digital pathology components +* Various `monai.bundle` enhancements for MONAI model-zoo usability, including config debug mode and `get_all_bundles_list` +* new `monai.transforms` components including `SignalContinuousWavelet` for 1D signal, `ComputeHoVerMaps` for digital pathology, and `SobelGradients` for spatial gradients +* `VarianceMetric` and `LabelQualityScore` metrics for active learning +* Dataset API for real-time stream and videos +* Several networks and building blocks including `FlexibleUNet` and `HoVerNet` +* `MeanIoUHandler` and `LogfileHandler` workflow event handlers +* `WSIReader` with the TiffFile backend +* Multi-threading in `WSIReader` with cuCIM backend +* `get_stats` API in `monai.engines.Workflow` +* `prune_meta_pattern` in `monai.transforms.LoadImage` +* `max_interactions` for deepedit interaction workflow +* Various profiling utilities in `monai.utils.profiling` +### Changed +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:22.08-py3` from `nvcr.io/nvidia/pytorch:22.06-py3` +* Optionally depend on PyTorch-Ignite v0.4.10 instead of v0.4.9 +* The cache-based dataset now matches the transform information when read/write the cache +* `monai.losses.ContrastiveLoss` now infers `batch_size` during `forward()` +* Rearrange the spatial axes in `RandSmoothDeform` transforms following PyTorch's convention +* Unified several environment flags into `monai.utils.misc.MONAIEnvVars` +* Simplified `__str__` implementation of `MetaTensor` instead of relying on the `__repr__` implementation +### Fixed +* Improved error messages when both `monai` and `monai-weekly` are pip-installed +* Inconsistent pseudo number sequences for different `num_workers` in `DataLoader` +* Issue of repeated sequences for `monai.data.ShuffleBuffer` +* Issue of not preserving the physical extent in `monai.transforms.Spacing` +* Issue of using `inception_v3` as the backbone of `monai.networks.nets.TorchVisionFCModel` +* Index device issue for `monai.transforms.Crop` +* Efficiency issue when converting the array dtype and contiguous memory +### Deprecated +* `Addchannel` and `AsChannelFirst` transforms in favor of `EnsureChannelFirst` +* `monai.apps.pathology.data` components in favor of the corresponding components from `monai.data` +* `monai.apps.pathology.handlers` in favor of the corresponding components from `monai.handlers` +### Removed +* `Status` section in the pull request template in favor of the pull request draft mode +* `monai.engines.BaseWorkflow` +* `ndim` and `dimensions` arguments in favor of `spatial_dims` +* `n_classes`, `num_classes` arguments in `AsDiscrete` in favor of `to_onehot` +* `logit_thresh`, `threshold_values` arguments in `AsDiscrete` in favor of `threshold` +* `torch.testing.assert_allclose` in favor of `tests.utils.assert_allclose` + +## [0.9.1] - 2022-07-22 +### Added +* Support of `monai.data.MetaTensor` as core data structure across the modules +* Support of `inverse` in array-based transforms +* `monai.apps.TciaDataset` APIs for The Cancer Imaging Archive (TCIA) datasets, including a pydicom-backend reader +* Initial release of components for MRI reconstruction in `monai.apps.reconstruction`, including various FFT utilities +* New metrics and losses, including mean IoU and structural similarity index +* `monai.utils.StrEnum` class to simplify Enum-based type annotations +### Changed +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:22.06-py3` from `nvcr.io/nvidia/pytorch:22.04-py3` +* Optionally depend on PyTorch-Ignite v0.4.9 instead of v0.4.8 +### Fixed +* Fixed issue of not skipping post activations in `Convolution` when input arguments are None +* Fixed issue of ignoring dropout arguments in `DynUNet` +* Fixed issue of hard-coded non-linear function in ViT classification head +* Fixed issue of in-memory config overriding with `monai.bundle.ConfigParser.update` +* 2D SwinUNETR incompatible shapes +* Fixed issue with `monai.bundle.verify_metadata` not raising exceptions +* Fixed issue with `monai.transforms.GridPatch` returns inconsistent type location when padding +* Wrong generalized Dice score metric when denominator is 0 but prediction is non-empty +* Docker image build error due to NGC CLI upgrade +* Optional default value when parsing id unavailable in a ConfigParser instance +* Immutable data input for the patch-based WSI datasets +### Deprecated +* `*_transforms` and `*_meta_dict` fields in dictionary-based transforms in favor of MetaTensor +* `meta_keys`, `meta_key_postfix`, `src_affine` arguments in various transforms, in favor of MetaTensor +* `AsChannelFirst` and `AddChannel`, in favor of `EnsureChannelFirst` transform + +## [0.9.0] - 2022-06-08 +### Added +* `monai.bundle` primary module with a `ConfigParser` and command-line interfaces for configuration-based workflows +* Initial release of MONAI bundle specification +* Initial release of volumetric image detection modules including bounding boxes handling, RetinaNet-based architectures +* API preview `monai.data.MetaTensor` +* Unified `monai.data.image_writer` to support flexible IO backends including an ITK writer +* Various new network blocks and architectures including `SwinUNETR` +* DeepEdit interactive training/validation workflow +* NuClick interactive segmentation transforms +* Patch-based readers and datasets for whole-slide imaging +* New losses and metrics including `SurfaceDiceMetric`, `GeneralizedDiceFocalLoss` +* New pre-processing transforms including `RandIntensityRemap`, `SpatialResample` +* Multi-output and slice-based inference for `SlidingWindowInferer` +* `NrrdReader` for NRRD file support +* Torchscript utilities to save models with meta information +* Gradient-based visualization module `SmoothGrad` +* Automatic regular source code scanning for common vulnerabilities and coding errors + +### Changed +* Simplified `TestTimeAugmentation` using de-collate and invertible transforms APIs +* Refactoring `monai.apps.pathology` modules into `monai.handlers` and `monai.transforms` +* Flexible activation and normalization layers for `TopologySearch` and `DiNTS` +* Anisotropic first layers for 3D resnet +* Flexible ordering of activation, normalization in `UNet` +* Enhanced performance of connected-components analysis using Cupy +* `INSTANCE_NVFUSER` for enhanced performance in 3D instance norm +* Support of string representation of dtype in `convert_data_type` +* Added new options `iteration_log`, `iteration_log` to the logging handlers +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:22.04-py3` from `nvcr.io/nvidia/pytorch:21.10-py3` +* `collate_fn` generates more data-related debugging info with `dev_collate` + +### Fixed +* Unified the spellings of "meta data", "metadata", "meta-data" to "metadata" +* Various inaccurate error messages when input data are in invalid shapes +* Issue of computing symmetric distances in `compute_average_surface_distance` +* Unnecessary layer `self.conv3` in `UnetResBlock` +* Issue of torchscript compatibility for `ViT` and self-attention blocks +* Issue of hidden layers in `UNETR` +* `allow_smaller` in spatial cropping transforms +* Antialiasing in `Resize` +* Issue of bending energy loss value at different resolutions +* `kwargs_read_csv` in `CSVDataset` +* In-place modification in `Metric` reduction +* `wrap_array` for `ensure_tuple` +* Contribution guide for introducing new third-party dependencies + +### Removed +* Deprecated `nifti_writer`, `png_writer` in favor of `monai.data.image_writer` +* Support for PyTorch 1.6 + +## [0.8.1] - 2022-02-16 +### Added +* Support of `matshow3d` with given `channel_dim` +* Support of spatial 2D for `ViTAutoEnc` +* Support of `dataframe` object input in `CSVDataset` +* Support of tensor backend for `Orientation` +* Support of configurable delimiter for CSV writers +* A base workflow API +* `DataFunc` API for dataset-level preprocessing +* `write_scalar` API for logging with additional `engine` parameter in `TensorBoardHandler` +* Enhancements for NVTX Range transform logging +* Enhancements for `set_determinism` +* Performance enhancements in the cache-based datasets +* Configurable metadata keys for `monai.data.DatasetSummary` +* Flexible `kwargs` for `WSIReader` +* Logging for the learning rate schedule handler +* `GridPatchDataset` as subclass of `monai.data.IterableDataset` +* `is_onehot` option in `KeepLargestConnectedComponent` +* `channel_dim` in the image readers and support of stacking images with channels +* Skipping workflow `run` if epoch length is 0 +* Enhanced `CacheDataset` to avoid duplicated cache items +* `save_state` utility function + +### Changed +* Optionally depend on PyTorch-Ignite v0.4.8 instead of v0.4.6 +* `monai.apps.mmars.load_from_mmar` defaults to the latest version + +### Fixed +* Issue when caching large items with `pickle` +* Issue of hard-coded activation functions in `ResBlock` +* Issue of `create_file_name` assuming local disk file creation +* Issue of `WSIReader` when the backend is `TiffFile` +* Issue of `deprecated_args` when the function signature contains kwargs +* Issue of `channel_wise` computations for the intensity-based transforms +* Issue of inverting `OneOf` +* Issue of removing temporary caching file for the persistent dataset +* Error messages when reader backend is not available +* Output type casting issue in `ScaleIntensityRangePercentiles` +* Various docstring typos and broken URLs +* `mode` in the evaluator engine +* Ordering of `Orientation` and `Spacing` in `monai.apps.deepgrow.dataset` + +### Removed +* Additional deep supervision modules in `DynUnet` +* Deprecated `reduction` argument for `ContrastiveLoss` +* Decollate warning in `Workflow` +* Unique label exception in `ROCAUCMetric` +* Logger configuration logic in the event handlers + +## [0.8.0] - 2021-11-25 +### Added +* Overview of [new features in v0.8](docs/source/whatsnew_0_8.md) +* Network modules for differentiable neural network topology search (DiNTS) +* Multiple Instance Learning transforms and models for digital pathology WSI analysis +* Vision transformers for self-supervised representation learning +* Contrastive loss for self-supervised learning +* Finalized major improvements of 200+ components in `monai.transforms` to support input and backend in PyTorch and NumPy +* Initial registration module benchmarking with `GlobalMutualInformationLoss` as an example +* `monai.transforms` documentation with visual examples and the utility functions +* Event handler for `MLfLow` integration +* Enhanced data visualization functions including `blend_images` and `matshow3d` +* `RandGridDistortion` and `SmoothField` in `monai.transforms` +* Support of randomized shuffle buffer in iterable datasets +* Performance review and enhancements for data type casting +* Cumulative averaging API with distributed environment support +* Module utility functions including `require_pkg` and `pytorch_after` +* Various usability enhancements such as `allow_smaller` when sampling ROI and `wrap_sequence` when casting object types +* `tifffile` support in `WSIReader` +* Regression tests for the fast training workflows +* Various tutorials and demos including educational contents at [MONAI Bootcamp 2021](https://github.com/Project-MONAI/MONAIBootcamp2021) +### Changed +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:21.10-py3` from `nvcr.io/nvidia/pytorch:21.08-py3` +* Decoupled `TraceKeys` and `TraceableTransform` APIs from `InvertibleTransform` +* Skipping affine-based resampling when `resample=False` in `NiftiSaver` +* Deprecated `threshold_values: bool` and `num_classes: int` in `AsDiscrete` +* Enhanced `apply_filter` for spatially 1D, 2D and 3D inputs with non-separable kernels +* Logging with `logging` in downloading and model archives in `monai.apps` +* API documentation site now defaults to `stable` instead of `latest` +* `skip-magic-trailing-comma` in coding style enforcements +* Pre-merge CI pipelines now include unit tests with Nvidia Ampere architecture +### Removed +* Support for PyTorch 1.5 +* The deprecated `DynUnetV1` and the related network blocks +* GitHub self-hosted CI/CD pipelines for package releases +### Fixed +* Support of path-like objects as file path inputs in most modules +* Issue of `decollate_batch` for dictionary of empty lists +* Typos in documentation and code examples in various modules +* Issue of no available keys when `allow_missing_keys=True` for the `MapTransform` +* Issue of redundant computation when normalization factors are 0.0 and 1.0 in `ScaleIntensity` +* Incorrect reports of registered readers in `ImageReader` +* Wrong numbering of iterations in `StatsHandler` +* Naming conflicts in network modules and aliases +* Incorrect output shape when `reduction="none"` in `FocalLoss` +* Various usability issues reported by users + +## [0.7.0] - 2021-09-24 +### Added +* Overview of [new features in v0.7](docs/source/whatsnew_0_7.md) +* Initial phase of major usability improvements in `monai.transforms` to support input and backend in PyTorch and NumPy +* Performance enhancements, with [profiling and tuning guides](https://github.com/Project-MONAI/tutorials/blob/master/acceleration/fast_model_training_guide.md) for typical use cases +* Reproducing [training modules and workflows](https://github.com/Project-MONAI/tutorials/tree/master/kaggle/RANZCR/4th_place_solution) of state-of-the-art Kaggle competition solutions +* 24 new transforms, including + * `OneOf` meta transform + * DeepEdit guidance signal transforms for interactive segmentation + * Transforms for self-supervised pre-training + * Integration of [NVIDIA Tools Extension](https://developer.nvidia.com/blog/nvidia-tools-extension-api-nvtx-annotation-tool-for-profiling-code-in-python-and-c-c/) (NVTX) + * Integration of [cuCIM](https://github.com/rapidsai/cucim) + * Stain normalization and contextual grid for digital pathology +* `Transchex` network for vision-language transformers for chest X-ray analysis +* `DatasetSummary` utility in `monai.data` +* `WarmupCosineSchedule` +* Deprecation warnings and documentation support for better backwards compatibility +* Padding with additional `kwargs` and different backend API +* Additional options such as `dropout` and `norm` in various networks and their submodules + +### Changed +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:21.08-py3` from `nvcr.io/nvidia/pytorch:21.06-py3` +* Deprecated input argument `n_classes`, in favor of `num_classes` +* Deprecated input argument `dimensions` and `ndims`, in favor of `spatial_dims` +* Updated the Sphinx-based documentation theme for better readability +* `NdarrayTensor` type is replaced by `NdarrayOrTensor` for simpler annotations +* Self-attention-based network blocks now support both 2D and 3D inputs + +### Removed +* The deprecated `TransformInverter`, in favor of `monai.transforms.InvertD` +* GitHub self-hosted CI/CD pipelines for nightly and post-merge tests +* `monai.handlers.utils.evenly_divisible_all_gather` +* `monai.handlers.utils.string_list_all_gather` + +### Fixed +* A Multi-thread cache writing issue in `LMDBDataset` +* Output shape convention inconsistencies of the image readers +* Output directory and file name flexibility issue for `NiftiSaver`, `PNGSaver` +* Requirement of the `label` field in test-time augmentation +* Input argument flexibility issues for `ThreadDataLoader` +* Decoupled `Dice` and `CrossEntropy` intermediate results in `DiceCELoss` +* Improved documentation, code examples, and warning messages in various modules +* Various usability issues reported by users + +## [0.6.0] - 2021-07-08 +### Added +* 10 new transforms, a masked loss wrapper, and a `NetAdapter` for transfer learning +* APIs to load networks and pre-trained weights from Clara Train [Medical Model ARchives (MMARs)](https://docs.nvidia.com/clara/clara-train-sdk/pt/mmar.html) +* Base metric and cumulative metric APIs, 4 new regression metrics +* Initial CSV dataset support +* Decollating mini-batch as the default first postprocessing step, [Migrating your v0.5 code to v0.6](https://github.com/Project-MONAI/MONAI/wiki/v0.5-to-v0.6-migration-guide) wiki shows how to adapt to the breaking changes +* Initial backward compatibility support via `monai.utils.deprecated` +* Attention-based vision modules and `UNETR` for segmentation +* Generic module loaders and Gaussian mixture models using the PyTorch JIT compilation +* Inverse of image patch sampling transforms +* Network block utilities `get_[norm, act, dropout, pool]_layer` +* `unpack_items` mode for `apply_transform` and `Compose` +* New event `INNER_ITERATION_STARTED` in the deepgrow interactive workflow +* `set_data` API for cache-based datasets to dynamically update the dataset content +* Fully compatible with PyTorch 1.9 +* `--disttests` and `--min` options for `runtests.sh` +* Initial support of pre-merge tests with Nvidia Blossom system + +### Changed +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:21.06-py3` from + `nvcr.io/nvidia/pytorch:21.04-py3` +* Optionally depend on PyTorch-Ignite v0.4.5 instead of v0.4.4 +* Unified the demo, tutorial, testing data to the project shared drive, and + [`Project-MONAI/MONAI-extra-test-data`](https://github.com/Project-MONAI/MONAI-extra-test-data) +* Unified the terms: `post_transform` is renamed to `postprocessing`, `pre_transform` is renamed to `preprocessing` +* Unified the postprocessing transforms and event handlers to accept the "channel-first" data format +* `evenly_divisible_all_gather` and `string_list_all_gather` moved to `monai.utils.dist` + +### Removed +* Support of 'batched' input for postprocessing transforms and event handlers +* `TorchVisionFullyConvModel` +* `set_visible_devices` utility function +* `SegmentationSaver` and `TransformsInverter` handlers + +### Fixed +* Issue of handling big-endian image headers +* Multi-thread issue for non-random transforms in the cache-based datasets +* Persistent dataset issue when multiple processes sharing a non-exist cache location +* Typing issue with Numpy 1.21.0 +* Loading checkpoint with both `model` and `optmizier` using `CheckpointLoader` when `strict_shape=False` +* `SplitChannel` has different behaviour depending on numpy/torch inputs +* Transform pickling issue caused by the Lambda functions +* Issue of filtering by name in `generate_param_groups` +* Inconsistencies in the return value types of `class_activation_maps` +* Various docstring typos +* Various usability enhancements in `monai.transforms` + +## [0.5.3] - 2021-05-28 +### Changed +* Project default branch renamed to `dev` from `master` +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:21.04-py3` from `nvcr.io/nvidia/pytorch:21.02-py3` +* Enhanced type checks for the `iteration_metric` handler +* Enhanced `PersistentDataset` to use `tempfile` during caching computation +* Enhanced various info/error messages +* Enhanced performance of `RandAffine` +* Enhanced performance of `SmartCacheDataset` +* Optionally requires `cucim` when the platform is `Linux` +* Default `device` of `TestTimeAugmentation` changed to `cpu` + +### Fixed +* Download utilities now provide better default parameters +* Duplicated `key_transforms` in the patch-based transforms +* A multi-GPU issue in `ClassificationSaver` +* A default `meta_data` issue in `SpacingD` +* Dataset caching issue with the persistent data loader workers +* A memory issue in `permutohedral_cuda` +* Dictionary key issue in `CopyItemsd` +* `box_start` and `box_end` parameters for deepgrow `SpatialCropForegroundd` +* Tissue mask array transpose issue in `MaskedInferenceWSIDataset` +* Various type hint errors +* Various docstring typos + +### Added +* Support of `to_tensor` and `device` arguments for `TransformInverter` +* Slicing options with SpatialCrop +* Class name alias for the networks for backward compatibility +* `k_divisible` option for CropForeground +* `map_items` option for `Compose` +* Warnings of `inf` and `nan` for surface distance computation +* A `print_log` flag to the image savers +* Basic testing pipelines for Python 3.9 + +## [0.5.0] - 2021-04-09 +### Added +* Overview document for [feature highlights in v0.5.0](https://github.com/Project-MONAI/MONAI/blob/master/docs/source/highlights.md) +* Invertible spatial transforms + * `InvertibleTransform` base APIs + * Batch inverse and decollating APIs + * Inverse of `Compose` + * Batch inverse event handling + * Test-time augmentation as an application +* Initial support of learning-based image registration: + * Bending energy, LNCC, and global mutual information loss + * Fully convolutional architectures + * Dense displacement field, dense velocity field computation + * Warping with high-order interpolation with C++/CUDA implementations +* Deepgrow modules for interactive segmentation: + * Workflows with simulations of clicks + * Distance-based transforms for guidance signals +* Digital pathology support: + * Efficient whole slide imaging IO and sampling with Nvidia cuCIM and SmartCache + * FROC measurements for lesion + * Probabilistic post-processing for lesion detection + * TorchVision classification model adaptor for fully convolutional analysis +* 12 new transforms, grid patch dataset, `ThreadDataLoader`, EfficientNets B0-B7 +* 4 iteration events for the engine for finer control of workflows +* New C++/CUDA extensions: + * Conditional random field + * Fast bilateral filtering using the permutohedral lattice +* Metrics summary reporting and saving APIs +* DiceCELoss, DiceFocalLoss, a multi-scale wrapper for segmentation loss computation +* Data loading utilities: + * `decollate_batch` + * `PadListDataCollate` with inverse support +* Support of slicing syntax for `Dataset` +* Initial Torchscript support for the loss modules +* Learning rate finder +* Allow for missing keys in the dictionary-based transforms +* Support of checkpoint loading for transfer learning +* Various summary and plotting utilities for Jupyter notebooks +* Contributor Covenant Code of Conduct +* Major CI/CD enhancements covering the tutorial repository +* Fully compatible with PyTorch 1.8 +* Initial nightly CI/CD pipelines using Nvidia Blossom Infrastructure + +### Changed +* Enhanced `list_data_collate` error handling +* Unified iteration metric APIs +* `densenet*` extensions are renamed to `DenseNet*` +* `se_res*` network extensions are renamed to `SERes*` +* Transform base APIs are rearranged into `compose`, `inverse`, and `transform` +* `_do_transform` flag for the random augmentations is unified via `RandomizableTransform` +* Decoupled post-processing steps, e.g. `softmax`, `to_onehot_y`, from the metrics computations +* Moved the distributed samplers to `monai.data.samplers` from `monai.data.utils` +* Engine's data loaders now accept generic iterables as input +* Workflows now accept additional custom events and state properties +* Various type hints according to Numpy 1.20 +* Refactored testing utility `runtests.sh` to have `--unittest` and `--net` (integration tests) options +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:21.02-py3` from `nvcr.io/nvidia/pytorch:20.10-py3` +* Docker images are now built with self-hosted environments +* Primary contact email updated to `monai.contact@gmail.com` +* Now using GitHub Discussions as the primary communication forum + +### Removed +* Compatibility tests for PyTorch 1.5.x +* Format specific loaders, e.g. `LoadNifti`, `NiftiDataset` +* Assert statements from non-test files +* `from module import *` statements, addressed flake8 F403 + +### Fixed +* Uses American English spelling for code, as per PyTorch +* Code coverage now takes multiprocessing runs into account +* SmartCache with initial shuffling +* `ConvertToMultiChannelBasedOnBratsClasses` now supports channel-first inputs +* Checkpoint handler to save with non-root permissions +* Fixed an issue for exiting the distributed unit tests +* Unified `DynUNet` to have single tensor output w/o deep supervision +* `SegmentationSaver` now supports user-specified data types and a `squeeze_end_dims` flag +* Fixed `*Saver` event handlers output filenames with a `data_root_dir` option +* Load image functions now ensure little-endian +* Fixed the test runner to support regex-based test case matching +* Usability issues in the event handlers + +## [0.4.0] - 2020-12-15 +### Added +* Overview document for [feature highlights in v0.4.0](https://github.com/Project-MONAI/MONAI/blob/master/docs/source/highlights.md) +* Torchscript support for the net modules +* New networks and layers: + * Discrete Gaussian kernels + * Hilbert transform and envelope detection + * Swish and mish activation + * Acti-norm-dropout block + * Upsampling layer + * Autoencoder, Variational autoencoder + * FCNet +* Support of initialisation from pretrained weights for densenet, senet, multichannel AHNet +* Layer-wise learning rate API +* New model metrics and event handlers based on occlusion sensitivity, confusion matrix, surface distance +* CAM/GradCAM/GradCAM++ +* File format-agnostic image loader APIs with Nibabel, ITK readers +* Enhancements for dataset partition, cross-validation APIs +* New data APIs: + * LMDB-based caching dataset + * Cache-N-transforms dataset + * Iterable dataset + * Patch dataset +* Weekly PyPI release +* Fully compatible with PyTorch 1.7 +* CI/CD enhancements: + * Skipping, speed up, fail fast, timed, quick tests + * Distributed training tests + * Performance profiling utilities +* New tutorials and demos: + * Autoencoder, VAE tutorial + * Cross-validation demo + * Model interpretability tutorial + * COVID-19 Lung CT segmentation challenge open-source baseline + * Threadbuffer demo + * Dataset partitioning tutorial + * Layer-wise learning rate demo + * [MONAI Bootcamp 2020](https://github.com/Project-MONAI/MONAIBootcamp2020) + +### Changed +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:20.10-py3` from `nvcr.io/nvidia/pytorch:20.08-py3` + +#### Backwards Incompatible Changes +* `monai.apps.CVDecathlonDataset` is extended to a generic `monai.apps.CrossValidation` with an `dataset_cls` option +* Cache dataset now requires a `monai.transforms.Compose` instance as the transform argument +* Model checkpoint file name extensions changed from `.pth` to `.pt` +* Readers' `get_spatial_shape` returns a numpy array instead of list +* Decoupled postprocessing steps such as `sigmoid`, `to_onehot_y`, `mutually_exclusive`, `logit_thresh` from metrics and event handlers, +the postprocessing steps should be used before calling the metrics methods +* `ConfusionMatrixMetric` and `DiceMetric` computation now returns an additional `not_nans` flag to indicate valid results +* `UpSample` optional `mode` now supports `"deconv"`, `"nontrainable"`, `"pixelshuffle"`; `interp_mode` is only used when `mode` is `"nontrainable"` +* `SegResNet` optional `upsample_mode` now supports `"deconv"`, `"nontrainable"`, `"pixelshuffle"` +* `monai.transforms.Compose` class inherits `monai.transforms.Transform` +* In `Rotate`, `Rotated`, `RandRotate`, `RandRotated` transforms, the `angle` related parameters are interpreted as angles in radians instead of degrees. +* `SplitChannel` and `SplitChanneld` moved from `transforms.post` to `transforms.utility` + +### Removed +* Support of PyTorch 1.4 + +### Fixed +* Enhanced loss functions for stability and flexibility +* Sliding window inference memory and device issues +* Revised transforms: + * Normalize intensity datatype and normalizer types + * Padding modes for zoom + * Crop returns coordinates + * Select items transform + * Weighted patch sampling + * Option to keep aspect ratio for zoom +* Various CI/CD issues + +## [0.3.0] - 2020-10-02 +### Added +* Overview document for [feature highlights in v0.3.0](https://github.com/Project-MONAI/MONAI/blob/master/docs/source/highlights.md) +* Automatic mixed precision support +* Multi-node, multi-GPU data parallel model training support +* 3 new evaluation metric functions +* 11 new network layers and blocks +* 6 new network architectures +* 14 new transforms, including an I/O adaptor +* Cross validation module for `DecathlonDataset` +* Smart Cache module in dataset +* `monai.optimizers` module +* `monai.csrc` module +* Experimental feature of ImageReader using ITK, Nibabel, Numpy, Pillow (PIL Fork) +* Experimental feature of differentiable image resampling in C++/CUDA +* Ensemble evaluator module +* GAN trainer module +* Initial cross-platform CI environment for C++/CUDA code +* Code style enforcement now includes isort and clang-format +* Progress bar with tqdm + +### Changed +* Now fully compatible with PyTorch 1.6 +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:20.08-py3` from `nvcr.io/nvidia/pytorch:20.03-py3` +* Code contributions now require signing off on the [Developer Certificate of Origin (DCO)](https://developercertificate.org/) +* Major work in type hinting finished +* Remote datasets migrated to [Open Data on AWS](https://registry.opendata.aws/) +* Optionally depend on PyTorch-Ignite v0.4.2 instead of v0.3.0 +* Optionally depend on torchvision, ITK +* Enhanced CI tests with 8 new testing environments + +### Removed +* `MONAI/examples` folder (relocated into [`Project-MONAI/tutorials`](https://github.com/Project-MONAI/tutorials)) +* `MONAI/research` folder (relocated to [`Project-MONAI/research-contributions`](https://github.com/Project-MONAI/research-contributions)) + +### Fixed +* `dense_patch_slices` incorrect indexing +* Data type issue in `GeneralizedWassersteinDiceLoss` +* `ZipDataset` return value inconsistencies +* `sliding_window_inference` indexing and `device` issues +* importing monai modules may cause namespace pollution +* Random data splits issue in `DecathlonDataset` +* Issue of randomising a `Compose` transform +* Various issues in function type hints +* Typos in docstring and documentation +* `PersistentDataset` issue with existing file folder +* Filename issue in the output writers + +## [0.2.0] - 2020-07-02 +### Added +* Overview document for [feature highlights in v0.2.0](https://github.com/Project-MONAI/MONAI/blob/master/docs/source/highlights.md) +* Type hints and static type analysis support +* `MONAI/research` folder +* `monai.engine.workflow` APIs for supervised training +* `monai.inferers` APIs for validation and inference +* 7 new tutorials and examples +* 3 new loss functions +* 4 new event handlers +* 8 new layers, blocks, and networks +* 12 new transforms, including post-processing transforms +* `monai.apps.datasets` APIs, including `MedNISTDataset` and `DecathlonDataset` +* Persistent caching, `ZipDataset`, and `ArrayDataset` in `monai.data` +* Cross-platform CI tests supporting multiple Python versions +* Optional import mechanism +* Experimental features for third-party transforms integration + +### Changed +> For more details please visit [the project wiki](https://github.com/Project-MONAI/MONAI/wiki/Notable-changes-between-0.1.0-and-0.2.0) +* Core modules now require numpy >= 1.17 +* Categorized `monai.transforms` modules into crop and pad, intensity, IO, post-processing, spatial, and utility. +* Most transforms are now implemented with PyTorch native APIs +* Code style enforcement and automated formatting workflows now use autopep8 and black +* Base Docker image upgraded to `nvcr.io/nvidia/pytorch:20.03-py3` from `nvcr.io/nvidia/pytorch:19.10-py3` +* Enhanced local testing tools +* Documentation website domain changed to https://docs.monai.io + +### Removed +* Support of Python < 3.6 +* Automatic installation of optional dependencies including pytorch-ignite, nibabel, tensorboard, pillow, scipy, scikit-image + +### Fixed +* Various issues in type and argument names consistency +* Various issues in docstring and documentation site +* Various issues in unit and integration tests +* Various issues in examples and notebooks + +## [0.1.0] - 2020-04-17 +### Added +* Public alpha source code release under the Apache 2.0 license ([highlights](https://github.com/Project-MONAI/MONAI/blob/0.1.0/docs/source/highlights.md)) +* Various tutorials and examples + - Medical image classification and segmentation workflows + - Spacing/orientation-aware preprocessing with CPU/GPU and caching + - Flexible workflows with PyTorch Ignite and Lightning +* Various GitHub Actions + - CI/CD pipelines via self-hosted runners + - Documentation publishing via readthedocs.org + - PyPI package publishing +* Contributing guidelines +* A project logo and badges + +[highlights]: https://github.com/Project-MONAI/MONAI/blob/master/docs/source/highlights.md + +[Unreleased]: https://github.com/Project-MONAI/MONAI/compare/1.5.2...HEAD +[1.5.2]: https://github.com/Project-MONAI/MONAI/compare/1.5.1...1.5.2 +[1.5.1]: https://github.com/Project-MONAI/MONAI/compare/1.5.0...1.5.1 +[1.5.0]: https://github.com/Project-MONAI/MONAI/compare/1.4.0...1.5.0 +[1.4.0]: https://github.com/Project-MONAI/MONAI/compare/1.3.2...1.4.0 +[1.3.2]: https://github.com/Project-MONAI/MONAI/compare/1.3.1...1.3.2 +[1.3.1]: https://github.com/Project-MONAI/MONAI/compare/1.3.0...1.3.1 +[1.3.0]: https://github.com/Project-MONAI/MONAI/compare/1.2.0...1.3.0 +[1.2.0]: https://github.com/Project-MONAI/MONAI/compare/1.1.0...1.2.0 +[1.1.0]: https://github.com/Project-MONAI/MONAI/compare/1.0.1...1.1.0 +[1.0.1]: https://github.com/Project-MONAI/MONAI/compare/1.0.0...1.0.1 +[1.0.0]: https://github.com/Project-MONAI/MONAI/compare/0.9.1...1.0.0 +[0.9.1]: https://github.com/Project-MONAI/MONAI/compare/0.9.0...0.9.1 +[0.9.0]: https://github.com/Project-MONAI/MONAI/compare/0.8.1...0.9.0 +[0.8.1]: https://github.com/Project-MONAI/MONAI/compare/0.8.0...0.8.1 +[0.8.0]: https://github.com/Project-MONAI/MONAI/compare/0.7.0...0.8.0 +[0.7.0]: https://github.com/Project-MONAI/MONAI/compare/0.6.0...0.7.0 +[0.6.0]: https://github.com/Project-MONAI/MONAI/compare/0.5.3...0.6.0 +[0.5.3]: https://github.com/Project-MONAI/MONAI/compare/0.5.0...0.5.3 +[0.5.0]: https://github.com/Project-MONAI/MONAI/compare/0.4.0...0.5.0 +[0.4.0]: https://github.com/Project-MONAI/MONAI/compare/0.3.0...0.4.0 +[0.3.0]: https://github.com/Project-MONAI/MONAI/compare/0.2.0...0.3.0 +[0.2.0]: https://github.com/Project-MONAI/MONAI/compare/0.1.0...0.2.0 +[0.1.0]: https://github.com/Project-MONAI/MONAI/commits/0.1.0 diff --git a/MONAI/source/CITATION.cff b/MONAI/source/CITATION.cff new file mode 100644 index 0000000000000000000000000000000000000000..c64aa0169d84b2e6b7c3aaf87f71b3211b73d0d1 --- /dev/null +++ b/MONAI/source/CITATION.cff @@ -0,0 +1,139 @@ +# YAML 1.2 +# Metadata for citation of this software according to the CFF format (https://citation-file-format.github.io/) +# +--- +title: "MONAI: Medical Open Network for AI" +abstract: "AI Toolkit for Healthcare Imaging" +authors: + - name: "MONAI Consortium" +date-released: 2026-01-29 +version: "1.5.2" +identifiers: + - description: "This DOI represents all versions of MONAI, and will always resolve to the latest one." + type: doi + value: "10.5281/zenodo.4323058" +license: "Apache-2.0" +repository-code: "https://github.com/Project-MONAI/MONAI" +url: "https://project-monai.github.io/" +cff-version: "1.2.0" +message: "If you use this software, please cite it using these metadata." +preferred-citation: + type: article + authors: + - given-names: "M. Jorge" + family-names: "Cardoso" + - given-names: "Wenqi" + family-names: "Li" + - given-names: "Richard" + family-names: "Brown" + - given-names: "Nic" + family-names: "Ma" + - given-names: "Eric" + family-names: "Kerfoot" + - given-names: "Yiheng" + family-names: "Wang" + - given-names: "Benjamin" + family-names: "Murray" + - given-names: "Andriy" + family-names: "Myronenko" + - given-names: "Can" + family-names: "Zhao" + - given-names: "Dong" + family-names: "Yang" + - given-names: "Vishwesh" + family-names: "Nath" + - given-names: "Yufan" + family-names: "He" + - given-names: "Ziyue" + family-names: "Xu" + - given-names: "Ali" + family-names: "Hatamizadeh" + - given-names: "Wentao" + family-names: "Zhu" + - given-names: "Yun" + family-names: "Liu" + - given-names: "Mingxin" + family-names: "Zheng" + - given-names: "Yucheng" + family-names: "Tang" + - given-names: "Isaac" + family-names: "Yang" + - given-names: "Michael" + family-names: "Zephyr" + - given-names: "Behrooz" + family-names: "Hashemian" + - given-names: "Sachidanand" + family-names: "Alle" + - given-names: "Mohammad" + family-names: "Zalbagi Darestani" + - given-names: "Charlie" + family-names: "Budd" + - given-names: "Marc" + family-names: "Modat" + - given-names: "Tom" + family-names: "Vercauteren" + - given-names: "Guotai" + family-names: "Wang" + - given-names: "Yiwen" + family-names: "Li" + - given-names: "Yipeng" + family-names: "Hu" + - given-names: "Yunguan" + family-names: "Fu" + - given-names: "Benjamin" + family-names: "Gorman" + - given-names: "Hans" + family-names: "Johnson" + - given-names: "Brad" + family-names: "Genereaux" + - given-names: "Barbaros S." + family-names: "Erdal" + - given-names: "Vikash" + family-names: "Gupta" + - given-names: "Andres" + family-names: "Diaz-Pinto" + - given-names: "Andre" + family-names: "Dourson" + - given-names: "Lena" + family-names: "Maier-Hein" + - given-names: "Paul F." + family-names: "Jaeger" + - given-names: "Michael" + family-names: "Baumgartner" + - given-names: "Jayashree" + family-names: "Kalpathy-Cramer" + - given-names: "Mona" + family-names: "Flores" + - given-names: "Justin" + family-names: "Kirby" + - given-names: "Lee A.D." + family-names: "Cooper" + - given-names: "Holger R." + family-names: "Roth" + - given-names: "Daguang" + family-names: "Xu" + - given-names: "David" + family-names: "Bericat" + - given-names: "Ralf" + family-names: "Floca" + - given-names: "S. Kevin" + family-names: "Zhou" + - given-names: "Haris" + family-names: "Shuaib" + - given-names: "Keyvan" + family-names: "Farahani" + - given-names: "Klaus H." + family-names: "Maier-Hein" + - given-names: "Stephen" + family-names: "Aylward" + - given-names: "Prerna" + family-names: "Dogra" + - given-names: "Sebastien" + family-names: "Ourselin" + - given-names: "Andrew" + family-names: "Feng" + doi: "https://doi.org/10.48550/arXiv.2211.02701" + month: 11 + year: 2022 + title: "MONAI: An open-source framework for deep learning in healthcare" +... diff --git a/MONAI/source/CODE_OF_CONDUCT.md b/MONAI/source/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000000000000000000000000000000000..c6f6fda20a502646430f5ab4f18f6ce4027fe8ec --- /dev/null +++ b/MONAI/source/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at monai.contact@gmail.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/MONAI/source/CONTRIBUTING.md b/MONAI/source/CONTRIBUTING.md new file mode 100644 index 0000000000000000000000000000000000000000..df7f5e336c202a7b9da5548889aca570ce205a20 --- /dev/null +++ b/MONAI/source/CONTRIBUTING.md @@ -0,0 +1,417 @@ +- [Introduction](#introduction) +- [The contribution process](#the-contribution-process) + - [Preparing pull requests](#preparing-pull-requests) + 1. [Checking the coding style](#checking-the-coding-style) + 1. [Unit testing](#unit-testing) + 1. [Building the documentation](#building-the-documentation) + 1. [Automatic code formatting](#automatic-code-formatting) + 1. [Adding new optional dependencies](#adding-new-optional-dependencies) + 1. [Signing your work](#signing-your-work) + 1. [Utility functions](#utility-functions) + 1. [Backwards compatibility](#backwards-compatibility) + - [Submitting pull requests](#submitting-pull-requests) +- [The code reviewing process (for the maintainers)](#the-code-reviewing-process) + - [Reviewing pull requests](#reviewing-pull-requests) +- [Admin tasks (for the maintainers)](#admin-tasks) + - [Releasing a new version](#release-a-new-version) + +## Introduction + +Welcome to Project MONAI! We're excited you're here and want to contribute. This documentation is intended for individuals and institutions interested in contributing to MONAI. MONAI is an open-source project and, as such, its success relies on its community of contributors willing to keep improving it. Your contribution will be a valued addition to the code base; we simply ask that you read this page and understand our contribution process, whether you are a seasoned open-source contributor or whether you are a first-time contributor. + +### Communicate with us + +We are happy to talk with you about your needs for MONAI and your ideas for contributing to the project. One way to do this is to create an issue discussing your thoughts. It might be that a very similar feature is under development or already exists, so an issue is a great starting point. If you are looking for an issue to resolve that will help Project MONAI, see the [*good first issue*](https://github.com/Project-MONAI/MONAI/labels/good%20first%20issue) and [*Contribution wanted*](https://github.com/Project-MONAI/MONAI/labels/Contribution%20wanted) labels. + +### Does it belong in PyTorch instead of MONAI? + +MONAI is part of [PyTorch Ecosystem](https://pytorch.org/ecosystem/), and mainly based on the PyTorch and Numpy libraries. These libraries implement what we consider to be best practice for general scientific computing and deep learning functionality. MONAI builds on these with a strong focus on medical applications. As such, it is a good idea to consider whether your functionality is medical-application specific or not. General deep learning functionality may be better off in PyTorch; you can find their contribution guidelines [here](https://pytorch.org/docs/stable/community/contribution_guide.html). + +## The contribution process + +*Pull request early* + +We encourage you to create pull requests early. It helps us track the contributions under development, whether they are ready to be merged or not. [Create a draft pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request) until it is ready for formal review. + +Please note that, as per PyTorch, MONAI uses American English spelling. This means classes and variables should be: normali**z**e, visuali**z**e, colo~~u~~r, etc. + +### Preparing pull requests + +To ensure the code quality, MONAI relies on several linting tools ([flake8 and its plugins](https://gitlab.com/pycqa/flake8), [black](https://github.com/psf/black), [isort](https://github.com/timothycrosley/isort), [ruff](https://github.com/astral-sh/ruff)), +static type analysis tools ([mypy](https://github.com/python/mypy), [pytype](https://github.com/google/pytype)), as well as a set of unit/integration tests. + +This section highlights all the necessary preparation steps required before sending a pull request. +To collaborate efficiently, please read through this section and follow them. + +- [Checking the coding style](#checking-the-coding-style) +- [Licensing information](#licensing-information) +- [Unit testing](#unit-testing) +- [Building documentation](#building-the-documentation) +- [Signing your work](#signing-your-work) + +#### Checking the coding style + +Coding style is checked and enforced by flake8, black, isort, and ruff, using [a flake8 configuration](./setup.cfg) similar to [PyTorch's](https://github.com/pytorch/pytorch/blob/master/.flake8). +Before submitting a pull request, we recommend that all linting should pass, by running the following command locally: + +```bash +# optionally update the dependencies and dev tools +python -m pip install -U pip +python -m pip install -U -r requirements-dev.txt + +# run the linting and type checking tools +./runtests.sh --codeformat + +# try to fix the coding style errors automatically +./runtests.sh --autofix +``` + +Full linting and type checking may take some time. If you need a quick check, run + +```bash +# run ruff only +./runtests.sh --ruff +``` + +#### Licensing information + +All source code files should start with this paragraph: + +``` +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +``` + +##### Exporting modules + +If you intend for any variables/functions/classes to be available outside of the file with the edited functionality, then: + +- Create or append to the `__all__` variable (in the file in which functionality has been added), and +- Add to the `__init__.py` file. + +#### Unit testing + +MONAI tests are located under `tests/`. + +- The unit test's file name currently follows `test_[module_name].py` or `test_[module_name]_dist.py`. +- The `test_[module_name]_dist.py` subset of unit tests requires a distributed environment to verify the module with distributed GPU-based computation. +- The integration test's file name follows `test_integration_[workflow_name].py`. + +A bash script (`runtests.sh`) is provided to run all tests locally. +Please run ``./runtests.sh -h`` to see all options. + +To run a particular test, for example `tests/losses/test_dice_loss.py`: + +``` +python -m tests.losses.test_dice_loss +``` + +Before submitting a pull request, we recommend that all linting and unit tests +should pass, by running the following command locally: + +```bash +./runtests.sh -f -u --net --coverage +``` + +or (for new features that would not break existing functionality): + +```bash +./runtests.sh --quick --unittests +``` + +It is recommended that the new test `test_[module_name].py` is constructed by using only +python 3.9+ build-in functions, `torch`, `numpy`, `coverage` (for reporting code coverages) and `parameterized` (for organising test cases) packages. +If it requires any other external packages, please make sure: + +- the packages are listed in [`requirements-dev.txt`](requirements-dev.txt) +- the new test `test_[module_name].py` is added to the `exclude_cases` in [`./tests/min_tests.py`](./tests/min_tests.py) so that +the minimal CI runner will not execute it. + +##### Testing data + +Testing data such as images and binary files should not be placed in the source code repository. +Please deploy them to a reliable file sharing location (the current preferred one is [https://github.com/Project-MONAI/MONAI-extra-test-data/releases](https://github.com/Project-MONAI/MONAI-extra-test-data/releases)). +At test time, the URLs within `tests/testing_data/data_config.json` are accessible +via the APIs provided in `tests.utils`: `tests.utils.testing_data_config` and `tests.utils.download_url_or_skip_test`. + +*If it's not tested, it's broken* + +All new functionality should be accompanied by an appropriate set of tests. +MONAI functionality has plenty of unit tests from which you can draw inspiration, +and you can reach out to us if you are unsure of how to proceed with testing. + +MONAI's code coverage report is available at [CodeCov](https://codecov.io/gh/Project-MONAI/MONAI). + +#### Building the documentation + +MONAI's documentation is located at `docs/`. + +```bash +# install the doc-related dependencies +pip install --upgrade pip +pip install -r docs/requirements.txt + +# build the docs +cd docs/ +make html +``` + +The above commands build html documentation, they are used to automatically generate [https://docs.monai.io](https://docs.monai.io). + +The Python code docstring are written in +[reStructuredText](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) and +the documentation pages can be in either [reStructuredText](https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) or [Markdown](https://en.wikipedia.org/wiki/Markdown). In general the Python docstrings follow the [Google style](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings). + +Before submitting a pull request, it is recommended to: + +- edit the relevant `.rst` files in [`docs/source`](./docs/source) accordingly. +- build html documentation locally +- check the auto-generated documentation (by browsing `./docs/build/html/index.html` with a web browser) +- type `make clean` in `docs/` folder to remove the current build files. + +Please type `make help` in `docs/` folder for all supported format options. + +#### Automatic code formatting + +MONAI provides support of automatic Python code formatting via [a customised GitHub action](https://github.com/Project-MONAI/monai-code-formatter). +This makes the project's Python coding style consistent and reduces maintenance burdens. +Commenting a pull request with `/black` triggers the formatting action based on [`psf/Black`](https://github.com/psf/black) (this is implemented with [`slash command dispatch`](https://github.com/marketplace/actions/slash-command-dispatch)). + +Steps for the formatting process: + +- After submitting a pull request or push to an existing pull request, +make a comment to the pull request to trigger the formatting action. +The first line of the comment must be `/black` so that it will be interpreted by [the comment parser](https://github.com/marketplace/actions/slash-command-dispatch#how-are-comments-parsed-for-slash-commands). +- [Auto] The GitHub action tries to format all Python files (using [`psf/Black`](https://github.com/psf/black)) in the branch and makes a commit under the name "MONAI bot" if there's code change. The actual formatting action is deployed at [project-monai/monai-code-formatter](https://github.com/Project-MONAI/monai-code-formatter). +- [Auto] After the formatting commit, the GitHub action adds an emoji to the comment that triggered the process. +- Repeat the above steps if necessary. + +#### Adding new optional dependencies + +In addition to the minimal requirements of PyTorch and Numpy, MONAI's core modules are built optionally based on 3rd-party packages. +The current set of dependencies is listed in [installing dependencies](https://monai.readthedocs.io/en/stable/installation.html#installing-the-recommended-dependencies). + +To allow for flexible integration of MONAI with other systems and environments, +the optional dependency APIs are always invoked lazily. For example, + +```py +from monai.utils import optional_import +itk, _ = optional_import("itk", ...) + +class ITKReader(ImageReader): + ... + def read(self, ...): + return itk.imread(...) +``` + +The availability of the external `itk.imread` API is not required unless `monai.data.ITKReader.read` is called by the user. +Integration tests with minimal requirements are deployed to ensure this strategy. + +To add new optional dependencies, please communicate with the core team during pull request reviews, +and add the necessary information (at least) to the following files: + +- [setup.cfg](https://github.com/Project-MONAI/MONAI/blob/dev/setup.cfg) (for package's `[options.extras_require]` config) +- [requirements-dev.txt](https://github.com/Project-MONAI/MONAI/blob/dev/requirements-dev.txt) (pip requirements file) +- [docs/requirements.txt](https://github.com/Project-MONAI/MONAI/blob/dev/docs/requirements.txt) (docs pip requirements file) +- [environment-dev.yml](https://github.com/Project-MONAI/MONAI/blob/dev/environment-dev.yml) (conda environment file) +- [installation.md](https://github.com/Project-MONAI/MONAI/blob/dev/docs/source/installation.md) (documentation) + +When writing unit tests that use 3rd-party packages, it is a good practice to always consider +an appropriate fallback default behaviour when the packages are not installed in +the testing environment. For example: + +```py +from monai.utils import optional_import +plt, has_matplotlib = optional_import("matplotlib.pyplot") + +@skipUnless(has_matplotlib, "Matplotlib required") +class TestBlendImages(unittest.TestCase): +``` + +It skips the test cases when `matplotlib.pyplot` APIs are not available. + +Alternatively, add the test file name to the ``exclude_cases`` in `tests/min_tests.py` to completely skip the test +cases when running in a minimal setup. + +#### Signing your work + +MONAI enforces the [Developer Certificate of Origin](https://developercertificate.org/) (DCO) on all pull requests. +All commit messages should contain the `Signed-off-by` line with an email address. The [GitHub DCO app](https://github.com/apps/dco) is deployed on MONAI. The pull request's status will be `failed` if commits do not contain a valid `Signed-off-by` line. + +Git has a `-s` (or `--signoff`) command-line option to append this automatically to your commit message: + +```bash +git commit -s -m 'a new commit' +``` + +The commit message will be: + +``` + a new commit + + Signed-off-by: Your Name +``` + +Full text of the DCO: + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +1 Letterman Drive +Suite D4700 +San Francisco, CA, 94129 + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +#### Utility functions + +MONAI provides a set of generic utility functions and frequently used routines. +These are located in [``monai/utils``](./monai/utils/) and in the module folders such as [``networks/utils.py``](./monai/networks/). +Users are encouraged to use these common routines to improve code readability and reduce the code maintenance burdens. + +Notably, + +- ``monai.module.export`` decorator can make the module name shorter when importing, +for example, ``import monai.transforms.Spacing`` is the equivalent of ``monai.transforms.spatial.array.Spacing`` if +``class Spacing`` defined in file `monai/transforms/spatial/array.py` is decorated with ``@export("monai.transforms")``. + +For string definition, [f-string](https://www.python.org/dev/peps/pep-0498/) is recommended to use over `%-print` and `format-print`. So please try to use `f-string` if you need to define any string object. + +#### Backwards compatibility + +MONAI in general follows [PyTorch's policy for backward compatibility](https://github.com/pytorch/pytorch/wiki/PyTorch's-Python-Frontend-Backward-and-Forward-Compatibility-Policy). +Utility functions are provided in `monai.utils.deprecated` to help migrate from the deprecated to new APIs. The use of these utilities is encouraged. +The pull request [template contains checkboxes](https://github.com/Project-MONAI/MONAI/blame/dev/.github/pull_request_template.md#L11-L12) that +the contributor should use accordingly to clearly indicate breaking changes. + +The process of releasing backwards incompatible API changes is as follows: + +1. discuss the breaking changes during pull requests or in dev meetings with a feature proposal if needed. +1. add a warning message in the upcoming release (version `X.Y`), the warning message should include a forecast of removing the deprecated API in: + 1. `X+1.0` -- major version `X+1` and minor version `0` the next major version if it's a significant change, + 1. `X.Y+2` -- major version `X` and minor version `Y+2` (the minor version after the next one), if it's a minor API change. + 1. Note that the versioning policy is similar to PyTorch's approach which does not precisely follow [the semantic versioning](https://semver.org/) definition. + Major version numbers are instead used to represent major product version (which is currently not planned to be greater than 1), + minor version for both compatible and incompatible, and patch version for bug fixes. + 1. when recommending new API to use in place of a deprecated API, the recommended version should + provide exact feature-like behaviour otherwise users will have a harder time migrating. +1. add new test cases by extending the existing unit tests to cover both the deprecated and updated APIs. +1. collect feedback from the users during the subsequent few releases, and reconsider step 1 if needed. +1. before each release, review the deprecating APIs and relevant tests, and clean up the removed APIs described in step 2. + +### Submitting pull requests + +All code changes to the dev branch must be done via [pull requests](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests). + +1. Create a new ticket or take a known ticket from [the issue list][monai issue list]. +1. Check if there's already a branch dedicated to the task. +1. If the task has not been taken, [create a new branch in your fork](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork) +of the codebase named `[ticket_id]-[task_name]`. +For example, branch name `19-ci-pipeline-setup` corresponds to [issue #19](https://github.com/Project-MONAI/MONAI/issues/19). +Ideally, the new branch should be based on the latest `dev` branch. +1. Make changes to the branch ([use detailed commit messages if possible](https://chris.beams.io/posts/git-commit/)). +1. Make sure that new tests cover the changes and the changed codebase [passes all tests locally](#unit-testing). +1. [Create a new pull request](https://help.github.com/en/desktop/contributing-to-projects/creating-a-pull-request) from the task branch to the dev branch, with detailed descriptions of the purpose of this pull request. +1. Check [the CI/CD status of the pull request][github ci], make sure all CI/CD tests passed. +1. Wait for reviews; if there are reviews, make point-to-point responses, make further code changes if needed. +1. If there are conflicts between the pull request branch and the dev branch, pull the changes from the dev and resolve the conflicts locally. +1. Reviewer and contributor may have discussions back and forth until all comments addressed. +1. Wait for the pull request to be merged. + +## The code reviewing process + +### Reviewing pull requests + +All code review comments should be specific, constructive, and actionable. + +1. Check [the CI/CD status of the pull request][github ci], make sure all CI/CD tests passed before reviewing (contact the branch owner if needed). +1. Read carefully the descriptions of the pull request and the files changed, write comments if needed. +1. Make in-line comments to specific code segments, [request for changes](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-request-reviews) if needed. +1. Review any further code changes until all comments addressed by the contributors. +1. Comment to trigger `/black` and/or `/integration-test` for optional auto code formatting and [integration tests](.github/workflows/integration.yml). +1. [Maintainers] Review the changes and comment `/build` to trigger internal full tests. +1. Merge the pull request to the dev branch. +1. Close the corresponding task ticket on [the issue list][monai issue list]. + +[github ci]: https://github.com/Project-MONAI/MONAI/actions +[monai issue list]: https://github.com/Project-MONAI/MONAI/issues + +## Admin tasks + +### Release a new version + +The `dev` branch's `HEAD` always corresponds to MONAI docker image's latest tag: `projectmonai/monai:latest`. +The `main` branch's `HEAD` always corresponds to the latest MONAI milestone release. + +When major features are ready for a milestone, to prepare for a new release: + +- Prepare [a release note](https://github.com/Project-MONAI/MONAI/releases) and release checklist. +- Check out or cherry-pick a new branch `releasing/[version number]` locally from the `dev` branch and push to the codebase. +- Create a release candidate tag, for example, `git tag -a 0.1.0rc1 -m "release candidate 1 of version 0.1.0"`. +- Push the tag to the codebase, for example, `git push origin 0.1.0rc1`. + This step will trigger package building and testing. + The resultant packages are automatically uploaded to + [TestPyPI](https://test.pypi.org/project/monai/). The packages are also available for downloading as + repository's artifacts (e.g. the file at ). +- Check the release test at [TestPyPI](https://test.pypi.org/project/monai/), download the artifacts when the CI finishes. +- Optionally run [the cron testing jobs](https://github.com/Project-MONAI/MONAI/blob/dev/.github/workflows/cron.yml) on `releasing/[version number]`. +- Rebase `releasing/[version number]` to `main`, make sure all the test pipelines succeed. +- Once the release candidate is verified, tag and push a milestone, for example, `git push origin 0.1.0`. + The tag must be with the latest commit of `releasing/[version number]`. +- Upload the packages to [PyPI](https://pypi.org/project/monai/). + This could be done manually by ``twine upload dist/*``, given the artifacts are unzipped to the folder ``dist/``. +- Merge `releasing/[version number]` to `dev`, this step must make sure that the tagging commit unchanged on `dev`. +- Publish the release note. + +Note that the release should be tagged with a [PEP440](https://www.python.org/dev/peps/pep-0440/) compliant version number. + +If any error occurs during the release process, first check out a new hotfix branch from the `releasing/[version number]`, +then make PRs to the `releasing/[version number]` to fix the bugs via the regular contribution procedure. + +If any error occurs after the release process, first check out a new hotfix branch from the `main` branch, +make a patch version release following the semantic versioning, for example, `releasing/0.1.1`. +Make sure the `releasing/0.1.1` is merged back into both `dev` and `main` and all the test pipelines succeed. + +

+ ⬆️ Back to Top +

diff --git a/MONAI/source/Dockerfile b/MONAI/source/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..d538fd3145ef5db5cc7360207bdca5b25e606df9 --- /dev/null +++ b/MONAI/source/Dockerfile @@ -0,0 +1,66 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# To build with a different base image +# please run `docker build` using the `--build-arg PYTORCH_IMAGE=...` flag. +ARG PYTORCH_IMAGE=nvcr.io/nvidia/pytorch:24.10-py3 +FROM ${PYTORCH_IMAGE} + +LABEL maintainer="monai.contact@gmail.com" + +# TODO: remark for issue [revise the dockerfile](https://github.com/zarr-developers/numcodecs/issues/431) +RUN if [[ $(uname -m) =~ "aarch64" ]]; then \ + export CFLAGS="-O3" && \ + export DISABLE_NUMCODECS_SSE2=true && \ + export DISABLE_NUMCODECS_AVX2=true && \ + pip install numcodecs; \ + fi + +WORKDIR /opt/monai + +# install full deps +COPY requirements.txt requirements-min.txt requirements-dev.txt /tmp/ +RUN cp /tmp/requirements.txt /tmp/req.bak \ + && awk '!/torch/' /tmp/requirements.txt > /tmp/tmp && mv /tmp/tmp /tmp/requirements.txt \ + && python -m pip install --upgrade --no-cache-dir pip \ + && python -m pip install --no-cache-dir -r /tmp/requirements-dev.txt + +# compile ext and remove temp files +# TODO: remark for issue [revise the dockerfile #1276](https://github.com/Project-MONAI/MONAI/issues/1276) +# please specify exact files and folders to be copied -- else, basically always, the Docker build process cannot cache +# this or anything below it and always will build from at most here; one file change leads to no caching from here on... + +COPY LICENSE CHANGELOG.md CODE_OF_CONDUCT.md CONTRIBUTING.md README.md versioneer.py setup.py setup.cfg runtests.sh MANIFEST.in ./ +COPY tests ./tests +COPY monai ./monai + +# TODO: remove this line and torch.patch for 24.11 +RUN patch -R -d /usr/local/lib/python3.10/dist-packages/torch/onnx/ < ./monai/torch.patch + +RUN BUILD_MONAI=1 FORCE_CUDA=1 python setup.py develop \ + && rm -rf build __pycache__ + +# NGC Client +WORKDIR /opt/tools +ARG NGC_CLI_URI="https://ngc.nvidia.com/downloads/ngccli_linux.zip" +RUN wget -q ${NGC_CLI_URI} && unzip ngccli_linux.zip && chmod u+x ngc-cli/ngc && \ + find ngc-cli/ -type f -exec md5sum {} + | LC_ALL=C sort | md5sum -c ngc-cli.md5 && \ + rm -rf ngccli_linux.zip ngc-cli.md5 +ENV PATH=${PATH}:/opt/tools:/opt/tools/ngc-cli +RUN apt-get update \ + && DEBIAN_FRONTEND="noninteractive" apt-get install -y libopenslide0 \ + && rm -rf /var/lib/apt/lists/* +# append /opt/tools to runtime path for NGC CLI to be accessible from all file system locations +ENV PATH=${PATH}:/opt/tools +ENV POLYGRAPHY_AUTOINSTALL_DEPS=1 + + +WORKDIR /opt/monai diff --git a/MONAI/source/LICENSE b/MONAI/source/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64 --- /dev/null +++ b/MONAI/source/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/MONAI/source/MANIFEST.in b/MONAI/source/MANIFEST.in new file mode 100644 index 0000000000000000000000000000000000000000..cab456240cdd2d590684c5d186a331a2f7cca062 --- /dev/null +++ b/MONAI/source/MANIFEST.in @@ -0,0 +1,5 @@ +include versioneer.py +include monai/_version.py + +include README.md +include LICENSE diff --git a/MONAI/source/README.md b/MONAI/source/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c327846d8e8e743a49d7966c7813d9b57e16457f --- /dev/null +++ b/MONAI/source/README.md @@ -0,0 +1,96 @@ +

+project-monai +

+ +**M**edical **O**pen **N**etwork for **AI** + +![Supported Python versions](https://raw.githubusercontent.com/Project-MONAI/MONAI/dev/docs/images/python.svg) +[![License](https://img.shields.io/badge/license-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0) +[![auto-commit-msg](https://img.shields.io/badge/dynamic/json?label=citations&query=%24.citationCount&url=https%3A%2F%2Fapi.semanticscholar.org%2Fgraph%2Fv1%2Fpaper%2FDOI%3A10.48550%2FarXiv.2211.02701%3Ffields%3DcitationCount)](https://arxiv.org/abs/2211.02701) +[![PyPI version](https://badge.fury.io/py/monai.svg)](https://badge.fury.io/py/monai) +[![docker](https://img.shields.io/badge/docker-pull-green.svg?logo=docker&logoColor=white)](https://hub.docker.com/r/projectmonai/monai) +[![conda](https://img.shields.io/conda/vn/conda-forge/monai?color=green)](https://anaconda.org/conda-forge/monai) + +[![premerge](https://github.com/Project-MONAI/MONAI/actions/workflows/pythonapp.yml/badge.svg?branch=dev)](https://github.com/Project-MONAI/MONAI/actions/workflows/pythonapp.yml) +[![postmerge](https://img.shields.io/github/checks-status/project-monai/monai/dev?label=postmerge)](https://github.com/Project-MONAI/MONAI/actions?query=branch%3Adev) +[![Documentation Status](https://readthedocs.org/projects/monai/badge/?version=latest)](https://monai.readthedocs.io/en/latest/) +[![codecov](https://codecov.io/gh/Project-MONAI/MONAI/branch/dev/graph/badge.svg?token=6FTC7U1JJ4)](https://codecov.io/gh/Project-MONAI/MONAI) +[![monai Downloads Last Month](https://assets.piptrends.com/get-last-month-downloads-badge/monai.svg 'monai Downloads Last Month by pip Trends')](https://piptrends.com/package/monai) + +MONAI is a [PyTorch](https://pytorch.org/)-based, [open-source](https://github.com/Project-MONAI/MONAI/blob/dev/LICENSE) framework for deep learning in healthcare imaging, part of the [PyTorch Ecosystem](https://pytorch.org/ecosystem/). +Its ambitions are as follows: + +- Developing a community of academic, industrial and clinical researchers collaborating on a common foundation; +- Creating state-of-the-art, end-to-end training workflows for healthcare imaging; +- Providing researchers with the optimized and standardized way to create and evaluate deep learning models. + +## Features + +> _Please see [the technical highlights](https://monai.readthedocs.io/en/latest/highlights.html) and [What's New](https://monai.readthedocs.io/en/latest/whatsnew.html) of the milestone releases._ + +- flexible pre-processing for multi-dimensional medical imaging data; +- compositional & portable APIs for ease of integration in existing workflows; +- domain-specific implementations for networks, losses, evaluation metrics and more; +- customizable design for varying user expertise; +- multi-GPU multi-node data parallelism support. + +## Requirements + +MONAI works with the [currently supported versions of Python](https://devguide.python.org/versions), and depends directly on NumPy and PyTorch with many optional dependencies. + +* Major releases of MONAI will have dependency versions stated for them. The current state of the `dev` branch in this repository is the unreleased development version of MONAI which typically will support current versions of dependencies and include updates and bug fixes to do so. +* PyTorch support covers [the current version](https://github.com/pytorch/pytorch/releases) plus three previous minor versions. If compatibility issues with a PyTorch version and other dependencies arise, support for a version may be delayed until a major release. +* Our support policy for other dependencies adheres for the most part to [SPEC0](https://scientific-python.org/specs/spec-0000), where dependency versions are supported where possible for up to two years. Discovered vulnerabilities or defects may require certain versions to be explicitly not supported. +* See the `requirements*.txt` files for dependency version information. + +## Installation + +To install [the current release](https://pypi.org/project/monai/), you can simply run: + +```bash +pip install monai +``` + +Please refer to [the installation guide](https://monai.readthedocs.io/en/latest/installation.html) for other installation options. + +## Getting Started + +[MedNIST demo](https://colab.research.google.com/github/Project-MONAI/tutorials/blob/main/2d_classification/mednist_tutorial.ipynb) and [MONAI for PyTorch Users](https://colab.research.google.com/github/Project-MONAI/tutorials/blob/main/modules/developer_guide.ipynb) are available on Colab. + +Examples and notebook tutorials are located at [Project-MONAI/tutorials](https://github.com/Project-MONAI/tutorials). + +Technical documentation is available at [docs.monai.io](https://docs.monai.io). + +## Citation + +If you have used MONAI in your research, please cite us! The citation can be exported from: . + +## Model Zoo + +[The MONAI Model Zoo](https://github.com/Project-MONAI/model-zoo) is a place for researchers and data scientists to share the latest and great models from the community. +Utilizing [the MONAI Bundle format](https://monai.readthedocs.io/en/latest/bundle_intro.html) makes it easy to [get started](https://github.com/Project-MONAI/tutorials/tree/main/model_zoo) building workflows with MONAI. + +## Contributing + +For guidance on making a contribution to MONAI, see the [contributing guidelines](https://github.com/Project-MONAI/MONAI/blob/dev/CONTRIBUTING.md). + +## Community + +Join the conversation on Twitter/X [@ProjectMONAI](https://twitter.com/ProjectMONAI), [LinkedIn](https://www.linkedin.com/company/projectmonai), or join our [Slack channel](https://forms.gle/QTxJq3hFictp31UM9). + +Ask and answer questions over on [MONAI's GitHub Discussions tab](https://github.com/Project-MONAI/MONAI/discussions). + +## Links + +- Website: +- API documentation (milestone): +- API documentation (latest dev): +- Code: +- Project tracker: +- Issue tracker: +- Wiki: +- Test status: +- PyPI package: +- conda-forge: +- Weekly previews: +- Docker Hub: diff --git a/MONAI/source/SECURITY.md b/MONAI/source/SECURITY.md new file mode 100644 index 0000000000000000000000000000000000000000..8b4158eb73f7e97c90367782db9121ac9c2151ab --- /dev/null +++ b/MONAI/source/SECURITY.md @@ -0,0 +1,18 @@ +# Security Policy + +## Reporting a Vulnerability +MONAI takes security seriously and appreciate your efforts to responsibly disclose vulnerabilities. If you discover a security issue, please report it as soon as possible. + +To report a security issue: +* please use the GitHub Security Advisories tab to "[Open a draft security advisory](https://github.com/Project-MONAI/MONAI/security/advisories/new)". +* Include a detailed description of the issue, steps to reproduce, potential impact, and any possible mitigations. +* If applicable, please also attach proof-of-concept code or screenshots. +* We aim to acknowledge your report within 72 hours and provide a status update as we investigate. +* Please do not create public issues for security-related reports. + +## Disclosure Policy +* We follow a coordinated disclosure approach. +* We will not publicly disclose vulnerabilities until a fix has been developed and released. +* Credit will be given to researchers who responsibly disclose vulnerabilities, if requested. +## Acknowledgements +We greatly appreciate contributions from the security community and strive to recognize all researchers who help keep MONAI safe. diff --git a/MONAI/source/__init__.py b/MONAI/source/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..04c9c4b4d35aa5f8a3d7a5526ff6bf5f6edc9912 --- /dev/null +++ b/MONAI/source/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +""" +MONAI Project Package Initialization File +""" diff --git a/MONAI/source/docs/.readthedocs.yaml b/MONAI/source/docs/.readthedocs.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d2e2ee74a5b88a6383e30f214e65a319816926f4 --- /dev/null +++ b/MONAI/source/docs/.readthedocs.yaml @@ -0,0 +1,14 @@ +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file for details + +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.9" +sphinx: + configuration: docs/source/conf.py +python: + install: + - requirements: docs/requirements.txt diff --git a/MONAI/source/docs/Makefile b/MONAI/source/docs/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..57b69ce2730c08472d46b2968ce8597bb444fabf --- /dev/null +++ b/MONAI/source/docs/Makefile @@ -0,0 +1,29 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# https://github.com/Project-MONAI/MONAI/issues/4354 +export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION := python + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + PIP_ROOT_USER_ACTION=ignore pip install -r requirements.txt + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +clean: + rm -rf build/ + rm -rf source/_gen + rm -rf source/*_properties.csv diff --git a/MONAI/source/docs/_static/custom.css b/MONAI/source/docs/_static/custom.css new file mode 100644 index 0000000000000000000000000000000000000000..e0a3457ca607b5806297f5651bcc6cf9e8b3117b --- /dev/null +++ b/MONAI/source/docs/_static/custom.css @@ -0,0 +1,4 @@ +@import url('https://fonts.googleapis.com/css?family=Lekton:700|Roboto&display=swap'); +body{font-family:'Roboto',sans-serif;}.wy-menu-vertical p.caption{color:#7cccc7;} +*{font-variant-ligatures: none;}.autoclasstoc td {padding:0.2rem;line-height:normal;} +dl.field-list>dt{word-break: normal} diff --git a/MONAI/source/docs/images/3d_paired.png b/MONAI/source/docs/images/3d_paired.png new file mode 100644 index 0000000000000000000000000000000000000000..811ba40c854e9e8e6cb01b6c3fc832167080188d Binary files /dev/null and b/MONAI/source/docs/images/3d_paired.png differ diff --git a/MONAI/source/docs/images/BTCV_organs.png b/MONAI/source/docs/images/BTCV_organs.png new file mode 100644 index 0000000000000000000000000000000000000000..4665660812a95896c057b4132c561b0e8333c44d --- /dev/null +++ b/MONAI/source/docs/images/BTCV_organs.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6edb3358d9ac570c2495ebffb0069e79535f92f00f3592ef23e86268e97afd45 +size 725804 diff --git a/MONAI/source/docs/images/MONAI-logo-color.png b/MONAI/source/docs/images/MONAI-logo-color.png new file mode 100644 index 0000000000000000000000000000000000000000..d1e8b6b7be2c1675cd75aa8d0ad3edd23ede7197 Binary files /dev/null and b/MONAI/source/docs/images/MONAI-logo-color.png differ diff --git a/MONAI/source/docs/images/MONAI_arch.png b/MONAI/source/docs/images/MONAI_arch.png new file mode 100644 index 0000000000000000000000000000000000000000..10aa147a3da0e81e0ecb201f49bf1c1b792a4d47 Binary files /dev/null and b/MONAI/source/docs/images/MONAI_arch.png differ diff --git a/MONAI/source/docs/images/MONAI_bundle_cloud.png b/MONAI/source/docs/images/MONAI_bundle_cloud.png new file mode 100644 index 0000000000000000000000000000000000000000..fba9e8f3a70863ca803424e3fd667f33da932f03 --- /dev/null +++ b/MONAI/source/docs/images/MONAI_bundle_cloud.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fdf5b545aafc80c218bc3bc84698e9ded6a167fff5199227ecc9a97f2a837a92 +size 130939 diff --git a/MONAI/source/docs/images/MONAI_clouds.png b/MONAI/source/docs/images/MONAI_clouds.png new file mode 100644 index 0000000000000000000000000000000000000000..b513f67bffc637861b8cf63260dae7d520032106 Binary files /dev/null and b/MONAI/source/docs/images/MONAI_clouds.png differ diff --git a/MONAI/source/docs/images/MONAI_map_cloud.png b/MONAI/source/docs/images/MONAI_map_cloud.png new file mode 100644 index 0000000000000000000000000000000000000000..08765466d78269bf9a9a9d318aaa50cc41bbbe13 --- /dev/null +++ b/MONAI/source/docs/images/MONAI_map_cloud.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:feaefb6801b016082651b5c9ddd1bd8f29792012c5493b8b13e4f80edcb52fcb +size 104574 diff --git a/MONAI/source/docs/images/UNETR.png b/MONAI/source/docs/images/UNETR.png new file mode 100644 index 0000000000000000000000000000000000000000..724aa4bc5315c15585a1201aa3270524d0473e8d --- /dev/null +++ b/MONAI/source/docs/images/UNETR.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a3aa9f4ae4160c9553a5dfdeba1b40d21e8a8c5f6690757ce3e5941fa9ce674 +size 246147 diff --git a/MONAI/source/docs/images/affine.png b/MONAI/source/docs/images/affine.png new file mode 100644 index 0000000000000000000000000000000000000000..46716478b23971b1c0de202b217e10293e289f49 --- /dev/null +++ b/MONAI/source/docs/images/affine.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f4b1541b36faf9fadb91aa50835bdd1c385ba07ca6273c753f2720c6a165a42 +size 258264 diff --git a/MONAI/source/docs/images/amp_training_a100.png b/MONAI/source/docs/images/amp_training_a100.png new file mode 100644 index 0000000000000000000000000000000000000000..37edc8aa47432b9e43ebf84e3ed18d06a09f72aa --- /dev/null +++ b/MONAI/source/docs/images/amp_training_a100.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b3da4a2ce9bb75baade874f953fa4efa9288246a1a0791bf3ea4a27595e888a8 +size 543011 diff --git a/MONAI/source/docs/images/amp_training_v100.png b/MONAI/source/docs/images/amp_training_v100.png new file mode 100644 index 0000000000000000000000000000000000000000..a182b1221c39e83502c75a5342b0631f183aa97b --- /dev/null +++ b/MONAI/source/docs/images/amp_training_v100.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe4bf2dbed97d9429a1cb1bbe452eeabc9ed266dccecad1d828e660f98f94d1c +size 678304 diff --git a/MONAI/source/docs/images/arch_modules.png b/MONAI/source/docs/images/arch_modules.png new file mode 100644 index 0000000000000000000000000000000000000000..b07aee330494b0d16ebd6b9dc50d8ee2429b9685 --- /dev/null +++ b/MONAI/source/docs/images/arch_modules.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42314a4e5dfc3feebe587caeb337ac190cabd317dfd78927b1fa2c140eedaa44 +size 240110 diff --git a/MONAI/source/docs/images/auto3dseg.png b/MONAI/source/docs/images/auto3dseg.png new file mode 100644 index 0000000000000000000000000000000000000000..0a97d80dcb78cd95e6abc4591b2c3238d25bb3e9 --- /dev/null +++ b/MONAI/source/docs/images/auto3dseg.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:899fa0a81474c13701f027135010fb5b42ba8f1284895010ce73b037e40846bf +size 235411 diff --git a/MONAI/source/docs/images/blend.png b/MONAI/source/docs/images/blend.png new file mode 100644 index 0000000000000000000000000000000000000000..a67a49adb4422ccdc6d5873ccf511b85dd19c8e1 Binary files /dev/null and b/MONAI/source/docs/images/blend.png differ diff --git a/MONAI/source/docs/images/blend_images.png b/MONAI/source/docs/images/blend_images.png new file mode 100644 index 0000000000000000000000000000000000000000..d387a7f08fe58248b4fe580a78eeb0cca676372b Binary files /dev/null and b/MONAI/source/docs/images/blend_images.png differ diff --git a/MONAI/source/docs/images/brats_distributed.png b/MONAI/source/docs/images/brats_distributed.png new file mode 100644 index 0000000000000000000000000000000000000000..378c56e667e9178ef972ef8deae467decb9eb211 --- /dev/null +++ b/MONAI/source/docs/images/brats_distributed.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:abb44e898239d33a9b79580d9d8b4266817a5f5089b6ae928ab4a303ffff8502 +size 190736 diff --git a/MONAI/source/docs/images/cache_dataset.png b/MONAI/source/docs/images/cache_dataset.png new file mode 100644 index 0000000000000000000000000000000000000000..20eeebd98014d02939aa198c80682fb5de478469 --- /dev/null +++ b/MONAI/source/docs/images/cache_dataset.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e5a859f718cbcb496fd4aad5556423b96ba1bb62a321f6375033e37a9807a3b5 +size 339617 diff --git a/MONAI/source/docs/images/cam.png b/MONAI/source/docs/images/cam.png new file mode 100644 index 0000000000000000000000000000000000000000..ad5018858aac101f971af67b2b972eea4d269905 Binary files /dev/null and b/MONAI/source/docs/images/cam.png differ diff --git a/MONAI/source/docs/images/coplenet.png b/MONAI/source/docs/images/coplenet.png new file mode 100644 index 0000000000000000000000000000000000000000..8e4501f7f73d2874c5736c16b1333bddca1ec8c2 --- /dev/null +++ b/MONAI/source/docs/images/coplenet.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf293e2294c6678f1d69a73520645755a50e95c04fe8f90fda9c325cb0d36116 +size 645042 diff --git a/MONAI/source/docs/images/dataset_progress.png b/MONAI/source/docs/images/dataset_progress.png new file mode 100644 index 0000000000000000000000000000000000000000..ad941440a165f9a47d663331e2feabe1568d01c5 Binary files /dev/null and b/MONAI/source/docs/images/dataset_progress.png differ diff --git a/MONAI/source/docs/images/datasets_speed.png b/MONAI/source/docs/images/datasets_speed.png new file mode 100644 index 0000000000000000000000000000000000000000..2aa35b2597b9c5631b81695fbfb96fd0b678e89a Binary files /dev/null and b/MONAI/source/docs/images/datasets_speed.png differ diff --git a/MONAI/source/docs/images/decollate_batch.png b/MONAI/source/docs/images/decollate_batch.png new file mode 100644 index 0000000000000000000000000000000000000000..d353483cb745289103bdd22790b10f9110e4ad39 Binary files /dev/null and b/MONAI/source/docs/images/decollate_batch.png differ diff --git a/MONAI/source/docs/images/deepedit.png b/MONAI/source/docs/images/deepedit.png new file mode 100644 index 0000000000000000000000000000000000000000..5e5438a7b18b4193a9e8b0b77efcbc89882c317a --- /dev/null +++ b/MONAI/source/docs/images/deepedit.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51cf2f3fd467f7292e1b8fbd374d1a33593b4f986e4d2a3cabe31bf2d21e33e2 +size 105363 diff --git a/MONAI/source/docs/images/deepgrow.png b/MONAI/source/docs/images/deepgrow.png new file mode 100644 index 0000000000000000000000000000000000000000..dcef67608a0105baabfe1e018bd975db1aedeec9 Binary files /dev/null and b/MONAI/source/docs/images/deepgrow.png differ diff --git a/MONAI/source/docs/images/deepgrow_scheme.png b/MONAI/source/docs/images/deepgrow_scheme.png new file mode 100644 index 0000000000000000000000000000000000000000..1af5b2a068361e810876a7539ee059c23a767360 --- /dev/null +++ b/MONAI/source/docs/images/deepgrow_scheme.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd52b58c2a9873e4249b3a996b8c2336b941addd7ab9429f3288af97dfc66cd1 +size 120122 diff --git a/MONAI/source/docs/images/detection.png b/MONAI/source/docs/images/detection.png new file mode 100644 index 0000000000000000000000000000000000000000..69396962332839a162661fff69adf98a7987d023 --- /dev/null +++ b/MONAI/source/docs/images/detection.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:abe77f20a3991cf21e2bc8dee6bc94a996d0404077c6f440857324122a6a0097 +size 367426 diff --git a/MONAI/source/docs/images/dints-overview.png b/MONAI/source/docs/images/dints-overview.png new file mode 100644 index 0000000000000000000000000000000000000000..5d3b3f7227689ad462535bf80487def16d9bf05f --- /dev/null +++ b/MONAI/source/docs/images/dints-overview.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:902fd47db9dba1217400eb4124f93523d58909cf7ba375a959325bbfd84354fc +size 195070 diff --git a/MONAI/source/docs/images/exp_mgmt.png b/MONAI/source/docs/images/exp_mgmt.png new file mode 100644 index 0000000000000000000000000000000000000000..f3faf4c09ff96edb92751e773513f2dd5b27028e Binary files /dev/null and b/MONAI/source/docs/images/exp_mgmt.png differ diff --git a/MONAI/source/docs/images/fast_training.png b/MONAI/source/docs/images/fast_training.png new file mode 100644 index 0000000000000000000000000000000000000000..b2ad0023fb7ca310786a7eef6f16f66a54e04acb --- /dev/null +++ b/MONAI/source/docs/images/fast_training.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:00b1860c8a23286c4d2e205ea6d617600e099ca3bde354bea28a7656d93d01f6 +size 257895 diff --git a/MONAI/source/docs/images/favicon.ico b/MONAI/source/docs/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..dd19d06d64b9677190d8b04e6f6a7020961bc502 Binary files /dev/null and b/MONAI/source/docs/images/favicon.ico differ diff --git a/MONAI/source/docs/images/federated.svg b/MONAI/source/docs/images/federated.svg new file mode 100644 index 0000000000000000000000000000000000000000..1c59878877087866aa0bdc68062ca111cb8d9049 --- /dev/null +++ b/MONAI/source/docs/images/federated.svg @@ -0,0 +1,245 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MONAI/source/docs/images/gmm_feature_set_comparison_s.png b/MONAI/source/docs/images/gmm_feature_set_comparison_s.png new file mode 100644 index 0000000000000000000000000000000000000000..bde9339fb0a47353fe18d07c5f4f632b293287a1 --- /dev/null +++ b/MONAI/source/docs/images/gmm_feature_set_comparison_s.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8e25ef5d1e09e3fbe49ee2249b2482f5c0114d15c301ba9ef488d1f1ed47bcb +size 251480 diff --git a/MONAI/source/docs/images/hovernet_diagram.png b/MONAI/source/docs/images/hovernet_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..aa7adcbdcfe0666c033715e10ae1dbcdedf493a4 Binary files /dev/null and b/MONAI/source/docs/images/hovernet_diagram.png differ diff --git a/MONAI/source/docs/images/invert_transforms.png b/MONAI/source/docs/images/invert_transforms.png new file mode 100644 index 0000000000000000000000000000000000000000..802110085f7bfddede8d6dec133e24af79930688 --- /dev/null +++ b/MONAI/source/docs/images/invert_transforms.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:151f58c3756a2a9a501528896ca7e8e21384d6c34fe14ffcbe22ba4a27ac5afa +size 285097 diff --git a/MONAI/source/docs/images/lazy_resampling_apply_pending_example.svg b/MONAI/source/docs/images/lazy_resampling_apply_pending_example.svg new file mode 100644 index 0000000000000000000000000000000000000000..2e23a10e82d2835bc94ba751bffedb35b1bbf9a0 --- /dev/null +++ b/MONAI/source/docs/images/lazy_resampling_apply_pending_example.svg @@ -0,0 +1 @@ + diff --git a/MONAI/source/docs/images/lazy_resampling_homogeneous_matrices.svg b/MONAI/source/docs/images/lazy_resampling_homogeneous_matrices.svg new file mode 100644 index 0000000000000000000000000000000000000000..2a20b120dac8bb300288fafc0017c023f16a1064 --- /dev/null +++ b/MONAI/source/docs/images/lazy_resampling_homogeneous_matrices.svg @@ -0,0 +1 @@ + diff --git a/MONAI/source/docs/images/lazy_resampling_lazy_example_1.svg b/MONAI/source/docs/images/lazy_resampling_lazy_example_1.svg new file mode 100644 index 0000000000000000000000000000000000000000..655463580962984f504c3910a8afb30d99004ede --- /dev/null +++ b/MONAI/source/docs/images/lazy_resampling_lazy_example_1.svg @@ -0,0 +1 @@ + diff --git a/MONAI/source/docs/images/lazy_resampling_none_example.svg b/MONAI/source/docs/images/lazy_resampling_none_example.svg new file mode 100644 index 0000000000000000000000000000000000000000..ca83fa5449f2b7cde650ce21f295f5d3354cc826 --- /dev/null +++ b/MONAI/source/docs/images/lazy_resampling_none_example.svg @@ -0,0 +1 @@ + diff --git a/MONAI/source/docs/images/lazy_resampling_trad_example_1.svg b/MONAI/source/docs/images/lazy_resampling_trad_example_1.svg new file mode 100644 index 0000000000000000000000000000000000000000..5d29bb08f27095e696e9ddbfb21d382515b14129 --- /dev/null +++ b/MONAI/source/docs/images/lazy_resampling_trad_example_1.svg @@ -0,0 +1 @@ + diff --git a/MONAI/source/docs/images/lr_finder.png b/MONAI/source/docs/images/lr_finder.png new file mode 100644 index 0000000000000000000000000000000000000000..3fd72b233c57c025bafdb60d5eb1ccec869f6c42 Binary files /dev/null and b/MONAI/source/docs/images/lr_finder.png differ diff --git a/MONAI/source/docs/images/maisi_infer.png b/MONAI/source/docs/images/maisi_infer.png new file mode 100644 index 0000000000000000000000000000000000000000..39f5ea973e9ede447202aff805d11d936268ca66 --- /dev/null +++ b/MONAI/source/docs/images/maisi_infer.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96486c5d2f006047b0d085bfacb355c7055b2d93c7a57893a4c778ed0eb95aa4 +size 130987 diff --git a/MONAI/source/docs/images/maisi_train.png b/MONAI/source/docs/images/maisi_train.png new file mode 100644 index 0000000000000000000000000000000000000000..47437fb35d920d5f2ee7a97c6d085255905de2a7 --- /dev/null +++ b/MONAI/source/docs/images/maisi_train.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6769bc69e8bbc8cc32e07d4c8937c8f78082b3636eb15e181dddc7ff6ce70964 +size 187025 diff --git a/MONAI/source/docs/images/matshow3d.png b/MONAI/source/docs/images/matshow3d.png new file mode 100644 index 0000000000000000000000000000000000000000..7ecb180b8e8d2ce40d6eb78c7fcd1bbb24e06b54 --- /dev/null +++ b/MONAI/source/docs/images/matshow3d.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d998a2807c4241b946e01f0b2c58830eaa59e3761a5c3f3f6946f6f6b997b19 +size 117523 diff --git a/MONAI/source/docs/images/medical_transforms.png b/MONAI/source/docs/images/medical_transforms.png new file mode 100644 index 0000000000000000000000000000000000000000..7a2f0f5f54fa4235b85cd3ec078c602a6ac5bebb --- /dev/null +++ b/MONAI/source/docs/images/medical_transforms.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8753100ee8b3e81691eb8714fe49fb04e0f36a24c87430f6616dfd901ff0c546 +size 531712 diff --git a/MONAI/source/docs/images/metrics_report.png b/MONAI/source/docs/images/metrics_report.png new file mode 100644 index 0000000000000000000000000000000000000000..941c791c555dd3aa6d0a9e5ebcb7fdef527a1f7d --- /dev/null +++ b/MONAI/source/docs/images/metrics_report.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b14fa1e77d6fa280ac5f98d8de0662baf7c495a72a74aebc57e58c843101568c +size 107076 diff --git a/MONAI/source/docs/images/mil-patches.jpg b/MONAI/source/docs/images/mil-patches.jpg new file mode 100644 index 0000000000000000000000000000000000000000..668ba31567ff201fcc2cc627dc4e1df9abfd9e1a Binary files /dev/null and b/MONAI/source/docs/images/mil-patches.jpg differ diff --git a/MONAI/source/docs/images/models_ensemble.png b/MONAI/source/docs/images/models_ensemble.png new file mode 100644 index 0000000000000000000000000000000000000000..64e69b0fceb8a666a27a48a7cb22631ad3a94f81 Binary files /dev/null and b/MONAI/source/docs/images/models_ensemble.png differ diff --git a/MONAI/source/docs/images/mri_recon.png b/MONAI/source/docs/images/mri_recon.png new file mode 100644 index 0000000000000000000000000000000000000000..bbf775aa0c096e49089d9eea7ffee5c1c8ab3f87 Binary files /dev/null and b/MONAI/source/docs/images/mri_recon.png differ diff --git a/MONAI/source/docs/images/nsight_comparison.png b/MONAI/source/docs/images/nsight_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..8278fecf1687a443bbf7cb5c852e577539d7f8f7 --- /dev/null +++ b/MONAI/source/docs/images/nsight_comparison.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a01b7c50922399d2c42bce7e2db7c35cfe679de18e78a6d8262ca5c825624403 +size 147592 diff --git a/MONAI/source/docs/images/nuclick.png b/MONAI/source/docs/images/nuclick.png new file mode 100644 index 0000000000000000000000000000000000000000..a864612c43ac269f3d68b9d69025af1517d8bae8 --- /dev/null +++ b/MONAI/source/docs/images/nuclick.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34faf24e8c445a793885c0ed6b0fd92e462bb94d7551c21d99206d01f6dccaff +size 326274 diff --git a/MONAI/source/docs/images/pathology-meta.png b/MONAI/source/docs/images/pathology-meta.png new file mode 100644 index 0000000000000000000000000000000000000000..428d872efc2bb783cf1059fe4946c5b6dc0faa71 --- /dev/null +++ b/MONAI/source/docs/images/pathology-meta.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be50a9f4fe051888ac6df1e33fecfd1c8813c9c88a0aa032084b26c768ddf94d +size 471608 diff --git a/MONAI/source/docs/images/pathology.png b/MONAI/source/docs/images/pathology.png new file mode 100644 index 0000000000000000000000000000000000000000..08338db19a34cfebaf518de72193870905ca7479 --- /dev/null +++ b/MONAI/source/docs/images/pathology.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:20b7882f7cd3be3bb40f00613208dead736adeab0995a905fdb403652fc0973b +size 291620 diff --git a/MONAI/source/docs/images/postprocessing_transforms.png b/MONAI/source/docs/images/postprocessing_transforms.png new file mode 100644 index 0000000000000000000000000000000000000000..aabc039e37d35f80ddf99b5eb2f7a0ece95c3325 --- /dev/null +++ b/MONAI/source/docs/images/postprocessing_transforms.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf3fe0761c8dec3edd0a3ba4514a94c9132ec6a97fef5d6ec56044ecba0d9f56 +size 318071 diff --git a/MONAI/source/docs/images/precision_options.png b/MONAI/source/docs/images/precision_options.png new file mode 100644 index 0000000000000000000000000000000000000000..269560d80f0b0f1cb9769088fcbf8b030144b63c Binary files /dev/null and b/MONAI/source/docs/images/precision_options.png differ diff --git a/MONAI/source/docs/images/python.svg b/MONAI/source/docs/images/python.svg new file mode 100644 index 0000000000000000000000000000000000000000..8ef6b61c03574815c3dbff881142b5b9e5565c1c --- /dev/null +++ b/MONAI/source/docs/images/python.svg @@ -0,0 +1 @@ +pythonpython3.9+3.9+ diff --git a/MONAI/source/docs/images/sliding_window.png b/MONAI/source/docs/images/sliding_window.png new file mode 100644 index 0000000000000000000000000000000000000000..9984c6fd45e017fd3eed776b860ccb70ad54ca0c --- /dev/null +++ b/MONAI/source/docs/images/sliding_window.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbbd54783492c716095b5eee6d45870886e886e527c0b1b388dc432de1112f99 +size 547950 diff --git a/MONAI/source/docs/images/ssl_overview.png b/MONAI/source/docs/images/ssl_overview.png new file mode 100644 index 0000000000000000000000000000000000000000..9764d7fc750ce60635222497fe4f14a6dc0b9991 --- /dev/null +++ b/MONAI/source/docs/images/ssl_overview.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9ab553b1a0bb86f277e87e186a15b8fa77980ec840a11daf520b35e36c7c4a50 +size 194085 diff --git a/MONAI/source/docs/images/swin_unetr.png b/MONAI/source/docs/images/swin_unetr.png new file mode 100644 index 0000000000000000000000000000000000000000..591fff61e34f52ac12cd86079aff83eb58acb23a --- /dev/null +++ b/MONAI/source/docs/images/swin_unetr.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:38d9562949b247cf6d79b9b02810cf2e9d1e81be4e7b929bf1064f48db9a0c07 +size 107439 diff --git a/MONAI/source/docs/images/threaddataloader.png b/MONAI/source/docs/images/threaddataloader.png new file mode 100644 index 0000000000000000000000000000000000000000..03db9cc830385f1caf0c98f7009f69eab38b7240 Binary files /dev/null and b/MONAI/source/docs/images/threaddataloader.png differ diff --git a/MONAI/source/docs/images/transfer_mmar.png b/MONAI/source/docs/images/transfer_mmar.png new file mode 100644 index 0000000000000000000000000000000000000000..e100d01334411041cb1b22eb8acc42cf44ed4501 Binary files /dev/null and b/MONAI/source/docs/images/transfer_mmar.png differ diff --git a/MONAI/source/docs/images/tta.png b/MONAI/source/docs/images/tta.png new file mode 100644 index 0000000000000000000000000000000000000000..e748ec1fed7379dc7cc748533b13ab9becdbf776 --- /dev/null +++ b/MONAI/source/docs/images/tta.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:40d5de0a3ccec37739b69a68a16d47e88b6c69d2410cd76fa78c952f29af011c +size 211645 diff --git a/MONAI/source/docs/images/unet-pipe.png b/MONAI/source/docs/images/unet-pipe.png new file mode 100644 index 0000000000000000000000000000000000000000..759bdbe80998af2f6e673232a92bb1ce542c8efe --- /dev/null +++ b/MONAI/source/docs/images/unet-pipe.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c65af5daebc509617a50b0d20b79e82297dd450a14007b5fa34af3d0e9362862 +size 282535 diff --git a/MONAI/source/docs/images/vista2d.png b/MONAI/source/docs/images/vista2d.png new file mode 100644 index 0000000000000000000000000000000000000000..10dd328b93771bc060a08286f5b8b60151b8b919 --- /dev/null +++ b/MONAI/source/docs/images/vista2d.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d753ed74d8ba6b8d1cc2fe2f82656bc1f04deb5540867d197e11c165283dd62a +size 455243 diff --git a/MONAI/source/docs/images/vista3d.png b/MONAI/source/docs/images/vista3d.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a94fbecdb30b74e667938283348cfc58f4495d Binary files /dev/null and b/MONAI/source/docs/images/vista3d.png differ diff --git a/MONAI/source/docs/images/workflows.png b/MONAI/source/docs/images/workflows.png new file mode 100644 index 0000000000000000000000000000000000000000..2e370400067dc0a94abba855ab199974307ce040 --- /dev/null +++ b/MONAI/source/docs/images/workflows.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:57e67139b92287e1ff59a4501d787346796c5846687eaa8c6f9d6932e551d0d6 +size 107592 diff --git a/MONAI/source/docs/requirements.txt b/MONAI/source/docs/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..f44f41a6b22f3e527712dd74324c05b6ee3b9e64 --- /dev/null +++ b/MONAI/source/docs/requirements.txt @@ -0,0 +1,45 @@ +-f https://download.pytorch.org/whl/cpu/torch-2.4.1%2Bcpu-cp39-cp39-linux_x86_64.whl +torch>=2.4.1 +pytorch-ignite==0.4.11 +numpy>=1.20 +itk>=5.2 +nibabel +parameterized +scikit-image>=0.19.0 +scipy>=1.12.0; python_version >= '3.9' +tensorboard +commonmark==0.9.1 +recommonmark==0.6.0 +Sphinx +pydata-sphinx-theme +sphinxcontrib-applehelp +sphinxcontrib-devhelp +sphinxcontrib-htmlhelp +sphinxcontrib-jsmath +sphinxcontrib-qthelp +sphinxcontrib-serializinghtml +sphinx-autodoc-typehints==1.11.1 +pandas +einops +transformers>=4.53.0 +mlflow>=2.12.2 +clearml>=1.10.0rc0 +tensorboardX +imagecodecs; platform_system == "Linux" or platform_system == "Darwin" +tifffile; platform_system == "Linux" or platform_system == "Darwin" +pyyaml +fire +jsonschema +pynrrd +pydicom +h5py +nni; platform_system == "Linux" +optuna +opencv-python-headless +onnx>=1.13.0 +onnxruntime; python_version <= '3.10' +zarr +huggingface_hub +pyamg>=5.0.0, <5.3.0 +packaging +polygraphy diff --git a/MONAI/source/docs/source/MONAI-logo-color.png b/MONAI/source/docs/source/MONAI-logo-color.png new file mode 100644 index 0000000000000000000000000000000000000000..d1e8b6b7be2c1675cd75aa8d0ad3edd23ede7197 Binary files /dev/null and b/MONAI/source/docs/source/MONAI-logo-color.png differ diff --git a/MONAI/source/docs/source/api.rst b/MONAI/source/docs/source/api.rst new file mode 100644 index 0000000000000000000000000000000000000000..e16a19f488287c3fb3bcd56b234d895b213389dd --- /dev/null +++ b/MONAI/source/docs/source/api.rst @@ -0,0 +1,23 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +API Reference +============= + +.. toctree:: + :maxdepth: 1 + + apps + auto3dseg + fl + bundle + transforms + losses + networks + metrics + optimizers + data + engines + inferers + handlers + visualize + utils diff --git a/MONAI/source/docs/source/apidocs/modules.rst b/MONAI/source/docs/source/apidocs/modules.rst new file mode 100644 index 0000000000000000000000000000000000000000..b8373368ecf4e9a373ec269284a8793737e6fc26 --- /dev/null +++ b/MONAI/source/docs/source/apidocs/modules.rst @@ -0,0 +1,9 @@ +:orphan: + +monai +===== + +.. toctree:: + :maxdepth: 4 + + monai diff --git a/MONAI/source/docs/source/apidocs/monai.rst b/MONAI/source/docs/source/apidocs/monai.rst new file mode 100644 index 0000000000000000000000000000000000000000..de36f29e4a147df40d5f44e6aa6e9a248fbbaff4 --- /dev/null +++ b/MONAI/source/docs/source/apidocs/monai.rst @@ -0,0 +1,10 @@ +monai package +============= + +Module contents +--------------- + +.. automodule:: monai + :members: + :undoc-members: + :show-inheritance: diff --git a/MONAI/source/docs/source/applications.md b/MONAI/source/docs/source/applications.md new file mode 100644 index 0000000000000000000000000000000000000000..44fb9bbf147f1dc01c43e6d5db07c7a5d81b947c --- /dev/null +++ b/MONAI/source/docs/source/applications.md @@ -0,0 +1,79 @@ +# Research and Application Highlights + +### COPLE-Net for COVID-19 Pneumonia Lesion Segmentation +[A reimplementation](https://project-monai.github.io/research/coplenet-pneumonia-lesion-segmentation.html) of the COPLE-Net originally proposed by: + +G. Wang, X. Liu, C. Li, Z. Xu, J. Ruan, H. Zhu, T. Meng, K. Li, N. Huang, S. Zhang. (2020) "A Noise-robust Framework for Automatic Segmentation of COVID-19 Pneumonia Lesions from CT Images." IEEE Transactions on Medical Imaging. 2020. [DOI: 10.1109/TMI.2020.3000314](https://doi.org/10.1109/TMI.2020.3000314) +![coplenet](../images/coplenet.png) + +### LAMP: Large Deep Nets with Automated Model Parallelism for Image Segmentation +[A reimplementation](https://project-monai.github.io/research/lamp-automated-model-parallelism.html) of the LAMP system originally proposed by: + +Wentao Zhu, Can Zhao, Wenqi Li, Holger Roth, Ziyue Xu, and Daguang Xu (2020) "LAMP: Large Deep Nets with Automated Model Parallelism for Image Segmentation." MICCAI 2020 (Early Accept, paper link: https://arxiv.org/abs/2006.12575) + +![LAMP UNet](../images/unet-pipe.png) + +### DiNTS: Differentiable Neural Network Topology Search for 3D Medical Image Segmentation +MONAI integrated the `DiNTS` module to support more flexible topologies and joint two-level search. It provides a topology guaranteed discretization algorithm and a discretization aware topology loss for the search stage to minimize the discretization gap, and a cost usage aware search method which can search 3D networks with different GPU memory requirements. For more details, please check the [DiNTS tutorial](https://project-monai.github.io/research/dints.html). + +![DiNTS](../images/dints-overview.png) + +### Accounting for Dependencies in Deep Learning Based Multiple Instance Learning for Whole Slide Imaging +For [classification of digital pathology whole slide images (WSI)](https://arxiv.org/abs/2111.01556), MONAI introduces new transforms and network modules for multiple instance learning. These include self-attention transformer blocks for explicitly accounting of the dependencies between instances (image patches) during training. For more details, please check out the [multiple instance learning tutorial](https://github.com/Project-MONAI/tutorials/tree/master/pathology/multiple_instance_learning). ![multi-instance](../images/mil-patches.jpg) + +### Self-supervised representation learning +MONAI starts to explore self-supervised representation learning in this milestone release. The Vision Transformer has been extended to learn from self-supervised reconstruction tasks with various data augmentation and a regularized contrastive loss. The weights of the pre-trained backbone could be used to enhance the performance of the novel downstream deep learning tasks. + +The [tutorial](https://github.com/Project-MONAI/tutorials/tree/master/self_supervised_pretraining) shows how to generate a good set of pre-trained weights using unlabeled data with self-supervised tasks, then use the pre-trained weights to perform fine-tuning on a fully supervised volumetric segmentation task using a transformer based `UNETR`. + +![self-supervised](../images/ssl_overview.png) + +### Swin UNETR model for the task of multi-organ segmentation +For [Swin UNETR: Swin Transformers for Semantic Segmentation of Brain Tumors in MRI Images](https://arxiv.org/abs/2201.01266), MONAI introduces new network modules for multi-organ segmentation task using the BTCV challenge dataset. The architecture of Swin UNETR: + +![swin-unetr](../images/swin_unetr.png) + +The [tutorial](https://github.com/Project-MONAI/tutorials/blob/main/3d_segmentation/swin_unetr_btcv_segmentation_3d.ipynb) shows a typical pipeline of multi-organ segmentation based on Swin UNETR model, DiceCE loss function, Mean Dice, etc. And we used weights from self-supervised pre-training of Swin UNETR encoder (3D Swin Transformer) on a cohort of 5050 CT scans from publicly available datasets. + +### DeepGrow modules for interactive segmentation +[A reimplementation](https://github.com/Project-MONAI/MONAI/tree/master/monai/apps/deepgrow) of the DeepGrow components, which is deep learning based semi-automated segmentation approach that aims to be a "smart" interactive tool for region of interest delineation in medical images, originally proposed by: + +Sakinis, Tomas, et al. "Interactive segmentation of medical images through fully convolutional neural networks." arXiv preprint arXiv:1903.08205 (2019). + +![deepgrow scheme](../images/deepgrow.png) + +### DeepEdit workflow for interactive segmentation +DeepEdit is a method that combines an automatic and a semi-automatic approach for 3D medical images into a single deep learning-based model. The [implementation](https://github.com/Project-MONAI/MONAI/tree/dev/monai/apps/deepedit) of the DeepEdit modules provides essential components for interactive segmentation. More details are available in the training and inference [tutorial](https://github.com/Project-MONAI/tutorials/tree/main/deepedit/ignite). + +The following figure shows the typical workflow of interactive segmentation: + +![deepedit workflow](../images/deepedit.png) + +### NuClick modules for interactive nuclei segmentation +NuClick is a CNN-based approach to speed up collecting annotations for microscopic objects requiring minimum interaction from the annotator. The [implementation](https://github.com/Project-MONAI/MONAI/tree/dev/monai/apps/nuclick) contains essential components for the training and inference workflows of NuClick interactive nuclei segmentation. + +The following figure is example outputs of NuClick (annotator click inside the nucleus and the mask will be generated by CNN): + +![nuclick output](../images/nuclick.png) + +### Lesion detection in digital pathology +[Implementation](https://github.com/Project-MONAI/MONAI/tree/master/monai/apps/pathology) of the pathology detection components, which includes efficient whole slide imaging IO and several patch sampling methods with NVIDIA cuCIM library and SmartCache mechanism, FROC measurements for lesion and probabilistic post-processing for lesion detection. + +![digital pathology](../images/pathology.png) + +### Learning-based image registration +Starting from v0.5.0, MONAI provides experimental features for building learning-based 2D/3D registration workflows. These include image similarity measures as loss functions, bending energy as model regularization, network architectures, warping modules. The components can be used to build the major unsupervised and weakly-supervised algorithms. + +The following figure shows the registration of CT images acquired at different time points for a single patient using MONAI: + +![3d registration](../images/3d_paired.png) + +### 2D and 3D detection workflow +The [implementation](https://github.com/Project-MONAI/MONAI/tree/dev/monai/apps/detection) contains 2D and 3D bounding box detection components of `RetinaNet`, which includes:bounding box operations, hard negative sampler, and RetinaNet detectors. + +The following figure shows the detection training and inference workflows: + +![detection workflow](../images/detection.png) + +### Reproducing the state-of-the-art Kaggle competition solutions +[A reimplementation](https://github.com/Project-MONAI/tutorials/tree/main/competitions/kaggle/RANZCR/4th_place_solution) of the 4th place solution of RANZCR CLiP - Catheter and Line Position Challenge in Kaggle: https://www.kaggle.com/c/ranzcr-clip-catheter-line-classification diff --git a/MONAI/source/docs/source/apps.rst b/MONAI/source/docs/source/apps.rst new file mode 100644 index 0000000000000000000000000000000000000000..3239dc535195de85a4e9efe1f809854d8bebd7c5 --- /dev/null +++ b/MONAI/source/docs/source/apps.rst @@ -0,0 +1,291 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. _apps: + +Applications +============ +.. currentmodule:: monai.apps + +`Datasets` +---------- + +.. autoclass:: MedNISTDataset + :members: + +.. autoclass:: DecathlonDataset + :members: + +.. autoclass:: TciaDataset + :members: + +.. autoclass:: CrossValidation + :members: + + +`Clara MMARs` +------------- +.. autofunction:: download_mmar + +.. autofunction:: load_from_mmar + +.. autodata:: monai.apps.MODEL_DESC + :annotation: + + +`Utilities` +----------- + +.. autofunction:: check_hash + +.. autofunction:: download_url + +.. autofunction:: extractall + +.. autofunction:: download_and_extract + +`Deepgrow` +---------- + +.. automodule:: monai.apps.deepgrow.dataset +.. autofunction:: create_dataset + +.. automodule:: monai.apps.deepgrow.interaction +.. autoclass:: Interaction + :members: + +.. automodule:: monai.apps.deepgrow.transforms +.. autoclass:: AddInitialSeedPointd + :members: +.. autoclass:: AddGuidanceSignald + :members: +.. autoclass:: AddRandomGuidanced + :members: +.. autoclass:: AddGuidanceFromPointsd + :members: +.. autoclass:: SpatialCropForegroundd + :members: +.. autoclass:: SpatialCropGuidanced + :members: +.. autoclass:: RestoreLabeld + :members: +.. autoclass:: ResizeGuidanced + :members: +.. autoclass:: FindDiscrepancyRegionsd + :members: +.. autoclass:: FindAllValidSlicesd + :members: +.. autoclass:: Fetch2DSliced + :members: + +`Pathology` +----------- + +.. automodule:: monai.apps.pathology.inferers +.. autoclass:: SlidingWindowHoVerNetInferer + :members: + +.. automodule:: monai.apps.pathology.losses.hovernet_loss +.. autoclass:: HoVerNetLoss + :members: + +.. automodule:: monai.apps.pathology.metrics +.. autoclass:: LesionFROC + :members: + +.. automodule:: monai.apps.pathology.utils +.. autofunction:: compute_multi_instance_mask +.. autofunction:: compute_isolated_tumor_cells +.. autoclass:: PathologyProbNMS + :members: + +.. automodule:: monai.apps.pathology.transforms.stain.array +.. autoclass:: ExtractHEStains + :members: +.. autoclass:: NormalizeHEStains + :members: + +.. automodule:: monai.apps.pathology.transforms.stain.dictionary +.. autoclass:: ExtractHEStainsd + :members: +.. autoclass:: NormalizeHEStainsd + :members: + +.. automodule:: monai.apps.pathology.transforms.post.array +.. autoclass:: GenerateSuccinctContour + :members: +.. autoclass:: GenerateInstanceContour + :members: +.. autoclass:: GenerateInstanceCentroid + :members: +.. autoclass:: GenerateInstanceType + :members: +.. autoclass:: Watershed + :members: +.. autoclass:: GenerateWatershedMask + :members: +.. autoclass:: GenerateInstanceBorder + :members: +.. autoclass:: GenerateDistanceMap + :members: +.. autoclass:: GenerateWatershedMarkers + :members: +.. autoclass:: HoVerNetNuclearTypePostProcessing + :members: +.. autoclass:: HoVerNetInstanceMapPostProcessing + :members: + +.. automodule:: monai.apps.pathology.transforms.post.dictionary +.. autoclass:: GenerateSuccinctContourd + :members: +.. autoclass:: GenerateInstanceContourd + :members: +.. autoclass:: GenerateInstanceCentroidd + :members: +.. autoclass:: GenerateInstanceTyped + :members: +.. autoclass:: Watershedd + :members: +.. autoclass:: GenerateWatershedMaskd + :members: +.. autoclass:: GenerateInstanceBorderd + :members: +.. autoclass:: GenerateDistanceMapd + :members: +.. autoclass:: GenerateWatershedMarkersd + :members: +.. autoclass:: HoVerNetInstanceMapPostProcessingd + :members: +.. autoclass:: HoVerNetNuclearTypePostProcessingd + :members: + +`Detection` +----------- + +`Hard Negative Sampler` +~~~~~~~~~~~~~~~~~~~~~~~ +.. automodule:: monai.apps.detection.utils.hard_negative_sampler + :members: + +`RetinaNet Network` +~~~~~~~~~~~~~~~~~~~ +.. automodule:: monai.apps.detection.networks.retinanet_network + :members: + +`RetinaNet Detector` +~~~~~~~~~~~~~~~~~~~~ +.. automodule:: monai.apps.detection.networks.retinanet_detector + :members: + +`Transforms` +~~~~~~~~~~~~ +.. automodule:: monai.apps.detection.transforms.box_ops + :members: +.. automodule:: monai.apps.detection.transforms.array + :members: +.. automodule:: monai.apps.detection.transforms.dictionary + :members: + +`Anchor` +~~~~~~~~ +.. automodule:: monai.apps.detection.utils.anchor_utils + :members: + +`Matcher` +~~~~~~~~~ +.. automodule:: monai.apps.detection.utils.ATSS_matcher + :members: + +`Box coder` +~~~~~~~~~~~ +.. automodule:: monai.apps.detection.utils.box_coder + :members: + +`Detection Utilities` +~~~~~~~~~~~~~~~~~~~~~ +.. automodule:: monai.apps.detection.utils.detector_utils + :members: + +.. automodule:: monai.apps.detection.utils.predict_utils + :members: + +`Inference box selector` +~~~~~~~~~~~~~~~~~~~~~~~~ +.. automodule:: monai.apps.detection.utils.box_selector + :members: + +`Detection metrics` +~~~~~~~~~~~~~~~~~~~ +.. automodule:: monai.apps.detection.metrics.coco + :members: +.. automodule:: monai.apps.detection.metrics.matching + :members: + +`Reconstruction` +---------------- + +FastMRIReader +~~~~~~~~~~~~~ +.. autoclass:: monai.apps.reconstruction.fastmri_reader.FastMRIReader + :members: + +`ConvertToTensorComplex` +~~~~~~~~~~~~~~~~~~~~~~~~ +.. autofunction:: monai.apps.reconstruction.complex_utils.convert_to_tensor_complex + +`ComplexAbs` +~~~~~~~~~~~~ +.. autofunction:: monai.apps.reconstruction.complex_utils.complex_abs + +`RootSumOfSquares` +~~~~~~~~~~~~~~~~~~ +.. autofunction:: monai.apps.reconstruction.mri_utils.root_sum_of_squares + +`ComplexMul` +~~~~~~~~~~~~ +.. autofunction:: monai.apps.reconstruction.complex_utils.complex_mul + +`ComplexConj` +~~~~~~~~~~~~~ +.. autofunction:: monai.apps.reconstruction.complex_utils.complex_conj + +`Vista3d` +--------- +.. automodule:: monai.apps.vista3d.inferer +.. autofunction:: point_based_window_inferer + +.. automodule:: monai.apps.vista3d.transforms +.. autoclass:: VistaPreTransformd + :members: +.. autoclass:: VistaPostTransformd + :members: +.. autoclass:: Relabeld + :members: + +.. automodule:: monai.apps.vista3d.sampler +.. autofunction:: sample_prompt_pairs + +`Auto3DSeg` +----------- +.. automodule:: monai.apps.auto3dseg + :members: + :special-members: __call__ + :imported-members: + +`nnUNet` +-------- +.. automodule:: monai.apps.nnunet.__main__ + +.. autoclass:: monai.apps.nnunet.nnUNetV2Runner + :members: + +`nnUNet Bundle` +--------------- +.. autoclass:: monai.apps.nnunet.ModelnnUNetWrapper + :members: + :special-members: + +.. autofunction:: monai.apps.nnunet.get_nnunet_trainer +.. autofunction:: monai.apps.nnunet.get_nnunet_monai_predictor +.. autofunction:: monai.apps.nnunet.convert_nnunet_to_monai_bundle +.. autofunction:: monai.apps.nnunet.convert_monai_bundle_to_nnunet +.. autofunction:: monai.apps.nnunet.get_network_from_nnunet_plans diff --git a/MONAI/source/docs/source/auto3dseg.rst b/MONAI/source/docs/source/auto3dseg.rst new file mode 100644 index 0000000000000000000000000000000000000000..4130880caecedcdc8a49b944b6463079930ba2dc --- /dev/null +++ b/MONAI/source/docs/source/auto3dseg.rst @@ -0,0 +1,10 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. _auto3dseg: + +Auto3dseg +========= + +.. automodule:: monai.auto3dseg + :members: + :imported-members: diff --git a/MONAI/source/docs/source/bundle.rst b/MONAI/source/docs/source/bundle.rst new file mode 100644 index 0000000000000000000000000000000000000000..fdf745e951313f338843c68aa5299d7197cd2111 --- /dev/null +++ b/MONAI/source/docs/source/bundle.rst @@ -0,0 +1,53 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. _bundle: + +Model Bundle +============ +.. currentmodule:: monai.bundle + +`Config Item` +------------- +.. autoclass:: Instantiable + :members: + +.. autoclass:: ComponentLocator + :members: + +.. autoclass:: ConfigComponent + :members: + +.. autoclass:: ConfigExpression + :members: + +.. autoclass:: ConfigItem + :members: + +`Reference Resolver` +-------------------- +.. autoclass:: ReferenceResolver + :members: + +`Config Parser` +--------------- +.. autoclass:: ConfigParser + :members: + :special-members: + + +`Scripts` +--------- +.. autofunction:: ckpt_export +.. autofunction:: trt_export +.. autofunction:: onnx_export +.. autofunction:: download +.. autofunction:: load +.. autofunction:: get_all_bundles_list +.. autofunction:: get_bundle_info +.. autofunction:: get_bundle_versions +.. autofunction:: run +.. autofunction:: verify_metadata +.. autofunction:: verify_net_in_out +.. autofunction:: init_bundle +.. autofunction:: push_to_hf_hub +.. autofunction:: update_kwargs diff --git a/MONAI/source/docs/source/bundle_intro.rst b/MONAI/source/docs/source/bundle_intro.rst new file mode 100644 index 0000000000000000000000000000000000000000..45074126101ec63f527ecfd98cd9262b4f61278d --- /dev/null +++ b/MONAI/source/docs/source/bundle_intro.rst @@ -0,0 +1,43 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +Bundle +====== + +MONAI Bundles are a specification and file structure based way of distributing trained MONAI models with associated +metadata, code, documentation, and other resources. These are meant to make it easier for you to distribute your model +in a format that explains what the model is for, how to use it, how to reproduce the science you've done with it, and +use it in other applications such as Label and Deploy. + +.. toctree:: + :maxdepth: 1 + + mb_specification + config_syntax.md + mb_properties + +Detailed bundle examples and get started tutorial: https://github.com/Project-MONAI/tutorials/tree/main/bundle + +A collection of medical imaging models in the MONAI Bundle format: https://github.com/Project-MONAI/model-zoo + + +Bundle vs. MAPs +--------------- + +Bundles differ from MONAI Application Packages (MAPs) in that they focus on description, code definition, application, +usage. MAPs focus on deployment, containerisation, integration into existing clinical systems, and other application +areas relating to putting models into use. + +.. image:: ../images/MONAI_clouds.png + :alt: Bundle and MAP Concepts + :align: center + +As a user, bundles are networks and "programs" you would use directly for training, inference, reproducing results, +and other tasks. Bundles can be integrated into MONAI Label apps to perform segmentation tasks through user interfaces, +or into MAPs for deployment. They can be integrated into other container environments but this isn't their focus. A +bundle in general is a more lightweight concept with less infrastructure + +For all applications relating to containerisation, portability, and deployment, MAPs are what you're looking for. A MAP +is the contained environment for running an inference application directly or within an orchestration system. A bundle +alone doesn't have the structure suitable for this use, a MAP must be provided which uses a bundle as the inference object. +MAPs are also meant for inference only unlike bundles which should include training scripts. DICOM access is emphasised in +MAPs since they are meant for clinical deployment and so must interface with clinical databases. diff --git a/MONAI/source/docs/source/conf.py b/MONAI/source/docs/source/conf.py new file mode 100644 index 0000000000000000000000000000000000000000..a91f38081f1be456470e174b71a08fb40cdc1448 --- /dev/null +++ b/MONAI/source/docs/source/conf.py @@ -0,0 +1,223 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import subprocess +import sys +import importlib +import inspect + +sys.path.insert(0, os.path.abspath("..")) +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))) +print(sys.path) + +import monai # noqa: E402 + +# -- Project information ----------------------------------------------------- +project = "MONAI" +copyright = "MONAI Consortium" +author = "MONAI Contributors" + +# The full version, including alpha/beta/rc tags +short_version = monai.__version__.split("+")[0] +release = short_version +version = short_version + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [ + "transforms", + "networks", + "metrics", + "engines", + "data", + "apps", + "fl", + "bundle", + "config", + "handlers", + "losses", + "visualize", + "utils", + "inferers", + "optimizers", + "auto3dseg", +] + + +def generate_apidocs(*args): + """Generate API docs automatically by trawling the available modules""" + + import pandas as pd + from monai.bundle.properties import TrainProperties, InferProperties, MetaProperties + + csv_file = os.path.join(os.path.dirname(__file__), "train_properties.csv") # used in mb_properties.rst + pd.DataFrame.from_dict(TrainProperties, orient="index").iloc[:, :3].to_csv(csv_file) + csv_file = os.path.join(os.path.dirname(__file__), "infer_properties.csv") + pd.DataFrame.from_dict(InferProperties, orient="index").iloc[:, :3].to_csv(csv_file) + csv_file = os.path.join(os.path.dirname(__file__), "meta_properties.csv") + pd.DataFrame.from_dict(MetaProperties, orient="index").iloc[:, :3].to_csv(csv_file) + + module_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, "monai")) + output_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "apidocs")) + apidoc_command_path = "sphinx-apidoc" + if hasattr(sys, "real_prefix"): # called from a virtualenv + apidoc_command_path = os.path.join(sys.prefix, "bin", "sphinx-apidoc") + apidoc_command_path = os.path.abspath(apidoc_command_path) + print(f"output_path {output_path}") + print(f"module_path {module_path}") + subprocess.check_call( + [apidoc_command_path, "-e"] + + ["-o", output_path] + + [module_path] + + [os.path.join(module_path, p) for p in exclude_patterns] + ) + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +source_suffix = {".rst": "restructuredtext", ".txt": "restructuredtext", ".md": "markdown"} + +extensions = [ + "recommonmark", + "sphinx.ext.intersphinx", + "sphinx.ext.mathjax", + "sphinx.ext.napoleon", + "sphinx.ext.autodoc", + "sphinx.ext.linkcode", + "sphinx.ext.autosectionlabel", + "sphinx.ext.autosummary", + "sphinx_autodoc_typehints", +] + +autoclass_content = "class" +add_module_names = True +source_encoding = "utf-8" +autosectionlabel_prefix_document = True +napoleon_use_param = True +napoleon_include_init_with_doc = True +set_type_checking_flag = True + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "pydata_sphinx_theme" +# html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] +html_theme_options = { + "external_links": [{"url": "https://github.com/Project-MONAI/tutorials", "name": "Tutorials"}], + "icon_links": [ + {"name": "GitHub", "url": "https://github.com/project-monai/monai", "icon": "fab fa-github-square"}, + {"name": "Twitter", "url": "https://twitter.com/projectmonai", "icon": "fab fa-twitter-square"}, + ], + "collapse_navigation": True, + "navigation_with_keys": True, + "navigation_depth": 1, + "show_toc_level": 1, + "footer_start": ["copyright"], + "navbar_align": "content", + "logo": {"image_light": "MONAI-logo-color.png", "image_dark": "MONAI-logo-color.png"}, +} +html_context = { + "github_user": "Project-MONAI", + "github_repo": "MONAI", + "github_version": "dev", + "doc_path": "docs/source", + "conf_py_path": "/docs/source", + "VERSION": version, +} +html_scaled_image_link = False +html_show_sourcelink = True +html_favicon = "../images/favicon.ico" +html_logo = "../images/MONAI-logo-color.png" +html_sidebars = {"**": ["search-field", "sidebar-nav-bs"]} +pygments_style = "sphinx" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["../_static"] +html_css_files = ["custom.css"] +html_title = f"{project} {version} Documentation" + +# -- Auto-convert markdown pages to demo -------------------------------------- + + +def setup(app): + # Hook to allow for automatic generation of API docs + # before doc deployment begins. + app.connect("builder-inited", generate_apidocs) + + +# -- Linkcode configuration -------------------------------------------------- +DEFAULT_REF = "dev" +read_the_docs_ref = os.environ.get("READTHEDOCS_GIT_IDENTIFIER", None) +if read_the_docs_ref: + # When building on ReadTheDocs, link to the specific commit + # https://docs.readthedocs.io/en/stable/reference/environment-variables.html#envvar-READTHEDOCS_GIT_IDENTIFIER + git_ref = read_the_docs_ref +elif os.environ.get("GITHUB_REF_TYPE", "branch") == "tag": + # When building a tag, link to the tag itself + git_ref = os.environ.get("GITHUB_REF", DEFAULT_REF) +else: + git_ref = os.environ.get("GITHUB_SHA", DEFAULT_REF) + +DEFAULT_REPOSITORY = "Project-MONAI/MONAI" +repository = os.environ.get("GITHUB_REPOSITORY", DEFAULT_REPOSITORY) + +base_code_url = f"https://github.com/{repository}/blob/{git_ref}" +MODULE_ROOT_FOLDER = "monai" +repo_root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) + + +# Adjusted from https://github.com/python-websockets/websockets/blob/main/docs/conf.py +def linkcode_resolve(domain, info): + if domain != "py": + raise ValueError( + f"expected domain to be 'py', got {domain}." + "Please adjust linkcode_resolve to either handle this domain or ignore it." + ) + + mod = importlib.import_module(info["module"]) + if "." in info["fullname"]: + objname, attrname = info["fullname"].split(".") + obj = getattr(mod, objname) + try: + # object is a method of a class + obj = getattr(obj, attrname) + except AttributeError: + # object is an attribute of a class + return None + else: + obj = getattr(mod, info["fullname"]) + + try: + file = inspect.getsourcefile(obj) + source, lineno = inspect.getsourcelines(obj) + except TypeError: + # e.g. object is a typing.Union + return None + file = os.path.relpath(file, repo_root_path) + if not file.startswith(MODULE_ROOT_FOLDER): + # e.g. object is a typing.NewType + return None + start, end = lineno, lineno + len(source) - 1 + url = f"{base_code_url}/{file}#L{start}-L{end}" + return url diff --git a/MONAI/source/docs/source/config_syntax.md b/MONAI/source/docs/source/config_syntax.md new file mode 100644 index 0000000000000000000000000000000000000000..4b24415d2f3c642b01961970936dbe5395ebf4ae --- /dev/null +++ b/MONAI/source/docs/source/config_syntax.md @@ -0,0 +1,252 @@ +# MONAI Bundle Configuration + +The `monai.bundle` module supports building Python-based workflows via structured configurations. + +The main benefits are threefold: + +- it provides good readability and usability by separating system parameter settings from the Python code. +- it describes workflow at a relatively high level and allows for different low-level implementations. +- learning paradigms at a higher level such as federated learning and AutoML can be decoupled from the component details. + +Content: + +- [A basic example](#a-basic-example) +- [Syntax examples explained](#syntax-examples-explained) + - [`@` to reference Python objects in configurations](#to-reference-python-objects-in-configurations) + - [`$` to evaluate as Python expressions](#to-evaluate-as-python-expressions) + - [`%` to textually replace configuration elements](#to-textually-replace-configuration-elements) + - [`_target_` (`_disabled_`, `_desc_`, `_requires_`, `_mode_`) to instantiate a Python object](#instantiate-a-python-object) + - [`+` to alter semantics of merging config keys from multiple configuration files](#multiple-config-files) +- [The command line interface](#the-command-line-interface) +- [Recommendations](#recommendations) + +## A basic example + +Components as part of a workflow can be specified using `JSON` or `YAML` syntax, for example, a network architecture +definition could be stored in a `demo_config.json` file with the following content: + +```json +{ + "demo_net": { + "_target_": "monai.networks.nets.BasicUNet", + "spatial_dims": 3, + "in_channels": 1, + "out_channels": 2, + "features": [16, 16, 32, 32, 64, 64] + } +} +``` + +or alternatively, in `YAML` format (`demo_config.yaml`): + +```yaml +demo_net: + _target_: monai.networks.nets.BasicUNet + spatial_dims: 3 + in_channels: 1 + out_channels: 2 + features: [16, 16, 32, 32, 64, 64] +``` + +The configuration parser can instantiate the component as a Python object: + +```py +>>> from monai.bundle import ConfigParser +>>> config = ConfigParser() +>>> config.read_config("demo_config.json") +>>> net = config.get_parsed_content("demo_net", instantiate=True) +BasicUNet features: (16, 16, 32, 32, 64, 64). +>>> print(type(net)) + +``` + +or additionally, tune the input parameters then instantiate the component: + +```py +>>> config["demo_net"]["features"] = [32, 32, 32, 64, 64, 64] +>>> net = config.get_parsed_content("demo_net", instantiate=True) +BasicUNet features: (32, 32, 32, 64, 64, 64). +``` + +For more details on the `ConfigParser` API, please see [`monai.bundle.ConfigParser`](https://monai.readthedocs.io/en/latest/bundle.html#config-parser). + +## Syntax examples explained + +A few characters and keywords are interpreted beyond the plain texts, here are examples of the syntax: + +### To reference Python objects in configurations + +```json +"@preprocessing::transforms::keys" +``` + +_Description:_ `@` character indicates a reference to another configuration value defined at `preprocessing::transforms::keys`. +where `::` indicates a sub-structure of this configuration file. (`#` is a synonym for `::`, `preprocessing#transforms#keys` +refers to the same object.) + +```json +"@preprocessing::1" +``` + +_Description:_ `1` is referencing as an integer, which is used to index (zero-based indexing) the `preprocessing` sub-structure. + +Relative reference is supported by starting the reference with `#`. For example, `@#A` is to use `A` at the +same config structure level, and `@##A` refers to `A` at one level above. + +### To evaluate as Python expressions + +```json +"$print(42)" +``` + +_Description:_ `$` is a special character to indicate evaluating `print(42)` at runtime. + +```json +"$[i for i in @datalist]" +``` + +_Description:_ Create a list at runtime using the values in `datalist` as input. + +```json +"$from torchvision.models import resnet18" +``` + +_Description:_ `$` followed by an import statement is handled slightly differently from the +Python expressions. The imported module `resnet18` will be available as a global variable +to the other configuration sections. This is to simplify the use of external modules in the configuration. + +The config expressions may use `@` to reference other config items. For example, in `$lambda x: x + @a + @b`, +`@a` and `@b` are references to other Python objects and are made available to the anonymous function +as 'globals'. +It's therefore possible to modify the Python objects within an expression, for example, +`$lambda x: @my_list.pop() + x` will pop the last element from `@my_list` and add it to `x`. + +### To textually replace configuration elements + +```json +"%demo_config.json::demo_net::in_channels" +``` + +_Description:_ `%` character indicates a macro to replace the current configuration element with the texts at `demo_net::in_channels` in the +`demo_config.json` file. The replacement is done before instantiating or evaluating the components. + +### Instantiate a Python object + +```json +{ + "demo_name":{ + "_target_": "my.python.module.Class", + "args1": "string", + "args2": 42} +} +``` + +_Description:_ This dictionary defines an object with a reference name `demo_name`, with an instantiable type +specified at `_target_` and with input arguments `args1` and `args2`. +This dictionary will be instantiated as a Pytorch object at runtime. + +`_target_` is a required key by monai bundle syntax for the Python object name. +`args1` and `args2` should be compatible with the Python object to instantiate. + +```json +{ + "component_name": { + "_target_": "my.module.Class", + "_desc_": "this is a customized class which also triggers 'cudnn_opt' reference", + "_requires_": "@cudnn_opt", + "_disabled_": "true", + "_mode_": "default"} +} +``` + +_Description:_ `_requires_`, `_disabled_`, `_desc_`, and `_mode_` are optional keys. +- `_requires_` specifies references (string starts with `@`) or + Python expression that will be evaluated/instantiated before `_target_` object is instantiated. + It is useful when the component does not explicitly depend on the other ConfigItems via + its arguments, but requires the dependencies to be instantiated/evaluated beforehand. +- `_disabled_` specifies a flag to indicate whether to skip the instantiation. +- `_desc_` can be used for providing free text descriptions. +- `_mode_` specifies the operating mode when the component is instantiated or the callable is called. + it currently supports the following values: + - `"default"` (default) -- return the return value of ``_target_(**kwargs)`` + - `"callable"` -- return a callable, either as ``_target_`` itself or, if ``kwargs`` are provided, as a + partial function of ``functools.partial(_target_, **kwargs)``. Useful for defining a class or function + that will be instantied or called later. User can pre-define some arguments to the ``_target_`` and call + it with additional arguments later. + - `"debug"` -- execute with debug prompt and return the return value of ``pdb.runcall(_target_, **kwargs)``, + see also [`pdb.runcall`](https://docs.python.org/3/library/pdb.html#pdb.runcall). + +## Multiple config files + +_Description:_ Multiple config files may be specified on the command line. +The content of those config files is being merged. When same keys are specifiled in more than one config file, +the value associated with the key is being overridden, in the order config files are specified. +If the desired behaviour is to merge values from both files, the key in second config file should be prefixed with `+`. +The value types for the merged contents must match and be both of `dict` or both of `list` type. +`dict` values will be merged via update(), `list` values - concatenated via extend(). +Here's an example. In this case, "amp" value will be overridden by extra_config.json. +`imports` and `preprocessing#transforms` lists will be merged. An error would be thrown if the value type in `"+imports"` is not `list`: + +config.json: +```json +{ + "amp": "$True" + "imports": [ + "$import torch" + ], + "preprocessing": { + "_target_": "Compose", + "transforms": [ + "$@t1", + "$@t2" + ] + }, +} +``` + +extra_config.json: +```json +{ + "amp": "$False" + "+imports": [ + "$from monai.networks import trt_compile" + ], + "+preprocessing#transforms": [ + "$@t3" + ] +} +``` + +## The command line interface + +In addition to the Pythonic APIs, a few command line interfaces (CLI) are provided to interact with the bundle. +The primary usage is: +```bash +python -m monai.bundle COMMANDS +``` + +where `COMMANDS` is one of the following: `run`, `verify_metadata`, `ckpt_export`, ... +(please see `python -m monai.bundle --help` for a list of available options). + +The CLI supports flexible use cases, such as overriding configs at runtime and predefining arguments in a file. +To display a usage page for a command, for example `run`: +```bash +python -m monai.bundle run -- --help +``` + +The support is provided by [Python Fire](https://github.com/google/python-fire), please +make sure the optional dependency is installed, for example, +using `pip install monai[fire]` or `pip install fire`. +Details on the CLI argument parsing is provided in the +[Python Fire Guide](https://github.com/google/python-fire/blob/master/docs/guide.md#argument-parsing). + +## Recommendations +- Both `YAML` and `JSON` are supported, but the advanced features of these formats are not supported. +- Using meaningful names for the configuration elements can improve the readability. +- While it is possible to build complex configurations with the bundle syntax, + simple structures with sparse uses of expressions or references are preferred. +- For `$import ` in the configuration, please make sure there are instructions for the users to install + the `` if it is not a (optional) dependency of MONAI. +- As `#`, `::`, and `$` might be interpreted differently by the `shell` or `CLI` tools, may need to add escape characters + or quotes for them in the command line, like: `"\$torch.device('cuda:1')"`, `"'train_part#trainer'"`. +- For more details and examples, please see [the tutorials](https://github.com/Project-MONAI/tutorials/tree/main/bundle). diff --git a/MONAI/source/docs/source/contrib.rst b/MONAI/source/docs/source/contrib.rst new file mode 100644 index 0000000000000000000000000000000000000000..8f50824bf9802f7243cb3e222d44cb631448231f --- /dev/null +++ b/MONAI/source/docs/source/contrib.rst @@ -0,0 +1,7 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +Development +=========== + +For guidance on making a contribution to MONAI, see the `contributing guidelines +`_. diff --git a/MONAI/source/docs/source/data.rst b/MONAI/source/docs/source/data.rst new file mode 100644 index 0000000000000000000000000000000000000000..63d5e0e23d5b19fb491a4bd5fcc481ddb53de61b --- /dev/null +++ b/MONAI/source/docs/source/data.rst @@ -0,0 +1,351 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. _data: + +Data +==== + +Generic Interfaces +------------------ +.. currentmodule:: monai.data + +`Dataset` +~~~~~~~~~ +.. autoclass:: Dataset + :members: + :special-members: __getitem__ + +`IterableDataset` +~~~~~~~~~~~~~~~~~ +.. autoclass:: IterableDataset + :members: + :special-members: __next__ + +`DatasetFunc` +~~~~~~~~~~~~~ +.. autoclass:: DatasetFunc + :members: + :special-members: __next__ + +`ShuffleBuffer` +~~~~~~~~~~~~~~~ +.. autoclass:: ShuffleBuffer + :members: + :special-members: __next__ + +`CSVIterableDataset` +~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: CSVIterableDataset + :members: + :special-members: __next__ + +`PersistentDataset` +~~~~~~~~~~~~~~~~~~~ +.. autoclass:: PersistentDataset + :members: + :special-members: __getitem__ + +`GDSDataset` +~~~~~~~~~~~~~~~~~~~ +.. autoclass:: GDSDataset + :members: + :special-members: __getitem__ + + +`CacheNTransDataset` +~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: CacheNTransDataset + :members: + :special-members: __getitem__ + +`LMDBDataset` +~~~~~~~~~~~~~ +.. autoclass:: LMDBDataset + :members: + :special-members: __getitem__ + +`CacheDataset` +~~~~~~~~~~~~~~ +.. autoclass:: CacheDataset + :members: + :special-members: __getitem__ + +`SmartCacheDataset` +~~~~~~~~~~~~~~~~~~~ +.. autoclass:: SmartCacheDataset + :members: + :special-members: __getitem__ + +`ZipDataset` +~~~~~~~~~~~~ +.. autoclass:: ZipDataset + :members: + :special-members: __getitem__ + +`ArrayDataset` +~~~~~~~~~~~~~~ +.. autoclass:: ArrayDataset + :members: + :special-members: __getitem__ + +`ImageDataset` +~~~~~~~~~~~~~~ +.. autoclass:: ImageDataset + :members: + :special-members: __getitem__ + +`NPZDictItemDataset` +~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: NPZDictItemDataset + :members: + :special-members: __getitem__ + +`CSVDataset` +~~~~~~~~~~~~ +.. autoclass:: CSVDataset + :members: + :special-members: __getitem__ + +Patch-based dataset +------------------- + +`GridPatchDataset` +~~~~~~~~~~~~~~~~~~ +.. autoclass:: GridPatchDataset + :members: + +`PatchDataset` +~~~~~~~~~~~~~~ +.. autoclass:: PatchDataset + :members: + +`PatchIter` +""""""""""" +.. autoclass:: PatchIter + :members: + :special-members: __call__ + +`PatchIterd` +"""""""""""" +.. autoclass:: PatchIterd + :members: + :special-members: __call__ + +Image reader +------------ + +ImageReader +~~~~~~~~~~~ +.. autoclass:: ImageReader + :members: + +ITKReader +~~~~~~~~~ +.. autoclass:: ITKReader + :members: + +NibabelReader +~~~~~~~~~~~~~ +.. autoclass:: NibabelReader + :members: + +NumpyReader +~~~~~~~~~~~ +.. autoclass:: NumpyReader + :members: + +PILReader +~~~~~~~~~ +.. autoclass:: PILReader + :members: + +NrrdReader +~~~~~~~~~~ +.. autoclass:: NrrdReader + :members: + +Image writer +------------ + +resolve_writer +~~~~~~~~~~~~~~ +.. autofunction:: resolve_writer + +register_writer +~~~~~~~~~~~~~~~ +.. autofunction:: register_writer + +ImageWriter +~~~~~~~~~~~ +.. autoclass:: ImageWriter + :members: + +ITKWriter +~~~~~~~~~ +.. autoclass:: ITKWriter + :members: + +NibabelWriter +~~~~~~~~~~~~~ +.. autoclass:: NibabelWriter + :members: + +PILWriter +~~~~~~~~~ +.. autoclass:: PILWriter + :members: + +Synthetic +--------- +.. automodule:: monai.data.synthetic + :members: + + +Ouput folder layout +------------------- +.. automodule:: monai.data.folder_layout + :members: + + +Utilities +--------- +.. automodule:: monai.data.utils + :members: + +Partition Dataset +~~~~~~~~~~~~~~~~~ +.. autofunction:: monai.data.partition_dataset + +Partition Dataset based on classes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autofunction:: monai.data.partition_dataset_classes + +DistributedSampler +~~~~~~~~~~~~~~~~~~ +.. autoclass:: monai.data.DistributedSampler + +DistributedWeightedRandomSampler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: monai.data.DistributedWeightedRandomSampler + +DatasetSummary +~~~~~~~~~~~~~~ +.. autoclass:: monai.data.DatasetSummary + +Decathlon Datalist +~~~~~~~~~~~~~~~~~~ +.. autofunction:: monai.data.load_decathlon_datalist +.. autofunction:: monai.data.load_decathlon_properties +.. autofunction:: monai.data.check_missing_files +.. autofunction:: monai.data.create_cross_validation_datalist + + +DataLoader +~~~~~~~~~~ +.. autoclass:: monai.data.DataLoader + + +ThreadBuffer +~~~~~~~~~~~~ +.. autoclass:: monai.data.ThreadBuffer + +ThreadDataLoader +~~~~~~~~~~~~~~~~ +.. autoclass:: monai.data.ThreadDataLoader + +TestTimeAugmentation +~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: monai.data.TestTimeAugmentation + +N-Dim Fourier Transform +~~~~~~~~~~~~~~~~~~~~~~~~~ +.. automodule:: monai.data.fft_utils +.. autofunction:: monai.data.fft_utils.fftn_centered +.. autofunction:: monai.data.fft_utils.ifftn_centered + +ITK Torch Bridge +~~~~~~~~~~~~~~~~ +.. automodule:: monai.data.itk_torch_bridge + :members: + + +Meta Object +----------- +.. automodule:: monai.data.meta_obj + :members: + +MetaTensor +---------- +.. autoclass:: monai.data.MetaTensor + :members: + :show-inheritance: + :inherited-members: MetaObj + + + +Whole slide image reader +------------------------ + +BaseWSIReader +~~~~~~~~~~~~~ +.. autoclass:: monai.data.BaseWSIReader + :members: + +WSIReader +~~~~~~~~~ +.. autoclass:: monai.data.WSIReader + :members: + +CuCIMWSIReader +~~~~~~~~~~~~~~ +.. autoclass:: monai.data.CuCIMWSIReader + :members: + +OpenSlideWSIReader +~~~~~~~~~~~~~~~~~~ +.. autoclass:: monai.data.OpenSlideWSIReader + :members: + +TiffFileWSIReader +~~~~~~~~~~~~~~~~~ +.. autoclass:: monai.data.TiffFileWSIReader + :members: + + +Whole slide image datasets +-------------------------- + +PatchWSIDataset +~~~~~~~~~~~~~~~ +.. autoclass:: monai.data.PatchWSIDataset + :members: + +MaskedPatchWSIDataset +~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: monai.data.MaskedPatchWSIDataset + :members: + +SlidingPatchWSIDataset +~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: monai.data.SlidingPatchWSIDataset + :members: + +Bounding box +------------ +.. automodule:: monai.data.box_utils + :members: + +Video datasets +-------------- + +VideoDataset +~~~~~~~~~~~~ +.. autoclass:: monai.data.video_dataset.VideoDataset + +VideoFileDataset +~~~~~~~~~~~~~~~~ +.. autoclass:: monai.data.video_dataset.VideoFileDataset + +CameraDataset +~~~~~~~~~~~~~ +.. autoclass:: monai.data.video_dataset.CameraDataset diff --git a/MONAI/source/docs/source/engines.rst b/MONAI/source/docs/source/engines.rst new file mode 100644 index 0000000000000000000000000000000000000000..a015c7b2a3f9c5b1f46ade05248480f935fd8983 --- /dev/null +++ b/MONAI/source/docs/source/engines.rst @@ -0,0 +1,56 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. _engines: + +Engines +======= + +Workflows +--------- + +.. currentmodule:: monai.engines + +`Workflow` +~~~~~~~~~~ +.. autoclass:: Workflow + :members: + +`Trainer` +~~~~~~~~~ +.. autoclass:: Trainer + :members: + +`SupervisedTrainer` +~~~~~~~~~~~~~~~~~~~ +.. autoclass:: SupervisedTrainer + :members: + +`GanTrainer` +~~~~~~~~~~~~ +.. autoclass:: GanTrainer + :members: + +`AdversarialTrainer` +~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: AdversarialTrainer + :members: + +`Evaluator` +~~~~~~~~~~~ +.. autoclass:: Evaluator + :members: + +`SupervisedEvaluator` +~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: SupervisedEvaluator + :members: + +`EnsembleEvaluator` +~~~~~~~~~~~~~~~~~~~ +.. autoclass:: EnsembleEvaluator + :members: + +Utilities +--------- +.. automodule:: monai.engines.utils + :members: diff --git a/MONAI/source/docs/source/fl.rst b/MONAI/source/docs/source/fl.rst new file mode 100644 index 0000000000000000000000000000000000000000..412063013ed9f71d20e603eaadd7bc658dcd0a35 --- /dev/null +++ b/MONAI/source/docs/source/fl.rst @@ -0,0 +1,28 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. _fl: + +Federated Learning +================== +.. currentmodule:: monai.fl.client + +`Client Base Classes` +--------------------- + +.. autoclass:: BaseClient + :members: + +.. autoclass:: ClientAlgo + :members: + +.. autoclass:: ClientAlgoStats + :members: + +`MONAI Bundle Reference Implementations` +---------------------------------------- + +.. autoclass:: MonaiAlgo + :members: + +.. autoclass:: MonaiAlgoStats + :members: diff --git a/MONAI/source/docs/source/handlers.rst b/MONAI/source/docs/source/handlers.rst new file mode 100644 index 0000000000000000000000000000000000000000..49c84dab28a108fa689651121e1c08216a4f0d50 --- /dev/null +++ b/MONAI/source/docs/source/handlers.rst @@ -0,0 +1,222 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. _handlers: + +Event handlers +============== +.. currentmodule:: monai.handlers + +Model checkpoint loader +----------------------- +.. autoclass:: CheckpointLoader + :members: + +Model checkpoint saver +---------------------- +.. autoclass:: CheckpointSaver + :members: + + +Metrics saver +------------- +.. autoclass:: MetricsSaver + :members: + + +CSV saver +--------- +.. autoclass:: ClassificationSaver + :members: + + +Ignite Metric Handler +--------------------- +.. autoclass:: IgniteMetricHandler + :members: + + +Mean Dice metrics handler +------------------------- +.. autoclass:: MeanDice + :members: + + +Mean IoU metric handler +----------------------- +.. autoclass:: MeanIoUHandler + :members: + + +ROC AUC metrics handler +----------------------- +.. autoclass:: ROCAUC + :members: + + +Average Precision metric handler +-------------------------------- +.. autoclass:: AveragePrecision + :members: + + +Confusion matrix metrics handler +-------------------------------- +.. autoclass:: ConfusionMatrix + :members: + + +Hausdorff distance metrics handler +---------------------------------- +.. autoclass:: HausdorffDistance + :members: + + +Surface distance metrics handler +-------------------------------- +.. autoclass:: SurfaceDistance + :members: + + +Panoptic Quality metrics handler +-------------------------------- +.. autoclass:: PanopticQuality + :members: + + +Mean squared error metrics handler +---------------------------------- +.. autoclass:: MeanSquaredError + :members: + + +Mean absolute error metrics handler +----------------------------------- +.. autoclass:: MeanAbsoluteError + :members: + + +Root mean squared error metrics handler +--------------------------------------- +.. autoclass:: RootMeanSquaredError + :members: + + +Peak signal to noise ratio metrics handler +------------------------------------------ +.. autoclass:: PeakSignalToNoiseRatio + :members: + + +Metrics reloaded binary handler +------------------------------- +.. autoclass:: MetricsReloadedBinaryHandler + :members: + + +Metrics reloaded categorical handler +------------------------------------ +.. autoclass:: MetricsReloadedCategoricalHandler + :members: + + +Metric logger +------------- +.. autoclass:: MetricLogger + :members: + + +Logfile handler +--------------- +.. autoclass:: LogfileHandler + :members: + + +Training stats handler +---------------------- +.. autoclass:: StatsHandler + :members: + + +Tensorboard handlers +-------------------- +.. autoclass:: TensorBoardHandler + :members: + +.. autoclass:: TensorBoardStatsHandler + :members: + +.. autoclass:: TensorBoardImageHandler + :members: + + +LR Schedule handler +------------------- +.. autoclass:: LrScheduleHandler + :members: + + +Validation handler +------------------ +.. autoclass:: ValidationHandler + :members: + +SmartCache handler +------------------ +.. autoclass:: SmartCacheHandler + :members: + +Parameter Scheduler handler +--------------------------- +.. autoclass:: ParamSchedulerHandler + :members: + +EarlyStop handler +----------------- +.. autoclass:: EarlyStopHandler + :members: + +GarbageCollector handler +------------------------ +.. autoclass:: GarbageCollector + :members: + +Post processing +--------------- +.. autoclass:: PostProcessing + :members: + +Decollate batch +--------------- +.. autoclass:: DecollateBatch + :members: + +MLFlow handler +-------------- +.. autoclass:: MLFlowHandler + :members: + +ClearML handlers +---------------- +.. autoclass:: ClearMLHandler + :members: + +.. autoclass:: ClearMLStatsHandler + :members: + +.. autoclass:: ClearMLImageHandler + :members: + +NVTX Handlers +------------- +.. automodule:: monai.handlers.nvtx_handlers + :members: + +Utilities +--------- +.. automodule:: monai.handlers.utils + :members: + +Probability Map Handlers +------------------------ +.. automodule:: monai.handlers.probability_maps + :members: diff --git a/MONAI/source/docs/source/highlights.rst b/MONAI/source/docs/source/highlights.rst new file mode 100644 index 0000000000000000000000000000000000000000..29682a05fdbb0e0a87f3737c75331e292a047ee2 --- /dev/null +++ b/MONAI/source/docs/source/highlights.rst @@ -0,0 +1,10 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +Highlights +========== + +.. toctree:: + :maxdepth: 1 + + modules.md + applications.md diff --git a/MONAI/source/docs/source/index.rst b/MONAI/source/docs/source/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..4279e522d38db93a834378a3de96e156568565fa --- /dev/null +++ b/MONAI/source/docs/source/index.rst @@ -0,0 +1,114 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. MONAI documentation main file, created by + sphinx-quickstart on Wed Feb 5 09:40:29 2020. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Project MONAI +============= + + +*Medical Open Network for AI* + +MONAI is a `PyTorch `_-based, `open-source `_ framework +for deep learning in healthcare imaging, part of the `PyTorch Ecosystem `_. + +Its ambitions are: + +- developing a community of academic, industrial and clinical researchers collaborating on a common foundation; +- creating state-of-the-art, end-to-end training workflows for healthcare imaging; +- providing researchers with an optimized and standardized way to create and evaluate deep learning models. + +.. image:: ../images/MONAI_arch.png + :alt: MONAI Architecture + :align: center + +Features +-------- + +- flexible pre-processing for multi-dimensional medical imaging data; +- compositional & portable APIs for ease of integration in existing workflows; +- domain-specific implementations for networks, losses, evaluation metrics and more; +- customizable design for varying user expertise; +- multi-GPU multi-node data parallelism support. + + +Getting started +--------------- + +`MedNIST demo `_ and `MONAI for PyTorch Users `_ are available on Colab. + +Examples and notebook tutorials are located at `Project-MONAI/tutorials `_. + +Technical documentation is available at `docs.monai.io `_. + +.. toctree:: + :maxdepth: 1 + :caption: Feature highlights + + whatsnew + highlights.md + +.. toctree:: + :maxdepth: 1 + :caption: API Reference + + api + +.. toctree:: + :maxdepth: 1 + :caption: Installation + + installation + +.. toctree:: + :maxdepth: 1 + :caption: Precision and Accelerating + + precision_accelerating + +.. toctree:: + :maxdepth: 1 + :caption: Contributing + + contrib + +.. toctree:: + :maxdepth: 1 + :caption: Specifications + + bundle_intro + lazy_resampling + +Model Zoo +--------- + +`The MONAI Model Zoo `_ is a place for researchers and data scientists to share the latest and great models from the community. +Utilizing `the MONAI Bundle format `_ makes it easy to `get started `_ building workflows with MONAI. + + +Links +----- + +- Website: https://project-monai.github.io/ +- API documentation (milestone): https://monai.readthedocs.io/ +- API documentation (latest dev): https://monai.readthedocs.io/en/latest/ +- Code: https://github.com/Project-MONAI/MONAI +- Project tracker: https://github.com/Project-MONAI/MONAI/projects +- Issue tracker: https://github.com/Project-MONAI/MONAI/issues +- Changelog: https://github.com/Project-MONAI/MONAI/blob/dev/CHANGELOG.md +- Wiki: https://github.com/Project-MONAI/MONAI/wiki +- FAQ: https://github.com/Project-MONAI/MONAI/wiki/Frequently-asked-questions-and-answers +- Test status: https://github.com/Project-MONAI/MONAI/actions +- PyPI package: https://pypi.org/project/monai/ +- conda-forge: https://anaconda.org/conda-forge/monai +- Weekly previews: https://pypi.org/project/monai-weekly/ +- Docker Hub: https://hub.docker.com/r/projectmonai/monai + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` diff --git a/MONAI/source/docs/source/inferers.rst b/MONAI/source/docs/source/inferers.rst new file mode 100644 index 0000000000000000000000000000000000000000..326f56e96c9cfbe384802b9a1342874721c33105 --- /dev/null +++ b/MONAI/source/docs/source/inferers.rst @@ -0,0 +1,119 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. _inferers: + +Inference methods +================= + +Inferers +-------- + +.. currentmodule:: monai.inferers +.. autoclass:: Inferer + :members: + :special-members: __call__ + +`PatchInferer` +~~~~~~~~~~~~~~ +.. autoclass:: PatchInferer + :members: + :special-members: __call__ + +`SimpleInferer` +~~~~~~~~~~~~~~~ +.. autoclass:: SimpleInferer + :members: + :special-members: __call__ + +`SlidingWindowInferer` +~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: SlidingWindowInferer + :members: + :special-members: __call__ + +`SlidingWindowInfererAdapt` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: SlidingWindowInfererAdapt + :members: + :special-members: __call__ + +`SaliencyInferer` +~~~~~~~~~~~~~~~~~ +.. autoclass:: SaliencyInferer + :members: + :special-members: __call__ + +`SliceInferer` +~~~~~~~~~~~~~~ +.. autoclass:: SliceInferer + :members: + :special-members: __call__ + +`DiffusionInferer` +~~~~~~~~~~~~~~~~~~ +.. autoclass:: DiffusionInferer + :members: + :special-members: __call__ + +`LatentDiffusionInferer` +~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: LatentDiffusionInferer + :members: + :special-members: __call__ + +`ControlNetDiffusionInferer` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: ControlNetDiffusionInferer + :members: + :special-members: __call__ + +`ControlNetLatentDiffusionInferer` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: ControlNetLatentDiffusionInferer + :members: + :special-members: __call__ + +Splitters +--------- +.. currentmodule:: monai.inferers +.. autoclass:: Splitter + :members: + :special-members: __call__ + +`SlidingWindowSplitter` +~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: SlidingWindowSplitter + :members: + :special-members: __call__ + +`WSISlidingWindowSplitter` +~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: WSISlidingWindowSplitter + :members: + :special-members: __call__ + + +Mergers +------- +.. currentmodule:: monai.inferers +.. autoclass:: Merger + :members: + :special-members: __call__ + +`AvgMerger` +~~~~~~~~~~~ +.. autoclass:: AvgMerger + :members: + :special-members: __call__ + +`ZarrAvgMerger` +~~~~~~~~~~~~~~~ +.. autoclass:: ZarrAvgMerger + :members: + :special-members: __call__ + + +Sliding Window Inference Function +--------------------------------- + +.. autofunction:: monai.inferers.sliding_window_inference diff --git a/MONAI/source/docs/source/installation.md b/MONAI/source/docs/source/installation.md new file mode 100644 index 0000000000000000000000000000000000000000..b8befc10b4e8c21e6933cb3def19b09e8663aa25 --- /dev/null +++ b/MONAI/source/docs/source/installation.md @@ -0,0 +1,263 @@ +# Installation Guide + +## Table of Contents + +- [Installation Guide](#installation-guide) + - [Table of Contents](#table-of-contents) + - [From PyPI](#from-pypi) + - [Milestone release](#milestone-release) + - [Weekly preview release](#weekly-preview-release) + - [Uninstall the packages](#uninstall-the-packages) + - [From conda-forge](#from-conda-forge) + - [From GitHub](#from-github) + - [Option 1 (as a part of your system-wide module):](#option-1-as-a-part-of-your-system-wide-module) + - [Option 2 (editable installation):](#option-2-editable-installation) + - [Validating the install](#validating-the-install) + - [MONAI version string](#monai-version-string) + - [From DockerHub](#from-dockerhub) + - [Installing the recommended dependencies](#installing-the-recommended-dependencies) + +--- + +MONAI's core functionality is written in Python 3 (>= 3.9) and only requires [Numpy](https://numpy.org/) and [Pytorch](https://pytorch.org/). + +The package is currently distributed via Github as the primary source code repository, +and the Python package index (PyPI). The pre-built Docker images are made available on DockerHub. + +To install optional features such as handling the NIfTI files using +[Nibabel](https://nipy.org/nibabel/), or building workflows using [Pytorch +Ignite](https://pytorch.org/ignite/), please follow the instructions: + +- [Installing the recommended dependencies](#installing-the-recommended-dependencies) + +The installation commands below usually end up installing CPU variant of PyTorch. To install GPU-enabled PyTorch: + +1. Install the latest NVIDIA driver. +1. Check [PyTorch Official Guide](https://pytorch.org/get-started/locally/) for the recommended CUDA versions. For Pip package, the user needs to download the CUDA manually, install it on the system, and ensure CUDA_PATH is set properly. +1. Continue to follow the guide and install PyTorch. +1. Install MONAI using one the ways described below. + +--- + +## From PyPI + +### Milestone release + +To install the [current milestone release](https://pypi.org/project/monai/): + +```bash +pip install monai +``` + +### Weekly preview release + +To install the [weekly preview release](https://pypi.org/project/monai-weekly/): + +```bash +pip install monai-weekly +``` + +The weekly build is released to PyPI every Sunday with a pre-release build number `dev[%y%U]`. +To report any issues on the weekly preview, please include the version information: + +```bash +python -c "import monai; print(monai.__version__)" +``` + +Coexistence of package `monai` and `monai-weekly` in a system may cause namespace conflicts +and `ImportError`. +This is usually a result of running both `pip install monai` and `pip install monai-weekly` +without uninstalling the existing one first. +To address this issue, please uninstall both packages, and retry the installation. + +### Uninstall the packages + +The packages installed using `pip install` could be removed by: + +```bash +pip uninstall -y monai +pip uninstall -y monai-weekly +``` + +## From conda-forge + +To install the [current milestone release](https://anaconda.org/conda-forge/monai): + +```bash +conda install -c conda-forge monai +``` + +## From GitHub + +(_If you have installed the +PyPI release version using `pip install monai`, please run `pip uninstall +monai` before using the commands from this section. Because `pip` by +default prefers the milestone release_.) + +The milestone versions are currently planned and released every few months. As the +codebase is under active development, you may want to install MONAI from GitHub +for the latest features: + +### Option 1 (as a part of your system-wide module): + +```bash +pip install git+https://github.com/Project-MONAI/MONAI +``` + +or, to build with MONAI C++/CUDA extensions: + +```bash +BUILD_MONAI=1 pip install git+https://github.com/Project-MONAI/MONAI +``` + +To build the extensions, if the system environment already has a version of Pytorch installed, +`--no-build-isolation` might be preferred: + +```bash +BUILD_MONAI=1 pip install --no-build-isolation git+https://github.com/Project-MONAI/MONAI +``` + +this command will download and install the current `dev` branch of [MONAI from +GitHub](https://github.com/Project-MONAI/MONAI). + +This documentation website by default shows the information for the latest version. + +### Option 2 (editable installation): + +To install an editable version of MONAI, it is recommended to clone the codebase directly: + +```bash +git clone https://github.com/Project-MONAI/MONAI.git +``` + +This command will create a `MONAI/` folder in your current directory. +You can install it by running: + +```bash +cd MONAI/ +python setup.py develop +``` + +or, to build with MONAI C++/CUDA extensions and install: + +```bash +cd MONAI/ +BUILD_MONAI=1 python setup.py develop +# for MacOS +BUILD_MONAI=1 CC=clang CXX=clang++ python setup.py develop +``` + +To uninstall the package please run: + +```bash +cd MONAI/ +python setup.py develop --uninstall + +# to further clean up the MONAI/ folder (Bash script) +./runtests.sh --clean +``` + +Alternatively, simply adding the root directory of the cloned source code (e.g., `/workspace/Documents/MONAI`) to your `$PYTHONPATH` +and the codebase is ready to use (without the additional features of MONAI C++/CUDA extensions). + +> The C++/CUDA extension features are currently experimental, a pre-compiled version is made available via +> [the recent docker image releases](https://hub.docker.com/r/projectmonai/monai). +> Building the extensions from source may require [Ninja](https://ninja-build.org/) and [CUDA Toolkit](https://developer.nvidia.com/cuda-toolkit). +> By default, CUDA extension is built if `torch.cuda.is_available()`. It's possible to force building by +> setting `FORCE_CUDA=1` environment variable. + +## Validating the install + +You can verify the installation by: + +```bash +python -c "import monai; monai.config.print_config()" +``` + +If the installation is successful, this command will print out the MONAI version information, and this confirms the core +modules of MONAI are ready-to-use. + +## MONAI version string + +The MONAI version string shows the current status of your local installation. For example: + +``` +MONAI version: 0.1.0+144.g52c763d.dirty +``` + +- `0.1.0` indicates that your installation is based on the `0.1.0` milestone release. +- `+144` indicates that your installation is 144 git commits ahead of the milestone release. +- `g52c763d` indicates that your installation corresponds to the git commit hash `52c763d`. +- `dirty` indicates that you have modified the codebase locally, and the codebase is inconsistent with `52c763d`. + +## From DockerHub + +Make sure you have installed the NVIDIA driver and Docker 19.03+ for your Linux distribution. +Note that you do not need to install the CUDA toolkit on the host, but the driver needs to be installed. +Please find out more information on [nvidia-docker](https://github.com/NVIDIA/nvidia-docker). + +Assuming that you have the Nvidia driver and Docker 19.03+ installed, running the following command will +download and start a container with the latest version of MONAI. The latest `dev` branch of MONAI from GitHub +is included in the image. + +```bash +docker run --gpus all --rm -ti --ipc=host projectmonai/monai:latest +``` + +You can also run a milestone release docker image by specifying the image tag, for example: + +``` +docker run --gpus all --rm -ti --ipc=host projectmonai/monai:0.1.0 +``` + +## Installing the recommended dependencies + +By default, the installation steps will only download and install the minimal requirements of MONAI. +Optional dependencies can be installed using [the extras syntax](https://packaging.python.org/tutorials/installing-packages/#installing-setuptools-extras) to support additional features. + +For example, to install MONAI with Nibabel and Scikit-image support: + +```bash +git clone https://github.com/Project-MONAI/MONAI.git +cd MONAI/ +pip install -e '.[nibabel,skimage]' +``` + +Alternatively, to install all optional dependencies: + +```bash +git clone https://github.com/Project-MONAI/MONAI.git +cd MONAI/ +pip install -e ".[all]" +``` + +To install all optional dependencies with `pip` based on MONAI development environment settings: + +```bash +git clone https://github.com/Project-MONAI/MONAI.git +cd MONAI/ +pip install -r requirements-dev.txt +``` + +To install all optional dependencies with `conda` based on MONAI development environment settings (`environment-dev.yml`; +this will install PyTorch as well as `pytorch-cuda`, please follow https://pytorch.org/get-started/locally/#start-locally for more details about installing PyTorch): + +```bash +git clone https://github.com/Project-MONAI/MONAI.git +cd MONAI/ +conda create -n python= # eg 3.9 +conda env update -n -f environment-dev.yml +``` + +Since MONAI v0.2.0, the extras syntax such as `pip install 'monai[nibabel]'` is available via PyPI. + +- The options are + +``` +[nibabel, skimage, scipy, pillow, tensorboard, gdown, ignite, torchvision, itk, tqdm, lmdb, psutil, cucim, openslide, pandas, einops, transformers, mlflow, clearml, matplotlib, tensorboardX, tifffile, imagecodecs, pyyaml, fire, jsonschema, ninja, pynrrd, pydicom, h5py, nni, optuna, onnx, onnxruntime, zarr, lpips, pynvml, huggingface_hub] +``` + +which correspond to `nibabel`, `scikit-image`,`scipy`, `pillow`, `tensorboard`, +`gdown`, `pytorch-ignite`, `torchvision`, `itk`, `tqdm`, `lmdb`, `psutil`, `cucim`, `openslide-python`, `pandas`, `einops`, `transformers`, `mlflow`, `clearml`, `matplotlib`, `tensorboardX`, `tifffile`, `imagecodecs`, `pyyaml`, `fire`, `jsonschema`, `ninja`, `pynrrd`, `pydicom`, `h5py`, `nni`, `optuna`, `onnx`, `onnxruntime`, `zarr`, `lpips`, `nvidia-ml-py`, `huggingface_hub` and `pyamg` respectively. + +- `pip install 'monai[all]'` installs all the optional dependencies. diff --git a/MONAI/source/docs/source/lazy_resampling.rst b/MONAI/source/docs/source/lazy_resampling.rst new file mode 100644 index 0000000000000000000000000000000000000000..7b809965f33b8e67fecead114513e4df45f1ec18 --- /dev/null +++ b/MONAI/source/docs/source/lazy_resampling.rst @@ -0,0 +1,273 @@ +.. _lazy_resampling: + +:github_url: https://github.com/Project-MONAI/MONAI + +Lazy Resampling +=============== + +.. toctree:: + :maxdepth: 2 + +Introduction +^^^^^^^^^^^^ + +Lazy Resampling is a new feature introduced in MONAI 1.2. This feature is still experimental at this time and it is +possible that behaviour and APIs will change in upcoming releases. + +Lazy resampling reworks the way that preprocessing is performed. It improves upon standard preprocessing pipelines and +can provide significant benefits over traditional preprocessing. It can improve: +* pipeline execution time +* pipeline memory usage in CPU or GPU +* image and segmentation quality by reducing incidental noise and artifacts caused by resampling + +The way it does this is by adopting the methods used in computer graphics pipelines, in which transformations to objects +in a scene are modified by composing together a sequence of "homogeneous matrices". + +Rather than each transform being executed in isolation, potentially requiring the data to be resampled to make a new +tensor, transforms whose operations can be described in terms of homogeneous transforms do not execute their transforms +immediately. Instead, they create a "pending operation", which is added to a list of operations that will be fused +together and carried out at the point that they are required. + + +How Lazy Resampling changes preprocessing +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In order to understand the difference between traditional pipelines and lazy pipelines, it is best to look at an example +pipeline and the differences between their execution strategies: + + +Traditional execution ++++++++++++++++++++++ + +With traditional resampling, found both in MONAI and many other preprocessing libraries, you typically define a sequence +of transforms and pass them to a ``Compose`` object, such as :class:`monai.transforms.compose.Compose`. + +Example:: + + transforms = [ + Spacingd(keys=["img", "seg"], ...), + Orientationd(keys=["img", "seg"], ...), + RandSpatialCropd(keys=["img", "seg"], ...), + RandRotate90d(keys=["img", "seg"], ...), + RandRotated(keys=["img", "seg"], ...), + RandZoomd(keys=["img", "seg"], ...), + RandGaussianNoised(keys="img", ...), + ] + pipeline = Compose(transforms) + + # elsewhere this will be called many times (such as in a Dataset instance) + outputs = pipeline(inputs) + + +The following will then happen when we call ``pipeline(inputs)``: + +1. ``Spacingd`` is called and interpolates the data samples +2. ``Orientationd`` permutes the data samples so that their spatial dimensions are reorganised +3. ``RandSpatialCropd`` crops a random patch of the data samples, throwing away the rest of the data in the process +4. ``RandRotate90d`` has a chance of performing a tensor-based rotation of the data samples +5. ``RandRotated`` has a chance of performing a full resample of the data samples +6. ``RandZoomd`` has a chance of performing a interpolation of the data samples +7. ``RandGaussianNoised`` has a chance of adding noise to ``img`` + +.. figure:: ../images/lazy_resampling_trad_example_1.svg + + Figure showing traditional pipeline execution. Tensors (the boxes in the main body of the image) are passed through + the pipeline, and the state of their `applied_operations` property is shown at each step. Tensors with a thick red + border have undergone some kind of resample operation at that stage. + +Overall, there are up to three occasions where the data is either interpolated or resampled through spatial transforms +(``Spacingd``, ``RandRotated`` and ``RandZoomd``). Furthermore, the crop that occurs means that the output data +samples might contain pixels for which there is data but that show padding values, because the data was thrown away by +``RandSpatialCrop``. + +Each of these operations takes time and memory, but, as we can see in the example above, also creates resampling +artifacts and can even destroy data in the resulting data samples. + +Lazy execution +++++++++++++++ + +Lazy resampling works very differently. When you execute the same pipeline with `lazy=True`, the following happens: + +#. ``Spacingd`` is executed lazily. It puts a description of the operation that it wants to perform onto a list of + pending operations +#. ``Orientationd`` is executed lazily. It adds a description of its own operation to the pending operation list so + now there are 2 pending operations +#. ``RandSpatialCropd`` is executed lazily. It adds a description of its own operation to the pending + operation list so now there are 3 pending operations +#. ``RandRotate90d`` is executed lazily. It adds a description of its own operation to the pending operation + list so now there are 4 pending operations +#. ``RandRotated`` is executed lazily. It adds a description of its own operation to the pending operation + list so now there are 5 pending operations +#. ``RandZoomd`` is executed lazily. It adds a description of its own operation to the pending operation + list so now there are 6 pending operations + + #. [Spacingd, Orientationd, RandSpatialCropd, RandRotate90d, RandRotated, RandZoomd] are all on the pending + operations list but have yet to be carried out on the data +#. ``RandGaussianNoised`` is not a lazy transform. It is now time for the pending operations to be evaluated. Their + descriptions are mathematically composited together, to determine the operation that results from all of them being + carried out. This is then applied in a single resample operation. Once that is done, RandGaussianNoised operates on + the resulting data + +.. figure:: ../images/lazy_resampling_lazy_example_1.svg + + Figure showing lazy pipeline execution. We show the state of the `pending_operations` and `applied_operations` + properties of the tensor as it is processed by the pipeline. Thick red borders indicate some kind of resampling + operation has taken place at that step. Lazy resampling performs far fewer of these operations. + +The single resampling operation has less noise induced by resampling, as it only occurs once in this pipeline rather +than three times in the traditional pipeline. More importantly, although the crop describes an operation to keep only a +subset of the data sample, the crop is not performed until after the spatial transforms are completed, which means that +all of the data sample that is within bounds is preserved and is part of the resulting output. + + +Composing homogeneous matrices +++++++++++++++++++++++++++++++ + +.. image:: ../images/lazy_resampling_homogeneous_matrices.svg + + +Although a full treatment of homogeneous matrices is outside the scope of this document, a brief overview of them is +useful to understand the mechanics of lazy resampling. Homogeneous matrices are used in computer graphics to describe +operations in cartesian space in a unified (homogeneous) fashion. Rotation, scaling, translation, and skewing are +amongst the operations that can be performed. Homogeneous matrices have the interesting property that they can be +composited together, thus describing the result of a sequence of operations. Note that ordering is important; +`scale -> rotate -> translation` gives a very different result to `translation -> rotate -> scale`. + +The ability to composite homogeneous matrices together allows a sequence of operations to be carried out as a single +operation, which is the key mechanism by which lazy resampling functions. + + +API changes +^^^^^^^^^^^ + +A number of new arguments have been added to existing properties, which we'll go over in detail here. In particular, +we'll focus on :class:`Compose and +:class:`LazyTrait`/ :class:`LazyTransform` +and the way that they interact with each other. + + +Compose ++++++++ + +:class:`Compose` gains a number of new arguments that can be used to control +resampling behaviour. Each of them is covered in its own section: + + +lazy +"""" + +``lazy`` controls whether execution is carried out in a lazy manner or not. It has three values that it can take: + +* `lazy=False` forces the pipeline to be executed in the standard way with every transform applied immediately +* `lazy=True` forces the pipeline to be executed lazily. Every transform that implements + :class:`LazyTrait` (or inherits + :class:`LazyTransform`) will be executed lazily +* `lazy=None` means that the pipeline can execute lazily, but only on transforms that have their own `lazy` property + set to True. + + +overrides +""""""""" + +``overrides`` allows the user to specify certain parameters that transforms can be overridden with when they are +executed lazily. This parameter is primarily provided to allow you to run a pipeline without having to modify fields +like ``mode`` and ``padding_mode``. +When executing dictionary-based transforms, you provide a dictionary containing overrides for each key, as follows. You +can omit keys that don't require overrides: + +.. code-block:: + + { + "image": {"mode": "bilinear"}, + "label": {"padding_mode": "zeros"} + } + + +log_stats +""""""""" + +Logging of transform execution is provided if you wish to understand exactly how your pipelines execute. It can take a +``bool`` or ``str`` value, and is False by default, which disables logging. Otherwise, you can enable it by passing it +the name of a logger that you wish to use (note, you don't have to construct the logger beforehand). + + +LazyTrait / LazyTransform ++++++++++++++++++++++++++ + +Many transforms now implement either `LazyTrait` or +`LazyTransform`. Doing so marks the transform for lazy execution. Lazy +transforms have the following in common: + + +``__init__`` has a ``lazy`` argument +"""""""""""""""""""""""""""""""""""" + +``lazy`` is a ``bool`` value that can be passed to the initialiser when a lazy transform is instantiated. This +indicates to the transform that it should execute lazily or not lazily. Note that this value can be overridden by +passing ``lazy`` to ``__init__``. ``lazy`` is ``False`` by default + + +``__call__`` has a ``lazy`` argument +"""""""""""""""""""""""""""""""""""" + +``lazy`` is an optional ``bool`` value that can be passed at call time to override the behaviour defined during +initialisation. It has a default value of ``None``. If it is not ``None``, then this value is used instead of +``self.lazy``. This allows the calling :class:`Compose` instance to override +default values rather than having to set it on every lazy transform (unless the user sets +:class:`Compose.lazy` to ``None``). + + +lazy property +""""""""""""" + +The lazy property allows you to get or set the lazy status of a lazy transform after constructing it. + + +requires_current_data property (get only) +""""""""""""""""""""""""""""""""""""""""" + +The ``requires_current_data`` property indicates that a transform makes use of the data in one or more of the tensors +that it is passed during its execution. Such transforms require that the tensors must therefore be up to date, even if +the transform itself is executing lazily. This is required for transforms such as ``CropForeground[d]``, +``RandCropByPosNegLabel[d]``, and ``RandCropByLabelClasses[d]``. This property is implemented to return ``False`` on +``LazyTransform`` and must be overridden to return ``True`` by transforms that check data values when executing. + + +Controlling laziness +^^^^^^^^^^^^^^^^^^^^ + +There are two ways that a user can provide more fine-grained control over laziness. One is to make use of lazy=None +when initialising or calling ``Compose`` instances. The other is to use the ``ApplyPending[d]`` transforms. These +techniques can be freely mixed and matched. + + +Using ``lazy=None`` ++++++++++++++++++++ + +``Lazy=None`` tells ``Compose`` to honor the lazy flags set on each lazy transform. These are set to False by default +so the user must set lazy=True on the transforms that they still wish to execute lazily. + + +``lazy=None`` example: +"""""""""""""""""""""" + +.. figure:: ../images/lazy_resampling_none_example.svg + + Figure shwoing the effect of using ``lazy=False`` when ``Compose`` is being executed with ``lazy=None``. Note that + the additional resamples that occur due to ``RandRotate90d`` being executed in a non-lazy fashion. + + +Using ``ApplyPending[d]`` ++++++++++++++++++++++++++ + +``ApplyPending[d]`` causes all pending transforms to be executed before the following transform, regardless of whether +the following transform is a lazy transform, or is configured to execute lazily. + + +``ApplyPending`` Example: +""""""""""""""""""""""""" + +.. figure:: ../images/lazy_resampling_apply_pending_example.svg + + Figure showing the use of :class:`ApplyPendingd` to cause + resampling to occur in the midele of a chain of lazy transforms. diff --git a/MONAI/source/docs/source/losses.rst b/MONAI/source/docs/source/losses.rst new file mode 100644 index 0000000000000000000000000000000000000000..528ccd11730214a2e8257f245cc79b1c31e94eba --- /dev/null +++ b/MONAI/source/docs/source/losses.rst @@ -0,0 +1,169 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. _losses: + +Loss functions +============== + +Segmentation Losses +------------------- + +.. automodule:: monai.losses +.. currentmodule:: monai.losses + +`DiceLoss` +~~~~~~~~~~ +.. autoclass:: DiceLoss + :members: + +.. autoclass:: Dice + :members: + +.. autoclass:: dice + :members: + +`MaskedDiceLoss` +~~~~~~~~~~~~~~~~ +.. autoclass:: MaskedDiceLoss + :members: + +`GeneralizedDiceLoss` +~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: GeneralizedDiceLoss + :members: + +.. autoclass:: generalized_dice + :members: + +`GeneralizedWassersteinDiceLoss` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: GeneralizedWassersteinDiceLoss + :members: + +.. autoclass:: generalized_wasserstein_dice + :members: + +`DiceCELoss` +~~~~~~~~~~~~ +.. autoclass:: DiceCELoss + :members: + +`DiceFocalLoss` +~~~~~~~~~~~~~~~ +.. autoclass:: DiceFocalLoss + :members: + +`GeneralizedDiceFocalLoss` +~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: GeneralizedDiceFocalLoss + :members: + +`FocalLoss` +~~~~~~~~~~~ +.. autoclass:: FocalLoss + :members: + +`TverskyLoss` +~~~~~~~~~~~~~ +.. autoclass:: TverskyLoss + :members: + +`ContrastiveLoss` +~~~~~~~~~~~~~~~~~ +.. autoclass:: ContrastiveLoss + :members: + +`BarlowTwinsLoss` +~~~~~~~~~~~~~~~~~ +.. autoclass:: BarlowTwinsLoss + :members: + +`HausdorffDTLoss` +~~~~~~~~~~~~~~~~~ +.. autoclass:: HausdorffDTLoss + :members: + +`SoftclDiceLoss` +~~~~~~~~~~~~~~~~ +.. autoclass:: SoftclDiceLoss + :members: + +`SoftDiceclDiceLoss` +~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: SoftDiceclDiceLoss + :members: + +`NACLLoss` +~~~~~~~~~~ +.. autoclass:: NACLLoss + :members: + +Registration Losses +------------------- + +`BendingEnergyLoss` +~~~~~~~~~~~~~~~~~~~ +.. autoclass:: BendingEnergyLoss + :members: + +`DiffusionLoss` +~~~~~~~~~~~~~~~ +.. autoclass:: DiffusionLoss + :members: + +`LocalNormalizedCrossCorrelationLoss` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: LocalNormalizedCrossCorrelationLoss + :members: + +`GlobalMutualInformationLoss` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: GlobalMutualInformationLoss + :members: + +Reconstruction Losses +--------------------- + +`SSIMLoss` +~~~~~~~~~~ +.. autoclass:: monai.losses.ssim_loss.SSIMLoss + :members: + +`PatchAdversarialLoss` +~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: PatchAdversarialLoss + :members: + +`PerceptualLoss` +~~~~~~~~~~~~~~~~~ +.. autoclass:: PerceptualLoss + :members: + +`JukeboxLoss` +~~~~~~~~~~~~~~ +.. autoclass:: JukeboxLoss + :members: + +`SURELoss` +~~~~~~~~~~ +.. autoclass:: SURELoss + :members: + + +Loss Wrappers +------------- + +`MultiScaleLoss` +~~~~~~~~~~~~~~~~ +.. autoclass:: MultiScaleLoss + :members: + +`MaskedLoss` +~~~~~~~~~~~~ +.. autoclass:: MaskedLoss + :members: + +`DeepSupervisionLoss` +~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: DeepSupervisionLoss + :members: diff --git a/MONAI/source/docs/source/mb_properties.rst b/MONAI/source/docs/source/mb_properties.rst new file mode 100644 index 0000000000000000000000000000000000000000..f261480f55c4aee245aa5084903d4b3a57b007a6 --- /dev/null +++ b/MONAI/source/docs/source/mb_properties.rst @@ -0,0 +1,30 @@ +MONAI Bundle Properties +======================= + + +Train properties +---------------- + +.. csv-table:: + :header-rows: 1 + :file: train_properties.csv + :class: longtable + :widths: 10, 55, 5, 30 + +Infer properties +---------------- + +.. csv-table:: + :header-rows: 1 + :file: infer_properties.csv + :class: longtable + :widths: 10, 55, 5, 30 + +Meta properties +--------------- + +.. csv-table:: + :header-rows: 1 + :file: meta_properties.csv + :class: longtable + :widths: 10, 55, 5, 30 diff --git a/MONAI/source/docs/source/mb_specification.rst b/MONAI/source/docs/source/mb_specification.rst new file mode 100644 index 0000000000000000000000000000000000000000..56d660e35c24761a9f61fd15d155d589ecfa27e9 --- /dev/null +++ b/MONAI/source/docs/source/mb_specification.rst @@ -0,0 +1,174 @@ + +========================== +MONAI Bundle Specification +========================== + +Overview +======== + +This is the specification for the MONAI Bundle (MB) format of portable described deep learning models. The objective of a MB is to define a packaged network or model which includes the critical information necessary to allow users and programs to understand how the model is used and for what purpose. A bundle includes the stored weights of a single network as a pickled state dictionary plus optionally a Torchscript object and/or an ONNX object. Additional JSON files are included to store metadata about the model, information for constructing training, inference, and post-processing transform sequences, plain-text description, legal information, and other data the model creator wishes to include. + +This specification defines the directory structure a bundle must have and the necessary files it must contain. Additional files may be included and the directory packaged into a zip file or included as extra files directly in a Torchscript file. + +Directory Structure +=================== + +A MONAI Bundle is defined primarily as a directory with a set of specifically named subdirectories containing the model, metadata files and license. The root directory should be named for the model, given as "ModelName" in this example, and should contain the following structure: + +:: + + ModelName + ┣━ LICENSE + ┣━ configs + ┃ ┗━ metadata.json + ┣━ models + ┃ ┣━ model.pt + ┃ ┣━ *model.ts + ┃ ┗━ *model.onnx + ┗━ docs + ┣━ *README.md + ┗━ *license.txt + + +The following files are **required** to be present with the given filenames for the directory to define a valid bundle: + +* **LICENSE**: a license for the software itself comprising the configuration files and model weights. +* **metadata.json**: metadata information in JSON format relating to the type of model, definition of input and output tensors, versions of the model and used software, and other information described below. +* **model.pt**: the state dictionary of a saved model, the information to instantiate the model must be found in the metadata file. + +The following files are optional but must have these names in the directory given above: + +* **model.ts**: the Torchscript saved model if the model is compatible with being saved correctly in this format. +* **model.onnx**: the ONNX model if the model is compatible with being saved correctly in this format. +* **README.md**: plain-language information on the model, how to use it, author information, etc. in Markdown format. +* **license.txt**: software license attached to the data, can be left blank if no license needed. + +Other files can be included in any of the above directories. For example, `configs` can contain further configuration JSON or YAML files to define scripts for training or inference, overriding configuration values, environment definitions such as network instantiations, and so forth. One common file to include is `inference.json` which is used to define a basic inference script which uses input files with the stored network to produce prediction output files. + +Archive Format +============== + +The bundle directory and its contents can be compressed into a zip file to constitute a single file package. When unzipped into a directory this file will reproduce the above directory structure, and should itself also be named after the model it contains. For example, `ModelName.zip` would contain at least `ModelName/configs/metadata.json` and `ModelName/models/model.pt`, thus when unzipped would place files into the directory `ModelName` rather than into the current working directory. + +The Torchscript file format is also just a zip file with a specific structure. When creating such an archive with `save_net_with_metadata` a MB-compliant Torchscript file can be created by including the contents of `metadata.json` as the `meta_values` argument of the function, and other files included as `more_extra_files` entries. These will be stored in a `extras` directory in the zip file and can be retrieved with `load_net_with_metadata` or with any other library/tool that can read zip data. In this format the `model.*` files are obviously not needed but `README.md` and `license.txt` as well as any others provided can be added as more extra files. + +The `bundle` submodule of MONAI contains a number of command line programs. To produce a Torchscript bundle use `ckpt_export` with a set of specified components such as the saved weights file and metadata file. Config files can be provided as JSON or YAML dictionaries defining Python constructs used by the `ConfigParser`, however regardless of format the produced bundle Torchscript object will store the files as JSON. + +metadata.json File +================== + +This file contains the metadata information relating to the model, including what the shape and format of inputs and outputs are, what the meaning of the outputs are, what type of model is present, and other information. The JSON structure is a dictionary containing a defined set of keys with additional user-specified keys. The mandatory keys are as follows: + +* **version**: version of the stored model, this allows multiple versions of the same model to be differentiated. Versions should follow semantic versioning and contain only characters valid in filenames as we may include the version to construct bundle file name. +* **monai_version**: version of MONAI the bundle was generated on, later versions expected to work. +* **pytorch_version**: version of Pytorch the bundle was generated on, later versions expected to work. +* **numpy_version**: version of Numpy the bundle was generated on, later versions expected to work. +* **required_packages_version**: dictionary relating required package names to their versions. These are packages in addition to the base requirements of MONAI which this bundle absolutely needs. For example, if the bundle must load Nifti files the Nibabel package will be required. +* **task**: plain-language description of what the model is meant to do. +* **description**: longer form plain-language description of what the model is, what it does, etc. +* **authors**: state author(s) of the model. +* **copyright**: state model copyright. +* **network_data_format**: defines the format, shape, and meaning of inputs and outputs to the (primary) model, contains keys "inputs" and "outputs" relating named inputs/outputs to their format specifiers (defined below). There is also an optional "post_processed_outputs" key stating the format of "outputs" after postprocessing transforms are applied, this is used to describe the final output from the bundle if it varies from the raw network output. These keys can also relate to primitive values (number, string, boolean), instead of the tensor format specified below. + +Tensor format specifiers are used to define input and output tensors and their meanings, and must be a dictionary containing at least these keys: + +* **type**: what sort of data the tensor represents: "image" for any spatial regular data whether an actual image or just data with that sort of shape, "series" for (time-) sequences of values such as signals, "tuples" for a series of items defined by a known number of values such as N-sized points in ND space, "probabilities" for a set of probabilities such as classifier output, this useful for interpreting what the dimensions and shape of the data represent and allow users to guess how to plot the data +* **format**: what format of information is stored, see below for list of known formats +* **modality**: describes the modality, protocol type, sort of capturing technology, or other property of the data not described by either it's type or format, known modalities are "MR", "CT", "US", "EKG", but can include any custom types or protocol types (eg. "T1"), default value is "n/a" +* **num_channels**: number of channels the tensor has, assumed channel dimension first +* **spatial_shape**: shape of the spatial dimensions of the form "[H]", "[H, W]", or "[H, W, D]", see below for possible values of H, W, and D +* **dtype**: data type of tensor, eg. "float32", "int32" +* **value_range**: minimum and maximum values the input data is expected to have of the form "[MIN, MAX]" or "[]" if not known +* **is_patch_data**: "true" if the data is a patch of an input/output tensor or the entirely of the tensor, "false" otherwise +* **channel_def**: dictionary relating channel indices to plain-language description of what the channel contains + +Optional keys: + +* **changelog**: dictionary relating previous version names to strings describing the version. +* **intended_use**: what the model is to be used for, ie. what task it accomplishes. +* **data_source**: description of where training/validation can be sourced. +* **data_type**: type of source data used for training/validation. +* **references**: list of published referenced relating to the model. +* **supported_apps**: list of supported applications which use bundles, eg. 'monai-label' would be present if the bundle is compatible with MONAI Label applications. +* **\*_data_format**: defines the format, shape, and meaning of inputs and outputs to additional models which are secondary to the main model. This contains the same sort of information as **network_data_format** which describes networks providing secondary functionality, eg. a localisation network used to identify ROI in an image for cropping before data is sent to the primary network of this bundle. + +The format for tensors used as inputs and outputs can be used to specify semantic meaning of these values, and later is used by software handling bundles to determine how to process and interpret this data. There are various types of image data that MONAI is uses, and other data types such as point clouds, dictionary sequences, time signals, and others. The following list is provided as a set of supported definitions of what a tensor "format" is but is not exhaustive and users can provide their own which would be left up to the model users to interpret: + +* **magnitude**: ND field of continuous magnitude values with one or more channels, eg. MR T1 image having 1 channel or natural RGB image with 3 channels +* **hounsfield**: ND field of semi-categorical values given in Hounsfield, eg. CT image +* **kspace**: 2D/3D fourier transform image associated with MR imaging +* **raw**: ND field of values considered unprocessed from an image acquisition device, eg. directly from a MR scanner without reconstruction or other processing +* **labels**: ND categorical image with N one-hot channels for N-class segmentation/labels, the "channel_def" states in plain language what the interpretation of each channel is, for each pixel/voxel the predicted label is the index of the largest channel value +* **classes**: ND categorical image with N channels for N-class classes, the "channel_def" states in plain language what the interpretation of each channel is, this permits multi-class labeling as the channels need not be one-hot encoded +* **segmentation**: ND categorical image with one channel assigning each pixel/voxel to a label described in "channel_def" +* **points**: list of points/nodes/coordinates/vertices/vectors in ND space, so having a shape of [I, N] for I points with N dimensions +* **normals**: list of vectors (possible of unit length) in ND space, so having a shape of [I, N] for I vectors with N dimensions +* **indices**: list of indices into a vertices array and/or other array representing a set of shapes, so having a shape of [I, N] for I shapes defined by N values +* **sequence**: time-related sequence of values having one or more channels, such as a signal or dictionary lookup sentence, so having a shape of [C, N] for C channels of data at N time points. +* **latent**: ND tensor of data from the latent space from some layer of a network +* **gradient**: ND tensor of gradients from some layer of a network + +Spatial shape definition can be complex for models accepting inputs of varying shapes, especially if there are specific conditions on what those shapes can be. Shapes are specified as lists of either positive integers for fixed sizes or strings containing expressions defining the condition a size depends on. This can be "*" to mean any size, or use an expression with Python mathematical operators and one character variables to represent dependence on an unknown quantity. For example, "2**p" represents a size which must be a power of 2, "2**p*n" must be a multiple of a power of 2. Variables are shared between dimension expressions, a spatial shape example: `["*", "16*n", "2**p*n"]`. + +The download link of a JSON schema to verify this file can be found within it with key "schema". + +An example JSON metadata file: + +:: + + { + "schema": "https://github.com/Project-MONAI/MONAI-extra-test-data/releases/download/0.8.1/meta_schema_20220324.json", + "version": "0.1.0", + "changelog": { + "0.1.0": "complete the model package", + "0.0.1": "initialize the model package structure" + }, + "monai_version": "0.9.0", + "pytorch_version": "1.10.0", + "numpy_version": "1.21.2", + "required_packages_version": {"nibabel": "3.2.1"}, + "task": "Decathlon spleen segmentation", + "description": "A pre-trained model for volumetric (3D) segmentation of the spleen from CT image", + "authors": "MONAI team", + "copyright": "Copyright (c) MONAI Consortium", + "data_source": "Task09_Spleen.tar from http://medicaldecathlon.com/", + "data_type": "dicom", + "image_classes": "single channel data, intensity scaled to [0, 1]", + "label_classes": "single channel data, 1 is spleen, 0 is everything else", + "pred_classes": "2 channels OneHot data, channel 1 is spleen, channel 0 is background", + "eval_metrics": { + "mean_dice": 0.96 + }, + "intended_use": "This is an example, not to be used for diagnostic purposes", + "references": [ + "Xia, Yingda, et al. '3D Semi-Supervised Learning with Uncertainty-Aware Multi-View Co-Training.' arXiv preprint arXiv:1811.12506 (2018). https://arxiv.org/abs/1811.12506.", + "Kerfoot E., Clough J., Oksuz I., Lee J., King A.P., Schnabel J.A. (2019) Left-Ventricle Quantification Using Residual U-Net. In: Pop M. et al. (eds) Statistical Atlases and Computational Models of the Heart. Atrial Segmentation and LV Quantification Challenges. STACOM 2018. Lecture Notes in Computer Science, vol 11395. Springer, Cham. https://doi.org/10.1007/978-3-030-12029-0_40" + ], + "network_data_format":{ + "inputs": { + "image": { + "type": "image", + "format": "magnitude", + "modality": "MR", + "num_channels": 1, + "spatial_shape": [160, 160, 160], + "dtype": "float32", + "value_range": [0, 1], + "is_patch_data": false, + "channel_def": {"0": "image"} + } + }, + "outputs":{ + "pred": { + "type": "image", + "format": "labels", + "num_channels": 2, + "spatial_shape": [160, 160, 160], + "dtype": "float32", + "value_range": [], + "is_patch_data": false, + "channel_def": {"0": "background", "1": "spleen"} + } + } + } + } diff --git a/MONAI/source/docs/source/metrics.rst b/MONAI/source/docs/source/metrics.rst new file mode 100644 index 0000000000000000000000000000000000000000..88fbea7ff0c9bc05fecb736398e17cd63207cfd6 --- /dev/null +++ b/MONAI/source/docs/source/metrics.rst @@ -0,0 +1,188 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. _metrics: + +Metrics +======= +.. currentmodule:: monai.metrics + +`FROC` +------ +.. autofunction:: compute_fp_tp_probs +.. autofunction:: compute_froc_curve_data +.. autofunction:: compute_froc_score + +`Metric` +-------- +.. autoclass:: Metric + :members: + +`Variance` +-------------- +.. autofunction:: compute_variance + +.. autoclass:: VarianceMetric + :members: + +`LabelQualityScore` +-------------------- +.. autofunction:: label_quality_score + +.. autoclass:: LabelQualityScore + :members: + +`IterationMetric` +----------------- +.. autoclass:: IterationMetric + :members: + +`Cumulative` +------------ +.. autoclass:: Cumulative + :members: + +`CumulativeIterationMetric` +--------------------------- +.. autoclass:: CumulativeIterationMetric + :members: + +`LossMetric` +------------ +.. autoclass:: LossMetric + :members: + +`Mean Dice` +----------- +.. autoclass:: DiceMetric + :members: + +.. autoclass:: DiceHelper + :members: + +`Mean IoU` +---------- +.. autofunction:: compute_iou + +.. autoclass:: MeanIoU + :members: + +`Generalized Dice Score` +------------------------ +.. autofunction:: compute_generalized_dice + +.. autoclass:: GeneralizedDiceScore + :members: + +`Area under the ROC curve` +-------------------------- +.. autofunction:: compute_roc_auc + +.. autoclass:: ROCAUCMetric + :members: + +`Average Precision` +------------------- +.. autofunction:: compute_average_precision + +.. autoclass:: AveragePrecisionMetric + :members: + +`Confusion matrix` +------------------ +.. autofunction:: get_confusion_matrix +.. autofunction:: compute_confusion_matrix_metric + +.. autoclass:: ConfusionMatrixMetric + :members: + +`Hausdorff distance` +-------------------- +.. autofunction:: compute_hausdorff_distance + +.. autoclass:: HausdorffDistanceMetric + :members: + +`Average surface distance` +-------------------------- +.. autofunction:: compute_average_surface_distance + +.. autoclass:: SurfaceDistanceMetric + :members: + +`Surface dice` +-------------- +.. autofunction:: compute_surface_dice + +.. autoclass:: SurfaceDiceMetric + :members: + +`PanopticQualityMetric` +----------------------- +.. autofunction:: compute_panoptic_quality + +.. autoclass:: PanopticQualityMetric + :members: + +`Mean squared error` +-------------------- +.. autoclass:: MSEMetric + :members: + +`Mean absolute error` +--------------------- +.. autoclass:: MAEMetric + :members: + +`Root mean squared error` +------------------------- +.. autoclass:: RMSEMetric + :members: + +`Peak signal to noise ratio` +---------------------------- +.. autoclass:: PSNRMetric + :members: + +`Structural similarity index measure` +------------------------------------- +.. autoclass:: monai.metrics.regression.SSIMMetric + +`Multi-scale structural similarity index measure` +------------------------------------------------- +.. autoclass:: MultiScaleSSIMMetric + +`Fréchet Inception Distance` +------------------------------ +.. autofunction:: compute_frechet_distance + +.. autoclass:: FIDMetric + :members: + +`Maximum Mean Discrepancy` +------------------------------ +.. autofunction:: compute_mmd + +.. autoclass:: MMDMetric + :members: + +`Cumulative average` +-------------------- +.. autoclass:: CumulativeAverage + :members: + +`Metrics reloaded binary` +------------------------- +.. autoclass:: MetricsReloadedBinary + :members: + +`Metrics reloaded categorical` +------------------------------ +.. autoclass:: MetricsReloadedCategorical + :members: + + + +Utilities +--------- +.. automodule:: monai.metrics.utils + :members: diff --git a/MONAI/source/docs/source/modules.md b/MONAI/source/docs/source/modules.md new file mode 100644 index 0000000000000000000000000000000000000000..b2e95658bf7df0efafe1beb8a3e9fb78110a403d --- /dev/null +++ b/MONAI/source/docs/source/modules.md @@ -0,0 +1,331 @@ +# Modules + +MONAI aims at facilitating deep learning in medical image analysis at multiple granularities. This document provides an +overview of the modules and highlights the key capabilities. + +The core codebase is designed as a library of lightweight, flexible, and comprehensive APIs for users with varying expertise. +The building blocks are made easy to understand and use, they are carefully decoupled and can be readily integrated +into existing PyTorch programs and larger systems. By leveraging the workflow and bundle APIs, users can also quickly +set up efficient and robust model training or evaluation pipelines for various domain-specific applications. + +The overall architecture and modules are shown in the following figure: + +![architecture overview](../images/arch_modules.png) + +* [I/O, processing and augmentation](#i-o-processing-and-augmentation) +* [Datasets and Data Loading](#datasets-and-data-loading) +* [Differentiable components, networks, losses and optimizers](#differentiable-components-networks-losses-and-optimizers) +* [Evaluation](#evaluation) +* [Visualization](#visualization) +* [Workflows](#workflows) +* [Bundle](#bundle) +* [Federated Learning](#federated-learning) +* [Auto3dseg](#auto3dseg) +* [GPU acceleration, performance profiling and optimization](#gpu-acceleration-performance-profiling-and-optimization) + +## I/O, processing and augmentation +Medical images require specialized methods for I/O, preprocessing and augmentation. They often follow specific formats, +are handled with specific protocols, and the data arrays are often high-dimensional. +[`monai.transforms`](https://github.com/Project-MONAI/MONAI/tree/dev/monai/transforms) and +[`monai.data`](https://github.com/Project-MONAI/MONAI/tree/dev/monai/data) modules include a set of domain-specific APIs +for various deep learning applications: + +### Transforms with data in array and dictionary styles + +![3d transform examples](../images/affine.png) + +This enables basic image transformations, as well as more complex preprocessing pipelines such as synchronized operations +across different modalities and model supervision inputs. [[array and dict examples]](https://github.com/Project-MONAI/tutorials/tree/main/3d_segmentation/torch) + +### Various image patch-based sampling mechanisms + +![2d transform examples](../images/medical_transforms.png) + +Advanced patch sampling methods are implemented for selective preprocessing, such as weighted, class-balanced sampling +from user-specified sampling weight maps. +The output can be in a sequence or iterator pattern which allows for different types of shuffling strategies. + +### Image IO with third-party library integrations + +Several backends are built-in and can support various formats. It is easily extensible for customized format readers. + +### monai.data.MetaTensor + +Core data structure combines PyTorch native Tensor APIs with metadata handling, +so that the deep learning models and pipelines can readily incorporate the meta information. [[MetaTensor]](https://colab.research.google.com/drive/1T4iAys-cC2qL80oJkIbAXAPlWNPwp4H7) + +### GPU-based accelerations + +Implementations are provided to ensure optimal usage of the underlying hardware resources. [[fast training guide]](https://github.com/Project-MONAI/tutorials/blob/main/acceleration) + +### Determinism and reproducibility + +They can be achieved with fine-level of local controls via the `Randomizable` API as well as globally +using `set_determinism`. + +### Decollating and invertible transforms + +![invert transform](../images/invert_transforms.png) +The mini-batch data output from a model can be decollated, post-processed independently, including inverting +the outputs to an earlier step of the preprocessing according to the tracked metadata and applied operations. +[[inverse transform demo]](https://github.com/Project-MONAI/tutorials/blob/main/modules/inverse_transforms_and_test_time_augmentations.ipynb) + +### Enhanced usability + +Additionally, utilities such as `DataStats` transform, `dev_collate`, and [visualization +methods](https://github.com/Project-MONAI/tutorials/blob/main/modules/transform_visualization.ipynb) are provided as +extensions to PyTorch for improved overall debugability. + +## Datasets and Data Loading +Following PyTorch's design pattern, MONAI extends the `Dataset` and `DataLoader` APIs as major enhancements in terms of +domain-specific usability and pipeline performance. + +### Cache IO and transforms data to accelerate training + +Data-driven methods require many (potentially thousands of) epochs of training data reading and preprocessing. MONAI +provides multi-threaded cache-based datasets to accelerate the process [[Datasets experiment]](https://github.com/Project-MONAI/tutorials/blob/main/acceleration/dataset_type_performance.ipynb). The +cache can be persistent and dynamic (`SmartCacheDataset`) and reused across different experiments [[SmartCache example]](https://github.com/Project-MONAI/tutorials/blob/main/acceleration/distributed_training/unet_training_smartcache.py). +The following figure illustrates the training speedup compared with a regular PyTorch program. + +![cachedataset speed](../images/datasets_speed.png) + +### `ThreadDataLoader` vs. `DataLoader` + +If the transforms are light-weighted, especially when we cache all the data in RAM, the multiprocessing of PyTorch +`DataLoader` may cause unnecessary IPC time and decrease GPU utilization. MONAI provides `ThreadDataLoader` which +executes the transforms in a separate thread: + +![threaddataloader](../images/threaddataloader.png) + +a `ThreadDataLoader` example is within the [Spleen fast training tutorial](https://github.com/Project-MONAI/tutorials/blob/main/acceleration/fast_training_tutorial.ipynb). + +### Public datasets + +To quickly get started with popular training data, MONAI provides several ready-to-integrate Dataset classes +(such as `MedNISTDataset`, `DecathlonDataset`, [`TciaDataset`](https://github.com/Project-MONAI/tutorials/blob/main/modules/tcia_dataset.ipynb)), which include data downloading, and support training/evaluation splits generation with transforms. +[[Public datasets tutorial]](https://github.com/Project-MONAI/tutorials/blob/main/modules/public_datasets.ipynb) +The common workflow of predefined datasets: + +![pre-defined dataset](../images/dataset_progress.png) + +### Dataset type extensions + +Other extensions of the `Dataset` API include: `ZipDataset` for associating multiple data sources, `PatchDataset` for +handling both image- and patch-level preprocessing, `CSVDataset` for multi-modal inputs, and `partition_dataset` for +cross-validation data preparations. + +## Differentiable components, networks, losses and optimizers + +Some deep neural network architectures have shown to be particularly effective for medical imaging analysis tasks. +MONAI implements reference networks with the aim of both flexibility and code readability. + +### Predefined layers and blocks + +Network layers and blocks are in general implemented to be compatible with spatial 1D, 2D and 3D inputs. +Users can easily integrate the layers, blocks and networks as part of their customised pipelines. +Various utilities are provided to leverage the existing model weights, e.g. [from a bundle in MONAI model-zoo](https://github.com/Project-MONAI/tutorials/tree/main/model_zoo). + +### C++/CUDA optimized modules + +To further accelerate the domain-specific routines, MONAI C++/CUDA implementation is introduced as extensions of the PyTorch native implementations. +MONAI provides the modules using [the two ways of building C++ extensions from PyTorch](https://pytorch.org/tutorials/advanced/cpp_extension.html#custom-c-and-cuda-extensions): +- via `setuptools`, for modules including `Resampler`, `Conditional random field (CRF)`, `Fast bilateral filtering using the permutohedral lattice`. +- via just-in-time (JIT) compilation, for the `Gaussian mixtures` module. This approach allows for dynamic optimisation according to the user-specified parameters and local system environments. +The following figure shows results of MONAI's Gaussian mixture models applied to tissue and surgical tools segmentation: +![Gaussian mixture models as a postprocessing step](../images/gmm_feature_set_comparison_s.png) + + +### Losses and optimizers + +Commonly used loss functions for various applications are (re-)implemented from the literature, such as `DiceLoss`, `GeneralizedDiceLoss`, `TverskyLoss`, `DiceFocalLoss`. +The numerical optimizations and relevant utilities include `Novograd` and `LearningRateFinder`. +The following figure shows a learning rate search process. + +![learning rate finder plot](../images/lr_finder.png) + +## Evaluation +To run model inferences and evaluate the model quality, MONAI provides reference implementations for the relevant +widely-used approaches. Currently, several popular evaluation metrics and inference patterns are included: + +### Sliding window inference + +For model inferences on large volumes, the sliding window approach is a popular choice to achieve high performance while +having flexible memory requirements (_alternatively, please check out the latest research on [model parallel +training](https://github.com/Project-MONAI/research-contributions/tree/main/lamp-automated-model-parallelism). It also supports +`overlap` and `blending_mode` configurations to handle the overlapped windows for better performances. + +![sliding window scheme](../images/sliding_window.png) + +### Metrics for medical tasks + +Various useful evaluation metrics have been implemented to measure the quality of medical image specific models. +These include `Mean Dice`, `ROCAUC`, `Confusion Matrices`, `Hausdorff +Distance`, `Surface Distance`, `Occlusion Sensitivity`. +The APIs also support [multi-processing computation](https://github.com/Project-MONAI/tutorials/blob/main/modules/compute_metric.py). + +### Report generation +`MetricsSaver` is provided to write the final metric summary report: `mean`, `median`, `max`, `min`, `percentile`, `std`: + +![metrics report example](../images/metrics_report.png) + +## Visualization +Beyond the simple point and curve plotting, intuitive interfaces are provided to visualize multidimensional data as GIF animations in TensorBoard. This could provide a quick qualitative assessment of the model by visualizing, for example, the volumetric inputs, segmentation maps, and intermediate feature maps. A runnable example with visualization is available at [UNet training example](https://github.com/Project-MONAI/tutorials/blob/main/3d_segmentation/torch/unet_training_dict.py). To work with ignite program, MONAI also provides several ignite handlers to visualize training curve and metrics with `TensorBoard` or `MLFlow`, more details is available in [TensorBoard and MLFlow handlers example](https://github.com/Project-MONAI/tutorials/blob/main/3d_segmentation/unet_segmentation_3d_ignite.ipynb). + +To easily visualize a 3D image as frames of 2D images, MONAI provides the utility `matshow3d` based on `matplotlib` library. It can plot frames of image for the specified dimension, showing a spleen 3D image as example: +`matshow3d(volume=image, figsize=(100, 100), every_n=10, frame_dim=-1 show=True, cmap="gray")` + +![matshow3d example](../images/matshow3d.png) + +MONAI also provides the `blend_images` utility to blend the `image` and `label` to an RGB color image to better visualize the segmentation regions with the specified `cmap` mode and weights, etc. Showing a spleen segmentation `image` and the corresponding `label` as example: + +![blend example](../images/blend.png) + +For more details of `TensorBoard utility`, `matshow3d` and `blend_images`, please check the [visualization tutorial](https://github.com/Project-MONAI/tutorials/blob/main/modules/transform_visualization.ipynb). + +And to visualize the class activation mapping for a trained classification model, MONAI provides CAM, GradCAM, GradCAM++ APIs for both 2D and 3D models: + +![CAM visualization example](../images/cam.png) + +The above example is generated by computing [GradCAM/GradCAM++ from a lung CT lesion classification model](https://github.com/Project-MONAI/tutorials/tree/main/modules/interpretability). + +## Workflows + +MONAI engines and workflows enable quick start of training and evaluation experiments. + +These features decouple the domain-specific components and the generic machine learning processes. +They also provide a set of unify APIs for higher level applications (such as AutOML, Federated Learning). +The trainers and evaluators of the workflows are compatible with pytorch-ignite `Engine` and `Event-Handler` mechanism. + +### General workflows pipeline + +The workflow and some of MONAI event handlers are shown as below [[Workflow examples]](https://github.com/Project-MONAI/tutorials/tree/main/modules/engines): + +![workflow pipeline](../images/workflows.png) + + +### EnsembleEvaluator + +A typical ensemble procoess is implemented as a ready-to-use workflow [[Cross validation and model ensemble tutorial]](https://github.com/Project-MONAI/tutorials/blob/main/modules/cross_validation_models_ensemble.ipynb): +1. Split all the training dataset into K folds. +2. Train K models with every K-1 folds data. +3. Execute inference on the test data with all the K models. +4. Compute the average values with weights or vote the most common value as the final result. + +![model ensemble](../images/models_ensemble.png) + + +### Decollate batch data for flexible post-processings + +`decollate batch` is introduced since MONAI v0.6, which simplifies the post-processing transforms and provides flexible following operations on a batch of data with various data shapes. It can decollate batched data (e.g. model predictions) into a list of tensors, for the benefits such as: +1. enabling postprocessing transforms for each item independently -- randomised transforms could be applied differently for each predicted item in a batch. +2. simplifying the transform APIs and reducing the input validation burdens because both the preprocessing and postprocessing transforms now only need to support the "channel-first" input format. +3. enabling the `Invertd` transform for the predictions and the inverted data with different shapes, as the data items are in a list, not stacked in a single tensor. +4. allowing for both batch-first tensor and list of channel-first tensors in a flexible metric computation. [[decollate batch tutorial]](https://github.com/Project-MONAI/tutorials/blob/main/modules/decollate_batch.ipynb) + +A typical process of `decollate batch` is illustrated as follows (with a `batch_size=N` model predictions and labels as an example): + +![decollate_batch](../images/decollate_batch.png) + +### Easy to integrate into popular workflows + +Except for the pytorch-ignite based `monai.engines`, most of the MONAI modules could be used independently or combined +with other software packages. For example, MONAI can be easily integrated into popular frameworks such as +[PyTorch-Lightning](https://github.com/Project-MONAI/tutorials/blob/main/3d_segmentation/spleen_segmentation_3d_lightning.ipynb) +and [MLflow](https://github.com/Project-MONAI/tutorials/blob/main/experiment_management/spleen_segmentation_mlflow.ipynb). + +## Bundle + +The objective of a MONAI bundle is to define a packaged model which includes the critical information necessary to allow +users and programs to understand how the model is used and for what purpose. A bundle includes the stored weights of a +single network as a pickled state dictionary plus optionally a Torchscript object and/or an ONNX object. Additional JSON +files are included to store metadata about the model, information for constructing training, inference, and +post-processing transform sequences, plain-text description, legal information, and other data the model creator wishes +to include. More details are available at [bundle specification](https://monai.readthedocs.io/en/latest/mb_specification.html). + +The key benefits of bundle are to define the model package and support building Python-based workflows via structured configurations: +- Self-contained model package include all the necessary information. +- Structured config can be used to easily reconstruct or prototype deep learning workflows. +- Config files can provide good readability and usability by separating parameter settings from the Python code. +- Config files can describe flexible workflow and components, allows for different low-level Python implementations +- Learning paradigms at a higher level such as federated learning and AutoML can be decoupled from the component details. + +A typical bundle example can include: +``` + ModelName + ┣━ configs + ┃ ┗━ metadata.json + ┣━ models + ┃ ┣━ model.pt + ┃ ┣━ *model.ts + ┃ ┗━ *model.onnx + ┗━ docs + ┣━ *README.md + ┗━ *license.txt +``` +Details about the bundle config definition and syntax & examples are at [config syntax](https://monai.readthedocs.io/en/latest/config_syntax.html). +A step-by-step [get started](https://github.com/Project-MONAI/tutorials/blob/main/bundle/README.md) tutorial notebook can help users quickly set up a bundle. [[bundle examples](https://github.com/Project-MONAI/tutorials/tree/main/bundle), [model-zoo](https://github.com/Project-MONAI/model-zoo)] + +## Federated Learning + +![federated-learning](../images/federated.svg) + +Using the MONAI bundle configurations, we can use MONAI's [`MonaiAlgo`](https://monai.readthedocs.io/en/latest/fl.html#monai.fl.client.MonaiAlgo) +class, an implementation of the abstract [`ClientAlgo`](https://monai.readthedocs.io/en/latest/fl.html#clientalgo) class for federated learning (FL), +to execute bundles from the [MONAI model zoo](https://github.com/Project-MONAI/model-zoo). +Note that [`ClientAlgo`](https://monai.readthedocs.io/en/latest/fl.html#clientalgo) is provided as an abstract base class for +defining an algorithm to be run on any federated learning platform. +[`MonaiAlgo`](https://monai.readthedocs.io/en/latest/fl.html#monai.fl.client.MonaiAlgo) implements the main functionalities needed +to run federated learning experiments, namely `train()`, `get_weights()`, and `evaluate()`, that can be run using single- or multi-GPU training. +On top, it provides implementations for life-cycle management of the component such as `initialize()`, `abort()`, and `finalize()`. +The MONAI FL client also allows computing summary data statistics (e.g., intensity histograms) on the datasets defined in the bundle configs +using the [`MonaiAlgoStats`](https://monai.readthedocs.io/en/latest/fl.html#monai.fl.client.MonaiAlgoStats) class. +These statistics can be shared and visualized on the FL server. +[NVIDIA FLARE](https://github.com/NVIDIA/NVFlare), the federated learning platform developed by NVIDIA, has already built [the integration piece](https://github.com/NVIDIA/NVFlare/tree/2.2/integration/monai) +with [`ClientAlgo`](https://monai.readthedocs.io/en/latest/fl.html#clientalgo) to allow easy experimentation with MONAI bundles within their federated environment. +Our [[federated learning tutorials]](https://github.com/Project-MONAI/tutorials/tree/main/federated_learning/nvflare) shows +examples of single- & multi-GPU training and federated statistics workflows. + +## Auto3dseg + +![auto3dseg](../images/auto3dseg.png) + +[Auto3DSeg](https://project-monai.github.io/apps/auto3dseg.html) is a comprehensive solution for large-scale 3D medical image segmentation. +It leverages the latest advances in MONAI +and GPUs to efficiently develop and deploy algorithms with state-of-the-art performance. +It first analyzes the global information such as intensity, dimensionality, and resolution of the dataset, +then generates algorithms in MONAI bundle format based on data statistics and [algorithm templates](https://github.com/Project-MONAI/research-contributions/tree/main/auto3dseg). +Next, all algorithms initiate model training to obtain checkpoints with the best validation performance. +Finally, the ensemble module selects the algorithms via ranking trained checkpoints and creates ensemble predictions. + +The solution offers different levels of user experience for beginners and advanced researchers. +It has been tested on large-scale 3D medical imaging datasets in different modalities. + +## GPU acceleration, performance profiling and optimization + +MONAI provides state-of-the-art performance optimization methods including: + +### Auto mixed precision (AMP) + +Simply set `amp=True/False` in `SupervisedTrainer` or `SupervisedEvaluator` during training or evaluation to enable/disable AMP +Example benchmark results are as follows [[AMP training tutorial]](https://github.com/Project-MONAI/tutorials/blob/main/acceleration/automatic_mixed_precision.ipynb): + +training with AMP ON/OFF on a NVIDIA V100 GPU with CUDA 11 and PyTorch 1.6: + +![amp v100 results](../images/amp_training_v100.png) + +training with AMP ON/OFF on a NVIDIA A100 GPU with CUDA 11 and PyTorch 1.6: + +![amp a100 results](../images/amp_training_a100.png) + +Several tools including `DLProf`, `Nsight`, `NVTX` and `NVML` can be used with MONAI to identify the performance bottleneck. [[profiling tutorial]](https://github.com/Project-MONAI/tutorials/blob/main/performance_profiling/radiology/profiling_train_base_nvtx.md) + +### Distributed training + +The distributed data-parallel APIs of MONAI are compatible with the native PyTorch distributed module, pytorch-ignite distributed module, Horovod, XLA, and the SLURM platform. +[[distributed training tutorial]](https://github.com/Project-MONAI/tutorials/blob/main/acceleration/distributed_training/brats_training_ddp.py) + +![distributed training results](../images/brats_distributed.png) + +The [fast training tutorial](https://github.com/Project-MONAI/tutorials/blob/main/acceleration/fast_training_tutorial.ipynb) +combines `AMP` with `CacheDataset`, `GPU cache`, `GPU transforms`, `ThreadDataLoader`, tuning of networks and optimizers, can achieve substantial speedup compared +with a PyTorch regular implementation. diff --git a/MONAI/source/docs/source/networks.rst b/MONAI/source/docs/source/networks.rst new file mode 100644 index 0000000000000000000000000000000000000000..de0aece3f7afa2cf94768df16fbdb35be5403ab7 --- /dev/null +++ b/MONAI/source/docs/source/networks.rst @@ -0,0 +1,812 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. _networks: + +Network architectures +===================== + +Blocks +------ +.. automodule:: monai.networks.blocks +.. currentmodule:: monai.networks.blocks + +`ADN` +~~~~~ +.. autoclass:: ADN + :members: + +`Convolution` +~~~~~~~~~~~~~ +.. autoclass:: Convolution + :members: + +`CRF` +~~~~~ +.. autoclass:: CRF + :members: + +`ResidualUnit` +~~~~~~~~~~~~~~ +.. autoclass:: ResidualUnit + :members: + +`Swish` +~~~~~~~ +.. autoclass:: Swish + :members: + +`MemoryEfficientSwish` +~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: MemoryEfficientSwish + :members: + +`FPN` +~~~~~ +.. autoclass:: ExtraFPNBlock + :members: +.. autoclass:: FeaturePyramidNetwork + :members: +.. autoclass:: LastLevelMaxPool + :members: +.. autoclass:: LastLevelP6P7 + :members: +.. autoclass:: BackboneWithFPN + :members: + +`Mish` +~~~~~~ +.. autoclass:: Mish + :members: + +`GEGLU` +~~~~~~~ +.. autoclass:: GEGLU + :members: + +`GCN Module` +~~~~~~~~~~~~ +.. autoclass:: GCN + :members: + +`Refinement Module` +~~~~~~~~~~~~~~~~~~~ +.. autoclass:: Refine + :members: + +`FCN Module` +~~~~~~~~~~~~ +.. autoclass:: FCN + :members: + +`Multi-Channel FCN Module` +~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: MCFCN + :members: + +`Dynamic-Unet Block` +~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: UnetResBlock + :members: +.. autoclass:: UnetBasicBlock + :members: +.. autoclass:: UnetUpBlock + :members: +.. autoclass:: UnetOutBlock + :members: + +`DenseBlock` +~~~~~~~~~~~~~ +.. autoclass:: DenseBlock + :members: + +`SegResnet Block` +~~~~~~~~~~~~~~~~~ +.. autoclass:: ResBlock + :members: + +`SABlock Block` +~~~~~~~~~~~~~~~ +.. autoclass:: SABlock + :members: + +`CABlock Block` +~~~~~~~~~~~~~~~ +.. autoclass:: CABlock + :members: + +`FeedForward Block` +~~~~~~~~~~~~~~~~~~~ +.. autoclass:: FeedForward + :members: + +`Squeeze-and-Excitation` +~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: ChannelSELayer + :members: + +`Transformer Block` +~~~~~~~~~~~~~~~~~~~ +.. autoclass:: TransformerBlock + :members: + +`UNETR Block` +~~~~~~~~~~~~~ +.. autoclass:: UnetrBasicBlock + :members: +.. autoclass:: UnetrUpBlock + :members: +.. autoclass:: UnetrPrUpBlock + :members: + +`Residual Squeeze-and-Excitation` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: ResidualSELayer + :members: + +`Squeeze-and-Excitation Block` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: SEBlock + :members: + +`Squeeze-and-Excitation Bottleneck` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: SEBottleneck + :members: + +`Squeeze-and-Excitation Resnet Bottleneck` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: SEResNetBottleneck + :members: + +`Squeeze-and-Excitation ResNeXt Bottleneck` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: SEResNeXtBottleneck + :members: + +`Simple ASPP` +~~~~~~~~~~~~~ +.. autoclass:: SimpleASPP + :members: + +`MaxAvgPooling` +~~~~~~~~~~~~~~~ +.. autoclass:: MaxAvgPool + :members: + +`Upsampling` +~~~~~~~~~~~~ +.. autoclass:: UpSample + :members: +.. autoclass:: Upsample +.. autoclass:: SubpixelUpsample + :members: +.. autoclass:: Subpixelupsample +.. autoclass:: SubpixelUpSample + +`Downsampling` +~~~~~~~~~~~~~~ +.. autoclass:: DownSample + :members: +.. autoclass:: Downsample +.. autoclass:: SubpixelDownsample + :members: +.. autoclass:: Subpixeldownsample +.. autoclass:: SubpixelDownSample + +`Registration Residual Conv Block` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: RegistrationResidualConvBlock + :members: + +`Registration Down Sample Block` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: RegistrationDownSampleBlock + :members: + +`Registration Extraction Block` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: RegistrationExtractionBlock + :members: + +`LocalNet DownSample Block` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: LocalNetDownSampleBlock + :members: + +`LocalNet UpSample Block` +~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: LocalNetUpSampleBlock + :members: + +`LocalNet Feature Extractor Block` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: LocalNetFeatureExtractorBlock + :members: + +`MLP Block` +~~~~~~~~~~~ +.. autoclass:: MLPBlock + :members: + +`Patch Embedding Block` +~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: PatchEmbeddingBlock + :members: + +`FactorizedIncreaseBlock` +~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: FactorizedIncreaseBlock + :members: + +`FactorizedReduceBlock` +~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: FactorizedReduceBlock + :members: + +`P3DActiConvNormBlock` +~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: P3DActiConvNormBlock + :members: + +`ActiConvNormBlock` +~~~~~~~~~~~~~~~~~~~ +.. autoclass:: ActiConvNormBlock + :members: + +`Warp` +~~~~~~ +.. autoclass:: Warp + :members: + +`DVF2DDF` +~~~~~~~~~ +.. autoclass:: DVF2DDF + :members: + +`VarNetBlock` +~~~~~~~~~~~~~ +.. autoclass:: monai.apps.reconstruction.networks.blocks.varnetblock.VarNetBlock + :members: + +N-Dim Fourier Transform +~~~~~~~~~~~~~~~~~~~~~~~~ +.. automodule:: monai.networks.blocks.fft_utils_t +.. autofunction:: monai.networks.blocks.fft_utils_t.fftn_centered_t +.. autofunction:: monai.networks.blocks.fft_utils_t.ifftn_centered_t +.. autofunction:: monai.networks.blocks.fft_utils_t.roll +.. autofunction:: monai.networks.blocks.fft_utils_t.roll_1d +.. autofunction:: monai.networks.blocks.fft_utils_t.fftshift +.. autofunction:: monai.networks.blocks.fft_utils_t.ifftshift + +Layers +------ + +`Factories` +~~~~~~~~~~~ +.. automodule:: monai.networks.layers.factories + +.. autoclass:: monai.networks.layers.LayerFactory + :members: + +.. currentmodule:: monai.networks.layers + +`split_args` +~~~~~~~~~~~~ +.. autofunction:: monai.networks.layers.split_args + +`Dropout` +~~~~~~~~~ +.. automodule:: monai.networks.layers.Dropout + :members: + +`Act` +~~~~~ +.. automodule:: monai.networks.layers.Act + :members: + +`Norm` +~~~~~~ +.. automodule:: monai.networks.layers.Norm + :members: + +`Conv` +~~~~~~ +.. automodule:: monai.networks.layers.Conv + :members: + +`Pad` +~~~~~ +.. automodule:: monai.networks.layers.Pad + :members: + +`Pool` +~~~~~~ +.. automodule:: monai.networks.layers.Pool + :members: + +.. currentmodule:: monai.networks.layers + +`ChannelPad` +~~~~~~~~~~~~ +.. autoclass:: ChannelPad + :members: + +`SkipConnection` +~~~~~~~~~~~~~~~~ +.. autoclass:: SkipConnection + :members: + +`Flatten` +~~~~~~~~~ +.. autoclass:: Flatten + :members: + +`Reshape` +~~~~~~~~~ +.. autoclass:: Reshape + :members: + +`separable_filtering` +~~~~~~~~~~~~~~~~~~~~~ +.. autofunction:: separable_filtering + +`apply_filter` +~~~~~~~~~~~~~~ +.. autofunction:: apply_filter + +`GaussianFilter` +~~~~~~~~~~~~~~~~ +.. autoclass:: GaussianFilter + :members: + +`MedianFilter` +~~~~~~~~~~~~~~ +.. autoclass:: MedianFilter + :members: + +`median_filter` +~~~~~~~~~~~~~~~ +.. autoclass:: median_filter + :members: + +`BilateralFilter` +~~~~~~~~~~~~~~~~~ +.. autoclass:: BilateralFilter + :members: + +`TrainableBilateralFilter` +~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: TrainableBilateralFilter + :members: + +`TrainableJointBilateralFilter` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: TrainableJointBilateralFilter + :members: + +`PHLFilter` +~~~~~~~~~~~ +.. autoclass:: PHLFilter + +`GaussianMixtureModel` +~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: GaussianMixtureModel + +`SavitzkyGolayFilter` +~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: SavitzkyGolayFilter + :members: + +`HilbertTransform` +~~~~~~~~~~~~~~~~~~ +.. autoclass:: HilbertTransform + :members: + +`Affine Transform` +~~~~~~~~~~~~~~~~~~ +.. autoclass:: monai.networks.layers.AffineTransform + :members: + +`grid_pull` +~~~~~~~~~~~ +.. autofunction:: monai.networks.layers.grid_pull + +`grid_push` +~~~~~~~~~~~ +.. autofunction:: monai.networks.layers.grid_push + +`grid_count` +~~~~~~~~~~~~ +.. autofunction:: monai.networks.layers.grid_count + +`grid_grad` +~~~~~~~~~~~ +.. autofunction:: monai.networks.layers.grid_grad + +`LLTM` +~~~~~~ +.. autoclass:: LLTM + :members: + +`ConjugateGradient` +~~~~~~~~~~~~~~~~~~~ +.. autoclass:: ConjugateGradient + :members: + +`Utilities` +~~~~~~~~~~~ +.. automodule:: monai.networks.layers.convutils + :members: +.. automodule:: monai.networks.layers.utils + :members: + + +Nets +---- +.. currentmodule:: monai.networks.nets + +`AHNet` +~~~~~~~ +.. autoclass:: AHNet + :members: + +`DenseNet` +~~~~~~~~~~ +.. autoclass:: DenseNet + :members: + +`DenseNet121` +~~~~~~~~~~~~~ +.. autoclass:: DenseNet121 + +`DenseNet169` +~~~~~~~~~~~~~ +.. autoclass:: DenseNet169 + +`DenseNet201` +~~~~~~~~~~~~~ +.. autoclass:: DenseNet201 + +`DenseNet264` +~~~~~~~~~~~~~ +.. autoclass:: DenseNet264 + +`EfficientNet` +~~~~~~~~~~~~~~ +.. autoclass:: EfficientNet + :members: + +`BlockArgs` +~~~~~~~~~~~ +.. autoclass:: BlockArgs + :members: + +`EfficientNetBN` +~~~~~~~~~~~~~~~~ +.. autoclass:: EfficientNetBN + :members: + +`EfficientNetBNFeatures` +~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: EfficientNetBNFeatures + :members: + +`SegResNet` +~~~~~~~~~~~ +.. autoclass:: SegResNet + :members: + +`SegResNetDS` +~~~~~~~~~~~~~ +.. autoclass:: SegResNetDS + :members: + +`SegResNetDS2` +~~~~~~~~~~~~~~ +.. autoclass:: SegResNetDS2 + :members: + +`SegResNetVAE` +~~~~~~~~~~~~~~ +.. autoclass:: SegResNetVAE + :members: + +`ResNet` +~~~~~~~~ +.. autoclass:: ResNet + :members: + +`ResNetFeatures` +~~~~~~~~~~~~~~~~ +.. autoclass:: ResNetFeatures + :members: + +`SENet` +~~~~~~~ +.. autoclass:: SENet + :members: + +`SENet154` +~~~~~~~~~~ +.. autoclass:: SENet154 + +`SEResNet50` +~~~~~~~~~~~~ +.. autoclass:: SEResNet50 + +`SEResNet101` +~~~~~~~~~~~~~ +.. autoclass:: SEResNet101 + +`SEResNet152` +~~~~~~~~~~~~~ +.. autoclass:: SEResNet152 + +`SEResNext50` +~~~~~~~~~~~~~ +.. autoclass:: SEResNext50 + +`SEResNext101` +~~~~~~~~~~~~~~ +.. autoclass:: SEResNext101 + +`HighResNet` +~~~~~~~~~~~~ +.. autoclass:: HighResNet + :members: +.. autoclass:: HighResBlock + :members: + +`DynUNet` +~~~~~~~~~ +.. autoclass:: DynUNet + :members: +.. autoclass:: DynUnet +.. autoclass:: Dynunet + +`UNet` +~~~~~~ +.. autoclass:: UNet + :members: +.. autoclass:: Unet +.. autoclass:: unet + +`AttentionUnet` +~~~~~~~~~~~~~~~ +.. autoclass:: AttentionUnet + :members: + +`UNETR` +~~~~~~~ +.. autoclass:: UNETR + :members: + +`VISTA3D` +~~~~~~~~~ +.. autoclass:: VISTA3D + :members: + +`SwinUNETR` +~~~~~~~~~~~ +.. autoclass:: SwinUNETR + :members: + +`BasicUNet` +~~~~~~~~~~~ +.. autoclass:: BasicUNet + :members: +.. autoclass:: BasicUnet +.. autoclass:: Basicunet + +`BasicUNetPlusPlus` +~~~~~~~~~~~~~~~~~~~ +.. autoclass:: BasicUNetPlusPlus + :members: +.. autoclass:: BasicUnetPlusPlus +.. autoclass:: BasicunetPlusPlus + +`FlexibleUNet` +~~~~~~~~~~~~~~ +.. autoclass:: FlexibleUNet + :members: + +`VNet` +~~~~~~ +.. autoclass:: VNet + :members: + +`RegUNet` +~~~~~~~~~ +.. autoclass:: RegUNet + :members: + +`GlobalNet` +~~~~~~~~~~~~ +.. autoclass:: GlobalNet + :members: + +`LocalNet` +~~~~~~~~~~~ +.. autoclass:: LocalNet + :members: + +`AutoEncoder` +~~~~~~~~~~~~~ +.. autoclass:: AutoEncoder + :members: + +`VarAutoEncoder` +~~~~~~~~~~~~~~~~ +.. autoclass:: VarAutoEncoder + :members: + +`ViT` +~~~~~ +.. autoclass:: ViT + :members: + +`Restormer` +~~~~~~~~~~~ +.. autoclass:: restormer + :members: + +`ViTAutoEnc` +~~~~~~~~~~~~ +.. autoclass:: ViTAutoEnc + :members: + +`MaskedAutoEncoderViT` +~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: MaskedAutoEncoderViT + :members: + +`FullyConnectedNet` +~~~~~~~~~~~~~~~~~~~ +.. autoclass:: FullyConnectedNet + :members: + +`VarFullyConnectedNet` +~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: VarFullyConnectedNet + :members: + +`Generator` +~~~~~~~~~~~ +.. autoclass:: Generator + :members: + +`Regressor` +~~~~~~~~~~~ +.. autoclass:: Regressor + :members: + +`Classifier` +~~~~~~~~~~~~ +.. autoclass:: Classifier + :members: + +`Discriminator` +~~~~~~~~~~~~~~~ +.. autoclass:: Discriminator + :members: + +`Critic` +~~~~~~~~ +.. autoclass:: Critic + :members: + +`Transchex` +~~~~~~~~~~~~~~~~ +.. autoclass:: Transchex + :members: + +`NetAdapter` +~~~~~~~~~~~~ +.. autoclass:: NetAdapter + :members: + +`TorchVisionFCModel` +~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: TorchVisionFCModel + :members: + +`MILModel` +~~~~~~~~~~ +.. autoclass:: MILModel + :members: + +`DiNTS` +~~~~~~~ +.. autoclass:: DiNTS + :members: + +`TopologyConstruction for DiNTS` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: TopologyConstruction + :members: + +`TopologyInstance for DiNTS` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: TopologyInstance + :members: + +`TopologySearch for DiNTS` +~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: TopologySearch + :members: + +`ComplexUnet` +~~~~~~~~~~~~~ +.. autoclass:: monai.apps.reconstruction.networks.nets.complex_unet.ComplexUnet + :members: + +`CoilSensitivityModel` +~~~~~~~~~~~~~~~~~~~~~~ +.. autoclass:: monai.apps.reconstruction.networks.nets.coil_sensitivity_model.CoilSensitivityModel + :members: + +`e2e-VarNet` +~~~~~~~~~~~~ +.. autoclass:: monai.apps.reconstruction.networks.nets.varnet.VariationalNetworkModel + :members: + +`DAF3D` +~~~~~~~~~~~~ +.. autoclass:: DAF3D + :members: + +`Quicknat` +~~~~~~~~~~~~ +.. autoclass:: Quicknat + :members: + +`VoxelMorph` +~~~~~~~~~~~~ +.. autoclass:: VoxelMorphUNet + :members: + +.. autoclass:: VoxelMorph + :members: + +Utilities +--------- +.. automodule:: monai.networks.utils + :members: + +.. automodule:: monai.apps.reconstruction.networks.nets.utils + :members: + +Noise Schedulers +---------------- +.. automodule:: monai.networks.schedulers +.. currentmodule:: monai.networks.schedulers + +`Scheduler` +~~~~~~~~~~~ +.. autoclass:: Scheduler + :members: + +`NoiseSchedules` +~~~~~~~~~~~~~~~~ +.. autoclass:: NoiseSchedules + :members: + +`DDPMScheduler` +~~~~~~~~~~~~~~~ +.. autoclass:: DDPMScheduler + :members: + +`DDIMScheduler` +~~~~~~~~~~~~~~~ +.. autoclass:: DDIMScheduler + :members: + +`PNDMScheduler` +~~~~~~~~~~~~~~~ +.. autoclass:: PNDMScheduler + :members: + +`RFlowScheduler` +~~~~~~~~~~~~~~~~ +.. autoclass:: RFlowScheduler + :members: diff --git a/MONAI/source/docs/source/optimizers.rst b/MONAI/source/docs/source/optimizers.rst new file mode 100644 index 0000000000000000000000000000000000000000..67cbdc0951565ba10f74eb59f47f7fe7541b8e19 --- /dev/null +++ b/MONAI/source/docs/source/optimizers.rst @@ -0,0 +1,36 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. _optimizers: + +Optimizers +========== +.. currentmodule:: monai.optimizers + +`LearningRateFinder` +-------------------- +.. autoclass:: LearningRateFinder + :members: + +`Novograd` +---------- +.. autoclass:: Novograd + :members: + +`Generate parameter groups` +--------------------------- +.. autofunction:: generate_param_groups + +`ExponentialLR` +--------------- +.. autoclass:: ExponentialLR + :members: + +`LinearLR` +---------- +.. autoclass:: LinearLR + :members: + +`WarmupCosineSchedule` +---------------------- +.. autoclass:: WarmupCosineSchedule + :members: diff --git a/MONAI/source/docs/source/precision_accelerating.md b/MONAI/source/docs/source/precision_accelerating.md new file mode 100644 index 0000000000000000000000000000000000000000..897a6f1652fbc490bb2ef176f2bfa82e13e1682e --- /dev/null +++ b/MONAI/source/docs/source/precision_accelerating.md @@ -0,0 +1,42 @@ +# Precision and Accelerating + +Modern GPU architectures usually can use reduced precision tensor data or computational operations to save memory and increase throughput. However, in some cases, the reduced precision will cause numerical stability issues, and further cause reproducibility issues. Therefore, please ensure that you are using appropriate precision. + + + +## TensorFloat-32 (TF32) + +### Introduction + +NVIDIA introduced a new math mode TensorFloat-32 (TF32) for NVIDIA Ampere GPUs and above, see [Accelerating AI Training with NVIDIA TF32 Tensor Cores](https://developer.nvidia.com/blog/accelerating-ai-training-with-tf32-tensor-cores/), [TRAINING NEURAL NETWORKS +WITH TENSOR CORES](https://nvlabs.github.io/eccv2020-mixed-precision-tutorial/files/dusan_stosic-training-neural-networks-with-tensor-cores.pdf), [CUDA 11](https://developer.nvidia.com/blog/cuda-11-features-revealed/) and [Ampere architecture](https://developer.nvidia.com/blog/nvidia-ampere-architecture-in-depth/). + +TF32 adopts 8 exponent bits, 10 bits of mantissa, and one sign bit. + +![Precision options used for AI training.](../images/precision_options.png) + +### Potential Impact + +Although NVIDIA has shown that TF32 mode can reach the same accuracy and convergence as float32 for most AI workloads, some users still find some significant effect on their applications, see [PyTorch and TensorFloat32](https://dev-discuss.pytorch.org/t/pytorch-and-tensorfloat32/504). Users who need high-precision matrix operation, such as traditional computer graphics operation and kernel method, may be affected by TF32 precision. + +Note that all operations that use `cuda.matmul` may be affected +by TF32 mode so the impact is very wide. + +### Settings + +[PyTorch TF32](https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices) default value: +```python +torch.backends.cuda.matmul.allow_tf32 = False # in PyTorch 1.12 and later. +torch.backends.cudnn.allow_tf32 = True +``` +Please note that there are environment variables that can override the flags above. For example, the environment variable `NVIDIA_TF32_OVERRIDE` mentioned in [Accelerating AI Training with NVIDIA TF32 Tensor Cores](https://developer.nvidia.com/blog/accelerating-ai-training-with-tf32-tensor-cores/) and `TORCH_ALLOW_TF32_CUBLAS_OVERRIDE` used by PyTorch. Thus, in some cases, the flags may be accidentally changed or overridden. + +If you are using an [NGC PyTorch container](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/pytorch), the container includes a layer `ENV TORCH_ALLOW_TF32_CUBLAS_OVERRIDE=1`. +The default value `torch.backends.cuda.matmul.allow_tf32` will be overridden to `True`. +To restore the upstream default value, please run `unset TORCH_ALLOW_TF32_CUBLAS_OVERRIDE` in the container, +and use the Pytorch API `torch.set_float32_matmul_precision`, `torch.backends.cudnn.allow_tf32=False` accordingly. + + +We recommend that users print out these two flags for confirmation when unsure. + +If you can confirm through experiments that your model has no accuracy or convergence issues in TF32 mode and you have NVIDIA Ampere GPUs or above, you can set the two flags above to `True` to speed up your model. diff --git a/MONAI/source/docs/source/transforms.rst b/MONAI/source/docs/source/transforms.rst new file mode 100644 index 0000000000000000000000000000000000000000..2d5d452dc03aed9b59cbc2ded40157965f503fe0 --- /dev/null +++ b/MONAI/source/docs/source/transforms.rst @@ -0,0 +1,2410 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. _transform_api: + +Transforms +========== + +Generic Interfaces +------------------ +.. automodule:: monai.transforms +.. currentmodule:: monai.transforms + +`Transform` +^^^^^^^^^^^ +.. autoclass:: Transform + :members: + :special-members: __call__ + +`MapTransform` +^^^^^^^^^^^^^^ +.. autoclass:: MapTransform + :members: + :special-members: __call__ + +`RandomizableTrait` +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: RandomizableTrait + :members: + +`LazyTrait` +^^^^^^^^^^^ +.. autoclass:: LazyTrait + :members: + +`MultiSampleTrait` +^^^^^^^^^^^^^^^^^^ +.. autoclass:: MultiSampleTrait + :members: + +`ReduceTrait` +^^^^^^^^^^^^^^^^^^ +.. autoclass:: ReduceTrait + :members: + +`Randomizable` +^^^^^^^^^^^^^^ +.. autoclass:: Randomizable + :members: + +`LazyTransform` +^^^^^^^^^^^^^^^ +.. autoclass:: LazyTransform + :members: + +`RandomizableTransform` +^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: RandomizableTransform + :members: + +`Compose` +^^^^^^^^^ +.. autoclass:: Compose + :members: + :special-members: __call__ + +`InvertibleTransform` +^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: InvertibleTransform + :members: + +`TraceableTransform` +^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: TraceableTransform + :members: + +`BatchInverseTransform` +^^^^^^^^^^^^^^^^^^^^^^^ +.. autoclass:: BatchInverseTransform + :members: + +`Decollated` +^^^^^^^^^^^^ +.. autoclass:: Decollated + :members: + +`OneOf` +^^^^^^^ +.. autoclass:: OneOf + :members: + +`RandomOrder` +^^^^^^^^^^^^^ +.. autoclass:: RandomOrder + :members: + +`SomeOf` +^^^^^^^^^^^^^ +.. autoclass:: SomeOf + :members: + +Functionals +----------- + +Crop and Pad (functional) +^^^^^^^^^^^^^^^^^^^^^^^^^ +.. automodule:: monai.transforms.croppad.functional + :members: + +Spatial (functional) +^^^^^^^^^^^^^^^^^^^^ +.. automodule:: monai.transforms.spatial.functional + :members: + +.. currentmodule:: monai.transforms + +Vanilla Transforms +------------------ + +Crop and Pad +^^^^^^^^^^^^ + +`PadListDataCollate` +"""""""""""""""""""" +.. autoclass:: PadListDataCollate + :members: + :special-members: __call__ + +`Pad` +""""" +.. autoclass:: Pad + :members: + :special-members: __call__ + +`SpatialPad` +"""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/SpatialPad.png + :alt: example of SpatialPad +.. autoclass:: SpatialPad + :members: + :special-members: __call__ + +`BorderPad` +""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/BorderPad.png + :alt: example of BorderPad +.. autoclass:: BorderPad + :members: + :special-members: __call__ + +`DivisiblePad` +"""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/DivisiblePad.png + :alt: example of DivisiblePad +.. autoclass:: DivisiblePad + :members: + :special-members: __call__ + +`Crop` +"""""" +.. autoclass:: Crop + :members: + :special-members: __call__ + +`SpatialCrop` +""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/SpatialCrop.png + :alt: example of SpatialCrop +.. autoclass:: SpatialCrop + :members: + :special-members: __call__ + +`CenterSpatialCrop` +""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/CenterSpatialCrop.png + :alt: example of CenterSpatialCrop +.. autoclass:: CenterSpatialCrop + :members: + :special-members: __call__ + +`RandSpatialCrop` +""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandSpatialCrop.png + :alt: example of RandSpatialCrop +.. autoclass:: RandSpatialCrop + :members: + :special-members: __call__ + +`RandSpatialCropSamples` +"""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandSpatialCropSamples.png + :alt: example of RandSpatialCropSamples +.. autoclass:: RandSpatialCropSamples + :members: + :special-members: __call__ + +`CropForeground` +"""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/CropForeground.png + :alt: example of CropForeground +.. autoclass:: CropForeground + :members: + :special-members: __call__ + +`RandWeightedCrop` +"""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandWeightedCrop.png + :alt: example of RandWeightedCrop +.. autoclass:: RandWeightedCrop + :members: + :special-members: __call__ + +`RandCropByPosNegLabel` +""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandCropByPosNegLabel.png + :alt: example of RandCropByPosNegLabel +.. autoclass:: RandCropByPosNegLabel + :members: + :special-members: __call__ + +`RandCropByLabelClasses` +"""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandCropByLabelClasses.png + :alt: example of RandCropByLabelClasses +.. autoclass:: RandCropByLabelClasses + :members: + :special-members: __call__ + +`ResizeWithPadOrCrop` +""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/ResizeWithPadOrCrop.png + :alt: example of ResizeWithPadOrCrop +.. autoclass:: ResizeWithPadOrCrop + :members: + :special-members: __call__ + +`BoundingRect` +"""""""""""""" +.. autoclass:: BoundingRect + :members: + :special-members: __call__ + +`RandScaleCrop` +""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandScaleCrop.png + :alt: example of RandScaleCrop +.. autoclass:: RandScaleCrop + :members: + :special-members: __call__ + +`CenterScaleCrop` +""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/CenterScaleCrop.png + :alt: example of CenterScaleCrop +.. autoclass:: CenterScaleCrop + :members: + :special-members: __call__ + +Intensity +^^^^^^^^^ + +`RandGaussianNoise` +""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandGaussianNoise.png + :alt: example of RandGaussianNoise +.. autoclass:: RandGaussianNoise + :members: + :special-members: __call__ + +`ShiftIntensity` +"""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/ShiftIntensity.png + :alt: example of ShiftIntensity +.. autoclass:: ShiftIntensity + :members: + :special-members: __call__ + +`RandShiftIntensity` +"""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandShiftIntensity.png + :alt: example of RandShiftIntensity +.. autoclass:: RandShiftIntensity + :members: + :special-members: __call__ + +`StdShiftIntensity` +""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/StdShiftIntensity.png + :alt: example of StdShiftIntensity +.. autoclass:: StdShiftIntensity + :members: + :special-members: __call__ + +`RandStdShiftIntensity` +""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandStdShiftIntensity.png + :alt: example of RandStdShiftIntensity +.. autoclass:: RandStdShiftIntensity + :members: + :special-members: __call__ + +`RandBiasField` +""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandBiasField.png + :alt: example of RandBiasField +.. autoclass:: RandBiasField + :members: + :special-members: __call__ + +`ScaleIntensity` +"""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/ScaleIntensity.png + :alt: example of ScaleIntensity +.. autoclass:: ScaleIntensity + :members: + :special-members: __call__ + +`ClipIntensityPercentiles` +"""""""""""""""""""""""""" +.. autoclass:: ClipIntensityPercentiles + :members: + :special-members: __call__ + +`RandScaleIntensity` +"""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandScaleIntensity.png + :alt: example of RandScaleIntensity +.. autoclass:: RandScaleIntensity + :members: + :special-members: __call__ + +`ScaleIntensityFixedMean` +""""""""""""""""""""""""" +.. autoclass:: ScaleIntensityFixedMean + :members: + :special-members: __call__ + +`RandScaleIntensityFixedMean` +""""""""""""""""""""""""""""" +.. autoclass:: RandScaleIntensityFixedMean + :members: + :special-members: __call__ + +`NormalizeIntensity` +"""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/NormalizeIntensity.png + :alt: example of NormalizeIntensity +.. autoclass:: NormalizeIntensity + :members: + :special-members: __call__ + +`ThresholdIntensity` +"""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/ThresholdIntensity.png + :alt: example of ThresholdIntensity +.. autoclass:: ThresholdIntensity + :members: + :special-members: __call__ + +`ScaleIntensityRange` +""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/ScaleIntensityRange.png + :alt: example of ScaleIntensityRange +.. autoclass:: ScaleIntensityRange + :members: + :special-members: __call__ + +`ScaleIntensityRangePercentiles` +"""""""""""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/ScaleIntensityRangePercentiles.png + :alt: example of ScaleIntensityRangePercentiles +.. autoclass:: ScaleIntensityRangePercentiles + :members: + :special-members: __call__ + +`AdjustContrast` +"""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/AdjustContrast.png + :alt: example of AdjustContrast +.. autoclass:: AdjustContrast + :members: + :special-members: __call__ + +`RandAdjustContrast` +"""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandAdjustContrast.png + :alt: example of RandAdjustContrast +.. autoclass:: RandAdjustContrast + :members: + :special-members: __call__ + +`MaskIntensity` +""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/MaskIntensity.png + :alt: example of MaskIntensity +.. autoclass:: MaskIntensity + :members: + :special-members: __call__ + +`SavitzkyGolaySmooth` +""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/SavitzkyGolaySmooth.png + :alt: example of SavitzkyGolaySmooth +.. autoclass:: SavitzkyGolaySmooth + :members: + :special-members: __call__ + +`MedianSmooth` +"""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/MedianSmooth.png + :alt: example of MedianSmooth +.. autoclass:: MedianSmooth + :members: + :special-members: __call__ + +`GaussianSmooth` +"""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/GaussianSmooth.png + :alt: example of GaussianSmooth +.. autoclass:: GaussianSmooth + :members: + :special-members: __call__ + +`RandGaussianSmooth` +"""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandGaussianSmooth.png + :alt: example of RandGaussianSmooth +.. autoclass:: RandGaussianSmooth + :members: + :special-members: __call__ + +`GaussianSharpen` +""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/GaussianSharpen.png + :alt: example of GaussianSharpen +.. autoclass:: GaussianSharpen + :members: + :special-members: __call__ + +`RandGaussianSharpen` +""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandGaussianSharpen.png + :alt: example of RandGaussianSharpen +.. autoclass:: RandGaussianSharpen + :members: + :special-members: __call__ + +`RandHistogramShift` +"""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandHistogramShift.png + :alt: example of RandHistogramShift +.. autoclass:: RandHistogramShift + :members: + :special-members: __call__ + +`DetectEnvelope` +"""""""""""""""" +.. autoclass:: DetectEnvelope + :members: + :special-members: __call__ + +`GibbsNoise` +"""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/GibbsNoise.png + :alt: example of GibbsNoise +.. autoclass:: GibbsNoise + :members: + :special-members: __call__ + +`RandGibbsNoise` +"""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandGibbsNoise.png + :alt: example of RandGibbsNoise +.. autoclass:: RandGibbsNoise + :members: + :special-members: __call__ + +`KSpaceSpikeNoise` +"""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/KSpaceSpikeNoise.png + :alt: example of KSpaceSpikeNoise +.. autoclass:: KSpaceSpikeNoise + :members: + :special-members: __call__ + +`RandKSpaceSpikeNoise` +"""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandKSpaceSpikeNoise.png + :alt: example of RandKSpaceSpikeNoise +.. autoclass:: RandKSpaceSpikeNoise + :members: + :special-members: __call__ + +`RandRicianNoise` +""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandRicianNoise.png + :alt: example of RandRicianNoise +.. autoclass:: RandRicianNoise + :members: + :special-members: __call__ + +`RandCoarseTransform` +""""""""""""""""""""" +.. autoclass:: RandCoarseTransform + :members: + :special-members: __call__ + +`RandCoarseDropout` +""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandCoarseDropout.png + :alt: example of RandCoarseDropout +.. autoclass:: RandCoarseDropout + :members: + :special-members: __call__ + +`RandCoarseShuffle` +""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandCoarseShuffle.png + :alt: example of RandCoarseShuffle +.. autoclass:: RandCoarseShuffle + :members: + :special-members: __call__ + +`HistogramNormalize` +"""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/HistogramNormalize.png + :alt: example of HistogramNormalize +.. autoclass:: HistogramNormalize + :members: + :special-members: __call__ + + +`ForegroundMask` +"""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/ForegroundMask.png + :alt: example of ForegroundMask +.. autoclass:: ForegroundMask + :members: + :special-members: __call__ + +`ComputeHoVerMaps` +"""""""""""""""""" +.. autoclass:: ComputeHoVerMaps + :members: + :special-members: __call__ + + +IO +^^ + +`LoadImage` +""""""""""" +.. autoclass:: LoadImage + :members: + :special-members: __call__ + +`SaveImage` +""""""""""" +.. autoclass:: SaveImage + :members: + :special-members: __call__ + +`WriteFileMapping` +"""""""""""""""""" +.. autoclass:: WriteFileMapping + :members: + :special-members: __call__ + + +NVIDIA Tool Extension (NVTX) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +`RangePush` +""""""""""" +.. autoclass:: RangePush + +`RandRangePush` +""""""""""""""" +.. autoclass:: RandRangePush + +`RangePop` +"""""""""" +.. autoclass:: RangePop + +`RandRangePop` +"""""""""""""" +.. autoclass:: RandRangePop + +`Mark` +"""""" +.. autoclass:: Mark + +`RandMark` +"""""""""" +.. autoclass:: RandMark + + +Post-processing +^^^^^^^^^^^^^^^ + +`Activations` +""""""""""""" +.. autoclass:: Activations + :members: + :special-members: __call__ + +`AsDiscrete` +"""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/AsDiscrete.png + :alt: example of AsDiscrete +.. autoclass:: AsDiscrete + :members: + :special-members: __call__ + +`KeepLargestConnectedComponent` +""""""""""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/KeepLargestConnectedComponent.png + :alt: example of KeepLargestConnectedComponent +.. autoclass:: KeepLargestConnectedComponent + :members: + :special-members: __call__ + +`DistanceTransformEDT` +""""""""""""""""""""""""""""""" +.. autoclass:: DistanceTransformEDT + :members: + :special-members: __call__ + +`RemoveSmallObjects` +"""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RemoveSmallObjects.png + :alt: example of RemoveSmallObjects +.. autoclass:: RemoveSmallObjects + :members: + :special-members: __call__ + +`LabelFilter` +""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/LabelFilter.png + :alt: example of LabelFilter +.. autoclass:: LabelFilter + :members: + :special-members: __call__ + +`FillHoles` +""""""""""" +.. autoclass:: FillHoles + :members: + :special-members: __call__ + +`LabelToContour` +"""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/LabelToContour.png + :alt: example of LabelToContour +.. autoclass:: LabelToContour + :members: + :special-members: __call__ + +`MeanEnsemble` +"""""""""""""" +.. autoclass:: MeanEnsemble + :members: + :special-members: __call__ + +`ProbNMS` +""""""""" +.. autoclass:: ProbNMS + :members: + +`SobelGradients` +"""""""""""""""" +.. autoclass:: SobelGradients + :members: + :special-members: __call__ + +`VoteEnsemble` +"""""""""""""" +.. autoclass:: VoteEnsemble + :members: + :special-members: __call__ + +`Invert` +""""""""" +.. autoclass:: Invert + :members: + :special-members: __call__ + +Regularization +^^^^^^^^^^^^^^ + +`CutMix` +"""""""" +.. autoclass:: CutMix + :members: + :special-members: __call__ + +`CutOut` +"""""""" +.. autoclass:: CutOut + :members: + :special-members: __call__ + +`MixUp` +""""""" +.. autoclass:: MixUp + :members: + :special-members: __call__ + +Signal +^^^^^^^ + +`SignalRandDrop` +"""""""""""""""" +.. autoclass:: SignalRandDrop + :members: + :special-members: __call__ + +`SignalRandScale` +""""""""""""""""" +.. autoclass:: SignalRandScale + :members: + :special-members: __call__ + +`SignalRandShift` +""""""""""""""""" +.. autoclass:: SignalRandShift + :members: + :special-members: __call__ + +`SignalRandAddSine` +""""""""""""""""""" +.. autoclass:: SignalRandAddSine + :members: + :special-members: __call__ + +`SignalRandAddSquarePulse` +"""""""""""""""""""""""""" +.. autoclass:: SignalRandAddSquarePulse + :members: + :special-members: __call__ + +`SignalRandAddGaussianNoise` +"""""""""""""""""""""""""""" +.. autoclass:: SignalRandAddGaussianNoise + :members: + :special-members: __call__ + +`SignalRandAddSinePartial` +"""""""""""""""""""""""""" +.. autoclass:: SignalRandAddSinePartial + :members: + :special-members: __call__ + +`SignalRandAddSquarePulsePartial` +""""""""""""""""""""""""""""""""" +.. autoclass:: SignalRandAddSquarePulsePartial + :members: + :special-members: __call__ + +`SignalFillEmpty` +""""""""""""""""" +.. autoclass:: SignalFillEmpty + :members: + :special-members: __call__ + +`SignalRemoveFrequency` +""""""""""""""""""""""" +.. autoclass:: SignalRemoveFrequency + :members: + :special-members: __call__ + +`SignalContinuousWavelet` +""""""""""""""""""""""""" +.. autoclass:: SignalContinuousWavelet + :members: + :special-members: __call__ + +Spatial +^^^^^^^ + +`SpatialResample` +""""""""""""""""" +.. autoclass:: SpatialResample + :members: + :special-members: __call__ + +`ResampleToMatch` +""""""""""""""""" +.. autoclass:: ResampleToMatch + :members: + :special-members: __call__ + +`Spacing` +""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Spacing.png + :alt: example of Spacing +.. autoclass:: Spacing + :members: + :special-members: __call__ + +`Orientation` +""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Orientation.png + :alt: example of Orientation +.. autoclass:: Orientation + :members: + :special-members: __call__ + +`RandRotate` +"""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandRotate.png + :alt: example of RandRotate +.. autoclass:: RandRotate + :members: + :special-members: __call__ + +`RandFlip` +"""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandFlip.png + :alt: example of RandFlip +.. autoclass:: RandFlip + :members: + :special-members: __call__ + +`RandAxisFlip` +"""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandAxisFlip.png + :alt: example of RandAxisFlip +.. autoclass:: RandAxisFlip + :members: + :special-members: __call__ + +`RandZoom` +"""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandZoom.png + :alt: example of RandZoom +.. autoclass:: RandZoom + :members: + :special-members: __call__ + +`Affine` +"""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Affine.png + :alt: example of Affine +.. autoclass:: Affine + :members: + :special-members: __call__ + +`Resample` +"""""""""" +.. autoclass:: Resample + :members: + :special-members: __call__ + +`RandAffine` +"""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandAffine.png + :alt: example of RandAffine +.. autoclass:: RandAffine + :members: + :special-members: __call__ + +`RandDeformGrid` +"""""""""""""""" +.. autoclass:: RandDeformGrid + :members: + :special-members: __call__ + +`AffineGrid` +"""""""""""""""" +.. autoclass:: AffineGrid + :members: + :special-members: __call__ + +`RandAffineGrid` +"""""""""""""""" +.. autoclass:: RandAffineGrid + :members: + :special-members: __call__ + +`GridDistortion` +"""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/GridDistortion.png + :alt: example of GridDistortion +.. autoclass:: GridDistortion + :members: + :special-members: __call__ + +`RandGridDistortion` +"""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandGridDistortion.png + :alt: example of RandGridDistortion +.. autoclass:: RandGridDistortion + :members: + :special-members: __call__ + +`Rand2DElastic` +""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Rand2DElastic.png + :alt: example of Rand2DElastic +.. autoclass:: Rand2DElastic + :members: + :special-members: __call__ + +`Rand3DElastic` +""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Rand3DElastic.png + :alt: example of Rand3DElastic +.. autoclass:: Rand3DElastic + :members: + :special-members: __call__ + +`Rotate90` +"""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Rotate90.png + :alt: example of Rotate90 +.. autoclass:: Rotate90 + :members: + :special-members: __call__ + +`RandRotate90` +"""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandRotate90.png + :alt: example of RandRotate90 +.. autoclass:: RandRotate90 + :members: + :special-members: __call__ + +`Flip` +"""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Flip.png + :alt: example of Flip +.. autoclass:: Flip + :members: + :special-members: __call__ + +`Resize` +"""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Resize.png + :alt: example of Resize +.. autoclass:: Resize + :members: + :special-members: __call__ + +`Rotate` +"""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Rotate.png + :alt: example of Rotate +.. autoclass:: Rotate + :members: + :special-members: __call__ + +`Zoom` +"""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Zoom.png + :alt: example of Zoom +.. autoclass:: Zoom + :members: + :special-members: __call__ + +`GridPatch` +""""""""""" +.. autoclass:: GridPatch + :members: + :special-members: __call__ + +`RandGridPatch` +""""""""""""""" +.. autoclass:: RandGridPatch + :members: + :special-members: __call__ + +`GridSplit` +""""""""""" +.. autoclass:: GridSplit + :members: + :special-members: __call__ + +`RandSimulateLowResolution` +""""""""""""""""""""""""""" +.. autoclass:: RandSimulateLowResolution + :members: + :special-members: __call__ + +`ConvertBoxToPoints` +"""""""""""""""""""" +.. autoclass:: ConvertBoxToPoints + :members: + :special-members: __call__ + +`ConvertPointsToBoxes` +"""""""""""""""""""""" +.. autoclass:: ConvertPointsToBoxes + :members: + :special-members: __call__ + + +Smooth Field +^^^^^^^^^^^^ + +`RandSmoothFieldAdjustContrast` +""""""""""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandSmoothFieldAdjustContrast.png + :alt: example of RandSmoothFieldAdjustContrast +.. autoclass:: RandSmoothFieldAdjustContrast + :members: + :special-members: __call__ + +`RandSmoothFieldAdjustIntensity` +"""""""""""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandSmoothFieldAdjustIntensity.png + :alt: example of RandSmoothFieldAdjustIntensity +.. autoclass:: RandSmoothFieldAdjustIntensity + :members: + :special-members: __call__ + +`RandSmoothDeform` +"""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandSmoothDeform.png + :alt: example of RandSmoothDeform +.. autoclass:: RandSmoothDeform + :members: + :special-members: __call__ + + +MRI Transforms +^^^^^^^^^^^^^^ + +`Kspace under-sampling` +""""""""""""""""""""""" +.. autoclass:: monai.apps.reconstruction.transforms.array.KspaceMask + :members: + :special-members: __call__ + +.. autoclass:: monai.apps.reconstruction.transforms.array.RandomKspaceMask + :special-members: __call__ + +.. autoclass:: monai.apps.reconstruction.transforms.array.EquispacedKspaceMask + :special-members: __call__ + + +Lazy +^^^^ + +`ApplyPending` +"""""""""""""" + +.. autoclass:: ApplyPending + :members: + :special-members: __call__ + + +Utility +^^^^^^^ + +`Identity` +"""""""""" +.. autoclass:: Identity + :members: + :special-members: __call__ + +`AsChannelLast` +""""""""""""""" +.. autoclass:: AsChannelLast + :members: + :special-members: __call__ + +`EnsureChannelFirst` +"""""""""""""""""""" +.. autoclass:: EnsureChannelFirst + :members: + :special-members: __call__ + +`RepeatChannel` +""""""""""""""" +.. autoclass:: RepeatChannel + :members: + :special-members: __call__ + +`SplitDim` +"""""""""" +.. autoclass:: SplitDim + :members: + :special-members: __call__ + +`CastToType` +"""""""""""" +.. autoclass:: CastToType + :members: + :special-members: __call__ + +`ToTensor` +"""""""""" +.. autoclass:: ToTensor + :members: + :special-members: __call__ + +`ToNumpy` +""""""""" +.. autoclass:: ToNumpy + :members: + :special-members: __call__ + +`ToCupy` +"""""""" +.. autoclass:: ToCupy + :members: + :special-members: __call__ + +`Transpose` +""""""""""" +.. autoclass:: Transpose + :members: + :special-members: __call__ + +`SqueezeDim` +"""""""""""" +.. autoclass:: SqueezeDim + :members: + :special-members: __call__ + +`DataStats` +""""""""""" +.. autoclass:: DataStats + :members: + :special-members: __call__ + +`SimulateDelay` +""""""""""""""" +.. autoclass:: SimulateDelay + :members: + :special-members: __call__ + + +`Lambda` +"""""""" +.. autoclass:: Lambda + :members: + :special-members: __call__ + +`RandLambda` +"""""""""""" +.. autoclass:: RandLambda + :members: + :special-members: __call__ + +`RemoveRepeatedChannel` +""""""""""""""""""""""" +.. autoclass:: RemoveRepeatedChannel + :members: + :special-members: __call__ + +`LabelToMask` +""""""""""""" +.. autoclass:: LabelToMask + :members: + :special-members: __call__ + +`FgBgToIndices` +""""""""""""""" +.. autoclass:: FgBgToIndices + :members: + :special-members: __call__ + +`ClassesToIndices` +"""""""""""""""""" +.. autoclass:: ClassesToIndices + :members: + :special-members: __call__ + +`ConvertToMultiChannelBasedOnBratsClasses` +"""""""""""""""""""""""""""""""""""""""""" +.. autoclass:: ConvertToMultiChannelBasedOnBratsClasses + :members: + :special-members: __call__ + +`AddExtremePointsChannel` +""""""""""""""""""""""""" +.. autoclass:: AddExtremePointsChannel + :members: + :special-members: __call__ + +`TorchVision` +""""""""""""" +.. autoclass:: TorchVision + :members: + :special-members: __call__ + +`TorchIO` +""""""""" +.. autoclass:: TorchIO + :members: + :special-members: __call__ + +`RandTorchIO` +""""""""""""" +.. autoclass:: RandTorchIO + :members: + :special-members: __call__ + +`MapLabelValue` +""""""""""""""" +.. autoclass:: MapLabelValue + :members: + :special-members: __call__ + +`EnsureType` +"""""""""""" +.. autoclass:: EnsureType + :members: + :special-members: __call__ + +`IntensityStats` +"""""""""""""""" +.. autoclass:: IntensityStats + :members: + :special-members: __call__ + +`ToDevice` +"""""""""" +.. autoclass:: ToDevice + :members: + :special-members: __call__ + +`CuCIM` +""""""" +.. autoclass:: CuCIM + :members: + :special-members: __call__ + +`RandCuCIM` +""""""""""" +.. autoclass:: RandCuCIM + :members: + :special-members: __call__ + +`AddCoordinateChannels` +""""""""""""""""""""""" +.. autoclass:: AddCoordinateChannels + :members: + :special-members: __call__ + +`ImageFilter` +""""""""""""" +.. autoclass:: ImageFilter + :members: + :special-members: __call__ + +`RandImageFilter` +""""""""""""""""" +.. autoclass:: RandImageFilter + :members: + :special-members: __call__ + +`ApplyTransformToPoints` +"""""""""""""""""""""""" +.. autoclass:: ApplyTransformToPoints + :members: + :special-members: __call__ + +`FlattenSequence` +"""""""""""""""""""""""" +.. autoclass:: FlattenSequence + :members: + :special-members: __call__ + +Dictionary Transforms +--------------------- + +Crop and Pad (Dict) +^^^^^^^^^^^^^^^^^^^ + +`Padd` +"""""" +.. autoclass:: Padd + :members: + :special-members: __call__ + +`SpatialPadd` +""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/SpatialPadd.png + :alt: example of SpatialPadd +.. autoclass:: SpatialPadd + :members: + :special-members: __call__ + +`BorderPadd` +"""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/BorderPadd.png + :alt: example of BorderPadd +.. autoclass:: BorderPadd + :members: + :special-members: __call__ + +`DivisiblePadd` +""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/DivisiblePadd.png + :alt: example of DivisiblePadd +.. autoclass:: DivisiblePadd + :members: + :special-members: __call__ + +`Cropd` +""""""" +.. autoclass:: Cropd + :members: + :special-members: __call__ + +`RandCropd` +""""""""""" +.. autoclass:: RandCropd + :members: + :special-members: __call__ + +`SpatialCropd` +"""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/SpatialCropd.png + :alt: example of SpatialCropd +.. autoclass:: SpatialCropd + :members: + :special-members: __call__ + +`CenterSpatialCropd` +"""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/CenterSpatialCropd.png + :alt: example of CenterSpatialCropd +.. autoclass:: CenterSpatialCropd + :members: + :special-members: __call__ + +`RandSpatialCropd` +"""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandSpatialCropd.png + :alt: example of RandSpatialCropd +.. autoclass:: RandSpatialCropd + :members: + :special-members: __call__ + +`RandSpatialCropSamplesd` +""""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandSpatialCropSamplesd.png + :alt: example of RandSpatialCropSamplesd +.. autoclass:: RandSpatialCropSamplesd + :members: + :special-members: __call__ + +`CropForegroundd` +""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/CropForegroundd.png + :alt: example of CropForegroundd +.. autoclass:: CropForegroundd + :members: + :special-members: __call__ + +`RandWeightedCropd` +""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandWeightedCropd.png + :alt: example of RandWeightedCropd +.. autoclass:: RandWeightedCropd + :members: + :special-members: __call__ + +`RandCropByPosNegLabeld` +"""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandCropByPosNegLabeld.png + :alt: example of RandCropByPosNegLabeld +.. autoclass:: RandCropByPosNegLabeld + :members: + :special-members: __call__ + +`RandCropByLabelClassesd` +""""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandCropByLabelClassesd.png + :alt: example of RandCropByLabelClassesd +.. autoclass:: RandCropByLabelClassesd + :members: + :special-members: __call__ + +`ResizeWithPadOrCropd` +"""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/ResizeWithPadOrCropd.png + :alt: example of ResizeWithPadOrCropd +.. autoclass:: ResizeWithPadOrCropd + :members: + :special-members: __call__ + +`BoundingRectd` +""""""""""""""" +.. autoclass:: BoundingRectd + :members: + :special-members: __call__ + +`RandScaleCropd` +"""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandScaleCropd.png + :alt: example of RandScaleCropd +.. autoclass:: RandScaleCropd + :members: + :special-members: __call__ + +`CenterScaleCropd` +"""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/CenterScaleCropd.png + :alt: example of CenterScaleCropd +.. autoclass:: CenterScaleCropd + :members: + :special-members: __call__ + +Intensity (Dict) +^^^^^^^^^^^^^^^^ + +`RandGaussianNoised` +"""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandGaussianNoised.png + :alt: example of RandGaussianNoised +.. autoclass:: RandGaussianNoised + :members: + :special-members: __call__ + +`ShiftIntensityd` +""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/ShiftIntensityd.png + :alt: example of ShiftIntensityd +.. autoclass:: ShiftIntensityd + :members: + :special-members: __call__ + +`RandShiftIntensityd` +""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandShiftIntensityd.png + :alt: example of RandShiftIntensityd +.. autoclass:: RandShiftIntensityd + :members: + :special-members: __call__ + +`StdShiftIntensityd` +"""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/StdShiftIntensityd.png + :alt: example of StdShiftIntensityd +.. autoclass:: StdShiftIntensityd + :members: + :special-members: __call__ + +`RandStdShiftIntensityd` +"""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandStdShiftIntensityd.png + :alt: example of RandStdShiftIntensityd +.. autoclass:: RandStdShiftIntensityd + :members: + :special-members: __call__ + +`RandBiasFieldd` +"""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandBiasFieldd.png + :alt: example of RandBiasFieldd +.. autoclass:: RandBiasFieldd + :members: + :special-members: __call__ + +`ScaleIntensityd` +""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/ScaleIntensityd.png + :alt: example of ScaleIntensityd +.. autoclass:: ScaleIntensityd + :members: + :special-members: __call__ + +`ClipIntensityPercentilesd` +""""""""""""""""""""""""""" +.. autoclass:: ClipIntensityPercentilesd + :members: + :special-members: __call__ + +`RandScaleIntensityd` +""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandScaleIntensityd.png + :alt: example of RandScaleIntensityd +.. autoclass:: RandScaleIntensityd + :members: + :special-members: __call__ + +`RandScaleIntensityFixedMeand` +""""""""""""""""""""""""""""""" +.. autoclass:: RandScaleIntensityFixedMeand + :members: + :special-members: __call__ + +`NormalizeIntensityd` +""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/NormalizeIntensityd.png + :alt: example of NormalizeIntensityd +.. autoclass:: NormalizeIntensityd + :members: + :special-members: __call__ + +`ThresholdIntensityd` +""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/ThresholdIntensityd.png + :alt: example of ThresholdIntensityd +.. autoclass:: ThresholdIntensityd + :members: + :special-members: __call__ + +`ScaleIntensityRanged` +"""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/ScaleIntensityRanged.png + :alt: example of ScaleIntensityRanged +.. autoclass:: ScaleIntensityRanged + :members: + :special-members: __call__ + +`GibbsNoised` +"""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/GibbsNoised.png + :alt: example of GibbsNoised +.. autoclass:: GibbsNoised + :members: + :special-members: __call__ + +`RandGibbsNoised` +"""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandGibbsNoised.png + :alt: example of RandGibbsNoised +.. autoclass:: RandGibbsNoised + :members: + :special-members: __call__ + +`KSpaceSpikeNoised` +"""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/KSpaceSpikeNoised.png + :alt: example of KSpaceSpikeNoised +.. autoclass:: KSpaceSpikeNoised + :members: + :special-members: __call__ + +`RandKSpaceSpikeNoised` +""""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandKSpaceSpikeNoised.png + :alt: example of RandKSpaceSpikeNoised +.. autoclass:: RandKSpaceSpikeNoised + :members: + :special-members: __call__ + +`RandRicianNoised` +"""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandRicianNoised.png + :alt: example of RandRicianNoised +.. autoclass:: RandRicianNoised + :members: + :special-members: __call__ + +`ScaleIntensityRangePercentilesd` +""""""""""""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/ScaleIntensityRangePercentilesd.png + :alt: example of ScaleIntensityRangePercentilesd +.. autoclass:: ScaleIntensityRangePercentilesd + :members: + :special-members: __call__ + +`AdjustContrastd` +""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/AdjustContrastd.png + :alt: example of AdjustContrastd +.. autoclass:: AdjustContrastd + :members: + :special-members: __call__ + +`RandAdjustContrastd` +""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandAdjustContrastd.png + :alt: example of RandAdjustContrastd +.. autoclass:: RandAdjustContrastd + :members: + :special-members: __call__ + +`MaskIntensityd` +"""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/MaskIntensityd.png + :alt: example of MaskIntensityd +.. autoclass:: MaskIntensityd + :members: + :special-members: __call__ + +`SavitzkyGolaySmoothd` +"""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/SavitzkyGolaySmoothd.png + :alt: example of SavitzkyGolaySmoothd +.. autoclass:: SavitzkyGolaySmoothd + :members: + :special-members: __call__ + +`MedianSmoothd` +""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/MedianSmoothd.png + :alt: example of MedianSmoothd +.. autoclass:: MedianSmoothd + :members: + :special-members: __call__ + +`GaussianSmoothd` +""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/GaussianSmoothd.png + :alt: example of GaussianSmoothd +.. autoclass:: GaussianSmoothd + :members: + :special-members: __call__ + +`RandGaussianSmoothd` +""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandGaussianSmoothd.png + :alt: example of RandGaussianSmoothd +.. autoclass:: RandGaussianSmoothd + :members: + :special-members: __call__ + +`GaussianSharpend` +"""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/GaussianSharpend.png + :alt: example of GaussianSharpend +.. autoclass:: GaussianSharpend + :members: + :special-members: __call__ + +`RandGaussianSharpend` +"""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandGaussianSharpend.png + :alt: example of RandGaussianSharpend +.. autoclass:: RandGaussianSharpend + :members: + :special-members: __call__ + +`RandHistogramShiftd` +""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandHistogramShiftd.png + :alt: example of RandHistogramShiftd +.. autoclass:: RandHistogramShiftd + :members: + :special-members: __call__ + +`RandCoarseDropoutd` +"""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandCoarseDropoutd.png + :alt: example of RandCoarseDropoutd +.. autoclass:: RandCoarseDropoutd + :members: + :special-members: __call__ + +`RandCoarseShuffled` +"""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandCoarseShuffled.png + :alt: example of RandCoarseShuffled +.. autoclass:: RandCoarseShuffled + :members: + :special-members: __call__ + +`HistogramNormalized` +""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/HistogramNormalized.png + :alt: example of HistogramNormalized +.. autoclass:: HistogramNormalized + :members: + :special-members: __call__ + +`ForegroundMaskd` +""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/ForegroundMaskd.png + :alt: example of ForegroundMaskd +.. autoclass:: ForegroundMaskd + :members: + :special-members: __call__ + +`ComputeHoVerMapsd` +""""""""""""""""""" +.. autoclass:: ComputeHoVerMapsd + :members: + :special-members: __call__ + +IO (Dict) +^^^^^^^^^ + +`LoadImaged` +"""""""""""" +.. autoclass:: LoadImaged + :members: + :special-members: __call__ + +`SaveImaged` +"""""""""""" +.. autoclass:: SaveImaged + :members: + :special-members: __call__ + +`WriteFileMappingd` +""""""""""""""""""" +.. autoclass:: WriteFileMappingd + :members: + :special-members: __call__ + +Post-processing (Dict) +^^^^^^^^^^^^^^^^^^^^^^ + +`Activationsd` +"""""""""""""" +.. autoclass:: Activationsd + :members: + :special-members: __call__ + +`AsDiscreted` +""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/AsDiscreted.png + :alt: example of AsDiscreted +.. autoclass:: AsDiscreted + :members: + :special-members: __call__ + +`KeepLargestConnectedComponentd` +"""""""""""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/KeepLargestConnectedComponentd.png + :alt: example of KeepLargestConnectedComponentd +.. autoclass:: KeepLargestConnectedComponentd + :members: + :special-members: __call__ + +`DistanceTransformEDTd` +"""""""""""""""""""""""""""""""" +.. autoclass:: DistanceTransformEDTd + :members: + :special-members: __call__ + +`RemoveSmallObjectsd` +""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RemoveSmallObjectsd.png + :alt: example of RemoveSmallObjectsd +.. autoclass:: RemoveSmallObjectsd + :members: + :special-members: __call__ + +`LabelFilterd` +"""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/LabelFilterd.png + :alt: example of LabelFilterd +.. autoclass:: LabelFilterd + :members: + :special-members: __call__ + +`FillHolesd` +"""""""""""" +.. autoclass:: FillHolesd + :members: + :special-members: __call__ + +`LabelToContourd` +""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/LabelToContourd.png + :alt: example of LabelToContourd +.. autoclass:: LabelToContourd + :members: + :special-members: __call__ + +`Ensembled` +""""""""""" +.. autoclass:: Ensembled + :members: + :special-members: __call__ + +`MeanEnsembled` +""""""""""""""" +.. autoclass:: MeanEnsembled + :members: + :special-members: __call__ + +`VoteEnsembled` +""""""""""""""" +.. autoclass:: VoteEnsembled + :members: + :special-members: __call__ + +`Invertd` +""""""""" +.. autoclass:: Invertd + :members: + :special-members: __call__ + +`SaveClassificationd` +""""""""""""""""""""" +.. autoclass:: SaveClassificationd + :members: + :special-members: __call__ + +`ProbNMSd` +"""""""""" +.. autoclass:: ProbNMSd + :members: + :special-members: __call__ + + +`SobelGradientsd` +""""""""""""""""" +.. autoclass:: SobelGradientsd + :members: + :special-members: __call__ + +Regularization (Dict) +^^^^^^^^^^^^^^^^^^^^^ + +`CutMixd` +""""""""" +.. autoclass:: CutMixd + :members: + :special-members: __call__ + +`CutOutd` +""""""""" +.. autoclass:: CutOutd + :members: + :special-members: __call__ + +`MixUpd` +"""""""" +.. autoclass:: MixUpd + :members: + :special-members: __call__ + +Signal (Dict) +^^^^^^^^^^^^^ + +`SignalFillEmptyd` +"""""""""""""""""" +.. autoclass:: SignalFillEmptyd + :members: + :special-members: __call__ + + +Spatial (Dict) +^^^^^^^^^^^^^^ + +`SpatialResampled` +"""""""""""""""""" +.. autoclass:: SpatialResampled + :members: + :special-members: __call__ + +`ResampleToMatchd` +"""""""""""""""""" +.. autoclass:: ResampleToMatchd + :members: + :special-members: __call__ + +`Spacingd` +"""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Spacingd.png + :alt: example of Spacingd +.. autoclass:: Spacingd + :members: + :special-members: __call__ + +`Orientationd` +"""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Orientationd.png + :alt: example of Orientationd +.. autoclass:: Orientationd + :members: + :special-members: __call__ + +`Flipd` +""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Flipd.png + :alt: example of Flipd +.. autoclass:: Flipd + :members: + :special-members: __call__ + +`RandFlipd` +""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandFlipd.png + :alt: example of RandFlipd +.. autoclass:: RandFlipd + :members: + :special-members: __call__ + +`RandAxisFlipd` +""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandAxisFlipd.png + :alt: example of RandAxisFlipd +.. autoclass:: RandAxisFlipd + :members: + :special-members: __call__ + +`Rotated` +""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Rotated.png + :alt: example of Rotated +.. autoclass:: Rotated + :members: + :special-members: __call__ + +`RandRotated` +""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandRotated.png + :alt: example of RandRotated +.. autoclass:: RandRotated + :members: + :special-members: __call__ + +`Zoomd` +""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Zoomd.png + :alt: example of Zoomd +.. autoclass:: Zoomd + :members: + :special-members: __call__ + +`RandZoomd` +""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandZoomd.png + :alt: example of RandZoomd +.. autoclass:: RandZoomd + :members: + :special-members: __call__ + +`GridPatchd` +"""""""""""" +.. autoclass:: GridPatchd + :members: + :special-members: __call__ + +`RandGridPatchd` +"""""""""""""""" +.. autoclass:: RandGridPatchd + :members: + :special-members: __call__ + +`GridSplitd` +"""""""""""" +.. autoclass:: GridSplitd + :members: + :special-members: __call__ + + +`RandRotate90d` +""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandRotate90d.png + :alt: example of RandRotate90d +.. autoclass:: RandRotate90d + :members: + :special-members: __call__ + +`Rotate90d` +""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Rotate90d.png + :alt: example of Rotate90d +.. autoclass:: Rotate90d + :members: + :special-members: __call__ + +`Resized` +""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Resized.png + :alt: example of Resized +.. autoclass:: Resized + :members: + :special-members: __call__ + +`Affined` +""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Affined.png + :alt: example of Affined +.. autoclass:: Affined + :members: + :special-members: __call__ + +`RandAffined` +""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandAffined.png + :alt: example of RandAffined +.. autoclass:: RandAffined + :members: + :special-members: __call__ + +`Rand2DElasticd` +"""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Rand2DElasticd.png + :alt: example of Rand2DElasticd +.. autoclass:: Rand2DElasticd + :members: + :special-members: __call__ + +`Rand3DElasticd` +"""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/Rand3DElasticd.png + :alt: example of Rand3DElasticd +.. autoclass:: Rand3DElasticd + :members: + :special-members: __call__ + +`GridDistortiond` +""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/GridDistortiond.png + :alt: example of GridDistortiond +.. autoclass:: GridDistortiond + :members: + :special-members: __call__ + +`RandGridDistortiond` +""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandGridDistortiond.png + :alt: example of RandGridDistortiond +.. autoclass:: RandGridDistortiond + :members: + :special-members: __call__ + +`RandSimulateLowResolutiond` +"""""""""""""""""""""""""""" +.. autoclass:: RandSimulateLowResolutiond + :members: + :special-members: __call__ + +`ConvertBoxToPointsd` +""""""""""""""""""""" +.. autoclass:: ConvertBoxToPointsd + :members: + :special-members: __call__ + +`ConvertPointsToBoxesd` +""""""""""""""""""""""" +.. autoclass:: ConvertPointsToBoxesd + :members: + :special-members: __call__ + + +Smooth Field (Dict) +^^^^^^^^^^^^^^^^^^^ + +`RandSmoothFieldAdjustContrastd` +"""""""""""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandSmoothFieldAdjustContrastd.png + :alt: example of RandSmoothFieldAdjustContrastd +.. autoclass:: RandSmoothFieldAdjustContrastd + :members: + :special-members: __call__ + +`RandSmoothFieldAdjustIntensityd` +""""""""""""""""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandSmoothFieldAdjustIntensityd.png + :alt: example of RandSmoothFieldAdjustIntensityd +.. autoclass:: RandSmoothFieldAdjustIntensityd + :members: + :special-members: __call__ + +`RandSmoothDeformd` +""""""""""""""""""" +.. image:: https://raw.githubusercontent.com/Project-MONAI/DocImages/main/transforms/RandSmoothDeformd.png + :alt: example of RandSmoothDeformd +.. autoclass:: RandSmoothDeformd + :members: + :special-members: __call__ + + +`MRI transforms (Dict)` +^^^^^^^^^^^^^^^^^^^^^^^ + +`Kspace under-sampling (Dict)` +"""""""""""""""""""""""""""""" +.. autoclass:: monai.apps.reconstruction.transforms.dictionary.RandomKspaceMaskd + :special-members: __call__ + +.. autoclass:: monai.apps.reconstruction.transforms.dictionary.EquispacedKspaceMaskd + :special-members: __call__ + +`ExtractDataKeyFromMetaKeyd` +"""""""""""""""""""""""""""" +.. autoclass:: monai.apps.reconstruction.transforms.dictionary.ExtractDataKeyFromMetaKeyd + :special-members: __call__ + +`ReferenceBasedSpatialCropd` +"""""""""""""""""""""""""""" +.. autoclass:: monai.apps.reconstruction.transforms.dictionary.ReferenceBasedSpatialCropd + :special-members: __call__ + +`ReferenceBasedNormalizeIntensityd` +""""""""""""""""""""""""""""""""""" +.. autoclass:: monai.apps.reconstruction.transforms.dictionary.ReferenceBasedNormalizeIntensityd + :special-members: __call__ + + +Lazy (Dict) +^^^^^^^^^^^ + +`ApplyPendingd` +""""""""""""""" + +.. autoclass:: ApplyPendingd + :members: + :special-members: __call__ + + +Utility (Dict) +^^^^^^^^^^^^^^ + +`Identityd` +""""""""""" +.. autoclass:: Identityd + :members: + :special-members: __call__ + +`AsChannelLastd` +"""""""""""""""" +.. autoclass:: AsChannelLastd + :members: + :special-members: __call__ + +`EnsureChannelFirstd` +""""""""""""""""""""" +.. autoclass:: EnsureChannelFirstd + :members: + :special-members: __call__ + +`RepeatChanneld` +"""""""""""""""" +.. autoclass:: RepeatChanneld + :members: + :special-members: __call__ + +`SplitDimd` +""""""""""" +.. autoclass:: SplitDimd + :members: + :special-members: __call__ + +`CastToTyped` +""""""""""""" +.. autoclass:: CastToTyped + :members: + :special-members: __call__ + +`ToTensord` +""""""""""" +.. autoclass:: ToTensord + :members: + :special-members: __call__ + +`ToNumpyd` +"""""""""" +.. autoclass:: ToNumpyd + :members: + :special-members: __call__ + +`ToPIL` +""""""" +.. autoclass:: ToPIL + :members: + :special-members: __call__ + +`ToCupyd` +""""""""" +.. autoclass:: ToCupyd + :members: + :special-members: __call__ + +`ToPILd` +"""""""" +.. autoclass:: ToPILd + :members: + :special-members: __call__ + +`DeleteItemsd` +"""""""""""""" +.. autoclass:: DeleteItemsd + :members: + :special-members: __call__ + +`SelectItemsd` +"""""""""""""" +.. autoclass:: SelectItemsd + :members: + :special-members: __call__ + +`FlattenSubKeysd` +""""""""""""""""" +.. autoclass:: FlattenSubKeysd + :members: + :special-members: __call__ + +`Transposed` +"""""""""""" +.. autoclass:: Transposed + :members: + :special-members: __call__ + +`SqueezeDimd` +""""""""""""" +.. autoclass:: SqueezeDimd + :members: + :special-members: __call__ + +`DataStatsd` +"""""""""""" +.. autoclass:: DataStatsd + :members: + :special-members: __call__ + +`SimulateDelayd` +"""""""""""""""" +.. autoclass:: SimulateDelayd + :members: + :special-members: __call__ + +`CopyItemsd` +"""""""""""" +.. autoclass:: CopyItemsd + :members: + :special-members: __call__ + +`ConcatItemsd` +"""""""""""""" +.. autoclass:: ConcatItemsd + :members: + :special-members: __call__ + +`Lambdad` +""""""""" +.. autoclass:: Lambdad + :members: + :special-members: __call__ + +`RandLambdad` +""""""""""""" +.. autoclass:: RandLambdad + :members: + :special-members: __call__ + +`RemoveRepeatedChanneld` +"""""""""""""""""""""""" +.. autoclass:: RemoveRepeatedChanneld + :members: + :special-members: __call__ + +`LabelToMaskd` +"""""""""""""" +.. autoclass:: LabelToMaskd + :members: + :special-members: __call__ + +`FgBgToIndicesd` +"""""""""""""""" +.. autoclass:: FgBgToIndicesd + :members: + :special-members: __call__ + +`ClassesToIndicesd` +""""""""""""""""""" +.. autoclass:: ClassesToIndicesd + :members: + :special-members: __call__ + +`ConvertToMultiChannelBasedOnBratsClassesd` +""""""""""""""""""""""""""""""""""""""""""" +.. autoclass:: ConvertToMultiChannelBasedOnBratsClassesd + :members: + :special-members: __call__ + +`AddExtremePointsChanneld` +"""""""""""""""""""""""""" +.. autoclass:: AddExtremePointsChanneld + :members: + :special-members: __call__ + +`TorchVisiond` +"""""""""""""" +.. autoclass:: TorchVisiond + :members: + :special-members: __call__ + +`RandTorchVisiond` +"""""""""""""""""" +.. autoclass:: RandTorchVisiond + :members: + :special-members: __call__ + +`TorchIOd` +"""""""""" +.. autoclass:: TorchIOd + :members: + :special-members: __call__ + +`RandTorchIOd` +"""""""""""""" +.. autoclass:: RandTorchIOd + :members: + :special-members: __call__ + +`MapLabelValued` +"""""""""""""""" +.. autoclass:: MapLabelValued + :members: + :special-members: __call__ + +`EnsureTyped` +""""""""""""" +.. autoclass:: EnsureTyped + :members: + :special-members: __call__ + +`IntensityStatsd` +""""""""""""""""" +.. autoclass:: IntensityStatsd + :members: + :special-members: __call__ + +`ToDeviced` +""""""""""" +.. autoclass:: ToDeviced + :members: + :special-members: __call__ + +`CuCIMd` +"""""""" +.. autoclass:: CuCIMd + :members: + :special-members: __call__ + +`RandCuCIMd` +"""""""""""" +.. autoclass:: RandCuCIMd + :members: + :special-members: __call__ + +`AddCoordinateChannelsd` +"""""""""""""""""""""""" +.. autoclass:: AddCoordinateChannelsd + :members: + :special-members: __call__ + +`ImageFilterd` +"""""""""""""" +.. autoclass:: ImageFilterd + :members: + :special-members: __call__ + +`RandImageFilterd` +"""""""""""""""""" +.. autoclass:: RandImageFilterd + :members: + :special-members: __call__ + +`ApplyTransformToPointsd` +""""""""""""""""""""""""" +.. autoclass:: ApplyTransformToPointsd + :members: + :special-members: __call__ + +`FlattenSequenced` +""""""""""""""""""""""""" +.. autoclass:: FlattenSequenced + :members: + :special-members: __call__ + + +MetaTensor +^^^^^^^^^^ + +`ToMetaTensord` +""""""""""""""" +.. autoclass:: ToMetaTensord + :members: + :special-members: __call__ + +`FromMetaTensord` +""""""""""""""""" +.. autoclass:: FromMetaTensord + :members: + :special-members: __call__ + +Transform Adaptors +------------------ +.. automodule:: monai.transforms.adaptors + +`FunctionSignature` +^^^^^^^^^^^^^^^^^^^ +.. autoclass:: FunctionSignature + :members: + +`adaptor` +^^^^^^^^^ +.. autofunction:: monai.transforms.adaptors.adaptor + +`apply_alias` +^^^^^^^^^^^^^ +.. autofunction:: monai.transforms.adaptors.apply_alias + +`to_kwargs` +^^^^^^^^^^^ +.. autofunction:: monai.transforms.adaptors.to_kwargs + +Utilities +--------- +.. automodule:: monai.transforms.utils + :members: + +.. automodule:: monai.transforms.utils_pytorch_numpy_unification + :members: + +.. automodule:: monai.transforms.utils_morphological_ops + :members: + +By Categories +------------- +.. toctree:: + :maxdepth: 1 + + transforms_idx diff --git a/MONAI/source/docs/source/transforms_idx.rst b/MONAI/source/docs/source/transforms_idx.rst new file mode 100644 index 0000000000000000000000000000000000000000..650d45db716e171e42c1e0747b30771dbde1a8f6 --- /dev/null +++ b/MONAI/source/docs/source/transforms_idx.rst @@ -0,0 +1,114 @@ +.. _transforms_idx: + +.. currentmodule:: monai.transforms + +Crop and pad +^^^^^^^^^^^^ + +.. autosummary:: + :toctree: _gen + :nosignatures: + + croppad.array + croppad.dictionary + croppad.batch + +Spatial +^^^^^^^ + +.. autosummary:: + :toctree: _gen + :nosignatures: + + spatial.array + spatial.dictionary + + +Intensity +^^^^^^^^^ + +.. autosummary:: + :toctree: _gen + :nosignatures: + + intensity.array + intensity.dictionary + +IO +^^ + +.. autosummary:: + :toctree: _gen + :nosignatures: + + io.array + io.dictionary + +Lazy +^^^^ + +.. autosummary:: + :toctree: _gen + :nosignatures: + + lazy.array + lazy.dictionary + lazy.utils + +MetaTensor utilities +^^^^^^^^^^^^^^^^^^^^ + +.. autosummary:: + :toctree: _gen + :nosignatures: + + meta_utility.dictionary + +Post-processing +^^^^^^^^^^^^^^^ + +.. autosummary:: + :toctree: _gen + :nosignatures: + + post.array + post.dictionary + +Regularization +^^^^^^^^^^^^^^ + +.. autosummary:: + :toctree: _gen + :nosignatures: + + regularization.array + regularization.dictionary + +Signal +^^^^^^ + +.. autosummary:: + :toctree: _gen + :nosignatures: + + signal.array + +Smooth field +^^^^^^^^^^^^ + +.. autosummary:: + :toctree: _gen + :nosignatures: + + smooth_field.array + smooth_field.dictionary + +Utility +^^^^^^^ + +.. autosummary:: + :toctree: _gen + :nosignatures: + + utility.array + utility.dictionary diff --git a/MONAI/source/docs/source/utils.rst b/MONAI/source/docs/source/utils.rst new file mode 100644 index 0000000000000000000000000000000000000000..ae3b476c3e1e1b038aeaf6a2a596f5cf052d8213 --- /dev/null +++ b/MONAI/source/docs/source/utils.rst @@ -0,0 +1,82 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. _utils: + +Utilities +========= + +Configurations +-------------- +.. automodule:: monai.config.deviceconfig + :members: + + +Module utils +------------ +.. automodule:: monai.utils.module + :members: + + +Misc +---- +.. automodule:: monai.utils.misc + :members: + + +NVTX Annotations +---------------- +.. automodule:: monai.utils.nvtx + :members: + + +Profiling +--------- +.. automodule:: monai.utils.profiling + :members: + + +Deprecated +---------- +.. automodule:: monai.utils.deprecate_utils + :members: + + +Type conversion +--------------- +.. automodule:: monai.utils.type_conversion + :members: + +Decorators +---------- +.. automodule:: monai.utils.decorators + :members: + +Distributed Data Parallel +------------------------- +.. automodule:: monai.utils.dist + :members: + +Enums +----- +.. automodule:: monai.utils.enums + :members: + +Jupyter Utilities +----------------- +.. automodule:: monai.utils.jupyter_utils + :members: + +State Cacher +------------ +.. automodule:: monai.utils.state_cacher + :members: + +Component store +--------------- +.. autoclass:: monai.utils.component_store.ComponentStore + :members: + +Ordering +-------- +.. automodule:: monai.utils.ordering + :members: diff --git a/MONAI/source/docs/source/visualize.rst b/MONAI/source/docs/source/visualize.rst new file mode 100644 index 0000000000000000000000000000000000000000..1860b65e03dddda5eb4c1736cb2b83c119222248 --- /dev/null +++ b/MONAI/source/docs/source/visualize.rst @@ -0,0 +1,39 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +.. _visualize: + +Visualizations +============== + +.. currentmodule:: monai.visualize + +Tensorboard visuals +------------------- + +.. automodule:: monai.visualize.img2tensorboard + :members: + +Class activation map +-------------------- + +.. automodule:: monai.visualize.class_activation_maps + :members: + +Occlusion sensitivity +--------------------- + +.. automodule:: monai.visualize.occlusion_sensitivity + :members: + +Gradient-based saliency maps +---------------------------- + +.. automodule:: monai.visualize.gradient_based + :members: + + +Utilities +--------- + +.. automodule:: monai.visualize.utils + :members: diff --git a/MONAI/source/docs/source/whatsnew.rst b/MONAI/source/docs/source/whatsnew.rst new file mode 100644 index 0000000000000000000000000000000000000000..1fa4680055d3ec7f3f3eb3c59ba7fd45d0dcade5 --- /dev/null +++ b/MONAI/source/docs/source/whatsnew.rst @@ -0,0 +1,21 @@ +:github_url: https://github.com/Project-MONAI/MONAI + +What's New +========== + +.. toctree:: + :maxdepth: 1 + + whatsnew_1_5_2.md + whatsnew_1_5_1.md + whatsnew_1_5.md + whatsnew_1_4.md + whatsnew_1_3.md + whatsnew_1_2.md + whatsnew_1_1.md + whatsnew_1_0.md + whatsnew_0_9.md + whatsnew_0_8.md + whatsnew_0_7.md + whatsnew_0_6.md + whatsnew_0_5.md diff --git a/MONAI/source/docs/source/whatsnew_0_5.md b/MONAI/source/docs/source/whatsnew_0_5.md new file mode 100644 index 0000000000000000000000000000000000000000..f353084303d67a85e4f5236d2a2d17e822008758 --- /dev/null +++ b/MONAI/source/docs/source/whatsnew_0_5.md @@ -0,0 +1,78 @@ +# What's new in 0.5 + +- Invert spatial transforms and test-time augmentations +- Lesion detection in digital pathology +- DeepGrow modules for interactive segmentation +- Various usability improvements + +## Invert spatial transforms and test-time augmentations +It is often desirable to invert the previously applied spatial transforms (resize, flip, rotate, zoom, crop, pad, etc.) with the deep learning workflows, for example, to resume to the original imaging space after processing the image data in a normalized data space. We enhance almost all the spatial transforms with an `inverse` operation and release this experimental feature in v0.5. Users can easily invert all the spatial transforms for one transformed data item or a batch of data items. It also can be achieved within the workflows by using the `TransformInverter` handler. + +If the pipeline includes random transformations, users may want to observe the effect that these transformations have on the output. The typical approach is that we pass the same input through the transforms multiple times with different random realizations. Then use the inverse transforms to move all the results to a common space, and calculate the metrics. MONAI provided `TestTimeAugmentation` for this feature, which by default will calculate the `mode`, `mean`, `standard deviation` and `volume variation coefficient`. + +[Invert transforms and TTA tutorials](https://github.com/Project-MONAI/tutorials/blob/master/modules/inverse_transforms_and_test_time_augmentations.ipynb) introduce details about the API with examples. + +(1) The last column is the inverted data of model output: +![invert transform](../images/invert_transforms.png) + +(2) The TTA results of `mode`, `mean` and `standard deviation`: +![test time augmentation](../images/tta.png) + +## Lesion detection in digital pathology +MONAI starts to support digital pathology deep learning tasks. The initial [implementation](https://github.com/Project-MONAI/MONAI/tree/master/monai/apps/pathology) of the pathology detection components includes: +- Efficient whole slide imaging IO with NVIDIA cuCIM library +- Patch-based sampling and training strategies with the SmartCache mechanism +- FROC measurements for lesion detection +- Probabilistic post-processing for lesion ROIs. + +![digital pathology](../images/pathology.png) + +## DeepGrow modules for interactive segmentation +Towards an interactive workflow with manual input during training and inference, +[a reimplementation](https://github.com/Project-MONAI/MONAI/tree/master/monai/apps/deepgrow) of the DeepGrow components is included in this release. +DeepGrow is a deep learning based semi-automated segmentation approach that aims to be a "smart" interactive tool for regions of interest delineation in medical images. + +![deepgrow scheme](../images/deepgrow_scheme.png) + +An end-to-end example is presented at [`project-monai/tutorials`](https://github.com/Project-MONAI/tutorials/tree/master/deepgrow/ignite). +![deepgrow end-to-end](../images/deepgrow.png) + +## Learning-based image registration +Starting from v0.5, MONAI provides experimental features for building learning-based 2D/3D registration workflows. These include image similarity measures as loss functions, bending energy as model regularization, network architectures, warping modules. The components can be used to build the major unsupervised and weakly-supervised algorithms. + +The following figure shows the registration of CT images acquired at different time points for a single patient using MONAI: + +![3d registration](../images/3d_paired.png) + +## Various usability improvements +### IO factory for medical image formats +Many popular image formats exist in the medical domain, and they are quite different with rich metadata information. To easily handle different medical image formats in the same pipeline, [MONAI provides `LoadImage` transform](https://github.com/Project-MONAI/tutorials/blob/master/modules/load_medical_images.ipynb), which can automatically choose image readers based on the supported suffixes and in the below priority order: +- User-specified reader at runtime when call this loader. +- Registered readers from the latest to the first in list. +- Default readers: (nii, nii.gz -> NibabelReader), (png, jpg, bmp -> PILReader), (npz, npy -> NumpyReader), (others -> ITKReader). + +The `ImageReader` API is quite straight-forward, users can easily extend for their own customized image readers. + +With these pre-defined image readers, MONAI can load images in formats: `NIfTI`, `DICOM`, `PNG`, `JPG`, `BMP`, `NPY/NPZ`, etc. + +### Save transform data into NIfTI or PNG files +To convert images into files or debug the transform chain, MONAI provides `SaveImage` transform. Users can inject this transform into the transform chain to save the results. + +### Automatically ensure `channel-first` data shape +Medical images have different shape formats. They can be `channel-last`, `channel-first` or even `no-channel`. We may, for example, want to load several `no-channel` images and stack them as `channel-first` data. To improve the user experience, MONAI provided an `EnsureChannelFirst` transform to automatically detect data shape according to the meta information and convert it to the `channel-first` format consistently. + +### Network architectures +Various ready-to-use architectures with pretrained model weights from `torch.hub`. + +### Result writing +Currently MONAI supports writing the model outputs as NIfTI files or PNG files for segmentation tasks, and as CSV files for classification tasks. And the writers can restore the data spacing, orientation or shape according to the `original_shape` or `original_affine` information from the input image. + +A rich set of formats will be supported soon, along with relevant statistics and evaluation metrics automatically computed from the outputs. + +### Transfer learning for different input / output classes +`Transfer-learning` is a very common and efficient training approach, especially in the medical-specific domain where obtaining large datasets for training can be difficult. So transfer-learning from a pre-trained checkpoint can significantly improve the model metrics and shorten training time. + +MONAI provided `CheckpointLoader` to load a checkpoint for the workflow before training, and it allows some `layer names` of current network don't match the checkpoint, or some `layer shapes` don't match the checkpoint, which can be useful if the current task has different input image classes or output classes. + +### C++/CUDA optimized modules +To accelerate some heavy computation progress, C++/CUDA implementation can be an impressive method, which usually brings even hundreds of times faster performance. MONAI contains some C++/CUDA optimized modules, like `Resampler`, `Conditional random field (CRF)`, `Fast bilateral filtering using the permutohedral lattice`, and fully support C++/CUDA programs in CI/CD and building package. diff --git a/MONAI/source/docs/source/whatsnew_0_6.md b/MONAI/source/docs/source/whatsnew_0_6.md new file mode 100644 index 0000000000000000000000000000000000000000..0efe9847cb55f53ff18d5fbec909fedec5adabc8 --- /dev/null +++ b/MONAI/source/docs/source/whatsnew_0_6.md @@ -0,0 +1,96 @@ +# What's new in 0.6 + +- Decollating mini-batches as an essential post-processing step +- Pythonic APIs to load the pretrained models from Clara Train MMARs +- UNETR: Transformers for Medical Image Segmentation +- Enhancements of the base metric interfaces +- C++/CUDA extension modules via PyTorch JIT compilation +- Backward compatibility and enhanced continuous integration/continuous delivery +- Collaboration with Project-MONAI/MONAILabel for smooth integration + + +## Decollating mini-batches as an essential post-processing step +`decollate batch` is introduced in MONAI v0.6, to simplify the post-processing transforms and enable flexible operations on a batch of model outputs. +It can decollate batched data (e.g. model inference results) into a list of tensors -- as an 'inverse' operation of `collate_fn` of the PyTorch data loader. It has the benefits such as: +- enabling postprocessing transforms for each item independently, for example, randomised transforms could be applied differently for each predicted item in a batch. +- simplifying the transform APIs and reducing the input validation burdens, because both the preprocessing and postprocessing transforms now only support the "channel-first" input format. +- enabling the transform inverse operation for data items in different original shapes, as the inverted items are in a list, instead of being stacked in a single tensor. +- allowing for both a "batch-first" tensor and a list of "channel-first" tensors for flexible metric computation. + +A typical process of `decollate batch` is illustrated as follows (with a `batch_size=N` model predictions and labels as an example): +![decollate_batch](../images/decollate_batch.png) + +[decollate batch tutorial](https://github.com/Project-MONAI/tutorials/blob/master/modules/decollate_batch.ipynb) shows a detailed usage example based on a PyTorch native workflow. + +[Migrating your v0.5 code to v0.6](https://github.com/Project-MONAI/MONAI/wiki/v0.5-to-v0.6-migration-guide) wiki shows how to migrate an existing program from v0.5 to v0.6 to adapt to the `decollate batch` logic. + +## UNETR: Transformers for Medical Image Segmentation +[UNETR](https://arxiv.org/abs/2103.10504) is a transformer-based model for volumetric (3D) medical image segmentation and is currently the state-of-the-art on [BTCV dataset](https://www.synapse.org/#!Synapse:syn3193805/wiki/217752) test server for the task of multi-organ semantic segmentation. UNETR is introduced in MONAI v0.6 and its flexible implementation supports various segmentation tasks. +![UNETR](../images/UNETR.png) + +A tutorial for the task of 3D multi-organ semantic segmentation using UNETR is provided within +[`project-monai/tutorials`](https://github.com/Project-MONAI/tutorials/blob/master/3d_segmentation/unetr_btcv_segmentation_3d.ipynb). +And it contains the following features: +- Transforms for dictionary format data, +- Defining a new transform according to MONAI transform API, +- Loading Nifti image with metadata, loading a list of images and stacking them, +- Randomly adjusting the intensity for data augmentation, +- Optimized cache IO and transforms to accelerate training and validation, +- 3D UNETR model, DiceCE loss function and Mean Dice metric for multi-organ segmentation task, + +The following illustrates target body organs that are segmentation in this tutorial: +![BTCV_organs](../images/BTCV_organs.png) + +Please visit UNETR repository for more details: +https://project-monai.github.io/research/unetr-btcv-multi-organ-segmentation + +## Pythonic APIs to load the pretrained models from Clara Train MMARs +[The MMAR (Medical Model ARchive)](https://docs.nvidia.com/clara/clara-train-sdk/pt/mmar.html) +defines a data structure for organizing all artifacts produced during the model development life cycle. +NVIDIA Clara provides [various MMARs of medical domain-specific models](https://ngc.nvidia.com/catalog/models?orderBy=scoreDESC&pageNumber=0&query=clara_pt&quickFilter=&filters=). +These MMARs include all the information about the model including configurations and scripts to provide a workspace to perform model development tasks. To better leverage the trained MMARs released on Nvidia GPU cloud, MONAI provides pythonic APIs to access them. + +To demonstrate this new feature, a medical image segmentation tutorial is created within +[`project-monai/tutorials`](https://github.com/Project-MONAI/tutorials/blob/master/modules/transfer_mmar.ipynb). +It mainly produces the following figure to compare the loss curves and validation scores for +- training from scratch (the green line), +- applying pretrained MMAR weights without training (the magenta line), +- training from the MMAR model weights (the blue line), + +according to the number of training epochs: + +![transfer_mmar](../images/transfer_mmar.png) + +The tutorial shows the capability of encapsulating the details of MMAR parsing, as well as the potential of using pretrained MMARs for transfer learning. +These APIs are also being integrated into AI-assisted interactive workflows to accelerate the manual annotating processes (e.g. via [project-MONAI/MONAILabel](https://github.com/Project-MONAI/MONAILabel)). + +## Enhancements of the base metric interfaces +The base API for metrics is now enhanced to support the essential computation logic for both iteration and epoch-based metrics. +With this update, the MONAI metrics module becomes more extensible, and thus a good starting point for customised metrics. +The APIs also by default support data parallel computation and consider the computation efficiency: with a `Cumulative` base class, intermediate metric outcomes can be automatically buffered, cumulated, synced across distributed processes, and aggregated for the final results. The [multi-processing computation example](https://github.com/Project-MONAI/tutorials/blob/master/modules/compute_metric.py) shows how to compute metrics based on saved predictions and labels in multi-processing environment. + +## C++/CUDA extension modules via PyTorch JIT compilation +To further accelerate the domain-specific routines in the workflows, MONAI C++/CUDA modules are introduced as extensions of the PyTorch native implementation. +It now provides modules using [the two ways of building C++ extensions from PyTorch](https://pytorch.org/tutorials/advanced/cpp_extension.html#custom-c-and-cuda-extensions): +- via `setuptools` (since MONAI v0.5), for modules including `Resampler`, `Conditional random field (CRF)`, `Fast bilateral filtering using the permutohedral lattice`. +- via just-in-time (JIT) compilation (since MONAI v0.6), for the `Gaussian mixtures` module. This approach allows for dynamic optimisation according to the user-specified parameters and local system environments. +The following figure shows results of MONAI's Gaussian mixture models applied to a tissue and surgical tools segmentation task: +![Gaussian mixture models as a postprocessing step](../images/gmm_feature_set_comparison_s.png) + +## Backward compatibility and enhanced continuous integration/continuous delivery +Starting from this version, we experiment with basic policies of backward compatibility. +New utilities are introduced on top of the existing semantic versioning modules, and the git branching model. + +At the same time, we actively analyze efficient, scalable, and secure CI/CD solutions to accommodate fast and collaborative codebase development. + +Although a complete mechanism is still under development, these provide another essential step towards API-stable versions of MONAI, sustainable release cycles, and efficient open-source collaborations. + +## Collaboration with [`Project-MONAI/MONAILabel`](https://github.com/Project-MONAI/MONAILabel) for smooth integration +Since MONAI v0.6, we welcome [`MONAILabel`](https://github.com/Project-MONAI/MONAILabel) under [`Project-MONAI`](https://github.com/Project-MONAI). + +MONAI Label is an intelligent open source image labeling and learning tool that enables users to create annotated datasets and build AI annotation models for clinical evaluation. +MONAI Label enables application developers to build labeling apps in a serverless way, +where custom labeling apps are exposed as a service through the MONAI Label Server. + +Please visit MONAILabel documentation website for details: +https://monai.readthedocs.io/projects/label/en/latest/ diff --git a/MONAI/source/docs/source/whatsnew_0_7.md b/MONAI/source/docs/source/whatsnew_0_7.md new file mode 100644 index 0000000000000000000000000000000000000000..6f515e64c3fba378620494fd240bc8afaa75c544 --- /dev/null +++ b/MONAI/source/docs/source/whatsnew_0_7.md @@ -0,0 +1,63 @@ +# What's new in 0.7 + +- Performance enhancements with profiling and tuning guides +- Major usability improvements in `monai.transforms` +- Reimplementing state-of-the-art Kaggle solutions +- Vision-language multimodal transformer architectures + +## Performance enhancements with profiling and tuning guides + +Model training is often a time-consuming step during deep learning development, +especially for medical imaging applications. Even with powerful hardware (e.g. +CPU/GPU with large RAM), the workflows often require careful profiling and +tuning to achieve high performance. MONAI has been focusing on performance +enhancements, and in this version, a fast model training guide is provided +to help build highly performant workflows, with a comprehensive overview of +the profiling tools and practical strategies: +https://github.com/Project-MONAI/tutorials/blob/master/acceleration/fast_model_training_guide.md. + +The following figure shows the use of [Nvidia Nsight™ Systems](https://developer.nvidia.com/nsight-systems) for system-wide +performance analysis during a performance enhancement study. +![nsight_vis](../images/nsight_comparison.png) + +With the performance profiling and enhancements, several typical use cases were studied to +improve the training efficiency. The following figure shows that fast +training using MONAI can be `200` times faster than a regular baseline ([learn +more](https://github.com/Project-MONAI/tutorials/blob/master/acceleration/fast_training_tutorial.ipynb)), and it's `20` times faster than the MONAI v0.6 fast training solution. +![fast_training](../images/fast_training.png) + +## Major usability improvements in `monai.transforms` for NumPy/PyTorch inputs and backends + + MONAI starts to roll out major usability enhancements for the + `monai.transforms` module. Many transforms are now supporting both NumPy and + PyTorch, as input types and computational backends. To get the supported backends of every transform, please execute: `python monai/transforms/utils.py`. + +One benefit of these enhancements is that the users can now better leverage the +GPUs for preprocessing. By transferring the input data onto GPU using +`ToTensor` or `EnsureType`, and applying the GPU-based transforms to the data, +[the tutorial of spleen +segmentation](https://github.com/Project-MONAI/tutorials/blob/master/acceleration/fast_training_tutorial.ipynb) +shows the great potential of using the flexible modules for fast and efficient +training. + +## Reimplementing state-of-the-art Kaggle solutions + +With this release, we actively evaluate and enhance the quality and flexibility +of the MONAI core modules, using the public Kaggle challenge as a testbed. [A +reimplementation](https://github.com/Project-MONAI/tutorials/tree/main/competitions/kaggle/RANZCR/4th_place_solution) +of a state-of-the-art solution at [Kaggle RANZCR CLiP - Catheter and Line +Position +Challenge](https://www.kaggle.com/c/ranzcr-clip-catheter-line-classification) +is made available in this version. + +## Vision-language multimodal transformers + +In this release, MONAI adds support for training multimodal (vision + language) +transformers that can handle both image and textual data. MONAI introduces the +`TransCheX` model which consists of vision, language, and mixed-modality +transformer layers for processing chest X-ray and their corresponding +radiological reports within a unified framework. In addition to `TransCheX`, +users have the flexibility to alter the architecture by varying the number of +vision, language and mixed-modality layers and customizing the classification +head. In addition, the model can be initialized from pre-trained BERT language +models for fine-tuning. diff --git a/MONAI/source/docs/source/whatsnew_0_8.md b/MONAI/source/docs/source/whatsnew_0_8.md new file mode 100644 index 0000000000000000000000000000000000000000..ac63c8dbd0bd59036d866512552fa5e62f9fdaf3 --- /dev/null +++ b/MONAI/source/docs/source/whatsnew_0_8.md @@ -0,0 +1,56 @@ +# What's new in 0.8 + +- Differentiable neural network topology search +- Multiple instance learning for digital pathology WSI analysis +- Self-supervised representation learning +- Major usability improvements in `monai.transforms` + +## Differentiable neural network topology search +MONAI integrates `DiNTS`: [Differentiable Neural Network Topology Search for 3D +Medical Image Segmentation](https://arxiv.org/abs/2103.15954). The neural +architecture search module supports flexible multi-path topology search with +high search efficiency and budgeted memory usage. + +It provides a topology guaranteed discretization algorithm and a +discretization-aware topology loss for the search stage to minimize the +discretization gap. The module is memory usage aware and is able to search 3D +networks with different GPU memory requirements. For more details, please check out the +[DiNTS tutorial](https://project-monai.github.io/research/dints.html). + +![DiNTS](../images/dints-overview.png) + +## Multiple instance learning for digital pathology WSI analysis +For [classification of digital pathology whole slide images +(WSI)](https://arxiv.org/abs/2111.01556), MONAI introduces new transforms and +network modules for multiple instance learning. These include self-attention +transformer blocks for explicitly accounting of the dependencies between instances +(image patches) during training. For more details, +please check out the [multiple instance learning tutorial](https://github.com/Project-MONAI/tutorials/tree/master/pathology/multiple_instance_learning). + +![multi-instance](../images/mil-patches.jpg) + +## Self-supervised representation learning +MONAI starts to explore self-supervised representation learning in this +milestone release. The Vision Transformer has been extended to learn from self-supervised +reconstruction tasks with various data augmentation and a regularized +contrastive loss. The weights of the pre-trained backbone could be used to +enhance the performance of the novel downstream deep learning tasks. + +The [tutorial](https://github.com/Project-MONAI/tutorials/tree/master/self_supervised_pretraining) +shows how to generate a good set of pre-trained weights using unlabeled data +with self-supervised tasks, then use the pre-trained weights to perform +fine-tuning on a fully supervised volumetric segmentation task using a transformer based `UNETR`. + +![self-supervised](../images/ssl_overview.png) + +## Major usability improvements in `monai.transforms` +`monai.transforms` are now more flexible and easy to use in version 0.8. +- Input type handling and backend APIs are improved to support both + NumPy and PyTorch where possible. +- Visual examples are added to the documentation to illustrate the effects of + various image processing. +- New visualization utilities are provided and enhanced for quick qualitative + assessments of the model by visualizing, for example, the volumetric image + inputs, segmentation maps, and intermediate feature maps. + The visualization tutorial is available for + [TensorBoard utility, `matshow3d` and `blend_images`](https://github.com/Project-MONAI/tutorials/blob/master/modules/transform_visualization.ipynb). diff --git a/MONAI/source/docs/source/whatsnew_0_9.md b/MONAI/source/docs/source/whatsnew_0_9.md new file mode 100644 index 0000000000000000000000000000000000000000..4b884ebb78969286720890cfbe690a0dc29e3231 --- /dev/null +++ b/MONAI/source/docs/source/whatsnew_0_9.md @@ -0,0 +1,61 @@ +# What's new in 0.9 + +- MONAI Bundle +- Object detection in medical images +- Swin Transformers for 3D medical image analysis +- New interactive segmentation components +- MetaTensor API preview + +## MONAI Bundle +MONAI Bundle format defines portable described of deep learning models ([docs](https://monai.readthedocs.io/en/latest/bundle_intro.html)). +A bundle includes the critical information necessary during a model development life cycle, +and allows users and programs to understand the purpose and usage of the models. +The key benefits of Bundle and the `monai.bundle` APIs are: +- Standardized packaging format for storing and sharing models, +- Structured configuration files for fast prototyping of deep learning workflows, +- Easy to program APIs to separate deep learning hyperparameter settings from the Python code, +- Flexible config components to allow for different low-level Python implementations, +- Help to decouple the component details from higher level learning paradigms such as federated learning and AutoML. + +More details are [in the tutorials](https://github.com/Project-MONAI/tutorials/tree/main/bundle). + +## Object detection in medical images +This release includes essential components for object localization and categorization workflows. +The initial developments include 2D and 3D bounding box handling, network blocks and architectures of RetinaNet, +and common utility modules such as coordinate-based preprocessing, hard negative sampler. + +The application specific modules are made available at +[monai.apps.detection](https://github.com/Project-MONAI/MONAI/tree/dev/monai/apps/detection). + +![detection workflow](../images/detection.png) + + +## Swin Transformers for 3D medical image analysis +The Swin UNETR model is now implemented in MONAI. +[The tutorial](https://github.com/Project-MONAI/tutorials/blob/main/3d_segmentation/swin_unetr_btcv_segmentation_3d.ipynb) +shows examples of multi-organ segmentation using this state-of-the-art model, +with weights from self-supervised pre-training of +Swin UNETR encoder (3D Swin Transformer) on a cohort of 5050 CT scans from publicly available datasets. +[The research-contribution entry](https://github.com/Project-MONAI/research-contributions/tree/main/SwinUNETR) +includes further technical details. + +![swin-unetr](../images/swin_unetr.png) + +## New interactive segmentation components +New components from deep learning interactive segmentation workflows +such as [DeepEdit](https://github.com/Project-MONAI/tutorials/tree/main/deepedit/ignite) +and NuClick are integrated into the core codebase. They serve as basic building blocks for +[the latest features in MONAILabel](https://github.com/Project-MONAI/MONAILabel). + +![deepedit](../images/deepedit.png) + +![nuclick](../images/nuclick.png) + +## MetaTensor API preview +The metadata associated with the primary imaging modalities is important in many biomedical applications, +especially for the data-driven approaches that MONAI has been focusing. +Starting from this release, we roll out a major refactoring for data representation in MONAI. For the first +step, [the core data structures](https://github.com/Project-MONAI/MONAI/blob/dev/monai/data/meta_tensor.py) +`MetaTensor` and `MetaObj` are implemented as a feature preview. +Further developments [on the feature branch](https://github.com/Project-MONAI/MONAI/pull/4539) +will be made available in future milestone releases. diff --git a/MONAI/source/docs/source/whatsnew_1_0.md b/MONAI/source/docs/source/whatsnew_1_0.md new file mode 100644 index 0000000000000000000000000000000000000000..91ce13351c32fcf3685cbe85da94d9c6d06d166a --- /dev/null +++ b/MONAI/source/docs/source/whatsnew_1_0.md @@ -0,0 +1,67 @@ +# What's new in 1.0 + +- Model Zoo +- Auto3DSeg +- Federated Learning Client +- MetaTensor Support for Digital Pathology Workflows +- Accelerated MRI Reconstruction + + +## Model Zoo +The MONAI Model Zoo is a place for researchers and data scientists to use and share the latest and great models from the community. +Utilizing [the MONAI Bundle format](https://github.com/Project-MONAI/tutorials/tree/main/bundle) makes it easy to quickly get started using any model with any MONAI Framework (Core, Label, or Deploy). +Or, if you're interested in [contributing your models](https://github.com/project-monai/model-zoo), take a look at our contributing guidelines, +which walks you through the process and requirements for submitting your model. +For more details about how to use the models, please see [the tutorials](https://github.com/Project-MONAI/tutorials/tree/main/model_zoo). + +## Auto3DSeg +![auto3dseg](../images/auto3dseg.png) + +[Auto3DSeg](https://project-monai.github.io/apps/auto3dseg.html) is a comprehensive solution for large-scale 3D medical image segmentation. +It leverages the latest advances in MONAI +and GPUs to efficiently develop and deploy algorithms with state-of-the-art performance. +It first analyzes the global information such as intensity, dimensionality, and resolution of the dataset, +then generates algorithms in MONAI bundle format based on data statistics and [algorithm templates](https://github.com/Project-MONAI/research-contributions/tree/main/auto3dseg). +Next, all algorithms initiate model training to obtain checkpoints with the best validation performance. +Finally, the ensemble module selects the algorithms via ranking trained checkpoints and creates ensemble predictions. + +The solution offers different levels of user experience for beginners and advanced researchers. +It has been tested on large-scale 3D medical imaging datasets in different modalities. + +## Federated Learning Client +![federated-learning](../images/federated.svg) + +MONAI now includes the federated learning (FL) client algorithm APIs that are exposed as an abstract base class +for defining an algorithm to be run on any federated learning platform. +[NVIDIA FLARE](https://github.com/NVIDIA/NVFlare), the federated learning platform developed by [NVIDIA](https://www.nvidia.com/en-us/), +has already built [the integration piece](https://github.com/NVIDIA/NVFlare/tree/dev/integration/monai) with these new APIs. +With [the new federated learning APIs](https://monai.readthedocs.io/en/latest/fl.html), MONAI bundles can seamlessly be extended to a federated paradigm +and executed using single- or multi-GPU training. +The MONAI FL client also allows computing summary data statistics (e.g., intensity histograms) on the datasets defined in the bundle configs. +These can be shared and visualized on the FL server, for example, using NVIDIA FLARE's federated statistics operators, +see [here](https://github.com/NVIDIA/NVFlare/tree/dev/integration/monai/examples) for an example. + +We welcome other federated learning toolkits to integrate with MONAI FL APIs, building a common foundation for +collaborative learning in medical imaging. + +## MetaTensor Support for Digital Pathology Workflows +![pathology](../images/pathology-meta.png) + +In this release, we support MetaTensor in all digital pathology components, and +make sure that the future development can benefit from them. With the help of +MONAI Pathology Working Group, we have standardized a set of metadata +attributes for patches of images extracted from WSI to ensure reproducibility +and enhance functionality via relying on a standard set of attributes. The +figure above shows all the pathology metadata attributes and their relation to +MetaTensors. Please see [the tutorials and +examples](https://github.com/Project-MONAI/tutorials/tree/main/pathology). + +## Accelerated MRI Reconstruction +![MRI-reconstruction](../images/mri_recon.png) + +This release includes initial components for various popular accelerated MRI reconstruction workflows. +Many of them are general-purpose tools, for example the [`SSIMLoss`](https://monai.readthedocs.io/en/latest/losses.html?highlight=ssimloss#ssimloss) function. +Some new functionalities are task-specific, for example [`FastMRIReader`](https://monai.readthedocs.io/en/latest/data.html?highlight=fastmri#monai.apps.reconstruction.fastmri_reader.FastMRIReader). + +For more details, please see [this tutorial](https://github.com/Project-MONAI/tutorials/tree/main/reconstruction/MRI_reconstruction/unet_demo) for using a baseline model for this task, +and [this tutorial](https://github.com/Project-MONAI/tutorials/tree/main/reconstruction/MRI_reconstruction/varnet_demo) for using a state-of-the-art model. diff --git a/MONAI/source/docs/source/whatsnew_1_1.md b/MONAI/source/docs/source/whatsnew_1_1.md new file mode 100644 index 0000000000000000000000000000000000000000..b4b2f4026ef4a0b0070fd635cbe06e35f73974b8 --- /dev/null +++ b/MONAI/source/docs/source/whatsnew_1_1.md @@ -0,0 +1,75 @@ +# What's new in 1.1 + +- Digital pathology workflows +- Experiment management for MONAI bundle +- Auto3dSeg enhancements +- New models in MONAI Model Zoo +- State-of-the-art SurgToolLoc solution + +## Digital pathology workflows + +![hovernet](../images/hovernet_diagram.png) + +Hover-Net is a model for simultaneous segmentation and classification of nuclei in multi-tissue histology images (Graham et al. Medical Image Analysis, 2019). +We have added support for this model in MONAI by implementing several new components, enhancing existing ones and providing pipelines and examples for training, validation and inference. + +Along with the modules release, new digital pathology analysis tutorials are made available: + +- [HoVerNet pipelines](https://github.com/Project-MONAI/tutorials/tree/main/pathology/hovernet) based on MONAI workflows for training, validation and inference +- [HoVerNet tutorial](https://github.com/Project-MONAI/tutorials/blob/main/pathology/hovernet/hovernet_torch.ipynb) for training, validation and inference +- NuClick (Interactive Annotation for Pathology) tutorials for [training](https://github.com/Project-MONAI/tutorials/blob/main/pathology/nuclick/nuclick_training_notebook.ipynb) +and [inference](https://github.com/Project-MONAI/tutorials/blob/main/pathology/nuclick/nuclick_infer.ipynb) +- Nuclei classification tutorials for [training](https://github.com/Project-MONAI/tutorials/blob/main/pathology/nuclick/nuclei_classification_training_notebook.ipynb) +and [inference](https://github.com/Project-MONAI/tutorials/blob/main/pathology/nuclick/nuclei_classification_infer.ipynb) + +## Experiment management for MONAI bundle + +![exp_mgmt](../images/exp_mgmt.png) + +In this release, experiment management features are integrated with MONAI bundle. +It provides essential APIs for managing the end-to-end model bundle lifecycle. +Users can start tracking experiments by, for example, appending `--tracking "mlflow"` to the training or inference commands to enable the MLFlow-based management. +By default, MLFlow will track the executed bundle config, model quality measurements, and source code versioning. +For more details, please refer to the [tutorial](https://github.com/Project-MONAI/tutorials/blob/main/experiment_management/bundle_integrate_mlflow.ipynb). + +## Auto3dSeg enhancements + +Multiple improvements have been added in `Auto3DSeg` both in terms of +usability and performance. +- Multi-modality support is added and applied for +automated segmentation of the HECKTOR22 challenge dataset, which includes input 3D +CT and PET images of various resolutions and sizes. A tutorial example of +running Auto3DSeg on the HECKTOR22 challenge dataset is available in MONAI +Tutorials. The tutorial is based on [the HECKTOR22 challenge 1st place solution](https://arxiv.org/abs/2209.10809). +- A new improved version of `Segresnet` Algo is now available in `AutoRunner`. +In this version, data caching is more efficient and the preprocessing transforms are more flexible. +The workflow progresses including the timings of steps are written to console output as well as a YAML file. +- Automatic customization and optimization of the model training configuration +can be achieved according to the GPU devices used. The feature +focuses on determining parameters including batch size of model +training and sliding-window inference, allocated devices for +data in sliding-window inference. For more details about how to enable it, please see [the tutorials](https://github.com/Project-MONAI/tutorials/tree/main/auto3dseg). + +## New models in MONAI Model Zoo + +New pretrained models are being created and released [in the Model Zoo](https://project-monai.github.io/model-zoo.html). +Notably, + +- The `mednist_reg` model demonstrates how to build image registration workflows in MONAI bundle +format. The model uses a ResNet and spatial transformer for hand X-ray image registration based on +[the registration_mednist tutorial](https://github.com/Project-MONAI/tutorials/blob/main/2d_registration/registration_mednist.ipynb), +- `pathology_nuclei_segmentation_and_classification`, + `pathology_nuclick_annotation`, and `pathology_nuclei_classification` bundles + are built for [digital pathology image + analysis](https://github.com/Project-MONAI/model-zoo/tree/dev/models/pathology_nuclei_segmentation_classification). + +For more details about how to use the models, please see [the tutorials](https://github.com/Project-MONAI/tutorials/tree/main/model_zoo). + +## State-of-the-art SurgToolLoc solution + +[SurgToolLoc](https://surgtoolloc.grand-challenge.org/Home/) is a part of the +[EndoVis](https://endovis.grand-challenge.org/) challenge at [MICCAI 2022](https://conferences.miccai.org/2022/en/). +The challenge focuses on endoscopic video analysis and is divided into (1) fully supervised tool classification +and (2) weakly supervised tool classification/localization. +Team NVIDIA won prizes by finishing [third](https://surgtoolloc.grand-challenge.org/results/) in both categories. +The core components of the solutions [are released in MONAI](https://github.com/Project-MONAI/tutorials/tree/main/competitions/MICCAI/surgtoolloc). diff --git a/MONAI/source/docs/source/whatsnew_1_2.md b/MONAI/source/docs/source/whatsnew_1_2.md new file mode 100644 index 0000000000000000000000000000000000000000..a5ea7d31650fef156b4f0d5d570e4274faa82a68 --- /dev/null +++ b/MONAI/source/docs/source/whatsnew_1_2.md @@ -0,0 +1,77 @@ +# What's new in 1.2 + +- Auto3DSeg enhancements and benchmarks +- nnUNet integration +- TensorRT-optimized networks +- MetricsReloaded integration +- Bundle workflow APIs +- Modular patch inference +- Lazy resampling for preprocessing + +## Auto3DSeg enhancements and benchmarks +Auto3DSeg is an innovative solution for 3D medical image segmentation, leveraging the advancements in MONAI and GPUs for algorithm development and deployment. +Key improvements in this release include: +- Several new modules to the training pipelines, such as automated GPU-based hyperparameter scaling, early stopping mechanisms, and dynamic validation frequency. +- Multi-GPU parallelism has been activated for all GPU-related components including data analysis, model training, and model ensemble, to augment overall performance and capabilities. +- The algorithms were benchmarked for computational efficiency on the TotalSegmentator dataset, containing over 1,000 CT images. +- Multi-node training is implemented, reducing model training time significantly. + + +## nnUNet integration +The integration introduces a new class, `nnUNetV2Runner`, which leverages Python APIs to facilitate model training, validation, +and ensemble, thereby simplifying the data conversion process for users. +Benchmarking results from various public datasets confirm that nnUNetV2Runner performs as expected. +Users are required to prepare a data list and create an `input.yaml` file to install and use the system. +The framework also allows automatic execution of the entire nnU-Net pipeline, from model training to ensemble, +with options to specify the number of epochs. Users can access APIs for training, dataset conversion, data preprocessing, and other components. +Please check out [the tutorials](https://github.com/Project-MONAI/tutorials/tree/main/nnunet) for more details. + +## TensorRT-optimized networks +[NVIDIA TensorRT](https://developer.nvidia.com/tensorrt) is an SDK for high-performance deep learning inference, +includes a deep learning inference optimizer and runtime that delivers low latency and high throughput for inference applications. +It can accelerate the deep learning model forward computation on the NVIDIA GPU. +In this release, the `trt_export` API to export the TensorRT engine-based TorchScript model has been integrated into the MONAI bundle. +Users can try to export bundles with it. A few bundles in the MONAI model zoo, +like the [spleen_ct_segmentation](https://github.com/Project-MONAI/model-zoo/tree/dev/models/spleen_ct_segmentation) +and [endoscopic_tool_segmentation](https://github.com/Project-MONAI/model-zoo/tree/dev/models/endoscopic_tool_segmentation) bundles, +have already been exported and benchmarked. For more details about how to export and benchmark a model, +please go to this [tutorial](https://github.com/Project-MONAI/tutorials/blob/main/acceleration/TensorRT_inference_acceleration.ipynb). + + +## MetricsReloaded integration +MetricsReloaded - a new recommendation framework for biomedical image analysis validation - is released publicly +via https://github.com/Project-MONAI/MetricsReloaded. Binary and categorical metrics computing modules are included in this release, +using MetricsReloaded as the backend. [Example scripts](https://github.com/Project-MONAI/tutorials/tree/main/modules/metrics_reloaded) are made available to demonstrate the usage. + + +## Bundle workflow APIs +`BundleWorkflow` abstracts the typical workflows (such as training, evaluation, and inference) of a bundle with three main interfaces: +`initialize`, `run`, and `finalize`, applications use these APIs to execute a bundle. +It unifies the required properties and optional properties for the workflows, downstream applications +can invoke the components instead of parsing configs with keys. +In this release, `ConfigWorkflow` class is also created for JSON and YAML config-based bundle workflows for improved Pythonic usability. + + +## Modular patch inference +In patch inference, patches are extracted from the image, the inference is run on those patches, and outputs are merged +to construct the result image corresponding to the input image. Although depending on the task, model, and computational/memory resources, +the exact implementations of a patch inference may vary, the overall process of splitting, running inference, and merging the results remains the same. +In this release, we have created a modular design for patch inference, which defines the overall process while abstracting away the specific +behavior of how to split the image into patches, how to pre and post process each patch, and how to merge the output patches. + +## Lazy Resampling for preprocessing +Lazy Resampling is a new, experimental feature for preprocessing. It works under +the hood along with MONAI transforms to combine adjacent spatial and +cropping transforms into a single operation. This allows MONAI to reduce the number of data resamples + a pipeline undergoes. Depending on the preprocessing pipeline, it can potentially: + +* reduce processing time +* reduce processing memory +* reduce incidental artifacts added by resampling +* preserve data that would otherwise be cropped and replaced with padding + +Lazy Resampling pipelines can use a mixture of MONAI and non-MONAI transforms, so +should work with almost all existing pipelines simply by setting `lazy=True` +on MONAI `Compose` instances. See the +[Lazy Resampling topic](https://monai.readthedocs.io/en/stable/lazy_resampling.html) +in the documentation for more details. diff --git a/MONAI/source/docs/source/whatsnew_1_3.md b/MONAI/source/docs/source/whatsnew_1_3.md new file mode 100644 index 0000000000000000000000000000000000000000..6480547eecdcd82cb9a1d422c4c90d19e1eec460 --- /dev/null +++ b/MONAI/source/docs/source/whatsnew_1_3.md @@ -0,0 +1,23 @@ +# What's new in 1.3 + +- Bundle usability enhancements +- Integrating MONAI Generative into MONAI core + + +## Bundle usability enhancements + +Based on the experience of building MONAI model zoo and the feedback from the community, +MONAI 1.3 provides major enhancements in MONAI Bundle usability. These include: +- Pythonic APIs for Bundle trying to strike a balance between code readability and workflow standardization; +- Streamlined Bundle building processes with step-by-step guides to the concepts; +- Various utility functions for fetching and fine-tuning models from [MONAI Model Zoo](https://github.com/Project-MONAI/model-zoo); +- Various fixes for Bundle syntax and documentation, improved test coverage across the Bundle module and Model Zoo. + +For more details please visit [the Bundle tutorials](https://github.com/Project-MONAI/tutorials/tree/main/bundle) and +[the Model Zoo demos](https://github.com/Project-MONAI/tutorials/tree/main/model_zoo). + +## Integrating MONAI Generative into MONAI Core + +Main modules developed at [MONAI GenerativeModels](https://github.com/Project-MONAI/GenerativeModels) +are being ported into the core codebase, allowing for consistent maintenance and release of the key components for generative AI. +As a starting point, loss functions and metrics are integrated into this version. diff --git a/MONAI/source/docs/source/whatsnew_1_4.md b/MONAI/source/docs/source/whatsnew_1_4.md new file mode 100644 index 0000000000000000000000000000000000000000..b3a2877d98d09c4319717e2dcf4dc58780c978df --- /dev/null +++ b/MONAI/source/docs/source/whatsnew_1_4.md @@ -0,0 +1,68 @@ +# What's new in 1.4 + +- MAISI: state-of-the-art 3D Latent Diffusion Model +- VISTA-3D: interactive foundation model for segmenting and anotating human anatomies +- VISTA-2D: cell segmentation pipeline +- Integrating MONAI Generative into MONAI core +- Lazy TensorRT export via `trt_compile` +- Geometric Data Support + + +## MAISI: state-of-the-art 3D Latent Diffusion Model + +![maisi](../images/maisi_train.png) + +MAISI (Medical AI for Synthetic Imaging) is a state-of-the-art three-dimensional (3D) Latent Diffusion Model designed for generating high-quality synthetic CT images with or without anatomical annotations. This AI model excels in data augmentation and creating realistic medical imaging data to supplement limited datasets due to privacy concerns or rare conditions. It can also significantly enhance the performance of other medical imaging AI models by generating diverse and realistic training data. + +A tutorial for generating large CT images accompanied by corresponding segmentation masks using MAISI is provided within +[`project-monai/tutorials`](https://github.com/Project-MONAI/tutorials/blob/main/generation/maisi). +It contains the following features: +- A foundation Variational Auto-Encoder (VAE) model for latent feature compression that works for both CT and MRI with flexible volume size and voxel size +- A foundation Diffusion model that can generate large CT volumes up to 512 × 512 × 768 size, with flexible volume size and voxel size +- A ControlNet to generate image/mask pairs that can improve downstream tasks, with controllable organ/tumor size + +## VISTA-3D: state-of-the-art 3D Latent Diffusion Model + +![vista-3d](../images/vista3d.png) + +VISTA-3D is a specialized interactive foundation model for 3D medical imaging. It excels in providing accurate and adaptable segmentation analysis across anatomies and modalities. Utilizing a multi-head architecture, VISTA-3D adapts to varying conditions and anatomical areas, helping guide users' annotation workflow. + +A tutorial showing how to finetune VISTA-3D on spleen dataset is provided within +[`project-monai/tutorials`](https://github.com/Project-MONAI/tutorials/blob/main/vista_3d). +It supports three core workflows: +- Segment everything: Enables whole body exploration, crucial for understanding complex diseases affecting multiple organs and for holistic treatment planning. +- Segment using class: Provides detailed sectional views based on specific classes, essential for targeted disease analysis or organ mapping, such as tumor identification in critical organs. +- Segment point prompts: Enhances segmentation precision through user-directed, click-based selection. This interactive approach accelerates the creation of accurate ground-truth data, essential in medical imaging analysis. + +## VISTA-2D: cell segmentation pipeline + +![vista-2d](../images/vista2d.png) + +VISTA-2D is a comprehensive training and inference pipeline for cell segmentation in imaging applications. For more information, refer to this [Blog](https://developer.nvidia.com/blog/advancing-cell-segmentation-and-morphology-analysis-with-nvidia-ai-foundation-model-vista-2d/) + +Key features of the model include: +- A robust deep learning algorithm utilizing transformers +- Foundational model as compared to specialist models +- Supports a wide variety of datasets and file formats +- Capable of handling multiple imaging modalities +- Multi-GPU and multinode training support + +A tutorial demonstrating how to train a cell segmentation model using the MONAI framework on the Cellpose dataset can be found in [`project-monai/tutorials`](https://github.com/Project-MONAI/tutorials/blob/main/vista_2d). + +## Integrating MONAI Generative into MONAI Core + +Key modules originally developed in the [MONAI GenerativeModels](https://github.com/Project-MONAI/GenerativeModels) repository have been integrated into the core MONAI codebase. This integration ensures consistent maintenance and streamlined release of essential components for generative AI. In this version, all utilities, networks, diffusion schedulers, inferers, and engines have been migrated into the core codebase. Special care has been taken to ensure saved weights from models trained using GenerativeModels can be loaded into those now integrated into core. + +Additionally, several tutorials have been ported and are available within [`project-monai/tutorials`](https://github.com/Project-MONAI/tutorials/blob/main/generation) + +## Lazy TensorRT export via `trt_compile` +This release expands TensorRT optimization options for MONAI bundles with `trt_compile` API. +The existing `trt_export` API requires the user to run a separate export script to prepare a TensorRT engine-based TorchScript model. +`trt_compile` builds and saves a TensorRT engine the first time a bundle is run and provides limited dependency support. +It also allows partial TensorRT export where only a certain submodule is being optimized, which improves usability. +A few bundles in the MONAI model zoo, like the new [VISTA-3D](https://github.com/Project-MONAI/model-zoo/tree/dev/models/vista3d) +and [VISTA-2D](https://github.com/Project-MONAI/model-zoo/tree/dev/models/vista2d) bundles, already come with `trt_inference.json` config files which use `trt_compile`. + +## Geometric Data Support + +MONAI introduces support for geometric data transformations as a key feature. As a starting point, ApplyTransformToPoints transform is added to facilitate matrix operations on points, enabling flexible and efficient handling of geometric transformations. Alongside this, the framework now supports conversions between boxes and points, providing seamless interoperability within detection pipelines. These updates have been integrated into existing pipelines, such as the [detection tutorial](https://github.com/Project-MONAI/tutorials/blob/main/detection) and the [3D registration workflow](https://github.com/Project-MONAI/tutorials/blob/main/3d_registration/learn2reg_nlst_paired_lung_ct.ipynb), leveraging the latest APIs for improved functionality. diff --git a/MONAI/source/docs/source/whatsnew_1_5.md b/MONAI/source/docs/source/whatsnew_1_5.md new file mode 100644 index 0000000000000000000000000000000000000000..8b68d7168667abff4093fa4ebabf28eb15b32c9c --- /dev/null +++ b/MONAI/source/docs/source/whatsnew_1_5.md @@ -0,0 +1,56 @@ + +# What's new in 1.5 + +- Support numpy 2.x and Pytorch 2.6 +- MAISI inference accelerate +- Bundles storage changed to huggingface and correspoinding api updated in core +- Ported remaining generative tutorials and bundles +- New tutorials: + - [2d_regression/image_restoration.ipynb](https://github.com/Project-MONAI/tutorials/blob/main/2d_regression/image_restoration.ipynb) + - [generation/2d_diffusion_autoencoder/2d_diffusion_autoencoder_tutorial.ipynb](https://github.com/Project-MONAI/tutorials/blob/main/generation/2d_diffusion_autoencoder/2d_diffusion_autoencoder_tutorial.ipynb) + - [generation/3d_ddpm/3d_ddpm_tutorial.ipynb](https://github.com/Project-MONAI/tutorials/blob/main/generation/3d_ddpm/3d_ddpm_tutorial.ipynb) + - [generation/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.ipynb](https://github.com/Project-MONAI/tutorials/blob/main/generation/classifier_free_guidance/2d_ddpm_classifier_free_guidance_tutorial.ipynb) + - [hugging_face/finetune_vista3d_for_hugging_face_pipeline.ipynb](https://github.com/Project-MONAI/tutorials/blob/main/hugging_face/finetune_vista3d_for_hugging_face_pipeline.ipynb) + - [hugging_face/hugging_face_pipeline_for_monai.ipynb](https://github.com/Project-MONAI/tutorials/blob/main/hugging_face/hugging_face_pipeline_for_monai.ipynb) + - [modules/omniverse/omniverse_integration.ipynb](https://github.com/Project-MONAI/tutorials/blob/main/modules/omniverse/omniverse_integration.ipynb) +- New Bundles: + - [models/cxr_image_synthesis_latent_diffusion_model](https://github.com/Project-MONAI/model-zoo/blob/dev/models/cxr_image_synthesis_latent_diffusion_model) + - [models/mednist_ddpm](https://github.com/Project-MONAI/model-zoo/blob/dev/models/mednist_ddpm) + - [models/brain_image_synthesis_latent_diffusion_model](https://github.com/Project-MONAI/model-zoo/blob/dev/models/mednist_ddpm) + - [hf_models/exaonepath-crc-msi-predictor](https://github.com/Project-MONAI/model-zoo/blob/dev/hf_models/exaonepath-crc-msi-predictor) + - All existing bundles are also now [hosted on Huggingface](https://huggingface.co/MONAI)! + +## Supported Dependency Versions + +This release adds support for NumPy 2.0 and PyTorch 2.6. We plan to add support for PyTorch 2.7 in an upcoming version once some compatibility issues have been addressed. + +As stated in the updated [README.md](https://github.com/Project-MONAI/MONAI/blob/main/README.md) file, MONAI's policy for the support of dependency versions has been updated for clarity. + +MONAI will continue to support [currently supported versions of Python](https://devguide.python.org/versions), and for other dependencies the following apply: + +* Major releases of MONAI will have dependency versions stated for them. The current state of the `dev` branch in this repository is the unreleased development version of MONAI which typically will support current versions of dependencies and include updates and bug fixes to do so. +* PyTorch support covers [the current version](https://github.com/pytorch/pytorch/releases) plus three previous minor versions. If compatibility issues with a PyTorch version and other dependencies arise, support for a version may be delayed until a major release. +* Our support policy for other dependencies adheres for the most part to [SPEC0](https://scientific-python.org/specs/spec-0000), where dependency versions are supported where possible for up to two years. Discovered vulnerabilities or defects may require certain versions to be explicitly not supported. +* See the `requirements*.txt` files for dependency version information. + +## MAISI Update: Introducing MAISI Version maisi3d-rflow + +![maisi](../images/maisi_infer.png) + +We are excited to announce the release of MAISI Version _maisi3d-rflow_. This update brings significant improvements over the previous version, _maisi3d-ddpm_, with a remarkable 33x acceleration in latent diffusion model inference speed. The MAISI VAE remains unchanged. Here are the key differences: + 1. Scheduler Update: + + * _maisi3d-ddpm_: Uses the basic DDPM noise scheduler. + + * _maisi3d-rflow_: Introduces the Rectified Flow scheduler, allowing diffusion model inference to be 33 times faster. + 2. Training Data Preparation: + + * _maisi3d-ddpm_: Requires training images to be labeled with body regions (specifically “top_region_index” and “bottom_region_index”). + + * _maisi3d-rflow_: No such labeling is required, making it easier to prepare the training data. + 3. Image Quality: + + * For the released model weights, _maisi3d-rflow_ generates better-quality images for head regions and smaller output volumes compared to _maisi3d-ddpm_. For other regions, the image quality is comparable. + 4. Modality Input: + + * _maisi3d-rflow_ adds a new modality input to the diffusion model, offering flexibility for future extensions to other modalities. Currently, this input is set to always equal 1, as this version supports CT generation exclusively. diff --git a/MONAI/source/docs/source/whatsnew_1_5_1.md b/MONAI/source/docs/source/whatsnew_1_5_1.md new file mode 100644 index 0000000000000000000000000000000000000000..6fb1c4af5a13030e1bbebeb638d010c7ce97a0ae --- /dev/null +++ b/MONAI/source/docs/source/whatsnew_1_5_1.md @@ -0,0 +1,12 @@ + +# What's new in 1.5.1 + +This is a minor update for MONAI to address security concerns and improve compatibility with the newest PyTorch release. + +With the upgrade support for PyTorch 2.8, MONAI now directly support NVIDIA GeForce RTX 50 series GPUs and other Blackwell-based GPUs! + +- Support up to PyTorch 2.8. +- Security fixes to address advisories [GHSA-x6ww-pf9m-m73m](https://github.com/Project-MONAI/MONAI/security/advisories/GHSA-x6ww-pf9m-m73m), [GHSA-6vm5-6jv9-rjpj](https://github.com/Project-MONAI/MONAI/security/advisories/GHSA-6vm5-6jv9-rjpj), and [GHSA-p8cm-mm2v-gwjm](https://github.com/Project-MONAI/MONAI/security/advisories/GHSA-p8cm-mm2v-gwjm), +- Updated version of supported Huggingface Transformers library to address security advisories raised for it. +- Updated Torchvision pretrained network loading to use current arguments. +- Many minor fixes to identified issues, see release notes for details on merged PRs. diff --git a/MONAI/source/docs/source/whatsnew_1_5_2.md b/MONAI/source/docs/source/whatsnew_1_5_2.md new file mode 100644 index 0000000000000000000000000000000000000000..7a468956c595d5b7a3849444f1ff58fb1285a531 --- /dev/null +++ b/MONAI/source/docs/source/whatsnew_1_5_2.md @@ -0,0 +1,6 @@ + +# What's new in 1.5.2 🎉🎉 + +This is a minor update for MONAI to address a security concern. + +- Security fix to address advisory [GHSA-9rg3-9pvr-6p27](https://github.com/Project-MONAI/MONAI/security/advisories/GHSA-9rg3-9pvr-6p27). diff --git a/MONAI/source/environment-dev.yml b/MONAI/source/environment-dev.yml new file mode 100644 index 0000000000000000000000000000000000000000..9358cdc83b3c793ef18683b587328f24f93ff520 --- /dev/null +++ b/MONAI/source/environment-dev.yml @@ -0,0 +1,15 @@ +name: monai +channels: + - pytorch + - defaults + - nvidia + - conda-forge +dependencies: + - numpy>=1.24,<3.0 + - pytorch>=2.3.0 + - torchio + - torchvision + - pytorch-cuda>=11.6 + - pip + - pip: + - -r requirements-dev.txt diff --git a/MONAI/source/monai/README.md b/MONAI/source/monai/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a1e36c62100cc46ec01c41a4d1bd72aa93705649 --- /dev/null +++ b/MONAI/source/monai/README.md @@ -0,0 +1,38 @@ +# MONAI + +* **apps**: high level medical domain specific deep learning applications. + +* **auto3dseg**: automated machine learning (AutoML) components for volumetric image analysis. + +* **bundle**: components to build the portable self-descriptive model bundle. + +* **config**: for system configuration and diagnostic output. + +* **csrc**: for C++/CUDA extensions. + +* **data**: for the datasets, readers/writers, and synthetic data. + +* **engines**: engine-derived classes for extending Ignite behaviour. + +* **fl**: federated learning components to allow pipeline integration with any federated learning framework. + +* **handlers**: defines handlers for implementing functionality at various stages in the training process. + +* **inferers**: defines model inference methods. + +* **losses**: classes defining loss functions, which follow the pattern of `torch.nn.modules.loss`. + +* **metrics**: defines metric tracking types. + +* **networks**: contains network definitions, component definitions, and Pytorch specific utilities. + +* **optimizers**: classes defining optimizers, which follow the pattern of `torch.optim`. + +* **transforms**: defines data transforms for preprocessing and postprocessing. + +* **utils**: generic utilities intended to be implemented in pure Python or using Numpy, +and not with Pytorch, such as namespace aliasing, auto module loading. + +* **visualize**: utilities for data visualization. + +* **_extensions**: C++/CUDA extensions to be loaded in a just-in-time manner using `torch.utils.cpp_extension.load`. diff --git a/MONAI/source/monai/__init__.py b/MONAI/source/monai/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d92557a8e1abc31598c2c4e07fa51f95517706e3 --- /dev/null +++ b/MONAI/source/monai/__init__.py @@ -0,0 +1,138 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import logging +import os +import sys +import warnings + +from ._version import get_versions + +old_showwarning = warnings.showwarning + + +def custom_warning_handler(message, category, filename, lineno, file=None, line=None): + ignore_files = ["ignite/handlers/checkpoint", "modelopt/torch/quantization/tensor_quant"] + if any(ignore in filename for ignore in ignore_files): + return + old_showwarning(message, category, filename, lineno, file, line) + + +class DeprecatedTypesWarningFilter(logging.Filter): + def filter(self, record): + message_bodies_to_ignore = [ + "np.bool8", + "np.object0", + "np.int0", + "np.uint0", + "np.void0", + "np.str0", + "np.bytes0", + "@validator", + "@root_validator", + "class-based `config`", + "pkg_resources", + "Implicitly cleaning up", + ] + for message in message_bodies_to_ignore: + if message in record.getMessage(): + return False + return True + + +# workaround for https://github.com/Project-MONAI/MONAI/issues/8060 +# TODO: remove this workaround after upstream fixed the warning +# Set the custom warning handler to filter warning +warnings.showwarning = custom_warning_handler +# Get the logger for warnings and add the filter to the logger +logging.getLogger("py.warnings").addFilter(DeprecatedTypesWarningFilter()) + + +PY_REQUIRED_MAJOR = 3 +PY_REQUIRED_MINOR = 9 + +version_dict = get_versions() +__version__: str = version_dict.get("version", "0+unknown") +__revision_id__: str = version_dict.get("full-revisionid") +del get_versions, version_dict + +__copyright__ = "(c) MONAI Consortium" + +__basedir__ = os.path.dirname(__file__) + +if sys.version_info.major != PY_REQUIRED_MAJOR or sys.version_info.minor < PY_REQUIRED_MINOR: + import warnings + + warnings.warn( + f"MONAI requires Python {PY_REQUIRED_MAJOR}.{PY_REQUIRED_MINOR} or higher. " + f"But the current Python is: {sys.version}", + category=RuntimeWarning, + ) + + +from .utils.module import load_submodules # noqa: E402 + +# handlers_* have some external decorators the users may not have installed +# *.so files and folder "_C" may not exist when the cpp extensions are not compiled +excludes = "|".join( + [ + "(^(monai.handlers))", + "(^(monai.bundle))", + "(^(monai.fl))", + "((\\.so)$)", + "(^(monai._C))", + "(.*(__main__)$)", + "(.*(video_dataset)$)", + "(.*(nnunet).*$)", + ] +) + +# load directory modules only, skip loading individual files +load_submodules(sys.modules[__name__], False, exclude_pattern=excludes) + +# load all modules, this will trigger all export decorations +load_submodules(sys.modules[__name__], True, exclude_pattern=excludes) + +__all__ = [ + "apps", + "auto3dseg", + "bundle", + "config", + "data", + "engines", + "fl", + "handlers", + "inferers", + "losses", + "metrics", + "networks", + "optimizers", + "transforms", + "utils", + "visualize", +] + +try: + from .utils.tf32 import detect_default_tf32 + + detect_default_tf32() + import torch + + # workaround related to https://github.com/Project-MONAI/MONAI/issues/7575 + if hasattr(torch.cuda.device_count, "cache_clear"): + torch.cuda.device_count.cache_clear() +except BaseException: + from .utils.misc import MONAIEnvVars + + if MONAIEnvVars.debug(): + raise diff --git a/MONAI/source/monai/_extensions/__init__.py b/MONAI/source/monai/_extensions/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..47d0c7021ac5adb355ded3b6c0d68b920b88d6ad --- /dev/null +++ b/MONAI/source/monai/_extensions/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from .loader import load_module diff --git a/MONAI/source/monai/_extensions/gmm/gmm.cpp b/MONAI/source/monai/_extensions/gmm/gmm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..577e5b117ef13fb81f9aeaac8c70bd22fda963cb --- /dev/null +++ b/MONAI/source/monai/_extensions/gmm/gmm.cpp @@ -0,0 +1,83 @@ +/* +Copyright (c) MONAI Consortium +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include + +#include "gmm.h" + +py::tuple init() { + torch::Tensor gmm_tensor = + torch::zeros({GMM_COUNT, GMM_COMPONENT_COUNT}, torch::dtype(torch::kFloat32).device(torch::kCUDA)); + torch::Tensor scratch_tensor = torch::empty({1}, torch::dtype(torch::kFloat32).device(torch::kCUDA)); + return py::make_tuple(gmm_tensor, scratch_tensor); +} + +void learn( + torch::Tensor gmm_tensor, + torch::Tensor scratch_tensor, + torch::Tensor input_tensor, + torch::Tensor label_tensor) { + c10::DeviceType device_type = input_tensor.device().type(); + + unsigned int batch_count = input_tensor.size(0); + unsigned int element_count = input_tensor.stride(1); + + unsigned int scratch_size = + batch_count * (element_count + GMM_COMPONENT_COUNT * GMM_COUNT * (element_count / (32 * 32))); + + if (scratch_tensor.size(0) < scratch_size) { + scratch_tensor.resize_({scratch_size}); + } + + float* gmm = gmm_tensor.data_ptr(); + float* scratch = scratch_tensor.data_ptr(); + float* input = input_tensor.data_ptr(); + int* labels = label_tensor.data_ptr(); + + if (device_type == torch::kCUDA) { + learn_cuda(input, labels, gmm, scratch, batch_count, element_count); + } else { + learn_cpu(input, labels, gmm, scratch, batch_count, element_count); + } +} + +torch::Tensor apply(torch::Tensor gmm_tensor, torch::Tensor input_tensor) { + c10::DeviceType device_type = input_tensor.device().type(); + + unsigned int dim = input_tensor.dim(); + unsigned int batch_count = input_tensor.size(0); + unsigned int element_count = input_tensor.stride(1); + + auto output_size = input_tensor.sizes().vec(); + output_size[1] = MIXTURE_COUNT; + torch::Tensor output_tensor = + torch::empty(c10::IntArrayRef(output_size), torch::dtype(torch::kFloat32).device(device_type)); + + const float* gmm = gmm_tensor.data_ptr(); + const float* input = input_tensor.data_ptr(); + float* output = output_tensor.data_ptr(); + + if (device_type == torch::kCUDA) { + apply_cuda(gmm, input, output, batch_count, element_count); + } else { + apply_cpu(gmm, input, output, batch_count, element_count); + } + + return output_tensor; +} + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("init", torch::wrap_pybind_function(init)); + m.def("learn", torch::wrap_pybind_function(learn)); + m.def("apply", torch::wrap_pybind_function(apply)); +} diff --git a/MONAI/source/monai/_extensions/gmm/gmm.h b/MONAI/source/monai/_extensions/gmm/gmm.h new file mode 100644 index 0000000000000000000000000000000000000000..09c0389ae66f0161e3ca4d997f0ce0a95e66e5df --- /dev/null +++ b/MONAI/source/monai/_extensions/gmm/gmm.h @@ -0,0 +1,53 @@ +/* +Copyright (c) MONAI Consortium +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#if !defined(CHANNEL_COUNT) || !defined(MIXTURE_COUNT) || !defined(MIXTURE_SIZE) +#error Definition of CHANNEL_COUNT, MIXTURE_COUNT, and MIXTURE_SIZE required +#endif + +#if CHANNEL_COUNT < 1 || MIXTURE_COUNT < 1 || MIXTURE_SIZE < 1 +#error CHANNEL_COUNT, MIXTURE_COUNT, and MIXTURE_SIZE must be positive +#endif + +#define MATRIX_COMPONENT_COUNT ((CHANNEL_COUNT + 1) * (CHANNEL_COUNT + 2) / 2) +#define SUB_MATRIX_COMPONENT_COUNT (CHANNEL_COUNT * (CHANNEL_COUNT + 1) / 2) +#define GMM_COMPONENT_COUNT (MATRIX_COMPONENT_COUNT + 1) +#define GMM_COUNT (MIXTURE_COUNT * MIXTURE_SIZE) + +void learn_cpu( + const float* input, + const int* labels, + float* gmm, + float* scratch_memory, + unsigned int batch_count, + unsigned int element_count); +void apply_cpu( + const float* gmm, + const float* input, + float* output, + unsigned int batch_count, + unsigned int element_count); + +void learn_cuda( + const float* input, + const int* labels, + float* gmm, + float* scratch_memory, + unsigned int batch_count, + unsigned int element_count); +void apply_cuda( + const float* gmm, + const float* input, + float* output, + unsigned int batch_count, + unsigned int element_count); diff --git a/MONAI/source/monai/_extensions/gmm/gmm_cpu.cpp b/MONAI/source/monai/_extensions/gmm/gmm_cpu.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d7eedc07c8602b9c08e09d2be1c4431eb6045d7e --- /dev/null +++ b/MONAI/source/monai/_extensions/gmm/gmm_cpu.cpp @@ -0,0 +1,35 @@ +/* +Copyright (c) MONAI Consortium +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include + +#include "gmm.h" + +void learn_cpu( + const float* input, + const int* labels, + float* gmm, + float* scratch_memory, + unsigned int batch_count, + unsigned int element_count) { + throw std::invalid_argument("GMM received a cpu tensor but is not yet implemented for the cpu"); +} + +void apply_cpu( + const float* gmm, + const float* input, + float* output, + unsigned int batch_count, + unsigned int element_count) { + throw std::invalid_argument("GMM received a cpu tensor but is not yet implemented for the cpu"); +} diff --git a/MONAI/source/monai/_extensions/gmm/gmm_cuda.cu b/MONAI/source/monai/_extensions/gmm/gmm_cuda.cu new file mode 100644 index 0000000000000000000000000000000000000000..0c808d3165d29c4e646bb0390e7ccb62a84a1f6e --- /dev/null +++ b/MONAI/source/monai/_extensions/gmm/gmm_cuda.cu @@ -0,0 +1,516 @@ +/* +Copyright (c) MONAI Consortium +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include + +#include "gmm.h" + +#include "gmm_cuda_linalg.cuh" + +#define EPSILON 1e-5 +#define BLOCK_SIZE 32 +#define TILE(SIZE, STRIDE) ((((SIZE)-1) / (STRIDE)) + 1) +#ifdef __HIP_PLATFORM_AMD__ +#define __SHFL_DOWN(a, b) __shfl_down(a, b) +#define __SHFL_XOR(a, b) __shfl_xor(a, b) +#else +#define __SHFL_DOWN(a, b) __shfl_down_sync(0xffffffff, a, b) +#define __SHFL_XOR(a, b) __shfl_xor_sync(0xffffffff, a, b) +#endif + +template +__global__ void CovarianceReductionKernel( + int gaussian_index, + const float* g_image, + const int* g_alpha, + float* g_matrices, + int element_count) { + constexpr int block_size = warp_count * 32; + + __shared__ float s_matrix_component[warp_count]; + + int batch_index = blockIdx.z; + + const float* g_batch_image = g_image + batch_index * element_count * CHANNEL_COUNT; + const int* g_batch_alpha = g_alpha + batch_index * element_count; + float* g_batch_matrices = g_matrices + batch_index * GMM_COUNT * GMM_COMPONENT_COUNT * gridDim.x; + + int local_index = threadIdx.x; + int block_index = blockIdx.x; + int warp_index = local_index >> 5; + int lane_index = local_index & 31; + int global_index = local_index + block_index * block_size * load_count; + int matrix_offset = (gaussian_index * gridDim.x + block_index) * GMM_COMPONENT_COUNT; + + float matrix[MATRIX_COMPONENT_COUNT]; + + for (int i = 0; i < MATRIX_COMPONENT_COUNT; i++) { + matrix[i] = 0; + } + + for (int load = 0; load < load_count; load++) { + global_index += load * block_size; + + if (global_index < element_count) { + int my_alpha = g_batch_alpha[global_index]; + + if (my_alpha != -1) { + if (gaussian_index == (my_alpha & 15) + (my_alpha >> 4) * MIXTURE_COUNT) { + float feature[CHANNEL_COUNT + 1]; + + feature[0] = 1; + + for (int i = 0; i < CHANNEL_COUNT; i++) { + feature[i + 1] = g_batch_image[global_index + i * element_count]; + } + + for (int index = 0, i = 0; i < CHANNEL_COUNT + 1; i++) { + for (int j = i; j < CHANNEL_COUNT + 1; j++, index++) { + matrix[index] += feature[i] * feature[j]; + } + } + } + } + } + } + + __syncthreads(); + + for (int i = 0; i < MATRIX_COMPONENT_COUNT; i++) { + float matrix_component = matrix[i]; + matrix_component += __SHFL_DOWN(matrix_component, 16); + matrix_component += __SHFL_DOWN(matrix_component, 8); + matrix_component += __SHFL_DOWN(matrix_component, 4); + matrix_component += __SHFL_DOWN(matrix_component, 2); + matrix_component += __SHFL_DOWN(matrix_component, 1); + if (lane_index == 0) { + s_matrix_component[warp_index] = matrix_component; + } + + __syncthreads(); + + if (warp_index == 0) { + matrix_component = s_matrix_component[lane_index]; + if (warp_count >= 32) { + matrix_component += __SHFL_DOWN(matrix_component, 16); + } + if (warp_count >= 16) { + matrix_component += __SHFL_DOWN(matrix_component, 8); + } + if (warp_count >= 8) { + matrix_component += __SHFL_DOWN(matrix_component, 4); + } + if (warp_count >= 4) { + matrix_component += __SHFL_DOWN(matrix_component, 2); + } + if (warp_count >= 2) { + matrix_component += __SHFL_DOWN(matrix_component, 1); + } + if (lane_index == 0) { + g_batch_matrices[matrix_offset + i] = matrix_component; + } + } + + __syncthreads(); + } +} + +template +__global__ void CovarianceFinalizationKernel(const float* g_matrices, float* g_gmm, int matrix_count) { + constexpr int block_size = warp_count * 32; + + __shared__ float s_matrix_component[warp_count]; + __shared__ float s_gmm[GMM_COMPONENT_COUNT]; + + int batch_index = blockIdx.z; + + const float* g_batch_matrices = g_matrices + batch_index * GMM_COUNT * GMM_COMPONENT_COUNT * matrix_count; + float* g_batch_gmm = g_gmm + batch_index * GMM_COUNT * GMM_COMPONENT_COUNT; + + int local_index = threadIdx.x; + int warp_index = local_index >> 5; + int lane_index = local_index & 31; + int gmm_index = blockIdx.x; + int matrix_offset = gmm_index * matrix_count; + + int load_count = TILE(matrix_count, block_size); + + float norm_factor = 1.0f; + + for (int index = 0, i = 0; i < CHANNEL_COUNT + 1; i++) { + for (int j = i; j < CHANNEL_COUNT + 1; j++, index++) { + float matrix_component = 0.0f; + + for (int load = 0; load < load_count; load++) { + int matrix_index = local_index + load * block_size; + + if (matrix_index < matrix_count) { + matrix_component += g_batch_matrices[(matrix_offset + matrix_index) * GMM_COMPONENT_COUNT + index]; + } + } + matrix_component += __SHFL_DOWN(matrix_component, 16); + matrix_component += __SHFL_DOWN(matrix_component, 8); + matrix_component += __SHFL_DOWN(matrix_component, 4); + matrix_component += __SHFL_DOWN(matrix_component, 2); + matrix_component += __SHFL_DOWN(matrix_component, 1); + if (lane_index == 0) { + s_matrix_component[warp_index] = matrix_component; + } + + __syncthreads(); + + if (warp_index == 0) { + matrix_component = s_matrix_component[lane_index]; + if (warp_count >= 32) { + matrix_component += __SHFL_DOWN(matrix_component, 16); + } + if (warp_count >= 16) { + matrix_component += __SHFL_DOWN(matrix_component, 8); + } + if (warp_count >= 8) { + matrix_component += __SHFL_DOWN(matrix_component, 4); + } + if (warp_count >= 4) { + matrix_component += __SHFL_DOWN(matrix_component, 2); + } + if (warp_count >= 2) { + matrix_component += __SHFL_DOWN(matrix_component, 1); + } + if (lane_index == 0) { + float constant = i == 0 ? 0.0f : s_gmm[i] * s_gmm[j]; + + if (i != 0 && i == j) { + constant -= EPSILON; + } + + s_gmm[index] = norm_factor * matrix_component - constant; + + if (index == 0 && matrix_component > 0) { + norm_factor = 1.0f / matrix_component; + } + } + } + + __syncthreads(); + } + } + + float* matrix = s_gmm + (CHANNEL_COUNT + 1); + float* det_ptr = s_gmm + MATRIX_COMPONENT_COUNT; + + if (local_index == 0) { + float square_mat[CHANNEL_COUNT][CHANNEL_COUNT]; + float cholesky_mat[CHANNEL_COUNT][CHANNEL_COUNT]; + + for (int i = 0; i < CHANNEL_COUNT; i++) { + for (int j = 0; j < CHANNEL_COUNT; j++) { + square_mat[i][j] = 0.0f; + cholesky_mat[i][j] = 0.0f; + } + } + + to_square(matrix, square_mat); + cholesky(square_mat, cholesky_mat); + + *det_ptr = chol_det(cholesky_mat); + + if (invert_matrix) { + chol_inv(cholesky_mat, square_mat); + to_triangle(square_mat, matrix); + } + } + + if (local_index < GMM_COMPONENT_COUNT) { + g_batch_gmm[gmm_index * GMM_COMPONENT_COUNT + local_index] = s_gmm[local_index]; + } +} + +struct GMMSplit_t { + int idx; + float threshold; + float eigenvector[CHANNEL_COUNT]; +}; + +// 1 Block, 32xMIXTURE_COUNT +__global__ void GMMFindSplit(GMMSplit_t* gmmSplit, int gmmK, float* gmm) { + int batch_index = blockIdx.z; + + float* g_batch_gmm = gmm + batch_index * GMM_COUNT * GMM_COMPONENT_COUNT; + GMMSplit_t* g_batch_gmmSplit = gmmSplit + batch_index * MIXTURE_COUNT; + + int gmm_idx = threadIdx.x * MIXTURE_COUNT + threadIdx.y; + + float eigenvalue = 0; + float eigenvector[CHANNEL_COUNT]; + + if (threadIdx.x < gmmK) { + float* matrix = g_batch_gmm + gmm_idx * GMM_COMPONENT_COUNT + (CHANNEL_COUNT + 1); + largest_eigenpair(matrix, eigenvector, &eigenvalue); + } + + float max_value = eigenvalue; + max_value = max(max_value, __SHFL_XOR(max_value, 16)); + max_value = max(max_value, __SHFL_XOR(max_value, 8)); + max_value = max(max_value, __SHFL_XOR(max_value, 4)); + max_value = max(max_value, __SHFL_XOR(max_value, 2)); + max_value = max(max_value, __SHFL_XOR(max_value, 1)); + if (max_value == eigenvalue) { + GMMSplit_t split; + + float* average_feature = gmm + gmm_idx * GMM_COMPONENT_COUNT + 1; + + split.idx = threadIdx.x; + split.threshold = scalar_prod(average_feature, eigenvector); + + for (int i = 0; i < CHANNEL_COUNT; i++) { + split.eigenvector[i] = eigenvector[i]; + } + + g_batch_gmmSplit[threadIdx.y] = split; + } +} + +#define DO_SPLIT_DEGENERACY 4 + +__global__ void GMMDoSplit(const GMMSplit_t* gmmSplit, int k, const float* image, int* alpha, int element_count) { + __shared__ GMMSplit_t s_gmmSplit[MIXTURE_COUNT]; + + int batch_index = blockIdx.z; + + const GMMSplit_t* g_batch_gmmSplit = gmmSplit + batch_index * MIXTURE_COUNT; + const float* g_batch_image = image + batch_index * element_count * CHANNEL_COUNT; + int* g_batch_alpha = alpha + batch_index * element_count; + + int* s_linear = (int*)s_gmmSplit; + int* g_linear = (int*)g_batch_gmmSplit; + + if (threadIdx.x < MIXTURE_COUNT * sizeof(GMMSplit_t)) { + s_linear[threadIdx.x] = g_linear[threadIdx.x]; + } + + __syncthreads(); + + int index = threadIdx.x + blockIdx.x * BLOCK_SIZE * DO_SPLIT_DEGENERACY; + + for (int i = 0; i < DO_SPLIT_DEGENERACY; i++) { + index += BLOCK_SIZE; + + if (index < element_count) { + int my_alpha = g_batch_alpha[index]; + + if (my_alpha != -1) { + int select = my_alpha & 15; + int gmm_idx = my_alpha >> 4; + + if (gmm_idx == s_gmmSplit[select].idx) { + // in the split cluster now + float feature[CHANNEL_COUNT]; + + for (int i = 0; i < CHANNEL_COUNT; i++) { + feature[i] = g_batch_image[index + i * element_count]; + } + + float value = scalar_prod(s_gmmSplit[select].eigenvector, feature); + + if (value > s_gmmSplit[select].threshold) { + // assign pixel to new cluster + g_batch_alpha[index] = k + select; + } + } + } + } + } +} + +// Single block, 32xMIXTURE_COUNT +__global__ void GMMcommonTerm(float* g_gmm) { + int batch_index = blockIdx.z; + + float* g_batch_gmm = g_gmm + batch_index * GMM_COUNT * GMM_COMPONENT_COUNT; + + int gmm_index = (threadIdx.x * MIXTURE_COUNT) + threadIdx.y; + + float gmm_n = threadIdx.x < MIXTURE_SIZE ? g_batch_gmm[gmm_index * GMM_COMPONENT_COUNT] : 0.0f; + + float sum = gmm_n; + sum += __SHFL_XOR(sum, 1); + sum += __SHFL_XOR(sum, 2); + sum += __SHFL_XOR(sum, 4); + sum += __SHFL_XOR(sum, 8); + sum += __SHFL_XOR(sum, 16); + + if (threadIdx.x < MIXTURE_SIZE) { + float det = g_batch_gmm[gmm_index * GMM_COMPONENT_COUNT + MATRIX_COMPONENT_COUNT] + EPSILON; + float commonTerm = det > 0.0f ? gmm_n / (sqrtf(det) * sum) : gmm_n / sum; + + g_batch_gmm[gmm_index * GMM_COMPONENT_COUNT + MATRIX_COMPONENT_COUNT] = commonTerm; + } +} + +__device__ float GMMTerm(float* feature, const float* gmm) { + const float* average_feature = gmm + 1; + const float* matrix = gmm + CHANNEL_COUNT + 1; + + float diff[CHANNEL_COUNT]; + + for (int i = 0; i < CHANNEL_COUNT; i++) { + diff[i] = feature[i] - average_feature[i]; + } + + float value = 0.0f; + + for (int index = 0, i = 0; i < CHANNEL_COUNT; i++) { + for (int j = i; j < CHANNEL_COUNT; j++, index++) { + float term = diff[i] * diff[j] * matrix[index]; + + value += i == j ? term : 2 * term; + } + } + + return gmm[MATRIX_COMPONENT_COUNT] * expf(-0.5 * value); +} + +__global__ void GMMDataTermKernel(const float* image, const float* gmm, float* output, int element_count) { + int batch_index = blockIdx.z; + + const float* g_batch_image = image + batch_index * element_count * CHANNEL_COUNT; + const float* g_batch_gmm = gmm + batch_index * GMM_COUNT * GMM_COMPONENT_COUNT; + float* g_batch_output = output + batch_index * element_count * MIXTURE_COUNT; + + int index = blockIdx.x * blockDim.x + threadIdx.x; + + if (index >= element_count) + return; + + float feature[CHANNEL_COUNT]; + + for (int i = 0; i < CHANNEL_COUNT; i++) { + feature[i] = g_batch_image[index + i * element_count]; + } + + float weights[MIXTURE_COUNT]; + float weight_total = 0.0f; + + for (int i = 0; i < MIXTURE_COUNT; i++) { + float mixture_weight = 0.0f; + + for (int j = 0; j < MIXTURE_SIZE; j++) { + mixture_weight += GMMTerm(feature, &g_batch_gmm[(MIXTURE_COUNT * j + i) * GMM_COMPONENT_COUNT]); + } + + weights[i] = mixture_weight; + weight_total += mixture_weight; + } + + for (int i = 0; i < MIXTURE_COUNT; i++) { + // protecting against pixels with 0 in all mixtures + float final_weight = weight_total > 0.0f ? weights[i] / weight_total : 0.0f; + g_batch_output[index + i * element_count] = final_weight; + } +} + +#define THREADS 512 +#define WARPS 16 +#define BLOCK (WARPS << 5) +#define LOAD 4 + +void GMMInitialize( + const float* image, + int* alpha, + float* gmm, + float* scratch_mem, + unsigned int batch_count, + unsigned int element_count) { + unsigned int block_count = TILE(element_count, BLOCK * LOAD); + + float* block_gmm_scratch = scratch_mem; + GMMSplit_t* gmm_split_scratch = (GMMSplit_t*)scratch_mem; + + int gmm_N = MIXTURE_COUNT * MIXTURE_SIZE; + + for (unsigned int k = MIXTURE_COUNT; k < gmm_N; k += MIXTURE_COUNT) { + for (unsigned int i = 0; i < k; ++i) { + CovarianceReductionKernel + <<>>(i, image, alpha, block_gmm_scratch, element_count); + } + + CovarianceFinalizationKernel<<>>(block_gmm_scratch, gmm, block_count); + + GMMFindSplit<<>>( + gmm_split_scratch, k / MIXTURE_COUNT, gmm); + GMMDoSplit<<>>( + gmm_split_scratch, (k / MIXTURE_COUNT) << 4, image, alpha, element_count); + } +} + +void GMMUpdate( + const float* image, + int* alpha, + float* gmm, + float* scratch_mem, + unsigned int batch_count, + unsigned int element_count) { + unsigned int block_count = TILE(element_count, BLOCK * LOAD); + + float* block_gmm_scratch = scratch_mem; + + unsigned int gmm_N = MIXTURE_COUNT * MIXTURE_SIZE; + + for (unsigned int i = 0; i < gmm_N; ++i) { + CovarianceReductionKernel + <<>>(i, image, alpha, block_gmm_scratch, element_count); + } + + CovarianceFinalizationKernel + <<>>(block_gmm_scratch, gmm, block_count); + + GMMcommonTerm<<>>(gmm); +} + +void GMMDataTerm( + const float* image, + const float* gmm, + float* output, + unsigned int batch_count, + unsigned int element_count) { + dim3 block(BLOCK_SIZE, 1); + dim3 grid(TILE(element_count, BLOCK_SIZE), 1, batch_count); + + GMMDataTermKernel<<>>(image, gmm, output, element_count); +} + +void learn_cuda( + const float* input, + const int* labels, + float* gmm, + float* scratch_memory, + unsigned int batch_count, + unsigned int element_count) { + int* alpha = (int*)scratch_memory; + float* scratch_mem = scratch_memory + batch_count * element_count; + + cudaMemcpyAsync(alpha, labels, batch_count * element_count * sizeof(int), cudaMemcpyDeviceToDevice); + + GMMInitialize(input, alpha, gmm, scratch_mem, batch_count, element_count); + GMMUpdate(input, alpha, gmm, scratch_mem, batch_count, element_count); +} + +void apply_cuda( + const float* gmm, + const float* input, + float* output, + unsigned int batch_count, + unsigned int element_count) { + GMMDataTerm(input, gmm, output, batch_count, element_count); +} diff --git a/MONAI/source/monai/_extensions/gmm/gmm_cuda_linalg.cuh b/MONAI/source/monai/_extensions/gmm/gmm_cuda_linalg.cuh new file mode 100644 index 0000000000000000000000000000000000000000..56c7c7ccdcd53b7bb5c24dcba660af35571caa76 --- /dev/null +++ b/MONAI/source/monai/_extensions/gmm/gmm_cuda_linalg.cuh @@ -0,0 +1,144 @@ +/* +Copyright (c) MONAI Consortium +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +__device__ void to_square(float in[SUB_MATRIX_COMPONENT_COUNT], float out[CHANNEL_COUNT][CHANNEL_COUNT]) { + for (int index = 0, i = 0; i < CHANNEL_COUNT; i++) { + for (int j = i; j < CHANNEL_COUNT; j++, index++) { + out[i][j] = in[index]; + out[j][i] = in[index]; + } + } +} + +__device__ void to_triangle(float in[CHANNEL_COUNT][CHANNEL_COUNT], float out[SUB_MATRIX_COMPONENT_COUNT]) { + for (int index = 0, i = 0; i < CHANNEL_COUNT; i++) { + for (int j = i; j < CHANNEL_COUNT; j++, index++) { + out[index] = in[j][i]; + } + } +} + +__device__ void cholesky(float in[CHANNEL_COUNT][CHANNEL_COUNT], float out[CHANNEL_COUNT][CHANNEL_COUNT]) { + for (int i = 0; i < CHANNEL_COUNT; i++) { + for (int j = 0; j < i + 1; j++) { + float sum = 0.0f; + + for (int k = 0; k < j; k++) { + sum += out[i][k] * out[j][k]; + } + + if (i == j) { + out[i][j] = sqrtf(in[i][i] - sum); + } else { + out[i][j] = (in[i][j] - sum) / out[j][j]; + } + } + } +} + +__device__ float chol_det(float in[CHANNEL_COUNT][CHANNEL_COUNT]) { + float det = 1.0f; + + for (int i = 0; i < CHANNEL_COUNT; i++) { + det *= in[i][i]; + } + + return det * det; +} + +__device__ void chol_inv(float in[CHANNEL_COUNT][CHANNEL_COUNT], float out[CHANNEL_COUNT][CHANNEL_COUNT]) { + // Invert cholesky matrix + for (int i = 0; i < CHANNEL_COUNT; i++) { + in[i][i] = 1.0f / (in[i][i] + 0.0001f); + + for (int j = 0; j < i; j++) { + float sum = 0.0f; + + for (int k = j; k < i; k++) { + sum += in[i][k] * in[k][j]; + } + + in[i][j] = -in[i][i] * sum; + } + } + + // Dot with transpose of self + for (int i = 0; i < CHANNEL_COUNT; i++) { + for (int j = 0; j < CHANNEL_COUNT; j++) { + out[i][j] = 0.0f; + + for (int k = max(i, j); k < CHANNEL_COUNT; k++) { + out[i][j] += in[k][i] * in[k][j]; + } + } + } +} + +__device__ void normalize(float* v) { + float norm = 0.0f; + + for (int i = 0; i < CHANNEL_COUNT; i++) { + norm += v[i] * v[i]; + } + + norm = 1.0f / sqrtf(norm); + + for (int i = 0; i < CHANNEL_COUNT; i++) { + v[i] *= norm; + } +} + +__device__ float scalar_prod(float* a, float* b) { + float product = 0.0f; + + for (int i = 0; i < CHANNEL_COUNT; i++) { + product += a[i] * b[i]; + } + + return product; +} + +__device__ void largest_eigenpair(const float* M, float* evec, float* eval) { + float scratch[CHANNEL_COUNT]; + + for (int i = 0; i < CHANNEL_COUNT; i++) { + scratch[i] = i + 1; + } + + for (int itr = 0; itr < 10; itr++) { + *eval = 0.0f; + + for (int i = 0; i < CHANNEL_COUNT; i++) { + int index = i; + + evec[i] = 0.0f; + + for (int j = 0; j < CHANNEL_COUNT; j++) { + evec[i] += M[index] * scratch[j]; + + if (j < i) { + index += CHANNEL_COUNT - (j + 1); + } else { + index += 1; + } + } + + *eval = max(*eval, evec[i]); + } + + for (int i = 0; i < CHANNEL_COUNT; i++) { + evec[i] /= *eval; + scratch[i] = evec[i]; + } + } +} diff --git a/MONAI/source/monai/_extensions/loader.py b/MONAI/source/monai/_extensions/loader.py new file mode 100644 index 0000000000000000000000000000000000000000..7affd1a3eb84ccb8fb81441e4099991b90167b78 --- /dev/null +++ b/MONAI/source/monai/_extensions/loader.py @@ -0,0 +1,93 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import platform +from _thread import interrupt_main +from contextlib import contextmanager +from glob import glob +from os import path +from threading import Timer +from types import ModuleType + +import torch + +from monai.utils.module import get_torch_version_tuple, optional_import + +dir_path = path.dirname(path.realpath(__file__)) + + +@contextmanager +def timeout(time, message): + timer = None + try: + timer = Timer(time, interrupt_main) + timer.daemon = True + timer.start() + yield + except KeyboardInterrupt as e: + if timer is not None and timer.is_alive(): + raise e # interrupt from user? + raise TimeoutError(message) from e + finally: + if timer is not None: + try: + timer.cancel() + finally: + pass + + +def load_module( + module_name: str, defines: dict | None = None, verbose_build: bool = False, build_timeout: int = 300 +) -> ModuleType: + """ + Handles the loading of c++ extension modules. + + Args: + module_name: Name of the module to load. + Must match the name of the relevant source directory in the `_extensions` directory. + defines: Dictionary containing names and values of compilation defines. + verbose_build: Set to true to enable build logging. + build_timeout: Time in seconds before the build will throw an exception to prevent hanging. + """ + + # Ensuring named module exists in _extensions directory. + module_dir = path.join(dir_path, module_name) + if not path.exists(module_dir): + raise ValueError(f"No extension module named {module_name}") + + platform_str = f"_{platform.system()}_{platform.python_version()}_" + platform_str += "".join(f"{v}" for v in get_torch_version_tuple()[:2]) + # Adding configuration to module name. + if defines is not None: + module_name = "_".join([module_name] + [f"{v}" for v in defines.values()]) + + # Gathering source files. + source = glob(path.join(module_dir, "**", "*.cpp"), recursive=True) + if torch.cuda.is_available(): + source += glob(path.join(module_dir, "**", "*.cu"), recursive=True) + platform_str += f"_{torch.version.cuda}" + + # Constructing compilation argument list. + define_args = [] if not defines else [f"-D {key}={defines[key]}" for key in defines] + + # Ninja may be blocked by something out of our control. + # This will error if the build takes longer than expected. + with timeout(build_timeout, "Build appears to be blocked. Is there a stopped process building the same extension?"): + load, _ = optional_import("torch.utils.cpp_extension", name="load") # main trigger some JIT config in pytorch + # This will either run the build or return the existing .so object. + name = module_name + platform_str.replace(".", "_") + module = load( + name=name, sources=source, extra_cflags=define_args, extra_cuda_cflags=define_args, verbose=verbose_build + ) + + return module # type: ignore[no-any-return] diff --git a/MONAI/source/monai/_version.py b/MONAI/source/monai/_version.py new file mode 100644 index 0000000000000000000000000000000000000000..256d3654d9319c244d06490866ab1684ba6b308f --- /dev/null +++ b/MONAI/source/monai/_version.py @@ -0,0 +1,657 @@ + +# This file helps to compute a version number in source trees obtained from +# git-archive tarball (such as those provided by githubs download-from-tag +# feature). Distribution tarballs (built by setup.py sdist) and build +# directories (produced by setup.py build) will contain a much shorter file +# that just contains the computed version number. + +# This file is released into the public domain. Generated by +# versioneer-0.23 (https://github.com/python-versioneer/python-versioneer) + +"""Git implementation of _version.py.""" + +import errno +import os +import re +import subprocess +import sys +from typing import Callable, Dict +import functools + + +def get_keywords(): + """Get the keywords needed to look up the version information.""" + # these strings will be replaced by git during git-archive. + # setup.py/versioneer.py will grep for the variable names, so they must + # each be defined on a line of their own. _version.py will just call + # get_keywords(). + git_refnames = "$Format:%d$" + git_full = "$Format:%H$" + git_date = "$Format:%ci$" + keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} + return keywords + + +class VersioneerConfig: + """Container for Versioneer configuration parameters.""" + + +def get_config(): + """Create, populate and return the VersioneerConfig() object.""" + # these strings are filled in when 'setup.py versioneer' creates + # _version.py + cfg = VersioneerConfig() + cfg.VCS = "git" + cfg.style = "pep440" + cfg.tag_prefix = "" + cfg.parentdir_prefix = "" + cfg.versionfile_source = "monai/_version.py" + cfg.verbose = False + return cfg + + +class NotThisMethod(Exception): + """Exception raised if a method is not valid for the current scenario.""" + + +LONG_VERSION_PY: Dict[str, str] = {} +HANDLERS: Dict[str, Dict[str, Callable]] = {} + + +def register_vcs_handler(vcs, method): # decorator + """Create decorator to mark a method as the handler of a VCS.""" + def decorate(f): + """Store f in HANDLERS[vcs][method].""" + if vcs not in HANDLERS: + HANDLERS[vcs] = {} + HANDLERS[vcs][method] = f + return f + return decorate + + +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, + env=None): + """Call the given command(s).""" + assert isinstance(commands, list) + process = None + + popen_kwargs = {} + if sys.platform == "win32": + # This hides the console window if pythonw.exe is used + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + popen_kwargs["startupinfo"] = startupinfo + + for command in commands: + try: + dispcmd = str([command] + args) + # remember shell=False, so use git.cmd on windows, not just git + process = subprocess.Popen([command] + args, cwd=cwd, env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr + else None), **popen_kwargs) + break + except OSError: + e = sys.exc_info()[1] + if e.errno == errno.ENOENT: + continue + if verbose: + print("unable to run %s" % dispcmd) + print(e) + return None, None + else: + if verbose: + print("unable to find command, tried %s" % (commands,)) + return None, None + stdout = process.communicate()[0].strip().decode() + if process.returncode != 0: + if verbose: + print("unable to run %s (error)" % dispcmd) + print("stdout was %s" % stdout) + return None, process.returncode + return stdout, process.returncode + + +def versions_from_parentdir(parentdir_prefix, root, verbose): + """Try to determine the version from the parent directory name. + + Source tarballs conventionally unpack into a directory that includes both + the project name and a version string. We will also support searching up + two directory levels for an appropriately named parent directory + """ + rootdirs = [] + + for _ in range(3): + dirname = os.path.basename(root) + if dirname.startswith(parentdir_prefix): + return {"version": dirname[len(parentdir_prefix):], + "full-revisionid": None, + "dirty": False, "error": None, "date": None} + rootdirs.append(root) + root = os.path.dirname(root) # up a level + + if verbose: + print("Tried directories %s but none started with prefix %s" % + (str(rootdirs), parentdir_prefix)) + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") + + +@register_vcs_handler("git", "get_keywords") +def git_get_keywords(versionfile_abs): + """Extract version information from the given file.""" + # the code embedded in _version.py can just fetch the value of these + # keywords. When used from setup.py, we don't want to import _version.py, + # so we do it with a regexp instead. This function is not used from + # _version.py. + keywords = {} + try: + with open(versionfile_abs, "r") as fobj: + for line in fobj: + if line.strip().startswith("git_refnames ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["refnames"] = mo.group(1) + if line.strip().startswith("git_full ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["full"] = mo.group(1) + if line.strip().startswith("git_date ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["date"] = mo.group(1) + except OSError: + pass + return keywords + + +@register_vcs_handler("git", "keywords") +def git_versions_from_keywords(keywords, tag_prefix, verbose): + """Get version information from git keywords.""" + if "refnames" not in keywords: + raise NotThisMethod("Short version file found") + date = keywords.get("date") + if date is not None: + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + + # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant + # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 + # -like" string, which we must then edit to make compliant), because + # it's been around since git-1.5.3, and it's too difficult to + # discover which version we're using, or to work around using an + # older one. + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + refnames = keywords["refnames"].strip() + if refnames.startswith("$Format"): + if verbose: + print("keywords are unexpanded, not using") + raise NotThisMethod("unexpanded keywords, not a git-archive tarball") + refs = {r.strip() for r in refnames.strip("()").split(",")} + # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of + # just "foo-1.0". If we see a "tag: " prefix, prefer those. + TAG = "tag: " + tags = {r[len(TAG):] for r in refs if r.startswith(TAG)} + if not tags: + # Either we're using git < 1.8.3, or there really are no tags. We use + # a heuristic: assume all version tags have a digit. The old git %d + # expansion behaves like git log --decorate=short and strips out the + # refs/heads/ and refs/tags/ prefixes that would let us distinguish + # between branches and tags. By ignoring refnames without digits, we + # filter out many common branch names like "release" and + # "stabilization", as well as "HEAD" and "master". + tags = {r for r in refs if re.search(r'\d', r)} + if verbose: + print("discarding '%s', no digits" % ",".join(refs - tags)) + if verbose: + print("likely tags: %s" % ",".join(sorted(tags))) + for ref in sorted(tags): + # sorting will prefer e.g. "2.0" over "2.0rc1" + if ref.startswith(tag_prefix): + r = ref[len(tag_prefix):] + # Filter out refs that exactly match prefix or that don't start + # with a number once the prefix is stripped (mostly a concern + # when prefix is '') + if not re.match(r'\d', r): + continue + if verbose: + print("picking %s" % r) + return {"version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": None, + "date": date} + # no suitable tags, so version is "0+unknown", but full hex is still there + if verbose: + print("no suitable tags, using unknown + full revision id") + return {"version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, "error": "no suitable tags", "date": None} + + +@register_vcs_handler("git", "pieces_from_vcs") +def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): + """Get version from 'git describe' in the root of the source tree. + + This only gets called if the git-archive 'subst' keywords were *not* + expanded, and _version.py hasn't already been rewritten with a short + version string, meaning we're inside a checked out source tree. + """ + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + + # GIT_DIR can interfere with correct operation of Versioneer. + # It may be intended to be passed to the Versioneer-versioned project, + # but that should not change where we get our version from. + env = os.environ.copy() + env.pop("GIT_DIR", None) + runner = functools.partial(runner, env=env) + + _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, + hide_stderr=True) + if rc != 0: + if verbose: + print("Directory %s not under git control" % root) + raise NotThisMethod("'git rev-parse --git-dir' returned error") + + # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] + # if there isn't one, this yields HEX[-dirty] (no NUM) + describe_out, rc = runner(GITS, [ + "describe", "--tags", "--dirty", "--always", "--long", + "--match", f"{tag_prefix}[[:digit:]]*" + ], cwd=root) + # --long was added in git-1.5.5 + if describe_out is None: + raise NotThisMethod("'git describe' failed") + describe_out = describe_out.strip() + full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root) + if full_out is None: + raise NotThisMethod("'git rev-parse' failed") + full_out = full_out.strip() + + pieces = {} + pieces["long"] = full_out + pieces["short"] = full_out[:7] # maybe improved later + pieces["error"] = None + + branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], + cwd=root) + # --abbrev-ref was added in git-1.6.3 + if rc != 0 or branch_name is None: + raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") + branch_name = branch_name.strip() + + if branch_name == "HEAD": + # If we aren't exactly on a branch, pick a branch which represents + # the current commit. If all else fails, we are on a branchless + # commit. + branches, rc = runner(GITS, ["branch", "--contains"], cwd=root) + # --contains was added in git-1.5.4 + if rc != 0 or branches is None: + raise NotThisMethod("'git branch --contains' returned error") + branches = branches.split("\n") + + # Remove the first line if we're running detached + if "(" in branches[0]: + branches.pop(0) + + # Strip off the leading "* " from the list of branches. + branches = [branch[2:] for branch in branches] + if "master" in branches: + branch_name = "master" + elif not branches: + branch_name = None + else: + # Pick the first branch that is returned. Good or bad. + branch_name = branches[0] + + pieces["branch"] = branch_name + + # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] + # TAG might have hyphens. + git_describe = describe_out + + # look for -dirty suffix + dirty = git_describe.endswith("-dirty") + pieces["dirty"] = dirty + if dirty: + git_describe = git_describe[:git_describe.rindex("-dirty")] + + # now we have TAG-NUM-gHEX or HEX + + if "-" in git_describe: + # TAG-NUM-gHEX + mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + if not mo: + # unparsable. Maybe git-describe is misbehaving? + pieces["error"] = ("unable to parse git-describe output: '%s'" + % describe_out) + return pieces + + # tag + full_tag = mo.group(1) + if not full_tag.startswith(tag_prefix): + if verbose: + fmt = "tag '%s' doesn't start with prefix '%s'" + print(fmt % (full_tag, tag_prefix)) + pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" + % (full_tag, tag_prefix)) + return pieces + pieces["closest-tag"] = full_tag[len(tag_prefix):] + + # distance: number of commits since tag + pieces["distance"] = int(mo.group(2)) + + # commit: short hex revision ID + pieces["short"] = mo.group(3) + + else: + # HEX: no tags + pieces["closest-tag"] = None + out, rc = runner(GITS, ["rev-list", "HEAD", "--left-right"], cwd=root) + pieces["distance"] = len(out.split()) # total number of commits + + # commit date: see ISO-8601 comment in git_versions_from_keywords() + date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() + # Use only the last line. Previous lines may contain GPG signature + # information. + date = date.splitlines()[-1] + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + + return pieces + + +def plus_or_dot(pieces): + """Return a + if we don't already have one, else return a .""" + if "+" in pieces.get("closest-tag", ""): + return "." + return "+" + + +def render_pep440(pieces): + """Build up version string, with post-release "local version identifier". + + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += plus_or_dot(pieces) + rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0+untagged.%d.g%s" % (pieces["distance"], + pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def render_pep440_branch(pieces): + """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] . + + The ".dev0" means not master branch. Note that .dev0 sorts backwards + (a feature branch will appear "older" than the master branch). + + Exceptions: + 1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + if pieces["branch"] != "master": + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0" + if pieces["branch"] != "master": + rendered += ".dev0" + rendered += "+untagged.%d.g%s" % (pieces["distance"], + pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def pep440_split_post(ver): + """Split pep440 version string at the post-release segment. + + Returns the release segments before the post-release and the + post-release version number (or -1 if no post-release segment is present). + """ + vc = str.split(ver, ".post") + return vc[0], int(vc[1] or 0) if len(vc) == 2 else None + + +def render_pep440_pre(pieces): + """TAG[.postN.devDISTANCE] -- No -dirty. + + Exceptions: + 1: no tags. 0.post0.devDISTANCE + """ + if pieces["closest-tag"]: + if pieces["distance"]: + # update the post release segment + tag_version, post_version = pep440_split_post(pieces["closest-tag"]) + rendered = tag_version + if post_version is not None: + rendered += ".post%d.dev%d" % (post_version + 1, pieces["distance"]) + else: + rendered += ".post0.dev%d" % (pieces["distance"]) + else: + # no commits, use the tag as the version + rendered = pieces["closest-tag"] + else: + # exception #1 + rendered = "0.post0.dev%d" % pieces["distance"] + return rendered + + +def render_pep440_post(pieces): + """TAG[.postDISTANCE[.dev0]+gHEX] . + + The ".dev0" means dirty. Note that .dev0 sorts backwards + (a dirty tree will appear "older" than the corresponding clean one), + but you shouldn't be releasing software with -dirty anyways. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "g%s" % pieces["short"] + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += "+g%s" % pieces["short"] + return rendered + + +def render_pep440_post_branch(pieces): + """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] . + + The ".dev0" means not master branch. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["branch"] != "master": + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "g%s" % pieces["short"] + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["branch"] != "master": + rendered += ".dev0" + rendered += "+g%s" % pieces["short"] + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def render_pep440_old(pieces): + """TAG[.postDISTANCE[.dev0]] . + + The ".dev0" means dirty. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + return rendered + + +def render_git_describe(pieces): + """TAG[-DISTANCE-gHEX][-dirty]. + + Like 'git describe --tags --dirty --always'. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render_git_describe_long(pieces): + """TAG-DISTANCE-gHEX[-dirty]. + + Like 'git describe --tags --dirty --always -long'. + The distance/hash is unconditional. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render(pieces, style): + """Render the given version pieces into the requested style.""" + if pieces["error"]: + return {"version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None} + + if not style or style == "default": + style = "pep440" # the default + + if style == "pep440": + rendered = render_pep440(pieces) + elif style == "pep440-branch": + rendered = render_pep440_branch(pieces) + elif style == "pep440-pre": + rendered = render_pep440_pre(pieces) + elif style == "pep440-post": + rendered = render_pep440_post(pieces) + elif style == "pep440-post-branch": + rendered = render_pep440_post_branch(pieces) + elif style == "pep440-old": + rendered = render_pep440_old(pieces) + elif style == "git-describe": + rendered = render_git_describe(pieces) + elif style == "git-describe-long": + rendered = render_git_describe_long(pieces) + else: + raise ValueError("unknown style '%s'" % style) + + return {"version": rendered, "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], "error": None, + "date": pieces.get("date")} + + +def get_versions(): + """Get version information or return default if unable to do so.""" + # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have + # __file__, we can work backwards from there to the root. Some + # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which + # case we can only use expanded keywords. + + cfg = get_config() + verbose = cfg.verbose + + try: + return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, + verbose) + except NotThisMethod: + pass + + try: + root = os.path.realpath(__file__) + # versionfile_source is the relative path from the top of the source + # tree (where the .git directory might live) to this file. Invert + # this to find the root from __file__. + for _ in cfg.versionfile_source.split('/'): + root = os.path.dirname(root) + except NameError: + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None} + + try: + pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) + return render(pieces, cfg.style) + except NotThisMethod: + pass + + try: + if cfg.parentdir_prefix: + return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) + except NotThisMethod: + pass + + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", "date": None} diff --git a/MONAI/source/monai/apps/__init__.py b/MONAI/source/monai/apps/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9cc7aeb8e052c23701c7d73a9a98dc37af1de77e --- /dev/null +++ b/MONAI/source/monai/apps/__init__.py @@ -0,0 +1,16 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from .datasets import CrossValidation, DecathlonDataset, MedNISTDataset, TciaDataset +from .mmars import MODEL_DESC, RemoteMMARKeys, download_mmar, get_model_spec, load_from_mmar +from .utils import SUPPORTED_HASH_TYPES, check_hash, download_and_extract, download_url, extractall, get_logger, logger diff --git a/MONAI/source/monai/apps/auto3dseg/__init__.py b/MONAI/source/monai/apps/auto3dseg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7096fb7520a78a02cdeffaaef1e77e246d992d21 --- /dev/null +++ b/MONAI/source/monai/apps/auto3dseg/__init__.py @@ -0,0 +1,25 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from .auto_runner import AutoRunner +from .bundle_gen import BundleAlgo, BundleGen +from .data_analyzer import DataAnalyzer +from .ensemble_builder import ( + AlgoEnsemble, + AlgoEnsembleBestByFold, + AlgoEnsembleBestN, + AlgoEnsembleBuilder, + EnsembleRunner, +) +from .hpo_gen import NNIGen, OptunaGen +from .utils import export_bundle_algo_history, get_name_from_algo_id, import_bundle_algo_history diff --git a/MONAI/source/monai/apps/auto3dseg/__main__.py b/MONAI/source/monai/apps/auto3dseg/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..dfc14c270b4f4edbabe5c394961d6c85ebb3271a --- /dev/null +++ b/MONAI/source/monai/apps/auto3dseg/__main__.py @@ -0,0 +1,35 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from monai.apps.auto3dseg.auto_runner import AutoRunner +from monai.apps.auto3dseg.bundle_gen import BundleAlgo, BundleGen +from monai.apps.auto3dseg.data_analyzer import DataAnalyzer +from monai.apps.auto3dseg.ensemble_builder import AlgoEnsembleBuilder, EnsembleRunner +from monai.apps.auto3dseg.hpo_gen import NNIGen, OptunaGen + +if __name__ == "__main__": + from monai.utils import optional_import + + fire, _ = optional_import("fire") + fire.Fire( + { + "DataAnalyzer": DataAnalyzer, + "BundleGen": BundleGen, + "BundleAlgo": BundleAlgo, + "AlgoEnsembleBuilder": AlgoEnsembleBuilder, + "EnsembleRunner": EnsembleRunner, + "AutoRunner": AutoRunner, + "NNIGen": NNIGen, + "OptunaGen": OptunaGen, + } + ) diff --git a/MONAI/source/monai/apps/auto3dseg/auto_runner.py b/MONAI/source/monai/apps/auto3dseg/auto_runner.py new file mode 100644 index 0000000000000000000000000000000000000000..d06effcd1a2bcb2899ed85c436b25486a1778f61 --- /dev/null +++ b/MONAI/source/monai/apps/auto3dseg/auto_runner.py @@ -0,0 +1,907 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import os +import shutil +import warnings +from copy import deepcopy +from time import sleep +from typing import Any, cast + +import torch + +from monai.apps.auto3dseg.bundle_gen import BundleGen +from monai.apps.auto3dseg.data_analyzer import DataAnalyzer +from monai.apps.auto3dseg.ensemble_builder import EnsembleRunner +from monai.apps.auto3dseg.hpo_gen import NNIGen +from monai.apps.auto3dseg.utils import export_bundle_algo_history, import_bundle_algo_history +from monai.apps.utils import get_logger +from monai.auto3dseg.utils import algo_to_pickle +from monai.bundle import ConfigParser +from monai.transforms import SaveImage +from monai.utils import AlgoKeys, has_option, look_up_option, optional_import +from monai.utils.misc import check_kwargs_exist_in_class_init, run_cmd + +logger = get_logger(module_name=__name__) + +nni, has_nni = optional_import("nni") + + +class AutoRunner: + """ + An interface for handling Auto3Dseg with minimal inputs and understanding of the internal states in Auto3Dseg. + The users can run the Auto3Dseg with default settings in one line of code. They can also customize the advanced + features Auto3Dseg in a few additional lines. Examples of customization include + + - change cross-validation folds + - change training/prediction parameters + - change ensemble methods + - automatic hyperparameter optimization. + + The output of the interface is a directory that contains + + - data statistics analysis report + - algorithm definition files (scripts, configs, pickle objects) and training results (checkpoints, accuracies) + - the predictions on the testing datasets from the final algorithm ensemble + - a copy of the input arguments in form of YAML + - cached intermediate results + + Args: + work_dir: working directory to save the intermediate and final results. + input: the configuration dictionary or the file path to the configuration in form of YAML. + The configuration should contain datalist, dataroot, modality, multigpu, and class_names info. + algos: optionally specify algorithms to use. If a dictionary, must be in the form + {"algname": dict(_target_="algname.scripts.algo.AlgnameAlgo", template_path="algname"), ...} + If a list or a string, defines a subset of names of the algorithms to use, e.g. 'segresnet' or + ['segresnet', 'dints'] out of the full set of algorithm templates provided by templates_path_or_url. + Defaults to None, to use all available algorithms. + analyze: on/off switch to run DataAnalyzer and generate a datastats report. Defaults to None, to automatically + decide based on cache, and run data analysis only if we have not completed this step yet. + algo_gen: on/off switch to run AlgoGen and generate templated BundleAlgos. Defaults to None, to automatically + decide based on cache, and run algorithm folders generation only if we have not completed this step yet. + train: on/off switch to run training and generate algorithm checkpoints. Defaults to None, to automatically + decide based on cache, and run training only if we have not completed this step yet. + hpo: use hyperparameter optimization (HPO) in the training phase. Users can provide a list of + hyper-parameter and a search will be performed to investigate the algorithm performances. + hpo_backend: a string that indicates the backend of the HPO. Currently, only NNI Grid-search mode + is supported + ensemble: on/off switch to run model ensemble and use the ensemble to predict outputs in testing + datasets. + not_use_cache: if the value is True, it will ignore all cached results in data analysis, + algorithm generation, or training, and start the pipeline from scratch. + templates_path_or_url: the folder with the algorithm templates or a url. If None provided, the default template + zip url will be downloaded and extracted into the work_dir. + allow_skip: a switch passed to BundleGen process which determines if some Algo in the default templates + can be skipped based on the analysis on the dataset from Auto3DSeg DataAnalyzer. + mlflow_tracking_uri: a tracking URI for MLflow server which could be local directory or address of the remote + tracking Server; MLflow runs will be recorded locally in algorithms' model folder if the value is None. + mlflow_experiment_name: the name of the experiment in MLflow server. + kwargs: image writing parameters for the ensemble inference. The kwargs format follows the SaveImage + transform. For more information, check https://monai.readthedocs.io/en/stable/transforms.html#saveimage. + + + Examples: + - User can use the one-liner to start the Auto3Dseg workflow + + .. code-block:: bash + + python -m monai.apps.auto3dseg AutoRunner run --input \ + '{"modality": "ct", "datalist": "dl.json", "dataroot": "/dr", "multigpu": true, "class_names": ["A", "B"]}' + + - User can also save the input dictionary as a input YAML file and use the following one-liner + + .. code-block:: bash + + python -m monai.apps.auto3dseg AutoRunner run --input=./input.yaml + + - User can specify work_dir and data source config input and run AutoRunner: + + .. code-block:: python + + work_dir = "./work_dir" + input = "path/to/input_yaml" + runner = AutoRunner(work_dir=work_dir, input=input) + runner.run() + + - User can specify a subset of algorithms to use and run AutoRunner: + + .. code-block:: python + + work_dir = "./work_dir" + input = "path/to/input_yaml" + algos = ["segresnet", "dints"] + runner = AutoRunner(work_dir=work_dir, input=input, algos=algos) + runner.run() + + - User can specify a local folder with algorithms templates and run AutoRunner: + + .. code-block:: python + + work_dir = "./work_dir" + input = "path/to/input_yaml" + algos = "segresnet" + templates_path_or_url = "./local_path_to/algorithm_templates" + runner = AutoRunner(work_dir=work_dir, input=input, algos=algos, templates_path_or_url=templates_path_or_url) + runner.run() + + - User can specify training parameters by: + + .. code-block:: python + + input = "path/to/input_yaml" + runner = AutoRunner(input=input) + train_param = { + "num_epochs_per_validation": 1, + "num_images_per_batch": 2, + "num_epochs": 2, + } + runner.set_training_params(params=train_param) # 2 epochs + runner.run() + + - User can specify the fold number of cross validation + + .. code-block:: python + + input = "path/to/input_yaml" + runner = AutoRunner(input=input) + runner.set_num_fold(n_fold = 2) + runner.run() + + - User can specify the prediction parameters during algo ensemble inference: + + .. code-block:: python + + input = "path/to/input_yaml" + pred_params = { + 'files_slices': slice(0,2), + 'mode': "vote", + 'sigmoid': True, + } + runner = AutoRunner(input=input) + runner.set_prediction_params(params=pred_params) + runner.run() + + - User can define a grid search space and use the HPO during training. + + .. code-block:: python + + input = "path/to/input_yaml" + runner = AutoRunner(input=input, hpo=True) + runner.set_nni_search_space({"learning_rate": {"_type": "choice", "_value": [0.0001, 0.001, 0.01, 0.1]}}) + runner.run() + + Notes: + Expected results in the work_dir as below:: + + work_dir/ + ├── algorithm_templates # bundle algo templates (scripts/configs) + ├── cache.yaml # Autorunner will automatically cache results to save time + ├── datastats.yaml # datastats of the dataset + ├── dints_0 # network scripts/configs/checkpoints and pickle object of the algo + ├── ensemble_output # the prediction of testing datasets from the ensemble of the algos + ├── input.yaml # copy of the input data source configs + ├── segresnet_0 # network scripts/configs/checkpoints and pickle object of the algo + ├── segresnet2d_0 # network scripts/configs/checkpoints and pickle object of the algo + └── swinunetr_0 # network scripts/configs/checkpoints and pickle object of the algo + + + The input config requires at least the following keys: + - ``modality``: the modality of the data, e.g. "ct", "mri", etc. + - ``datalist``: the path to the datalist file in JSON format. + - ``dataroot``: the root directory of the data files. + + For the datalist file format, see the description under :py:func:`monai.data.load_decathlon_datalist`. + Note that the AutoRunner will use the "validation" key in the datalist file if it exists, otherwise + it will do cross-validation, by default with five folds (this is hardcoded). + """ + + analyze_params: dict | None + + def __init__( + self, + work_dir: str = "./work_dir", + input: dict[str, Any] | str | None = None, + algos: dict | list | str | None = None, + analyze: bool | None = None, + algo_gen: bool | None = None, + train: bool | None = None, + hpo: bool = False, + hpo_backend: str = "nni", + ensemble: bool = True, + not_use_cache: bool = False, + templates_path_or_url: str | None = None, + allow_skip: bool = True, + mlflow_tracking_uri: str | None = None, + mlflow_experiment_name: str | None = None, + **kwargs: Any, + ): + if input is None and os.path.isfile(os.path.join(os.path.abspath(work_dir), "input.yaml")): + input = os.path.join(os.path.abspath(work_dir), "input.yaml") + logger.info(f"Input config is not provided, using the default {input}") + + self.data_src_cfg = dict() + if isinstance(input, dict): + self.data_src_cfg = input + elif isinstance(input, str) and os.path.isfile(input): + self.data_src_cfg = ConfigParser.load_config_file(input) + logger.info(f"Loading input config {input}") + else: + raise ValueError(f"{input} is not a valid file or dict") + + if "work_dir" in self.data_src_cfg: # override from config + work_dir = self.data_src_cfg["work_dir"] + self.work_dir = os.path.abspath(work_dir) + + logger.info(f"AutoRunner using work directory {self.work_dir}") + os.makedirs(self.work_dir, exist_ok=True) + self.data_src_cfg_name = os.path.join(self.work_dir, "input.yaml") + + self.algos = algos + self.templates_path_or_url = templates_path_or_url + self.allow_skip = allow_skip + + # cache.yaml + self.not_use_cache = not_use_cache + self.cache_filename = os.path.join(self.work_dir, "cache.yaml") + self.cache = self.read_cache() + self.export_cache() + + # determine if we need to analyze, algo_gen or train from cache, unless manually provided + self.analyze = not self.cache["analyze"] if analyze is None else analyze + self.algo_gen = not self.cache["algo_gen"] if algo_gen is None else algo_gen + self.train = train + self.ensemble = ensemble # last step, no need to check + self.hpo = hpo and has_nni + self.hpo_backend = hpo_backend + self.mlflow_tracking_uri = mlflow_tracking_uri + self.mlflow_experiment_name = mlflow_experiment_name + self.kwargs = deepcopy(kwargs) + + # parse input config for AutoRunner param overrides + for param in [ + "analyze", + "algo_gen", + "train", + "hpo", + "ensemble", + "not_use_cache", + "allow_skip", + ]: # override from config + if param in self.data_src_cfg and isinstance(self.data_src_cfg[param], bool): + setattr(self, param, self.data_src_cfg[param]) # e.g. self.analyze = self.data_src_cfg["analyze"] + + for param in [ + "algos", + "hpo_backend", + "templates_path_or_url", + "mlflow_tracking_uri", + "mlflow_experiment_name", + ]: # override from config + if param in self.data_src_cfg: + setattr(self, param, self.data_src_cfg[param]) # e.g. self.algos = self.data_src_cfg["algos"] + + missing_keys = {"dataroot", "datalist", "modality"}.difference(self.data_src_cfg.keys()) + if len(missing_keys) > 0: + raise ValueError(f"Config keys are missing {missing_keys}") + + if not os.path.exists(self.data_src_cfg["datalist"]): + raise ValueError(f"Datalist file is not found {self.data_src_cfg['datalist']}") + + # copy datalist to work_dir + datalist_filename = os.path.join(self.work_dir, os.path.basename(self.data_src_cfg["datalist"])) + if datalist_filename != self.data_src_cfg["datalist"]: + try: + shutil.copyfile(self.data_src_cfg["datalist"], datalist_filename) + logger.info(f"Datalist was copied to work_dir: {datalist_filename}") + except shutil.SameFileError: + pass + + # inspect and update folds + self.max_fold = self.inspect_datalist_folds(datalist_filename=datalist_filename) + if "num_fold" in self.data_src_cfg: + num_fold = int(self.data_src_cfg["num_fold"]) # override from config + logger.info(f"Setting num_fold {num_fold} based on the input config.") + else: + num_fold = self.max_fold + logger.info(f"Setting num_fold {num_fold} based on the input datalist {datalist_filename}.") + + self.data_src_cfg["datalist"] = datalist_filename # update path to a version in work_dir and save user input + ConfigParser.export_config_file( + config=self.data_src_cfg, filepath=self.data_src_cfg_name, fmt="yaml", sort_keys=False + ) + + self.dataroot = self.data_src_cfg["dataroot"] + self.datastats_filename = os.path.join(self.work_dir, "datastats.yaml") + self.datalist_filename = datalist_filename + + self.set_training_params() + self.set_device_info() + self.set_prediction_params() + self.set_analyze_params() + self.set_ensemble_method() + self.set_num_fold(num_fold=num_fold) + + self.gpu_customization = False + self.gpu_customization_specs: dict[str, Any] = {} + + # hpo + if self.hpo_backend.lower() != "nni": + raise NotImplementedError("HPOGen backend only supports NNI") + self.hpo = self.hpo and has_nni + self.set_hpo_params() + self.search_space: dict[str, dict[str, Any]] = {} + self.hpo_tasks = 0 + + if "sigmoid" not in self.kwargs and "sigmoid" in self.data_src_cfg: + self.kwargs["sigmoid"] = self.data_src_cfg["sigmoid"] + + def read_cache(self): + """ + Check if the intermediate result is cached after each step in the current working directory + + Returns: + a dict of cache results. If not_use_cache is set to True, or there is no cache file in the + working directory, the result will be ``empty_cache`` in which all ``has_cache`` keys are + set to False. + """ + + empty_cache = {"analyze": False, "datastats": None, "algo_gen": False, "train": False} + + if self.not_use_cache or not os.path.isfile(self.cache_filename): + return empty_cache + + cache = ConfigParser.load_config_file(self.cache_filename) + + for k, v in empty_cache.items(): + cache.setdefault(k, v) + + if cache["analyze"]: + if not (isinstance(cache["datastats"], str) and os.path.isfile(cache["datastats"])): + cache["analyze"] = False + cache["datastats"] = None + + if cache["algo_gen"]: + history = import_bundle_algo_history(self.work_dir, only_trained=False) + if len(history) == 0: # no saved algo_objects + cache["algo_gen"] = False + + if cache["train"]: + trained_history = import_bundle_algo_history(self.work_dir, only_trained=True) + if len(trained_history) == 0: + cache["train"] = False + + return cache + + def export_cache(self, **kwargs): + """ + Save the cache state as ``cache.yaml`` in the working directory + """ + self.cache.update(kwargs) + ConfigParser.export_config_file( + self.cache, self.cache_filename, fmt="yaml", default_flow_style=None, sort_keys=False + ) + + def inspect_datalist_folds(self, datalist_filename: str) -> int: + """ + Returns number of folds in the datalist file, and assigns fold numbers if not provided. + + Args: + datalist_filename: path to the datalist file. + + Notes: + If the fold key is not provided, it auto generates 5 folds assignments in the training key list. + If validation key list is available, then it assumes a single fold validation. + """ + + datalist = ConfigParser.load_config_file(datalist_filename) + if "training" not in datalist: + raise ValueError("Datalist files has no training key:" + str(datalist_filename)) + + fold_list = [int(d["fold"]) for d in datalist["training"] if "fold" in d] + + if len(fold_list) > 0: + num_fold = max(fold_list) + 1 + logger.info(f"Found num_fold {num_fold} based on the input datalist {datalist_filename}.") + # check if every fold is present + if len(set(fold_list)) != num_fold: + raise ValueError(f"Fold numbers are not continuous from 0 to {num_fold - 1}") + elif "validation" in datalist and len(datalist["validation"]) > 0: + logger.info("No fold numbers provided, attempting to use a single fold based on the validation key") + # update the datalist file + for d in datalist["training"]: + d["fold"] = 1 + for d in datalist["validation"]: + d["fold"] = 0 + + val_labels = {d["label"]: d for d in datalist["validation"] if "label" in d} + logger.info( + f"Found {len(val_labels)} items in the validation key, saving updated datalist to", datalist_filename + ) + + # check for duplicates + for d in datalist["training"]: + if d["label"] in val_labels: + d["fold"] = 0 + del val_labels[d["label"]] + + datalist["training"] = datalist["training"] + list(val_labels.values()) + + ConfigParser.export_config_file(datalist, datalist_filename, fmt="json", indent=4) + num_fold = 1 + + else: + num_fold = 5 + + warnings.warn( + f"Datalist has no folds specified {datalist_filename}..." + f"Generating {num_fold} folds randomly." + f"Please consider presaving fold numbers beforehand for repeated experiments." + ) + + from sklearn.model_selection import KFold + + kf = KFold(n_splits=num_fold, shuffle=True, random_state=0) + for i, (_, valid_idx) in enumerate(kf.split(datalist["training"])): + for vi in valid_idx: + datalist["training"][vi]["fold"] = i + + ConfigParser.export_config_file(datalist, datalist_filename, fmt="json", indent=4) + + return num_fold + + def set_gpu_customization( + self, gpu_customization: bool = False, gpu_customization_specs: dict[str, Any] | None = None + ) -> AutoRunner: + """ + Set options for GPU-based parameter customization/optimization. + + Args: + gpu_customization: the switch to determine automatically customize/optimize bundle script/config + parameters for each bundleAlgo based on gpus. Custom parameters are obtained through dummy + training to simulate the actual model training process and hyperparameter optimization (HPO) + experiments. + gpu_customization_specs (optional): the dictionary to enable users overwrite the HPO settings. user can + overwrite part of variables as follows or all of them. The structure is as follows. + + .. code-block:: python + + gpu_customization_specs = { + 'ALGO': { + 'num_trials': 6, + 'range_num_images_per_batch': [1, 20], + 'range_num_sw_batch_size': [1, 20] + } + } + + ALGO: the name of algorithm. It could be one of algorithm names (e.g., 'dints') or 'universal' which + would apply changes to all algorithms. Possible options are + + - {``"universal"``, ``"dints"``, ``"segresnet"``, ``"segresnet2d"``, ``"swinunetr"``}. + + num_trials: the number of HPO trials/experiments to run. + range_num_images_per_batch: the range of number of images per mini-batch. + range_num_sw_batch_size: the range of batch size in sliding-window inferer. + """ + self.gpu_customization = gpu_customization + if gpu_customization_specs is not None: + self.gpu_customization_specs = gpu_customization_specs + + return self + + def set_num_fold(self, num_fold: int = 5) -> AutoRunner: + """ + Set the number of cross validation folds for all algos. + + Args: + num_fold: a positive integer to define the number of folds. + """ + + if num_fold <= 0: + raise ValueError(f"num_fold is expected to be an integer greater than zero. Now it gets {num_fold}") + if num_fold > self.max_fold: + # Auto3DSeg must contain validation set, so the maximum fold number is max_fold. + raise ValueError( + f"num_fold is greater than the maximum fold number {self.max_fold} in {self.datalist_filename}." + ) + self.num_fold = num_fold + + return self + + def set_training_params(self, params: dict[str, Any] | None = None) -> AutoRunner: + """ + Set the training params for all algos. + + Args: + params: a dict that defines the overriding key-value pairs during training. The overriding method + is defined by the algo class. + + Examples: + For BundleAlgo objects, the training parameter to shorten the training time to a few epochs can be + {"num_epochs": 2, "num_epochs_per_validation": 1} + + """ + self.train_params = deepcopy(params) if params is not None else {} + if "CUDA_VISIBLE_DEVICES" in self.train_params: + warnings.warn( + "CUDA_VISIBLE_DEVICES is deprecated from 'set_training_params'. Use 'set_device_info' instead.", + DeprecationWarning, + ) + + return self + + def set_device_info( + self, + cuda_visible_devices: list[int] | str | None = None, + num_nodes: int | None = None, + mn_start_method: str | None = None, + cmd_prefix: str | None = None, + ) -> AutoRunner: + """ + Set the device related info + + Args: + cuda_visible_devices: define GPU ids for data analyzer, training, and ensembling. + List of GPU ids [0,1,2,3] or a string "0,1,2,3". + Default using env "CUDA_VISIBLE_DEVICES" or all devices available. + num_nodes: number of nodes for training and ensembling. + Default using env "NUM_NODES" or 1 if "NUM_NODES" is unset. + mn_start_method: multi-node start method. Autorunner will use the method to start multi-node processes. + Default using env "MN_START_METHOD" or 'bcprun' if "MN_START_METHOD" is unset. + cmd_prefix: command line prefix for subprocess running in BundleAlgo and EnsembleRunner. + Default using env "CMD_PREFIX" or None, examples are: + + - single GPU/CPU or multinode bcprun: "python " or "/opt/conda/bin/python3.9 ", + - single node multi-GPU running "torchrun --nnodes=1 --nproc_per_node=2 " + + If user define this prefix, please make sure --nproc_per_node matches cuda_visible_device or + os.env['CUDA_VISIBLE_DEVICES']. Also always set --nnodes=1. Set num_nodes for multi-node. + """ + self.device_setting: dict[str, Any] = {} + if cuda_visible_devices is None: + cuda_visible_devices = os.environ.get("CUDA_VISIBLE_DEVICES") + if cuda_visible_devices is None: # still None after reading the environ + self.device_setting["CUDA_VISIBLE_DEVICES"] = ",".join([str(x) for x in range(torch.cuda.device_count())]) + self.device_setting["n_devices"] = torch.cuda.device_count() + elif isinstance(cuda_visible_devices, str): + self.device_setting["CUDA_VISIBLE_DEVICES"] = cuda_visible_devices + self.device_setting["n_devices"] = len(cuda_visible_devices.split(",")) + elif isinstance(cuda_visible_devices, (list, tuple)): + self.device_setting["CUDA_VISIBLE_DEVICES"] = ",".join([str(x) for x in cuda_visible_devices]) + self.device_setting["n_devices"] = len(cuda_visible_devices) + else: + logger.warning(f"Wrong format of cuda_visible_devices {cuda_visible_devices}, devices not set") + + if num_nodes is None: + num_nodes = int(os.environ.get("NUM_NODES", 1)) + self.device_setting["NUM_NODES"] = num_nodes + + if mn_start_method is None: + mn_start_method = os.environ.get("MN_START_METHOD", "bcprun") + self.device_setting["MN_START_METHOD"] = mn_start_method + + if cmd_prefix is None: + cmd_prefix = os.environ.get("CMD_PREFIX", "") + self.device_setting["CMD_PREFIX"] = cmd_prefix + + if cmd_prefix is not None: + logger.info(f"Using user defined command running prefix {cmd_prefix}, will override other settings") + + return self + + def set_ensemble_method(self, ensemble_method_name: str = "AlgoEnsembleBestByFold", **kwargs: Any) -> AutoRunner: + """ + Set the bundle ensemble method name and parameters for save image transform parameters. + + Args: + ensemble_method_name: the name of the ensemble method. Only two methods are supported "AlgoEnsembleBestN" + and "AlgoEnsembleBestByFold". + kwargs: the keyword arguments used to define the ensemble method. Currently only ``n_best`` for + ``AlgoEnsembleBestN`` is supported. + """ + self.ensemble_method_name = look_up_option( + ensemble_method_name, supported=["AlgoEnsembleBestN", "AlgoEnsembleBestByFold"] + ) + self.kwargs.update(kwargs) + + return self + + def set_image_save_transform(self, **kwargs: Any) -> AutoRunner: + """ + Set the ensemble output transform. + + Args: + kwargs: image writing parameters for the ensemble inference. The kwargs format follows SaveImage + transform. For more information, check https://monai.readthedocs.io/en/stable/transforms.html#saveimage. + + """ + + are_all_args_present, extra_args = check_kwargs_exist_in_class_init(SaveImage, kwargs) + if are_all_args_present: + self.kwargs.update(kwargs) + else: + raise ValueError( + f"{extra_args} are not supported in monai.transforms.SaveImage," + "Check https://monai.readthedocs.io/en/stable/transforms.html#saveimage for more information." + ) + + return self + + def set_prediction_params(self, params: dict[str, Any] | None = None) -> AutoRunner: + """ + Set the prediction params for all algos. + + Args: + params: a dict that defines the overriding key-value pairs during prediction. The overriding method + is defined by the algo class. + + Examples: + + For BundleAlgo objects, this set of param will specify the algo ensemble to only inference the first + two files in the testing datalist {"file_slices": slice(0, 2)} + + """ + self.pred_params = deepcopy(params) if params is not None else {} + + return self + + def set_analyze_params(self, params: dict[str, Any] | None = None) -> AutoRunner: + """ + Set the data analysis extra params. + + Args: + params: a dict that defines the overriding key-value pairs during training. The overriding method + is defined by the algo class. + + """ + if params is None: + self.analyze_params = {"do_ccp": False, "device": "cuda"} + else: + self.analyze_params = deepcopy(params) + + return self + + def set_hpo_params(self, params: dict[str, Any] | None = None) -> AutoRunner: + """ + Set parameters for the HPO module and the algos before the training. It will attempt to (1) override bundle + templates with the key-value pairs in ``params`` (2) change the config of the HPO module (e.g. NNI) if the + key is found to be one of: + + - "trialCodeDirectory" + - "trialGpuNumber" + - "trialConcurrency" + - "maxTrialNumber" + - "maxExperimentDuration" + - "tuner" + - "trainingService" + + and (3) enable the dry-run mode if the user would generate the NNI configs without starting the NNI service. + + Args: + params: a dict that defines the overriding key-value pairs during instantiation of the algo. For + BundleAlgo, it will override the template config filling. + + Notes: + Users can set ``nni_dry_run`` to ``True`` in the ``params`` to enable the dry-run mode for the NNI backend. + + """ + self.hpo_params = self.train_params if params is None else params + + return self + + def set_nni_search_space(self, search_space: dict[str, Any]) -> AutoRunner: + """ + Set the search space for NNI parameter search. + + Args: + search_space: hyper parameter search space in the form of dict. For more information, please check + NNI documentation: https://nni.readthedocs.io/en/v2.2/Tutorial/SearchSpaceSpec.html . + """ + value_combinations = 1 + for k, v in search_space.items(): + if "_value" not in v: + raise ValueError(f"{search_space} key {k} value {v} has not _value") + value_combinations *= len(v["_value"]) + + self.search_space = search_space + self.hpo_tasks = value_combinations + + return self + + def _train_algo_in_sequence(self, history: list[dict[str, Any]]) -> None: + """ + Train the Algos in a sequential scheme. The order of training is randomized. + + Args: + history: the history of generated Algos. It is a list of dicts. Each element has the task name + (e.g. "dints_0" for dints network in fold 0) as the key and the algo object as the value. + After the training, the algo object with the ``best_metric`` will be saved as a pickle file. + + Note: + The final results of the model training will be written to all the generated algorithm's output + folders under the working directory. The results include the model checkpoints, a + progress.yaml, accuracies in CSV and a pickle file of the Algo object. + """ + for algo_dict in history: + algo = algo_dict[AlgoKeys.ALGO] + if has_option(algo.train, "device_setting"): + algo.train(self.train_params, self.device_setting) + else: + algo.train(self.train_params) + acc = algo.get_score() + + algo_meta_data = {str(AlgoKeys.SCORE): acc} + algo_to_pickle(algo, template_path=algo.template_path, **algo_meta_data) + + def _train_algo_in_nni(self, history: list[dict[str, Any]]) -> None: + """ + Train the Algos using HPO. + + Args: + history: the history of generated Algos. It is a list of dicts. Each element has the task name + (e.g. "dints_0" for dints network in fold 0) as the key and the algo object as the value. + After the training, the algo object with the ``best_metric`` will be saved as a pickle file. + + Note: + The final results of the model training will not be written to all the previously generated + algorithm's output folders. Instead, HPO will generate a new algo during the searching, and + the new algo will be saved under the working directory with a different format of the name. + For example, if the searching space has "learning_rate", the result of HPO will be written to + a folder name with original task name and the param (e.g. "dints_0_learning_rate_0.001"). + The results include the model checkpoints, a progress.yaml, accuracies in CSV and a pickle + file of the Algo object. + + """ + default_nni_config = { + "trialCodeDirectory": ".", + "trialGpuNumber": torch.cuda.device_count(), + "trialConcurrency": 1, + "maxTrialNumber": 10, + "maxExperimentDuration": "1h", + "tuner": {"name": "GridSearch"}, + "trainingService": {"platform": "local", "useActiveGpu": True}, + } + + last_total_tasks = len(import_bundle_algo_history(self.work_dir, only_trained=True)) + mode_dry_run = self.hpo_params.pop("nni_dry_run", False) + for algo_dict in history: + name = algo_dict[AlgoKeys.ID] + algo = algo_dict[AlgoKeys.ALGO] + nni_gen = NNIGen(algo=algo, params=self.hpo_params) + obj_filename = nni_gen.get_obj_filename() + nni_config = deepcopy(default_nni_config) + # override the default nni config with the same key in hpo_params + for key in self.hpo_params: + if key in nni_config: + nni_config[key] = self.hpo_params[key] + nni_config.update({"experimentName": name}) + nni_config.update({"search_space": self.search_space}) + trial_cmd = "python -m monai.apps.auto3dseg NNIGen run_algo " + obj_filename + " " + self.work_dir + nni_config.update({"trialCommand": trial_cmd}) + nni_config_filename = os.path.abspath(os.path.join(self.work_dir, f"{name}_nni_config.yaml")) + ConfigParser.export_config_file(nni_config, nni_config_filename, fmt="yaml", default_flow_style=None) + + max_trial = min(self.hpo_tasks, cast(int, default_nni_config["maxTrialNumber"])) + cmd = "nnictl create --config " + nni_config_filename + " --port 8088" + + if mode_dry_run: + logger.info(f"AutoRunner HPO is in dry-run mode. Please manually launch: {cmd}") + continue + + run_cmd(cmd.split(), check=True) + + n_trainings = len(import_bundle_algo_history(self.work_dir, only_trained=True)) + while n_trainings - last_total_tasks < max_trial: + sleep(1) + n_trainings = len(import_bundle_algo_history(self.work_dir, only_trained=True)) + + cmd = "nnictl stop --all" + run_cmd(cmd.split(), check=True) + logger.info(f"NNI completes HPO on {name}") + last_total_tasks = n_trainings + + def run(self): + """ + Run the AutoRunner pipeline + """ + # step 1: data analysis + if self.analyze and self.analyze_params is not None: + logger.info("Running data analysis...") + da = DataAnalyzer( + self.datalist_filename, self.dataroot, output_path=self.datastats_filename, **self.analyze_params + ) + da.get_all_case_stats() + + da = None # type: ignore + torch.cuda.empty_cache() + + self.export_cache(analyze=True, datastats=self.datastats_filename) + else: + logger.info("Skipping data analysis...") + + # step 2: algorithm generation + if self.algo_gen: + if not os.path.isfile(self.datastats_filename): + raise ValueError( + f"Could not find the datastats file {self.datastats_filename}. " + "Possibly the required data analysis step was not completed." + ) + + bundle_generator = BundleGen( + algos=self.algos, + algo_path=self.work_dir, + templates_path_or_url=self.templates_path_or_url, + data_stats_filename=self.datastats_filename, + data_src_cfg_name=self.data_src_cfg_name, + mlflow_tracking_uri=self.mlflow_tracking_uri, + mlflow_experiment_name=self.mlflow_experiment_name, + ) + + if self.gpu_customization: + bundle_generator.generate( + self.work_dir, + num_fold=self.num_fold, + gpu_customization=self.gpu_customization, + gpu_customization_specs=self.gpu_customization_specs, + allow_skip=self.allow_skip, + ) + else: + bundle_generator.generate(self.work_dir, num_fold=self.num_fold, allow_skip=self.allow_skip) + history = bundle_generator.get_history() + export_bundle_algo_history(history) + self.export_cache(algo_gen=True) + else: + logger.info("Skipping algorithm generation...") + + # step 3: algo training + auto_train_choice = self.train is None + if self.train or (auto_train_choice and not self.cache["train"]): + history = import_bundle_algo_history(self.work_dir, only_trained=False) + + if len(history) == 0: + raise ValueError( + f"Could not find training scripts in {self.work_dir}. " + "Possibly the required algorithms generation step was not completed." + ) + + if auto_train_choice: + skip_algos = [h[AlgoKeys.ID] for h in history if h[AlgoKeys.IS_TRAINED]] + if skip_algos: + logger.info( + f"Skipping already trained algos {skip_algos}." + "Set option train=True to always retrain all algos." + ) + history = [h for h in history if not h[AlgoKeys.IS_TRAINED]] + + if len(history) > 0: + if not self.hpo: + self._train_algo_in_sequence(history) + else: + self._train_algo_in_nni(history) + + self.export_cache(train=True) + else: + logger.info("Skipping algorithm training...") + + # step 4: model ensemble and write the prediction to disks. + if self.ensemble: + ensemble_runner = EnsembleRunner( + data_src_cfg_name=self.data_src_cfg_name, + work_dir=self.work_dir, + num_fold=self.num_fold, + ensemble_method_name=self.ensemble_method_name, + mgpu=int(self.device_setting["n_devices"]) > 1, + **self.kwargs, # for set_image_save_transform + **self.pred_params, + ) # for inference + ensemble_runner.run(self.device_setting) + logger.info("Auto3Dseg pipeline is completed successfully.") diff --git a/MONAI/source/monai/apps/auto3dseg/bundle_gen.py b/MONAI/source/monai/apps/auto3dseg/bundle_gen.py new file mode 100644 index 0000000000000000000000000000000000000000..87a612cae2d0701bc1cfad2d5b6745c8d3c3a480 --- /dev/null +++ b/MONAI/source/monai/apps/auto3dseg/bundle_gen.py @@ -0,0 +1,665 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import importlib +import os +import re +import shutil +import subprocess +import sys +import time +import warnings +from copy import deepcopy +from pathlib import Path +from tempfile import TemporaryDirectory +from typing import Any +from urllib.parse import urlparse + +import torch + +from monai.apps import download_and_extract +from monai.apps.utils import get_logger +from monai.auto3dseg.algo_gen import Algo, AlgoGen +from monai.auto3dseg.utils import ( + _prepare_cmd_bcprun, + _prepare_cmd_default, + _prepare_cmd_torchrun, + _run_cmd_bcprun, + _run_cmd_torchrun, + algo_to_pickle, +) +from monai.bundle.config_parser import ConfigParser +from monai.config import PathLike +from monai.utils import ensure_tuple, look_up_option, run_cmd +from monai.utils.enums import AlgoKeys +from monai.utils.misc import MONAIEnvVars + +logger = get_logger(module_name=__name__) +ALGO_HASH = MONAIEnvVars.algo_hash() + +__all__ = ["BundleAlgo", "BundleGen"] + + +class BundleAlgo(Algo): + """ + An algorithm represented by a set of bundle configurations and scripts. + + ``BundleAlgo.cfg`` is a ``monai.bundle.ConfigParser`` instance. + + .. code-block:: python + + from monai.apps.auto3dseg import BundleAlgo + + data_stats_yaml = "../datastats.yaml" + algo = BundleAlgo(template_path="../algorithm_templates") + algo.set_data_stats(data_stats_yaml) + # algo.set_data_src("../data_src.json") + algo.export_to_disk(".", algo_name="segresnet2d_1") + + This class creates MONAI bundles from a directory of 'bundle template'. Different from the regular MONAI bundle + format, the bundle template may contain placeholders that must be filled using ``fill_template_config`` during + ``export_to_disk``. Then created bundle keeps the same file structure as the template. + + """ + + def __init__(self, template_path: PathLike): + """ + Create an Algo instance based on the predefined Algo template. + + Args: + template_path: path to a folder that contains the algorithm templates. + Please check https://github.com/Project-MONAI/research-contributions/tree/main/auto3dseg/algorithm_templates + + """ + + self.template_path = template_path + self.data_stats_files = "" + self.data_list_file = "" + self.mlflow_tracking_uri: str | None = None + self.mlflow_experiment_name: str | None = None + self.output_path = "" + self.name = "" + self.best_metric = None + # track records when filling template config: {"": {"": value, ...}, ...} + self.fill_records: dict = {} + # device_setting set default value and sanity check, in case device_setting not from autorunner + self.device_setting: dict[str, int | str] = { + "CUDA_VISIBLE_DEVICES": ",".join([str(x) for x in range(torch.cuda.device_count())]), + "n_devices": int(torch.cuda.device_count()), + "NUM_NODES": int(os.environ.get("NUM_NODES", 1)), + "MN_START_METHOD": os.environ.get("MN_START_METHOD", "bcprun"), + "CMD_PREFIX": os.environ.get("CMD_PREFIX", ""), + } + + def pre_check_skip_algo(self, skip_bundlegen: bool = False, skip_info: str = "") -> tuple[bool, str]: + """ + Analyse the data analysis report and check if the algorithm needs to be skipped. + This function is overriden within algo. + Args: + skip_bundlegen: skip generating bundles for this algo if true. + skip_info: info to print when skipped. + """ + return skip_bundlegen, skip_info + + def set_data_stats(self, data_stats_files: str) -> None: + """ + Set the data analysis report (generated by DataAnalyzer). + + Args: + data_stats_files: path to the datastats yaml file + """ + self.data_stats_files = data_stats_files + + def set_data_source(self, data_src_cfg: str) -> None: + """ + Set the data source configuration file + + Args: + data_src_cfg: path to a configuration file (yaml) that contains datalist, dataroot, and other params. + The config will be in a form of {"modality": "ct", "datalist": "path_to_json_datalist", "dataroot": + "path_dir_data"} + """ + self.data_list_file = data_src_cfg + + def set_mlflow_tracking_uri(self, mlflow_tracking_uri: str | None) -> None: + """ + Set the tracking URI for MLflow server + + Args: + mlflow_tracking_uri: a tracking URI for MLflow server which could be local directory or address of + the remote tracking Server; MLflow runs will be recorded locally in algorithms' model folder if + the value is None. + """ + self.mlflow_tracking_uri = mlflow_tracking_uri + + def set_mlflow_experiment_name(self, mlflow_experiment_name: str | None) -> None: + """ + Set the experiment name for MLflow server + + Args: + mlflow_experiment_name: a string to specify the experiment name for MLflow server. + """ + self.mlflow_experiment_name = mlflow_experiment_name + + def fill_template_config(self, data_stats_filename: str, algo_path: str, **kwargs: Any) -> dict: + """ + The configuration files defined when constructing this Algo instance might not have a complete training + and validation pipelines. Some configuration components and hyperparameters of the pipelines depend on the + training data and other factors. This API is provided to allow the creation of fully functioning config files. + Return the records of filling template config: {"": {"": value, ...}, ...}. + + Args: + data_stats_filename: filename of the data stats report (generated by DataAnalyzer) + + Notes: + Template filling is optional. The user can construct a set of pre-filled configs without replacing values + by using the data analysis results. It is also intended to be re-implemented in subclasses of BundleAlgo + if the user wants their own way of auto-configured template filling. + """ + return {} + + def export_to_disk(self, output_path: str, algo_name: str, **kwargs: Any) -> None: + """ + Fill the configuration templates, write the bundle (configs + scripts) to folder `output_path/algo_name`. + + Args: + output_path: Path to export the 'scripts' and 'configs' directories. + algo_name: the identifier of the algorithm (usually contains the name and extra info like fold ID). + kwargs: other parameters, including: "copy_dirs=True/False" means whether to copy the template as output + instead of inplace operation, "fill_template=True/False" means whether to fill the placeholders + in the template. other parameters are for `fill_template_config` function. + + """ + if kwargs.pop("copy_dirs", True): + self.output_path = os.path.join(output_path, algo_name) + os.makedirs(self.output_path, exist_ok=True) + if os.path.isdir(self.output_path): + shutil.rmtree(self.output_path) + # copy algorithm_templates/ to the working directory output_path + shutil.copytree(os.path.join(str(self.template_path), self.name), self.output_path) + else: + self.output_path = str(self.template_path) + if kwargs.pop("fill_template", True): + self.fill_records = self.fill_template_config(self.data_stats_files, self.output_path, **kwargs) + logger.info(f"Generated:{self.output_path}") + + def _create_cmd(self, train_params: None | dict = None) -> tuple[str, str]: + """ + Create the command to execute training. + + """ + if train_params is None: + train_params = {} + params = deepcopy(train_params) + + train_py = os.path.join(self.output_path, "scripts", "train.py") + config_dir = os.path.join(self.output_path, "configs") + + config_files = [] + if os.path.isdir(config_dir): + for file in sorted(os.listdir(config_dir)): + if file.endswith("yaml") or file.endswith("json"): + # Python Fire may be confused by single-quoted WindowsPath + config_files.append(Path(os.path.join(config_dir, file)).as_posix()) + + if int(self.device_setting["NUM_NODES"]) > 1: + # multi-node command + # only bcprun is supported for now + try: + look_up_option(self.device_setting["MN_START_METHOD"], ["bcprun"]) + except ValueError as err: + raise NotImplementedError( + f"{self.device_setting['MN_START_METHOD']} is not supported yet." + "Try modify BundleAlgo._create_cmd for your cluster." + ) from err + + return ( + _prepare_cmd_bcprun( + f"{train_py} run", + cmd_prefix=f"{self.device_setting['CMD_PREFIX']}", + config_file=config_files, + **params, + ), + "", + ) + elif int(self.device_setting["n_devices"]) > 1: + return _prepare_cmd_torchrun(f"{train_py} run", config_file=config_files, **params), "" + else: + return ( + _prepare_cmd_default( + f"{train_py} run", + cmd_prefix=f"{self.device_setting['CMD_PREFIX']}", + config_file=config_files, + **params, + ), + "", + ) + + def _run_cmd(self, cmd: str, devices_info: str = "") -> subprocess.CompletedProcess: + """ + Execute the training command with target devices information. + + """ + if devices_info: + warnings.warn(f"input devices_info {devices_info} is deprecated and ignored.") + + ps_environ = os.environ.copy() + ps_environ["CUDA_VISIBLE_DEVICES"] = str(self.device_setting["CUDA_VISIBLE_DEVICES"]) + + # delete pattern "VAR=VALUE" at the beginning of the string, with optional leading/trailing whitespaces + cmd = re.sub(r"^\s*\w+=.*?\s+", "", cmd) + + if int(self.device_setting["NUM_NODES"]) > 1: + try: + look_up_option(self.device_setting["MN_START_METHOD"], ["bcprun"]) + except ValueError as err: + raise NotImplementedError( + f"{self.device_setting['MN_START_METHOD']} is not supported yet." + "Try modify BundleAlgo._run_cmd for your cluster." + ) from err + + return _run_cmd_bcprun(cmd, n=self.device_setting["NUM_NODES"], p=self.device_setting["n_devices"]) + elif int(self.device_setting["n_devices"]) > 1: + return _run_cmd_torchrun( + cmd, nnodes=1, nproc_per_node=self.device_setting["n_devices"], env=ps_environ, check=True + ) + else: + return run_cmd(cmd.split(), run_cmd_verbose=True, env=ps_environ, check=True) + + def train( + self, train_params: None | dict = None, device_setting: None | dict = None + ) -> subprocess.CompletedProcess: + """ + Load the run function in the training script of each model. Training parameter is predefined by the + algo_config.yaml file, which is pre-filled by the fill_template_config function in the same instance. + + Args: + train_params: training parameters + device_setting: device related settings, should follow the device_setting in auto_runner.set_device_info. + 'CUDA_VISIBLE_DEVICES' should be a string e.g. '0,1,2,3' + """ + if device_setting is not None: + self.device_setting.update(device_setting) + self.device_setting["n_devices"] = len(str(self.device_setting["CUDA_VISIBLE_DEVICES"]).split(",")) + + if train_params is not None and "CUDA_VISIBLE_DEVICES" in train_params: + warnings.warn("CUDA_VISIBLE_DEVICES is deprecated from train_params!") + train_params.pop("CUDA_VISIBLE_DEVICES") + + cmd, _unused_return = self._create_cmd(train_params) + return self._run_cmd(cmd) + + def get_score(self, *args, **kwargs): + """ + Returns validation scores of the model trained by the current Algo. + """ + config_yaml = os.path.join(self.output_path, "configs", "hyper_parameters.yaml") + parser = ConfigParser() + parser.read_config(config_yaml) + ckpt_path = parser.get_parsed_content("ckpt_path", default=self.output_path) + + dict_file = ConfigParser.load_config_file(os.path.join(ckpt_path, "progress.yaml")) + # dict_file: a list of scores saved in the form of dict in progress.yaml + return dict_file[-1]["best_avg_dice_score"] # the last one is the best one + + def get_inferer(self, *args, **kwargs): + """ + Load the InferClass from the infer.py. The InferClass should be defined in the template under the path of + `"scripts/infer.py"`. It is required to define the "InferClass" (name is fixed) with two functions at least + (``__init__`` and ``infer``). The init class has an override kwargs that can be used to override parameters in + the run-time optionally. + + Examples: + + .. code-block:: python + + class InferClass + def __init__(self, config_file: Optional[Union[str, Sequence[str]]] = None, **override): + # read configs from config_file (sequence) + # set up transforms + # set up model + # set up other hyper parameters + return + + @torch.no_grad() + def infer(self, image_file): + # infer the model and save the results to output + return output + + """ + infer_py = os.path.join(self.output_path, "scripts", "infer.py") + if not os.path.isfile(infer_py): + raise ValueError(f"{infer_py} is not found, please check the path.") + + config_dir = os.path.join(self.output_path, "configs") + configs_path = [os.path.join(config_dir, f) for f in os.listdir(config_dir)] + + spec = importlib.util.spec_from_file_location("InferClass", infer_py) + infer_class = importlib.util.module_from_spec(spec) # type: ignore + sys.modules["InferClass"] = infer_class + spec.loader.exec_module(infer_class) # type: ignore + return infer_class.InferClass(configs_path, *args, **kwargs) + + def predict(self, predict_files: list, predict_params: dict | None = None) -> list: + """ + Use the trained model to predict the outputs with a given input image. + + Args: + predict_files: a list of paths to files to run inference on ["path_to_image_1", "path_to_image_2"] + predict_params: a dict to override the parameters in the bundle config (including the files to predict). + + """ + params = {} if predict_params is None else deepcopy(predict_params) + inferer = self.get_inferer(**params) + return [inferer.infer(f) for f in ensure_tuple(predict_files)] + + def get_output_path(self): + """Returns the algo output paths to find the algo scripts and configs.""" + return self.output_path + + +# path to download the algo_templates +default_algo_zip = ( + f"https://github.com/Project-MONAI/research-contributions/releases/download/algo_templates/{ALGO_HASH}.tar.gz" +) + +# default algorithms +default_algos = { + "segresnet2d": dict(_target_="segresnet2d.scripts.algo.Segresnet2dAlgo"), + "dints": dict(_target_="dints.scripts.algo.DintsAlgo"), + "swinunetr": dict(_target_="swinunetr.scripts.algo.SwinunetrAlgo"), + "segresnet": dict(_target_="segresnet.scripts.algo.SegresnetAlgo"), +} + + +def _download_algos_url(url: str, at_path: str) -> dict[str, dict[str, str]]: + """ + Downloads the algorithm templates release archive, and extracts it into a parent directory of the at_path folder. + Returns a dictionary of the algorithm templates. + """ + at_path = os.path.abspath(at_path) + zip_download_dir = TemporaryDirectory() + algo_compressed_file = os.path.join(zip_download_dir.name, "algo_templates.tar.gz") + + download_attempts = 3 + for i in range(download_attempts): + try: + download_and_extract(url=url, filepath=algo_compressed_file, output_dir=os.path.dirname(at_path)) + except Exception as e: + msg = f"Download and extract of {url} failed, attempt {i + 1}/{download_attempts}." + if i < download_attempts - 1: + warnings.warn(msg) + time.sleep(i) + else: + zip_download_dir.cleanup() + raise ValueError(msg) from e + else: + break + + zip_download_dir.cleanup() + + algos_all = deepcopy(default_algos) + for name in algos_all: + algos_all[name]["template_path"] = at_path + + return algos_all + + +def _copy_algos_folder(folder, at_path): + """ + Copies the algorithm templates folder to at_path. + Returns a dictionary of algorithm templates. + """ + folder = os.path.abspath(folder) + at_path = os.path.abspath(at_path) + + if folder != at_path: + if os.path.exists(at_path): + shutil.rmtree(at_path) + shutil.copytree(folder, at_path) + + algos_all = {} + for name in os.listdir(at_path): + if os.path.exists(os.path.join(folder, name, "scripts", "algo.py")): + algos_all[name] = dict(_target_=f"{name}.scripts.algo.{name.capitalize()}Algo", template_path=at_path) + logger.info(f"Copying template: {name} -- {algos_all[name]}") + if not algos_all: + raise ValueError(f"Unable to find any algos in {folder}") + + return algos_all + + +class BundleGen(AlgoGen): + """ + This class generates a set of bundles according to the cross-validation folds, each of them can run independently. + + Args: + algo_path: the directory path to save the algorithm templates. Default is the current working dir. + algos: If dictionary, it outlines the algorithm to use. If a list or a string, defines a subset of names of + the algorithms to use, e.g. ('segresnet', 'dints') out of the full set of algorithm templates provided + by templates_path_or_url. Defaults to None - to use all available algorithms. + templates_path_or_url: the folder with the algorithm templates or a url. If None provided, the default template + zip url will be downloaded and extracted into the algo_path. The current default options are released at: + https://github.com/Project-MONAI/research-contributions/tree/main/auto3dseg. + data_stats_filename: the path to the data stats file (generated by DataAnalyzer). + data_src_cfg_name: the path to the data source config YAML file. The config will be in a form of + {"modality": "ct", "datalist": "path_to_json_datalist", "dataroot": "path_dir_data"}. + mlflow_tracking_uri: a tracking URI for MLflow server which could be local directory or address of + the remote tracking Server; MLflow runs will be recorded locally in algorithms' model folder if + the value is None. + mlfow_experiment_name: a string to specify the experiment name for MLflow server. + .. code-block:: bash + + python -m monai.apps.auto3dseg BundleGen generate --data_stats_filename="../algorithms/datastats.yaml" + """ + + def __init__( + self, + algo_path: str = ".", + algos: dict | list | str | None = None, + templates_path_or_url: str | None = None, + data_stats_filename: str | None = None, + data_src_cfg_name: str | None = None, + mlflow_tracking_uri: str | None = None, + mlflow_experiment_name: str | None = None, + ): + if algos is None or isinstance(algos, (list, tuple, str)): + if templates_path_or_url is None: + templates_path_or_url = default_algo_zip + + at_path = os.path.join(os.path.abspath(algo_path), "algorithm_templates") + + if os.path.isdir(templates_path_or_url): + # if a local folder, copy if necessary + logger.info(f"BundleGen from directory {templates_path_or_url}") + algos_all = _copy_algos_folder(folder=templates_path_or_url, at_path=at_path) + elif urlparse(templates_path_or_url).scheme in ("http", "https"): + # if url, trigger the download and extract process + logger.info(f"BundleGen from {templates_path_or_url}") + algos_all = _download_algos_url(url=templates_path_or_url, at_path=at_path) + else: + raise ValueError(f"{self.__class__} received invalid templates_path_or_url: {templates_path_or_url}") + + if algos is not None: + algos = {k: v for k, v in algos_all.items() if k in ensure_tuple(algos)} # keep only provided + if len(algos) == 0: + raise ValueError(f"Unable to find provided algos in {algos_all}") + else: + algos = algos_all + + self.algos: Any = [] + if isinstance(algos, dict): + for algo_name, algo_params in sorted(algos.items()): + template_path = algo_params.get("template_path", ".") + if len(template_path) > 0 and template_path not in sys.path: + sys.path.append(template_path) + + try: + onealgo = ConfigParser(algo_params).get_parsed_content() + onealgo.name = algo_name + self.algos.append(onealgo) + except RuntimeError as e: + msg = """Please make sure the folder structure of an Algo Template follows + [algo_name] + ├── configs + │ ├── hyper_parameters.yaml # automatically generated yaml from a set of ``template_configs`` + └── scripts + ├── test.py + ├── __init__.py + └── validate.py + """ + raise RuntimeError(msg) from e + else: + raise ValueError("Unexpected error algos is not a dict") + + self.data_stats_filename = data_stats_filename + self.data_src_cfg_name = data_src_cfg_name + self.mlflow_tracking_uri = mlflow_tracking_uri + self.mlflow_experiment_name = mlflow_experiment_name + self.history: list[dict] = [] + + def set_data_stats(self, data_stats_filename: str) -> None: + """ + Set the data stats filename + + Args: + data_stats_filename: filename of datastats + """ + self.data_stats_filename = data_stats_filename + + def get_data_stats(self): + """Get the filename of the data stats""" + return self.data_stats_filename + + def set_data_src(self, data_src_cfg_name): + """ + Set the data source filename + + Args: + data_src_cfg_name: filename of data_source file + """ + self.data_src_cfg_name = data_src_cfg_name + + def get_data_src(self): + """Get the data source filename""" + return self.data_src_cfg_name + + def set_mlflow_tracking_uri(self, mlflow_tracking_uri): + """ + Set the tracking URI for MLflow server + + Args: + mlflow_tracking_uri: a tracking URI for MLflow server which could be local directory or address of + the remote tracking Server; MLflow runs will be recorded locally in algorithms' model folder if + the value is None. + """ + self.mlflow_tracking_uri = mlflow_tracking_uri + + def set_mlflow_experiment_name(self, mlflow_experiment_name): + """ + Set the experiment name for MLflow server + + Args: + mlflow_experiment_name: a string to specify the experiment name for MLflow server. + """ + self.mlflow_experiment_name = mlflow_experiment_name + + def get_mlflow_tracking_uri(self): + """Get the tracking URI for MLflow server""" + return self.mlflow_tracking_uri + + def get_mlflow_experiment_name(self): + """Get the experiment name for MLflow server""" + return self.mlflow_experiment_name + + def get_history(self) -> list: + """Get the history of the bundleAlgo object with their names/identifiers""" + return self.history + + def generate( + self, + output_folder: str = ".", + num_fold: int = 5, + gpu_customization: bool = False, + gpu_customization_specs: dict[str, Any] | None = None, + allow_skip: bool = True, + ) -> None: + """ + Generate the bundle scripts/configs for each bundleAlgo + + Args: + output_folder: the output folder to save each algorithm. + num_fold: the number of cross validation fold. + gpu_customization: the switch to determine automatically customize/optimize bundle script/config + parameters for each bundleAlgo based on gpus. Custom parameters are obtained through dummy + training to simulate the actual model training process and hyperparameter optimization (HPO) + experiments. + gpu_customization_specs: the dictionary to enable users overwrite the HPO settings. user can + overwrite part of variables as follows or all of them. The structure is as follows. + allow_skip: a switch to determine if some Algo in the default templates can be skipped based on the + analysis on the dataset from Auto3DSeg DataAnalyzer. + + .. code-block:: python + + gpu_customization_specs = { + 'ALGO': { + 'num_trials': 6, + 'range_num_images_per_batch': [1, 20], + 'range_num_sw_batch_size': [1, 20] + } + } + + ALGO: the name of algorithm. It could be one of algorithm names (e.g., 'dints') or 'universal' which + would apply changes to all algorithms. Possible options are + + - {``"universal"``, ``"dints"``, ``"segresnet"``, ``"segresnet2d"``, ``"swinunetr"``}. + + num_trials: the number of HPO trials/experiments to run. + range_num_images_per_batch: the range of number of images per mini-batch. + range_num_sw_batch_size: the range of batch size in sliding-window inferer. + """ + fold_idx = list(range(num_fold)) + for algo in self.algos: + for f_id in ensure_tuple(fold_idx): + data_stats = self.get_data_stats() + data_src_cfg = self.get_data_src() + mlflow_tracking_uri = self.get_mlflow_tracking_uri() + mlflow_experiment_name = self.get_mlflow_experiment_name() + gen_algo = deepcopy(algo) + gen_algo.set_data_stats(data_stats) + gen_algo.set_data_source(data_src_cfg) + gen_algo.set_mlflow_tracking_uri(mlflow_tracking_uri) + gen_algo.set_mlflow_experiment_name(mlflow_experiment_name) + name = f"{gen_algo.name}_{f_id}" + + if allow_skip: + skip_bundlegen, skip_info = gen_algo.pre_check_skip_algo() + if skip_bundlegen: + logger.info(f"{name} is skipped! {skip_info}") + continue + + if gpu_customization: + gen_algo.export_to_disk( + output_folder, + name, + fold=f_id, + gpu_customization=True, + gpu_customization_specs=gpu_customization_specs, + ) + else: + gen_algo.export_to_disk(output_folder, name, fold=f_id) + + algo_to_pickle(gen_algo, template_path=algo.template_path) + self.history.append( + {AlgoKeys.ID: name, AlgoKeys.ALGO: gen_algo} + ) # track the previous, may create a persistent history diff --git a/MONAI/source/monai/apps/auto3dseg/data_analyzer.py b/MONAI/source/monai/apps/auto3dseg/data_analyzer.py new file mode 100644 index 0000000000000000000000000000000000000000..15e56abfea4c7e37148402e378368afae24f948a --- /dev/null +++ b/MONAI/source/monai/apps/auto3dseg/data_analyzer.py @@ -0,0 +1,386 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import warnings +from os import path +from typing import Any, cast + +import numpy as np +import torch +from torch.multiprocessing import get_context + +from monai.apps.auto3dseg.transforms import EnsureSameShaped +from monai.apps.utils import get_logger +from monai.auto3dseg import SegSummarizer +from monai.auto3dseg.utils import datafold_read +from monai.bundle import config_parser +from monai.bundle.config_parser import ConfigParser +from monai.data import DataLoader, Dataset, partition_dataset +from monai.data.utils import no_collation +from monai.transforms import Compose, EnsureTyped, LoadImaged, Orientationd +from monai.utils import ImageMetaKey, StrEnum, min_version, optional_import +from monai.utils.enums import DataStatsKeys, ImageStatsKeys + + +def strenum_representer(dumper, data): + return dumper.represent_scalar("tag:yaml.org,2002:str", data.value) + + +if optional_import("yaml")[1]: + config_parser.yaml.SafeDumper.add_multi_representer(StrEnum, strenum_representer) + +tqdm, has_tqdm = optional_import("tqdm", "4.47.0", min_version, "tqdm") +logger = get_logger(module_name=__name__) + +__all__ = ["DataAnalyzer"] + + +class DataAnalyzer: + """ + The DataAnalyzer automatically analyzes given medical image dataset and reports the statistics. + The module expects file paths to the image data and utilizes the LoadImaged transform to read the + files, which supports nii, nii.gz, png, jpg, bmp, npz, npy, and dcm formats. Currently, only + segmentation task is supported, so the user needs to provide paths to the image and label files + (if have). Also, label data format is preferred to be (1,H,W,D), with the label index in the + first dimension. If it is in onehot format, it will be converted to the preferred format. + + Args: + datalist: a Python dictionary storing group, fold, and other information of the medical + image dataset, or a string to the JSON file storing the dictionary. + dataroot: user's local directory containing the datasets. + output_path: path to save the analysis result. + average: whether to average the statistical value across different image modalities. + do_ccp: apply the connected component algorithm to process the labels/images + device: a string specifying hardware (CUDA/CPU) utilized for the operations. + worker: number of workers to use for loading datasets in each GPU/CPU sub-process. + image_key: a string that user specify for the image. The DataAnalyzer will look it up in the + datalist to locate the image files of the dataset. + label_key: a string that user specify for the label. The DataAnalyzer will look it up in the + datalist to locate the label files of the dataset. If label_key is NoneType or "None", + the DataAnalyzer will skip looking for labels and all label-related operations. + hist_bins: bins to compute histogram for each image channel. + hist_range: ranges to compute histogram for each image channel. + fmt: format used to save the analysis results. Currently support ``"json"`` and ``"yaml"``, defaults to "yaml". + histogram_only: whether to only compute histograms. Defaults to False. + extra_params: other optional arguments. Currently supported arguments are : + 'allowed_shape_difference' (default 5) can be used to change the default tolerance of + the allowed shape differences between the image and label items. In case of shape mismatch below + the tolerance, the label image will be resized to match the image using nearest interpolation. + + + Examples: + .. code-block:: python + + from monai.apps.auto3dseg.data_analyzer import DataAnalyzer + + datalist = { + "testing": [{"image": "image_003.nii.gz"}], + "training": [ + {"fold": 0, "image": "image_001.nii.gz", "label": "label_001.nii.gz"}, + {"fold": 0, "image": "image_002.nii.gz", "label": "label_002.nii.gz"}, + {"fold": 1, "image": "image_001.nii.gz", "label": "label_001.nii.gz"}, + {"fold": 1, "image": "image_004.nii.gz", "label": "label_004.nii.gz"}, + ], + } + + dataroot = '/datasets' # the directory where you have the image files (nii.gz) + DataAnalyzer(datalist, dataroot) + + Notes: + The module can also be called from the command line interface (CLI). + + For example: + + .. code-block:: bash + + python -m monai.apps.auto3dseg \\ + DataAnalyzer \\ + get_all_case_stats \\ + --datalist="my_datalist.json" \\ + --dataroot="my_dataroot_dir" + + """ + + def __init__( + self, + datalist: str | dict, + dataroot: str = "", + output_path: str = "./datastats.yaml", + average: bool = True, + do_ccp: bool = False, + device: str | torch.device = "cuda", + worker: int = 4, + image_key: str = "image", + label_key: str | None = "label", + hist_bins: list | int | None = 0, + hist_range: list | None = None, + fmt: str = "yaml", + histogram_only: bool = False, + **extra_params: Any, + ): + if path.isfile(output_path): + warnings.warn(f"File {output_path} already exists and will be overwritten.") + logger.debug(f"{output_path} will be overwritten by a new datastat.") + + self.datalist = datalist + self.dataroot = dataroot + self.output_path = output_path + self.average = average + self.do_ccp = do_ccp + self.device = torch.device(device) + self.worker = worker + self.image_key = image_key + self.label_key = None if label_key == "None" else label_key + self.hist_bins = hist_bins + self.hist_range: list = [-500, 500] if hist_range is None else hist_range + self.fmt = fmt + self.histogram_only = histogram_only + self.extra_params = extra_params + + @staticmethod + def _check_data_uniformity(keys: list[str], result: dict) -> bool: + """ + Check data uniformity since DataAnalyzer provides no support to multi-modal images with different + affine matrices/spacings due to monai transforms. + + Args: + keys: a list of string-type keys under image_stats dictionary. + + Returns: + False if one of the selected key values is not constant across the dataset images. + + """ + + if DataStatsKeys.SUMMARY not in result or DataStatsKeys.IMAGE_STATS not in result[DataStatsKeys.SUMMARY]: + return True + constant_props = [result[DataStatsKeys.SUMMARY][DataStatsKeys.IMAGE_STATS][key] for key in keys] + for prop in constant_props: + if "stdev" in prop and np.any(prop["stdev"]): + logger.debug(f"summary image_stats {prop} has non-zero stdev {prop['stdev']}.") + return False + + return True + + def get_all_case_stats(self, key="training", transform_list=None): + """ + Get all case stats. Caller of the DataAnalyser class. The function initiates multiple GPU or CPU processes of the internal + _get_all_case_stats functions, which iterates datalist and call SegSummarizer to generate stats for each case. + After all case stats are generated, SegSummarizer is called to combine results. + + Args: + key: dataset key + transform_list: option list of transforms before SegSummarizer + + Returns: + A data statistics dictionary containing + "stats_summary" (summary statistics of the entire datasets). Within stats_summary + there are "image_stats" (summarizing info of shape, channel, spacing, and etc + using operations_summary), "image_foreground_stats" (info of the intensity for the + non-zero labeled voxels), and "label_stats" (info of the labels, pixel percentage, + image_intensity, and each individual label in a list) + "stats_by_cases" (List type value. Each element of the list is statistics of + an image-label info. Within each element, there are: "image" (value is the + path to an image), "label" (value is the path to the corresponding label), "image_stats" + (summarizing info of shape, channel, spacing, and etc using operations), + "image_foreground_stats" (similar to the previous one but one foreground image), and + "label_stats" (stats of the individual labels ) + + Notes: + Since the backend of the statistics computation are torch/numpy, nan/inf value + may be generated and carried over in the computation. In such cases, the output + dictionary will include .nan/.inf in the statistics. + + """ + result: dict[DataStatsKeys, Any] = {DataStatsKeys.SUMMARY: {}, DataStatsKeys.BY_CASE: []} + result_bycase: dict[DataStatsKeys, Any] = {DataStatsKeys.SUMMARY: {}, DataStatsKeys.BY_CASE: []} + if self.device.type == "cpu": + nprocs = 1 + logger.info("Using CPU for data analyzing!") + else: + nprocs = torch.cuda.device_count() + logger.info(f"Found {nprocs} GPUs for data analyzing!") + if nprocs > 1: + tmp_ctx: Any = get_context("forkserver") + with tmp_ctx.Manager() as manager: + manager_list = manager.list() + processes = [] + for rank in range(nprocs): + p = tmp_ctx.Process( + target=self._get_all_case_stats, args=(rank, nprocs, manager_list, key, transform_list) + ) + processes.append(p) + for p in processes: + p.start() + for p in processes: + p.join() + # merge DataStatsKeys.BY_CASE + for _ in manager_list: + result_bycase[DataStatsKeys.BY_CASE].extend(_[DataStatsKeys.BY_CASE]) + else: + result_bycase = self._get_all_case_stats(0, 1, None, key, transform_list) + + summarizer = SegSummarizer( + self.image_key, + self.label_key, + average=self.average, + do_ccp=self.do_ccp, + hist_bins=self.hist_bins, + hist_range=self.hist_range, + histogram_only=self.histogram_only, + ) + n_cases = len(result_bycase[DataStatsKeys.BY_CASE]) + result[DataStatsKeys.SUMMARY] = summarizer.summarize(cast(list, result_bycase[DataStatsKeys.BY_CASE])) + result[DataStatsKeys.SUMMARY]["n_cases"] = n_cases + result_bycase[DataStatsKeys.SUMMARY] = result[DataStatsKeys.SUMMARY] + if not self._check_data_uniformity([ImageStatsKeys.SPACING], result): + logger.info("Data spacing is not completely uniform. MONAI transforms may provide unexpected result") + if self.output_path: + logger.info(f"Writing data stats to {self.output_path}.") + ConfigParser.export_config_file( + result, self.output_path, fmt=self.fmt, default_flow_style=None, sort_keys=False + ) + by_case_path = self.output_path.replace(f".{self.fmt}", f"_by_case.{self.fmt}") + if by_case_path == self.output_path: # self.output_path not ended with self.fmt? + by_case_path += f".by_case.{self.fmt}" + logger.info(f"Writing by-case data stats to {by_case_path}, this may take a while.") + ConfigParser.export_config_file( + result_bycase, by_case_path, fmt=self.fmt, default_flow_style=None, sort_keys=False + ) + # release memory + if self.device.type == "cuda": + # release unreferenced tensors to mitigate OOM + # limitation: https://github.com/pytorch/pytorch/issues/12873#issuecomment-482916237 + torch.cuda.empty_cache() + result[DataStatsKeys.BY_CASE] = result_bycase[DataStatsKeys.BY_CASE] + return result + + def _get_all_case_stats( + self, + rank: int = 0, + world_size: int = 1, + manager_list: list | None = None, + key: str = "training", + transform_list: list | None = None, + ) -> Any: + """ + Get all case stats from a partitioned datalist. The function can only be called internally by get_all_case_stats. + Args: + rank: GPU process rank, 0 for CPU process + world_size: total number of GPUs, 1 for CPU process + manager_list: multiprocessing manager list object, if using multi-GPU. + key: dataset key + transform_list: option list of transforms before SegSummarizer + """ + summarizer = SegSummarizer( + self.image_key, + self.label_key, + average=self.average, + do_ccp=self.do_ccp, + hist_bins=self.hist_bins, + hist_range=self.hist_range, + histogram_only=self.histogram_only, + ) + keys = list(filter(None, [self.image_key, self.label_key])) + if transform_list is None: + transform_list = [ + LoadImaged(keys=keys, ensure_channel_first=True, image_only=True), + EnsureTyped(keys=keys, data_type="tensor", dtype=torch.float), + Orientationd(keys=keys, axcodes="RAS"), + ] + if self.label_key is not None: + allowed_shape_difference = self.extra_params.pop("allowed_shape_difference", 5) + transform_list.append( + EnsureSameShaped( + keys=self.label_key, + source_key=self.image_key, + allowed_shape_difference=allowed_shape_difference, + ) + ) + + transform = Compose(transform_list) + files, _ = datafold_read(datalist=self.datalist, basedir=self.dataroot, fold=-1, key=key) + if world_size <= len(files): + files = partition_dataset(data=files, num_partitions=world_size)[rank] + else: + files = partition_dataset(data=files, num_partitions=len(files))[rank] if rank < len(files) else [] + dataset = Dataset(data=files, transform=transform) + dataloader = DataLoader( + dataset, + batch_size=1, + shuffle=False, + num_workers=self.worker, + collate_fn=no_collation, + pin_memory=self.device.type == "cuda", + ) + result_bycase: dict[DataStatsKeys, Any] = {DataStatsKeys.SUMMARY: {}, DataStatsKeys.BY_CASE: []} + device = self.device if self.device.type == "cpu" else torch.device("cuda", rank) + if device.type == "cuda" and not (torch.cuda.is_available() and torch.cuda.device_count() > 0): + logger.info(f"device={device} but CUDA device is not available, using CPU instead.") + device = torch.device("cpu") + if not has_tqdm: + warnings.warn("tqdm is not installed. not displaying the caching progress.") + + for batch_data in tqdm(dataloader) if (has_tqdm and rank == 0) else dataloader: + batch_data = batch_data[0] + try: + batch_data[self.image_key] = batch_data[self.image_key].to(device) + _label_argmax = False + if self.label_key is not None: + label = batch_data[self.label_key] + label = torch.argmax(label, dim=0) if label.shape[0] > 1 else label[0] + _label_argmax = True # track if label is argmaxed + batch_data[self.label_key] = label.to(device) + d = summarizer(batch_data) + except BaseException as err: + if "image_meta_dict" in batch_data.keys(): + filename = batch_data["image_meta_dict"][ImageMetaKey.FILENAME_OR_OBJ] + else: + filename = batch_data[self.image_key].meta[ImageMetaKey.FILENAME_OR_OBJ] + logger.info(f"Unable to process data {filename} on {device}. {err}") + if self.device.type == "cuda": + logger.info("DataAnalyzer `device` set to GPU execution hit an exception. Falling back to `cpu`.") + try: + batch_data[self.image_key] = batch_data[self.image_key].to("cpu") + if self.label_key is not None: + label = batch_data[self.label_key] + if not _label_argmax: + label = torch.argmax(label, dim=0) if label.shape[0] > 1 else label[0] + batch_data[self.label_key] = label.to("cpu") + d = summarizer(batch_data) + except BaseException as err: + logger.info(f"Unable to process data {filename} on {device}. {err}") + continue + else: + continue + + stats_by_cases = { + DataStatsKeys.BY_CASE_IMAGE_PATH: d[DataStatsKeys.BY_CASE_IMAGE_PATH], + DataStatsKeys.BY_CASE_LABEL_PATH: d[DataStatsKeys.BY_CASE_LABEL_PATH], + } + if not self.histogram_only: + stats_by_cases[DataStatsKeys.IMAGE_STATS] = d[DataStatsKeys.IMAGE_STATS] + if self.hist_bins != 0: + stats_by_cases[DataStatsKeys.IMAGE_HISTOGRAM] = d[DataStatsKeys.IMAGE_HISTOGRAM] + + if self.label_key is not None: + stats_by_cases.update( + { + DataStatsKeys.FG_IMAGE_STATS: d[DataStatsKeys.FG_IMAGE_STATS], + DataStatsKeys.LABEL_STATS: d[DataStatsKeys.LABEL_STATS], + } + ) + result_bycase[DataStatsKeys.BY_CASE].append(stats_by_cases) + if manager_list is None: + return result_bycase + else: + manager_list.append(result_bycase) diff --git a/MONAI/source/monai/apps/auto3dseg/ensemble_builder.py b/MONAI/source/monai/apps/auto3dseg/ensemble_builder.py new file mode 100644 index 0000000000000000000000000000000000000000..e574baf7c88ea8ce65a618488958af67d02fe320 --- /dev/null +++ b/MONAI/source/monai/apps/auto3dseg/ensemble_builder.py @@ -0,0 +1,660 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import os +from abc import ABC, abstractmethod +from collections.abc import Mapping, Sequence +from copy import deepcopy +from typing import Any, cast +from warnings import warn + +import numpy as np +import torch +import torch.distributed as dist + +from monai.apps.auto3dseg.bundle_gen import BundleAlgo +from monai.apps.auto3dseg.utils import get_name_from_algo_id, import_bundle_algo_history +from monai.apps.utils import get_logger +from monai.auto3dseg import concat_val_to_np +from monai.auto3dseg.utils import ( + _prepare_cmd_bcprun, + _prepare_cmd_torchrun, + _run_cmd_bcprun, + _run_cmd_torchrun, + datafold_read, +) +from monai.bundle import ConfigParser +from monai.data import partition_dataset +from monai.transforms import MeanEnsemble, SaveImage, VoteEnsemble +from monai.utils import RankFilter +from monai.utils.enums import AlgoKeys +from monai.utils.misc import check_kwargs_exist_in_class_init, prob2class +from monai.utils.module import look_up_option, optional_import + +tqdm, has_tqdm = optional_import("tqdm", name="tqdm") + +logger = get_logger(module_name=__name__) + + +class AlgoEnsemble(ABC): + """ + The base class of Ensemble methods + """ + + def __init__(self): + self.algos = [] + self.mode = "mean" + self.infer_files = [] + self.algo_ensemble = [] + + def set_algos(self, infer_algos): + """ + Register model in the ensemble + """ + self.algos = deepcopy(infer_algos) + + def get_algo(self, identifier): + """ + Get a model by identifier. + + Args: + identifier: the name of the bundleAlgo + """ + for algo in self.algos: + if identifier == algo[AlgoKeys.ID]: + return algo + + def get_algo_ensemble(self): + """ + Get the algo ensemble after ranking or a empty list if ranking was not started. + + Returns: + A list of Algo + """ + return self.algo_ensemble + + def set_infer_files(self, dataroot: str, data_list_or_path: str | list, data_key: str = "testing") -> None: + """ + Set the files to perform model inference. + + Args: + dataroot: the path of the files + data_list_or_path: the data source file path + """ + + self.infer_files = [] + + if isinstance(data_list_or_path, list): + self.infer_files = data_list_or_path + elif isinstance(data_list_or_path, str): + datalist = ConfigParser.load_config_file(data_list_or_path) + if data_key in datalist: + self.infer_files, _ = datafold_read(datalist=datalist, basedir=dataroot, fold=-1, key=data_key) + elif not hasattr(self, "rank") or self.rank == 0: + logger.info(f"Datalist file has no testing key - {data_key}. No data for inference is specified") + + else: + raise ValueError("Unsupported parameter type") + + def ensemble_pred(self, preds, sigmoid=False): + """ + ensemble the results using either "mean" or "vote" method + + Args: + preds: a list of probability prediction in Tensor-Like format. + sigmoid: use the sigmoid function to threshold probability one-hot map, + otherwise argmax is used. Defaults to False + + Returns: + a tensor which is the ensembled prediction. + """ + + if any(not p.is_cuda for p in preds): + preds = [p.cpu() for p in preds] # ensure CPU if at least one is on CPU + + if self.mode == "mean": + prob = MeanEnsemble()(preds) + return prob2class(cast(torch.Tensor, prob), dim=0, keepdim=True, sigmoid=sigmoid) + elif self.mode == "vote": + classes = [prob2class(p, dim=0, keepdim=True, sigmoid=sigmoid) for p in preds] + if sigmoid: + return VoteEnsemble()(classes) # do not specify num_classes for one-hot encoding + else: + return VoteEnsemble(num_classes=preds[0].shape[0])(classes) + + def _apply_algo_specific_param(self, algo_spec_param: dict, param: dict, algo_name: str) -> dict: + """ + Apply the model-specific params to the prediction params based on the name of the Algo. + + Args: + algo_spec_param: a dict that has structure of {"": ""}. + param: the prediction params to override. + algo_name: name of the Algo + + Returns: + param after being updated with the model-specific param + """ + _param_to_override = deepcopy(algo_spec_param) + _param = deepcopy(param) + for k, v in _param_to_override.items(): + if k.lower() == algo_name.lower(): + _param.update(v) + return _param + + def __call__(self, pred_param: dict | None = None) -> list: + """ + Use the ensembled model to predict result. + + Args: + pred_param: prediction parameter dictionary. The key has two groups: the first one will be consumed + in this function, and the second group will be passed to the `InferClass` to override the + parameters of the class functions. + The first group contains: + + - ``"infer_files"``: file paths to the images to read in a list. + - ``"files_slices"``: a value type of `slice`. The files_slices will slice the ``"infer_files"`` and + only make prediction on the infer_files[file_slices]. + - ``"mode"``: ensemble mode. Currently "mean" and "vote" (majority voting) schemes are supported. + - ``"image_save_func"``: a dictionary used to instantiate the ``SaveImage`` transform. When specified, + the ensemble prediction will save the prediction files, instead of keeping the files in the memory. + Example: `{"_target_": "SaveImage", "output_dir": "./"}` + - ``"sigmoid"``: use the sigmoid function (e.g. x > 0.5) to convert the prediction probability map + to the label class prediction, otherwise argmax(x) is used. + - ``"algo_spec_params"``: a dictionary to add pred_params that are specific to a model. + The dict has a format of {"": ""}. + + The parameters in the second group is defined in the ``config`` of each Algo templates. Please check: + https://github.com/Project-MONAI/research-contributions/tree/main/auto3dseg/algorithm_templates + + Returns: + A list of tensors or file paths, depending on whether ``"image_save_func"`` is set. + """ + param = {} if pred_param is None else deepcopy(pred_param) + files = self.infer_files + + if "infer_files" in param: + files = param.pop("infer_files") + + if "files_slices" in param: + slices = param.pop("files_slices") + files = files[slices] + + if "mode" in param: + mode = param.pop("mode") + self.mode = look_up_option(mode, supported=["mean", "vote"]) + + sigmoid = param.pop("sigmoid", False) + + if "image_save_func" in param: + img_saver = ConfigParser(param["image_save_func"]).get_parsed_content() + + algo_spec_params = param.pop("algo_spec_params", {}) + + outputs = [] + for _, file in ( + enumerate(tqdm(files, desc="Ensembling (rank 0)...")) + if has_tqdm and pred_param and pred_param.get("rank", 0) == 0 + else enumerate(files) + ): + preds = [] + for algo in self.algo_ensemble: + infer_algo_name = get_name_from_algo_id(algo[AlgoKeys.ID]) + infer_instance = algo[AlgoKeys.ALGO] + _param = self._apply_algo_specific_param(algo_spec_params, param, infer_algo_name) + pred = infer_instance.predict(predict_files=[file], predict_params=_param) + preds.append(pred[0]) + if "image_save_func" in param: + try: + ensemble_preds = self.ensemble_pred(preds, sigmoid=sigmoid) + except BaseException: + ensemble_preds = self.ensemble_pred([_.to("cpu") for _ in preds], sigmoid=sigmoid) + res = img_saver(ensemble_preds) + # res is the path to the saved results + if hasattr(res, "meta") and "saved_to" in res.meta.keys(): + res = res.meta["saved_to"] + else: + warn("Image save path not returned.") + res = None + else: + warn("Prediction returned in list instead of disk, provide image_save_func to avoid out of memory.") + res = self.ensemble_pred(preds, sigmoid=sigmoid) + outputs.append(res) + return outputs + + @abstractmethod + def collect_algos(self, *args, **kwargs): + raise NotImplementedError + + +class AlgoEnsembleBestN(AlgoEnsemble): + """ + Ensemble method that select N model out of all using the models' best_metric scores + + Args: + n_best: number of models to pick for ensemble (N). + """ + + def __init__(self, n_best: int = 5): + super().__init__() + self.n_best = n_best + + def sort_score(self): + """ + Sort the best_metrics + """ + scores = concat_val_to_np(self.algos, [AlgoKeys.SCORE]) + return np.argsort(scores).tolist() + + def collect_algos(self, n_best: int = -1) -> None: + """ + Rank the algos by finding the top N (n_best) validation scores. + """ + + if n_best <= 0: + n_best = self.n_best + + ranks = self.sort_score() + if len(ranks) < n_best: + warn(f"Found {len(ranks)} available algos (pre-defined n_best={n_best}). All {len(ranks)} will be used.") + n_best = len(ranks) + + # get the ranks for which the indices are lower than N-n_best + indices = [r for (i, r) in enumerate(ranks) if i < (len(ranks) - n_best)] + + # remove the found indices + indices = sorted(indices, reverse=True) + + self.algo_ensemble = deepcopy(self.algos) + for idx in indices: + if idx < len(self.algo_ensemble): + self.algo_ensemble.pop(idx) + + +class AlgoEnsembleBestByFold(AlgoEnsemble): + """ + Ensemble method that select the best models that are the tops in each fold. + + Args: + n_fold: number of cross-validation folds used in training + """ + + def __init__(self, n_fold: int = 5): + super().__init__() + self.n_fold = n_fold + + def collect_algos(self) -> None: + """ + Rank the algos by finding the best model in each cross-validation fold + """ + + self.algo_ensemble = [] + for f_idx in range(self.n_fold): + best_score = -1.0 + best_model: BundleAlgo | None = None + for algo in self.algos: + # algorithm folder: {net}_{fold_index}_{other} + identifier = algo[AlgoKeys.ID].split("_")[1] + try: + algo_id = int(identifier) + except ValueError as err: + raise ValueError(f"model identifier {identifier} is not number.") from err + if algo_id == f_idx and algo[AlgoKeys.SCORE] > best_score: + best_model = algo + best_score = algo[AlgoKeys.SCORE] + self.algo_ensemble.append(best_model) + + +class AlgoEnsembleBuilder: + """ + Build ensemble workflow from configs and arguments. + + Args: + history: a collection of trained bundleAlgo algorithms. + data_src_cfg_name: filename of the data source. + + Examples: + + .. code-block:: python + + builder = AlgoEnsembleBuilder(history, data_src_cfg) + builder.set_ensemble_method(BundleAlgoEnsembleBestN(3)) + ensemble = builder.get_ensemble() + + """ + + def __init__(self, history: Sequence[dict[str, Any]], data_src_cfg_name: str | None = None): + self.infer_algos: list[dict[AlgoKeys, Any]] = [] + self.ensemble: AlgoEnsemble + self.data_src_cfg = ConfigParser(globals=False) + + if data_src_cfg_name is not None and os.path.exists(str(data_src_cfg_name)): + self.data_src_cfg.read_config(data_src_cfg_name) + + for algo_dict in history: + # load inference_config_paths + + name = algo_dict[AlgoKeys.ID] + gen_algo = algo_dict[AlgoKeys.ALGO] + + best_metric = gen_algo.get_score() + algo_path = gen_algo.output_path + infer_path = os.path.join(algo_path, "scripts", "infer.py") + + if not os.path.isdir(algo_path): + warn(f"{gen_algo.output_path} is not a directory. Please check the path.") + + if not os.path.isfile(infer_path): + warn(f"{infer_path} is not found. Please check the path.") + + self.add_inferer(name, gen_algo, best_metric) + + def add_inferer(self, identifier: str, gen_algo: BundleAlgo, best_metric: float | None = None) -> None: + """ + Add model inferer to the builder. + + Args: + identifier: name of the bundleAlgo. + gen_algo: a trained BundleAlgo model object. + best_metric: the best metric in validation of the trained model. + """ + + if best_metric is None: + raise ValueError("Feature to re-validate is to be implemented") + + algo = {AlgoKeys.ID: identifier, AlgoKeys.ALGO: gen_algo, AlgoKeys.SCORE: best_metric} + self.infer_algos.append(algo) + + def set_ensemble_method(self, ensemble: AlgoEnsemble, *args: Any, **kwargs: Any) -> None: + """ + Set the ensemble method. + + Args: + ensemble: the AlgoEnsemble to build. + """ + + ensemble.set_algos(self.infer_algos) + ensemble.collect_algos(*args, **kwargs) + ensemble.set_infer_files(self.data_src_cfg["dataroot"], self.data_src_cfg["datalist"]) + + self.ensemble = ensemble + + def get_ensemble(self): + """Get the ensemble""" + + return self.ensemble + + +class EnsembleRunner: + """ + The Runner for ensembler. It ensembles predictions and saves them to the disk with a support of using multi-GPU. + + Args: + data_src_cfg_name: filename of the data source. + work_dir: working directory to save the intermediate and final results. Default is `./work_dir`. + num_fold: number of fold. Default is 5. + ensemble_method_name: method to ensemble predictions from different model. Default is AlgoEnsembleBestByFold. + Supported methods: ["AlgoEnsembleBestN", "AlgoEnsembleBestByFold"]. + mgpu: if using multi-gpu. Default is True. + kwargs: additional image writing, ensembling parameters and prediction parameters for the ensemble inference. + - for image saving, please check the supported parameters in SaveImage transform. + - for prediction parameters, please check the supported parameters in the ``AlgoEnsemble`` callables. + - for ensemble parameters, please check the documentation of the selected AlgoEnsemble callable. + + Example: + + .. code-block:: python + + ensemble_runner = EnsembleRunner(data_src_cfg_name, + work_dir, + ensemble_method_name, + mgpu=device_setting['n_devices']>1, + **kwargs, + **pred_params) + ensemble_runner.run(device_setting) + + """ + + def __init__( + self, + data_src_cfg_name: str, + work_dir: str = "./work_dir", + num_fold: int = 5, + ensemble_method_name: str = "AlgoEnsembleBestByFold", + mgpu: bool = True, + **kwargs: Any, + ) -> None: + self.data_src_cfg_name = data_src_cfg_name + self.work_dir = work_dir + self.num_fold = num_fold + self.ensemble_method_name = ensemble_method_name + self.mgpu = mgpu + self.kwargs = deepcopy(kwargs) + self.rank = 0 + self.world_size = 1 + self.device_setting: dict[str, int | str] = { + "CUDA_VISIBLE_DEVICES": ",".join([str(x) for x in range(torch.cuda.device_count())]), + "n_devices": torch.cuda.device_count(), + "NUM_NODES": int(os.environ.get("NUM_NODES", 1)), + "MN_START_METHOD": os.environ.get("MN_START_METHOD", "bcprun"), + "CMD_PREFIX": os.environ.get("CMD_PREFIX", ""), + } + + def set_ensemble_method(self, ensemble_method_name: str = "AlgoEnsembleBestByFold", **kwargs: Any) -> None: + """ + Set the bundle ensemble method + + Args: + ensemble_method_name: the name of the ensemble method. Only two methods are supported "AlgoEnsembleBestN" + and "AlgoEnsembleBestByFold". + kwargs: the keyword arguments used to define the ensemble method. Currently only ``n_best`` for + ``AlgoEnsembleBestN`` is supported. + + """ + self.ensemble_method_name = look_up_option( + ensemble_method_name, supported=["AlgoEnsembleBestN", "AlgoEnsembleBestByFold"] + ) + if self.ensemble_method_name == "AlgoEnsembleBestN": + n_best = kwargs.pop("n_best", 2) + self.ensemble_method = AlgoEnsembleBestN(n_best=n_best) + elif self.ensemble_method_name == "AlgoEnsembleBestByFold": + self.ensemble_method = AlgoEnsembleBestByFold(n_fold=self.num_fold) # type: ignore + else: + raise NotImplementedError(f"Ensemble method {self.ensemble_method_name} is not implemented.") + + def _pop_kwargs_to_get_image_save_transform(self, **kwargs): + """ + Pop the kwargs used to define ImageSave class for the ensemble output. + + Args: + kwargs: image writing parameters for the ensemble inference. The kwargs format follows SaveImage + transform. For more information, check https://monai.readthedocs.io/en/stable/transforms.html#saveimage . + + Returns: + save_image: a dictionary that can be used to instantiate a SaveImage class in ConfigParser. + """ + + output_dir = kwargs.pop("output_dir", None) + + if output_dir is None: + output_dir = os.path.join(self.work_dir, "ensemble_output") + logger.info(f"The output_dir is not specified. {output_dir} will be used to save ensemble predictions.") + + if not os.path.isdir(output_dir): + os.makedirs(output_dir, exist_ok=True) + logger.info(f"Directory {output_dir} is created to save ensemble predictions") + + input_yaml = ConfigParser.load_config_file(self.data_src_cfg_name) + data_root_dir = input_yaml.get("dataroot", "") + + save_image = { + "_target_": "SaveImage", + "output_dir": output_dir, + "output_postfix": kwargs.pop("output_postfix", "ensemble"), + "output_dtype": kwargs.pop("output_dtype", "$np.uint8"), + "resample": kwargs.pop("resample", False), + "print_log": False, + "savepath_in_metadict": True, + "data_root_dir": kwargs.pop("data_root_dir", data_root_dir), + "separate_folder": kwargs.pop("separate_folder", False), + } + + are_all_args_save_image, extra_args = check_kwargs_exist_in_class_init(SaveImage, kwargs) + if are_all_args_save_image: + save_image.update(kwargs) + else: + # kwargs has extra values for other purposes, for example, pred_params + for args in list(kwargs): + if args not in extra_args: + save_image.update({args: kwargs.pop(args)}) + + return save_image + + def set_image_save_transform(self, **kwargs: Any) -> None: + """ + Set the ensemble output transform. + + Args: + kwargs: image writing parameters for the ensemble inference. The kwargs format follows SaveImage + transform. For more information, check https://monai.readthedocs.io/en/stable/transforms.html#saveimage . + + """ + are_all_args_present, extra_args = check_kwargs_exist_in_class_init(SaveImage, kwargs) + if are_all_args_present: + self.kwargs.update(kwargs) + else: + raise ValueError( + f"{extra_args} are not supported in monai.transforms.SaveImage," + "Check https://monai.readthedocs.io/en/stable/transforms.html#saveimage for more information." + ) + + def set_num_fold(self, num_fold: int = 5) -> None: + """ + Set the number of cross validation folds for all algos. + + Args: + num_fold: a positive integer to define the number of folds. + """ + + if num_fold <= 0: + raise ValueError(f"num_fold is expected to be an integer greater than zero. Now it gets {num_fold}") + self.num_fold = num_fold + + def ensemble(self): + if self.mgpu: # torch.cuda.device_count() is not used because env is not set by autorunner + # init multiprocessing and update infer_files + dist.init_process_group(backend="nccl", init_method="env://") + self.world_size = dist.get_world_size() + self.rank = dist.get_rank() + logger.addFilter(RankFilter()) + # set params after init_process_group to know the rank + self.set_num_fold(num_fold=self.num_fold) + self.set_ensemble_method(self.ensemble_method_name, **self.kwargs) + # self.kwargs needs to pop out args for set_image_save_transform + save_image = self._pop_kwargs_to_get_image_save_transform(**self.kwargs) + + history = import_bundle_algo_history(self.work_dir, only_trained=False) + history_untrained = [h for h in history if not h[AlgoKeys.IS_TRAINED]] + if history_untrained: + logger.warning( + f"Ensembling step will skip {[h[AlgoKeys.ID] for h in history_untrained]} untrained algos." + "Generally it means these algos did not complete training." + ) + history = [h for h in history if h[AlgoKeys.IS_TRAINED]] + if len(history) == 0: + raise ValueError( + f"Could not find the trained results in {self.work_dir}. " + "Possibly the required training step was not completed." + ) + + builder = AlgoEnsembleBuilder(history, self.data_src_cfg_name) + builder.set_ensemble_method(self.ensemble_method) + self.ensembler = builder.get_ensemble() + infer_files = self.ensembler.infer_files + if len(infer_files) < self.world_size: + if len(infer_files) == 0: + logger.info("No testing files for inference is provided. Ensembler ending.") + return + infer_files = [infer_files[self.rank]] if self.rank < len(infer_files) else [] + else: + infer_files = partition_dataset( + data=infer_files, shuffle=False, num_partitions=self.world_size, even_divisible=False + )[self.rank] + + # TO DO: Add some function in ensembler for infer_files update? + self.ensembler.infer_files = infer_files + # add rank to pred_params + self.kwargs["rank"] = self.rank + self.kwargs["image_save_func"] = save_image + logger.info("Auto3Dseg picked the following networks to ensemble:") + for algo in self.ensembler.get_algo_ensemble(): + logger.info(algo[AlgoKeys.ID]) + output_dir = save_image["output_dir"] + logger.info(f"Auto3Dseg ensemble prediction outputs will be saved in {output_dir}.") + self.ensembler(pred_param=self.kwargs) + + if self.mgpu: + dist.destroy_process_group() + + def run(self, device_setting: dict | None = None) -> None: + """ + Load the run function in the training script of each model. Training parameter is predefined by the + algo_config.yaml file, which is pre-filled by the fill_template_config function in the same instance. + + Args: + device_setting: device related settings, should follow the device_setting in auto_runner.set_device_info. + 'CUDA_VISIBLE_DEVICES' should be a string e.g. '0,1,2,3' + """ + # device_setting set default value and sanity check, in case device_setting not from autorunner + if device_setting is not None: + self.device_setting.update(device_setting) + self.device_setting["n_devices"] = len(str(self.device_setting["CUDA_VISIBLE_DEVICES"]).split(",")) + self._create_cmd() + + def _create_cmd(self) -> None: + if int(self.device_setting["NUM_NODES"]) <= 1 and int(self.device_setting["n_devices"]) <= 1: + # if single GPU + logger.info("Ensembling using single GPU!") + self.ensemble() + return + + # define base cmd for subprocess + base_cmd = f"monai.apps.auto3dseg EnsembleRunner ensemble \ + --data_src_cfg_name {self.data_src_cfg_name} \ + --work_dir {self.work_dir} \ + --num_fold {self.num_fold} \ + --ensemble_method_name {self.ensemble_method_name} \ + --mgpu True" + + if self.kwargs and isinstance(self.kwargs, Mapping): + for k, v in self.kwargs.items(): + base_cmd += f" --{k}={v}" + # define env for subprocess + ps_environ = os.environ.copy() + ps_environ["CUDA_VISIBLE_DEVICES"] = str(self.device_setting["CUDA_VISIBLE_DEVICES"]) + if int(self.device_setting["NUM_NODES"]) > 1: + if self.device_setting["MN_START_METHOD"] != "bcprun": + raise NotImplementedError( + f"{self.device_setting['MN_START_METHOD']} is not supported yet. " + "Try modify EnsembleRunner._create_cmd for your cluster." + ) + logger.info(f"Ensembling on {self.device_setting['NUM_NODES']} nodes!") + cmd = _prepare_cmd_bcprun("-m " + base_cmd, cmd_prefix=f"{self.device_setting['CMD_PREFIX']}") + _run_cmd_bcprun(cmd, n=self.device_setting["NUM_NODES"], p=self.device_setting["n_devices"]) + + else: + logger.info(f"Ensembling using {self.device_setting['n_devices']} GPU!") + cmd = _prepare_cmd_torchrun("-m " + base_cmd) + _run_cmd_torchrun( + cmd, nnodes=1, nproc_per_node=self.device_setting["n_devices"], env=ps_environ, check=True + ) + return diff --git a/MONAI/source/monai/apps/auto3dseg/hpo_gen.py b/MONAI/source/monai/apps/auto3dseg/hpo_gen.py new file mode 100644 index 0000000000000000000000000000000000000000..ed6d903897b023dfce9a256643bf028de7200bbf --- /dev/null +++ b/MONAI/source/monai/apps/auto3dseg/hpo_gen.py @@ -0,0 +1,401 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import os +from abc import abstractmethod +from copy import deepcopy +from typing import Any, cast +from warnings import warn + +from monai.apps.auto3dseg.bundle_gen import BundleAlgo +from monai.apps.utils import get_logger +from monai.auto3dseg import Algo, AlgoGen, algo_from_pickle, algo_to_pickle +from monai.bundle.config_parser import ConfigParser +from monai.config import PathLike +from monai.utils import optional_import +from monai.utils.enums import AlgoKeys + +nni, has_nni = optional_import("nni") +optuna, has_optuna = optional_import("optuna") +logger = get_logger(module_name=__name__) + +__all__ = ["HPOGen", "NNIGen", "OptunaGen"] + + +class HPOGen(AlgoGen): + """ + The base class for hyperparameter optimization (HPO) interfaces to generate algos in the Auto3Dseg pipeline. + The auto-generated algos are saved at their ``output_path`` on the disk. The files in the ``output_path`` + may contain scripts that define the algo, configuration files, and pickle files that save the internal states + of the algo before/after the training. Compared to the BundleGen class, HPOGen generates Algo on-the-fly, so + training and algo generation may be executed alternatively and take a long time to finish the generation process. + + """ + + @abstractmethod + def get_hyperparameters(self): + """Get the hyperparameter from HPO.""" + raise NotImplementedError + + @abstractmethod + def update_params(self, *args, **kwargs): + """Update Algo parameters according to the hyperparameters to be evaluated.""" + raise NotImplementedError + + @abstractmethod + def set_score(self, *args, **kwargs): + """Report the evaluated results to HPO.""" + raise NotImplementedError + + @abstractmethod + def run_algo(self, *args, **kwargs): + """Interface for launch the training given the fetched hyperparameters.""" + raise NotImplementedError + + +class NNIGen(HPOGen): + """ + Generate algorithms for the NNI to automate hyperparameter tuning. The module has two major interfaces: + ``__init__`` which prints out how to set up the NNI, and a trialCommand function ``run_algo`` for the NNI library to + start the trial of the algo. More about trialCommand function can be found in ``trail code`` section in NNI webpage + https://nni.readthedocs.io/en/latest/tutorials/hpo_quickstart_pytorch/main.html . + + Args: + algo: an Algo object (e.g. BundleAlgo) with defined methods: ``get_output_path`` and train + and supports saving to and loading from pickle files via ``algo_from_pickle`` and ``algo_to_pickle``. + params: a set of parameter to override the algo if override is supported by Algo subclass. + + Examples:: + + The experiment will keep generating new folders to save the model checkpoints, scripts, and configs if available. + ├── algorithm_templates + │ └── unet + ├── unet_0 + │ ├── algo_object.pkl + │ ├── configs + │ └── scripts + ├── unet_0_learning_rate_0.01 + │ ├── algo_object.pkl + │ ├── configs + │ ├── model_fold0 + │ └── scripts + └── unet_0_learning_rate_0.1 + ├── algo_object.pkl + ├── configs + ├── model_fold0 + └── scripts + + .. code-block:: python + # Bundle Algorithms are already generated by BundleGen in work_dir + import_bundle_algo_history(work_dir, only_trained=False) + algo_dict = self.history[0] # pick the first algorithm + algo_name = algo_dict[AlgoKeys.ID] + onealgo = algo_dict[AlgoKeys.ALGO] + nni_gen = NNIGen(algo=onealgo) + nni_gen.print_bundle_algo_instruction() + + Notes: + The NNIGen will prepare the algorithms in a folder and suggest a command to replace trialCommand in the experiment + config. However, NNIGen will not trigger NNI. User needs to write their NNI experiment configs, and then run the + NNI command manually. + """ + + def __init__(self, algo: Algo | None = None, params: dict | None = None): + self.algo: Algo + self.hint = "" + self.obj_filename = "" + + if algo is not None: + if isinstance(algo, BundleAlgo): + if params is None: + self.algo = algo + else: + self.algo = deepcopy(algo) + name = os.path.basename(algo.get_output_path()) + "_override" + output_folder = os.path.dirname(algo.get_output_path()) + + params.update({"fill_with_datastats": False}) # just copy, not using datastats to fill + self.algo.export_to_disk(output_folder, name, **params) + else: + self.algo = algo + + self.obj_filename = algo_to_pickle(self.algo, template_path=self.algo.template_path) + + def get_obj_filename(self): + """Return the filename of the dumped pickle algo object.""" + return self.obj_filename + + def print_bundle_algo_instruction(self): + """ + Print how to write the trial commands for Bundle Algo. + """ + hint = "python -m monai.apps.auto3dseg NNIGen run_algo " + logger.info("=" * 140) + logger.info("If NNI will run in your local env: ") + logger.info("1. Add the following line to the trialCommand in your NNI config: ") + logger.info(f"{hint} {self.obj_filename} {{result_dir}}") + logger.info("-" * 140) + logger.info("If NNI will run in a remote env: ") + logger.info( + f"1. Copy the algorithm_templates folder {cast(BundleAlgo, self.algo).template_path} " + f"to remote {{remote_algorithm_templates_dir}}" + ) + logger.info(f"2. Copy the older {self.algo.get_output_path()} to the remote machine {{remote_algo_dir}}") + logger.info("Then add the following line to the trialCommand in your NNI config: ") + logger.info(f"{hint} {{remote_algo_dir}} {{result_dir}} {{remote_algorithm_templates_dir}}") + logger.info("=" * 140) + + def get_hyperparameters(self): + """ + Get parameter for next round of training from NNI server. + """ + if has_nni: + return nni.get_next_parameter() + warn("NNI is not detected. The code will continue to run without NNI.") + return {} + + def update_params(self, params: dict) -> None: + """ + Translate the parameter from monai bundle to meet NNI requirements. + + Args: + params: a dict of parameters. + """ + self.params = params + + def get_task_id(self): + """ + Get the identifier of the current experiment. In the format of listing the searching parameter name and values + connected by underscore in the file name. + """ + return "".join(f"_{k}_{v}" for k, v in self.params.items()) or "_None" + + def generate(self, output_folder: str = ".") -> None: + """ + Generate the record for each Algo. If it is a BundleAlgo, it will generate the config files. + + Args: + output_folder: the directory nni will save the results to. + """ + task_id = self.get_task_id() + task_prefix = os.path.basename(self.algo.get_output_path()) + write_path = os.path.join(output_folder, task_prefix + task_id) + self.obj_filename = os.path.join(write_path, "algo_object.pkl") + + if isinstance(self.algo, BundleAlgo): + self.algo.export_to_disk( + output_folder, task_prefix + task_id, bundle_root=write_path, fill_with_datastats=False + ) + else: + ConfigParser.export_config_file(self.params, write_path) + logger.info(write_path) + + def set_score(self, acc): + """ + Report the acc to NNI server. + """ + if has_nni: + nni.report_final_result(acc) + else: + warn("NNI is not detected. The code will continue to run without NNI.") + + def run_algo(self, obj_filename: str, output_folder: str = ".", template_path: PathLike | None = None) -> None: + """ + The python interface for NNI to run. + + Args: + obj_filename: the pickle-exported Algo object. + output_folder: the root path of the algorithms templates. + template_path: the algorithm_template. It must contain algo.py in the follow path: + ``{algorithm_templates_dir}/{network}/scripts/algo.py`` + """ + if not os.path.isfile(obj_filename): + raise ValueError(f"{obj_filename} is not found") + + self.algo, algo_meta_data = algo_from_pickle(obj_filename, template_path=template_path) + + # step 1 sample hyperparams + params = self.get_hyperparameters() + # step 2 set the update params for the algo to run in the next trial + self.update_params(params) + # step 3 generate the folder to save checkpoints and train + self.generate(output_folder) + self.algo.train(self.params) + # step 4 report validation acc to controller + acc = self.algo.get_score() + algo_meta_data = {str(AlgoKeys.SCORE): acc} + + algo_to_pickle(self.algo, template_path=self.algo.template_path, **algo_meta_data) + self.set_score(acc) + + +class OptunaGen(HPOGen): + """ + Generate algorithms for the Optuna to automate hyperparameter tuning. Please refer to NNI and Optuna + (https://optuna.readthedocs.io/en/stable/) for more information. Optuna has different running scheme + compared to NNI. The hyperparameter samples come from a trial object (trial.suggest...) created by Optuna, + so OptunaGen needs to accept this trial object as input. Meanwhile, Optuna calls OptunaGen, + thus OptunaGen.__call__() should return the accuracy. Use functools.partial to wrap OptunaGen + for addition input arguments. + + Args: + algo: an Algo object (e.g. BundleAlgo). The object must at least define two methods: get_output_path and train + and supports saving to and loading from pickle files via ``algo_from_pickle`` and ``algo_to_pickle``. + params: a set of parameter to override the algo if override is supported by Algo subclass. + + Examples:: + + The experiment will keep generating new folders to save the model checkpoints, scripts, and configs if available. + ├── algorithm_templates + │ └── unet + ├── unet_0 + │ ├── algo_object.pkl + │ ├── configs + │ └── scripts + ├── unet_0_learning_rate_0.01 + │ ├── algo_object.pkl + │ ├── configs + │ ├── model_fold0 + │ └── scripts + └── unet_0_learning_rate_0.1 + ├── algo_object.pkl + ├── configs + ├── model_fold0 + └── scripts + + Notes: + Different from NNI and NNIGen, OptunaGen and Optuna can be ran within the Python process. + + """ + + def __init__(self, algo: Algo | None = None, params: dict | None = None) -> None: + self.algo: Algo + self.obj_filename = "" + + if algo is not None: + if isinstance(algo, BundleAlgo): + if params is None: + self.algo = algo + else: + self.algo = deepcopy(algo) + name = os.path.basename(algo.get_output_path()) + "_override" + output_folder = os.path.dirname(algo.get_output_path()) + + params.update({"fill_with_datastats": False}) # just copy, not using datastats to fill + self.algo.export_to_disk(output_folder, name, **params) + else: + self.algo = algo + + self.obj_filename = algo_to_pickle(self.algo, template_path=self.algo.template_path) + + def get_obj_filename(self): + """Return the dumped pickle object of algo.""" + return self.obj_filename + + def get_hyperparameters(self): + """ + Get parameter for next round of training from optuna trial object. + This function requires user rewrite during usage for different search space. + """ + if has_optuna: + logger.info("Please rewrite this code by creating a child class") + return {"learning_rate": self.trial.suggest_float("learning_rate", 0.0001, 0.1)} + else: + warn("Optuna is not detected. The code will continue to run without Optuna.") + return {} + + def set_score(self, acc): + """Set the accuracy score""" + self.acc = acc + + def set_trial(self, trial): + """Set the Optuna trial""" + self.trial = trial + + def __call__( + self, trial: Any, obj_filename: str, output_folder: str = ".", template_path: PathLike | None = None + ) -> Any: + """ + Callable that Optuna will use to optimize the hyper-parameters + + Args: + obj_filename: the pickle-exported Algo object. + output_folder: the root path of the algorithms templates. + template_path: the algorithm_template. It must contain algo.py in the follow path: + ``{algorithm_templates_dir}/{network}/scripts/algo.py`` + """ + self.set_trial(trial) + self.run_algo(obj_filename, output_folder, template_path) + return self.acc + + def update_params(self, params: dict) -> None: + """ + Translate the parameter from monai bundle. + + Args: + params: a dict of parameters. + """ + self.params = params + + def get_task_id(self): + """ + Get the identifier of the current experiment. In the format of listing the searching parameter name and values + connected by underscore in the file name. + """ + return "".join(f"_{k}_{v}" for k, v in self.params.items()) or "_None" + + def generate(self, output_folder: str = ".") -> None: + """ + Generate the record for each Algo. If it is a BundleAlgo, it will generate the config files. + + Args: + output_folder: the directory nni will save the results to. + """ + task_id = self.get_task_id() + task_prefix = os.path.basename(self.algo.get_output_path()) + write_path = os.path.join(output_folder, task_prefix + task_id) + self.obj_filename = os.path.join(write_path, "algo_object.pkl") + + if isinstance(self.algo, BundleAlgo): + self.algo.export_to_disk(output_folder, task_prefix + task_id, fill_with_datastats=False) + else: + ConfigParser.export_config_file(self.params, write_path) + logger.info(write_path) + + def run_algo(self, obj_filename: str, output_folder: str = ".", template_path: PathLike | None = None) -> None: + """ + The python interface for NNI to run. + + Args: + obj_filename: the pickle-exported Algo object. + output_folder: the root path of the algorithms templates. + template_path: the algorithm_template. It must contain algo.py in the follow path: + ``{algorithm_templates_dir}/{network}/scripts/algo.py`` + """ + if not os.path.isfile(obj_filename): + raise ValueError(f"{obj_filename} is not found") + + self.algo, algo_meta_data = algo_from_pickle(obj_filename, template_path=template_path) + + # step 1 sample hyperparams + params = self.get_hyperparameters() + # step 2 set the update params for the algo to run in the next trial + self.update_params(params) + # step 3 generate the folder to save checkpoints and train + self.generate(output_folder) + self.algo.train(self.params) + # step 4 report validation acc to controller + acc = self.algo.get_score() + algo_meta_data = {str(AlgoKeys.SCORE): acc} + algo_to_pickle(self.algo, template_path=self.algo.template_path, **algo_meta_data) + self.set_score(acc) diff --git a/MONAI/source/monai/apps/auto3dseg/transforms.py b/MONAI/source/monai/apps/auto3dseg/transforms.py new file mode 100644 index 0000000000000000000000000000000000000000..736895b7327e1acba6c77e20a27b0c5a667de22a --- /dev/null +++ b/MONAI/source/monai/apps/auto3dseg/transforms.py @@ -0,0 +1,82 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import warnings +from collections.abc import Hashable, Mapping + +import numpy as np +import torch + +from monai.config import KeysCollection +from monai.transforms import MapTransform +from monai.utils.misc import ImageMetaKey + + +class EnsureSameShaped(MapTransform): + """ + Checks if segmentation label images (in keys) have the same spatial shape as the main image (in source_key), + and raise an error if the shapes are significantly different. + If the shapes are only slightly different (within an allowed_shape_difference in each dim), then resize the label using + nearest interpolation. This transform is designed to correct datasets with slight label shape mismatches. + Generally image and segmentation label must have the same spatial shape, however some public datasets are having slight + shape mismatches, which will cause potential crashes when calculating loss or metric functions. + """ + + def __init__( + self, + keys: KeysCollection = "label", + allow_missing_keys: bool = False, + source_key: str = "image", + allowed_shape_difference: int = 5, + warn: bool = True, + ) -> None: + """ + Args: + keys: keys of the corresponding items to be compared to the source_key item shape. + allow_missing_keys: do not raise exception if key is missing. + source_key: key of the item with the reference shape. + allowed_shape_difference: raises error if shapes are different more than this value in any dimension, + otherwise corrects for the shape mismatch using nearest interpolation. + warn: if `True` prints a warning if the label image is resized + + + """ + super().__init__(keys=keys, allow_missing_keys=allow_missing_keys) + self.source_key = source_key + self.allowed_shape_difference = allowed_shape_difference + self.warn = warn + + def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> dict[Hashable, torch.Tensor]: + d = dict(data) + image_shape = d[self.source_key].shape[1:] + for key in self.key_iterator(d): + label_shape = d[key].shape[1:] + if label_shape != image_shape: + filename = "" + if hasattr(d[key], "meta") and isinstance(d[key].meta, Mapping): # type: ignore[attr-defined] + filename = d[key].meta.get(ImageMetaKey.FILENAME_OR_OBJ) # type: ignore[attr-defined] + + if np.allclose(list(label_shape), list(image_shape), atol=self.allowed_shape_difference): + if self.warn: + warnings.warn( + f"The {key} with shape {label_shape} was resized to match the source shape {image_shape}" + f", the metadata was not updated {filename}." + ) + d[key] = torch.nn.functional.interpolate( + input=d[key].unsqueeze(0), size=image_shape, mode="nearest-exact" + ).squeeze(0) + else: + raise ValueError( + f"The {key} shape {label_shape} is different from the source shape {image_shape} {filename}." + ) + return d diff --git a/MONAI/source/monai/apps/auto3dseg/utils.py b/MONAI/source/monai/apps/auto3dseg/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..64e1d2ea2a30c9bfdcedf286886011b48b5a03fb --- /dev/null +++ b/MONAI/source/monai/apps/auto3dseg/utils.py @@ -0,0 +1,90 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import os + +from monai.apps.auto3dseg.bundle_gen import BundleAlgo +from monai.auto3dseg import algo_from_pickle, algo_to_pickle +from monai.utils.enums import AlgoKeys + +__all__ = ["import_bundle_algo_history", "export_bundle_algo_history", "get_name_from_algo_id"] + + +def import_bundle_algo_history( + output_folder: str = ".", template_path: str | None = None, only_trained: bool = True +) -> list: + """ + import the history of the bundleAlgo objects as a list of algo dicts. + each algo_dict has keys name (folder name), algo (bundleAlgo), is_trained (bool), + + Args: + output_folder: the root path of the algorithms templates. + template_path: the algorithm_template. It must contain algo.py in the follow path: + ``{algorithm_templates_dir}/{network}/scripts/algo.py``. + only_trained: only read the algo history if the algo is trained. + """ + + history = [] + + for name in sorted(os.listdir(output_folder)): + write_path = os.path.join(output_folder, name) + + if not os.path.isdir(write_path): + continue + + obj_filename = os.path.join(write_path, "algo_object.pkl") + if not os.path.isfile(obj_filename): # saved mode pkl + continue + + algo, algo_meta_data = algo_from_pickle(obj_filename, template_path=template_path) + + best_metric = algo_meta_data.get(AlgoKeys.SCORE, None) + if best_metric is None: + try: + best_metric = algo.get_score() + except BaseException: + pass + + is_trained = best_metric is not None + + if (only_trained and is_trained) or not only_trained: + history.append( + {AlgoKeys.ID: name, AlgoKeys.ALGO: algo, AlgoKeys.SCORE: best_metric, AlgoKeys.IS_TRAINED: is_trained} + ) + + return history + + +def export_bundle_algo_history(history: list[dict[str, BundleAlgo]]) -> None: + """ + Save all the BundleAlgo in the history to algo_object.pkl in each individual folder + + Args: + history: a List of Bundle. Typically, the history can be obtained from BundleGen get_history method + """ + for algo_dict in history: + algo = algo_dict[AlgoKeys.ALGO] + algo_to_pickle(algo, template_path=algo.template_path) + + +def get_name_from_algo_id(id: str) -> str: + """ + Get the name of Algo from the identifier of the Algo. + + Args: + id: identifier which follows a convention of "name_fold_other". + + Returns: + name of the Algo. + """ + return id.split("_")[0] diff --git a/MONAI/source/monai/apps/datasets.py b/MONAI/source/monai/apps/datasets.py new file mode 100644 index 0000000000000000000000000000000000000000..67ea3059cce5f098e87d8c5326133326bb2e2fdf --- /dev/null +++ b/MONAI/source/monai/apps/datasets.py @@ -0,0 +1,745 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import os +import shutil +import sys +import warnings +from collections.abc import Callable, Sequence +from pathlib import Path +from typing import Any + +import numpy as np + +from monai.apps.tcia import ( + DCM_FILENAME_REGEX, + download_tcia_series_instance, + get_tcia_metadata, + get_tcia_ref_uid, + match_tcia_ref_uid_in_study, +) +from monai.apps.utils import download_and_extract +from monai.config.type_definitions import PathLike +from monai.data import ( + CacheDataset, + PydicomReader, + load_decathlon_datalist, + load_decathlon_properties, + partition_dataset, + select_cross_validation_folds, +) +from monai.transforms import LoadImaged, Randomizable +from monai.utils import ensure_tuple + +__all__ = ["MedNISTDataset", "DecathlonDataset", "CrossValidation", "TciaDataset"] + + +class MedNISTDataset(Randomizable, CacheDataset): + """ + The Dataset to automatically download MedNIST data and generate items for training, validation or test. + It's based on `CacheDataset` to accelerate the training process. + + Args: + root_dir: target directory to download and load MedNIST dataset. + section: expected data section, can be: `training`, `validation` or `test`. + transform: transforms to execute operations on input data. + download: whether to download and extract the MedNIST from resource link, default is False. + if expected file already exists, skip downloading even set it to True. + user can manually copy `MedNIST.tar.gz` file or `MedNIST` folder to root directory. + seed: random seed to randomly split training, validation and test datasets, default is 0. + val_frac: percentage of validation fraction in the whole dataset, default is 0.1. + test_frac: percentage of test fraction in the whole dataset, default is 0.1. + cache_num: number of items to be cached. Default is `sys.maxsize`. + will take the minimum of (cache_num, data_length x cache_rate, data_length). + cache_rate: percentage of cached data in total, default is 1.0 (cache all). + will take the minimum of (cache_num, data_length x cache_rate, data_length). + num_workers: the number of worker threads if computing cache in the initialization. + If num_workers is None then the number returned by os.cpu_count() is used. + If a value less than 1 is specified, 1 will be used instead. + progress: whether to display a progress bar when downloading dataset and computing the transform cache content. + copy_cache: whether to `deepcopy` the cache content before applying the random transforms, + default to `True`. if the random transforms don't modify the cached content + (for example, randomly crop from the cached image and deepcopy the crop region) + or if every cache item is only used once in a `multi-processing` environment, + may set `copy=False` for better performance. + as_contiguous: whether to convert the cached NumPy array or PyTorch tensor to be contiguous. + it may help improve the performance of following logic. + runtime_cache: whether to compute cache at the runtime, default to `False` to prepare + the cache content at initialization. See: :py:class:`monai.data.CacheDataset`. + + Raises: + ValueError: When ``root_dir`` is not a directory. + RuntimeError: When ``dataset_dir`` doesn't exist and downloading is not selected (``download=False``). + + """ + + resource = "https://github.com/Project-MONAI/MONAI-extra-test-data/releases/download/0.8.1/MedNIST.tar.gz" + md5 = "0bc7306e7427e00ad1c5526a6677552d" + compressed_file_name = "MedNIST.tar.gz" + dataset_folder_name = "MedNIST" + + def __init__( + self, + root_dir: PathLike, + section: str, + transform: Sequence[Callable] | Callable = (), + download: bool = False, + seed: int = 0, + val_frac: float = 0.1, + test_frac: float = 0.1, + cache_num: int = sys.maxsize, + cache_rate: float = 1.0, + num_workers: int | None = 1, + progress: bool = True, + copy_cache: bool = True, + as_contiguous: bool = True, + runtime_cache: bool = False, + ) -> None: + root_dir = Path(root_dir) + if not root_dir.is_dir(): + raise ValueError("Root directory root_dir must be a directory.") + self.section = section + self.val_frac = val_frac + self.test_frac = test_frac + self.set_random_state(seed=seed) + tarfile_name = root_dir / self.compressed_file_name + dataset_dir = root_dir / self.dataset_folder_name + self.num_class = 0 + if download: + download_and_extract( + url=self.resource, + filepath=tarfile_name, + output_dir=root_dir, + hash_val=self.md5, + hash_type="md5", + progress=progress, + ) + + if not dataset_dir.is_dir(): + raise RuntimeError( + f"Cannot find dataset directory: {dataset_dir}, please use download=True to download it." + ) + data = self._generate_data_list(dataset_dir) + if transform == (): + transform = LoadImaged("image") + CacheDataset.__init__( + self, + data=data, + transform=transform, + cache_num=cache_num, + cache_rate=cache_rate, + num_workers=num_workers, + progress=progress, + copy_cache=copy_cache, + as_contiguous=as_contiguous, + runtime_cache=runtime_cache, + ) + + def randomize(self, data: np.ndarray) -> None: + self.R.shuffle(data) + + def get_num_classes(self) -> int: + """Get number of classes.""" + return self.num_class + + def _generate_data_list(self, dataset_dir: PathLike) -> list[dict]: + """ + Raises: + ValueError: When ``section`` is not one of ["training", "validation", "test"]. + + """ + dataset_dir = Path(dataset_dir) + class_names = sorted(f"{x.name}" for x in dataset_dir.iterdir() if x.is_dir()) # folder name as the class name + self.num_class = len(class_names) + image_files = [[f"{x}" for x in (dataset_dir / class_names[i]).iterdir()] for i in range(self.num_class)] + num_each = [len(image_files[i]) for i in range(self.num_class)] + image_files_list = [] + image_class = [] + class_name = [] + for i in range(self.num_class): + image_files_list.extend(image_files[i]) + image_class.extend([i] * num_each[i]) + class_name.extend([class_names[i]] * num_each[i]) + + length = len(image_files_list) + indices = np.arange(length) + self.randomize(indices) + + test_length = int(length * self.test_frac) + val_length = int(length * self.val_frac) + if self.section == "test": + section_indices = indices[:test_length] + elif self.section == "validation": + section_indices = indices[test_length : test_length + val_length] + elif self.section == "training": + section_indices = indices[test_length + val_length :] + else: + raise ValueError( + f'Unsupported section: {self.section}, available options are ["training", "validation", "test"].' + ) + # the types of label and class name should be compatible with the pytorch dataloader + return [ + {"image": image_files_list[i], "label": image_class[i], "class_name": class_name[i]} + for i in section_indices + ] + + +class DecathlonDataset(Randomizable, CacheDataset): + """ + The Dataset to automatically download the data of Medical Segmentation Decathlon challenge + (http://medicaldecathlon.com/) and generate items for training, validation or test. + It will also load these properties from the JSON config file of dataset. user can call `get_properties()` + to get specified properties or all the properties loaded. + It's based on :py:class:`monai.data.CacheDataset` to accelerate the training process. + + Args: + root_dir: user's local directory for caching and loading the MSD datasets. + task: which task to download and execute: one of list ("Task01_BrainTumour", "Task02_Heart", + "Task03_Liver", "Task04_Hippocampus", "Task05_Prostate", "Task06_Lung", "Task07_Pancreas", + "Task08_HepaticVessel", "Task09_Spleen", "Task10_Colon"). + section: expected data section, can be: `training`, `validation` or `test`. + transform: transforms to execute operations on input data. + for further usage, use `EnsureChannelFirstd` to convert the shape to [C, H, W, D]. + download: whether to download and extract the Decathlon from resource link, default is False. + if expected file already exists, skip downloading even set it to True. + user can manually copy tar file or dataset folder to the root directory. + val_frac: percentage of validation fraction in the whole dataset, default is 0.2. + seed: random seed to randomly shuffle the datalist before splitting into training and validation, default is 0. + note to set same seed for `training` and `validation` sections. + cache_num: number of items to be cached. Default is `sys.maxsize`. + will take the minimum of (cache_num, data_length x cache_rate, data_length). + cache_rate: percentage of cached data in total, default is 1.0 (cache all). + will take the minimum of (cache_num, data_length x cache_rate, data_length). + num_workers: the number of worker threads if computing cache in the initialization. + If num_workers is None then the number returned by os.cpu_count() is used. + If a value less than 1 is specified, 1 will be used instead. + progress: whether to display a progress bar when downloading dataset and computing the transform cache content. + copy_cache: whether to `deepcopy` the cache content before applying the random transforms, + default to `True`. if the random transforms don't modify the cached content + (for example, randomly crop from the cached image and deepcopy the crop region) + or if every cache item is only used once in a `multi-processing` environment, + may set `copy=False` for better performance. + as_contiguous: whether to convert the cached NumPy array or PyTorch tensor to be contiguous. + it may help improve the performance of following logic. + runtime_cache: whether to compute cache at the runtime, default to `False` to prepare + the cache content at initialization. See: :py:class:`monai.data.CacheDataset`. + + Raises: + ValueError: When ``root_dir`` is not a directory. + ValueError: When ``task`` is not one of ["Task01_BrainTumour", "Task02_Heart", + "Task03_Liver", "Task04_Hippocampus", "Task05_Prostate", "Task06_Lung", "Task07_Pancreas", + "Task08_HepaticVessel", "Task09_Spleen", "Task10_Colon"]. + RuntimeError: When ``dataset_dir`` doesn't exist and downloading is not selected (``download=False``). + + Example:: + + transform = Compose( + [ + LoadImaged(keys=["image", "label"]), + EnsureChannelFirstd(keys=["image", "label"]), + ScaleIntensityd(keys="image"), + ToTensord(keys=["image", "label"]), + ] + ) + + val_data = DecathlonDataset( + root_dir="./", task="Task09_Spleen", transform=transform, section="validation", seed=12345, download=True + ) + + print(val_data[0]["image"], val_data[0]["label"]) + + """ + + resource = { + "Task01_BrainTumour": "https://msd-for-monai.s3-us-west-2.amazonaws.com/Task01_BrainTumour.tar", + "Task02_Heart": "https://msd-for-monai.s3-us-west-2.amazonaws.com/Task02_Heart.tar", + "Task03_Liver": "https://msd-for-monai.s3-us-west-2.amazonaws.com/Task03_Liver.tar", + "Task04_Hippocampus": "https://msd-for-monai.s3-us-west-2.amazonaws.com/Task04_Hippocampus.tar", + "Task05_Prostate": "https://msd-for-monai.s3-us-west-2.amazonaws.com/Task05_Prostate.tar", + "Task06_Lung": "https://msd-for-monai.s3-us-west-2.amazonaws.com/Task06_Lung.tar", + "Task07_Pancreas": "https://msd-for-monai.s3-us-west-2.amazonaws.com/Task07_Pancreas.tar", + "Task08_HepaticVessel": "https://msd-for-monai.s3-us-west-2.amazonaws.com/Task08_HepaticVessel.tar", + "Task09_Spleen": "https://msd-for-monai.s3-us-west-2.amazonaws.com/Task09_Spleen.tar", + "Task10_Colon": "https://msd-for-monai.s3-us-west-2.amazonaws.com/Task10_Colon.tar", + } + md5 = { + "Task01_BrainTumour": "240a19d752f0d9e9101544901065d872", + "Task02_Heart": "06ee59366e1e5124267b774dbd654057", + "Task03_Liver": "a90ec6c4aa7f6a3d087205e23d4e6397", + "Task04_Hippocampus": "9d24dba78a72977dbd1d2e110310f31b", + "Task05_Prostate": "35138f08b1efaef89d7424d2bcc928db", + "Task06_Lung": "8afd997733c7fc0432f71255ba4e52dc", + "Task07_Pancreas": "4f7080cfca169fa8066d17ce6eb061e4", + "Task08_HepaticVessel": "641d79e80ec66453921d997fbf12a29c", + "Task09_Spleen": "410d4a301da4e5b2f6f86ec3ddba524e", + "Task10_Colon": "bad7a188931dc2f6acf72b08eb6202d0", + } + + def __init__( + self, + root_dir: PathLike, + task: str, + section: str, + transform: Sequence[Callable] | Callable = (), + download: bool = False, + seed: int = 0, + val_frac: float = 0.2, + cache_num: int = sys.maxsize, + cache_rate: float = 1.0, + num_workers: int = 1, + progress: bool = True, + copy_cache: bool = True, + as_contiguous: bool = True, + runtime_cache: bool = False, + ) -> None: + root_dir = Path(root_dir) + if not root_dir.is_dir(): + raise ValueError("Root directory root_dir must be a directory.") + self.section = section + self.val_frac = val_frac + self.set_random_state(seed=seed) + if task not in self.resource: + raise ValueError(f"Unsupported task: {task}, available options are: {list(self.resource.keys())}.") + dataset_dir = root_dir / task + tarfile_name = f"{dataset_dir}.tar" + if download: + download_and_extract( + url=self.resource[task], + filepath=tarfile_name, + output_dir=root_dir, + hash_val=self.md5[task], + hash_type="md5", + progress=progress, + ) + + if not dataset_dir.exists(): + raise RuntimeError( + f"Cannot find dataset directory: {dataset_dir}, please use download=True to download it." + ) + self.indices: np.ndarray = np.array([]) + data = self._generate_data_list(dataset_dir) + # as `release` key has typo in Task04 config file, ignore it. + property_keys = [ + "name", + "description", + "reference", + "licence", + "tensorImageSize", + "modality", + "labels", + "numTraining", + "numTest", + ] + self._properties = load_decathlon_properties(dataset_dir / "dataset.json", property_keys) + if transform == (): + transform = LoadImaged(["image", "label"]) + CacheDataset.__init__( + self, + data=data, + transform=transform, + cache_num=cache_num, + cache_rate=cache_rate, + num_workers=num_workers, + progress=progress, + copy_cache=copy_cache, + as_contiguous=as_contiguous, + runtime_cache=runtime_cache, + ) + + def get_indices(self) -> np.ndarray: + """ + Get the indices of datalist used in this dataset. + + """ + return self.indices + + def randomize(self, data: np.ndarray) -> None: + self.R.shuffle(data) + + def get_properties(self, keys: Sequence[str] | str | None = None) -> dict: + """ + Get the loaded properties of dataset with specified keys. + If no keys specified, return all the loaded properties. + + """ + if keys is None: + return self._properties + if self._properties is not None: + return {key: self._properties[key] for key in ensure_tuple(keys)} + return {} + + def _generate_data_list(self, dataset_dir: PathLike) -> list[dict]: + # the types of the item in data list should be compatible with the dataloader + dataset_dir = Path(dataset_dir) + section = "training" if self.section in ["training", "validation"] else "test" + datalist = load_decathlon_datalist(dataset_dir / "dataset.json", True, section) + return self._split_datalist(datalist) + + def _split_datalist(self, datalist: list[dict]) -> list[dict]: + if self.section == "test": + return datalist + length = len(datalist) + indices = np.arange(length) + self.randomize(indices) + + val_length = int(length * self.val_frac) + if self.section == "training": + self.indices = indices[val_length:] + else: + self.indices = indices[:val_length] + + return [datalist[i] for i in self.indices] + + +class TciaDataset(Randomizable, CacheDataset): + """ + The Dataset to automatically download the data from a public The Cancer Imaging Archive (TCIA) dataset + and generate items for training, validation or test. + + The Highdicom library is used to load dicom data with modality "SEG", but only a part of collections are + supported, such as: "C4KC-KiTS", "NSCLC-Radiomics", "NSCLC-Radiomics-Interobserver1", " QIN-PROSTATE-Repeatability" + and "PROSTATEx". Therefore, if "seg" is included in `keys` of the `LoadImaged` transform and loading some + other collections, errors may be raised. For supported collections, the original "SEG" information may not + always be consistent for each dicom file. Therefore, to avoid creating different format of labels, please use + the `label_dict` argument of `PydicomReader` when calling the `LoadImaged` transform. The prepared label dicts + of collections that are mentioned above is also saved in: `monai.apps.tcia.TCIA_LABEL_DICT`. You can also refer + to the second example bellow. + + + This class is based on :py:class:`monai.data.CacheDataset` to accelerate the training process. + + Args: + root_dir: user's local directory for caching and loading the TCIA dataset. + collection: name of a TCIA collection. + a TCIA dataset is defined as a collection. Please check the following list to browse + the collection list (only public collections can be downloaded): + https://www.cancerimagingarchive.net/collections/ + section: expected data section, can be: `training`, `validation` or `test`. + transform: transforms to execute operations on input data. + for further usage, use `EnsureChannelFirstd` to convert the shape to [C, H, W, D]. + If not specified, `LoadImaged(reader="PydicomReader", keys=["image"])` will be used as the default + transform. In addition, we suggest to set the argument `labels` for `PydicomReader` if segmentations + are needed to be loaded. The original labels for each dicom series may be different, using this argument + is able to unify the format of labels. + download: whether to download and extract the dataset, default is False. + if expected file already exists, skip downloading even set it to True. + user can manually copy tar file or dataset folder to the root directory. + download_len: number of series that will be downloaded, the value should be larger than 0 or -1, where -1 means + all series will be downloaded. Default is -1. + seg_type: modality type of segmentation that is used to do the first step download. Default is "SEG". + modality_tag: tag of modality. Default is (0x0008, 0x0060). + ref_series_uid_tag: tag of referenced Series Instance UID. Default is (0x0020, 0x000e). + ref_sop_uid_tag: tag of referenced SOP Instance UID. Default is (0x0008, 0x1155). + specific_tags: tags that will be loaded for "SEG" series. This argument will be used in + `monai.data.PydicomReader`. Default is [(0x0008, 0x1115), (0x0008,0x1140), (0x3006, 0x0010), + (0x0020,0x000D), (0x0010,0x0010), (0x0010,0x0020), (0x0020,0x0011), (0x0020,0x0012)]. + fname_regex: a regular expression to match the file names when the input is a folder. + If provided, only the matched files will be included. For example, to include the file name + "image_0001.dcm", the regular expression could be `".*image_(\\d+).dcm"`. + Default to `"^(?!.*LICENSE).*"`, ignoring any file name containing `"LICENSE"`. + val_frac: percentage of validation fraction in the whole dataset, default is 0.2. + seed: random seed to randomly shuffle the datalist before splitting into training and validation, default is 0. + note to set same seed for `training` and `validation` sections. + cache_num: number of items to be cached. Default is `sys.maxsize`. + will take the minimum of (cache_num, data_length x cache_rate, data_length). + cache_rate: percentage of cached data in total, default is 0.0 (no cache). + will take the minimum of (cache_num, data_length x cache_rate, data_length). + num_workers: the number of worker threads if computing cache in the initialization. + If num_workers is None then the number returned by os.cpu_count() is used. + If a value less than 1 is specified, 1 will be used instead. + progress: whether to display a progress bar when downloading dataset and computing the transform cache content. + copy_cache: whether to `deepcopy` the cache content before applying the random transforms, + default to `True`. if the random transforms don't modify the cached content + (for example, randomly crop from the cached image and deepcopy the crop region) + or if every cache item is only used once in a `multi-processing` environment, + may set `copy=False` for better performance. + as_contiguous: whether to convert the cached NumPy array or PyTorch tensor to be contiguous. + it may help improve the performance of following logic. + runtime_cache: whether to compute cache at the runtime, default to `False` to prepare + the cache content at initialization. See: :py:class:`monai.data.CacheDataset`. + + Example:: + + # collection is "Pancreatic-CT-CBCT-SEG", seg_type is "RTSTRUCT" + data = TciaDataset( + root_dir="./", collection="Pancreatic-CT-CBCT-SEG", seg_type="RTSTRUCT", download=True + ) + + # collection is "C4KC-KiTS", seg_type is "SEG", and load both images and segmentations + from monai.apps.tcia import TCIA_LABEL_DICT + transform = Compose( + [ + LoadImaged(reader="PydicomReader", keys=["image", "seg"], label_dict=TCIA_LABEL_DICT["C4KC-KiTS"]), + EnsureChannelFirstd(keys=["image", "seg"]), + ResampleToMatchd(keys="image", key_dst="seg"), + ] + ) + data = TciaDataset( + root_dir="./", collection="C4KC-KiTS", section="validation", seed=12345, download=True + ) + + print(data[0]["seg"].shape) + + """ + + def __init__( + self, + root_dir: PathLike, + collection: str, + section: str, + transform: Sequence[Callable] | Callable = (), + download: bool = False, + download_len: int = -1, + seg_type: str = "SEG", + modality_tag: tuple = (0x0008, 0x0060), + ref_series_uid_tag: tuple = (0x0020, 0x000E), + ref_sop_uid_tag: tuple = (0x0008, 0x1155), + specific_tags: tuple = ( + (0x0008, 0x1115), # Referenced Series Sequence + (0x0008, 0x1140), # Referenced Image Sequence + (0x3006, 0x0010), # Referenced Frame of Reference Sequence + (0x0020, 0x000D), # Study Instance UID + (0x0010, 0x0010), # Patient's Name + (0x0010, 0x0020), # Patient ID + (0x0020, 0x0011), # Series Number + (0x0020, 0x0012), # Acquisition Number + ), + fname_regex: str = DCM_FILENAME_REGEX, + seed: int = 0, + val_frac: float = 0.2, + cache_num: int = sys.maxsize, + cache_rate: float = 0.0, + num_workers: int = 1, + progress: bool = True, + copy_cache: bool = True, + as_contiguous: bool = True, + runtime_cache: bool = False, + ) -> None: + root_dir = Path(root_dir) + if not root_dir.is_dir(): + raise ValueError("Root directory root_dir must be a directory.") + + self.section = section + self.val_frac = val_frac + self.seg_type = seg_type + self.modality_tag = modality_tag + self.ref_series_uid_tag = ref_series_uid_tag + self.ref_sop_uid_tag = ref_sop_uid_tag + + self.set_random_state(seed=seed) + download_dir = os.path.join(root_dir, collection) + load_tags = list(specific_tags) + load_tags += [modality_tag] + self.load_tags = load_tags + if download: + seg_series_list = get_tcia_metadata( + query=f"getSeries?Collection={collection}&Modality={seg_type}", attribute="SeriesInstanceUID" + ) + if download_len > 0: + seg_series_list = seg_series_list[:download_len] + if len(seg_series_list) == 0: + raise ValueError(f"Cannot find data with collection: {collection} seg_type: {seg_type}") + for series_uid in seg_series_list: + self._download_series_reference_data(series_uid, download_dir) + + if not os.path.exists(download_dir): + raise RuntimeError(f"Cannot find dataset directory: {download_dir}.") + self.fname_regex = fname_regex + + self.indices: np.ndarray = np.array([]) + self.datalist = self._generate_data_list(download_dir) + + if transform == (): + transform = LoadImaged(keys=["image"], reader="PydicomReader", fname_regex=self.fname_regex) + CacheDataset.__init__( + self, + data=self.datalist, + transform=transform, + cache_num=cache_num, + cache_rate=cache_rate, + num_workers=num_workers, + progress=progress, + copy_cache=copy_cache, + as_contiguous=as_contiguous, + runtime_cache=runtime_cache, + ) + + def get_indices(self) -> np.ndarray: + """ + Get the indices of datalist used in this dataset. + + """ + return self.indices + + def randomize(self, data: np.ndarray) -> None: + self.R.shuffle(data) + + def _download_series_reference_data(self, series_uid: str, download_dir: str) -> None: + """ + First of all, download a series from TCIA according to `series_uid`. + Then find all referenced series and download. + """ + seg_first_dir = os.path.join(download_dir, "raw", series_uid) + download_tcia_series_instance( + series_uid=series_uid, download_dir=download_dir, output_dir=seg_first_dir, check_md5=False + ) + dicom_files = [f for f in sorted(os.listdir(seg_first_dir)) if f.endswith(".dcm")] + # achieve series number and patient id from the first dicom file + dcm_path = os.path.join(seg_first_dir, dicom_files[0]) + ds = PydicomReader(stop_before_pixels=True, specific_tags=self.load_tags).read(dcm_path) + # (0x0010,0x0020) and (0x0010,0x0010), better to be contained in `specific_tags` + patient_id = ds.PatientID if ds.PatientID else ds.PatientName + if not patient_id: + warnings.warn(f"unable to find patient name of dicom file: {dcm_path}, use 'patient' instead.") + patient_id = "patient" + # (0x0020,0x0011) and (0x0020,0x0012), better to be contained in `specific_tags` + series_num = ds.SeriesNumber if ds.SeriesNumber else ds.AcquisitionNumber + if not series_num: + warnings.warn(f"unable to find series number of dicom file: {dcm_path}, use '0' instead.") + series_num = 0 + + series_num = str(series_num) + seg_dir = os.path.join(download_dir, patient_id, series_num, self.seg_type.lower()) + dcm_dir = os.path.join(download_dir, patient_id, series_num, "image") + + # get ref uuid + ref_uid_list = [] + for dcm_file in dicom_files: + dcm_path = os.path.join(seg_first_dir, dcm_file) + ds = PydicomReader(stop_before_pixels=True, specific_tags=self.load_tags).read(dcm_path) + if ds[self.modality_tag].value == self.seg_type: + ref_uid = get_tcia_ref_uid( + ds, find_sop=False, ref_series_uid_tag=self.ref_series_uid_tag, ref_sop_uid_tag=self.ref_sop_uid_tag + ) + if ref_uid == "": + ref_sop_uid = get_tcia_ref_uid( + ds, + find_sop=True, + ref_series_uid_tag=self.ref_series_uid_tag, + ref_sop_uid_tag=self.ref_sop_uid_tag, + ) + ref_uid = match_tcia_ref_uid_in_study(ds.StudyInstanceUID, ref_sop_uid) + if ref_uid != "": + ref_uid_list.append(ref_uid) + if not ref_uid_list: + warnings.warn(f"Cannot find the referenced Series Instance UID from series: {series_uid}.") + else: + download_tcia_series_instance( + series_uid=ref_uid_list[0], download_dir=download_dir, output_dir=dcm_dir, check_md5=False + ) + if not os.path.exists(seg_dir): + shutil.copytree(seg_first_dir, seg_dir) + + def _generate_data_list(self, dataset_dir: PathLike) -> list[dict]: + # the types of the item in data list should be compatible with the dataloader + dataset_dir = Path(dataset_dir) + datalist = [] + patient_list = [f.name for f in os.scandir(dataset_dir) if f.is_dir() and f.name != "raw"] + for patient_id in patient_list: + series_list = [f.name for f in os.scandir(os.path.join(dataset_dir, patient_id)) if f.is_dir()] + for series_num in series_list: + seg_key = self.seg_type.lower() + image_path = os.path.join(dataset_dir, patient_id, series_num, "image") + mask_path = os.path.join(dataset_dir, patient_id, series_num, seg_key) + + if os.path.exists(image_path): + datalist.append({"image": image_path, seg_key: mask_path}) + else: + datalist.append({seg_key: mask_path}) + + return self._split_datalist(datalist) + + def _split_datalist(self, datalist: list[dict]) -> list[dict]: + if self.section == "test": + return datalist + length = len(datalist) + indices = np.arange(length) + self.randomize(indices) + + val_length = int(length * self.val_frac) + if self.section == "training": + self.indices = indices[val_length:] + else: + self.indices = indices[:val_length] + + return [datalist[i] for i in self.indices] + + +class CrossValidation: + """ + Cross validation dataset based on the general dataset which must have `_split_datalist` API. + + Args: + dataset_cls: dataset class to be used to create the cross validation partitions. + It must have `_split_datalist` API. + nfolds: number of folds to split the data for cross validation. + seed: random seed to randomly shuffle the datalist before splitting into N folds, default is 0. + dataset_params: other additional parameters for the dataset_cls base class. + + Example of 5 folds cross validation training:: + + cvdataset = CrossValidation( + dataset_cls=DecathlonDataset, + nfolds=5, + seed=12345, + root_dir="./", + task="Task09_Spleen", + section="training", + transform=train_transform, + download=True, + ) + dataset_fold0_train = cvdataset.get_dataset(folds=[1, 2, 3, 4]) + dataset_fold0_val = cvdataset.get_dataset(folds=0, transform=val_transform, download=False) + # execute training for fold 0 ... + + dataset_fold1_train = cvdataset.get_dataset(folds=[0, 2, 3, 4]) + dataset_fold1_val = cvdataset.get_dataset(folds=1, transform=val_transform, download=False) + # execute training for fold 1 ... + + ... + + dataset_fold4_train = ... + # execute training for fold 4 ... + + """ + + def __init__(self, dataset_cls: object, nfolds: int = 5, seed: int = 0, **dataset_params: Any) -> None: + if not hasattr(dataset_cls, "_split_datalist"): + raise ValueError("dataset class must have _split_datalist API.") + self.dataset_cls = dataset_cls + self.nfolds = nfolds + self.seed = seed + self.dataset_params = dataset_params + + def get_dataset(self, folds: Sequence[int] | int, **dataset_params: Any) -> object: + """ + Generate dataset based on the specified fold indices in the cross validation group. + + Args: + folds: index of folds for training or validation, if a list of values, concatenate the data. + dataset_params: other additional parameters for the dataset_cls base class, will override + the same parameters in `self.dataset_params`. + + """ + nfolds = self.nfolds + seed = self.seed + dataset_params_ = dict(self.dataset_params) + dataset_params_.update(dataset_params) + + class _NsplitsDataset(self.dataset_cls): # type: ignore + + def _split_datalist(self, datalist: list[dict]) -> list[dict]: + data = partition_dataset(data=datalist, num_partitions=nfolds, shuffle=True, seed=seed) + return select_cross_validation_folds(partitions=data, folds=folds) + + return _NsplitsDataset(**dataset_params_) diff --git a/MONAI/source/monai/apps/deepedit/__init__.py b/MONAI/source/monai/apps/deepedit/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1e97f8940782e96a77c1c08483fc41da9a48ae22 --- /dev/null +++ b/MONAI/source/monai/apps/deepedit/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/MONAI/source/monai/apps/deepedit/interaction.py b/MONAI/source/monai/apps/deepedit/interaction.py new file mode 100644 index 0000000000000000000000000000000000000000..33e50700caabf6df5e938c6d49429b1ec0c9b20f --- /dev/null +++ b/MONAI/source/monai/apps/deepedit/interaction.py @@ -0,0 +1,100 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from collections.abc import Callable, Sequence + +import numpy as np +import torch + +from monai.data import decollate_batch, list_data_collate +from monai.engines import SupervisedEvaluator, SupervisedTrainer +from monai.engines.utils import IterationEvents +from monai.transforms import Compose +from monai.utils.enums import CommonKeys + + +class Interaction: + """ + Ignite process_function used to introduce interactions (simulation of clicks) for DeepEdit Training/Evaluation. + + More details about this can be found at: + + Diaz-Pinto et al., MONAI Label: A framework for AI-assisted Interactive + Labeling of 3D Medical Images. (2022) https://arxiv.org/abs/2203.12362 + + Args: + deepgrow_probability: probability of simulating clicks in an iteration + transforms: execute additional transformation during every iteration (before train). + Typically, several Tensor based transforms composed by `Compose`. + train: True for training mode or False for evaluation mode + click_probability_key: key to click/interaction probability + label_names: Dict of label names + max_interactions: maximum number of interactions per iteration + """ + + def __init__( + self, + deepgrow_probability: float, + transforms: Sequence[Callable] | Callable, + train: bool, + label_names: None | dict[str, int] = None, + click_probability_key: str = "probability", + max_interactions: int = 1, + ) -> None: + self.deepgrow_probability = deepgrow_probability + self.transforms = Compose(transforms) if not isinstance(transforms, Compose) else transforms + self.train = train + self.label_names = label_names + self.click_probability_key = click_probability_key + self.max_interactions = max_interactions + + def __call__(self, engine: SupervisedTrainer | SupervisedEvaluator, batchdata: dict[str, torch.Tensor]) -> dict: + if batchdata is None: + raise ValueError("Must provide batch data for current iteration.") + + if np.random.choice([True, False], p=[self.deepgrow_probability, 1 - self.deepgrow_probability]): + for j in range(self.max_interactions): + inputs, _ = engine.prepare_batch(batchdata) + inputs = inputs.to(engine.state.device) + + engine.fire_event(IterationEvents.INNER_ITERATION_STARTED) + engine.network.eval() + + with torch.no_grad(): + if engine.amp: + with torch.autocast("cuda"): + predictions = engine.inferer(inputs, engine.network) + else: + predictions = engine.inferer(inputs, engine.network) + batchdata.update({CommonKeys.PRED: predictions}) + + # decollate/collate batchdata to execute click transforms + batchdata_list = decollate_batch(batchdata, detach=True) + for i in range(len(batchdata_list)): + batchdata_list[i][self.click_probability_key] = ( + (1.0 - ((1.0 / self.max_interactions) * j)) if self.train else 1.0 + ) + batchdata_list[i] = self.transforms(batchdata_list[i]) + + batchdata = list_data_collate(batchdata_list) + engine.fire_event(IterationEvents.INNER_ITERATION_COMPLETED) + else: + # zero out input guidance channels + batchdata_list = decollate_batch(batchdata, detach=True) + for i in range(1, len(batchdata_list[0][CommonKeys.IMAGE])): + batchdata_list[0][CommonKeys.IMAGE][i] *= 0 + batchdata = list_data_collate(batchdata_list) + + # first item in batch only + engine.state.batch = batchdata + return engine._iteration(engine, batchdata) # type: ignore[arg-type] diff --git a/MONAI/source/monai/apps/deepedit/transforms.py b/MONAI/source/monai/apps/deepedit/transforms.py new file mode 100644 index 0000000000000000000000000000000000000000..14c37be8602f7b2f909c9c037ba610a4f838198f --- /dev/null +++ b/MONAI/source/monai/apps/deepedit/transforms.py @@ -0,0 +1,960 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import json +import logging +import random +import warnings +from collections.abc import Hashable, Mapping, Sequence, Sized + +import numpy as np +import torch + +from monai.config import KeysCollection +from monai.data import MetaTensor +from monai.networks.layers import GaussianFilter +from monai.transforms.transform import MapTransform, Randomizable, Transform +from monai.utils import deprecated, min_version, optional_import + +measure, _ = optional_import("skimage.measure", "0.14.2", min_version) + +logger = logging.getLogger(__name__) + +distance_transform_cdt, _ = optional_import("scipy.ndimage", name="distance_transform_cdt") + + +class DiscardAddGuidanced(MapTransform): + + def __init__( + self, + keys: KeysCollection, + number_intensity_ch: int = 1, + probability: float = 1.0, + label_names: Sized | None = None, + allow_missing_keys: bool = False, + ): + """ + Discard positive and negative points according to discard probability + + Args: + keys: The ``keys`` parameter will be used to get and set the actual data item to transform + number_intensity_ch: number of intensity channels + probability: probability of discarding clicks + """ + super().__init__(keys, allow_missing_keys) + + self.number_intensity_ch = number_intensity_ch + self.discard_probability = probability + self.label_names = label_names or [] + + def _apply(self, image): + if self.discard_probability >= 1.0 or np.random.choice( + [True, False], p=[self.discard_probability, 1 - self.discard_probability] + ): + signal = np.zeros( + (len(self.label_names), image.shape[-3], image.shape[-2], image.shape[-1]), dtype=np.float32 + ) + if image.shape[0] == self.number_intensity_ch + len(self.label_names): + image[self.number_intensity_ch :, ...] = signal + else: + image = np.concatenate([image, signal], axis=0) + return image + + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) + for key in self.key_iterator(d): + if key == "image": + tmp_image = self._apply(d[key]) + if isinstance(d[key], MetaTensor): + d[key].array = tmp_image + else: + d[key] = tmp_image + else: + print("This transform only applies to the image") + return d + + +class RemapLabelsToSequentiald(MapTransform): + """ + Remap label values from a dataset-specific schema to sequential indices (0, 1, 2, 3, ...). + + This transform takes labels with arbitrary values defined in a label dictionary and remaps them + to a sequential range starting from 1 (with background always set to 0). This is useful for + standardizing labels across different datasets or ensuring labels are in a contiguous range. + + The output label indices are assigned in alphabetical order by label name to ensure + deterministic behavior regardless of input dictionary ordering. + + Args: + keys: The ``keys`` parameter will be used to get and set the actual data item to transform + label_names: Dictionary mapping label names to their current values in the dataset. + For example: {"spleen": 1, "liver": 6, "background": 0} + Will be remapped to: {"background": 0, "liver": 1, "spleen": 2} + (alphabetically sorted, excluding background) + allow_missing_keys: If True, missing keys in the data dictionary will not raise an error + + Example: + >>> transform = RemapLabelsToSequentiald( + ... keys="label", + ... label_names={"liver": 6, "spleen": 1, "background": 0} + ... ) + >>> # Input label has values [0, 1, 6] + >>> # Output label will have values [0, 1, 2] (background=0, liver=1, spleen=2) + >>> # And updates d["label_names"] to {"background": 0, "liver": 1, "spleen": 2} + + Note: + - Background label (if present) is always mapped to 0 + - Non-background labels are mapped to sequential indices 1, 2, 3, ... in alphabetical order + - Undefined labels (not in label_names) will be set to 0 (background) + - The transform updates the data dictionary with a new "label_names" key containing the remapped values + """ + + def __init__( + self, keys: KeysCollection, label_names: dict[str, int] | None = None, allow_missing_keys: bool = False + ): + super().__init__(keys, allow_missing_keys) + + self.label_names = label_names or {} + + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) + for key in self.key_iterator(d): + # Dictionary containing new label numbers + new_label_names = {} + label = np.zeros(d[key].shape) + + # Sort label names to ensure deterministic ordering (exclude background) + sorted_labels = sorted([(k, v) for k, v in self.label_names.items() if k != "background"]) + + # Always set background to 0 first + if "background" in self.label_names: + new_label_names["background"] = 0 + + # Assign sequential indices to sorted non-background labels + for idx, (key_label, val_label) in enumerate(sorted_labels, start=1): + new_label_names[key_label] = idx + label[d[key] == val_label] = idx + + d["label_names"] = new_label_names + if isinstance(d[key], MetaTensor): + d[key].array = label + else: + d[key] = label + return d + + +@deprecated(since="1.6", removed="1.8", msg_suffix="Use `RemapLabelsToSequentiald` instead.") +class NormalizeLabelsInDatasetd(RemapLabelsToSequentiald): + """ + .. deprecated:: 1.6.0 + `NormalizeLabelsInDatasetd` is deprecated and will be removed in version 1.8.0. + Use :class:`RemapLabelsToSequentiald` instead. + + This class is maintained for backward compatibility. Please use RemapLabelsToSequentiald + which better describes the transform's functionality. + """ + + pass + + +class SingleLabelSelectiond(MapTransform): + + def __init__( + self, keys: KeysCollection, label_names: Sequence[str] | None = None, allow_missing_keys: bool = False + ): + """ + Selects one label at a time to train the DeepEdit + + Args: + keys: The ``keys`` parameter will be used to get and set the actual data item to transform + label_names: all label names + """ + super().__init__(keys, allow_missing_keys) + + self.label_names: Sequence[str] = label_names or [] + self.all_label_values = { + "spleen": 1, + "right kidney": 2, + "left kidney": 3, + "gallbladder": 4, + "esophagus": 5, + "liver": 6, + "stomach": 7, + "aorta": 8, + "inferior vena cava": 9, + "portal_vein": 10, + "splenic_vein": 11, + "pancreas": 12, + "right adrenal gland": 13, + "left adrenal gland": 14, + } + + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) + for key in self.key_iterator(d): + if key == "label": + # Taking one label at a time + t_label = np.random.choice(self.label_names) + d["current_label"] = t_label + d[key][d[key] != self.all_label_values[t_label]] = 0.0 + # Convert label to index values following label_names argument + max_label_val = self.label_names.index(t_label) + 1 + d[key][d[key] > 0] = max_label_val + print(f"Using label {t_label} with number: {d[key].max()}") + else: + warnings.warn("This transform only applies to the label") + return d + + +class AddGuidanceSignalDeepEditd(MapTransform): + """ + Add Guidance signal for input image. Multilabel DeepEdit + + Based on the "guidance" points, apply Gaussian to them and add them as new channel for input image. + + Args: + guidance: key to store guidance. + sigma: standard deviation for Gaussian kernel. + number_intensity_ch: channel index. + """ + + def __init__( + self, + keys: KeysCollection, + guidance: str = "guidance", + sigma: int = 3, + number_intensity_ch: int = 1, + allow_missing_keys: bool = False, + ): + super().__init__(keys, allow_missing_keys) + self.guidance = guidance + self.sigma = sigma + self.number_intensity_ch = number_intensity_ch + + def _get_signal(self, image, guidance): + dimensions = 3 if len(image.shape) > 3 else 2 + guidance = guidance.tolist() if isinstance(guidance, np.ndarray) else guidance + guidance = json.loads(guidance) if isinstance(guidance, str) else guidance + + # In inference the user may not provide clicks for some channels/labels + if len(guidance): + if dimensions == 3: + # Assume channel is first and depth is last CHWD + signal = np.zeros((1, image.shape[-3], image.shape[-2], image.shape[-1]), dtype=np.float32) + else: + signal = np.zeros((1, image.shape[-2], image.shape[-1]), dtype=np.float32) + + sshape = signal.shape + for point in guidance: # TO DO: make the guidance a list only - it is currently a list of list + if np.any(np.asarray(point) < 0): + continue + + if dimensions == 3: + # Making sure points fall inside the image dimension + p1 = max(0, min(int(point[-3]), sshape[-3] - 1)) + p2 = max(0, min(int(point[-2]), sshape[-2] - 1)) + p3 = max(0, min(int(point[-1]), sshape[-1] - 1)) + signal[:, p1, p2, p3] = 1.0 + else: + p1 = max(0, min(int(point[-2]), sshape[-2] - 1)) + p2 = max(0, min(int(point[-1]), sshape[-1] - 1)) + signal[:, p1, p2] = 1.0 + + # Apply a Gaussian filter to the signal + if np.max(signal[0]) > 0: + signal_tensor = torch.tensor(signal[0]) + pt_gaussian = GaussianFilter(len(signal_tensor.shape), sigma=self.sigma) + signal_tensor = pt_gaussian(signal_tensor.unsqueeze(0).unsqueeze(0)) + signal_tensor = signal_tensor.squeeze(0).squeeze(0) + signal[0] = signal_tensor.detach().cpu().numpy() + signal[0] = (signal[0] - np.min(signal[0])) / (np.max(signal[0]) - np.min(signal[0])) + return signal + else: + if dimensions == 3: + signal = np.zeros((1, image.shape[-3], image.shape[-2], image.shape[-1]), dtype=np.float32) + else: + signal = np.zeros((1, image.shape[-2], image.shape[-1]), dtype=np.float32) + return signal + + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) + for key in self.key_iterator(d): + if key == "image": + image = d[key] + tmp_image = image[0 : 0 + self.number_intensity_ch, ...] + guidance = d[self.guidance] + for key_label in guidance.keys(): + # Getting signal based on guidance + signal = self._get_signal(image, guidance[key_label]) + tmp_image = np.concatenate([tmp_image, signal], axis=0) + if isinstance(d[key], MetaTensor): + d[key].array = tmp_image + else: + d[key] = tmp_image + return d + else: + print("This transform only applies to image key") + return d + + +class FindAllValidSlicesDeepEditd(MapTransform): + """ + Find/List all valid slices in the labels. + Label is assumed to be a 4D Volume with shape CHWD, where C=1. + + Args: + sids: key to store slices indices having valid label map. + """ + + def __init__(self, keys: KeysCollection, sids: Hashable = "sids", allow_missing_keys: bool = False): + super().__init__(keys, allow_missing_keys) + self.sids = sids + + def _apply(self, label, d): + sids = {} + for key_label in d["label_names"].keys(): + l_ids = [] + for sid in range(label.shape[-1]): # Assume channel is first and depth is last CHWD + if d["label_names"][key_label] in label[0][..., sid]: + l_ids.append(sid) + sids[key_label] = l_ids + return sids + + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) + for key in self.key_iterator(d): + if key == "label": + label = d[key] + if label.shape[0] != 1: + raise ValueError("Only supports single channel labels!") + + if len(label.shape) != 4: # only for 3D + raise ValueError("Only supports label with shape CHWD!") + + sids = self._apply(label, d) + if sids is not None and len(sids.keys()): + d[self.sids] = sids + return d + else: + print("This transform only applies to label key") + return d + + +class AddInitialSeedPointDeepEditd(Randomizable, MapTransform): + """ + Add random guidance as initial seed point for a given label. + + Note that the label is of size (C, D, H, W) or (C, H, W) + + The guidance is of size (2, N, # of dims) where N is number of guidance added. + # of dims = 4 when C, D, H, W; # of dims = 3 when (C, H, W) + + Args: + guidance: key to store guidance. + sids: key that represents lists of valid slice indices for the given label. + sid: key that represents the slice to add initial seed point. If not present, random sid will be chosen. + connected_regions: maximum connected regions to use for adding initial points. + """ + + def __init__( + self, + keys: KeysCollection, + guidance: str = "guidance", + sids: str = "sids", + sid: str = "sid", + connected_regions: int = 5, + allow_missing_keys: bool = False, + ): + super().__init__(keys, allow_missing_keys) + self.sids_key = sids + self.sid_key = sid + self.sid: dict[str, int] = dict() + self.guidance = guidance + self.connected_regions = connected_regions + + def _apply(self, label, sid, key_label): + dimensions = 3 if len(label.shape) > 3 else 2 + self.default_guidance = [-1] * (dimensions + 1) + + dims = dimensions + if sid is not None and dimensions == 3: + dims = 2 + label = label[0][..., sid][np.newaxis] # Assume channel is first and depth is last CHWD + + # THERE MAY BE MULTIPLE BLOBS FOR SINGLE LABEL IN THE SELECTED SLICE + label = (label > 0.5).astype(np.float32) + # measure.label: Label connected regions of an integer array - Two pixels are connected + # when they are neighbors and have the same value + blobs_labels = measure.label(label.astype(int), background=0) if dims == 2 else label + if np.max(blobs_labels) <= 0: + raise AssertionError(f"SLICES NOT FOUND FOR LABEL: {key_label}") + + pos_guidance = [] + for ridx in range(1, 2 if dims == 3 else self.connected_regions + 1): + if dims == 2: + label = (blobs_labels == ridx).astype(np.float32) + if np.sum(label) == 0: + pos_guidance.append(self.default_guidance) + continue + + # The distance transform provides a metric or measure of the separation of points in the image. + # This function calculates the distance between each pixel that is set to off (0) and + # the nearest nonzero pixel for binary images - http://matlab.izmiran.ru/help/toolbox/images/morph14.html + distance = distance_transform_cdt(label).flatten() + probability = np.exp(distance) - 1.0 + + idx = np.where(label.flatten() > 0)[0] + seed = self.R.choice(idx, size=1, p=probability[idx] / np.sum(probability[idx])) + dst = distance[seed] + + g = np.asarray(np.unravel_index(seed, label.shape)).transpose().tolist()[0] + g[0] = dst[0] # for debug + if dimensions == 2 or dims == 3: + pos_guidance.append(g) + else: + # Clicks are created using this convention Channel Height Width Depth (CHWD) + pos_guidance.append([g[0], g[-2], g[-1], sid]) # Assume channel is first and depth is last CHWD + + return np.asarray([pos_guidance]) + + def _randomize(self, d, key_label): + sids = d.get(self.sids_key).get(key_label) if d.get(self.sids_key) is not None else None + sid = d.get(self.sid_key).get(key_label) if d.get(self.sid_key) is not None else None + if sids is not None and sids: + if sid is None or sid not in sids: + sid = self.R.choice(sids, replace=False) + else: + logger.info(f"Not slice IDs for label: {key_label}") + sid = None + self.sid[key_label] = sid + + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) + for key in self.key_iterator(d): + if key == "label": + label_guidances = {} + for key_label in d["sids"].keys(): + # Randomize: Select a random slice + self._randomize(d, key_label) + # Generate guidance base on selected slice + tmp_label = np.copy(d[key]) + # Taking one label to create the guidance + if key_label != "background": + tmp_label[tmp_label != float(d["label_names"][key_label])] = 0 + else: + tmp_label[tmp_label != float(d["label_names"][key_label])] = 1 + tmp_label = 1 - tmp_label + label_guidances[key_label] = json.dumps( + self._apply(tmp_label, self.sid.get(key_label), key_label).astype(int).tolist() + ) + d[self.guidance] = label_guidances + return d + else: + print("This transform only applies to label key") + return d + + +class FindDiscrepancyRegionsDeepEditd(MapTransform): + """ + Find discrepancy between prediction and actual during click interactions during training. + + Args: + pred: key to prediction source. + discrepancy: key to store discrepancies found between label and prediction. + """ + + def __init__( + self, + keys: KeysCollection, + pred: str = "pred", + discrepancy: str = "discrepancy", + allow_missing_keys: bool = False, + ): + super().__init__(keys, allow_missing_keys) + self.pred = pred + self.discrepancy = discrepancy + + @staticmethod + def disparity(label, pred): + disparity = label - pred + # Negative ONES mean predicted label is not part of the ground truth + # Positive ONES mean predicted label missed that region of the ground truth + pos_disparity = (disparity > 0).astype(np.float32) + neg_disparity = (disparity < 0).astype(np.float32) + return [pos_disparity, neg_disparity] + + def _apply(self, label, pred): + return self.disparity(label, pred) + + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) + for key in self.key_iterator(d): + if key == "label": + all_discrepancies = {} + for _, (key_label, val_label) in enumerate(d["label_names"].items()): + if key_label != "background": + # Taking single label + label = np.copy(d[key]) + label[label != val_label] = 0 + # Label should be represented in 1 + label = (label > 0.5).astype(np.float32) + # Taking single prediction + pred = np.copy(d[self.pred]) + pred[pred != val_label] = 0 + # Prediction should be represented in one + pred = (pred > 0.5).astype(np.float32) + else: + # Taking single label + label = np.copy(d[key]) + label[label != val_label] = 1 + label = 1 - label + # Label should be represented in 1 + label = (label > 0.5).astype(np.float32) + # Taking single prediction + pred = np.copy(d[self.pred]) + pred[pred != val_label] = 1 + pred = 1 - pred + # Prediction should be represented in one + pred = (pred > 0.5).astype(np.float32) + all_discrepancies[key_label] = self._apply(label, pred) + d[self.discrepancy] = all_discrepancies + return d + else: + print("This transform only applies to 'label' key") + return d + + +class AddRandomGuidanceDeepEditd(Randomizable, MapTransform): + """ + Add random guidance based on discrepancies that were found between label and prediction. + + Args: + guidance: key to guidance source, shape (2, N, # of dim) + discrepancy: key to discrepancy map between label and prediction shape (2, C, H, W, D) or (2, C, H, W) + probability: key to click/interaction probability, shape (1) + """ + + def __init__( + self, + keys: KeysCollection, + guidance: str = "guidance", + discrepancy: str = "discrepancy", + probability: str = "probability", + allow_missing_keys: bool = False, + ): + super().__init__(keys, allow_missing_keys) + self.guidance_key = guidance + self.discrepancy = discrepancy + self.probability = probability + self._will_interact = None + self.is_pos: bool | None = None + self.is_other: bool | None = None + self.default_guidance = None + self.guidance: dict[str, list[list[int]]] = {} + + def randomize(self, data=None): + probability = data[self.probability] + self._will_interact = self.R.choice([True, False], p=[probability, 1.0 - probability]) + + def find_guidance(self, discrepancy): + distance = distance_transform_cdt(discrepancy).flatten() + probability = np.exp(distance.flatten()) - 1.0 + idx = np.where(discrepancy.flatten() > 0)[0] + + if np.sum(discrepancy > 0) > 0: + seed = self.R.choice(idx, size=1, p=probability[idx] / np.sum(probability[idx])) + dst = distance[seed] + + g = np.asarray(np.unravel_index(seed, discrepancy.shape)).transpose().tolist()[0] + g[0] = dst[0] + return g + return None + + def add_guidance(self, guidance, discrepancy, label_names, labels): + # Positive clicks of the segment in the iteration + pos_discr = discrepancy[0] # idx 0 is positive discrepancy and idx 1 is negative discrepancy + + # Check the areas that belong to other segments + other_discrepancy_areas = {} + for _, (key_label, val_label) in enumerate(label_names.items()): + if key_label != "background": + tmp_label = np.copy(labels) + tmp_label[tmp_label != val_label] = 0 + tmp_label = (tmp_label > 0.5).astype(np.float32) + other_discrepancy_areas[key_label] = np.sum(discrepancy[1] * tmp_label) + else: + tmp_label = np.copy(labels) + tmp_label[tmp_label != val_label] = 1 + tmp_label = 1 - tmp_label + other_discrepancy_areas[key_label] = np.sum(discrepancy[1] * tmp_label) + + # Add guidance to the current key label + if np.sum(pos_discr) > 0: + guidance.append(self.find_guidance(pos_discr)) + self.is_pos = True + + # Add guidance to the other areas + for key_label in label_names.keys(): + # Areas that cover more than 50 voxels + if other_discrepancy_areas[key_label] > 50: + self.is_other = True + if key_label != "background": + tmp_label = np.copy(labels) + tmp_label[tmp_label != label_names[key_label]] = 0 + tmp_label = (tmp_label > 0.5).astype(np.float32) + self.guidance[key_label].append(self.find_guidance(discrepancy[1] * tmp_label)) + else: + tmp_label = np.copy(labels) + tmp_label[tmp_label != label_names[key_label]] = 1 + tmp_label = 1 - tmp_label + self.guidance[key_label].append(self.find_guidance(discrepancy[1] * tmp_label)) + + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) + guidance = d[self.guidance_key] + discrepancy = d[self.discrepancy] + self.randomize(data) + if self._will_interact: + # Convert all guidance to lists so new guidance can be easily appended + for key_label in d["label_names"].keys(): + tmp_gui = guidance[key_label] + tmp_gui = tmp_gui.tolist() if isinstance(tmp_gui, np.ndarray) else tmp_gui + tmp_gui = json.loads(tmp_gui) if isinstance(tmp_gui, str) else tmp_gui + self.guidance[key_label] = [j for j in tmp_gui if -1 not in j] + + # Add guidance according to discrepancy + for key_label in d["label_names"].keys(): + # Add guidance based on discrepancy + self.add_guidance(self.guidance[key_label], discrepancy[key_label], d["label_names"], d["label"]) + + # Checking the number of clicks + num_clicks = random.randint(1, 10) + counter = 0 + keep_guidance = [] + while True: + aux_label = random.choice(list(d["label_names"].keys())) + if aux_label in keep_guidance: + pass + else: + keep_guidance.append(aux_label) + counter = counter + len(self.guidance[aux_label]) + # If collected clicks is bigger than max clicks, discard the others + if counter >= num_clicks: + for key_label in d["label_names"].keys(): + if key_label not in keep_guidance: + self.guidance[key_label] = [] + logger.info(f"Number of simulated clicks: {counter}") + break + + # Breaking once all labels are covered + if len(keep_guidance) == len(d["label_names"].keys()): + logger.info(f"Number of simulated clicks: {counter}") + break + d[self.guidance_key] = self.guidance # Update the guidance + return d + + +class AddGuidanceFromPointsDeepEditd(Transform): + """ + Add guidance based on user clicks. ONLY WORKS FOR 3D + + We assume the input is loaded by LoadImaged and has the shape of (H, W, D) originally. + Clicks always specify the coordinates in (H, W, D) + + Args: + ref_image: key to reference image to fetch current and original image details. + guidance: output key to store guidance. + meta_keys: explicitly indicate the key of the metadata dictionary of `ref_image`. + for example, for data with key `image`, the metadata by default is in `image_meta_dict`. + the metadata is a dictionary object which contains: filename, original_shape, etc. + if None, will try to construct meta_keys by `{ref_image}_{meta_key_postfix}`. + meta_key_postfix: if meta_key is None, use `{ref_image}_{meta_key_postfix}` to fetch the metadata according + to the key data, default is `meta_dict`, the metadata is a dictionary object. + For example, to handle key `image`, read/write affine matrices from the + metadata `image_meta_dict` dictionary's `affine` field. + + """ + + def __init__( + self, + ref_image: str, + guidance: str = "guidance", + label_names: dict | None = None, + meta_keys: str | None = None, + meta_key_postfix: str = "meta_dict", + ): + self.ref_image = ref_image + self.guidance = guidance + self.label_names = label_names or {} + self.meta_keys = meta_keys + self.meta_key_postfix = meta_key_postfix + + @staticmethod + def _apply(clicks, factor): + if len(clicks): + guidance = np.multiply(clicks, factor).astype(int).tolist() + return guidance + else: + return [] + + def __call__(self, data): + d = dict(data) + meta_dict_key = self.meta_keys or f"{self.ref_image}_{self.meta_key_postfix}" + # extract affine matrix from metadata + if isinstance(d[self.ref_image], MetaTensor): + meta_dict = d[self.ref_image].meta + elif meta_dict_key in d: + meta_dict = d[meta_dict_key] + else: + raise ValueError( + f"{meta_dict_key} is not found. Please check whether it is the correct the image meta key." + ) + + if "spatial_shape" not in meta_dict: + raise RuntimeError('Missing "spatial_shape" in meta_dict!') + + # Assume channel is first and depth is last CHWD + original_shape = meta_dict["spatial_shape"] + current_shape = list(d[self.ref_image].shape)[1:] + + # in here we assume the depth dimension is in the last dimension of "original_shape" and "current_shape" + factor = np.array(current_shape) / original_shape + + # Creating guidance for all clicks + all_guidances = {} + for key_label in self.label_names.keys(): + clicks = d.get(key_label, []) + clicks = list(np.array(clicks).astype(int)) + all_guidances[key_label] = self._apply(clicks, factor) + d[self.guidance] = all_guidances + return d + + +class ResizeGuidanceMultipleLabelDeepEditd(Transform): + """ + Resize the guidance based on cropped vs resized image. + + """ + + def __init__(self, guidance: str, ref_image: str) -> None: + self.guidance = guidance + self.ref_image = ref_image + + def __call__(self, data): + d = dict(data) + # Assume channel is first and depth is last CHWD + current_shape = d[self.ref_image].shape[1:] + + meta_dict_key = "image_meta_dict" + # extract affine matrix from metadata + if isinstance(d[self.ref_image], MetaTensor): + meta_dict = d[self.ref_image].meta + elif meta_dict_key in d: + meta_dict = d[meta_dict_key] + else: + raise ValueError( + f"{meta_dict_key} is not found. Please check whether it is the correct the image meta key." + ) + + original_shape = meta_dict["spatial_shape"] + + factor = np.divide(current_shape, original_shape) + all_guidances = {} + for key_label in d[self.guidance].keys(): + guidance = ( + np.multiply(d[self.guidance][key_label], factor).astype(int).tolist() + if len(d[self.guidance][key_label]) + else [] + ) + all_guidances[key_label] = guidance + + d[self.guidance] = all_guidances + return d + + +class SplitPredsLabeld(MapTransform): + """ + Split preds and labels for individual evaluation + + """ + + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) + for key in self.key_iterator(d): + if key == "pred": + for idx, (key_label, _) in enumerate(d["label_names"].items()): + if key_label != "background": + d[f"pred_{key_label}"] = d[key][idx + 1, ...][None] + d[f"label_{key_label}"] = d["label"][idx + 1, ...][None] + elif key != "pred": + logger.info("This is only for pred key") + return d + + +class AddInitialSeedPointMissingLabelsd(Randomizable, MapTransform): + """ + Add random guidance as initial seed point for a given label. + Note that the label is of size (C, D, H, W) or (C, H, W) + The guidance is of size (2, N, # of dims) where N is number of guidance added. + # of dims = 4 when C, D, H, W; # of dims = 3 when (C, H, W) + Args: + guidance: key to store guidance. + sids: key that represents lists of valid slice indices for the given label. + sid: key that represents the slice to add initial seed point. If not present, random sid will be chosen. + connected_regions: maximum connected regions to use for adding initial points. + """ + + def __init__( + self, + keys: KeysCollection, + guidance: str = "guidance", + sids: str = "sids", + sid: str = "sid", + connected_regions: int = 5, + allow_missing_keys: bool = False, + ): + super().__init__(keys, allow_missing_keys) + self.sids_key = sids + self.sid_key = sid + self.sid: dict[str, int] = dict() + self.guidance = guidance + self.connected_regions = connected_regions + + def _apply(self, label, sid): + dimensions = 3 if len(label.shape) > 3 else 2 + self.default_guidance = [-1] * (dimensions + 1) + + dims = dimensions + if sid is not None and dimensions == 3: + dims = 2 + label = label[0][..., sid][np.newaxis] # Assume channel is first and depth is last CHWD + + # THERE MAY BE MULTIPLE BLOBS FOR SINGLE LABEL IN THE SELECTED SLICE + label = (label > 0.5).astype(np.float32) + # measure.label: Label connected regions of an integer array - Two pixels are connected + # when they are neighbors and have the same value + blobs_labels = measure.label(label.astype(int), background=0) if dims == 2 else label + + label_guidance = [] + # If there are is presence of that label in this slice + if np.max(blobs_labels) <= 0: + label_guidance.append(self.default_guidance) + else: + for ridx in range(1, 2 if dims == 3 else self.connected_regions + 1): + if dims == 2: + label = (blobs_labels == ridx).astype(np.float32) + if np.sum(label) == 0: + label_guidance.append(self.default_guidance) + continue + + # The distance transform provides a metric or measure of the separation of points in the image. + # This function calculates the distance between each pixel that is set to off (0) and + # the nearest nonzero pixel for binary images + # http://matlab.izmiran.ru/help/toolbox/images/morph14.html + distance = distance_transform_cdt(label).flatten() + probability = np.exp(distance) - 1.0 + + idx = np.where(label.flatten() > 0)[0] + seed = self.R.choice(idx, size=1, p=probability[idx] / np.sum(probability[idx])) + dst = distance[seed] + + g = np.asarray(np.unravel_index(seed, label.shape)).transpose().tolist()[0] + g[0] = dst[0] # for debug + if dimensions == 2 or dims == 3: + label_guidance.append(g) + else: + # Clicks are created using this convention Channel Height Width Depth (CHWD) + label_guidance.append([g[0], g[-2], g[-1], sid]) # Assume channel is first and depth is last CHWD + + return np.asarray(label_guidance) + + def _randomize(self, d, key_label): + sids = d.get(self.sids_key).get(key_label) if d.get(self.sids_key) is not None else None + sid = d.get(self.sid_key).get(key_label) if d.get(self.sid_key) is not None else None + if sids is not None and sids: + if sid is None or sid not in sids: + sid = self.R.choice(sids, replace=False) + else: + logger.info(f"Not slice IDs for label: {key_label}") + sid = None + self.sid[key_label] = sid + + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) + for key in self.key_iterator(d): + if key == "label": + label_guidances = {} + for key_label in d["sids"].keys(): + # Randomize: Select a random slice + self._randomize(d, key_label) + # Generate guidance base on selected slice + tmp_label = np.copy(d[key]) + # Taking one label to create the guidance + if key_label != "background": + tmp_label[tmp_label != float(d["label_names"][key_label])] = 0 + else: + tmp_label[tmp_label != float(d["label_names"][key_label])] = 1 + tmp_label = 1 - tmp_label + label_guidances[key_label] = json.dumps( + self._apply(tmp_label, self.sid.get(key_label)).astype(int).tolist() + ) + d[self.guidance] = label_guidances + return d + else: + print("This transform only applies to label key") + return d + + +class FindAllValidSlicesMissingLabelsd(MapTransform): + """ + Find/List all valid slices in the labels. + Label is assumed to be a 4D Volume with shape CHWD, where C=1. + Args: + sids: key to store slices indices having valid label map. + """ + + def __init__(self, keys: KeysCollection, sids: Hashable = "sids", allow_missing_keys: bool = False): + super().__init__(keys, allow_missing_keys) + self.sids = sids + + def _apply(self, label, d): + sids = {} + for key_label in d["label_names"].keys(): + l_ids = [] + for sid in range(label.shape[-1]): # Assume channel is first and depth is last CHWD + if d["label_names"][key_label] in label[0][..., sid]: + l_ids.append(sid) + # If there are not slices with the label + if l_ids == []: + l_ids = [-1] * 10 + sids[key_label] = l_ids + return sids + + def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]: + d: dict = dict(data) + for key in self.key_iterator(d): + if key == "label": + label = d[key] + if label.shape[0] != 1: + raise ValueError("Only supports single channel labels!") + + if len(label.shape) != 4: # only for 3D + raise ValueError("Only supports label with shape CHWD!") + + sids = self._apply(label, d) + if sids is not None and len(sids.keys()): + d[self.sids] = sids + return d + else: + print("This transform only applies to label key") + return d diff --git a/MONAI/source/monai/apps/deepgrow/__init__.py b/MONAI/source/monai/apps/deepgrow/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1e97f8940782e96a77c1c08483fc41da9a48ae22 --- /dev/null +++ b/MONAI/source/monai/apps/deepgrow/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/MONAI/source/monai/apps/deepgrow/dataset.py b/MONAI/source/monai/apps/deepgrow/dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..e597188e7456ce9c32d3378bc2af774ba9574f03 --- /dev/null +++ b/MONAI/source/monai/apps/deepgrow/dataset.py @@ -0,0 +1,261 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import logging +import os +from collections.abc import Sequence + +import numpy as np + +from monai.config import PathLike +from monai.transforms import Compose, EnsureChannelFirstd, LoadImaged, Orientationd, Spacingd, SqueezeDimd, Transform +from monai.utils import GridSampleMode + + +def create_dataset( + datalist: list[dict], + output_dir: str, + dimension: int, + pixdim: Sequence[float] | float, + image_key: str = "image", + label_key: str = "label", + base_dir: PathLike | None = None, + limit: int = 0, + relative_path: bool = False, + transforms: Transform | None = None, +) -> list[dict]: + """ + Utility to pre-process and create dataset list for Deepgrow training over on existing one. + The input data list is normally a list of images and labels (3D volume) that needs pre-processing + for Deepgrow training pipeline. + + Args: + datalist: A list of data dictionary. Each entry should at least contain 'image_key': . + For example, typical input data can be a list of dictionaries:: + + [{'image': , 'label':