openads 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of openads might be problematic. Click here for more details.
- openads-2.0.0/CHANGELOG.md +112 -0
- openads-2.0.0/LICENSE +24 -0
- openads-2.0.0/MANIFEST.in +10 -0
- openads-2.0.0/PKG-INFO +342 -0
- openads-2.0.0/README.md +285 -0
- openads-2.0.0/docs/architecture.md +154 -0
- openads-2.0.0/docs/configuration.md +400 -0
- openads-2.0.0/docs/extending.md +269 -0
- openads-2.0.0/docs/refactor/decisions.md +627 -0
- openads-2.0.0/docs/refactor/functions.md +1683 -0
- openads-2.0.0/docs/refactor/mapping.md +464 -0
- openads-2.0.0/docs/refactor/notes/apps.md +195 -0
- openads-2.0.0/docs/refactor/notes/consolidation_a.md +184 -0
- openads-2.0.0/docs/refactor/notes/consolidation_b.md +179 -0
- openads-2.0.0/docs/refactor/notes/consolidation_c.md +102 -0
- openads-2.0.0/docs/refactor/notes/consolidation_d.md +179 -0
- openads-2.0.0/docs/refactor/notes/gen_ttp.md +82 -0
- openads-2.0.0/docs/refactor/notes/inference_dwi.md +155 -0
- openads-2.0.0/docs/refactor/notes/inference_pwi.md +99 -0
- openads-2.0.0/docs/refactor/notes/masking.md +119 -0
- openads-2.0.0/docs/refactor/notes/prepdata.md +148 -0
- openads-2.0.0/docs/refactor/notes/registration.md +146 -0
- openads-2.0.0/docs/refactor/notes/report.md +115 -0
- openads-2.0.0/docs/refactor/stage_guide.md +97 -0
- openads-2.0.0/docs/user_guide.md +157 -0
- openads-2.0.0/pyproject.toml +181 -0
- openads-2.0.0/setup.cfg +4 -0
- openads-2.0.0/src/openads/__init__.py +77 -0
- openads-2.0.0/src/openads/__main__.py +8 -0
- openads-2.0.0/src/openads/_lazy.py +70 -0
- openads-2.0.0/src/openads/_version.py +5 -0
- openads-2.0.0/src/openads/assets/__init__.py +162 -0
- openads-2.0.0/src/openads/assets/fetch.py +157 -0
- openads-2.0.0/src/openads/assets/manifest.json +545 -0
- openads-2.0.0/src/openads/assets/manifest.py +105 -0
- openads-2.0.0/src/openads/cli/__init__.py +5 -0
- openads-2.0.0/src/openads/cli/main.py +408 -0
- openads-2.0.0/src/openads/config/__init__.py +32 -0
- openads-2.0.0/src/openads/config/convert.py +155 -0
- openads-2.0.0/src/openads/config/defaults/dwi.yaml +123 -0
- openads-2.0.0/src/openads/config/defaults/keep_files.yaml +132 -0
- openads-2.0.0/src/openads/config/defaults/pwi.yaml +204 -0
- openads-2.0.0/src/openads/config/loader.py +254 -0
- openads-2.0.0/src/openads/config/schema.py +177 -0
- openads-2.0.0/src/openads/core/__init__.py +23 -0
- openads-2.0.0/src/openads/core/artifacts.py +192 -0
- openads-2.0.0/src/openads/core/layout.py +1180 -0
- openads-2.0.0/src/openads/core/result.py +55 -0
- openads-2.0.0/src/openads/core/types.py +42 -0
- openads-2.0.0/src/openads/core/workspace.py +327 -0
- openads-2.0.0/src/openads/errors.py +106 -0
- openads-2.0.0/src/openads/inputs/__init__.py +45 -0
- openads-2.0.0/src/openads/inputs/builtin_derivations.py +208 -0
- openads-2.0.0/src/openads/inputs/derivations.py +297 -0
- openads-2.0.0/src/openads/inputs/discovery.py +284 -0
- openads-2.0.0/src/openads/inputs/sidecars.py +102 -0
- openads-2.0.0/src/openads/inputs/spec.py +165 -0
- openads-2.0.0/src/openads/io/__init__.py +26 -0
- openads-2.0.0/src/openads/io/ants.py +244 -0
- openads-2.0.0/src/openads/io/cleanup.py +209 -0
- openads-2.0.0/src/openads/io/figures.py +48 -0
- openads-2.0.0/src/openads/io/json.py +39 -0
- openads-2.0.0/src/openads/io/manifest.py +150 -0
- openads-2.0.0/src/openads/io/nifti.py +224 -0
- openads-2.0.0/src/openads/io/surfa.py +53 -0
- openads-2.0.0/src/openads/io/tables.py +58 -0
- openads-2.0.0/src/openads/io/text.py +25 -0
- openads-2.0.0/src/openads/io/transforms.py +106 -0
- openads-2.0.0/src/openads/logging.py +37 -0
- openads-2.0.0/src/openads/models/__init__.py +19 -0
- openads-2.0.0/src/openads/models/checkpoints.py +112 -0
- openads-2.0.0/src/openads/models/dagmnet_dwi.py +679 -0
- openads-2.0.0/src/openads/models/dagmnet_pwi.py +199 -0
- openads-2.0.0/src/openads/models/device.py +95 -0
- openads-2.0.0/src/openads/models/synthstrip.py +300 -0
- openads-2.0.0/src/openads/ops/__init__.py +83 -0
- openads-2.0.0/src/openads/ops/adc.py +43 -0
- openads-2.0.0/src/openads/ops/atlas.py +858 -0
- openads-2.0.0/src/openads/ops/bimodal.py +84 -0
- openads-2.0.0/src/openads/ops/hp_symclassic.py +227 -0
- openads-2.0.0/src/openads/ops/inference_dwi.py +152 -0
- openads-2.0.0/src/openads/ops/inference_pwi.py +184 -0
- openads-2.0.0/src/openads/ops/interpret.py +392 -0
- openads-2.0.0/src/openads/ops/masks.py +97 -0
- openads-2.0.0/src/openads/ops/metrics.py +122 -0
- openads-2.0.0/src/openads/ops/morphology.py +190 -0
- openads-2.0.0/src/openads/ops/normalize.py +289 -0
- openads-2.0.0/src/openads/ops/padding.py +62 -0
- openads-2.0.0/src/openads/ops/postprocess.py +75 -0
- openads-2.0.0/src/openads/ops/postprocess_hp.py +104 -0
- openads-2.0.0/src/openads/ops/prob_is.py +91 -0
- openads-2.0.0/src/openads/ops/pseudo_dwi.py +95 -0
- openads-2.0.0/src/openads/ops/radiology.py +964 -0
- openads-2.0.0/src/openads/ops/registration.py +63 -0
- openads-2.0.0/src/openads/ops/ttp/__init__.py +17 -0
- openads-2.0.0/src/openads/ops/ttp/fallback.py +54 -0
- openads-2.0.0/src/openads/ops/ttp/fill.py +177 -0
- openads-2.0.0/src/openads/ops/ttp/gd.py +145 -0
- openads-2.0.0/src/openads/ops/ttp/motion.py +16 -0
- openads-2.0.0/src/openads/ops/ttp/peaks.py +96 -0
- openads-2.0.0/src/openads/ops/ttp/slicetime.py +152 -0
- openads-2.0.0/src/openads/ops/viz.py +273 -0
- openads-2.0.0/src/openads/ops/volume_order.py +137 -0
- openads-2.0.0/src/openads/ops/volumes.py +69 -0
- openads-2.0.0/src/openads/pipelines/__init__.py +59 -0
- openads-2.0.0/src/openads/pipelines/base.py +600 -0
- openads-2.0.0/src/openads/pipelines/combined.py +135 -0
- openads-2.0.0/src/openads/pipelines/dwi.py +50 -0
- openads-2.0.0/src/openads/pipelines/pwi.py +53 -0
- openads-2.0.0/src/openads/py.typed +0 -0
- openads-2.0.0/src/openads/registry/__init__.py +331 -0
- openads-2.0.0/src/openads/registry/base.py +97 -0
- openads-2.0.0/src/openads/stages/__init__.py +261 -0
- openads-2.0.0/src/openads/stages/base.py +378 -0
- openads-2.0.0/src/openads/stages/cleanup/__init__.py +55 -0
- openads-2.0.0/src/openads/stages/cleanup/methods/__init__.py +6 -0
- openads-2.0.0/src/openads/stages/cleanup/methods/keep_manifest.py +43 -0
- openads-2.0.0/src/openads/stages/cleanup/params.py +29 -0
- openads-2.0.0/src/openads/stages/gen_mask/__init__.py +53 -0
- openads-2.0.0/src/openads/stages/gen_mask/common.py +33 -0
- openads-2.0.0/src/openads/stages/gen_mask/methods/__init__.py +12 -0
- openads-2.0.0/src/openads/stages/gen_mask/methods/synthstrip.py +88 -0
- openads-2.0.0/src/openads/stages/gen_mask/params.py +37 -0
- openads-2.0.0/src/openads/stages/gen_ttp/__init__.py +55 -0
- openads-2.0.0/src/openads/stages/gen_ttp/common.py +169 -0
- openads-2.0.0/src/openads/stages/gen_ttp/methods/__init__.py +13 -0
- openads-2.0.0/src/openads/stages/gen_ttp/methods/argmin.py +47 -0
- openads-2.0.0/src/openads/stages/gen_ttp/methods/peak_legacy.py +393 -0
- openads-2.0.0/src/openads/stages/gen_ttp/moco.py +262 -0
- openads-2.0.0/src/openads/stages/gen_ttp/params.py +248 -0
- openads-2.0.0/src/openads/stages/inference/__init__.py +91 -0
- openads-2.0.0/src/openads/stages/inference/methods/__init__.py +13 -0
- openads-2.0.0/src/openads/stages/inference/methods/dagmnet_dwi.py +124 -0
- openads-2.0.0/src/openads/stages/inference/methods/dagmnet_pwi.py +280 -0
- openads-2.0.0/src/openads/stages/inference/params.py +144 -0
- openads-2.0.0/src/openads/stages/prepdata/__init__.py +60 -0
- openads-2.0.0/src/openads/stages/prepdata/methods/__init__.py +13 -0
- openads-2.0.0/src/openads/stages/prepdata/methods/standard.py +267 -0
- openads-2.0.0/src/openads/stages/prepdata/params.py +34 -0
- openads-2.0.0/src/openads/stages/registration/__init__.py +151 -0
- openads-2.0.0/src/openads/stages/registration/methods/__init__.py +12 -0
- openads-2.0.0/src/openads/stages/registration/methods/ants_affine_syn.py +337 -0
- openads-2.0.0/src/openads/stages/registration/params.py +78 -0
- openads-2.0.0/src/openads/stages/report/__init__.py +107 -0
- openads-2.0.0/src/openads/stages/report/methods/__init__.py +13 -0
- openads-2.0.0/src/openads/stages/report/methods/dwi_standard.py +96 -0
- openads-2.0.0/src/openads/stages/report/methods/pwi_standard.py +64 -0
- openads-2.0.0/src/openads/stages/report/params.py +103 -0
- openads-2.0.0/src/openads/stages/report/steps/__init__.py +13 -0
- openads-2.0.0/src/openads/stages/report/steps/figures.py +230 -0
- openads-2.0.0/src/openads/stages/report/steps/interpretation.py +83 -0
- openads-2.0.0/src/openads/stages/report/steps/qfv.py +113 -0
- openads-2.0.0/src/openads/stages/report/steps/radiology.py +281 -0
- openads-2.0.0/src/openads/stages/skull_strip/__init__.py +58 -0
- openads-2.0.0/src/openads/stages/skull_strip/methods/__init__.py +12 -0
- openads-2.0.0/src/openads/stages/skull_strip/methods/apply_mask.py +86 -0
- openads-2.0.0/src/openads/stages/skull_strip/params.py +36 -0
- openads-2.0.0/src/openads/stages/ttpadc_coreg/__init__.py +119 -0
- openads-2.0.0/src/openads/stages/ttpadc_coreg/methods/__init__.py +12 -0
- openads-2.0.0/src/openads/stages/ttpadc_coreg/methods/ants_affine_masks.py +157 -0
- openads-2.0.0/src/openads/stages/ttpadc_coreg/params.py +36 -0
- openads-2.0.0/src/openads/steps/__init__.py +38 -0
- openads-2.0.0/src/openads/steps/normalize.py +46 -0
- openads-2.0.0/src/openads/steps/segmentation_dwi.py +274 -0
- openads-2.0.0/src/openads/steps/segmentation_pwi.py +285 -0
- openads-2.0.0/src/openads/steps/warp.py +54 -0
- openads-2.0.0/src/openads.egg-info/PKG-INFO +342 -0
- openads-2.0.0/src/openads.egg-info/SOURCES.txt +230 -0
- openads-2.0.0/src/openads.egg-info/dependency_links.txt +1 -0
- openads-2.0.0/src/openads.egg-info/entry_points.txt +3 -0
- openads-2.0.0/src/openads.egg-info/requires.txt +29 -0
- openads-2.0.0/src/openads.egg-info/top_level.txt +1 -0
- openads-2.0.0/tests/conftest.py +95 -0
- openads-2.0.0/tests/data/expected_tree_sub-02e8eb42.txt +156 -0
- openads-2.0.0/tests/unit/stubs.py +73 -0
- openads-2.0.0/tests/unit/test_app_gui_command.py +165 -0
- openads-2.0.0/tests/unit/test_app_scripts.py +159 -0
- openads-2.0.0/tests/unit/test_app_server.py +230 -0
- openads-2.0.0/tests/unit/test_assets.py +92 -0
- openads-2.0.0/tests/unit/test_assets_download.py +65 -0
- openads-2.0.0/tests/unit/test_cleanup.py +212 -0
- openads-2.0.0/tests/unit/test_cli.py +226 -0
- openads-2.0.0/tests/unit/test_config.py +173 -0
- openads-2.0.0/tests/unit/test_convert.py +69 -0
- openads-2.0.0/tests/unit/test_derivations.py +102 -0
- openads-2.0.0/tests/unit/test_docs_examples.py +171 -0
- openads-2.0.0/tests/unit/test_gen_mask.py +166 -0
- openads-2.0.0/tests/unit/test_gen_mask_plugin.py +92 -0
- openads-2.0.0/tests/unit/test_gen_ttp_moco.py +105 -0
- openads-2.0.0/tests/unit/test_gen_ttp_stage.py +184 -0
- openads-2.0.0/tests/unit/test_import.py +76 -0
- openads-2.0.0/tests/unit/test_inference_dwi.py +184 -0
- openads-2.0.0/tests/unit/test_inference_dwi_plugin.py +65 -0
- openads-2.0.0/tests/unit/test_inference_pwi.py +154 -0
- openads-2.0.0/tests/unit/test_inputs.py +173 -0
- openads-2.0.0/tests/unit/test_io_ants.py +67 -0
- openads-2.0.0/tests/unit/test_io_misc.py +122 -0
- openads-2.0.0/tests/unit/test_io_nifti.py +70 -0
- openads-2.0.0/tests/unit/test_io_surfa.py +27 -0
- openads-2.0.0/tests/unit/test_layout.py +152 -0
- openads-2.0.0/tests/unit/test_models.py +154 -0
- openads-2.0.0/tests/unit/test_ops_adc.py +107 -0
- openads-2.0.0/tests/unit/test_ops_atlas.py +118 -0
- openads-2.0.0/tests/unit/test_ops_bimodal.py +28 -0
- openads-2.0.0/tests/unit/test_ops_hp_symclassic.py +106 -0
- openads-2.0.0/tests/unit/test_ops_inference_pwi.py +85 -0
- openads-2.0.0/tests/unit/test_ops_interpret.py +69 -0
- openads-2.0.0/tests/unit/test_ops_masks.py +73 -0
- openads-2.0.0/tests/unit/test_ops_metrics.py +34 -0
- openads-2.0.0/tests/unit/test_ops_morphology.py +31 -0
- openads-2.0.0/tests/unit/test_ops_normalize.py +96 -0
- openads-2.0.0/tests/unit/test_ops_padding.py +20 -0
- openads-2.0.0/tests/unit/test_ops_postprocess.py +26 -0
- openads-2.0.0/tests/unit/test_ops_postprocess_hp.py +42 -0
- openads-2.0.0/tests/unit/test_ops_prob_is.py +26 -0
- openads-2.0.0/tests/unit/test_ops_radiology.py +133 -0
- openads-2.0.0/tests/unit/test_ops_ttp_numerics.py +178 -0
- openads-2.0.0/tests/unit/test_ops_viz.py +46 -0
- openads-2.0.0/tests/unit/test_ops_volumes.py +29 -0
- openads-2.0.0/tests/unit/test_packaging.py +145 -0
- openads-2.0.0/tests/unit/test_pipelines.py +203 -0
- openads-2.0.0/tests/unit/test_prepdata.py +290 -0
- openads-2.0.0/tests/unit/test_registration.py +145 -0
- openads-2.0.0/tests/unit/test_registry.py +197 -0
- openads-2.0.0/tests/unit/test_report_stage.py +135 -0
- openads-2.0.0/tests/unit/test_run_stage.py +181 -0
- openads-2.0.0/tests/unit/test_skull_strip.py +137 -0
- openads-2.0.0/tests/unit/test_stage_base.py +190 -0
- openads-2.0.0/tests/unit/test_stage_plugin.py +245 -0
- openads-2.0.0/tests/unit/test_steps.py +149 -0
- openads-2.0.0/tests/unit/test_ttpadc_coreg.py +112 -0
- openads-2.0.0/tests/unit/test_workspace.py +98 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
|
+
|
|
6
|
+
## [2.0.0] - unreleased
|
|
7
|
+
|
|
8
|
+
### Changed for the PyPI release (ADR-041)
|
|
9
|
+
- Import package renamed from `ads` to `openads` (`import openads as ads`); a
|
|
10
|
+
different PyPI distribution also installs a module `ads`. Console scripts:
|
|
11
|
+
`openads` and `ads`. Plugin entry-point groups: `openads.plugins`, `openads.stages`.
|
|
12
|
+
- `openads assets download [--kind model|atlas|example] [--assets-dir] [--url]`
|
|
13
|
+
(and `openads.download_assets()`): fetches the assets from `assets/` of
|
|
14
|
+
farialab/OpenADS on GitHub (pinned commit; `ADS_ASSETS_URL` for another mirror),
|
|
15
|
+
verifies SHA-256, prints the JHU licence it accepts.
|
|
16
|
+
- `runtime.device` defaults to `auto`: CUDA when visible, else the CPU (logged).
|
|
17
|
+
- The wheel and sdist contain code only; the sdist no longer carries the
|
|
18
|
+
repository-only regression harness (`tools/`) or verification reports.
|
|
19
|
+
|
|
20
|
+
A refactor of OpenADS 1.0 (HEAD 6c378d1) into one library with the same output
|
|
21
|
+
tree and the same numerics. Every original function is mapped in
|
|
22
|
+
`docs/refactor/mapping.md`; the decisions are ADRs in
|
|
23
|
+
`docs/refactor/decisions.md`; the regression evidence (stage gates and
|
|
24
|
+
verification reports) stays in the development repository.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- Package layout `src/openads` with layers cli -> pipelines -> stages -> steps ->
|
|
28
|
+
{ops, io, models, core/config/registry/inputs/assets}
|
|
29
|
+
(`docs/architecture.md`).
|
|
30
|
+
- Python API: `openads.run_dwi`, `openads.run_pwi`, `openads.run_combined`,
|
|
31
|
+
`openads.run_stage` (run one stage on an existing output folder),
|
|
32
|
+
`openads.load_config`, `openads.SubjectWorkspace`; `import openads` loads no heavy
|
|
33
|
+
library.
|
|
34
|
+
- Method registry: `@register(stage, name, params=..., modalities=...)`,
|
|
35
|
+
modality-aware (`Registry.available(stage, modality)`), built-in methods
|
|
36
|
+
auto-discovered (`openads.registry.import_submodules`), plugins through the
|
|
37
|
+
`openads.plugins` entry-point group.
|
|
38
|
+
- Stage registry: `openads.stages.register_stage`, entry-point group `openads.stages`,
|
|
39
|
+
`Pipeline.with_stages(...)`, `OutputLayout.extended(...)`; cleanup keeps the
|
|
40
|
+
layout's non-transient artifacts.
|
|
41
|
+
- `openads.steps`: shared file-level building blocks (`write_normalized`,
|
|
42
|
+
`write_dwi_segmentation`, PWI segmentation helpers, `warp_to_affsyn`).
|
|
43
|
+
- `openads.ops`: the 1.0 numerics as pure, unit-tested functions (ADC, volume
|
|
44
|
+
order, pseudo-DWI, masks, KDE normaliser, padding, DAGMNet inference
|
|
45
|
+
helpers, symClassic, post-processing, metrics, TTP, atlas QFV, radiology
|
|
46
|
+
report, SHAP, figures).
|
|
47
|
+
- Typed configuration with packaged defaults (`openads/config/defaults`), strict
|
|
48
|
+
keys, `--config` / `--set key=value` layering, `ads config show`.
|
|
49
|
+
- One output-layout table (`openads.core.layout`) for all 156 files of the 1.0
|
|
50
|
+
tree; data import with input specs, discovery and named derivations
|
|
51
|
+
(`openads.inputs`).
|
|
52
|
+
- Asset resolution (`ADS_ASSETS_DIR`, repository checkout, user cache) with a
|
|
53
|
+
SHA-256 manifest: `ads assets where|verify`.
|
|
54
|
+
- CLI: `ads dwi|pwi|combined|stages list|stages run|config show|assets|version`
|
|
55
|
+
(in-process), `--set`, `--dwi-set`/`--pwi-set`; stage help generated from
|
|
56
|
+
the stage specs; `ads dwi|pwi --dry-run` and `ads stages list --json` print
|
|
57
|
+
JSON for other tools.
|
|
58
|
+
- Client helpers: `Pipeline.plan()` / `openads.RunPlan` (stages and output folder
|
|
59
|
+
without running), `openads.example_subjects()`, `openads.parse_ref` /
|
|
60
|
+
`openads.stages.resolve_ref` (stage input/output references).
|
|
61
|
+
- User documentation: README, `docs/user_guide.md`, `docs/configuration.md`
|
|
62
|
+
(generated by `tools/gen_config_doc.py`), `docs/extending.md`,
|
|
63
|
+
`docs/architecture.md`.
|
|
64
|
+
- Refactor records: `docs/refactor/{mapping,decisions,harness,functions}.md`,
|
|
65
|
+
verification reports, regression harness in `tools/` (multi-run noise
|
|
66
|
+
envelope, population noise for motion-correction transforms, strict2
|
|
67
|
+
reference with the cuDNN autotuner pinned off).
|
|
68
|
+
|
|
69
|
+
- Thin clients on the public API, outside the wheel: web server `apps/server`
|
|
70
|
+
(FastAPI, 1.0 endpoints + a job API), Qt GUI launcher `apps/gui`, web pages
|
|
71
|
+
`apps/web`, Docker images `docker/`, 1.0-compatible `scripts/*.sh` wrappers.
|
|
72
|
+
|
|
73
|
+
### Changed
|
|
74
|
+
- Faster DWI inference (ADR-039): DAGMNet-DWI's transposed convolutions run on
|
|
75
|
+
the model device (`stages.inference.params.transpose_backend: torch`; 1.0's
|
|
76
|
+
CPU NumPy loop is `numpy`): the stage takes ~10 s instead of ~60 s, with
|
|
77
|
+
identical output files on the example subject. `runtime.cudnn_benchmark`
|
|
78
|
+
defaults to `false` (1.0: `true`): SynthStrip 2.4 s instead of 5.8 s and no
|
|
79
|
+
run-to-run mask flips from the cuDNN autotuner.
|
|
80
|
+
- Faster HP_symClassic generation (ADR-040): vectorised supervoxel selection
|
|
81
|
+
and threaded per-slice SLIC, ~2 s instead of ~17 s, bit-identical masks.
|
|
82
|
+
- Distribution renamed to `openads` (import package and console script stay
|
|
83
|
+
`ads`).
|
|
84
|
+
- The CLI no longer shells out to `scripts/run_ads_*.py`; the shell wrappers
|
|
85
|
+
are replaced by `ads dwi|pwi|combined`.
|
|
86
|
+
- Fail fast: no silent CUDA->CPU or `cuda:1`->`cuda:0` fallback (`DeviceError`);
|
|
87
|
+
places where 1.0 logged an error and continued now raise (listed per stage in
|
|
88
|
+
the ADRs). Tolerated, configurable exceptions: the motion-correction timeout
|
|
89
|
+
skip and the argmin TTP for PWI without a JSON sidecar
|
|
90
|
+
(`no_json_policy: legacy`).
|
|
91
|
+
- Motion correction runs in a `fork`ed child by default (as 1.0); `spawn` is
|
|
92
|
+
opt-in (`motion_correction.start_method`).
|
|
93
|
+
- Packaging: PEP 639 license metadata (`LicenseRef-JHU-Non-Profit-Research`),
|
|
94
|
+
`setuptools>=77`, dependency ranges instead of exact pins, `MANIFEST.in`
|
|
95
|
+
(no assets and no verification reports in the sdist). Every dependency of
|
|
96
|
+
`ads dwi --all` / `ads pwi --all` is in the default install.
|
|
97
|
+
- Default output root is `output` relative to the working directory.
|
|
98
|
+
- The `report` numerics (`openads.ops.interpret`, `openads.ops.radiology`) import
|
|
99
|
+
pandas lazily.
|
|
100
|
+
|
|
101
|
+
### Removed
|
|
102
|
+
- Unused 1.0 modules (dead parallel implementations, unused services, domain
|
|
103
|
+
dataclasses, legacy loaders); each is listed with its reason in
|
|
104
|
+
`docs/refactor/mapping.md`.
|
|
105
|
+
- `tqdm` dependency (never imported).
|
|
106
|
+
|
|
107
|
+
### Known behaviour kept from 1.0
|
|
108
|
+
- Run-to-run differences of the original method: multithreaded ANTs
|
|
109
|
+
registration, the cuDNN autotuner (1-10 voxels of a network mask), and the
|
|
110
|
+
bistable KDE ADC normaliser (ADR-020 to ADR-022). A 1-voxel brain-mask
|
|
111
|
+
difference can change the downstream registration (ADR-026).
|
|
112
|
+
- Numerical quirks of 1.0 are kept verbatim and listed in ADR-034.
|
openads-2.0.0/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
JHU Non-Profit Research Software License Agreement
|
|
2
|
+
|
|
3
|
+
This license agreement ("License") is effective automatically between you, a non-profit academic or non-profit research institution ("Licensee"), and The Johns Hopkins University (“JHU”) for use of the software with which this License is distributed (“Software”) so long as Licensee complies with the following terms and conditions:
|
|
4
|
+
|
|
5
|
+
The requirement to acknowledge the copyright of JHU as follows: “© The Johns Hopkins University” and copyrights of any incorporated third-party software as described in the associated documentation.
|
|
6
|
+
|
|
7
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the source code must retain the above copyright notice and these terms and conditions.
|
|
8
|
+
|
|
9
|
+
Neither the name of JHU nor its affiliates may be used to endorse or promote products derived from this Software without specific prior written permission from an authorized JHU representative.
|
|
10
|
+
|
|
11
|
+
THIS SOFTWARE IS PROVIDED BY JHU "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JHU BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. LICENSEE AGREES TO DEFEND, INDEMNIFY AND HOLD HARMLESS JHU FOR ANY CLAIMS ARISING FROM LICENSEE’S USE OF THE SOFTWARE TO THE FULLEST EXTENT PERMITTED BY LAW.
|
|
12
|
+
|
|
13
|
+
Only a non-exclusive, nontransferable license is granted to Licensee to use the Software for non-commercial, non-profit purposes. Commercial use of the Software, including research use by for-profit entities, requires a separately executed written license agreement.
|
|
14
|
+
|
|
15
|
+
Licensee agrees that it will use the Software, and any modifications, improvements, or derivatives of the Software that the Licensee may create (collectively, "Improvements") solely for non-commercial, non-profit purposes and shall not distribute or transfer the Software or Improvements to any third-party without requiring that such third-parties adhere to the terms of this License. Licensee agrees that any Improvements made by Licensee shall be subject to the same terms and conditions as the Software.
|
|
16
|
+
|
|
17
|
+
Licensee acknowledges that JHU holds copyright in the Software or portions of the Software, and that the Software may incorporate third-party software which may be subject to additional terms and conditions. Licensee shall comply with any additional terms and conditions appliable to such third-party software.
|
|
18
|
+
|
|
19
|
+
Licensee agrees that any publication of results obtained with the Software will acknowledge its use by an appropriate citation.
|
|
20
|
+
|
|
21
|
+
Licensee’s rights under this License terminate automatically without notice from JHU if Licensee fail to comply with any term(s) of this License.
|
|
22
|
+
|
|
23
|
+
This License shall be governed by the laws of the State of Maryland, excluding the application of its conflicts of law rules. Licensee agrees that any dispute shall be appropriate only in the state and federal courts located within the State of Maryland.
|
|
24
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# sdist contents. The wheel only ships src/openads (+ package data in pyproject.toml).
|
|
2
|
+
include LICENSE README.md CHANGELOG.md pyproject.toml MANIFEST.in
|
|
3
|
+
graft src/openads
|
|
4
|
+
graft tests
|
|
5
|
+
graft docs
|
|
6
|
+
# Models, atlases and examples (~320 MB) are never packaged; see ADS_ASSETS_DIR.
|
|
7
|
+
prune assets
|
|
8
|
+
# Gate reports reference local baseline runs; they stay in the repository only.
|
|
9
|
+
prune docs/refactor/verification
|
|
10
|
+
global-exclude __pycache__ *.py[cod] *.so .DS_Store
|
openads-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openads
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: OpenADS: acute stroke detection pipelines for DWI and PWI MRI
|
|
5
|
+
Author: Joshua Shun Liu, Chin-Fu Liu
|
|
6
|
+
Author-email: "Andreia V. Faria" <andreia.faria@gmail.com>
|
|
7
|
+
License-Expression: LicenseRef-JHU-Non-Profit-Research
|
|
8
|
+
Project-URL: Homepage, https://github.com/farialab/OpenADS
|
|
9
|
+
Project-URL: Repository, https://github.com/farialab/OpenADS
|
|
10
|
+
Project-URL: Issues, https://github.com/farialab/OpenADS/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/farialab/OpenADS/blob/main/CHANGELOG.md
|
|
12
|
+
Project-URL: Documentation, https://github.com/farialab/OpenADS/tree/main/docs
|
|
13
|
+
Keywords: stroke,MRI,DWI,PWI,segmentation,neuroimaging
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Environment :: GPU :: NVIDIA CUDA
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: Intended Audience :: Healthcare Industry
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: <3.13,>=3.10
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Requires-Dist: antspyx<0.7,>=0.5.4
|
|
31
|
+
Requires-Dist: joblib<2,>=1.4
|
|
32
|
+
Requires-Dist: matplotlib<4,>=3.9
|
|
33
|
+
Requires-Dist: nibabel<6,>=5.3
|
|
34
|
+
Requires-Dist: numpy<3,>=2.0
|
|
35
|
+
Requires-Dist: pandas<3,>=2.2
|
|
36
|
+
Requires-Dist: pyyaml<7,>=6.0
|
|
37
|
+
Requires-Dist: scikit-image<1,>=0.24
|
|
38
|
+
Requires-Dist: scikit-learn<2,>=1.7
|
|
39
|
+
Requires-Dist: scipy<2,>=1.13
|
|
40
|
+
Requires-Dist: shap<1,>=0.50
|
|
41
|
+
Requires-Dist: surfa<0.7,>=0.6.3
|
|
42
|
+
Requires-Dist: torch<3,>=2.5
|
|
43
|
+
Provides-Extra: dev
|
|
44
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
45
|
+
Requires-Dist: ruff>=0.13; extra == "dev"
|
|
46
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
47
|
+
Requires-Dist: types-PyYAML; extra == "dev"
|
|
48
|
+
Requires-Dist: build; extra == "dev"
|
|
49
|
+
Requires-Dist: twine; extra == "dev"
|
|
50
|
+
Provides-Extra: server
|
|
51
|
+
Requires-Dist: fastapi>=0.110; extra == "server"
|
|
52
|
+
Requires-Dist: uvicorn[standard]>=0.30; extra == "server"
|
|
53
|
+
Requires-Dist: python-multipart; extra == "server"
|
|
54
|
+
Provides-Extra: gui
|
|
55
|
+
Requires-Dist: PyQt5>=5.15; extra == "gui"
|
|
56
|
+
Dynamic: license-file
|
|
57
|
+
|
|
58
|
+
# OpenADS
|
|
59
|
+
|
|
60
|
+
OpenADS is an end-to-end pipeline for acute stroke MRI. For one subject folder
|
|
61
|
+
it runs preprocessing, brain masking, MNI registration, lesion segmentation
|
|
62
|
+
(DWI) or hypoperfusion segmentation (PWI), and report generation, and writes
|
|
63
|
+
NIfTI masks, metrics, QFV/lesion-load tables, a text radiological report, SHAP
|
|
64
|
+
interpretation PDFs and PNG figures.
|
|
65
|
+
|
|
66
|
+
- Distribution name: `openads`. Import package: `openads` (`import openads as ads`).
|
|
67
|
+
Console scripts: `openads` and `ads` (the same program).
|
|
68
|
+
- Version 2.0 is a refactor of OpenADS 1.0. It writes the same output tree with
|
|
69
|
+
the same numerics (see [`docs/refactor/`](docs/refactor/)), and adds a
|
|
70
|
+
Python API, a method and stage plugin system, and typed configuration.
|
|
71
|
+
- Platform: Linux, Python 3.10-3.12. An NVIDIA GPU with CUDA is used when
|
|
72
|
+
present; without one OpenADS runs on the CPU (slower).
|
|
73
|
+
|
|
74
|
+
## Install
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
python -m venv .venv && source .venv/bin/activate
|
|
78
|
+
python -m pip install -U pip
|
|
79
|
+
python -m pip install openads
|
|
80
|
+
openads assets download # model weights, atlases, example subjects (~360 MB, once)
|
|
81
|
+
openads version
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
From a Jupyter notebook, install into the notebook's own environment with `%pip`
|
|
85
|
+
(not `!pip`, which may use another Python):
|
|
86
|
+
|
|
87
|
+
```ipython
|
|
88
|
+
%pip install openads
|
|
89
|
+
!openads assets download
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**CPU or GPU.** `pip install openads` installs torch as well. On Linux, PyPI's
|
|
93
|
+
torch is the CUDA build (large); on Windows and macOS it is the CPU build. To
|
|
94
|
+
choose explicitly, install torch first and then OpenADS, which keeps the torch
|
|
95
|
+
you already have:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip install torch --index-url https://download.pytorch.org/whl/cpu # CPU only, small
|
|
99
|
+
# or: uv pip install --torch-backend=auto openads # uv picks CUDA or CPU for this machine
|
|
100
|
+
pip install openads
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
In an environment that already has a CUDA build of torch (>= 2.5), `pip install
|
|
104
|
+
openads` uses it. The default `runtime.device: auto` takes the GPU when torch
|
|
105
|
+
sees one and otherwise the CPU, and logs which; `--set runtime.device=cuda`
|
|
106
|
+
insists on the GPU and fails without one.
|
|
107
|
+
|
|
108
|
+
Every dependency that `ads dwi --all` and `ads pwi --all` need is in the
|
|
109
|
+
default install (torch, antspyx, nibabel, scipy, scikit-image, scikit-learn,
|
|
110
|
+
pandas, matplotlib, shap, surfa, joblib, PyYAML). Optional extras: `dev`
|
|
111
|
+
(pytest, ruff, mypy, build, twine), `server` and `gui` (for the web server and
|
|
112
|
+
GUI launcher, see [GUI / server / Docker](#gui--server--docker)).
|
|
113
|
+
|
|
114
|
+
For development, from a source checkout: `pip install -e ".[dev]"`.
|
|
115
|
+
|
|
116
|
+
## Assets (models, atlases, examples)
|
|
117
|
+
|
|
118
|
+
The model checkpoints, atlases and example subjects are not in the wheel.
|
|
119
|
+
`openads assets download` fetches them from the `assets/` folder of the official
|
|
120
|
+
[farialab/OpenADS](https://github.com/farialab/OpenADS) repository (pinned commit), checks
|
|
121
|
+
every file against the SHA-256 in the packaged manifest, and stores them in
|
|
122
|
+
`~/.cache/openads/assets` (or `ADS_ASSETS_DIR`). It prints the licence first:
|
|
123
|
+
**downloading accepts the JHU Non-Profit Research Software License** (see
|
|
124
|
+
[License](#license)). Re-running it skips files that are already correct.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
openads assets download # everything
|
|
128
|
+
openads assets download --kind atlas # only atlases (also: model, example)
|
|
129
|
+
openads assets download --assets-dir DIR # somewhere else
|
|
130
|
+
ADS_ASSETS_URL=https://my.mirror/openads-assets openads assets download # another mirror
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
OpenADS looks for the asset root in this order and never downloads on its own:
|
|
134
|
+
|
|
135
|
+
1. an explicit `paths.assets_dir` in the configuration,
|
|
136
|
+
2. the environment variable `ADS_ASSETS_DIR`,
|
|
137
|
+
3. `<repository>/assets` when OpenADS runs from a source checkout,
|
|
138
|
+
4. `~/.cache/openads/assets`.
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
openads assets where # show the candidates and the root in use
|
|
142
|
+
openads assets verify # check sizes and SHA-256 against the manifest
|
|
143
|
+
openads assets verify --no-hash # sizes only (fast)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Command line quick start
|
|
147
|
+
|
|
148
|
+
Each OpenADS 1.0 command has a 2.0 equivalent:
|
|
149
|
+
|
|
150
|
+
| OpenADS 1.0 | OpenADS 2.0 |
|
|
151
|
+
|---|---|
|
|
152
|
+
| `./scripts/run_dwi.sh SUBJECT --all --gpu 1` | `ads dwi --subject-path SUBJECT --all --gpu 1` |
|
|
153
|
+
| `./scripts/run_pwi.sh SUBJECT --all --gpu 1` | `ads pwi --subject-path SUBJECT --all --gpu 1` |
|
|
154
|
+
| `./scripts/run_combined.sh --dwi D --pwi P --all --gpu 1` | `ads combined --dwi-subject-path D --pwi-subject-path P --all --gpu 1` |
|
|
155
|
+
| `python scripts/run_ads_dwi.py --subject-path S --config C ...` | `ads dwi --subject-path S --config my.yaml ...` |
|
|
156
|
+
| `python -m openads.cli dwi ...` | `ads dwi ...` or `python -m openads dwi ...` |
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Full DWI and PWI pipelines on the example subject (after `openads assets download`;
|
|
160
|
+
# from a source checkout the examples are in assets/examples, else ~/.cache/openads/assets/examples)
|
|
161
|
+
ads dwi --subject-path assets/examples/dwi/sub-02e8eb42 --all --gpu 0 --output-root out
|
|
162
|
+
ads pwi --subject-path assets/examples/pwi/sub-02e8eb42 --all --gpu 0 --output-root out
|
|
163
|
+
|
|
164
|
+
# Selected stages, a custom output root
|
|
165
|
+
ads dwi --subject-path /data/dwi/sub-0037761d \
|
|
166
|
+
--stages prepdata,gen_mask,skull_strip,registration,inference,report --gpu 0 --output-root /output
|
|
167
|
+
ads pwi --subject-path /data/pwi/sub-0037761d \
|
|
168
|
+
--stages prepdata,gen_mask,skull_strip,gen_ttp,registration,ttpadc_coreg,inference,report \
|
|
169
|
+
--gpu 0 --output-root /output
|
|
170
|
+
|
|
171
|
+
# Combined: DWI first, then PWI (the DWI brain mask is copied into PWI when missing)
|
|
172
|
+
ads combined --dwi-subject-path assets/examples/dwi/sub-02e8eb42 \
|
|
173
|
+
--pwi-subject-path assets/examples/pwi/sub-02e8eb42 --all --gpu 0
|
|
174
|
+
ads combined --dwi-subject-path D --pwi-subject-path P \
|
|
175
|
+
--dwi-stages prepdata,gen_mask,skull_strip,registration,inference,report \
|
|
176
|
+
--pwi-stages prepdata,gen_mask,skull_strip,gen_ttp,registration,ttpadc_coreg,inference,report \
|
|
177
|
+
--gpu 0 --output-root /data/openads_output
|
|
178
|
+
|
|
179
|
+
# One stage on an existing output folder
|
|
180
|
+
ads stages run gen_mask --modality dwi --subject-id sub-02e8eb42 --output-root out --gpu 0
|
|
181
|
+
|
|
182
|
+
# Inspect
|
|
183
|
+
ads stages list dwi # stages and the methods available for DWI (--json for tools)
|
|
184
|
+
ads pwi --subject-path DIR --all --dry-run # print stages and output folder as JSON, run nothing
|
|
185
|
+
ads config show pwi # the merged configuration (packaged defaults + --config + --set)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Configuration is layered: packaged defaults < `--config my.yaml` <
|
|
189
|
+
`--set key=value` (repeatable, value parsed as YAML), for example
|
|
190
|
+
`--set stages.report.enabled=false` or `--set runtime.device=cuda:1`.
|
|
191
|
+
`--gpu N` sets `CUDA_VISIBLE_DEVICES`; `--gpu cpu` hides all GPUs, and the
|
|
192
|
+
default `runtime.device: auto` then runs on the CPU (it logs the choice; an
|
|
193
|
+
explicit `--set runtime.device=cuda` fails instead). See [`docs/configuration.md`](docs/configuration.md).
|
|
194
|
+
|
|
195
|
+
Pass one subject folder to `--subject-path`, not a dataset root. The subject
|
|
196
|
+
folder holds files such as `<id>_DWI.nii.gz`, `<id>_b0.nii.gz`, `<id>_ADC.nii.gz`,
|
|
197
|
+
`<id>_stroke.nii.gz` (DWI) and additionally `<id>_PWI.nii.gz`, `<id>_PWI.json`,
|
|
198
|
+
`<id>_HP_manual.nii.gz` (PWI). A missing ADC is computed from DWI and b0; a
|
|
199
|
+
4D DWI is split into b0 and DWI ([`docs/user_guide.md`](docs/user_guide.md)).
|
|
200
|
+
|
|
201
|
+
## Python API quick start
|
|
202
|
+
|
|
203
|
+
`import openads` is light (no torch, ANTs or pandas). Heavy libraries load when a
|
|
204
|
+
stage runs. The examples use the full name; `import openads as ads` works the same way.
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
import openads
|
|
208
|
+
from openads.models.device import set_visible_gpus
|
|
209
|
+
|
|
210
|
+
set_visible_gpus("0") # like --gpu 0; call before anything initialises CUDA
|
|
211
|
+
result = openads.run_dwi("assets/examples/dwi/sub-02e8eb42", all_stages=True, output_root="out")
|
|
212
|
+
print(result.workspace.modality_root) # out/sub-02e8eb42/DWI
|
|
213
|
+
print(sorted(result.results)) # stage name -> StageResult
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Plan a run without running it, and find the bundled example subjects:
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
plan = openads.get_pipeline("dwi").plan("assets/examples/dwi/sub-02e8eb42", output_root="out")
|
|
220
|
+
print(plan.output.subject_root) # out/sub-02e8eb42 (where run() writes)
|
|
221
|
+
print([p.name for p in openads.example_subjects("dwi")]) # ['sub-02e8eb42']
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Configuration objects and overrides:
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
cfg = openads.load_config(pipeline="dwi", overrides=["stages.report.enabled=false"])
|
|
228
|
+
print(cfg.stage("gen_mask").method) # synthstrip
|
|
229
|
+
print(cfg.stage("report").enabled) # False
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Run one stage on an existing output folder (its inputs must already be there):
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
res = openads.run_stage("gen_mask", subject_id="sub-02e8eb42", modality="DWI", output_root="out")
|
|
236
|
+
print(res.produced["preprocess.brain_mask"]) # out/sub-02e8eb42/DWI/preprocess/sub-02e8eb42_DWIbrain-mask.nii.gz
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The numerics are plain functions on arrays in `openads.ops`:
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
import numpy as np
|
|
243
|
+
from openads.ops.adc import compute_adc
|
|
244
|
+
|
|
245
|
+
dwi = np.full((4, 4, 4), 400.0)
|
|
246
|
+
b0 = np.full((4, 4, 4), 1000.0)
|
|
247
|
+
adc = compute_adc(dwi, b0, bvalue=1000.0) # float32, -ln(dwi/b0)/b
|
|
248
|
+
print(round(float(adc[0, 0, 0]), 6)) # 0.000916
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Add your own method (here a brain-mask method) in one file and select it by
|
|
252
|
+
configuration. See [`docs/extending.md`](docs/extending.md) for plugins and new
|
|
253
|
+
stages.
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
from dataclasses import dataclass
|
|
257
|
+
from pathlib import Path
|
|
258
|
+
|
|
259
|
+
import nibabel as nib
|
|
260
|
+
import numpy as np
|
|
261
|
+
|
|
262
|
+
from openads.registry import register
|
|
263
|
+
from openads.stages import StageContext
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
@dataclass(frozen=True)
|
|
267
|
+
class ThresholdParams:
|
|
268
|
+
level: float = 50.0
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@register("gen_mask", "threshold", params=ThresholdParams)
|
|
272
|
+
def threshold(ctx: StageContext) -> dict[str, Path]:
|
|
273
|
+
"""Toy brain mask: DWI > level."""
|
|
274
|
+
img = nib.load(ctx.ws.path("preprocess.dwi"))
|
|
275
|
+
mask = (np.asarray(img.dataobj) > ctx.params.level).astype(np.uint8)
|
|
276
|
+
out = ctx.ws.path("preprocess.brain_mask")
|
|
277
|
+
nib.save(nib.Nifti1Image(mask, img.affine), out)
|
|
278
|
+
return {"preprocess.brain_mask": out}
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
res = openads.run_stage(
|
|
282
|
+
"gen_mask", subject_id="sub-02e8eb42", modality="DWI", output_root="out",
|
|
283
|
+
method="threshold", overrides=["stages.gen_mask.params.level=55"],
|
|
284
|
+
)
|
|
285
|
+
print(res.method) # threshold
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Output tree
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
out/sub-02e8eb42
|
|
292
|
+
├── DWI
|
|
293
|
+
│ ├── preprocess DWI, B0, ADC (+ _brain), DWIbrain-mask, stroke
|
|
294
|
+
│ ├── registration *_space-MNI152_aff[_desc-norm], *_affsyn, transforms (.mat, warps), manifest
|
|
295
|
+
│ ├── segment stroke-mask (native), _space-MNI152, _space-MNI152_affsyn, metrics.json
|
|
296
|
+
│ └── reporting *_QFV.csv, *_lesionload.csv, automatic_radiological_report.txt,
|
|
297
|
+
│ *_report_interpretation.pdf, DWIstroke*.png
|
|
298
|
+
└── PWI
|
|
299
|
+
├── preprocess PWI, PWI.json, PWI_STC, PWI_average, TTP, masks, *_PWI_moco_aff/*.mat
|
|
300
|
+
├── registration TTP/HP_manual in DWI and MNI space, coregistration transforms
|
|
301
|
+
├── segmentation HP-mask (native), _space-MNI152[_affsyn|_desc-postproc], metrics.json
|
|
302
|
+
└── reporting *_HPQFV.csv, *_HPload.csv, automatic_radiological_report.txt, HP*.png
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
A PWI run after a DWI run of the same subject reuses the DWI registration. The
|
|
306
|
+
file names are defined in one table (`openads.core.layout`); cleanup keeps exactly
|
|
307
|
+
the files of this tree.
|
|
308
|
+
|
|
309
|
+
## GUI / server / Docker
|
|
310
|
+
|
|
311
|
+
Thin clients in [`apps/`](apps/) build `ads ...` command lines (stages from
|
|
312
|
+
`openads.get_pipeline(...).stage_names`) and run them as subprocesses. They are not
|
|
313
|
+
in the wheel: run them from a source checkout with the extras installed.
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
pip install -e ".[server,gui]"
|
|
317
|
+
python -m apps.server --host 0.0.0.0 --port 8000 # web pages + API on :8000 (apps/web)
|
|
318
|
+
python -m apps.gui # PyQt5 launcher (1.0 GUI_launcher.py)
|
|
319
|
+
scripts/run_dwi.sh SUBJECT --all --gpu 0 --dry-run # 1.0 wrappers: print or run `ads ...`
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
The server keeps the 1.0 endpoints (`/api/run` streams the log,
|
|
323
|
+
`/api/outputs`, `/api/download`, `/api/download-all`, `/api/upload-subject`,
|
|
324
|
+
`/api/example-subjects`) and adds `/api/stages` and a job API
|
|
325
|
+
(`/api/jobs`, `/api/jobs/{id}`, `/api/jobs/{id}/log`, `/api/jobs/{id}/stream`).
|
|
326
|
+
Docker images (GPU and CPU) and their entrypoint: [`docker/README.md`](docker/README.md).
|
|
327
|
+
|
|
328
|
+
## Documentation
|
|
329
|
+
|
|
330
|
+
- [`docs/user_guide.md`](docs/user_guide.md): inputs, stages, outputs, GPU, troubleshooting.
|
|
331
|
+
- [`docs/configuration.md`](docs/configuration.md): every configuration key and default.
|
|
332
|
+
- [`docs/extending.md`](docs/extending.md): new methods, plugins, new stages, `openads.steps`.
|
|
333
|
+
- [`docs/architecture.md`](docs/architecture.md): layers, the data-import design, design decisions.
|
|
334
|
+
- [`docs/refactor/`](docs/refactor/): the 1.0 -> 2.0 refactor records (mapping,
|
|
335
|
+
ADRs, regression harness, API catalogue).
|
|
336
|
+
- [`CHANGELOG.md`](CHANGELOG.md).
|
|
337
|
+
|
|
338
|
+
## License
|
|
339
|
+
|
|
340
|
+
JHU Non-Profit Research Software License (see [`LICENSE`](LICENSE)): non-profit
|
|
341
|
+
research use only; commercial use needs a separate license from Johns Hopkins
|
|
342
|
+
University.
|