├── .bazeliskrc ├── .bazelrc ├── .bazelrc.local ├── .bazelrc.local.example ├── .bazelversion ├── .coveragerc ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── direct.png ├── labeler.yml └── workflows │ ├── black.yml │ ├── coverage.yml │ ├── pr-labels.yml │ ├── pylint.yml │ ├── stale.yml │ └── tox.yml ├── .gitignore ├── .npmrc ├── .pre-commit-config.yaml ├── .prospector.yml ├── .ruff.toml ├── BUILD.bazel ├── CITATION.cff ├── CODE_OF_CONDUCT.md ├── LICENSE ├── MANIFEST.in ├── MODULE.bazel ├── Makefile ├── README.rst ├── authors.rst ├── codecov.yml ├── contributing.rst ├── direct ├── BUILD.bazel ├── __init__.py ├── algorithms │ ├── __init__.py │ ├── mri_algorithms.py │ └── optimization.py ├── checkpointer.py ├── cli │ ├── __init__.py │ ├── cli.py │ ├── predict.py │ ├── train.py │ ├── upload.py │ └── utils.py ├── common │ ├── __init__.py │ ├── _gaussian.pyx │ ├── _poisson.pyx │ ├── subsample.py │ └── subsample_config.py ├── config │ ├── __init__.py │ └── defaults.py ├── constants.py ├── data │ ├── __init__.py │ ├── bbox.py │ ├── datasets.py │ ├── datasets_config.py │ ├── fake.py │ ├── h5_data.py │ ├── lr_scheduler.py │ ├── mri_transforms.py │ ├── samplers.py │ ├── sens.py │ └── transforms.py ├── engine.py ├── environment.py ├── exceptions.py ├── functionals │ ├── __init__.py │ ├── challenges.py │ ├── grad.py │ ├── hfen.py │ ├── nmae.py │ ├── nmse.py │ ├── psnr.py │ ├── snr.py │ └── ssim.py ├── inference.py ├── launch.py ├── nn │ ├── __init__.py │ ├── cirim │ │ ├── __init__.py │ │ ├── cirim.py │ │ ├── cirim_engine.py │ │ └── config.py │ ├── conjgradnet │ │ ├── __init__.py │ │ ├── config.py │ │ ├── conjgrad.py │ │ ├── conjgradnet.py │ │ └── conjgradnet_engine.py │ ├── conv │ │ ├── __init__.py │ │ └── conv.py │ ├── crossdomain │ │ ├── __init__.py │ │ ├── crossdomain.py │ │ └── multicoil.py │ ├── didn │ │ ├── __init__.py │ │ └── didn.py │ ├── get_nn_model_config.py │ ├── iterdualnet │ │ ├── __init__.py │ │ ├── config.py │ │ ├── iterdualnet.py │ │ └── iterdualnet_engine.py │ ├── jointicnet │ │ ├── __init__.py │ │ ├── config.py │ │ ├── jointicnet.py │ │ └── jointicnet_engine.py │ ├── kikinet │ │ ├── __init__.py │ │ ├── config.py │ │ ├── kikinet.py │ │ └── kikinet_engine.py │ ├── lpd │ │ ├── __init__.py │ │ ├── config.py │ │ ├── lpd.py │ │ └── lpd_engine.py │ ├── mobilenet │ │ ├── __init__.py │ │ ├── config.py │ │ └── mobilenet.py │ ├── mri_models.py │ ├── multidomainnet │ │ ├── __init__.py │ │ ├── config.py │ │ ├── multidomain.py │ │ ├── multidomainnet.py │ │ └── multidomainnet_engine.py │ ├── mwcnn │ │ ├── __init__.py │ │ └── mwcnn.py │ ├── recurrent │ │ ├── __init__.py │ │ └── recurrent.py │ ├── recurrentvarnet │ │ ├── __init__.py │ │ ├── config.py │ │ ├── recurrentvarnet.py │ │ └── recurrentvarnet_engine.py │ ├── resnet │ │ ├── __init__.py │ │ ├── config.py │ │ └── resnet.py │ ├── rim │ │ ├── __init__.py │ │ ├── config.py │ │ ├── rim.py │ │ └── rim_engine.py │ ├── ssl │ │ ├── __init__.py │ │ └── mri_models.py │ ├── transformers │ │ ├── __init__.py │ │ ├── config.py │ │ ├── transformers.py │ │ ├── transformers_engine.py │ │ ├── uformer.py │ │ ├── utils.py │ │ └── vit.py │ ├── types.py │ ├── unet │ │ ├── __init__.py │ │ ├── config.py │ │ ├── unet_2d.py │ │ ├── unet_3d.py │ │ └── unet_engine.py │ ├── varnet │ │ ├── __init__.py │ │ ├── config.py │ │ ├── varnet.py │ │ └── varnet_engine.py │ ├── varsplitnet │ │ ├── __init__.py │ │ ├── config.py │ │ ├── varsplitnet.py │ │ └── varsplitnet_engine.py │ ├── vsharp │ │ ├── __init__.py │ │ ├── config.py │ │ ├── vsharp.py │ │ └── vsharp_engine.py │ └── xpdnet │ │ ├── __init__.py │ │ ├── config.py │ │ ├── xpdnet.py │ │ └── xpdnet_engine.py ├── predict.py ├── ssl │ ├── __init__.py │ ├── _gaussian_fill.pyx │ ├── mask_fillers.py │ └── ssl.py ├── train.py ├── types.py └── utils │ ├── __init__.py │ ├── asserts.py │ ├── bbox.py │ ├── communication.py │ ├── dataset.py │ ├── events.py │ ├── imports.py │ ├── io.py │ ├── logging.py │ ├── models.py │ └── writers.py ├── docker ├── Dockerfile ├── README.rst └── jupyter_notebook_config.py ├── docs ├── .nojekyll ├── Makefile ├── authors.rst ├── calgary_campinas.rst ├── cmrxrecon.rst ├── colab.rst ├── conf.py ├── config.rst ├── cvpr2022_recurrentvarnet.rst ├── datasets.rst ├── examples.rst ├── examples │ └── calgary_campinas_dataset.rst ├── ext │ └── doi_role.py ├── getting_started.rst ├── history.rst ├── index.rst ├── inference.rst ├── installation.rst ├── jssl.rst ├── make.bat ├── model_zoo.rst ├── models.rst ├── modules.rst ├── samplers.rst ├── shepp_logan.rst ├── spie2022_radial_subsampling.rst ├── tensorboard.rst └── training.rst ├── installation.rst ├── logo ├── direct_logo_horizontal.svg └── direct_logo_square.svg ├── package.json ├── pnpm-lock.yaml ├── prettier.config.cjs ├── projects ├── BUILD.bazel ├── CMRxRecon │ ├── README.rst │ ├── configs │ │ ├── base_rvn.yaml │ │ ├── base_varnet.yaml │ │ ├── base_vsharp_2D.yaml │ │ └── base_vsharp_2D_dynamic_recon.yaml │ ├── lists │ │ ├── 116x384_map_train.lst │ │ ├── 116x384_map_with_masks_train.lst │ │ ├── 132x448_cine_train.lst │ │ ├── 132x448_cine_with_masks_train.lst │ │ ├── 144x512_map_train.lst │ │ ├── 144x512_map_with_masks_train.lst │ │ ├── 162x512_cine_train.lst │ │ ├── 162x512_cine_with_masks_train.lst │ │ ├── 168x448_cine_train.lst │ │ ├── 168x448_cine_with_masks_train.lst │ │ ├── 204x448_cine_train.lst │ │ ├── 204x448_cine_with_masks_train.lst │ │ ├── 204x512_cine_train.lst │ │ ├── 204x512_cine_with_masks_train.lst │ │ ├── 246x512_cine_train.lst │ │ └── 246x512_cine_with_masks_train.lst │ └── tools │ │ ├── create_data_dir.py │ │ ├── create_data_with_masks.py │ │ └── create_symlinks.py ├── JSSL │ ├── README.rst │ ├── configs │ │ ├── unet_jssl.yaml │ │ ├── unet_ssl.yaml │ │ ├── varnet_jssl.yaml │ │ ├── varnet_ssl.yaml │ │ ├── vsharp_jssl.yaml │ │ ├── vsharp_jssl_oversampled.yaml │ │ ├── vsharp_jssl_single_q.yaml │ │ ├── vsharp_sl_all.yaml │ │ ├── vsharp_sl_all_oversampled.yaml │ │ ├── vsharp_sl_prostate_only.yaml │ │ ├── vsharp_ssl.yaml │ │ ├── vsharp_ssl_all.yaml │ │ ├── vsharp_ssl_all_oversampled.yaml │ │ ├── vsharp_ssl_all_single_q.yaml │ │ └── vsharp_ssl_single_q.yaml │ └── lists │ │ ├── test_prostate.lst │ │ ├── train_brain_16_coils_640x320.lst │ │ ├── train_brain_16_coils_768x396.lst │ │ ├── train_brain_20_coils_640x320.lst │ │ ├── train_brain_20_coils_768x396.lst │ │ ├── train_brain_4_coils_512x276.lst │ │ ├── train_brain_4_coils_640x320.lst │ │ ├── train_brain_4_coils_640x322.lst │ │ ├── train_brain_4_coils_768x392.lst │ │ ├── train_brain_6_coils_640x320.lst │ │ ├── train_knee.lst │ │ ├── train_prostate_10_coils.lst │ │ ├── train_prostate_14_coils.lst │ │ ├── train_prostate_16_coils.lst │ │ ├── train_prostate_20_coils.lst │ │ ├── train_prostate_24_coils.lst │ │ ├── train_prostate_26_coils.lst │ │ ├── train_prostate_30_coils.lst │ │ └── val_prostate.lst ├── calgary_campinas │ ├── README.rst │ ├── compute_masks.py │ ├── configs │ │ ├── base_cirim.yaml │ │ ├── base_conjgradnet.yaml │ │ ├── base_jointicnet.yaml │ │ ├── base_kikinet.yaml │ │ ├── base_lpd.yaml │ │ ├── base_multidomainnet.yaml │ │ ├── base_recurrentvarnet.yaml │ │ ├── base_rim.yaml │ │ ├── base_unet.yaml │ │ ├── base_varnet.yaml │ │ └── base_xpdnet.yaml │ ├── lists │ │ ├── train │ │ │ ├── 12x218x170_train.lst │ │ │ └── 12x218x180_train.lst │ │ └── val │ │ │ ├── 12x218x170_val.lst │ │ │ └── 12x218x180_val.lst │ ├── predict_test.py │ └── utils │ │ └── __init__.py ├── cvpr2022_recurrentvarnet │ ├── README.rst │ ├── calgary_campinas │ │ ├── configs │ │ │ ├── ablation │ │ │ │ ├── base_recurrentvarnet_T11.yaml │ │ │ │ ├── base_recurrentvarnet_noRSI.yaml │ │ │ │ ├── base_recurrentvarnet_noSER.yaml │ │ │ │ └── base_recurrentvarnet_shared.yaml │ │ │ ├── base_recurrentvarnet.yaml │ │ │ └── comparisons │ │ │ │ ├── base_rim.yaml │ │ │ │ ├── base_unet.yaml │ │ │ │ ├── base_varnet.yaml │ │ │ │ └── base_xpdnet.yaml │ │ ├── configs_inference │ │ │ ├── 10x │ │ │ │ ├── ablation │ │ │ │ │ ├── base_recurrentvarnet_T11.yaml │ │ │ │ │ ├── base_recurrentvarnet_noRSI.yaml │ │ │ │ │ ├── base_recurrentvarnet_noSER.yaml │ │ │ │ │ └── base_recurrentvarnet_shared.yaml │ │ │ │ ├── base_recurrentvarnet.yaml │ │ │ │ └── comparisons │ │ │ │ │ ├── base_rim.yaml │ │ │ │ │ ├── base_unet.yaml │ │ │ │ │ ├── base_varnet.yaml │ │ │ │ │ └── base_xpdnet.yaml │ │ │ └── 5x │ │ │ │ ├── ablation │ │ │ │ ├── base_recurrentvarnet_T11.yaml │ │ │ │ ├── base_recurrentvarnet_noRSI.yaml │ │ │ │ ├── base_recurrentvarnet_noSER.yaml │ │ │ │ └── base_recurrentvarnet_shared.yaml │ │ │ │ ├── base_recurrentvarnet.yaml │ │ │ │ └── comparisons │ │ │ │ ├── base_rim.yaml │ │ │ │ ├── base_unet.yaml │ │ │ │ ├── base_varnet.yaml │ │ │ │ └── base_xpdnet.yaml │ │ └── lists │ │ │ ├── test │ │ │ └── test.lst │ │ │ ├── train │ │ │ ├── 12x218x170_train.lst │ │ │ └── 12x218x180_train.lst │ │ │ └── val │ │ │ ├── 12x218x170_val.lst │ │ │ ├── 12x218x180_val.lst │ │ │ └── val.lst │ └── fastmri │ │ └── AXT1_brain │ │ ├── configs │ │ ├── base_lpd.yaml │ │ ├── base_recurrentvarnet.yaml │ │ ├── base_recurrentvarnet_ablation.yaml │ │ ├── base_rim.yaml │ │ ├── base_unet.yaml │ │ └── base_varnet.yaml │ │ ├── configs_inference │ │ ├── 4x │ │ │ ├── base_lpd.yaml │ │ │ ├── base_recurrentvarnet.yaml │ │ │ ├── base_recurrentvarnet_ablation.yaml │ │ │ ├── base_rim.yaml │ │ │ ├── base_unet.yaml │ │ │ └── base_varnet.yaml │ │ └── 8x │ │ │ ├── base_lpd.yaml │ │ │ ├── base_recurrentvarnet.yaml │ │ │ ├── base_recurrentvarnet_ablation.yaml │ │ │ ├── base_rim.yaml │ │ │ ├── base_unet.yaml │ │ │ └── base_varnet.yaml │ │ └── lists │ │ ├── test.lst │ │ ├── train.lst │ │ └── val.lst ├── predict_val.py ├── spie2022_radial_subsampling │ ├── README.rst │ ├── compute_metrics.py │ ├── configs │ │ ├── base_radial.yaml │ │ ├── base_rectilinear.yaml │ │ └── inference │ │ │ ├── 10x │ │ │ ├── base_radial.yaml │ │ │ └── base_rectilinear.yaml │ │ │ └── 5x │ │ │ ├── base_radial.yaml │ │ │ └── base_rectilinear.yaml │ ├── lists │ │ ├── test │ │ │ ├── 12x218x170_test.lst │ │ │ ├── 12x218x180_test.lst │ │ │ └── test.lst │ │ ├── train │ │ │ ├── 12x218x170_train.lst │ │ │ └── 12x218x180_train.lst │ │ └── val │ │ │ ├── 12x218x170_val.lst │ │ │ ├── 12x218x180_val.lst │ │ │ └── val.lst │ └── plot_zoomed.py ├── toy │ ├── base.yaml │ └── shepp_logan │ │ ├── README.rst │ │ └── base_unet.yaml └── vSHARP │ ├── README.rst │ └── fastmri_prostate │ ├── configs │ ├── base_recurrentvarnet.yaml │ ├── base_unet.yaml │ ├── base_varnet.yaml │ └── base_vsharp.yaml │ └── lists │ ├── train_10_coils.lst │ ├── train_14_coils.lst │ ├── train_16_coils.lst │ ├── train_20_coils.lst │ ├── train_24_coils.lst │ ├── train_26_coils.lst │ └── train_30_coils.lst ├── pyproject.toml ├── requirements.in ├── requirements_darwin.txt ├── requirements_linux.txt ├── setup.cfg ├── setup.py ├── tests ├── BUILD.bazel ├── __init__.py ├── checkpointer_test.py ├── test_cli │ ├── BUILD.bazel │ ├── __init__.py │ └── utils_test.py ├── tests_common │ ├── BUILD.bazel │ ├── __init__.py │ └── subsample_test.py ├── tests_data │ ├── BUILD.bazel │ ├── __init__.py │ ├── algorithms_test.py │ ├── datasets_test.py │ ├── fake_test.py │ ├── lr_scheduler_test.py │ ├── mri_transforms_test.py │ ├── samplers_test.py │ ├── sens_test.py │ └── transforms_test.py ├── tests_functionals │ ├── BUILD.bazel │ ├── __init__.py │ ├── gradloss_test.py │ ├── hfen_test.py │ ├── nmae_test.py │ ├── nmse_test.py │ ├── psnr_test.py │ ├── snr_test.py │ └── ssim_test.py ├── tests_nn │ ├── BUILD.bazel │ ├── __init__.py │ ├── cirim_engine_test.py │ ├── cirim_test.py │ ├── conjgradnet_engine_test.py │ ├── conjgradnet_test.py │ ├── conv_test.py │ ├── didn_test.py │ ├── iterdualnet_engine_test.py │ ├── iterdualnet_test.py │ ├── jointicnet_engine_test.py │ ├── jointicnet_test.py │ ├── kikinet_engine_test.py │ ├── kikinet_test.py │ ├── lpd_engine_test.py │ ├── lpd_test.py │ ├── mri_models_test.py │ ├── multidomainnet_engine_test.py │ ├── multidomainnet_test.py │ ├── mwcnn_test.py │ ├── recurrent_test.py │ ├── recurrentvarnet_engine_test.py │ ├── recurrentvarnet_test.py │ ├── resnet_test.py │ ├── rim_engine_test.py │ ├── rim_test.py │ ├── transformers_engine_test.py │ ├── transformers_test.py │ ├── unet_2d_test.py │ ├── unet_3d_test.py │ ├── unet_engine_test.py │ ├── varnet_engine_test.py │ ├── varnet_test.py │ ├── varsplitnet_engine_test.py │ ├── varsplitnet_test.py │ ├── vsharp_engine_test.py │ ├── vsharp_test.py │ ├── xpdnet_engine_test.py │ └── xpdnet_test.py ├── tests_ssl │ ├── BUILD.bazel │ ├── ssl_test.py │ ├── unet_engine_test.py │ ├── varnet_engine_test.py │ └── vsharp_engine_test.py ├── tests_utils │ ├── BUILD.bazel │ ├── __init__.py │ ├── imports_test.py │ ├── io_test.py │ └── utils_test.py └── train_test.py └── tools ├── BUILD.bazel ├── README.md ├── cython_rules.bzl ├── format └── BUILD.bazel └── parse_metrics_log.py /.bazeliskrc: -------------------------------------------------------------------------------- 1 | BAZELISK_BASE_URL=https://static.aspect.build/aspect 2 | USE_BAZEL_VERSION=aspect/2025.15.20 3 | 4 | -------------------------------------------------------------------------------- /.bazelrc.local: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Local-only builds — no remote interaction 3 | build:local --disk_cache=~/.cache/bazel 4 | 5 | ############################################################################### 6 | # Local builds + remote cache (shared cache only, for reuse) 7 | build:remote_cache --remote_cache=http://gorgophone.rhpc.nki.nl:8080 8 | build:remote_cache --remote_timeout=60 9 | build:remote_cache --remote_accept_cached=true 10 | build:remote_cache --remote_upload_local_results=true 11 | build:remote_cache --remote_download_outputs=minimal 12 | build:remote_cache --remote_local_fallback=false 13 | ############################################################################### 14 | # Fully remote builds (remote execution + remote cache) 15 | build:remote_build --remote_executor=grpc://gorgophone.rhpc.nki.nl:8980 16 | build:remote_build --remote_cache=grpc://gorgophone.rhpc.nki.nl:8980 17 | build:remote_build --remote_timeout=60 18 | build:remote_build --remote_accept_cached=true 19 | build:remote_build --remote_upload_local_results=true 20 | build:remote_build --remote_download_outputs=minimal 21 | 22 | # Optionally only offload specific strategies (customizable) 23 | build:remote_build --spawn_strategy=remote 24 | build:remote_build --strategy=CppCompile=remote 25 | build:remote_build --strategy=Javac=remote 26 | build:remote_build --strategy=GoCompile=remote 27 | -------------------------------------------------------------------------------- /.bazelrc.local.example: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Local-only builds — no remote interaction 3 | build:local --disk_cache=~/.cache/bazel 4 | 5 | ############################################################################### 6 | # Local builds + remote cache (shared cache only, for reuse) 7 | build:remote_cache --remote_cache=http://gorgophone.rhpc.nki.nl:8080 8 | build:remote_cache --remote_timeout=60 9 | build:remote_cache --remote_accept_cached=true 10 | build:remote_cache --remote_upload_local_results=true 11 | build:remote_cache --remote_download_outputs=minimal 12 | build:remote_cache --remote_local_fallback=false 13 | ############################################################################### 14 | # Fully remote builds (remote execution + remote cache) 15 | build:remote_build --remote_executor=grpc://gorgophone.rhpc.nki.nl:8980 16 | build:remote_build --remote_cache=grpc://gorgophone.rhpc.nki.nl:8980 17 | build:remote_build --remote_timeout=60 18 | build:remote_build --remote_accept_cached=true 19 | build:remote_build --remote_upload_local_results=true 20 | build:remote_build --remote_download_outputs=minimal 21 | 22 | # Optionally only offload specific strategies (customizable) 23 | build:remote_build --spawn_strategy=remote 24 | build:remote_build --strategy=CppCompile=remote 25 | build:remote_build --strategy=Javac=remote 26 | build:remote_build --strategy=GoCompile=remote 27 | -------------------------------------------------------------------------------- /.bazelversion: -------------------------------------------------------------------------------- 1 | 7.6.1 -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | tests/* 4 | setup.py 5 | */tests/*.py 6 | */config.py 7 | source = 8 | . 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | charset = utf-8 11 | end_of_line = lf 12 | 13 | [*.bat] 14 | indent_style = tab 15 | end_of_line = crlf 16 | 17 | [LICENSE] 18 | insert_final_newline = false 19 | 20 | [Makefile] 21 | indent_style = tab 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: bug 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | A clear and concise description on how to reproduce the problem. 14 | 15 | **Expected behavior** 16 | A clear and concise description of what you expected to happen. 17 | 18 | **Environment** 19 | dlup version: 20 | How installed: 21 | Python version: 22 | Operating System: 23 | 24 | **Additional context** 25 | Add any other context about the problem here. 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: enhancement 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/direct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NKI-AI/direct/50b9704d2d7aa8eede763ced92e71b69baa8189d/.github/direct.png -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | python: 2 | - "**/*.py" 3 | - "*.py" 4 | documentation: 5 | - "**/*.md" 6 | - "*.md" 7 | - "**/*.rst" 8 | - "*.rst" 9 | docker: 10 | - "docker/Dockerfile" 11 | ci: 12 | - ".github/**" 13 | -------------------------------------------------------------------------------- /.github/workflows/black.yml: -------------------------------------------------------------------------------- 1 | name: Black 2 | on: [pull_request] 3 | jobs: 4 | black: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-python@v4 9 | - uses: psf/black@22.12.0 10 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: CodeCov 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | python-version: ['3.10'] 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Set up Python ${{ matrix.python-version }} 14 | uses: actions/setup-python@v2 15 | with: 16 | python-version: ${{ matrix.python-version }} 17 | - name: Install dependencies 18 | run: | 19 | python -m pip install --upgrade pip setuptools wheel 20 | pip install coverage 21 | pip install -e ".[dev]" 22 | - name: Run Coverage 23 | run: | 24 | coverage run -m pytest --ignore=projects 25 | - name: Upload Coverage to Codecov 26 | uses: codecov/codecov-action@v1 27 | -------------------------------------------------------------------------------- /.github/workflows/pr-labels.yml: -------------------------------------------------------------------------------- 1 | name: PR Labeler 2 | on: 3 | - pull_request 4 | jobs: 5 | label: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/labeler@v2 9 | with: 10 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 11 | -------------------------------------------------------------------------------- /.github/workflows/pylint.yml: -------------------------------------------------------------------------------- 1 | name: Pylint 2 | on: [pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - name: Set up Python 3.10 9 | uses: actions/setup-python@v1 10 | with: 11 | python-version: '3.10' 12 | - name: Install dependencies 13 | run: | 14 | python -m pip install --upgrade pip 15 | pip install pylint==2.17.7 sewar 16 | pip install -e ".[dev]" 17 | - name: Analysing the code with pylint 18 | run: | 19 | pylint direct --errors-only 20 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues" 2 | on: 3 | schedule: 4 | - cron: "0 0 * * *" 5 | jobs: 6 | stale: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/stale@v3 10 | with: 11 | repo-token: ${{ secrets.GITHUB_TOKEN }} 12 | stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 15 days' 13 | days-before-stale: 60 14 | days-before-close: 15 15 | -------------------------------------------------------------------------------- /.github/workflows/tox.yml: -------------------------------------------------------------------------------- 1 | name: Tox 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | python-version: ['3.9', '3.10'] 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Set up Python ${{ matrix.python-version }} 14 | uses: actions/setup-python@v4 15 | with: 16 | python-version: ${{ matrix.python-version }} 17 | - name: Install dependencies 18 | run: | 19 | python -m pip install --upgrade pip setuptools wheel 20 | pip install tox tox-gh-actions 21 | python -m pip install -e ".[dev]" 22 | - name: Test with tox 23 | run: tox 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Bazel 10 | bazel-* 11 | MODULE.bazel.lock 12 | 13 | # node 14 | node_modules 15 | 16 | # OS X 17 | .DS_Store 18 | 19 | # Pycharm 20 | .idea/ 21 | 22 | # Distribution / packaging 23 | .Python 24 | env/ 25 | build/ 26 | develop-eggs/ 27 | dist/ 28 | downloads/ 29 | eggs/ 30 | .eggs/ 31 | lib/ 32 | lib64/ 33 | parts/ 34 | sdist/ 35 | var/ 36 | wheels/ 37 | *.egg-info/ 38 | .installed.cfg 39 | *.egg 40 | 41 | # PyInstaller 42 | # Usually these files are written by a python script from a template 43 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 44 | *.manifest 45 | *.spec 46 | 47 | # Installer logs 48 | pip-log.txt 49 | pip-delete-this-directory.txt 50 | 51 | # Unit test / coverage reports 52 | htmlcov/ 53 | .tox/ 54 | .coverage 55 | .coverage.* 56 | .cache 57 | nosetests.xml 58 | coverage.xml 59 | *.cover 60 | .hypothesis/ 61 | .pytest_cache/ 62 | 63 | # Translations 64 | *.mo 65 | *.pot 66 | 67 | # Django stuff: 68 | *.log 69 | local_settings.py 70 | 71 | # Flask stuff: 72 | instance/ 73 | .webassets-cache 74 | 75 | # Scrapy stuff: 76 | .scrapy 77 | 78 | # Sphinx documentation 79 | docs/_build/ 80 | 81 | # PyBuilder 82 | target/ 83 | 84 | # Jupyter Notebook 85 | .ipynb_checkpoints 86 | 87 | # pyenv 88 | .python-version 89 | 90 | # celery beat schedule file 91 | celerybeat-schedule 92 | 93 | # SageMath parsed files 94 | *.sage.py 95 | 96 | # dotenv 97 | .env 98 | 99 | # virtualenv 100 | .venv 101 | venv/ 102 | ENV/ 103 | 104 | # Spyder project settings 105 | .spyderproject 106 | .spyproject 107 | 108 | # Rope project settings 109 | .ropeproject 110 | 111 | # mkdocs documentation 112 | /site 113 | 114 | # mypy 115 | .mypy_cache/ 116 | 117 | # IDE settings 118 | .vscode/ 119 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | hoist=false 2 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v3.4.0 4 | hooks: 5 | - id: check-ast 6 | - id: check-case-conflict 7 | - id: check-executables-have-shebangs 8 | - id: check-json 9 | - id: check-merge-conflict 10 | - id: check-symlinks 11 | - id: check-vcs-permalinks 12 | - id: check-xml 13 | - id: check-yaml 14 | args: [--unsafe] 15 | - id: end-of-file-fixer 16 | - id: fix-encoding-pragma 17 | args: ['--pragma=# coding=utf-8'] 18 | - id: requirements-txt-fixer 19 | - id: check-added-large-files 20 | args: ['--maxkb=500'] 21 | - id: sort-simple-yaml 22 | - id: trailing-whitespace 23 | - id: no-commit-to-branch 24 | args: [--branch, main] 25 | - id: forbid-new-submodules 26 | - repo: https://github.com/pycqa/isort 27 | rev: 5.8.0 28 | hooks: 29 | - id: isort 30 | name: isort (python) 31 | - id: isort 32 | name: isort (cython) 33 | types: [cython] 34 | - id: isort 35 | name: isort (pyi) 36 | types: [pyi] 37 | - repo: https://github.com/psf/black 38 | rev: 21.6b0 39 | hooks: 40 | - id: black 41 | - repo: https://github.com/pre-commit/mirrors-mypy 42 | rev: v0.902 43 | hooks: 44 | - id: mypy 45 | additional_dependencies: [types-requests, types-docutils] 46 | - repo: local 47 | hooks: 48 | - id: pylint 49 | name: pylint 50 | entry: pylint 51 | language: system 52 | types: [python] 53 | args: 54 | - dlup 55 | - --errors-only 56 | -------------------------------------------------------------------------------- /.prospector.yml: -------------------------------------------------------------------------------- 1 | pep257: 2 | disable: 3 | - D203 # Conflict with numpydocs 4 | - D213 # Conflict with numpydocs 5 | - D212 # Conflict with numpydocs 6 | pep8: 7 | disable: 8 | - E501 # Handled by black 9 | - W605 # Conflict with numpydocs - invalid escape sequence 10 | pylint: 11 | disable: 12 | - import-outside-toplevel 13 | options: 14 | max-args: 20 15 | -------------------------------------------------------------------------------- /.ruff.toml: -------------------------------------------------------------------------------- 1 | # Exclude a variety of commonly ignored directories. 2 | exclude = [ 3 | ".bzr", 4 | ".direnv", 5 | ".eggs", 6 | ".git", 7 | ".git-rewrite", 8 | ".hg", 9 | ".ipynb_checkpoints", 10 | ".mypy_cache", 11 | ".nox", 12 | ".pants.d", 13 | ".pyenv", 14 | ".pytest_cache", 15 | ".pytype", 16 | ".ruff_cache", 17 | ".svn", 18 | ".tox", 19 | ".venv", 20 | ".vscode", 21 | "__pypackages__", 22 | "_build", 23 | "buck-out", 24 | "build", 25 | "dist", 26 | "node_modules", 27 | "site-packages", 28 | "venv", 29 | ] 30 | 31 | # Same as Black. 32 | line-length = 120 33 | indent-width = 4 34 | 35 | target-version = "py311" 36 | 37 | [lint] 38 | # Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. 39 | # Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or 40 | # McCabe complexity (`C901`) by default. 41 | select = ["E4", "E7", "E9", "F"] 42 | ignore = [] 43 | 44 | # Allow fix for all enabled rules (when `--fix`) is provided. 45 | fixable = ["ALL"] 46 | unfixable = [] 47 | 48 | # Allow unused variables when underscore-prefixed. 49 | dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" 50 | 51 | [format] 52 | # Like Black, use double quotes for strings. 53 | quote-style = "double" 54 | 55 | # Like Black, indent with spaces, rather than tabs. 56 | indent-style = "space" 57 | 58 | # Like Black, respect magic trailing commas. 59 | skip-magic-trailing-comma = false 60 | 61 | # Like Black, automatically detect the appropriate line ending. 62 | line-ending = "auto" 63 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_js//js:defs.bzl", "js_library") 2 | load("@npm//:defs.bzl", "npm_link_all_packages") 3 | load("@rules_multirun//:defs.bzl", "multirun") 4 | load("@rules_uv//uv:pip.bzl", "pip_compile") 5 | 6 | package(default_visibility = ["//visibility:public"]) 7 | 8 | npm_link_all_packages(name = "node_modules") 9 | 10 | exports_files( 11 | [ 12 | ".ruff.toml", 13 | ], 14 | visibility = ["//visibility:public"], 15 | ) 16 | 17 | pip_compile( 18 | name = "generate_requirements_linux_txt", 19 | python_platform = "x86_64-unknown-linux-gnu", 20 | requirements_in = "requirements.in", 21 | requirements_txt = "requirements_linux.txt", 22 | ) 23 | 24 | pip_compile( 25 | name = "generate_requirements_darwin_txt", 26 | python_platform = "aarch64-apple-darwin", 27 | requirements_in = "requirements.in", 28 | requirements_txt = "requirements_darwin.txt", 29 | ) 30 | 31 | multirun( 32 | name = "generate_requirements_lock", 33 | commands = [ 34 | ":generate_requirements_linux_txt", 35 | ":generate_requirements_darwin_txt", 36 | ], 37 | # Running in a single threaded mode allows consecutive `uv` invocations to benefit 38 | # from the `uv` cache from the first run. 39 | jobs = 1, 40 | ) 41 | 42 | js_library( 43 | name = "prettierrc", 44 | srcs = ["prettier.config.cjs"], 45 | visibility = ["//tools/format:__pkg__"], 46 | deps = [ 47 | ":node_modules/@prettier/plugin-xml", 48 | ":node_modules/prettier-plugin-gherkin", 49 | ":node_modules/prettier-plugin-sql", 50 | ], 51 | ) 52 | 53 | alias( 54 | name = "format", 55 | actual = "//tools/format", 56 | ) 57 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: Yiasemis 5 | given-names: George 6 | email: g.yiasemis@nki.nl 7 | orcid: https://orcid.org/0000-0002-1348-8987 8 | - family-names: Moriakov 9 | given-names: Nikita 10 | email: n.moriakov@nki.nl 11 | orcid: https://orcid.org/0000-0002-7127-1006 12 | - family-names: Karkalousos 13 | given-names: Dimitrios 14 | email: d.karkalousos@amsterdamumc.nl 15 | orcid: https://orcid.org/0000-0001-5983-0322 16 | - family-names: Caan 17 | given-names: Matthan 18 | email: m.w.a.caan@amsterdamumc.nl 19 | orcid: https://orcid.org/0000-0002-5162-8880 20 | - family-names: Teuwen 21 | given-names: Jonas 22 | email: j.teuwen@nki.nl 23 | orcid: https://orcid.org/0000-0002-1825-1428 24 | title: "DIRECT: Deep Image REConstruction Toolkit" 25 | doi: 10.21105/joss.04278 26 | url: https://doi.org/10.21105/joss.04278 27 | journal: Journal of Open Source Software 28 | publisher: he Open Journal 29 | volume: "7" 30 | number: "73" 31 | pages: "4278" 32 | year: "2022" 33 | 34 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS.md 2 | include CONTRIBUTING.md 3 | include HISTORY.md 4 | include LICENSE 5 | include README.md 6 | 7 | recursive-include tests * 8 | recursive-include direct *.pyx *.pxd *.pxi *.py *.h *.ini *.npy *.txt *.in *.md 9 | recursive-exclude * __pycache__ 10 | recursive-exclude * *.py[co] 11 | 12 | recursive-include docs *.rst *.md conf.py Makefile make.bat *.jpg *.png *.gif 13 | -------------------------------------------------------------------------------- /authors.rst: -------------------------------------------------------------------------------- 1 | Credits 2 | ======= 3 | 4 | Development Lead 5 | ---------------- 6 | * Jonas Teuwen j.teuwen@nki.nl 7 | * George Yiasemis g.yiasemis@nki.nl 8 | 9 | Contributors 10 | ------------ 11 | * Nikita Moriakov n.moriakov@nki.nl 12 | * Dimitrios Karkalousos d.karkalousos@amsterdamumc.nl 13 | * Matthan W.A. Caan m.w.a.caan@amsterdamumc.nl 14 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | range: 50..90 # coverage lower than 50 is red, higher than 90 green, between color code 3 | status: 4 | project: # settings affecting project coverage 5 | default: 6 | target: auto # auto % coverage target 7 | threshold: 1% # allow for 1% reduction of coverage without failing 8 | # do not run coverage on patch nor changes 9 | patch: false 10 | -------------------------------------------------------------------------------- /direct/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | __author__ = """direct contributors""" 15 | __version__ = "2.1.0" 16 | -------------------------------------------------------------------------------- /direct/algorithms/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Direct module for traditional mathematical optimization techniques, general or mri-specific.""" 15 | -------------------------------------------------------------------------------- /direct/cli/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """DIRECT Command-line interface.""" 15 | -------------------------------------------------------------------------------- /direct/cli/cli.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """DIRECT Command-line interface. 15 | 16 | This is the file which builds the main parser. 17 | """ 18 | 19 | import argparse 20 | import sys 21 | 22 | 23 | def main(): 24 | """Console script for direct.""" 25 | # From https://stackoverflow.com/questions/17073688/how-to-use-argparse-subparsers-correctly 26 | root_parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) 27 | 28 | root_subparsers = root_parser.add_subparsers(help="Direct CLI utils to run.") 29 | root_subparsers.required = True 30 | root_subparsers.dest = "subcommand" 31 | 32 | # Prevent circular imports 33 | from direct.cli.predict import register_parser as register_predict_subcommand 34 | from direct.cli.train import register_parser as register_train_subcommand 35 | from direct.cli.upload import register_parser as register_upload_subcommand 36 | 37 | # Training images related commands. 38 | register_train_subcommand(root_subparsers) 39 | # Inference images related commands. 40 | register_predict_subcommand(root_subparsers) 41 | # Data related comments. 42 | register_upload_subcommand(root_subparsers) 43 | 44 | args = root_parser.parse_args() 45 | args.subcommand(args) 46 | 47 | 48 | if __name__ == "__main__": 49 | sys.exit(main()) 50 | -------------------------------------------------------------------------------- /direct/cli/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import argparse 15 | import pathlib 16 | import sys 17 | 18 | from direct.types import FileOrUrl, PathOrString 19 | from direct.utils.io import check_is_valid_url 20 | 21 | 22 | def is_file(path): 23 | path = pathlib.Path(path) 24 | if path.is_file(): 25 | return path 26 | raise argparse.ArgumentTypeError(f"{path} is not a valid file or url.") 27 | 28 | 29 | def file_or_url(path: PathOrString) -> FileOrUrl: 30 | if check_is_valid_url(path): 31 | return FileOrUrl(path) 32 | path = pathlib.Path(path) 33 | if path.is_file(): 34 | return FileOrUrl(path) 35 | raise argparse.ArgumentTypeError(f"{path} is not a valid file or url.") 36 | 37 | 38 | def check_train_val(key, name): 39 | if key is not None and len(key) != 2: 40 | sys.exit(f"--{name} has to be of the form `train_folder, validation_folder` if a validation folder is set.") 41 | -------------------------------------------------------------------------------- /direct/common/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Common utilities for DIRECT.""" 15 | -------------------------------------------------------------------------------- /direct/common/subsample_config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from __future__ import annotations 15 | 16 | from dataclasses import dataclass 17 | from typing import Optional 18 | 19 | from omegaconf import MISSING 20 | 21 | from direct.config.defaults import BaseConfig 22 | from direct.types import MaskFuncMode 23 | 24 | 25 | @dataclass 26 | class MaskingConfig(BaseConfig): 27 | name: str = MISSING 28 | accelerations: tuple[float, ...] = (5.0,) 29 | center_fractions: Optional[tuple[float, ...]] = (0.1,) 30 | uniform_range: bool = False 31 | mode: MaskFuncMode = MaskFuncMode.STATIC 32 | 33 | val_accelerations: tuple[float, ...] = (5.0, 10.0) 34 | val_center_fractions: Optional[tuple[float, ...]] = (0.1, 0.05) 35 | -------------------------------------------------------------------------------- /direct/config/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from dataclasses import dataclass 15 | 16 | 17 | @dataclass 18 | class BaseConfig: 19 | pass 20 | -------------------------------------------------------------------------------- /direct/constants.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | COMPLEX_SIZE = 2 15 | -------------------------------------------------------------------------------- /direct/data/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """DIRECT data module.""" 15 | -------------------------------------------------------------------------------- /direct/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import logging 15 | 16 | 17 | class DirectException(BaseException): 18 | def __init__(self, *args, **kwargs): 19 | super().__init__() 20 | self.logger = logging.getLogger(__name__) 21 | 22 | 23 | class ProcessKilledException(DirectException): 24 | """The process received SIGINT signal.""" 25 | 26 | def __init__(self, signal_id: int, signal_name: str): 27 | """ 28 | Parameters 29 | ---------- 30 | signal_id: str 31 | signal_name: str 32 | """ 33 | super().__init__() 34 | self.logger.exception( 35 | f"Received signal (signal_id = {signal_id} - signal_name = {signal_name}). Critical. Process will stop." 36 | ) 37 | self.signal_id = signal_id 38 | self.signal_name = signal_name 39 | 40 | 41 | class TrainingException(DirectException): 42 | def __init__(self, message=None): 43 | super().__init__() 44 | if message: 45 | self.logger.exception("TrainingException") 46 | else: 47 | self.logger.exception(f"TrainingException: {message}") 48 | 49 | 50 | class ItemNotFoundException(DirectException): 51 | def __init__(self, item_name, message=None): 52 | super().__init__() 53 | error_name = "".join([s.capitalize() for s in item_name.split(" ")]) + "Exception" 54 | if message: 55 | self.logger.exception(error_name) 56 | else: 57 | self.logger.exception("%s: %s", error_name, message) 58 | -------------------------------------------------------------------------------- /direct/functionals/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """direct.nn.functionals module. 15 | 16 | This module contains functionals for the direct package as well as the loss 17 | functions needed for training models.""" 18 | 19 | __all__ = [ 20 | "HFENL1Loss", 21 | "HFENL2Loss", 22 | "HFENLoss", 23 | "NMAELoss", 24 | "NMSELoss", 25 | "NRMSELoss", 26 | "PSNRLoss", 27 | "SNRLoss", 28 | "SSIM3DLoss", 29 | "SSIMLoss", 30 | "SobelGradL1Loss", 31 | "SobelGradL2Loss", 32 | "batch_psnr", 33 | "calgary_campinas_psnr", 34 | "calgary_campinas_ssim", 35 | "calgary_campinas_vif", 36 | "fastmri_nmse", 37 | "fastmri_psnr", 38 | "fastmri_ssim", 39 | "hfen_l1", 40 | "hfen_l2", 41 | "snr_metric", 42 | ] 43 | 44 | from direct.functionals.challenges import * 45 | from direct.functionals.grad import * 46 | from direct.functionals.hfen import * 47 | from direct.functionals.nmae import NMAELoss 48 | from direct.functionals.nmse import * 49 | from direct.functionals.psnr import * 50 | from direct.functionals.snr import * 51 | from direct.functionals.ssim import * 52 | -------------------------------------------------------------------------------- /direct/functionals/nmae.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import torch 15 | import torch.nn as nn 16 | 17 | __all__ = ["NMAELoss"] 18 | 19 | 20 | class NMAELoss(nn.Module): 21 | r"""Computes the Normalized Mean Absolute Error (NMAE), i.e.: 22 | 23 | .. math:: 24 | 25 | \frac{||u - v||_1}{||u||_1}, 26 | 27 | where :math:`u` and :math:`v` denote the target and the input. 28 | """ 29 | 30 | def __init__(self, reduction="mean") -> None: 31 | """Inits :class:`NMAELoss` 32 | 33 | Parameters 34 | ---------- 35 | reduction: str 36 | Specifies the reduction to apply to the output. Can be "none", "mean" or "sum". 37 | Note that "mean" or "sum" will yield the same output. Default: "mean". 38 | """ 39 | super().__init__() 40 | self.mae_loss = nn.L1Loss(reduction=reduction) 41 | 42 | def forward(self, input: torch.Tensor, target: torch.Tensor): 43 | """Forward method of :class:`NMAELoss`. 44 | 45 | Parameters 46 | ---------- 47 | input: torch.Tensor 48 | Tensor of shape (*), where * means any number of dimensions. 49 | target: torch.Tensor 50 | Tensor of same shape as the input. 51 | """ 52 | return self.mae_loss(input, target) / self.mae_loss( 53 | torch.zeros_like(target, dtype=target.dtype, device=target.device), target 54 | ) 55 | -------------------------------------------------------------------------------- /direct/nn/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/cirim/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/cirim/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from dataclasses import dataclass 15 | 16 | from direct.config.defaults import ModelConfig 17 | 18 | 19 | @dataclass 20 | class CIRIMConfig(ModelConfig): 21 | time_steps: int = 8 # :math:`T` 22 | depth: int = 2 23 | recurrent_hidden_channels: int = 64 24 | num_cascades: int = 8 25 | no_parameter_sharing: bool = True 26 | -------------------------------------------------------------------------------- /direct/nn/conjgradnet/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/conjgradnet/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from dataclasses import dataclass 15 | from typing import Optional 16 | 17 | from direct.config.defaults import ModelConfig 18 | from direct.nn.conjgradnet.conjgrad import CGUpdateType 19 | from direct.nn.types import ActivationType, InitType, ModelName 20 | 21 | 22 | @dataclass 23 | class ConjGradNetConfig(ModelConfig): 24 | num_steps: int = 8 25 | image_init: str = InitType.ZEROS 26 | no_parameter_sharing: bool = True 27 | cg_tol: float = 1e-7 28 | cg_iters: int = 10 29 | cg_param_update_type: str = CGUpdateType.FR 30 | denoiser_architecture: str = ModelName.RESNET 31 | resnet_hidden_channels: int = 128 32 | resnet_num_blocks: int = 15 33 | resenet_batchnorm: bool = True 34 | resenet_scale: Optional[float] = 0.1 35 | unet_num_filters: Optional[int] = 32 36 | unet_num_pool_layers: Optional[int] = 4 37 | unet_dropout: Optional[float] = 0.0 38 | didn_hidden_channels: Optional[int] = 16 39 | didn_num_dubs: Optional[int] = 6 40 | didn_num_convs_recon: Optional[int] = 9 41 | conv_hidden_channels: Optional[int] = 64 42 | conv_n_convs: Optional[int] = 15 43 | conv_activation: Optional[str] = ActivationType.RELU 44 | conv_batchnorm: Optional[bool] = False 45 | -------------------------------------------------------------------------------- /direct/nn/conv/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/crossdomain/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/didn/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/iterdualnet/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/iterdualnet/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from dataclasses import dataclass 15 | 16 | from direct.config.defaults import ModelConfig 17 | 18 | 19 | @dataclass 20 | class IterDualNetConfig(ModelConfig): 21 | num_iter: int = 10 22 | image_normunet: bool = False 23 | kspace_normunet: bool = False 24 | image_unet_num_filters: int = 8 25 | image_unet_num_pool_layers: int = 4 26 | image_unet_dropout: float = 0.0 27 | kspace_unet_num_filters: int = 8 28 | kspace_unet_num_pool_layers: int = 4 29 | kspace_unet_dropout: float = 0.0 30 | image_no_parameter_sharing: bool = True 31 | kspace_no_parameter_sharing: bool = False 32 | compute_per_coil: bool = True 33 | -------------------------------------------------------------------------------- /direct/nn/jointicnet/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/jointicnet/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from dataclasses import dataclass 15 | 16 | from direct.config.defaults import ModelConfig 17 | 18 | 19 | @dataclass 20 | class JointICNetConfig(ModelConfig): 21 | num_iter: int = 10 22 | use_norm_unet: bool = False 23 | image_unet_num_filters: int = 8 24 | image_unet_num_pool_layers: int = 4 25 | image_unet_dropout: float = 0.0 26 | kspace_unet_num_filters: int = 8 27 | kspace_unet_num_pool_layers: int = 4 28 | kspace_unet_dropout: float = 0.0 29 | sens_unet_num_filters: int = 8 30 | sens_unet_num_pool_layers: int = 4 31 | sens_unet_dropout: float = 0.0 32 | -------------------------------------------------------------------------------- /direct/nn/jointicnet/jointicnet_engine.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from typing import Any, Callable, Dict, Optional, Tuple 16 | 17 | import torch 18 | from torch import nn 19 | 20 | from direct.config import BaseConfig 21 | from direct.nn.mri_models import MRIModelEngine 22 | 23 | 24 | class JointICNetEngine(MRIModelEngine): 25 | """Joint-ICNet Engine.""" 26 | 27 | def __init__( 28 | self, 29 | cfg: BaseConfig, 30 | model: nn.Module, 31 | device: str, 32 | forward_operator: Optional[Callable] = None, 33 | backward_operator: Optional[Callable] = None, 34 | mixed_precision: bool = False, 35 | **models: nn.Module, 36 | ): 37 | """Inits :class:`JointICNetEngine`.""" 38 | super().__init__( 39 | cfg, 40 | model, 41 | device, 42 | forward_operator=forward_operator, 43 | backward_operator=backward_operator, 44 | mixed_precision=mixed_precision, 45 | **models, 46 | ) 47 | 48 | def forward_function(self, data: Dict[str, Any]) -> Tuple[torch.Tensor, None]: 49 | output_image = self.model( 50 | masked_kspace=data["masked_kspace"], 51 | sampling_mask=data["sampling_mask"], 52 | sensitivity_map=data["sensitivity_map"], 53 | ) # shape (batch, height, width) 54 | 55 | output_kspace = None 56 | 57 | return output_image, output_kspace 58 | -------------------------------------------------------------------------------- /direct/nn/kikinet/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/kikinet/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from dataclasses import dataclass 15 | 16 | from direct.config.defaults import ModelConfig 17 | 18 | 19 | @dataclass 20 | class KIKINetConfig(ModelConfig): 21 | num_iter: int = 10 22 | image_model_architecture: str = "MWCNN" 23 | kspace_model_architecture: str = "UNET" 24 | image_mwcnn_hidden_channels: int = 16 25 | image_mwcnn_num_scales: int = 4 26 | image_mwcnn_bias: bool = True 27 | image_mwcnn_batchnorm: bool = False 28 | image_unet_num_filters: int = 8 29 | image_unet_num_pool_layers: int = 4 30 | image_unet_dropout_probability: float = 0.0 31 | kspace_conv_hidden_channels: int = 16 32 | kspace_conv_n_convs: int = 4 33 | kspace_conv_batchnorm: bool = False 34 | kspace_didn_hidden_channels: int = 64 35 | kspace_didn_num_dubs: int = 6 36 | kspace_didn_num_convs_recon: int = 9 37 | kspace_unet_num_filters: int = 8 38 | kspace_unet_num_pool_layers: int = 4 39 | kspace_unet_dropout_probability: float = 0.0 40 | normalize: bool = False 41 | -------------------------------------------------------------------------------- /direct/nn/kikinet/kikinet_engine.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from typing import Any, Callable, Dict, Optional, Tuple 16 | 17 | import torch 18 | from torch import nn 19 | 20 | from direct.config import BaseConfig 21 | from direct.nn.mri_models import MRIModelEngine 22 | 23 | 24 | class KIKINetEngine(MRIModelEngine): 25 | """KIKINet Engine.""" 26 | 27 | def __init__( 28 | self, 29 | cfg: BaseConfig, 30 | model: nn.Module, 31 | device: str, 32 | forward_operator: Optional[Callable] = None, 33 | backward_operator: Optional[Callable] = None, 34 | mixed_precision: bool = False, 35 | **models: nn.Module, 36 | ): 37 | """Inits :class:`KIKINetEngine.""" 38 | super().__init__( 39 | cfg, 40 | model, 41 | device, 42 | forward_operator=forward_operator, 43 | backward_operator=backward_operator, 44 | mixed_precision=mixed_precision, 45 | **models, 46 | ) 47 | 48 | def forward_function(self, data: Dict[str, Any]) -> Tuple[torch.Tensor, None]: 49 | output_image = self.model( 50 | masked_kspace=data["masked_kspace"], 51 | sampling_mask=data["sampling_mask"], 52 | sensitivity_map=data["sensitivity_map"], 53 | scaling_factor=data["scaling_factor"], 54 | ) # shape (batch, height, width, complex[=2]) 55 | 56 | output_kspace = None 57 | 58 | return output_image, output_kspace 59 | -------------------------------------------------------------------------------- /direct/nn/lpd/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/lpd/config.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # Copyright (c) DIRECT Contributors 3 | from dataclasses import dataclass 4 | 5 | from direct.config.defaults import ModelConfig 6 | 7 | 8 | @dataclass 9 | class LPDNetConfig(ModelConfig): 10 | num_iter: int = 25 11 | num_primal: int = 5 12 | num_dual: int = 5 13 | primal_model_architecture: str = "MWCNN" 14 | dual_model_architecture: str = "DIDN" 15 | primal_mwcnn_hidden_channels: int = 16 16 | primal_mwcnn_num_scales: int = 4 17 | primal_mwcnn_bias: bool = True 18 | primal_mwcnn_batchnorm: bool = False 19 | primal_unet_num_filters: int = 8 20 | primal_unet_num_pool_layers: int = 4 21 | primal_unet_dropout_probability: float = 0.0 22 | dual_conv_hidden_channels: int = 16 23 | dual_conv_n_convs: int = 4 24 | dual_conv_batchnorm: bool = False 25 | dual_didn_hidden_channels: int = 64 26 | dual_didn_num_dubs: int = 6 27 | dual_didn_num_convs_recon: int = 9 28 | dual_unet_num_filters: int = 8 29 | dual_unet_num_pool_layers: int = 4 30 | dual_unet_dropout_probability: float = 0.0 31 | -------------------------------------------------------------------------------- /direct/nn/lpd/lpd_engine.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from typing import Any, Callable, Dict, Optional, Tuple 15 | 16 | import torch 17 | from torch import nn 18 | 19 | from direct.config import BaseConfig 20 | from direct.nn.mri_models import MRIModelEngine 21 | 22 | 23 | class LPDNetEngine(MRIModelEngine): 24 | """LPDNet Engine.""" 25 | 26 | def __init__( 27 | self, 28 | cfg: BaseConfig, 29 | model: nn.Module, 30 | device: str, 31 | forward_operator: Optional[Callable] = None, 32 | backward_operator: Optional[Callable] = None, 33 | mixed_precision: bool = False, 34 | **models: nn.Module, 35 | ): 36 | """Inits :class:`LPDNetEngine.""" 37 | super().__init__( 38 | cfg, 39 | model, 40 | device, 41 | forward_operator=forward_operator, 42 | backward_operator=backward_operator, 43 | mixed_precision=mixed_precision, 44 | **models, 45 | ) 46 | 47 | def forward_function(self, data: Dict[str, Any]) -> Tuple[torch.Tensor, None]: 48 | data["sensitivity_map"] = self.compute_sensitivity_map(data["sensitivity_map"]) 49 | 50 | output_image = self.model( 51 | masked_kspace=data["masked_kspace"], 52 | sampling_mask=data["sampling_mask"], 53 | sensitivity_map=data["sensitivity_map"], 54 | ) # shape (batch, height, width) 55 | return output_image, None 56 | -------------------------------------------------------------------------------- /direct/nn/mobilenet/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/mobilenet/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from dataclasses import dataclass 15 | from typing import List, Optional 16 | 17 | from direct.config.defaults import ModelConfig 18 | 19 | 20 | @dataclass 21 | class MobileNetV2Config(ModelConfig): 22 | num_channels: int = 2 23 | num_classes: int = 1000 24 | width_mult: float = 1.0 25 | inverted_residual_setting: Optional[List] = None 26 | round_nearest: int = 8 27 | # block = None 28 | norm_layer: Optional[str] = None 29 | -------------------------------------------------------------------------------- /direct/nn/multidomainnet/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/multidomainnet/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from dataclasses import dataclass 15 | 16 | from direct.config.defaults import ModelConfig 17 | 18 | 19 | @dataclass 20 | class MultiDomainNetConfig(ModelConfig): 21 | standardization: bool = True 22 | num_filters: int = 16 23 | num_pool_layers: int = 4 24 | dropout_probability: float = 0.0 25 | -------------------------------------------------------------------------------- /direct/nn/multidomainnet/multidomainnet_engine.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from typing import Any, Callable, Dict, Optional, Tuple 15 | 16 | import torch 17 | from torch import nn 18 | from torch.cuda.amp import autocast 19 | 20 | import direct.data.transforms as T 21 | from direct.config import BaseConfig 22 | from direct.engine import DoIterationOutput 23 | from direct.nn.mri_models import MRIModelEngine 24 | from direct.utils import detach_dict, dict_to_device, reduce_list_of_dicts 25 | 26 | 27 | class MultiDomainNetEngine(MRIModelEngine): 28 | """Multi Domain Network Engine.""" 29 | 30 | def __init__( 31 | self, 32 | cfg: BaseConfig, 33 | model: nn.Module, 34 | device: str, 35 | forward_operator: Optional[Callable] = None, 36 | backward_operator: Optional[Callable] = None, 37 | mixed_precision: bool = False, 38 | **models: nn.Module, 39 | ): 40 | """Inits :class:`MultiDomainNetEngine.""" 41 | super().__init__( 42 | cfg, 43 | model, 44 | device, 45 | forward_operator=forward_operator, 46 | backward_operator=backward_operator, 47 | mixed_precision=mixed_precision, 48 | **models, 49 | ) 50 | 51 | def forward_function(self, data: Dict[str, Any]) -> Tuple[torch.Tensor, None]: 52 | output_multicoil_image = self.model( 53 | masked_kspace=data["masked_kspace"], 54 | sensitivity_map=data["sensitivity_map"], 55 | ) 56 | output_image = T.root_sum_of_squares( 57 | output_multicoil_image, self._coil_dim, self._complex_dim 58 | ) # shape (batch, height, width) 59 | 60 | output_kspace = None 61 | 62 | return output_image, output_kspace 63 | -------------------------------------------------------------------------------- /direct/nn/mwcnn/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/recurrent/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/recurrentvarnet/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/recurrentvarnet/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from dataclasses import dataclass 15 | from typing import Optional, Tuple 16 | 17 | from direct.config.defaults import ModelConfig 18 | from direct.nn.types import InitType 19 | 20 | 21 | @dataclass 22 | class RecurrentVarNetConfig(ModelConfig): 23 | num_steps: int = 15 # :math:`T` 24 | recurrent_hidden_channels: int = 64 25 | recurrent_num_layers: int = 4 # :math:`n_l` 26 | no_parameter_sharing: bool = True 27 | learned_initializer: bool = True 28 | initializer_initialization: Optional[str] = InitType.SENSE 29 | initializer_channels: Optional[Tuple[int, ...]] = (32, 32, 64, 64) # :math:`n_d` 30 | initializer_dilations: Optional[Tuple[int, ...]] = (1, 1, 2, 4) # :math:`p` 31 | initializer_multiscale: int = 1 32 | normalized: bool = False 33 | -------------------------------------------------------------------------------- /direct/nn/recurrentvarnet/recurrentvarnet_engine.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from typing import Any, Callable, Dict, Optional, Tuple 16 | 17 | import torch 18 | from torch import nn 19 | 20 | import direct.data.transforms as T 21 | from direct.config import BaseConfig 22 | from direct.nn.mri_models import MRIModelEngine 23 | 24 | 25 | class RecurrentVarNetEngine(MRIModelEngine): 26 | """Recurrent Variational Network Engine.""" 27 | 28 | def __init__( 29 | self, 30 | cfg: BaseConfig, 31 | model: nn.Module, 32 | device: str, 33 | forward_operator: Optional[Callable] = None, 34 | backward_operator: Optional[Callable] = None, 35 | mixed_precision: bool = False, 36 | **models: nn.Module, 37 | ): 38 | """Inits :class:`RecurrentVarNetEngine.""" 39 | super().__init__( 40 | cfg, 41 | model, 42 | device, 43 | forward_operator=forward_operator, 44 | backward_operator=backward_operator, 45 | mixed_precision=mixed_precision, 46 | **models, 47 | ) 48 | 49 | def forward_function(self, data: Dict[str, Any]) -> Tuple[torch.Tensor, torch.Tensor]: 50 | output_kspace = self.model( 51 | masked_kspace=data["masked_kspace"], 52 | sampling_mask=data["sampling_mask"], 53 | sensitivity_map=data["sensitivity_map"], 54 | ) 55 | output_kspace = T.apply_padding(output_kspace, data.get("padding", None)) 56 | 57 | output_image = T.root_sum_of_squares( 58 | self.backward_operator(output_kspace, dim=self._spatial_dims), 59 | dim=self._coil_dim, 60 | ) # shape (batch, height, width) 61 | 62 | return output_image, output_kspace 63 | -------------------------------------------------------------------------------- /direct/nn/resnet/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/resnet/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from dataclasses import dataclass 15 | from typing import Optional 16 | 17 | from direct.config.defaults import ModelConfig 18 | 19 | 20 | @dataclass 21 | class ResNetConfig(ModelConfig): 22 | in_channels: int = 2 23 | out_channels: Optional[int] = None 24 | hidden_channels: int = 32 25 | num_blocks: int = 15 26 | batchnorm: bool = True 27 | scale: Optional[float] = 0.1 28 | image_init: str = "sense" 29 | -------------------------------------------------------------------------------- /direct/nn/rim/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/rim/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from dataclasses import dataclass 15 | from typing import Optional, Tuple 16 | 17 | from direct.config.defaults import ModelConfig 18 | 19 | 20 | @dataclass 21 | class RIMConfig(ModelConfig): 22 | hidden_channels: int = 16 23 | length: int = 8 24 | depth: int = 2 25 | steps: int = 1 26 | no_parameter_sharing: bool = False 27 | instance_norm: bool = False 28 | dense_connect: bool = False 29 | whiten_input: bool = False 30 | replication_padding: bool = True 31 | image_initialization: str = "zero_filled" 32 | scale_loglikelihood: Optional[float] = None 33 | learned_initializer: bool = False 34 | initializer_channels: Tuple[int, ...] = (32, 32, 64, 64) 35 | initializer_dilations: Tuple[int, ...] = (1, 1, 2, 4) 36 | initializer_multiscale: int = 1 37 | normalized: bool = False 38 | -------------------------------------------------------------------------------- /direct/nn/ssl/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/transformers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """DIRECT transformers models.""" 15 | -------------------------------------------------------------------------------- /direct/nn/types.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """direct.nn.types module.""" 15 | 16 | from direct.types import DirectEnum 17 | 18 | 19 | class ActivationType(DirectEnum): 20 | RELU = "relu" 21 | PRELU = "prelu" 22 | LEAKY_RELU = "leaky_relu" 23 | 24 | 25 | class ModelName(DirectEnum): 26 | UNET = "unet" 27 | NORMUNET = "normunet" 28 | RESNET = "resnet" 29 | DIDN = "didn" 30 | CONV = "conv" 31 | 32 | 33 | class InitType(DirectEnum): 34 | INPUT_IMAGE = "input_image" 35 | SENSE = "sense" 36 | ZERO_FILLED = "zero_filled" 37 | ZEROS = "zeros" 38 | 39 | 40 | class LossFunType(DirectEnum): 41 | L1_LOSS = "l1_loss" 42 | KSPACE_L1_LOSS = "kspace_l1_loss" 43 | L2_LOSS = "l2_loss" 44 | KSPACE_L2_LOSS = "kspace_l2_loss" 45 | SSIM_LOSS = "ssim_loss" 46 | SSIM_3D_LOSS = "ssim_3d_loss" 47 | GRAD_L1_LOSS = "grad_l1_loss" 48 | GRAD_L2_LOSS = "grad_l2_loss" 49 | NMSE_LOSS = "nmse_loss" 50 | KSPACE_NMSE_LOSS = "kspace_nmse_loss" 51 | NRMSE_LOSS = "nrmse_loss" 52 | KSPACE_NRMSE_LOSS = "kspace_nrmse_loss" 53 | NMAE_LOSS = "nmae_loss" 54 | KSPACE_NMAE_LOSS = "kspace_nmae_loss" 55 | SNR_LOSS = "snr_loss" 56 | PSNR_LOSS = "psnr_loss" 57 | HFEN_L1_LOSS = "hfen_l1_loss" 58 | HFEN_L2_LOSS = "hfen_l2_loss" 59 | HFEN_L1_NORM_LOSS = "hfen_l1_norm_loss" 60 | HFEN_L2_NORM_LOSS = "hfen_l2_norm_loss" 61 | -------------------------------------------------------------------------------- /direct/nn/unet/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from direct.nn.unet.unet_2d import UnetModel2d 15 | -------------------------------------------------------------------------------- /direct/nn/unet/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from dataclasses import dataclass 15 | 16 | from direct.config.defaults import ModelConfig 17 | from direct.nn.types import InitType 18 | 19 | 20 | @dataclass 21 | class UnetModel2dConfig(ModelConfig): 22 | in_channels: int = 2 23 | out_channels: int = 2 24 | num_filters: int = 16 25 | num_pool_layers: int = 4 26 | dropout_probability: float = 0.0 27 | 28 | 29 | class NormUnetModel2dConfig(ModelConfig): 30 | in_channels: int = 2 31 | out_channels: int = 2 32 | num_filters: int = 16 33 | num_pool_layers: int = 4 34 | dropout_probability: float = 0.0 35 | norm_groups: int = 2 36 | 37 | 38 | @dataclass 39 | class Unet2dConfig(ModelConfig): 40 | num_filters: int = 16 41 | num_pool_layers: int = 4 42 | dropout_probability: float = 0.0 43 | skip_connection: bool = False 44 | normalized: bool = False 45 | image_initialization: InitType = InitType.ZERO_FILLED 46 | 47 | 48 | @dataclass 49 | class UnetModel3dConfig(ModelConfig): 50 | in_channels: int = 2 51 | out_channels: int = 2 52 | num_filters: int = 16 53 | num_pool_layers: int = 4 54 | dropout_probability: float = 0.0 55 | -------------------------------------------------------------------------------- /direct/nn/varnet/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/varnet/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from dataclasses import dataclass 15 | 16 | from direct.config.defaults import ModelConfig 17 | 18 | 19 | @dataclass 20 | class EndToEndVarNetConfig(ModelConfig): 21 | num_layers: int = 8 22 | regularizer_num_filters: int = 18 23 | regularizer_num_pull_layers: int = 4 24 | regularizer_dropout: float = 0.0 25 | -------------------------------------------------------------------------------- /direct/nn/varsplitnet/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/vsharp/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/xpdnet/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /direct/nn/xpdnet/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from dataclasses import dataclass 15 | 16 | from direct.config.defaults import ModelConfig 17 | 18 | 19 | @dataclass 20 | class XPDNetConfig(ModelConfig): 21 | num_primal: int = 5 22 | num_dual: int = 1 23 | num_iter: int = 10 24 | use_primal_only: bool = True 25 | kspace_model_architecture: str = "CONV" 26 | dual_conv_hidden_channels: int = 16 27 | dual_conv_n_convs: int = 4 28 | dual_conv_batchnorm: bool = False 29 | dual_didn_hidden_channels: int = 64 30 | dual_didn_num_dubs: int = 6 31 | dual_didn_num_convs_recon: int = 9 32 | mwcnn_hidden_channels: int = 16 33 | mwcnn_num_scales: int = 4 34 | mwcnn_bias: bool = True 35 | mwcnn_batchnorm: bool = False 36 | normalize: bool = False 37 | -------------------------------------------------------------------------------- /direct/nn/xpdnet/xpdnet_engine.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from typing import Any, Callable, Dict, Optional, Tuple 16 | 17 | import torch 18 | from torch import nn 19 | 20 | from direct.config import BaseConfig 21 | from direct.nn.mri_models import MRIModelEngine 22 | 23 | 24 | class XPDNetEngine(MRIModelEngine): 25 | """XPDNet Engine.""" 26 | 27 | def __init__( 28 | self, 29 | cfg: BaseConfig, 30 | model: nn.Module, 31 | device: str, 32 | forward_operator: Optional[Callable] = None, 33 | backward_operator: Optional[Callable] = None, 34 | mixed_precision: bool = False, 35 | **models: nn.Module, 36 | ): 37 | """Inits :class:`XPDNetEngine.""" 38 | super().__init__( 39 | cfg, 40 | model, 41 | device, 42 | forward_operator=forward_operator, 43 | backward_operator=backward_operator, 44 | mixed_precision=mixed_precision, 45 | **models, 46 | ) 47 | 48 | def forward_function(self, data: Dict[str, Any]) -> Tuple[torch.Tensor, None]: 49 | output_image = self.model( 50 | masked_kspace=data["masked_kspace"], 51 | sampling_mask=data["sampling_mask"], 52 | sensitivity_map=data["sensitivity_map"], 53 | scaling_factor=data["scaling_factor"], 54 | ) # shape (batch, height, width, complex[=2]) 55 | 56 | output_kspace = None 57 | 58 | return output_image, output_kspace 59 | -------------------------------------------------------------------------------- /direct/ssl/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Direct SSL module. 15 | 16 | Includes functions for self-supervised learning for MRI reconstruction. 17 | """ 18 | -------------------------------------------------------------------------------- /direct/utils/imports.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """General utilities for module imports.""" 15 | 16 | from importlib.util import find_spec 17 | 18 | 19 | def _module_available(module_path: str) -> bool: 20 | """Check if a path is available in your environment. 21 | 22 | >>> _module_available('os') 23 | True 24 | >>> _module_available('bla.bla') 25 | False 26 | Adapted from: https://github.com/PyTorchLightning/pytorch-lightning/blob/ef7d41692ca04bb9877da5c743f80fceecc6a100/pytorch_lightning/utilities/imports.py#L27 27 | Under Apache 2.0 license. 28 | """ 29 | try: 30 | return find_spec(module_path) is not None 31 | except ModuleNotFoundError: 32 | return False 33 | -------------------------------------------------------------------------------- /direct/utils/models.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | from collections import OrderedDict 15 | 16 | 17 | def fix_state_dict_module_prefix(state_dict): 18 | """If models are saved after being wrapped in e.g. DataParallel, the keys of the state dict are prefixed with 19 | `module.`. This function removes this prefix. 20 | 21 | Parameters 22 | ---------- 23 | state_dict: dict 24 | state_dict of a network module 25 | Returns 26 | ------- 27 | dict 28 | """ 29 | if list(state_dict.keys())[0].startswith("module."): 30 | new_ordered_dict = OrderedDict() 31 | for _, (k, v) in enumerate(state_dict.items()): 32 | name = k[7:] 33 | new_ordered_dict[name] = v 34 | state_dict = new_ordered_dict 35 | 36 | return state_dict 37 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG CUDA="11.3.0" 2 | ARG PYTORCH="1.10" 3 | ARG PYTHON="3.8" 4 | 5 | # TODO: conda installs its own version of cuda 6 | FROM nvidia/cuda:${CUDA}-devel-ubuntu18.04 7 | 8 | ENV CUDA_PATH /usr/local/cuda 9 | ENV CUDA_ROOT /usr/local/cuda/bin 10 | ENV LD_LIBRARY_PATH /usr/local/nvidia/lib64 11 | 12 | # libsm6 and libxext6 are needed for cv2 13 | RUN apt-get -qq update && apt-get install -y --no-install-recommends libxext6 libsm6 libxrender1 \ 14 | build-essential sudo libgl1-mesa-glx git wget rsync tmux nano dcmtk fftw3-dev liblapacke-dev \ 15 | libpng-dev libopenblas-dev jq \ 16 | && rm -rf /var/lib/apt/lists/* \ 17 | && ldconfig 18 | 19 | RUN git clone https://github.com/mrirecon/bart.git /tmp/bart \ 20 | && cd /tmp/bart \ 21 | && make -j4 \ 22 | && make install \ 23 | && rm -rf /tmp/bart 24 | 25 | # Make a user 26 | # Rename /home to /users to prevent issues with singularity 27 | RUN mkdir /users \ 28 | && adduser --disabled-password --gecos '' --home /users/direct direct \ 29 | && adduser direct sudo \ 30 | && echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers 31 | USER direct 32 | 33 | WORKDIR /tmp 34 | RUN wget -q https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh \ 35 | && bash Miniconda3-latest-Linux-x86_64.sh -b \ 36 | && rm -f Miniconda3-latest-Linux-x86_64.sh 37 | 38 | ENV PATH "/users/direct/miniconda3/bin:/tmp/bart/:$PATH:$CUDA_ROOT" 39 | 40 | # Setup python packages 41 | RUN conda update -n base conda -yq \ 42 | && conda install python=${PYTHON} \ 43 | && conda install jupyter \ 44 | && conda install cudatoolkit=${CUDA} torchvision -c pytorch 45 | 46 | RUN if [ "nightly$PYTORCH" = "nightly" ] ; then echo "Installing pytorch nightly" && \ 47 | conda install pytorch -c pytorch-nightly; else conda install pytorch=${PYTORCH} -c pytorch ; fi 48 | 49 | USER root 50 | RUN mkdir direct:direct /direct && chown direct:direct /direct && chmod 777 /direct 51 | 52 | USER direct 53 | 54 | RUN jupyter notebook --generate-config 55 | ENV CONFIG_PATH "/users/direct/.jupyter/jupyter_notebook_config.py" 56 | COPY "docker/jupyter_notebook_config.py" ${CONFIG_PATH} 57 | 58 | # Copy files into the docker 59 | COPY [".", "/direct"] 60 | WORKDIR /direct 61 | USER root 62 | RUN python -m pip install -e ".[dev]" 63 | USER direct 64 | 65 | ENV PYTHONPATH /tmp/bart/python:/direct 66 | 67 | # Provide an open entrypoint for the docker 68 | ENTRYPOINT $0 $@ 69 | -------------------------------------------------------------------------------- /docker/README.rst: -------------------------------------------------------------------------------- 1 | Docker Installation 2 | ------------------- 3 | 4 | Use the container 5 | ~~~~~~~~~~~~~~~~~ 6 | 7 | To build the image: 8 | 9 | .. code-block:: bash 10 | 11 | cd direct/ 12 | docker build -t direct:latest -f docker/Dockerfile . 13 | 14 | To run `DIRECT` using all GPUs: 15 | 16 | .. code-block:: bash 17 | 18 | docker run --gpus all -it \ 19 | --shm-size=24gb --volume=:/data --volume=:/output \ 20 | --name=direct direct:latest /bin/bash 21 | 22 | Requirements 23 | ~~~~~~~~~~~~ 24 | 25 | * docker ≥ 19.03 26 | -------------------------------------------------------------------------------- /docker/jupyter_notebook_config.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import os 3 | 4 | from IPython.lib import passwd 5 | 6 | c = get_config() # type: ignore 7 | c.NotebookApp.ip = "0.0.0.0" 8 | c.NotebookApp.port = int(os.getenv("PORT", 8888)) 9 | c.NotebookApp.open_browser = False 10 | 11 | password = os.environ.get("PASSWORD", False) 12 | if password: 13 | c.NotebookApp.password = passwd(password) 14 | else: 15 | c.NotebookApp.password = "" 16 | c.NotebookApp.token = "" 17 | 18 | try: 19 | del os.environ["PASSWORD"] 20 | except KeyError: 21 | pass 22 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NKI-AI/direct/50b9704d2d7aa8eede763ced92e71b69baa8189d/docs/.nojekyll -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = direct 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/authors.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../authors.rst 2 | -------------------------------------------------------------------------------- /docs/calgary_campinas.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../projects/calgary_campinas/README.rst 2 | -------------------------------------------------------------------------------- /docs/cmrxrecon.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../projects/CMRxRecon/README.rst 2 | -------------------------------------------------------------------------------- /docs/colab.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: shell 2 | 3 | ============================ 4 | Use DIRECT with Google Colab 5 | ============================ 6 | 7 | 8 | 9 | 1. First mount your Google drive in Colab and create a directory named, e.g. `DIRECT`, and `cd` there: 10 | 11 | .. code-block:: ipython3 12 | 13 | %cd /content/drive/MyDrive/DIRECT/ 14 | 15 | This `notebook `_ can help with mounting your Google drive. 16 | 17 | 18 | 2. Clone the repo: 19 | 20 | .. code-block:: ipython3 21 | 22 | !git clone https://github.com/NKI-AI/direct.git 23 | 24 | 3. Copy paste and run the following 25 | 26 | .. code-block:: ipython3 27 | 28 | !wget -O mini.sh https://repo.anaconda.com/miniconda/Miniconda3-py38_4.8.2-Linux-x86_64.sh 29 | !chmod +x mini.sh 30 | !bash ./mini.sh -b -f -p /usr/local 31 | !conda install -q -y jupyter 32 | !conda install -q -y google-colab -c conda-forge 33 | !python -m ipykernel install --name "py38" --user 34 | 35 | The above block is needed to install python 3.8 in Colab as it runs using Python 3.7. 36 | 37 | 4. Run the following to install the latest `PyTorch` version: 38 | 39 | .. code-block:: ipython3 40 | 41 | !pip3 uninstall torch 42 | !pip3 uninstall torchvision 43 | !pip3 uninstall torchaudio 44 | !pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu113 45 | 46 | 5. Navigate to the repo: 47 | 48 | .. code-block:: ipython3 49 | 50 | %cd direct/ 51 | 52 | 6. Install package. 53 | 54 | .. code-block:: ipython3 55 | 56 | !python3 setup.py install 57 | 58 | OR 59 | 60 | .. code-block:: ipython3 61 | 62 | !python3 -m pip install -e ".[dev]" 63 | 64 | 7. Run experiments using the configuration files in the `projects `_ folder, 65 | or you can set up your own configuration files following our `template `_. 66 | -------------------------------------------------------------------------------- /docs/cvpr2022_recurrentvarnet.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../projects/cvpr2022_recurrentvarnet/README.rst 2 | -------------------------------------------------------------------------------- /docs/examples.rst: -------------------------------------------------------------------------------- 1 | .. include:: ./examples/calgary_campinas_dataset.rst 2 | -------------------------------------------------------------------------------- /docs/ext/doi_role.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # Copyright (c) DIRECT Contributors 3 | """ 4 | doilinks 5 | ~~~~~~~~ 6 | Extension to add links to DOIs. With this extension you can use e.g. 7 | :doi:`10.1016/S0022-2836(05)80360-2` in your documents. This will 8 | create a link to a DOI resolver 9 | (``https://doi.org/10.1016/S0022-2836(05)80360-2``). 10 | The link caption will be the raw DOI. 11 | You can also give an explicit caption, e.g. 12 | :doi:`Basic local alignment search tool <10.1016/S0022-2836(05)80360-2>`. 13 | 14 | :copyright: Copyright 2015 Jon Lund Steffensen. Based on extlinks by 15 | the Sphinx team. 16 | :license: BSD. 17 | """ 18 | 19 | from docutils import nodes, utils 20 | from sphinx.util.nodes import split_explicit_title 21 | 22 | 23 | def doi_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): 24 | text = utils.unescape(text) 25 | has_explicit_title, title, part = split_explicit_title(text) 26 | full_url = "https://doi.org/" + part 27 | if not has_explicit_title: 28 | title = "DOI:" + part 29 | pnode = nodes.reference(title, title, internal=False, refuri=full_url) 30 | return [pnode], [] 31 | 32 | 33 | def arxiv_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): 34 | text = utils.unescape(text) 35 | has_explicit_title, title, part = split_explicit_title(text) 36 | full_url = "https://arxiv.org/abs/" + part 37 | if not has_explicit_title: 38 | title = "arXiv:" + part 39 | pnode = nodes.reference(title, title, internal=False, refuri=full_url) 40 | return [pnode], [] 41 | 42 | 43 | def setup_link_role(app): 44 | app.add_role("doi", doi_role, override=True) 45 | app.add_role("DOI", doi_role, override=True) 46 | app.add_role("arXiv", arxiv_role, override=True) 47 | app.add_role("arxiv", arxiv_role, override=True) 48 | 49 | 50 | def setup(app): 51 | app.connect("builder-inited", setup_link_role) 52 | return {"version": "0.1", "parallel_read_safe": True} 53 | -------------------------------------------------------------------------------- /docs/getting_started.rst: -------------------------------------------------------------------------------- 1 | Quick Start 2 | =========== 3 | This gives a brief quick start - introduction on how to download public datasets such as the Calgary-Campinas and FastMRI multi-coil MRI data and train models implemented in ``DIRECT``. 4 | 5 | 1. Downloading and Preparing MRI datasets 6 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 7 | 8 | The Multi-coil Calgary-Campinas dataset can be obtained following the instructions in the `Calgary Campinas website `_ and the FastMRI dataset can be obtained from the `FastMRI website `_ by filling in their form. 9 | Data should be arranged into training and validation folders. The testing set is not strictly required, and definitely not during training, if you do not want to compute the 10 | test set results. 11 | 12 | **Note:** Preferably use a fast drive, for instance an SSD to store these files to make sure to get the maximal performance. 13 | 14 | 2. Install ``DIRECT`` 15 | ^^^^^^^^^^^^^^^^^^^^^ 16 | 17 | Follow the instructions in `installation `_ in the docs. 18 | 19 | 3. Training and Inference 20 | ^^^^^^^^^^^^^^^^^^^^^^^^^ 21 | 22 | 3.1 Preparing a configuration file 23 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24 | To run experiments a configuration file must be created. For a sample configuration file please refer to `config `_ in the docs. 25 | 26 | 3.2 Projects 27 | ~~~~~~~~~~~~ 28 | In the `projects folder `_ folder you can find examples of baseline configurations for our experiments. 29 | 30 | Instructions on how to train a model or perform inference can be found in `training `_ in the docs. 31 | -------------------------------------------------------------------------------- /docs/history.rst: -------------------------------------------------------------------------------- 1 | History 2 | ======= 3 | 4 | **07 Jan 2022**: Release of version `1.0.0 `_. 5 | **22 Feb 2022**: Release of version `1.0.1 `_. 6 | **19 Oct 2022**: Release of version `1.0.2 `_. 7 | **24 Oct 2022**: Release of version `1.0.3 `_. 8 | **19 Oct 2022**: Release of version `1.0.4 `_. 9 | **02 Apr 2024**: Release of version `2.0.0 `_. 10 | **03 Jul 2024**: Release of version `2.1.0 `_. 11 | 12 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | :github_url: https://github.com/NKI-AI/direct/ 2 | 3 | 4 | DIRECT documentation 5 | ==================== 6 | DIRECT is a Python, end-to-end pipeline for solving Inverse Problems emerging in medical imaging. 7 | It is built with `PyTorch `_ and stores state-of-the-art Deep Learning imaging inverse problem solvers such as denoising, 8 | dealiasing and reconstruction. 9 | By defining a base forward linear or non-linear operator, DIRECT can be used for training models for recovering 10 | images such as MRIs from partially observed or noisy input data. 11 | 12 | 13 | 14 | .. toctree:: 15 | :maxdepth: 1 16 | :caption: Index 17 | 18 | installation 19 | getting_started 20 | authors 21 | history 22 | 23 | .. toctree:: 24 | :maxdepth: 1 25 | :caption: Training and inference 26 | 27 | training 28 | inference 29 | config 30 | tensorboard 31 | 32 | .. toctree:: 33 | :maxdepth: 1 34 | :caption: Add more Datasets and Sub-Samplers 35 | 36 | datasets 37 | samplers 38 | 39 | .. toctree:: 40 | :maxdepth: 1 41 | :caption: Add more Models 42 | 43 | models 44 | 45 | .. toctree:: 46 | :maxdepth: 1 47 | :caption: Examples 48 | 49 | examples.rst 50 | colab.rst 51 | 52 | .. toctree:: 53 | :maxdepth: 1 54 | :caption: Model Zoo 55 | 56 | model_zoo.rst 57 | 58 | .. toctree:: 59 | :maxdepth: 1 60 | :caption: Projects 61 | 62 | calgary_campinas 63 | spie2022_radial_subsampling 64 | cvpr2022_recurrentvarnet 65 | shepp_logan 66 | cmrxrecon 67 | jssl 68 | 69 | .. toctree:: 70 | :maxdepth: 2 71 | :caption: API Documentation 72 | 73 | modules 74 | 75 | 76 | Indices and tables 77 | ================== 78 | * :ref:`genindex` 79 | * :ref:`modindex` 80 | * :ref:`search` 81 | -------------------------------------------------------------------------------- /docs/inference.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: shell 2 | 3 | ========= 4 | Inference 5 | ========= 6 | 7 | After training a model, you can use the ``direct predict`` command to perform inference. 8 | 9 | To perform inference on a single machine run the following code block in your linux machine: 10 | 11 | .. code-block:: bash 12 | 13 | $ direct predict --cfg --checkpoint \ 14 | --num-gpus [--data-root ] [ --cfg .yaml --other-flags ] 15 | 16 | To predict using multiple machines run the following code (one command on each machine): 17 | 18 | .. code-block:: bash 19 | 20 | (machine0)$ direct predict --cfg --checkpoint \ 21 | --machine-rank 0 --num-machines 2 --dist-url [--data-root ] [--other-flags] 22 | (machine1)$ direct predict --cfg --checkpoint \ 23 | --machine-rank 1 --num-machines 2 --dist-url [--data-root ] [--other-flags] 24 | 25 | The ``cfg_path_or_url`` should point to a configuration file that includes all the model parameters used for the trained 26 | model checkpoint ``checkpoint_path_or_url`` and should also include an inference configuration as follows: 27 | 28 | .. code-block:: yaml 29 | 30 | inference: 31 | dataset: 32 | name: InferenceDataset 33 | 34 | transforms: 35 | masking: 36 | ... 37 | ... 38 | text_description: 39 | ... 40 | batch_size: 41 | ... 42 | 43 | Notes 44 | ----- 45 | Note that if the `dataset` you want to use requires downloaded path, you can pass the `--data-root` argument which 46 | points to the data directory. 47 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../installation.rst 2 | -------------------------------------------------------------------------------- /docs/jssl.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../projects/JSSL/README.rst 2 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=python -msphinx 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=direct 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The Sphinx module was not found. Make sure you have Sphinx installed, 20 | echo.then set the SPHINXBUILD environment variable to point to the full 21 | echo.path of the 'sphinx-build' executable. Alternatively you may add the 22 | echo.Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/modules.rst: -------------------------------------------------------------------------------- 1 | direct 2 | ====== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | direct 8 | -------------------------------------------------------------------------------- /docs/shepp_logan.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../projects/toy/shepp_logan/README.rst 2 | -------------------------------------------------------------------------------- /docs/spie2022_radial_subsampling.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../projects/spie2022_radial_subsampling/README.rst 2 | -------------------------------------------------------------------------------- /docs/tensorboard.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: shell 2 | 3 | =========== 4 | Tensorboard 5 | =========== 6 | 7 | To visualize training and validation metrics of an experiment in 8 | Tensorboard on your local machine run 9 | 10 | .. code-block:: bash 11 | 12 | tensorboard --logdir --port 13 | 14 | If you are working on a remote host and want to visualize the experiment 15 | on your local machine: 16 | 17 | 1. Run ``tensorboard --logdir --port `` on the remote host, and 18 | 2. Run ``ssh -N -f -L localhost::localhost: `` on your local machine. 19 | 3. Navigate to ``http://localhost:`` on your local machine. 20 | 21 | Example 22 | ======= 23 | +--------------------------------------------------------------+ 24 | | |direct_tensorboard| | 25 | +==============================================================+ 26 | | Tensorboard snippet of visualised validation reconstructions | 27 | +--------------------------------------------------------------+ 28 | 29 | .. |direct_tensorboard| image:: https://user-images.githubusercontent.com/71031687/137918503-84b894e4-b9db-42cd-8e94-03bb098171fa.gif 30 | -------------------------------------------------------------------------------- /installation.rst: -------------------------------------------------------------------------------- 1 | 2 | Installation 3 | ============ 4 | 5 | Requirements 6 | ------------ 7 | 8 | 9 | * CUDA ≥ 10.2 supported GPU. 10 | * Linux with Python ≥ 3.8 11 | * PyTorch ≥ 1.6 12 | 13 | Install using Docker 14 | -------------------- 15 | 16 | We provide a `Dockerfile `_ which install DIRECT with a few commands. While recommended due to the use of specific 17 | pytorch features, DIRECT should also work in a virtual environment. 18 | 19 | .. include:: ../docker/README.rst 20 | 21 | Install using ``conda`` 22 | --------------------------- 23 | 24 | 25 | #. 26 | First, install conda. Here is a guide on how to install conda on Linux if you don't already have it `here `_. If you downloaded conda for the first time it is possible that you will need to restart your machine. Once you have conda, create a python 3.9 conda environment: 27 | 28 | .. code-block:: 29 | 30 | conda create -n myenv python=3.9 31 | 32 | Then, activate the virtual environment ``myenv`` you created where you will install the software: 33 | 34 | .. code-block:: 35 | 36 | conda activate myenv 37 | 38 | #. 39 | If you are using GPUs, cuda is required for the project to run. To install `PyTorch `_ with cuda run: 40 | 41 | .. code-block:: 42 | 43 | pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu116 44 | 45 | **otherwise**\ , install the latest PyTorch CPU version (not recommended): 46 | 47 | .. code-block:: 48 | 49 | pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cpu 50 | 51 | #. 52 | Clone the repository using ``git clone`` and navigate to ``direct/direct/`` and run 53 | 54 | .. code-block:: 55 | 56 | python3 setup.py install 57 | 58 | or 59 | 60 | .. code-block:: 61 | 62 | python3 -m pip install -e ".[dev]" 63 | 64 | This will install ``direct`` as a python module. 65 | 66 | Common Installation Issues 67 | -------------------------- 68 | 69 | If you met issues using DIRECT, please first update the repository to the latest version, and rebuild the docker. When 70 | this does not work, create a GitHub issue so we can see whether this is a bug, or an installation problem. 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "dayjs": "^1.11.11", 4 | "moment": "*" 5 | }, 6 | "devDependencies": { 7 | "@eslint/js": "^9.16.0", 8 | "@types/node": "^18", 9 | "eslint": "^9.16.0", 10 | "prettier": "^2.8.7", 11 | "prettier-plugin-gherkin": "^1.1.0", 12 | "prettier-plugin-sql": "^0.14.0", 13 | "@prettier/plugin-xml": "^0.10.0", 14 | "stylelint": "^16", 15 | "stylelint-config-standard": "^36.0.1", 16 | "typescript": "4.9.5", 17 | "typescript-eslint": "^8.17.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /prettier.config.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * @see https://prettier.io/docs/en/configuration.html 3 | */ 4 | const config = { 5 | tabWidth: 2, 6 | printWidth: 80, 7 | plugins: [ 8 | require("prettier-plugin-sql"), 9 | require("@prettier/plugin-xml"), 10 | require("prettier-plugin-gherkin"), 11 | ], 12 | }; 13 | 14 | module.exports = config; 15 | 16 | -------------------------------------------------------------------------------- /projects/BUILD.bazel: -------------------------------------------------------------------------------- 1 | filegroup( 2 | name = "all_configs", 3 | srcs = glob(["**/*.yaml"]), 4 | visibility = ["//visibility:public"], 5 | ) 6 | -------------------------------------------------------------------------------- /projects/CMRxRecon/lists/132x448_cine_train.lst: -------------------------------------------------------------------------------- 1 | P046_cine_sax.mat 2 | P040_cine_sax.mat 3 | P114_cine_lax.mat 4 | P104_cine_lax.mat -------------------------------------------------------------------------------- /projects/CMRxRecon/lists/132x448_cine_with_masks_train.lst: -------------------------------------------------------------------------------- 1 | with_masks_P040_cine_sax.mat 2 | with_masks_P104_cine_lax.mat 3 | with_masks_P114_cine_lax.mat 4 | with_masks_P046_cine_sax.mat -------------------------------------------------------------------------------- /projects/CMRxRecon/lists/162x512_cine_train.lst: -------------------------------------------------------------------------------- 1 | P053_cine_sax.mat 2 | P008_cine_sax.mat 3 | P038_cine_sax.mat 4 | P077_cine_sax.mat 5 | P004_cine_sax.mat 6 | P050_cine_sax.mat 7 | P024_cine_sax.mat 8 | P114_cine_sax.mat 9 | P036_cine_sax.mat 10 | P037_cine_sax.mat 11 | P109_cine_sax.mat -------------------------------------------------------------------------------- /projects/CMRxRecon/lists/162x512_cine_with_masks_train.lst: -------------------------------------------------------------------------------- 1 | with_masks_P004_cine_sax.mat 2 | with_masks_P077_cine_sax.mat 3 | with_masks_P024_cine_sax.mat 4 | with_masks_P008_cine_sax.mat 5 | with_masks_P053_cine_sax.mat 6 | with_masks_P038_cine_sax.mat 7 | with_masks_P050_cine_sax.mat 8 | with_masks_P036_cine_sax.mat 9 | with_masks_P109_cine_sax.mat 10 | with_masks_P037_cine_sax.mat 11 | with_masks_P114_cine_sax.mat -------------------------------------------------------------------------------- /projects/CMRxRecon/lists/168x448_cine_train.lst: -------------------------------------------------------------------------------- 1 | P033_cine_lax.mat 2 | P079_cine_lax.mat 3 | P023_cine_lax.mat 4 | P058_cine_lax.mat 5 | P048_cine_lax.mat 6 | P039_cine_lax.mat 7 | P112_cine_lax.mat 8 | P078_cine_lax.mat 9 | P061_cine_lax.mat 10 | P120_cine_lax.mat 11 | P043_cine_lax.mat 12 | P007_cine_lax.mat 13 | P055_cine_lax.mat 14 | P080_cine_lax.mat 15 | P093_cine_lax.mat 16 | P116_cine_lax.mat 17 | P037_cine_lax.mat 18 | P047_cine_lax.mat 19 | P115_cine_lax.mat 20 | P011_cine_lax.mat 21 | P096_cine_lax.mat 22 | P010_cine_lax.mat 23 | P008_cine_lax.mat 24 | P006_cine_lax.mat 25 | P092_cine_lax.mat 26 | P068_cine_lax.mat 27 | P041_cine_lax.mat 28 | P028_cine_lax.mat 29 | P077_cine_lax.mat 30 | P095_cine_lax.mat 31 | P016_cine_lax.mat 32 | P054_cine_lax.mat 33 | P070_cine_lax.mat 34 | P029_cine_lax.mat 35 | P064_cine_lax.mat 36 | P024_cine_lax.mat 37 | P014_cine_lax.mat 38 | P035_cine_lax.mat 39 | P030_cine_lax.mat 40 | P018_cine_lax.mat 41 | P101_cine_lax.mat 42 | P102_cine_lax.mat 43 | P057_cine_lax.mat 44 | P009_cine_lax.mat 45 | P091_cine_lax.mat 46 | P042_cine_lax.mat 47 | -------------------------------------------------------------------------------- /projects/CMRxRecon/lists/168x448_cine_with_masks_train.lst: -------------------------------------------------------------------------------- 1 | with_masks_P092_cine_lax.mat 2 | with_masks_P024_cine_lax.mat 3 | with_masks_P018_cine_lax.mat 4 | with_masks_P041_cine_lax.mat 5 | with_masks_P055_cine_lax.mat 6 | with_masks_P116_cine_lax.mat 7 | with_masks_P093_cine_lax.mat 8 | with_masks_P096_cine_lax.mat 9 | with_masks_P014_cine_lax.mat 10 | with_masks_P070_cine_lax.mat 11 | with_masks_P078_cine_lax.mat 12 | with_masks_P057_cine_lax.mat 13 | with_masks_P043_cine_lax.mat 14 | with_masks_P030_cine_lax.mat 15 | with_masks_P023_cine_lax.mat 16 | with_masks_P009_cine_lax.mat 17 | with_masks_P115_cine_lax.mat 18 | with_masks_P068_cine_lax.mat 19 | with_masks_P112_cine_lax.mat 20 | with_masks_P064_cine_lax.mat 21 | with_masks_P029_cine_lax.mat 22 | with_masks_P101_cine_lax.mat 23 | with_masks_P061_cine_lax.mat 24 | with_masks_P008_cine_lax.mat 25 | with_masks_P077_cine_lax.mat 26 | with_masks_P006_cine_lax.mat 27 | with_masks_P033_cine_lax.mat 28 | with_masks_P054_cine_lax.mat 29 | with_masks_P048_cine_lax.mat 30 | with_masks_P047_cine_lax.mat 31 | with_masks_P037_cine_lax.mat 32 | with_masks_P042_cine_lax.mat 33 | with_masks_P028_cine_lax.mat 34 | with_masks_P079_cine_lax.mat 35 | with_masks_P039_cine_lax.mat 36 | with_masks_P095_cine_lax.mat 37 | with_masks_P120_cine_lax.mat 38 | with_masks_P010_cine_lax.mat 39 | with_masks_P102_cine_lax.mat 40 | with_masks_P007_cine_lax.mat 41 | with_masks_P035_cine_lax.mat 42 | with_masks_P016_cine_lax.mat 43 | with_masks_P058_cine_lax.mat 44 | with_masks_P091_cine_lax.mat 45 | with_masks_P011_cine_lax.mat 46 | with_masks_P080_cine_lax.mat -------------------------------------------------------------------------------- /projects/CMRxRecon/lists/204x448_cine_train.lst: -------------------------------------------------------------------------------- 1 | P001_cine_lax.mat 2 | P060_cine_lax.mat 3 | P097_cine_lax.mat 4 | P066_cine_lax.mat 5 | P053_cine_lax.mat 6 | P109_cine_lax.mat 7 | P069_cine_lax.mat 8 | P004_cine_lax.mat 9 | P036_cine_lax.mat 10 | P107_cine_lax.mat 11 | P074_cine_lax.mat 12 | P075_cine_lax.mat 13 | P106_cine_lax.mat 14 | P013_cine_lax.mat 15 | P071_cine_lax.mat 16 | P072_cine_lax.mat 17 | P050_cine_lax.mat 18 | P012_cine_lax.mat 19 | P056_cine_lax.mat 20 | P034_cine_lax.mat 21 | P098_cine_lax.mat 22 | P052_cine_lax.mat 23 | P002_cine_lax.mat 24 | P119_cine_lax.mat 25 | P118_cine_lax.mat 26 | P020_cine_lax.mat 27 | P015_cine_lax.mat 28 | P062_cine_lax.mat 29 | P019_cine_lax.mat 30 | P032_cine_lax.mat 31 | P067_cine_lax.mat 32 | P017_cine_lax.mat 33 | P076_cine_lax.mat 34 | P046_cine_lax.mat 35 | P063_cine_lax.mat 36 | P005_cine_lax.mat 37 | P099_cine_lax.mat 38 | P110_cine_lax.mat 39 | P113_cine_lax.mat 40 | P038_cine_lax.mat 41 | P025_cine_lax.mat 42 | P031_cine_lax.mat 43 | P094_cine_lax.mat 44 | P065_cine_lax.mat 45 | P105_cine_lax.mat 46 | P045_cine_lax.mat -------------------------------------------------------------------------------- /projects/CMRxRecon/lists/204x448_cine_with_masks_train.lst: -------------------------------------------------------------------------------- 1 | with_masks_P036_cine_lax.mat 2 | with_masks_P031_cine_lax.mat 3 | with_masks_P113_cine_lax.mat 4 | with_masks_P075_cine_lax.mat 5 | with_masks_P072_cine_lax.mat 6 | with_masks_P105_cine_lax.mat 7 | with_masks_P032_cine_lax.mat 8 | with_masks_P045_cine_lax.mat 9 | with_masks_P099_cine_lax.mat 10 | with_masks_P056_cine_lax.mat 11 | with_masks_P038_cine_lax.mat 12 | with_masks_P062_cine_lax.mat 13 | with_masks_P001_cine_lax.mat 14 | with_masks_P015_cine_lax.mat 15 | with_masks_P094_cine_lax.mat 16 | with_masks_P066_cine_lax.mat 17 | with_masks_P013_cine_lax.mat 18 | with_masks_P005_cine_lax.mat 19 | with_masks_P071_cine_lax.mat 20 | with_masks_P060_cine_lax.mat 21 | with_masks_P107_cine_lax.mat 22 | with_masks_P050_cine_lax.mat 23 | with_masks_P106_cine_lax.mat 24 | with_masks_P017_cine_lax.mat 25 | with_masks_P063_cine_lax.mat 26 | with_masks_P119_cine_lax.mat 27 | with_masks_P097_cine_lax.mat 28 | with_masks_P053_cine_lax.mat 29 | with_masks_P067_cine_lax.mat 30 | with_masks_P074_cine_lax.mat 31 | with_masks_P004_cine_lax.mat 32 | with_masks_P065_cine_lax.mat 33 | with_masks_P034_cine_lax.mat 34 | with_masks_P025_cine_lax.mat 35 | with_masks_P012_cine_lax.mat 36 | with_masks_P109_cine_lax.mat 37 | with_masks_P046_cine_lax.mat 38 | with_masks_P019_cine_lax.mat 39 | with_masks_P098_cine_lax.mat 40 | with_masks_P118_cine_lax.mat 41 | with_masks_P020_cine_lax.mat 42 | with_masks_P002_cine_lax.mat 43 | with_masks_P110_cine_lax.mat 44 | with_masks_P076_cine_lax.mat 45 | with_masks_P052_cine_lax.mat 46 | with_masks_P069_cine_lax.mat -------------------------------------------------------------------------------- /projects/CMRxRecon/lists/204x512_cine_train.lst: -------------------------------------------------------------------------------- 1 | P020_cine_sax.mat 2 | P095_cine_sax.mat 3 | P001_cine_sax.mat 4 | P073_cine_sax.mat 5 | P064_cine_sax.mat 6 | P066_cine_sax.mat 7 | P023_cine_sax.mat 8 | P072_cine_sax.mat 9 | P035_cine_sax.mat 10 | P018_cine_sax.mat 11 | P042_cine_sax.mat 12 | P101_cine_sax.mat 13 | P005_cine_sax.mat 14 | P070_cine_sax.mat 15 | P028_cine_sax.mat 16 | P031_cine_sax.mat 17 | P098_cine_sax.mat 18 | P116_cine_sax.mat 19 | P091_cine_sax.mat 20 | P118_cine_sax.mat 21 | P062_cine_sax.mat 22 | P117_cine_sax.mat 23 | P029_cine_sax.mat 24 | P045_cine_sax.mat 25 | P059_cine_sax.mat 26 | P105_cine_sax.mat 27 | P048_cine_sax.mat 28 | P074_cine_sax.mat 29 | P030_cine_sax.mat 30 | P032_cine_sax.mat 31 | P076_cine_sax.mat 32 | P006_cine_sax.mat -------------------------------------------------------------------------------- /projects/CMRxRecon/lists/204x512_cine_with_masks_train.lst: -------------------------------------------------------------------------------- 1 | with_masks_P116_cine_sax.mat 2 | with_masks_P023_cine_sax.mat 3 | with_masks_P095_cine_sax.mat 4 | with_masks_P006_cine_sax.mat 5 | with_masks_P030_cine_sax.mat 6 | with_masks_P005_cine_sax.mat 7 | with_masks_P001_cine_sax.mat 8 | with_masks_P062_cine_sax.mat 9 | with_masks_P059_cine_sax.mat 10 | with_masks_P064_cine_sax.mat 11 | with_masks_P028_cine_sax.mat 12 | with_masks_P098_cine_sax.mat 13 | with_masks_P031_cine_sax.mat 14 | with_masks_P048_cine_sax.mat 15 | with_masks_P045_cine_sax.mat 16 | with_masks_P032_cine_sax.mat 17 | with_masks_P076_cine_sax.mat 18 | with_masks_P118_cine_sax.mat 19 | with_masks_P073_cine_sax.mat 20 | with_masks_P117_cine_sax.mat 21 | with_masks_P101_cine_sax.mat 22 | with_masks_P042_cine_sax.mat 23 | with_masks_P105_cine_sax.mat 24 | with_masks_P070_cine_sax.mat 25 | with_masks_P074_cine_sax.mat 26 | with_masks_P018_cine_sax.mat 27 | with_masks_P091_cine_sax.mat 28 | with_masks_P020_cine_sax.mat 29 | with_masks_P035_cine_sax.mat 30 | with_masks_P029_cine_sax.mat 31 | with_masks_P066_cine_sax.mat 32 | with_masks_P072_cine_sax.mat -------------------------------------------------------------------------------- /projects/CMRxRecon/lists/246x512_cine_train.lst: -------------------------------------------------------------------------------- 1 | P055_cine_sax.mat 2 | P014_cine_sax.mat 3 | P097_cine_sax.mat 4 | P113_cine_sax.mat 5 | P119_cine_sax.mat 6 | P011_cine_sax.mat 7 | P021_cine_sax.mat 8 | P013_cine_sax.mat 9 | P052_cine_sax.mat 10 | P061_cine_sax.mat 11 | P099_cine_sax.mat 12 | P115_cine_sax.mat 13 | P104_cine_sax.mat 14 | P094_cine_sax.mat 15 | P009_cine_sax.mat 16 | P100_cine_sax.mat 17 | P025_cine_sax.mat 18 | P033_cine_sax.mat 19 | P003_cine_sax.mat 20 | P071_cine_sax.mat 21 | P022_cine_sax.mat 22 | P026_cine_sax.mat 23 | P007_cine_sax.mat 24 | P015_cine_sax.mat 25 | P027_cine_sax.mat 26 | P111_cine_sax.mat 27 | P067_cine_sax.mat 28 | P068_cine_sax.mat 29 | P078_cine_sax.mat 30 | P058_cine_sax.mat 31 | P054_cine_sax.mat 32 | P051_cine_sax.mat 33 | P107_cine_sax.mat 34 | P092_cine_sax.mat 35 | P110_cine_sax.mat 36 | P103_cine_sax.mat 37 | P044_cine_sax.mat 38 | P112_cine_sax.mat 39 | P096_cine_sax.mat 40 | P016_cine_sax.mat 41 | P017_cine_sax.mat 42 | P019_cine_sax.mat 43 | P010_cine_sax.mat 44 | P102_cine_sax.mat 45 | P057_cine_sax.mat 46 | P093_cine_sax.mat 47 | P034_cine_sax.mat 48 | P039_cine_sax.mat 49 | P012_cine_sax.mat 50 | P002_cine_sax.mat 51 | P079_cine_sax.mat 52 | P060_cine_sax.mat 53 | P065_cine_sax.mat 54 | P056_cine_sax.mat 55 | P041_cine_sax.mat 56 | P075_cine_sax.mat 57 | P108_cine_sax.mat 58 | P069_cine_sax.mat 59 | P120_cine_sax.mat 60 | P049_cine_sax.mat 61 | P043_cine_sax.mat 62 | P047_cine_sax.mat 63 | P106_cine_sax.mat 64 | P063_cine_sax.mat -------------------------------------------------------------------------------- /projects/CMRxRecon/lists/246x512_cine_with_masks_train.lst: -------------------------------------------------------------------------------- 1 | with_masks_P078_cine_sax.mat 2 | with_masks_P067_cine_sax.mat 3 | with_masks_P041_cine_sax.mat 4 | with_masks_P010_cine_sax.mat 5 | with_masks_P119_cine_sax.mat 6 | with_masks_P056_cine_sax.mat 7 | with_masks_P113_cine_sax.mat 8 | with_masks_P058_cine_sax.mat 9 | with_masks_P093_cine_sax.mat 10 | with_masks_P120_cine_sax.mat 11 | with_masks_P026_cine_sax.mat 12 | with_masks_P049_cine_sax.mat 13 | with_masks_P002_cine_sax.mat 14 | with_masks_P071_cine_sax.mat 15 | with_masks_P112_cine_sax.mat 16 | with_masks_P034_cine_sax.mat 17 | with_masks_P079_cine_sax.mat 18 | with_masks_P061_cine_sax.mat 19 | with_masks_P009_cine_sax.mat 20 | with_masks_P115_cine_sax.mat 21 | with_masks_P096_cine_sax.mat 22 | with_masks_P027_cine_sax.mat 23 | with_masks_P016_cine_sax.mat 24 | with_masks_P092_cine_sax.mat 25 | with_masks_P055_cine_sax.mat 26 | with_masks_P017_cine_sax.mat 27 | with_masks_P099_cine_sax.mat 28 | with_masks_P044_cine_sax.mat 29 | with_masks_P043_cine_sax.mat 30 | with_masks_P007_cine_sax.mat 31 | with_masks_P052_cine_sax.mat 32 | with_masks_P060_cine_sax.mat 33 | with_masks_P047_cine_sax.mat 34 | with_masks_P014_cine_sax.mat 35 | with_masks_P019_cine_sax.mat 36 | with_masks_P104_cine_sax.mat 37 | with_masks_P111_cine_sax.mat 38 | with_masks_P063_cine_sax.mat 39 | with_masks_P003_cine_sax.mat 40 | with_masks_P057_cine_sax.mat 41 | with_masks_P069_cine_sax.mat 42 | with_masks_P054_cine_sax.mat 43 | with_masks_P102_cine_sax.mat 44 | with_masks_P051_cine_sax.mat 45 | with_masks_P025_cine_sax.mat 46 | with_masks_P039_cine_sax.mat 47 | with_masks_P107_cine_sax.mat 48 | with_masks_P103_cine_sax.mat 49 | with_masks_P022_cine_sax.mat 50 | with_masks_P094_cine_sax.mat 51 | with_masks_P015_cine_sax.mat 52 | with_masks_P097_cine_sax.mat 53 | with_masks_P033_cine_sax.mat 54 | with_masks_P013_cine_sax.mat 55 | with_masks_P065_cine_sax.mat 56 | with_masks_P075_cine_sax.mat 57 | with_masks_P110_cine_sax.mat 58 | with_masks_P100_cine_sax.mat 59 | with_masks_P068_cine_sax.mat 60 | with_masks_P012_cine_sax.mat 61 | with_masks_P108_cine_sax.mat 62 | with_masks_P021_cine_sax.mat 63 | with_masks_P106_cine_sax.mat 64 | with_masks_P011_cine_sax.mat -------------------------------------------------------------------------------- /projects/JSSL/lists/test_prostate.lst: -------------------------------------------------------------------------------- 1 | file_prostate_AXT2_0004.h5 2 | file_prostate_AXT2_0091.h5 3 | file_prostate_AXT2_0178.h5 4 | file_prostate_AXT2_0222.h5 5 | file_prostate_AXT2_0299.h5 6 | file_prostate_AXT2_0028.h5 7 | file_prostate_AXT2_0100.h5 8 | file_prostate_AXT2_0180.h5 9 | file_prostate_AXT2_0224.h5 10 | file_prostate_AXT2_0300.h5 11 | file_prostate_AXT2_0036.h5 12 | file_prostate_AXT2_0104.h5 13 | file_prostate_AXT2_0183.h5 14 | file_prostate_AXT2_0225.h5 15 | file_prostate_AXT2_0304.h5 16 | file_prostate_AXT2_0037.h5 17 | file_prostate_AXT2_0125.h5 18 | file_prostate_AXT2_0184.h5 19 | file_prostate_AXT2_0256.h5 20 | file_prostate_AXT2_0305.h5 21 | file_prostate_AXT2_0038.h5 22 | file_prostate_AXT2_0135.h5 23 | file_prostate_AXT2_0193.h5 24 | file_prostate_AXT2_0265.h5 25 | file_prostate_AXT2_0307.h5 26 | file_prostate_AXT2_0049.h5 27 | file_prostate_AXT2_0136.h5 28 | file_prostate_AXT2_0196.h5 29 | file_prostate_AXT2_0269.h5 30 | file_prostate_AXT2_0312.h5 31 | file_prostate_AXT2_0050.h5 32 | file_prostate_AXT2_0138.h5 33 | file_prostate_AXT2_0204.h5 34 | file_prostate_AXT2_0273.h5 35 | file_prostate_AXT2_0052.h5 36 | file_prostate_AXT2_0140.h5 37 | file_prostate_AXT2_0207.h5 38 | file_prostate_AXT2_0275.h5 39 | file_prostate_AXT2_0061.h5 40 | file_prostate_AXT2_0165.h5 41 | file_prostate_AXT2_0212.h5 42 | file_prostate_AXT2_0281.h5 43 | file_prostate_AXT2_0086.h5 44 | file_prostate_AXT2_0168.h5 45 | file_prostate_AXT2_0215.h5 46 | file_prostate_AXT2_0289.h5 -------------------------------------------------------------------------------- /projects/JSSL/lists/train_brain_4_coils_512x276.lst: -------------------------------------------------------------------------------- 1 | file_brain_AXFLAIR_203_6000931.h5 2 | file_brain_AXFLAIR_203_6000941.h5 3 | file_brain_AXFLAIR_203_6000926.h5 4 | file_brain_AXFLAIR_203_6000927.h5 5 | file_brain_AXFLAIR_203_6000908.h5 6 | file_brain_AXFLAIR_203_6000940.h5 7 | file_brain_AXFLAIR_203_6000930.h5 8 | file_brain_AXFLAIR_203_6000942.h5 9 | file_brain_AXFLAIR_203_6000955.h5 10 | file_brain_AXFLAIR_203_6000954.h5 11 | file_brain_AXFLAIR_203_6000933.h5 12 | file_brain_AXFLAIR_203_6000920.h5 13 | file_brain_AXFLAIR_203_6000950.h5 14 | file_brain_AXFLAIR_203_6000947.h5 15 | file_brain_AXFLAIR_203_6000918.h5 16 | file_brain_AXFLAIR_203_6000937.h5 17 | file_brain_AXFLAIR_203_6000936.h5 18 | file_brain_AXFLAIR_203_6000946.h5 19 | file_brain_AXFLAIR_203_6000951.h5 20 | file_brain_AXFLAIR_203_6000934.h5 21 | file_brain_AXFLAIR_203_6000944.h5 22 | file_brain_AXFLAIR_203_6000945.h5 23 | file_brain_AXFLAIR_203_6000935.h5 24 | file_brain_AXFLAIR_203_6000922.h5 25 | file_brain_AXFLAIR_203_6000913.h5 26 | file_brain_AXFLAIR_203_6000963.h5 27 | file_brain_AXFLAIR_203_6000960.h5 28 | file_brain_AXFLAIR_203_6000910.h5 29 | file_brain_AXFLAIR_203_6000907.h5 30 | file_brain_AXFLAIR_203_6000958.h5 31 | file_brain_AXFLAIR_203_6000928.h5 32 | file_brain_AXFLAIR_203_6000929.h5 33 | file_brain_AXFLAIR_203_6000959.h5 34 | file_brain_AXFLAIR_203_6000911.h5 35 | file_brain_AXFLAIR_203_6000961.h5 36 | file_brain_AXFLAIR_203_6000915.h5 37 | file_brain_AXFLAIR_203_6000914.h5 38 | file_brain_AXFLAIR_203_6000964.h5 39 | file_brain_AXFLAIR_203_6000949.h5 40 | file_brain_AXFLAIR_203_6000916.h5 -------------------------------------------------------------------------------- /projects/JSSL/lists/train_brain_6_coils_640x320.lst: -------------------------------------------------------------------------------- 1 | file_brain_AXT2_208_2080131.h5 2 | file_brain_AXT2_207_2070279.h5 3 | file_brain_AXT2_207_2070256.h5 4 | file_brain_AXT2_208_2080228.h5 5 | file_brain_AXT2_208_2080126.h5 6 | file_brain_AXT2_208_2080682.h5 7 | file_brain_AXT2_207_2070194.h5 8 | file_brain_AXT2_207_2070350.h5 9 | file_brain_AXT2_207_2070417.h5 10 | file_brain_AXT2_208_2080554.h5 11 | file_brain_AXT2_207_2070017.h5 12 | file_brain_AXT2_208_2080367.h5 13 | file_brain_AXT2_208_2080051.h5 14 | file_brain_AXT2_208_2080201.h5 15 | file_brain_AXT2_207_2070013.h5 16 | file_brain_AXT1PRE_203_6000777.h5 17 | file_brain_AXT2_207_2070725.h5 18 | file_brain_AXT2_207_2070012.h5 19 | file_brain_AXT2_208_2080639.h5 20 | file_brain_AXT2_208_2080666.h5 21 | file_brain_AXT2_208_2080518.h5 22 | file_brain_AXT2_207_2070185.h5 23 | file_brain_AXT2_208_2080457.h5 24 | file_brain_AXT2_208_2080099.h5 25 | file_brain_AXT2_208_2080614.h5 26 | file_brain_AXT2_207_2070461.h5 27 | file_brain_AXT2_208_2080079.h5 28 | file_brain_AXT2_208_2080329.h5 29 | file_brain_AXT2_208_2080057.h5 30 | file_brain_AXT2_208_2080049.h5 31 | file_brain_AXT2_208_2080225.h5 32 | file_brain_AXT2_208_2080368.h5 33 | file_brain_AXT2_208_2080595.h5 34 | file_brain_AXT2_207_2070019.h5 35 | file_brain_AXT2_207_2070701.h5 36 | file_brain_AXT2_208_2080491.h5 37 | file_brain_AXT2_208_2080718.h5 38 | file_brain_AXT2_207_2070629.h5 39 | file_brain_AXT2_208_2080092.h5 40 | file_brain_AXT2_207_2070207.h5 41 | file_brain_AXT2_208_2080641.h5 42 | file_brain_AXT2_207_2070364.h5 43 | file_brain_AXT2_207_2070022.h5 44 | file_brain_AXT2_207_2070781.h5 45 | file_brain_AXT2_208_2080097.h5 46 | file_brain_AXT2_207_2070360.h5 47 | file_brain_AXT2_208_2080223.h5 48 | file_brain_AXT2_207_2070684.h5 49 | file_brain_AXT2_207_2070307.h5 50 | file_brain_AXT2_208_2080567.h5 51 | file_brain_AXT2_208_2080116.h5 -------------------------------------------------------------------------------- /projects/JSSL/lists/train_prostate_10_coils.lst: -------------------------------------------------------------------------------- 1 | file_prostate_AXT2_0023.h5 2 | -------------------------------------------------------------------------------- /projects/JSSL/lists/train_prostate_14_coils.lst: -------------------------------------------------------------------------------- 1 | file_prostate_AXT2_0190.h5 2 | file_prostate_AXT2_0174.h5 3 | file_prostate_AXT2_0233.h5 4 | file_prostate_AXT2_0002.h5 5 | file_prostate_AXT2_0147.h5 6 | file_prostate_AXT2_0290.h5 7 | file_prostate_AXT2_0056.h5 8 | -------------------------------------------------------------------------------- /projects/JSSL/lists/train_prostate_16_coils.lst: -------------------------------------------------------------------------------- 1 | file_prostate_AXT2_0007.h5 2 | file_prostate_AXT2_0139.h5 3 | file_prostate_AXT2_0005.h5 4 | file_prostate_AXT2_0243.h5 5 | file_prostate_AXT2_0015.h5 6 | file_prostate_AXT2_0003.h5 7 | file_prostate_AXT2_0227.h5 8 | file_prostate_AXT2_0151.h5 9 | file_prostate_AXT2_0123.h5 10 | file_prostate_AXT2_0310.h5 11 | file_prostate_AXT2_0306.h5 12 | file_prostate_AXT2_0068.h5 13 | file_prostate_AXT2_0020.h5 14 | file_prostate_AXT2_0154.h5 15 | -------------------------------------------------------------------------------- /projects/JSSL/lists/train_prostate_24_coils.lst: -------------------------------------------------------------------------------- 1 | file_prostate_AXT2_0101.h5 2 | file_prostate_AXT2_0221.h5 3 | file_prostate_AXT2_0033.h5 4 | file_prostate_AXT2_0213.h5 5 | -------------------------------------------------------------------------------- /projects/JSSL/lists/train_prostate_26_coils.lst: -------------------------------------------------------------------------------- 1 | file_prostate_AXT2_0080.h5 2 | file_prostate_AXT2_0253.h5 3 | file_prostate_AXT2_0097.h5 4 | file_prostate_AXT2_0102.h5 5 | file_prostate_AXT2_0170.h5 6 | file_prostate_AXT2_0246.h5 7 | file_prostate_AXT2_0255.h5 8 | file_prostate_AXT2_0197.h5 9 | file_prostate_AXT2_0106.h5 10 | file_prostate_AXT2_0208.h5 11 | file_prostate_AXT2_0025.h5 12 | file_prostate_AXT2_0270.h5 13 | file_prostate_AXT2_0043.h5 14 | file_prostate_AXT2_0119.h5 15 | file_prostate_AXT2_0030.h5 16 | file_prostate_AXT2_0122.h5 17 | file_prostate_AXT2_0145.h5 18 | file_prostate_AXT2_0280.h5 19 | file_prostate_AXT2_0126.h5 20 | file_prostate_AXT2_0277.h5 21 | file_prostate_AXT2_0199.h5 22 | file_prostate_AXT2_0296.h5 23 | file_prostate_AXT2_0130.h5 24 | file_prostate_AXT2_0035.h5 25 | file_prostate_AXT2_0157.h5 26 | file_prostate_AXT2_0045.h5 27 | file_prostate_AXT2_0155.h5 28 | file_prostate_AXT2_0301.h5 29 | file_prostate_AXT2_0142.h5 30 | file_prostate_AXT2_0021.h5 31 | file_prostate_AXT2_0051.h5 32 | file_prostate_AXT2_0046.h5 33 | file_prostate_AXT2_0019.h5 34 | file_prostate_AXT2_0282.h5 35 | -------------------------------------------------------------------------------- /projects/JSSL/lists/train_prostate_30_coils.lst: -------------------------------------------------------------------------------- 1 | file_prostate_AXT2_0268.h5 2 | file_prostate_AXT2_0026.h5 3 | file_prostate_AXT2_0009.h5 4 | file_prostate_AXT2_0228.h5 5 | file_prostate_AXT2_0294.h5 6 | -------------------------------------------------------------------------------- /projects/JSSL/lists/val_prostate.lst: -------------------------------------------------------------------------------- 1 | file_prostate_AXT2_0115.h5 2 | file_prostate_AXT2_0241.h5 3 | file_prostate_AXT2_0066.h5 4 | file_prostate_AXT2_0229.h5 5 | file_prostate_AXT2_0072.h5 6 | file_prostate_AXT2_0285.h5 7 | file_prostate_AXT2_0121.h5 8 | file_prostate_AXT2_0085.h5 9 | file_prostate_AXT2_0192.h5 10 | file_prostate_AXT2_0164.h5 11 | file_prostate_AXT2_0292.h5 12 | file_prostate_AXT2_0124.h5 13 | file_prostate_AXT2_0144.h5 14 | file_prostate_AXT2_0059.h5 15 | file_prostate_AXT2_0262.h5 16 | file_prostate_AXT2_0117.h5 17 | file_prostate_AXT2_0288.h5 18 | file_prostate_AXT2_0010.h5 19 | file_prostate_AXT2_0198.h5 20 | file_prostate_AXT2_0008.h5 21 | file_prostate_AXT2_0048.h5 22 | file_prostate_AXT2_0149.h5 23 | file_prostate_AXT2_0029.h5 24 | file_prostate_AXT2_0185.h5 25 | file_prostate_AXT2_0219.h5 26 | file_prostate_AXT2_0260.h5 27 | file_prostate_AXT2_0042.h5 28 | file_prostate_AXT2_0096.h5 29 | file_prostate_AXT2_0141.h5 30 | file_prostate_AXT2_0216.h5 31 | file_prostate_AXT2_0084.h5 32 | file_prostate_AXT2_0195.h5 33 | file_prostate_AXT2_0297.h5 34 | file_prostate_AXT2_0040.h5 35 | file_prostate_AXT2_0189.h5 36 | file_prostate_AXT2_0079.h5 37 | file_prostate_AXT2_0231.h5 38 | file_prostate_AXT2_0013.h5 39 | file_prostate_AXT2_0167.h5 40 | file_prostate_AXT2_0077.h5 41 | file_prostate_AXT2_0298.h5 42 | file_prostate_AXT2_0041.h5 43 | file_prostate_AXT2_0210.h5 44 | file_prostate_AXT2_0232.h5 45 | file_prostate_AXT2_0173.h5 46 | file_prostate_AXT2_0017.h5 47 | file_prostate_AXT2_0089.h5 48 | file_prostate_AXT2_0160.h5 49 | -------------------------------------------------------------------------------- /projects/calgary_campinas/compute_masks.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import argparse 15 | import pathlib 16 | 17 | import h5py 18 | import numpy as np 19 | from tqdm import tqdm 20 | 21 | 22 | def extract_mask(filename): 23 | """Extract the mask from masked k-space data, these are not explicitly given. 24 | 25 | Parameters 26 | ---------- 27 | filename : pathlib.Path 28 | 29 | Returns 30 | ------- 31 | np.ndarray 32 | """ 33 | with h5py.File(filename, "r") as f: 34 | kspace = f["kspace"] 35 | size = kspace.shape[0] 36 | out = np.abs(kspace[0]) 37 | for idx in range(1, size): 38 | out += np.abs(kspace[idx]) 39 | 40 | sampling_mask = ~(np.abs(out).sum(axis=-1) == 0) 41 | 42 | return sampling_mask 43 | 44 | 45 | if __name__ == "__main__": 46 | parser = argparse.ArgumentParser() 47 | parser.add_argument("testing_root", type=pathlib.Path, help="Path to the testing set.") 48 | parser.add_argument("output_directory", type=pathlib.Path, help="Path to the DoIterationOutput directory.") 49 | 50 | args = parser.parse_args() 51 | 52 | # Find all h5 files in the testing root 53 | testing_files = list(args.testing_root.glob("*.h5")) 54 | print(f"Found {len(testing_files)} files in {args.testing_root}.") 55 | print("Computing kspace masks...") 56 | 57 | for testing_file in tqdm(testing_files): 58 | mask = extract_mask(testing_file) 59 | np.save(args.output_directory / (testing_file.stem + ".npy"), mask) 60 | 61 | print("Computed masks.") 62 | -------------------------------------------------------------------------------- /projects/calgary_campinas/lists/train/12x218x170_train.lst: -------------------------------------------------------------------------------- 1 | e14396s3_P52224.7.h5 2 | e14537s3_P14336.7.h5 3 | e14195s3_P03584.7.h5 4 | e15494s3_P24064.7.h5 5 | e15581s3_P27136.7.h5 6 | e14441s5_P76800.7.h5 7 | e16882s14_P46080.7.h5 8 | e14134s3_P06656.7.h5 9 | e15198s3_P61952.7.h5 10 | e14423s3_P29696.7.h5 11 | e14141s3_P58880.7.h5 12 | e14268s3_P26112.7.h5 13 | e15598s3_P54784.7.h5 14 | e14377s6_P33280.7.h5 15 | e14302s3_P52224.7.h5 16 | e14352s3_P35840.7.h5 17 | e14507s5_P41472.7.h5 18 | e14508s5_P48128.7.h5 19 | e15135s4_P48640.7.h5 20 | e14532s5_P74752.7.h5 21 | e14437s5_P49152.7.h5 22 | e15862s13_P40960.7.h5 23 | e14477s5_P34816.7.h5 24 | e14351s3_P29184.7.h5 25 | e15195s3_P39424.7.h5 26 | e15828s13_P65536.7.h5 27 | e14140s3_P52224.7.h5 28 | e15802s13_P50176.7.h5 29 | e14427s3_P76288.7.h5 30 | e14292s3_P85504.7.h5 31 | e14089s3_P53248.7.h5 32 | e14304s3_P64000.7.h5 33 | e14487s5_P47616.7.h5 34 | e14530s5_P60416.7.h5 35 | e14296s4_P09216.7.h5 36 | e14369s5_P40960.7.h5 37 | e15184s3_P58880.7.h5 38 | e14378s5_P40448.7.h5 39 | e14191s3_P58368.7.h5 40 | e15523s3_P52224.7.h5 41 | -------------------------------------------------------------------------------- /projects/calgary_campinas/lists/train/12x218x180_train.lst: -------------------------------------------------------------------------------- 1 | e15802s3_P42496.7.h5 2 | e15578s13_P08192.7.h5 3 | e15828s3_P57856.7.h5 4 | e16971s3_P23040.7.h5 5 | e16972s3_P31232.7.h5 6 | e15862s3_P33792.7.h5 7 | e16882s4_P38912.7.h5 8 | -------------------------------------------------------------------------------- /projects/calgary_campinas/lists/val/12x218x170_val.lst: -------------------------------------------------------------------------------- 1 | e14313s5_P37888.7.h5 2 | e14258s3_P76800.7.h5 3 | e14691s3_P06656.7.h5 4 | e14531s6_P68096.7.h5 5 | e14280s3_P44032.7.h5 6 | e14498s5_P60928.7.h5 7 | e14120s11_P66048.7.h5 8 | e14110s3_P59904.7.h5 9 | e14583s3_P21504.7.h5 10 | e14542s5_P52224.7.h5 11 | e14584s5_P30208.7.h5 12 | e15183s3_P52224.7.h5 13 | e16673s13_P31744.7.h5 14 | e14692s5_P14848.7.h5 15 | e14264s3_P08192.7.h5 16 | e15197s3_P53760.7.h5 17 | -------------------------------------------------------------------------------- /projects/calgary_campinas/lists/val/12x218x180_val.lst: -------------------------------------------------------------------------------- 1 | e15652s4_P45056.7.h5 2 | e15652s14_P51712.7.h5 3 | e16673s3_P24576.7.h5 4 | e15521s3_P33280.7.h5 5 | -------------------------------------------------------------------------------- /projects/calgary_campinas/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # Copyright (c) DIRECT Contributors 3 | import numpy as np 4 | 5 | 6 | def volume_post_processing_func(volume): 7 | """Processing function for Calgary-Campinas, the challenge uses orthogonally normalized iFFT/FFT.""" 8 | # Only needed to fix a bug in Calgary Campinas training 9 | volume = volume / np.sqrt(np.prod(volume.shape[1:])) 10 | return volume 11 | -------------------------------------------------------------------------------- /projects/cvpr2022_recurrentvarnet/calgary_campinas/lists/test/test.lst: -------------------------------------------------------------------------------- 1 | e14313s5_P37888.7.h5 2 | e14258s3_P76800.7.h5 3 | e14691s3_P06656.7.h5 4 | e14531s6_P68096.7.h5 5 | e14280s3_P44032.7.h5 6 | e14498s5_P60928.7.h5 7 | e14120s11_P66048.7.h5 8 | e14110s3_P59904.7.h5 9 | e14583s3_P21504.7.h5 10 | e14542s5_P52224.7.h5 11 | e16673s3_P24576.7.h5 12 | -------------------------------------------------------------------------------- /projects/cvpr2022_recurrentvarnet/calgary_campinas/lists/train/12x218x170_train.lst: -------------------------------------------------------------------------------- 1 | e14396s3_P52224.7.h5 2 | e14537s3_P14336.7.h5 3 | e14195s3_P03584.7.h5 4 | e15494s3_P24064.7.h5 5 | e15581s3_P27136.7.h5 6 | e14441s5_P76800.7.h5 7 | e16882s14_P46080.7.h5 8 | e14134s3_P06656.7.h5 9 | e15198s3_P61952.7.h5 10 | e14423s3_P29696.7.h5 11 | e14141s3_P58880.7.h5 12 | e14268s3_P26112.7.h5 13 | e15598s3_P54784.7.h5 14 | e14377s6_P33280.7.h5 15 | e14302s3_P52224.7.h5 16 | e14352s3_P35840.7.h5 17 | e14507s5_P41472.7.h5 18 | e14508s5_P48128.7.h5 19 | e15135s4_P48640.7.h5 20 | e14532s5_P74752.7.h5 21 | e14437s5_P49152.7.h5 22 | e15862s13_P40960.7.h5 23 | e14477s5_P34816.7.h5 24 | e14351s3_P29184.7.h5 25 | e15195s3_P39424.7.h5 26 | e15828s13_P65536.7.h5 27 | e14140s3_P52224.7.h5 28 | e15802s13_P50176.7.h5 29 | e14427s3_P76288.7.h5 30 | e14292s3_P85504.7.h5 31 | e14089s3_P53248.7.h5 32 | e14304s3_P64000.7.h5 33 | e14487s5_P47616.7.h5 34 | e14530s5_P60416.7.h5 35 | e14296s4_P09216.7.h5 36 | e14369s5_P40960.7.h5 37 | e15184s3_P58880.7.h5 38 | e14378s5_P40448.7.h5 39 | e14191s3_P58368.7.h5 40 | e15523s3_P52224.7.h5 41 | -------------------------------------------------------------------------------- /projects/cvpr2022_recurrentvarnet/calgary_campinas/lists/train/12x218x180_train.lst: -------------------------------------------------------------------------------- 1 | e15802s3_P42496.7.h5 2 | e15578s13_P08192.7.h5 3 | e15828s3_P57856.7.h5 4 | e16971s3_P23040.7.h5 5 | e16972s3_P31232.7.h5 6 | e15862s3_P33792.7.h5 7 | e16882s4_P38912.7.h5 8 | -------------------------------------------------------------------------------- /projects/cvpr2022_recurrentvarnet/calgary_campinas/lists/val/12x218x170_val.lst: -------------------------------------------------------------------------------- 1 | e14584s5_P30208.7.h5 2 | e15183s3_P52224.7.h5 3 | e16673s13_P31744.7.h5 4 | e14692s5_P14848.7.h5 5 | e14264s3_P08192.7.h5 6 | e15197s3_P53760.7.h5 7 | -------------------------------------------------------------------------------- /projects/cvpr2022_recurrentvarnet/calgary_campinas/lists/val/12x218x180_val.lst: -------------------------------------------------------------------------------- 1 | e15652s4_P45056.7.h5 2 | e15652s14_P51712.7.h5 3 | e16673s3_P24576.7.h5 4 | e15521s3_P33280.7.h5 5 | -------------------------------------------------------------------------------- /projects/cvpr2022_recurrentvarnet/calgary_campinas/lists/val/val.lst: -------------------------------------------------------------------------------- 1 | e14584s5_P30208.7.h5 2 | e15183s3_P52224.7.h5 3 | e16673s13_P31744.7.h5 4 | e14692s5_P14848.7.h5 5 | e14264s3_P08192.7.h5 6 | e15197s3_P53760.7.h5 7 | e15652s4_P45056.7.h5 8 | e15652s14_P51712.7.h5 9 | e16673s3_P24576.7.h5 10 | e15521s3_P33280.7.h5 11 | -------------------------------------------------------------------------------- /projects/cvpr2022_recurrentvarnet/fastmri/AXT1_brain/lists/test.lst: -------------------------------------------------------------------------------- 1 | file_brain_AXT1_202_2020455.h5 2 | file_brain_AXT1_202_2020474.h5 3 | file_brain_AXT1_202_2020518.h5 4 | file_brain_AXT1_202_2020526.h5 5 | file_brain_AXT1_202_2020530.h5 6 | file_brain_AXT1_202_2020538.h5 7 | file_brain_AXT1_202_2020559.h5 8 | file_brain_AXT1_202_2020561.h5 9 | file_brain_AXT1_202_2020563.h5 10 | file_brain_AXT1_202_2020564.h5 11 | file_brain_AXT1_202_2020567.h5 12 | file_brain_AXT1_202_2020576.h5 13 | file_brain_AXT1_202_2020581.h5 14 | file_brain_AXT1_202_2020589.h5 15 | file_brain_AXT1_202_2020590.h5 16 | file_brain_AXT1_202_2120021.h5 17 | file_brain_AXT1_202_2120022.h5 18 | file_brain_AXT1_202_6000292.h5 19 | file_brain_AXT1_202_6000296.h5 20 | file_brain_AXT1_202_6000299.h5 21 | file_brain_AXT1_202_6000303.h5 22 | file_brain_AXT1_202_6000308.h5 23 | file_brain_AXT1_202_6000315.h5 24 | file_brain_AXT1_202_6000326.h5 25 | file_brain_AXT1_202_6000333.h5 26 | file_brain_AXT1_202_6000336.h5 27 | file_brain_AXT1_202_6000372.h5 28 | file_brain_AXT1_202_6000389.h5 29 | file_brain_AXT1_202_6000392.h5 30 | file_brain_AXT1_202_6000416.h5 31 | file_brain_AXT1_202_6000432.h5 32 | file_brain_AXT1_202_6000434.h5 33 | file_brain_AXT1_202_6000451.h5 34 | file_brain_AXT1_202_6000470.h5 35 | file_brain_AXT1_202_6000500.h5 36 | file_brain_AXT1_202_6000503.h5 37 | file_brain_AXT1_202_6000505.h5 38 | file_brain_AXT1_202_6000515.h5 39 | file_brain_AXT1_202_6000518.h5 40 | file_brain_AXT1_202_6000537.h5 41 | file_brain_AXT1_202_6000564.h5 42 | file_brain_AXT1_202_6000577.h5 43 | file_brain_AXT1_202_6000587.h5 44 | file_brain_AXT1_202_6000591.h5 45 | file_brain_AXT1_206_2060024.h5 46 | file_brain_AXT1_206_2060056.h5 47 | -------------------------------------------------------------------------------- /projects/cvpr2022_recurrentvarnet/fastmri/AXT1_brain/lists/val.lst: -------------------------------------------------------------------------------- 1 | file_brain_AXT1_201_6002725.h5 2 | file_brain_AXT1_201_6002736.h5 3 | file_brain_AXT1_201_6002740.h5 4 | file_brain_AXT1_201_6002759.h5 5 | file_brain_AXT1_201_6002786.h5 6 | file_brain_AXT1_201_6002793.h5 7 | file_brain_AXT1_201_6002828.h5 8 | file_brain_AXT1_201_6002841.h5 9 | file_brain_AXT1_201_6002861.h5 10 | file_brain_AXT1_201_6002885.h5 11 | file_brain_AXT1_202_2020003.h5 12 | file_brain_AXT1_202_2020029.h5 13 | file_brain_AXT1_202_2020033.h5 14 | file_brain_AXT1_202_2020038.h5 15 | file_brain_AXT1_202_2020066.h5 16 | file_brain_AXT1_202_2020082.h5 17 | file_brain_AXT1_202_2020093.h5 18 | file_brain_AXT1_202_2020102.h5 19 | file_brain_AXT1_202_2020105.h5 20 | file_brain_AXT1_202_2020111.h5 21 | file_brain_AXT1_202_2020126.h5 22 | file_brain_AXT1_202_2020129.h5 23 | file_brain_AXT1_202_2020158.h5 24 | file_brain_AXT1_202_2020181.h5 25 | file_brain_AXT1_202_2020188.h5 26 | file_brain_AXT1_202_2020194.h5 27 | file_brain_AXT1_202_2020197.h5 28 | file_brain_AXT1_202_2020215.h5 29 | file_brain_AXT1_202_2020228.h5 30 | file_brain_AXT1_202_2020241.h5 31 | file_brain_AXT1_202_2020270.h5 32 | file_brain_AXT1_202_2020281.h5 33 | file_brain_AXT1_202_2020283.h5 34 | file_brain_AXT1_202_2020287.h5 35 | file_brain_AXT1_202_2020302.h5 36 | file_brain_AXT1_202_2020305.h5 37 | file_brain_AXT1_202_2020313.h5 38 | file_brain_AXT1_202_2020318.h5 39 | file_brain_AXT1_202_2020325.h5 40 | file_brain_AXT1_202_2020342.h5 41 | file_brain_AXT1_202_2020368.h5 42 | file_brain_AXT1_202_2020386.h5 43 | file_brain_AXT1_202_2020414.h5 44 | file_brain_AXT1_202_2020438.h5 45 | file_brain_AXT1_202_2020444.h5 46 | file_brain_AXT1_202_2020446.h5 47 | -------------------------------------------------------------------------------- /projects/spie2022_radial_subsampling/lists/test/12x218x170_test.lst: -------------------------------------------------------------------------------- 1 | e14437s5_P49152.7.h5 2 | e15862s13_P40960.7.h5 3 | e14477s5_P34816.7.h5 4 | e14351s3_P29184.7.h5 5 | e15195s3_P39424.7.h5 6 | e14140s3_P52224.7.h5 7 | e14692s5_P14848.7.h5 8 | e14264s3_P08192.7.h5 9 | e15197s3_P53760.7.h5 10 | e15183s3_P52224.7.h5 11 | e16673s13_P31744.7.h5 12 | -------------------------------------------------------------------------------- /projects/spie2022_radial_subsampling/lists/test/12x218x180_test.lst: -------------------------------------------------------------------------------- 1 | e16882s4_P38912.7.h5 2 | e15521s3_P33280.7.h5 3 | -------------------------------------------------------------------------------- /projects/spie2022_radial_subsampling/lists/test/test.lst: -------------------------------------------------------------------------------- 1 | e14437s5_P49152.7.h5 2 | e15862s13_P40960.7.h5 3 | e14477s5_P34816.7.h5 4 | e14351s3_P29184.7.h5 5 | e15195s3_P39424.7.h5 6 | e14140s3_P52224.7.h5 7 | e14692s5_P14848.7.h5 8 | e14264s3_P08192.7.h5 9 | e15197s3_P53760.7.h5 10 | e15183s3_P52224.7.h5 11 | e16673s13_P31744.7.h5 12 | e16882s4_P38912.7.h5 13 | e15521s3_P33280.7.h5 14 | -------------------------------------------------------------------------------- /projects/spie2022_radial_subsampling/lists/train/12x218x170_train.lst: -------------------------------------------------------------------------------- 1 | e14396s3_P52224.7.h5 2 | e14537s3_P14336.7.h5 3 | e14195s3_P03584.7.h5 4 | e15494s3_P24064.7.h5 5 | e15581s3_P27136.7.h5 6 | e14441s5_P76800.7.h5 7 | e16882s14_P46080.7.h5 8 | e14134s3_P06656.7.h5 9 | e15198s3_P61952.7.h5 10 | e14423s3_P29696.7.h5 11 | e14141s3_P58880.7.h5 12 | e14268s3_P26112.7.h5 13 | e15598s3_P54784.7.h5 14 | e14377s6_P33280.7.h5 15 | e14302s3_P52224.7.h5 16 | e14352s3_P35840.7.h5 17 | e14507s5_P41472.7.h5 18 | e14508s5_P48128.7.h5 19 | e15135s4_P48640.7.h5 20 | e14532s5_P74752.7.h5 21 | e15828s13_P65536.7.h5 22 | e15802s13_P50176.7.h5 23 | e14427s3_P76288.7.h5 24 | e14292s3_P85504.7.h5 25 | e14089s3_P53248.7.h5 26 | e14304s3_P64000.7.h5 27 | e14487s5_P47616.7.h5 28 | e14530s5_P60416.7.h5 29 | e14296s4_P09216.7.h5 30 | e14369s5_P40960.7.h5 31 | e15184s3_P58880.7.h5 32 | e14378s5_P40448.7.h5 33 | e14191s3_P58368.7.h5 34 | e15523s3_P52224.7.h5 35 | -------------------------------------------------------------------------------- /projects/spie2022_radial_subsampling/lists/train/12x218x180_train.lst: -------------------------------------------------------------------------------- 1 | e15802s3_P42496.7.h5 2 | e15578s13_P08192.7.h5 3 | e15828s3_P57856.7.h5 4 | e16971s3_P23040.7.h5 5 | e16972s3_P31232.7.h5 6 | e15862s3_P33792.7.h5 7 | -------------------------------------------------------------------------------- /projects/spie2022_radial_subsampling/lists/val/12x218x170_val.lst: -------------------------------------------------------------------------------- 1 | e14313s5_P37888.7.h5 2 | e14258s3_P76800.7.h5 3 | e14691s3_P06656.7.h5 4 | e14531s6_P68096.7.h5 5 | e14280s3_P44032.7.h5 6 | e14498s5_P60928.7.h5 7 | e14120s11_P66048.7.h5 8 | e14110s3_P59904.7.h5 9 | e14583s3_P21504.7.h5 10 | e14542s5_P52224.7.h5 11 | e14584s5_P30208.7.h5 12 | -------------------------------------------------------------------------------- /projects/spie2022_radial_subsampling/lists/val/12x218x180_val.lst: -------------------------------------------------------------------------------- 1 | e15652s4_P45056.7.h5 2 | e15652s14_P51712.7.h5 3 | e16673s3_P24576.7.h5 4 | -------------------------------------------------------------------------------- /projects/spie2022_radial_subsampling/lists/val/val.lst: -------------------------------------------------------------------------------- 1 | e14313s5_P37888.7.h5 2 | e14258s3_P76800.7.h5 3 | e14691s3_P06656.7.h5 4 | e14531s6_P68096.7.h5 5 | e14280s3_P44032.7.h5 6 | e14498s5_P60928.7.h5 7 | e14120s11_P66048.7.h5 8 | e14110s3_P59904.7.h5 9 | e14583s3_P21504.7.h5 10 | e14542s5_P52224.7.h5 11 | e14584s5_P30208.7.h5 12 | e15652s4_P45056.7.h5 13 | e15652s14_P51712.7.h5 14 | e16673s3_P24576.7.h5 15 | -------------------------------------------------------------------------------- /projects/toy/shepp_logan/README.rst: -------------------------------------------------------------------------------- 1 | =================== 2 | Shepp Logan Dataset 3 | =================== 4 | 5 | Details 6 | ------- 7 | 8 | The Shepp Logan Dataset is based on the implementation of the 3-dimensional Shepp Logan phantom as presented in [1]_. 9 | More specifically, we use from [1]_ the ellipsoid and tissue parameters. 10 | 11 | In `DIRECT` are implemented three different Shepp Logan datasets: 12 | 13 | * T1-weighted (:class:`SheppLoganT1Dataset`) 14 | * T2-weighted (:class:`SheppLoganT2Dataset`) 15 | * Proton density (:class:`SheppLoganT1Dataset`) 16 | 17 | The Shepp Logan Datasets allow for data-less testing of the MRI models. 18 | 19 | Use the :class:`SheppLoganDataset` 20 | ---------------------------------- 21 | 22 | Configuration 23 | ~~~~~~~~~~~~~ 24 | 25 | A toy configuration file for training a :class:`Unet2d` model can be found in 26 | `https://github.com/NKI-AI/direct/blob/main/projects/toy/shepp_logan/base_unet.yaml `__. 27 | 28 | Training 29 | ~~~~~~~~ 30 | 31 | Since data are created on the fly, there is no need to pass to the command line training or validation directory paths. 32 | The following command might be used for training using the Shepp Logan Datasets: 33 | 34 | .. code-block::bash 35 | 36 | direct train \ 37 | --name \ 38 | --cfg projects/toy/shepp_logan/base_unet.yaml \ 39 | --num-gpus \ 40 | --num-workers \ 41 | [--other-flags] 42 | 43 | References 44 | ---------- 45 | 46 | .. [1] Gach, H. Michael, Costin Tanase, and Fernando Boada. "2D & 3D Shepp-Logan phantom standards for MRI." 2008 19th International Conference on Systems Engineering. IEEE, 2008. 47 | -------------------------------------------------------------------------------- /projects/vSHARP/fastmri_prostate/lists/train_10_coils.lst: -------------------------------------------------------------------------------- 1 | file_prostate_AXT2_0023.h5 2 | -------------------------------------------------------------------------------- /projects/vSHARP/fastmri_prostate/lists/train_14_coils.lst: -------------------------------------------------------------------------------- 1 | file_prostate_AXT2_0190.h5 2 | file_prostate_AXT2_0174.h5 3 | file_prostate_AXT2_0233.h5 4 | file_prostate_AXT2_0002.h5 5 | file_prostate_AXT2_0147.h5 6 | file_prostate_AXT2_0290.h5 7 | file_prostate_AXT2_0056.h5 8 | -------------------------------------------------------------------------------- /projects/vSHARP/fastmri_prostate/lists/train_16_coils.lst: -------------------------------------------------------------------------------- 1 | file_prostate_AXT2_0007.h5 2 | file_prostate_AXT2_0139.h5 3 | file_prostate_AXT2_0005.h5 4 | file_prostate_AXT2_0243.h5 5 | file_prostate_AXT2_0015.h5 6 | file_prostate_AXT2_0003.h5 7 | file_prostate_AXT2_0227.h5 8 | file_prostate_AXT2_0151.h5 9 | file_prostate_AXT2_0123.h5 10 | file_prostate_AXT2_0310.h5 11 | file_prostate_AXT2_0306.h5 12 | file_prostate_AXT2_0068.h5 13 | file_prostate_AXT2_0020.h5 14 | file_prostate_AXT2_0154.h5 15 | -------------------------------------------------------------------------------- /projects/vSHARP/fastmri_prostate/lists/train_24_coils.lst: -------------------------------------------------------------------------------- 1 | file_prostate_AXT2_0101.h5 2 | file_prostate_AXT2_0221.h5 3 | file_prostate_AXT2_0033.h5 4 | file_prostate_AXT2_0213.h5 5 | -------------------------------------------------------------------------------- /projects/vSHARP/fastmri_prostate/lists/train_26_coils.lst: -------------------------------------------------------------------------------- 1 | file_prostate_AXT2_0080.h5 2 | file_prostate_AXT2_0253.h5 3 | file_prostate_AXT2_0097.h5 4 | file_prostate_AXT2_0102.h5 5 | file_prostate_AXT2_0170.h5 6 | file_prostate_AXT2_0246.h5 7 | file_prostate_AXT2_0255.h5 8 | file_prostate_AXT2_0197.h5 9 | file_prostate_AXT2_0106.h5 10 | file_prostate_AXT2_0208.h5 11 | file_prostate_AXT2_0025.h5 12 | file_prostate_AXT2_0270.h5 13 | file_prostate_AXT2_0043.h5 14 | file_prostate_AXT2_0119.h5 15 | file_prostate_AXT2_0030.h5 16 | file_prostate_AXT2_0122.h5 17 | file_prostate_AXT2_0145.h5 18 | file_prostate_AXT2_0280.h5 19 | file_prostate_AXT2_0126.h5 20 | file_prostate_AXT2_0277.h5 21 | file_prostate_AXT2_0199.h5 22 | file_prostate_AXT2_0296.h5 23 | file_prostate_AXT2_0130.h5 24 | file_prostate_AXT2_0035.h5 25 | file_prostate_AXT2_0157.h5 26 | file_prostate_AXT2_0045.h5 27 | file_prostate_AXT2_0155.h5 28 | file_prostate_AXT2_0301.h5 29 | file_prostate_AXT2_0142.h5 30 | file_prostate_AXT2_0021.h5 31 | file_prostate_AXT2_0051.h5 32 | file_prostate_AXT2_0046.h5 33 | file_prostate_AXT2_0019.h5 34 | file_prostate_AXT2_0282.h5 35 | -------------------------------------------------------------------------------- /projects/vSHARP/fastmri_prostate/lists/train_30_coils.lst: -------------------------------------------------------------------------------- 1 | file_prostate_AXT2_0268.h5 2 | file_prostate_AXT2_0026.h5 3 | file_prostate_AXT2_0009.h5 4 | file_prostate_AXT2_0228.h5 5 | file_prostate_AXT2_0294.h5 6 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | # NOTE: you have to use single-quoted strings in TOML for regular expressions. 2 | # It's the equivalent of r-strings in Python. Multiline strings are treated as 3 | # verbose regular expressions by Black. Use [ ] to denote a significant space 4 | # character. 5 | 6 | [tool.black] 7 | line-length = 119 # PyCharm line length 8 | target-version = ['py311'] 9 | include = '\.pyi?$' 10 | exclude = ''' 11 | /( 12 | \.eggs 13 | | \.git 14 | | \.hg 15 | | \.mypy_cache 16 | | \.tox 17 | | \.venv 18 | | \.idea 19 | | _build 20 | | buck-out 21 | | build 22 | | dist 23 | )/ 24 | ''' 25 | 26 | [tool.isort] 27 | multi_line_output = 3 28 | include_trailing_comma = true 29 | force_grid_wrap = 0 30 | use_parentheses = true 31 | ensure_newline_before_comments = true 32 | line_length = 119 33 | 34 | 35 | [tool.pylint.messages_control] 36 | 37 | [tool.pylint.format] 38 | max-line-length = "120" 39 | 40 | [tool.pylint.'TYPECHECK'] 41 | generated-members=['numpy.*', 'torch.*', 'np.*'] 42 | 43 | [tool.tox] 44 | legacy_tox_ini = """ 45 | [tox] 46 | envlist = py39, py310 47 | skip_missing_interpreters=true 48 | 49 | [gh-actions] 50 | python = 51 | 3.9: py39 52 | 3.10: py310 53 | [testenv] 54 | deps = pytest 55 | extras = dev 56 | allowlist_externals = sh 57 | commands= 58 | sh -c "direct --help > /dev/null" 59 | sh -c "pytest --ignore=projects" 60 | """ 61 | -------------------------------------------------------------------------------- /requirements.in: -------------------------------------------------------------------------------- 1 | boto3==1.37.38 2 | einops==0.8.1 3 | h5py==3.11.0 4 | ismrmrd==1.14.1 5 | joblib==1.3.1 6 | lazy_loader==0.4 7 | myst_parser==4.0.1 8 | numpy==1.26.4 9 | numpydoc==1.8.0 10 | omegaconf==2.3.0 11 | packaging==25.0 12 | protobuf==3.20.2 13 | pylint==3.3.6 14 | pytest==8.3.5 15 | scikit-image>=0.19.0 16 | scikit-learn>=1.0.1 17 | sphinx-book-theme==1.1.4 18 | sphinx_copybutton==0.5.2 19 | tensorboard>=2.7.0 20 | threadpoolctl==3.1.0 21 | torch==2.6.0 22 | torchvision>=0.20.0 23 | tqdm==4.67.1 -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 2.1.0 3 | commit = True 4 | tag = False 5 | parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+)(?P\d+))? 6 | serialize = 7 | {major}.{minor}.{patch}-{release}{build} 8 | {major}.{minor}.{patch} 9 | 10 | [bumpversion:part:release] 11 | optional_value = prod 12 | first_value = dev 13 | values = 14 | dev 15 | prod 16 | 17 | [bumpversion:part:build] 18 | 19 | [bumpversion:file:direct/__init__.py] 20 | search = __version__ = "{current_version}" 21 | replace = __version__ = "{new_version}" 22 | 23 | [bdist_wheel] 24 | universal = 1 25 | 26 | [flake8] 27 | exclude = docs 28 | max-line-length = 120 29 | 30 | [aliases] 31 | test = pytest 32 | 33 | [tool:pytest] 34 | collect_ignore = ['setup.py'] 35 | -------------------------------------------------------------------------------- /tests/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_py//py:defs.bzl", "py_test") 2 | load("@direct_pip//:requirements.bzl", "requirement") 3 | 4 | REQUIREMENTS = [ 5 | "//direct:direct_lib", 6 | requirement("pytest"), 7 | ] 8 | 9 | py_test( 10 | name = "train_test", 11 | srcs = ["train_test.py"], 12 | pytest_main = True, 13 | deps = REQUIREMENTS, 14 | ) 15 | 16 | py_test( 17 | name = "checkpointer_test", 18 | srcs = ["checkpointer_test.py"], 19 | pytest_main = True, 20 | deps = REQUIREMENTS, 21 | ) 22 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /tests/test_cli/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_py//py:defs.bzl", "py_test") 2 | load("@direct_pip//:requirements.bzl", "requirement") 3 | 4 | REQUIREMENTS = [ 5 | "//direct:direct_lib", 6 | requirement("pytest"), 7 | ] 8 | 9 | py_test( 10 | name = "utils_test", 11 | srcs = ["utils_test.py"], 12 | pytest_main = True, 13 | deps = REQUIREMENTS, 14 | ) 15 | -------------------------------------------------------------------------------- /tests/test_cli/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /tests/test_cli/utils_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import argparse 15 | import pathlib 16 | import tempfile 17 | 18 | import pytest 19 | 20 | from direct.cli.utils import file_or_url, is_file 21 | 22 | 23 | @pytest.mark.parametrize("real_file", [True, False]) 24 | def test_is_file(real_file): 25 | if real_file: 26 | with tempfile.NamedTemporaryFile(suffix=".h5") as temp_file: 27 | assert pathlib.Path(temp_file.name) == is_file(temp_file.name) 28 | else: 29 | with pytest.raises(argparse.ArgumentTypeError): 30 | is_file("fake_name") 31 | -------------------------------------------------------------------------------- /tests/tests_common/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_py//py:defs.bzl", "py_test") 2 | load("@direct_pip//:requirements.bzl", "requirement") 3 | 4 | REQUIREMENTS = [ 5 | "//direct:direct_lib", 6 | requirement("pytest"), 7 | ] 8 | 9 | py_test( 10 | name = "subsample_test", 11 | srcs = ["subsample_test.py"], 12 | pytest_main = True, 13 | deps = REQUIREMENTS, 14 | ) 15 | -------------------------------------------------------------------------------- /tests/tests_common/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /tests/tests_data/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_py//py:defs.bzl", "py_test") 2 | load("@direct_pip//:requirements.bzl", "requirement") 3 | 4 | REQUIREMENTS = [ 5 | "//direct:direct_lib", 6 | requirement("pytest"), 7 | ] 8 | 9 | py_test( 10 | name = "transforms_test", 11 | srcs = ["transforms_test.py"], 12 | pytest_main = True, 13 | deps = REQUIREMENTS, 14 | ) 15 | 16 | py_test( 17 | name = "sens_test", 18 | srcs = ["sens_test.py"], 19 | pytest_main = True, 20 | deps = REQUIREMENTS, 21 | ) 22 | 23 | py_test( 24 | name = "samplers_test", 25 | srcs = ["samplers_test.py"], 26 | pytest_main = True, 27 | deps = REQUIREMENTS, 28 | ) 29 | 30 | py_test( 31 | name = "mri_transforms_test", 32 | srcs = ["mri_transforms_test.py"], 33 | pytest_main = True, 34 | deps = REQUIREMENTS, 35 | ) 36 | 37 | py_test( 38 | name = "lr_scheduler_test", 39 | srcs = ["lr_scheduler_test.py"], 40 | pytest_main = True, 41 | deps = REQUIREMENTS, 42 | ) 43 | 44 | py_test( 45 | name = "fake_test", 46 | srcs = ["fake_test.py"], 47 | pytest_main = True, 48 | deps = REQUIREMENTS, 49 | ) 50 | 51 | py_test( 52 | name = "datasets_test", 53 | srcs = ["datasets_test.py"], 54 | pytest_main = True, 55 | deps = REQUIREMENTS, 56 | ) 57 | 58 | py_test( 59 | name = "algorithms_test", 60 | srcs = ["algorithms_test.py"], 61 | pytest_main = True, 62 | deps = REQUIREMENTS, 63 | ) 64 | -------------------------------------------------------------------------------- /tests/tests_data/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /tests/tests_data/algorithms_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Tests for the direct.algorithms module.""" 15 | 16 | import pytest 17 | import torch 18 | 19 | from direct.algorithms.optimization import MaximumEigenvaluePowerMethod 20 | 21 | 22 | @pytest.mark.parametrize("size", [20, 30]) 23 | def test_power_method(size): 24 | mat = torch.rand((size, size)) + torch.rand((size, size)) * 1j 25 | x0 = torch.ones(size) + 0 * 1j 26 | 27 | def A(x): 28 | return mat @ x 29 | 30 | algo = MaximumEigenvaluePowerMethod(A) 31 | algo.fit(x0) 32 | 33 | all_eigenvalues = torch.linalg.eig(mat).eigenvalues 34 | max_eig_torch = all_eigenvalues[all_eigenvalues.abs().argmax()] 35 | 36 | assert torch.allclose(algo.max_eig, max_eig_torch, 0.001) 37 | -------------------------------------------------------------------------------- /tests/tests_data/fake_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Tests for the direct.data.fake module.""" 15 | 16 | import numpy as np 17 | import pytest 18 | 19 | from direct.data.fake import FakeMRIData 20 | 21 | 22 | @pytest.mark.parametrize( 23 | "size", 24 | [ 25 | 1, 26 | 3, 27 | ], 28 | ) 29 | @pytest.mark.parametrize( 30 | "num_coils", 31 | [ 32 | 1, 33 | 8, 34 | ], 35 | ) 36 | @pytest.mark.parametrize( 37 | "spatial_shape", 38 | [(32, 32), (10, 32, 32), [10, 32, 32]], 39 | ) 40 | def test_fake(size, num_coils, spatial_shape): 41 | fake_data = FakeMRIData(ndim=len(spatial_shape)) 42 | 43 | samples = fake_data(size, num_coils, spatial_shape) 44 | keys = ["kspace", "reconstruction_rss", "attrs"] 45 | 46 | assert all(_ in samples[0].keys() for _ in keys) 47 | 48 | assert len(samples) == size 49 | 50 | assert all(sample[keys[0]].shape[1] == num_coils for sample in samples) 51 | 52 | assert all(tuple(sample[keys[0]].shape)[-2:] == tuple(spatial_shape)[-2:] for sample in samples) 53 | assert all(tuple(sample[keys[1]].shape)[-2:] == tuple(spatial_shape)[-2:] for sample in samples) 54 | 55 | slice_num = 1 if len(spatial_shape) == 2 else spatial_shape[0] 56 | assert all(sample[keys[0]].shape[0] == slice_num for sample in samples) 57 | assert all(sample[keys[1]].shape[0] == slice_num for sample in samples) 58 | 59 | assert all(np.allclose(rss(sample[keys[0]]), sample[keys[1]]) for sample in samples) 60 | 61 | 62 | def ifft(data, dims=(-2, -1)): 63 | data = np.fft.ifftshift(data, dims) 64 | out = np.fft.ifft2(data, norm="ortho") 65 | out = np.fft.fftshift(out, dims) 66 | return out 67 | 68 | 69 | def rss(data, coil_dim=1): 70 | return np.sqrt((np.abs(ifft(data)) ** 2).sum(coil_dim)) 71 | -------------------------------------------------------------------------------- /tests/tests_data/lr_scheduler_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Tests for the direct.data.lr_scheduler module.""" 15 | 16 | import numpy as np 17 | import pytest 18 | import torch 19 | 20 | from direct.data.lr_scheduler import LRScheduler, WarmupCosineLR, WarmupMultiStepLR 21 | 22 | 23 | def create_model(): 24 | return torch.nn.Linear(2, 3) 25 | 26 | 27 | def create_optimizer(model): 28 | return torch.optim.Adam(model.parameters()) 29 | 30 | 31 | @pytest.mark.parametrize( 32 | "gamma, milestones, warm_up_iters", 33 | [[0.25, [10, 20, 25, 30], 15], [0.1, [10, 20, 25, 30], 45]], 34 | ) 35 | @pytest.mark.parametrize( 36 | "method", 37 | ["constant", "linear", None], 38 | ) 39 | def test_WarmupMultiStepLR(milestones, warm_up_iters, gamma, method): 40 | model = create_model() 41 | optimizer = create_optimizer(model) 42 | if method: 43 | scheduler = WarmupMultiStepLR( 44 | optimizer, milestones, warmup_iterations=warm_up_iters, gamma=gamma, warmup_method=method 45 | ) 46 | tmp = scheduler.get_lr() 47 | for iter in range(1, milestones[-1] * 2): 48 | optimizer.step() 49 | scheduler.step() 50 | lr = scheduler.get_lr() 51 | if iter >= warm_up_iters: 52 | if iter in milestones: 53 | assert np.allclose(lr[0] / tmp[0], gamma) 54 | elif (iter - 1) in milestones and iter not in milestones and (iter + 1) not in milestones: 55 | assert tmp == lr 56 | else: 57 | if iter in milestones: 58 | assert lr[0] < tmp[0] 59 | tmp = lr 60 | else: 61 | with pytest.raises(ValueError): 62 | scheduler = WarmupMultiStepLR(optimizer, milestones, warmup_method=method) 63 | -------------------------------------------------------------------------------- /tests/tests_data/sens_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import pytest 15 | 16 | from direct.data.sens import simulate_sensitivity_maps 17 | 18 | 19 | @pytest.mark.parametrize( 20 | "num_coils", 21 | [1, 8], 22 | ) 23 | @pytest.mark.parametrize( 24 | "shape", 25 | [(32, 32), (10, 32, 32), (11, 12, 13)], 26 | ) 27 | @pytest.mark.parametrize( 28 | "var", 29 | [0.5], 30 | ) 31 | @pytest.mark.parametrize( 32 | "seed", 33 | [None, 0], 34 | ) 35 | def test_simulate_sens_maps(num_coils, shape, var, seed): 36 | sensitivity_map = simulate_sensitivity_maps(shape, num_coils, var, seed) 37 | 38 | assert tuple(sensitivity_map.shape) == (num_coils,) + tuple(shape) 39 | -------------------------------------------------------------------------------- /tests/tests_functionals/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_py//py:defs.bzl", "py_test") 2 | load("@direct_pip//:requirements.bzl", "requirement") 3 | 4 | REQUIREMENTS = [ 5 | "//direct:direct_lib", 6 | requirement("pytest"), 7 | ] 8 | 9 | py_test( 10 | name = "ssim_test", 11 | srcs = ["ssim_test.py"], 12 | pytest_main = True, 13 | deps = REQUIREMENTS, 14 | ) 15 | 16 | py_test( 17 | name = "snr_test", 18 | srcs = ["snr_test.py"], 19 | pytest_main = True, 20 | deps = REQUIREMENTS, 21 | ) 22 | 23 | py_test( 24 | name = "psnr_test", 25 | srcs = ["psnr_test.py"], 26 | pytest_main = True, 27 | deps = REQUIREMENTS, 28 | ) 29 | 30 | py_test( 31 | name = "nmse_test", 32 | srcs = ["nmse_test.py"], 33 | pytest_main = True, 34 | deps = REQUIREMENTS, 35 | ) 36 | 37 | py_test( 38 | name = "nmae_test", 39 | srcs = ["nmae_test.py"], 40 | pytest_main = True, 41 | deps = REQUIREMENTS, 42 | ) 43 | 44 | py_test( 45 | name = "hfen_test", 46 | srcs = ["hfen_test.py"], 47 | pytest_main = True, 48 | deps = REQUIREMENTS, 49 | ) 50 | 51 | py_test( 52 | name = "gradloss_test", 53 | srcs = ["gradloss_test.py"], 54 | pytest_main = True, 55 | deps = REQUIREMENTS, 56 | ) 57 | -------------------------------------------------------------------------------- /tests/tests_functionals/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /tests/tests_functionals/gradloss_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import numpy as np 15 | import pytest 16 | import torch 17 | from skimage.color import rgb2gray 18 | from sklearn.datasets import load_sample_image 19 | 20 | from direct.functionals import SobelGradL1Loss, SobelGradL2Loss 21 | 22 | # Load two images and convert them to grayscale 23 | flower = rgb2gray(load_sample_image("flower.jpg"))[None].astype(np.float32) 24 | china = rgb2gray(load_sample_image("china.jpg"))[None].astype(np.float32) 25 | 26 | 27 | @pytest.mark.parametrize("image", [flower, china]) 28 | def test_nmse(image): 29 | image_batch = [] 30 | image_noise_batch = [] 31 | 32 | for sigma in range(1, 5): 33 | noise = sigma * np.random.rand(*image.shape) 34 | image_noise = (image + noise).astype(np.float32).clip(0, 255) 35 | 36 | image_batch.append(image) 37 | image_noise_batch.append(image_noise) 38 | 39 | image_batch_torch = torch.tensor(image_batch) 40 | image_noise_batch_torch = torch.tensor(image_noise_batch) 41 | 42 | grad_loss_l1 = SobelGradL1Loss(image_batch_torch, image_noise_batch_torch) 43 | grad_loss_l2 = SobelGradL2Loss(image_batch_torch, image_noise_batch_torch) 44 | -------------------------------------------------------------------------------- /tests/tests_functionals/nmae_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import numpy as np 15 | import pytest 16 | import torch 17 | from skimage.color import rgb2gray 18 | from sklearn.datasets import load_sample_image 19 | 20 | from direct.functionals.nmae import NMAELoss 21 | 22 | # Load two images and convert them to grayscale 23 | flower = rgb2gray(load_sample_image("flower.jpg"))[None].astype(np.float32) 24 | china = rgb2gray(load_sample_image("china.jpg"))[None].astype(np.float32) 25 | 26 | 27 | @pytest.mark.parametrize("image", [flower, china]) 28 | def test_nmae(image): 29 | image_batch = [] 30 | image_noise_batch = [] 31 | single_image_nmse = [] 32 | 33 | for sigma in range(1, 5): 34 | noise = sigma * np.random.rand(*image.shape) 35 | image_noise = (image + noise).astype(np.float32).clip(0, 255) 36 | 37 | image_batch.append(image) 38 | image_noise_batch.append(image_noise) 39 | 40 | image_batch = np.stack(image_batch) 41 | image_noise_batch = np.stack(image_noise_batch) 42 | 43 | np_nmae = np.abs(image_batch - image_noise_batch).mean() / np.abs(image_batch).mean() 44 | 45 | image_batch_torch = torch.tensor(image_batch) 46 | image_noise_batch_torch = torch.tensor(image_noise_batch) 47 | 48 | nmse_batch = NMAELoss().forward(image_noise_batch_torch, image_batch_torch) 49 | assert np.allclose(nmse_batch, np_nmae, atol=5e-4) 50 | -------------------------------------------------------------------------------- /tests/tests_functionals/snr_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import numpy as np 15 | import pytest 16 | import torch 17 | from skimage.color import rgb2gray 18 | from skimage.metrics import peak_signal_noise_ratio 19 | from sklearn.datasets import load_sample_image 20 | 21 | from direct.functionals.snr import SNRLoss 22 | 23 | # Load two images and convert them to grayscale 24 | flower = rgb2gray(load_sample_image("flower.jpg"))[None].astype(np.float32) 25 | china = rgb2gray(load_sample_image("china.jpg"))[None].astype(np.float32) 26 | 27 | 28 | @pytest.mark.parametrize("image", [flower, china]) 29 | @pytest.mark.parametrize("reduction", ["sum", "mean"]) 30 | def test_snr(image, reduction): 31 | image = torch.from_numpy(image).unsqueeze(0).unsqueeze(0) 32 | image_noise_batch = [] 33 | single_image_snr = [] 34 | 35 | for sigma in range(0, 101, 20): 36 | noise = sigma * torch.randn(*image.shape) 37 | image_noise = image + noise 38 | snr_torch = SNRLoss(reduction=reduction).forward(image_noise, image) 39 | image_noise_batch.append(image_noise) 40 | single_image_snr.append(snr_torch) 41 | 42 | image_batch = torch.cat([image] * len(image_noise_batch), dim=0) 43 | image_noise_batch = torch.cat(image_noise_batch, dim=0) 44 | snr_batch = SNRLoss(reduction=reduction).forward(image_noise_batch, image_batch) 45 | # Assert that batch snr matches single snrs 46 | assert np.allclose(snr_batch, np.average(single_image_snr), atol=5e-4) 47 | -------------------------------------------------------------------------------- /tests/tests_nn/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /tests/tests_nn/cirim_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import pytest 15 | import torch 16 | 17 | from direct.data.transforms import fft2, ifft2 18 | from direct.nn.cirim.cirim import CIRIM 19 | 20 | 21 | def create_input(shape): 22 | return torch.rand(shape).float() 23 | 24 | 25 | @pytest.mark.parametrize( 26 | "shape", 27 | [ 28 | [3, 3, 16, 16], 29 | [2, 5, 16, 32], 30 | ], 31 | ) 32 | @pytest.mark.parametrize( 33 | "depth", 34 | [2, 3], 35 | ) 36 | @pytest.mark.parametrize( 37 | "time_steps", 38 | [4, 6], 39 | ) 40 | @pytest.mark.parametrize( 41 | "recurrent_hidden_channels", 42 | [64], 43 | ) 44 | @pytest.mark.parametrize( 45 | "num_cascades", 46 | [1, 4], 47 | ) 48 | @pytest.mark.parametrize( 49 | "no_parameter_sharing", 50 | [True, False], 51 | ) 52 | def test_cirim(shape, depth, time_steps, recurrent_hidden_channels, num_cascades, no_parameter_sharing): 53 | model = CIRIM( 54 | fft2, 55 | ifft2, 56 | depth=depth, 57 | time_steps=time_steps, 58 | recurrent_hidden_channels=recurrent_hidden_channels, 59 | num_cascades=num_cascades, 60 | no_parameter_sharing=no_parameter_sharing, 61 | ).cpu() 62 | 63 | kspace = create_input(shape + [2]).cpu() 64 | sens = create_input(shape + [2]).cpu() 65 | mask = create_input([shape[0]] + [1] + shape[2:] + [1]).round().int().cpu() 66 | 67 | out = next(model(kspace, mask, sens))[-1][-1] # prediction of the last time step of the last cascade 68 | 69 | assert out.shape == (shape[0], shape[2], shape[3]) 70 | -------------------------------------------------------------------------------- /tests/tests_nn/conv_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import pytest 15 | import torch 16 | import torch.nn as nn 17 | 18 | from direct.nn.conv.conv import Conv2d 19 | 20 | 21 | def create_input(shape): 22 | data = torch.rand(shape).float() 23 | 24 | return data 25 | 26 | 27 | @pytest.mark.parametrize( 28 | "shape", 29 | [ 30 | [3, 2, 32, 32], 31 | [3, 2, 16, 16], 32 | ], 33 | ) 34 | @pytest.mark.parametrize( 35 | "out_channels", 36 | [3, 5], 37 | ) 38 | @pytest.mark.parametrize( 39 | "hidden_channels", 40 | [16, 8], 41 | ) 42 | @pytest.mark.parametrize( 43 | "n_convs", 44 | [2, 4], 45 | ) 46 | @pytest.mark.parametrize( 47 | "act", 48 | [nn.ReLU(), nn.PReLU()], 49 | ) 50 | @pytest.mark.parametrize( 51 | "batchnorm", 52 | [True, False], 53 | ) 54 | def test_conv(shape, out_channels, hidden_channels, n_convs, act, batchnorm): 55 | model = Conv2d(shape[1], out_channels, hidden_channels, n_convs, act, batchnorm) 56 | 57 | data = create_input(shape).cpu() 58 | 59 | out = model(data) 60 | 61 | assert list(out.shape) == [shape[0]] + [out_channels] + shape[2:] 62 | -------------------------------------------------------------------------------- /tests/tests_nn/didn_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import pytest 15 | import torch 16 | 17 | from direct.nn.didn.didn import DIDN 18 | 19 | 20 | def create_input(shape): 21 | data = torch.rand(shape).float() 22 | 23 | return data 24 | 25 | 26 | @pytest.mark.parametrize( 27 | "shape", 28 | [ 29 | [3, 2, 32, 32], 30 | [3, 2, 16, 16], 31 | ], 32 | ) 33 | @pytest.mark.parametrize( 34 | "out_channels", 35 | [3, 5], 36 | ) 37 | @pytest.mark.parametrize( 38 | "hidden_channels", 39 | [16, 8], 40 | ) 41 | @pytest.mark.parametrize( 42 | "n_dubs", 43 | [3, 4], 44 | ) 45 | @pytest.mark.parametrize( 46 | "num_convs_recon", 47 | [3, 4], 48 | ) 49 | @pytest.mark.parametrize( 50 | "skip", 51 | [True, False], 52 | ) 53 | def test_didn(shape, out_channels, hidden_channels, n_dubs, num_convs_recon, skip): 54 | model = DIDN(shape[1], out_channels, hidden_channels, n_dubs, num_convs_recon, skip) 55 | 56 | data = create_input(shape).cpu() 57 | 58 | out = model(data) 59 | 60 | assert list(out.shape) == [shape[0]] + [out_channels] + shape[2:] 61 | -------------------------------------------------------------------------------- /tests/tests_nn/iterdualnet_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import pytest 15 | import torch 16 | 17 | from direct.data.transforms import fft2, ifft2 18 | from direct.nn.iterdualnet.iterdualnet import IterDualNet 19 | 20 | 21 | def create_input(shape): 22 | data = torch.rand(shape).float() 23 | 24 | return data 25 | 26 | 27 | @pytest.mark.parametrize("shape", [[3, 3, 32, 32], [5, 11, 20, 22]]) 28 | @pytest.mark.parametrize("num_iter", [3, 5]) 29 | @pytest.mark.parametrize("image_no_parameter_sharing", [True, False]) 30 | @pytest.mark.parametrize("kspace_no_parameter_sharing", [True, False]) 31 | @pytest.mark.parametrize("compute_per_coil", [True, False]) 32 | @pytest.mark.parametrize("normalized", [True, False]) 33 | def test_iterdualnet( 34 | shape, num_iter, image_no_parameter_sharing, kspace_no_parameter_sharing, compute_per_coil, normalized 35 | ): 36 | model = IterDualNet( 37 | fft2, 38 | ifft2, 39 | num_iter=num_iter, 40 | image_normunet=normalized, 41 | kspace_normunet=normalized, 42 | image_no_parameter_sharing=image_no_parameter_sharing, 43 | kspace_no_parameter_sharing=kspace_no_parameter_sharing, 44 | compute_per_coil=compute_per_coil, 45 | image_unet_num_filters=4, 46 | image_unet_num_pool_layers=3, 47 | kspace_unet_num_filters=4, 48 | kspace_unet_num_pool_layers=3, 49 | ).cpu() 50 | 51 | kspace = create_input(shape + [2]).cpu() 52 | mask = create_input([shape[0]] + [1] + shape[2:] + [1]).round().int().cpu() 53 | sens = create_input(shape + [2]).cpu() 54 | 55 | out = model(kspace, mask, sens) 56 | 57 | assert list(out.shape) == [shape[0]] + shape[2:] + [2] 58 | -------------------------------------------------------------------------------- /tests/tests_nn/jointicnet_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import pytest 15 | import torch 16 | 17 | from direct.data.transforms import fft2, ifft2 18 | from direct.nn.jointicnet.jointicnet import JointICNet 19 | 20 | 21 | def create_input(shape): 22 | data = torch.rand(shape).float() 23 | 24 | return data 25 | 26 | 27 | @pytest.mark.parametrize( 28 | "shape", 29 | [ 30 | [3, 3, 16, 16], 31 | [2, 5, 16, 32], 32 | ], 33 | ) 34 | @pytest.mark.parametrize( 35 | "num_iter", 36 | [2, 4], 37 | ) 38 | @pytest.mark.parametrize( 39 | "use_norm_unet", 40 | [True, False], 41 | ) 42 | def test_jointicnet(shape, num_iter, use_norm_unet): 43 | model = JointICNet( 44 | fft2, 45 | ifft2, 46 | num_iter, 47 | use_norm_unet, 48 | image_unet_num_pool_layers=2, 49 | kspace_unet_num_pool_layers=2, 50 | sens_unet_num_pool_layers=2, 51 | ).cpu() 52 | 53 | kspace = create_input(shape + [2]).cpu() 54 | mask = create_input([shape[0]] + [1] + shape[2:] + [1]).round().int().cpu() 55 | sens = create_input(shape + [2]).cpu() 56 | 57 | out = model(kspace, mask, sens) 58 | 59 | assert list(out.shape) == [shape[0]] + shape[2:] + [2] 60 | -------------------------------------------------------------------------------- /tests/tests_nn/kikinet_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import pytest 15 | import torch 16 | 17 | from direct.data.transforms import fft2, ifft2 18 | from direct.nn.kikinet.kikinet import KIKINet 19 | 20 | 21 | def create_input(shape): 22 | data = torch.rand(shape).float() 23 | 24 | return data 25 | 26 | 27 | @pytest.mark.parametrize( 28 | "shape", 29 | [ 30 | [3, 3, 32, 32], 31 | ], 32 | ) 33 | @pytest.mark.parametrize( 34 | "num_iter", 35 | [1, 3], 36 | ) 37 | @pytest.mark.parametrize( 38 | "image_model_architecture", 39 | ["MWCNN", "UNET", "NORMUNET"], 40 | ) 41 | @pytest.mark.parametrize( 42 | "kspace_model_architecture", 43 | ["CONV", "DIDN", "UNET", "NORMUNET"], 44 | ) 45 | @pytest.mark.parametrize( 46 | "normalize", 47 | [True, False], 48 | ) 49 | def test_kikinet(shape, num_iter, image_model_architecture, kspace_model_architecture, normalize): 50 | model = KIKINet( 51 | fft2, 52 | ifft2, 53 | num_iter=num_iter, 54 | image_model_architecture=image_model_architecture, 55 | kspace_model_architecture=kspace_model_architecture, 56 | normalize=normalize, 57 | ).cpu() 58 | 59 | kspace = create_input(shape + [2]).cpu() 60 | mask = create_input([shape[0]] + [1] + shape[2:] + [1]).round().int().cpu() 61 | sens = create_input(shape + [2]).cpu() 62 | 63 | out = model(kspace, mask, sens) 64 | 65 | assert list(out.shape) == [shape[0]] + shape[2:] + [2] 66 | -------------------------------------------------------------------------------- /tests/tests_nn/lpd_test.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # Copyright (c) DIRECT Contributors 3 | 4 | import pytest 5 | import torch 6 | 7 | from direct.data.transforms import fft2, ifft2 8 | from direct.nn.lpd.lpd import LPDNet 9 | 10 | 11 | def create_input(shape): 12 | data = torch.rand(shape).float() 13 | 14 | return data 15 | 16 | 17 | @pytest.mark.parametrize( 18 | "shape", 19 | [ 20 | [3, 3, 32, 32], 21 | ], 22 | ) 23 | @pytest.mark.parametrize( 24 | "num_iter", 25 | [2, 3], 26 | ) 27 | @pytest.mark.parametrize( 28 | "num_primal", 29 | [2, 3], 30 | ) 31 | @pytest.mark.parametrize( 32 | "num_dual", 33 | [3], 34 | ) 35 | @pytest.mark.parametrize( 36 | "primal_model_architecture", 37 | ["MWCNN", "UNET", "NORMUNET", None], 38 | ) 39 | @pytest.mark.parametrize( 40 | "dual_model_architecture", 41 | ["CONV", "DIDN", "UNET", "NORMUNET", None], 42 | ) 43 | def test_lpd( 44 | shape, 45 | num_iter, 46 | num_primal, 47 | num_dual, 48 | primal_model_architecture, 49 | dual_model_architecture, 50 | ): 51 | kwargs = { 52 | "forward_operator": fft2, 53 | "backward_operator": ifft2, 54 | "num_iter": num_iter, 55 | "num_primal": num_primal, 56 | "num_dual": num_dual, 57 | "primal_model_architecture": primal_model_architecture, 58 | "dual_model_architecture": dual_model_architecture, 59 | } 60 | if primal_model_architecture is None or dual_model_architecture is None: 61 | with pytest.raises(NotImplementedError): 62 | model = LPDNet(**kwargs).cpu() 63 | else: 64 | model = LPDNet(**kwargs).cpu() 65 | 66 | kspace = create_input(shape + [2]).cpu() 67 | sens = create_input(shape + [2]).cpu() 68 | mask = create_input([shape[0]] + [1] + shape[2:] + [1]).round().int().cpu() 69 | 70 | out = model(kspace, sens, mask) 71 | 72 | assert list(out.shape) == [shape[0]] + shape[2:] + [2] 73 | -------------------------------------------------------------------------------- /tests/tests_nn/mwcnn_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import pytest 15 | import torch 16 | import torch.nn as nn 17 | 18 | from direct.nn.mwcnn.mwcnn import MWCNN 19 | 20 | 21 | def create_input(shape): 22 | data = torch.rand(shape).float() 23 | 24 | return data 25 | 26 | 27 | @pytest.mark.parametrize( 28 | "shape", 29 | [ 30 | [3, 2, 32, 32], 31 | [3, 2, 20, 34], 32 | ], 33 | ) 34 | @pytest.mark.parametrize( 35 | "first_conv_hidden_channels", 36 | [4, 8], 37 | ) 38 | @pytest.mark.parametrize( 39 | "n_scales", 40 | [2, 3], 41 | ) 42 | @pytest.mark.parametrize( 43 | "bias", 44 | [True, False], 45 | ) 46 | @pytest.mark.parametrize( 47 | "batchnorm", 48 | [True, False], 49 | ) 50 | @pytest.mark.parametrize( 51 | "act", 52 | [nn.ReLU(), nn.PReLU()], 53 | ) 54 | def test_mwcnn(shape, first_conv_hidden_channels, n_scales, bias, batchnorm, act): 55 | model = MWCNN(shape[1], first_conv_hidden_channels, n_scales, bias, batchnorm, act) 56 | 57 | data = create_input(shape).cpu() 58 | 59 | out = model(data) 60 | 61 | assert list(out.shape) == shape 62 | -------------------------------------------------------------------------------- /tests/tests_nn/recurrent_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import pytest 15 | import torch 16 | 17 | from direct.nn.recurrent.recurrent import Conv2dGRU, NormConv2dGRU 18 | 19 | 20 | def create_input(shape): 21 | data = torch.rand(shape).float() 22 | 23 | return data 24 | 25 | 26 | @pytest.mark.parametrize( 27 | "shape", 28 | [ 29 | [3, 2, 32, 32], 30 | [3, 2, 16, 16], 31 | [3, 2, 15, 17], 32 | ], 33 | ) 34 | @pytest.mark.parametrize( 35 | "hidden_channels", 36 | [4, 8], 37 | ) 38 | @pytest.mark.parametrize( 39 | "normalized", 40 | [True, False], 41 | ) 42 | def test_conv2dgru(shape, hidden_channels, normalized): 43 | model = ( 44 | NormConv2dGRU(shape[1], hidden_channels, shape[1]) 45 | if normalized 46 | else Conv2dGRU(shape[1], hidden_channels, shape[1]) 47 | ) 48 | data = create_input(shape).cpu() 49 | 50 | out = model(data, None)[0] 51 | 52 | assert list(out.shape) == shape 53 | -------------------------------------------------------------------------------- /tests/tests_nn/resnet_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import pytest 15 | import torch 16 | 17 | from direct.nn.resnet.resnet import ResNet 18 | 19 | 20 | def create_input(shape): 21 | data = torch.rand(shape).float() 22 | 23 | return data 24 | 25 | 26 | @pytest.mark.parametrize( 27 | "shape", 28 | [ 29 | [3, 2, 32, 32], 30 | [3, 2, 16, 16], 31 | [3, 2, 15, 17], 32 | ], 33 | ) 34 | @pytest.mark.parametrize( 35 | "hidden_channels", 36 | [4, 8], 37 | ) 38 | @pytest.mark.parametrize( 39 | "out_channels", 40 | [2, None], 41 | ) 42 | @pytest.mark.parametrize( 43 | "num_blocks", 44 | [3, 5], 45 | ) 46 | @pytest.mark.parametrize( 47 | "batchnorm", 48 | [True, False], 49 | ) 50 | @pytest.mark.parametrize( 51 | "scale", 52 | [None, 0.1], 53 | ) 54 | def test_resnet(shape, hidden_channels, out_channels, num_blocks, batchnorm, scale): 55 | model = ResNet( 56 | hidden_channels=hidden_channels, 57 | out_channels=out_channels, 58 | num_blocks=num_blocks, 59 | batchnorm=batchnorm, 60 | scale=scale, 61 | ) 62 | data = create_input(shape).cpu() 63 | out = model(data) 64 | assert list(out.shape) == shape 65 | -------------------------------------------------------------------------------- /tests/tests_nn/unet_2d_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Tests for `direct.nn.unet.unet_2d.Unet2d` model.""" 15 | 16 | import numpy as np 17 | import pytest 18 | import torch 19 | 20 | from direct.data.transforms import fft2, ifft2 21 | from direct.nn.types import InitType 22 | from direct.nn.unet.unet_2d import Unet2d 23 | 24 | 25 | def create_input(shape): 26 | data = np.random.randn(*shape).copy() 27 | data = torch.from_numpy(data).float() 28 | 29 | return data 30 | 31 | 32 | @pytest.mark.parametrize( 33 | "shape", 34 | [ 35 | [2, 3, 16, 16], 36 | [4, 5, 16, 32], 37 | [3, 4, 32, 32], 38 | [3, 4, 40, 20], 39 | ], 40 | ) 41 | @pytest.mark.parametrize( 42 | "num_filters", 43 | [4, 6, 8], 44 | ) 45 | @pytest.mark.parametrize( 46 | "num_pool_layers", 47 | [2, 3], 48 | ) 49 | @pytest.mark.parametrize( 50 | "skip", 51 | [True, False], 52 | ) 53 | @pytest.mark.parametrize( 54 | "normalized", 55 | [True, False], 56 | ) 57 | @pytest.mark.parametrize( 58 | "image_init", 59 | [InitType.SENSE, InitType.ZERO_FILLED], 60 | ) 61 | def test_unet_2d(shape, num_filters, num_pool_layers, skip, normalized, image_init): 62 | model = Unet2d( 63 | fft2, 64 | ifft2, 65 | num_filters=num_filters, 66 | num_pool_layers=num_pool_layers, 67 | skip_connection=skip, 68 | normalized=normalized, 69 | image_initialization=image_init, 70 | dropout_probability=0.05, 71 | ).cpu() 72 | 73 | data = create_input(shape + [2]).cpu() 74 | sens = create_input(shape + [2]).cpu() 75 | 76 | out = model(data, sens) 77 | 78 | assert list(out.shape) == [shape[0]] + shape[2:] + [2] 79 | -------------------------------------------------------------------------------- /tests/tests_nn/unet_3d_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Tests for direct.nn.unet.unet_3d module.""" 15 | 16 | import numpy as np 17 | import pytest 18 | import torch 19 | 20 | from direct.nn.unet.unet_3d import NormUnetModel3d, UnetModel3d 21 | 22 | 23 | def create_input(shape): 24 | data = np.random.randn(*shape).copy() 25 | data = torch.from_numpy(data).float() 26 | 27 | return data 28 | 29 | 30 | @pytest.mark.parametrize( 31 | "shape", 32 | [ 33 | [2, 3, 20, 16, 16], 34 | [4, 2, 30, 20, 30], 35 | [4, 2, 21, 24, 20], 36 | ], 37 | ) 38 | @pytest.mark.parametrize( 39 | "num_filters", 40 | [4, 6, 8], 41 | ) 42 | @pytest.mark.parametrize( 43 | "num_pool_layers", 44 | [2, 3], 45 | ) 46 | @pytest.mark.parametrize( 47 | "normalized", 48 | [True, False], 49 | ) 50 | def test_unet_3d(shape, num_filters, num_pool_layers, normalized): 51 | model_architecture = NormUnetModel3d if normalized else UnetModel3d 52 | model = model_architecture( 53 | in_channels=shape[1], 54 | out_channels=shape[1], 55 | num_filters=num_filters, 56 | num_pool_layers=num_pool_layers, 57 | dropout_probability=0.05, 58 | ).cpu() 59 | 60 | data = create_input(shape).cpu() 61 | 62 | out = model(data) 63 | 64 | assert list(out.shape) == shape 65 | -------------------------------------------------------------------------------- /tests/tests_nn/varnet_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import pytest 15 | import torch 16 | 17 | from direct.data.transforms import fft2, ifft2 18 | from direct.nn.varnet.varnet import EndToEndVarNet 19 | 20 | 21 | def create_input(shape): 22 | data = torch.rand(shape).float() 23 | 24 | return data 25 | 26 | 27 | @pytest.mark.parametrize( 28 | "shape", 29 | [[4, 3, 32, 32], [4, 5, 40, 20]], 30 | ) 31 | @pytest.mark.parametrize( 32 | "num_layers", 33 | [2, 3, 6], 34 | ) 35 | @pytest.mark.parametrize( 36 | "num_filters", 37 | [2, 4], 38 | ) 39 | @pytest.mark.parametrize( 40 | "num_pull_layers", 41 | [2, 4], 42 | ) 43 | def test_varnet(shape, num_layers, num_filters, num_pull_layers): 44 | model = EndToEndVarNet(fft2, ifft2, num_layers, num_filters, num_pull_layers, in_channels=2).cpu() 45 | 46 | kspace = create_input(shape + [2]).cpu() 47 | mask = create_input([shape[0]] + [1] + shape[2:] + [1]).round().int().cpu() 48 | sens = create_input(shape + [2]).cpu() 49 | 50 | out = model(kspace, mask, sens) 51 | 52 | assert list(out.shape) == shape + [2] 53 | -------------------------------------------------------------------------------- /tests/tests_ssl/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_py//py:defs.bzl", "py_test") 2 | load("@direct_pip//:requirements.bzl", "requirement") 3 | 4 | REQUIREMENTS = [ 5 | "//direct:direct_lib", 6 | requirement("pytest"), 7 | ] 8 | 9 | py_test( 10 | name = "vsharp_engine_test", 11 | srcs = ["vsharp_engine_test.py"], 12 | pytest_main = True, 13 | deps = REQUIREMENTS, 14 | ) 15 | 16 | py_test( 17 | name = "varnet_engine_test", 18 | srcs = ["varnet_engine_test.py"], 19 | pytest_main = True, 20 | deps = REQUIREMENTS, 21 | ) 22 | 23 | py_test( 24 | name = "unet_engine_test", 25 | srcs = ["unet_engine_test.py"], 26 | pytest_main = True, 27 | deps = REQUIREMENTS, 28 | ) 29 | 30 | py_test( 31 | name = "ssl_test", 32 | srcs = ["ssl_test.py"], 33 | pytest_main = True, 34 | deps = REQUIREMENTS, 35 | ) 36 | -------------------------------------------------------------------------------- /tests/tests_utils/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_py//py:defs.bzl", "py_test") 2 | load("@direct_pip//:requirements.bzl", "requirement") 3 | 4 | REQUIREMENTS = [ 5 | "//direct:direct_lib", 6 | requirement("pytest"), 7 | ] 8 | 9 | py_test( 10 | name = "utils_test", 11 | srcs = ["utils_test.py"], 12 | pytest_main = True, 13 | deps = REQUIREMENTS, 14 | ) 15 | 16 | py_test( 17 | name = "io_test", 18 | srcs = ["io_test.py"], 19 | pytest_main = True, 20 | deps = REQUIREMENTS, 21 | ) 22 | 23 | py_test( 24 | name = "imports_test", 25 | srcs = ["imports_test.py"], 26 | pytest_main = True, 27 | deps = REQUIREMENTS, 28 | ) 29 | -------------------------------------------------------------------------------- /tests/tests_utils/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /tests/tests_utils/imports_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import pytest 15 | 16 | from direct.utils.imports import _module_available 17 | 18 | 19 | @pytest.mark.parametrize( 20 | ["module", "is_available"], 21 | [ 22 | ("torch", True), 23 | ("numpy", True), 24 | ("non-existent", False), 25 | ], 26 | ) 27 | def test_module_available(module, is_available): 28 | assert _module_available(module) == is_available 29 | -------------------------------------------------------------------------------- /tests/tests_utils/io_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import pytest 15 | 16 | from direct.utils.io import check_is_valid_url 17 | 18 | 19 | @pytest.mark.parametrize( 20 | ["path", "is_url"], 21 | [ 22 | ("https://s3.aiforoncology.nl/checkpoint.ckpt", True), 23 | ("http://localhost:8000/checkpoint.ckpt", True), 24 | ("ftp://aiforoncology.nl/checkpoint.ckpt", True), 25 | ("checkpoint.ckpt", False), 26 | ("/mnt/checkpoint.ckpt", False), 27 | ], 28 | ) 29 | def test_check_valid_url(path, is_url): 30 | if is_url: 31 | assert check_is_valid_url(path) 32 | else: 33 | assert not check_is_valid_url(path) 34 | -------------------------------------------------------------------------------- /tools/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_py//py:defs.bzl", "py_binary") 2 | 3 | py_binary( 4 | name = "parse_metrics_log", 5 | srcs = ["parse_metrics_log.py"], 6 | ) 7 | -------------------------------------------------------------------------------- /tools/README.md: -------------------------------------------------------------------------------- 1 | # Direct tools 2 | 3 | Scripts are provided: 4 | 5 | - To extract the best checkpoint based on `metrics.json`, use `parse_metrics_log.py`. 6 | -------------------------------------------------------------------------------- /tools/format/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@aspect_rules_js//js:defs.bzl", "js_library") 2 | load("@aspect_rules_lint//format:defs.bzl", "format_multirun") 3 | load("@npm//:prettier/package_json.bzl", prettier = "bin") 4 | 5 | package(default_visibility = ["//:__subpackages__"]) 6 | 7 | prettier.prettier_binary( 8 | name = "prettier", 9 | # Include this js_library and its dependencies in the runfiles (runtime dependencies) 10 | data = ["//:prettierrc"], 11 | # Allow the binary to be run outside bazel 12 | env = {"BAZEL_BINDIR": "."}, 13 | fixed_args = [ 14 | # `require` statements in the config file will be resolved relative to its location 15 | # Therefore to make it hermetic, prettier must be pointed at the copy of the config file 16 | # in the runfiles folder rather than the one in the source folder. 17 | "--config=\"$$JS_BINARY__RUNFILES\"/$(rlocationpath //:prettierrc)", 18 | # default log level is "log" which spams on success 19 | # https://prettier.io/docs/en/cli.html#--log-level 20 | # NB: prettier 2 names this loglevel, in prettier 3 it's renamed log-level, see 21 | # https://prettier.io/blog/2023/07/05/3.0.0.html#cli-1 22 | "--loglevel=warn", 23 | ], 24 | ) 25 | 26 | format_multirun( 27 | name = "format", 28 | markdown = ":prettier", 29 | python = "@aspect_rules_lint//format:ruff", 30 | shell = "@aspect_rules_lint//format:shfmt", 31 | starlark = "@buildifier_prebuilt//:buildifier", 32 | yaml = "@aspect_rules_lint//format:yamlfmt", 33 | ) 34 | -------------------------------------------------------------------------------- /tools/parse_metrics_log.py: -------------------------------------------------------------------------------- 1 | # Copyright 2025 AI for Oncology Research Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import argparse 15 | import json 16 | import pathlib 17 | 18 | import numpy as np 19 | 20 | 21 | def parse_args(): 22 | """Parse input arguments.""" 23 | parser = argparse.ArgumentParser( 24 | description="Find the best checkpoint for a given metric", 25 | formatter_class=argparse.ArgumentDefaultsHelpFormatter, 26 | ) 27 | parser.add_argument("metrics_path", type=pathlib.Path, help="Path to metrics.json") 28 | parser.add_argument("key", type=str, help="Key to use to find the best checkpoint.") 29 | parser.add_argument( 30 | "--max", 31 | dest="max", 32 | help="If True, this computes on maximum, else minimum value for key.", 33 | action="store_true", 34 | ) 35 | return parser.parse_args() 36 | 37 | 38 | def main(): 39 | args = parse_args() 40 | 41 | with open(args.metrics_path / "metrics.json", "r") as f: 42 | data = f.readlines() 43 | data = [json.loads(_) for _ in data] 44 | x = np.asarray([(int(_["iteration"]), _[args.key]) for _ in data if args.key in _]) 45 | 46 | if args.max: 47 | out = x[np.where(x[:, 1] == x[:, 1].max())][0] 48 | else: 49 | out = x[np.where(x[:, 1] == x[:, 1].min())][0] 50 | 51 | print(f"{args.key} - {int(out[0])}: {out[1]}") 52 | 53 | 54 | if __name__ == "__main__": 55 | main() 56 | --------------------------------------------------------------------------------