multimodars 0.3.2__tar.gz → 0.3.4__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.
- {multimodars-0.3.2 → multimodars-0.3.4}/CHANGELOG.md +16 -4
- multimodars-0.3.4/CITATION.cff +82 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/Cargo.lock +1 -1
- {multimodars-0.3.2 → multimodars-0.3.4}/Cargo.toml +1 -1
- {multimodars-0.3.2 → multimodars-0.3.4}/PKG-INFO +3 -3
- {multimodars-0.3.2 → multimodars-0.3.4}/README.md +2 -2
- multimodars-0.3.4/ROADMAP.MD +43 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/benchmarks/BENCHMARK_PLAN.md +8 -8
- {multimodars-0.3.2 → multimodars-0.3.4}/benchmarks/benchmark_bruteforce_stepsize.py +10 -10
- {multimodars-0.3.2 → multimodars-0.3.4}/benchmarks/benchmark_cpu_scaling.py +7 -6
- multimodars-0.3.4/benchmarks/results/bruteforce_stepsize.png +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/_processing.py +42 -42
- {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/multimodars.pyi +6 -6
- multimodars-0.3.4/paper/REVIEWER2.md +48 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/paper/bibliography.bib +48 -12
- multimodars-0.3.4/paper/paper.md +126 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/pyproject.toml +1 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/ccta/adjust_mesh/label_coronary.rs +2 -2
- multimodars-0.3.4/src/intravascular/binding/align.rs +397 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/centerline_align/align_algorithms.rs +75 -62
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/centerline_align/mod.rs +132 -87
- multimodars-0.3.4/src/intravascular/to_object/mod.rs +130 -0
- multimodars-0.3.4/tests/test_intravascular.py +256 -0
- multimodars-0.3.2/ROADMAP.MD +0 -93
- multimodars-0.3.2/benchmarks/results/bruteforce_stepsize.png +0 -0
- multimodars-0.3.2/paper/paper.md +0 -128
- multimodars-0.3.2/src/intravascular/binding/align.rs +0 -335
- multimodars-0.3.2/src/intravascular/to_object/mod.rs +0 -66
- {multimodars-0.3.2 → multimodars-0.3.4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/.github/workflows/CI.yml +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/.github/workflows/draft-pdf.yml +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/.gitignore +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/.pre-commit-config.yaml +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/.readthedocs.yaml +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/CONTRIBUTING.md +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/LICENSE +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/benchmarks/results/cpu_scaling.png +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/branch_diastolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/branch_systolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/calcium_diastolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/calcium_systolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/diastolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/diastolic_reference_points.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/eem_diastolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/eem_systolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/systolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/idealized_geometry/systolic_reference_points.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/branch_diastolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/branch_systolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/calcium_diastolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/calcium_systolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/diastolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/diastolic_reference_points.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/eem_diastolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/eem_systolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/systolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_full/systolic_reference_points.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_rest/combined_sorted_manual.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_rest/diastolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_rest/diastolic_reference_points.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_rest/systolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_rest/systolic_reference_points.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_stress/combined_sorted_manual.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_stress/diastolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_stress/diastolic_reference_points.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_stress/systolic_contours.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/data/fixtures/ivus_stress/systolic_reference_points.csv +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/media/coronary_artery_disease.jpg +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/media/dynamic_lumen_changes.png +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/media/multimoda-rs.jpg +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/__init__.py +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/_converters.py +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/ccta/__init__.py +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/ccta/debug_plots.py +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/ccta/fixing_functions.py +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/ccta/labeling.py +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/ccta/manipulating.py +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/io/__init__.py +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/io/read_geometrical.py +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/io/write_geometries.py +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/multimodars/py.typed +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/paper/REVIEWER1.md +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/paper/figures/Figure1.jpg +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/paper/figures/Figure2.jpg +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/paper/figures/Figure3.png +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/paper/figures/Figure4.jpg +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/ccta/adjust_mesh/mod.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/ccta/adjust_mesh/scale_coronary.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/ccta/binding/ccta_py.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/ccta/binding/mod.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/ccta/mod.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/binding/classes.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/binding/entry.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/binding/mod.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/centerline_align/preprocessing.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/io/geometry.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/io/input.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/io/integrity_check.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/io/mod.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/io/output.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/mod.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/processing/align_between.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/processing/align_within.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/processing/mod.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/processing/postprocessing.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/processing/preprocessing.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/processing/process_utils.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/processing/wall.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/to_object/interpolation.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/to_object/texture.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/to_object/write_mtl.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/utils/general_utils.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/utils/mod.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/intravascular/utils/test_utils.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/src/lib.rs +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/tests/conftest.py +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/tests/test_ccta.py +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/tests/test_converters.py +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/tests/test_core.py +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/tests/test_wrappers.py +0 -0
- {multimodars-0.3.2 → multimodars-0.3.4}/tests/tests_todo.md +0 -0
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
This project adheres to [Semantic Versioning](https://semver.org/).
|
|
5
5
|
|
|
6
|
+
## [0.3.4] - 2026-05-13
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- `align_three_point`, `align_manual` and `align_combined` are now all generic, taking either `PyGeometryPair` or `PyGeometry` as input
|
|
10
|
+
- Test file added `test_intravascular.py` to demonstrate that same results with both
|
|
11
|
+
|
|
12
|
+
## [0.3.3] - 2026-05-06
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- `CITATION.cff` with full author list, ORCIDs, and preferred citation for JOSS/Zenodo archival.
|
|
16
|
+
- Zenodo DOI badge added to README.
|
|
17
|
+
|
|
6
18
|
## [0.3.2] - 2026-05-05
|
|
7
19
|
|
|
8
20
|
### Added
|
|
@@ -14,14 +26,14 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
14
26
|
|
|
15
27
|
### Changed
|
|
16
28
|
- `paper/paper.md` Performance section updated with quantified benchmark results and
|
|
17
|
-
corrected
|
|
29
|
+
corrected parallelization description.
|
|
18
30
|
|
|
19
31
|
### Performance
|
|
20
|
-
- Intra-pullback alignment (`search_range`) now
|
|
32
|
+
- Intra-pullback alignment (`search_range`) now parallelizes over candidate rotation
|
|
21
33
|
angles via `par_iter` instead of the point-rotation loop. The inner cost-function
|
|
22
34
|
loop is now sequential. This provides enough rayon tasks per frame step to saturate
|
|
23
|
-
all cores: bruteforce scales 6.5× and
|
|
24
|
-
~1.4× for both). Combined algorithmic +
|
|
35
|
+
all cores: bruteforce scales 6.5× and optimized 4.2× from 2 to 16 cores (previously
|
|
36
|
+
~1.4× for both). Combined algorithmic + parallelization gain reaches **38.5×** vs.
|
|
25
37
|
brute-force at 2 cores.
|
|
26
38
|
|
|
27
39
|
## [0.3.1] - 2026-05-04
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software, please cite it using the metadata below."
|
|
3
|
+
title: "multimodars: A Rust-powered toolkit for multi-modality cardiac image fusion and registration"
|
|
4
|
+
version: 0.3.2
|
|
5
|
+
date-released: 2026-05-05
|
|
6
|
+
license: MIT
|
|
7
|
+
repository-code: "https://github.com/yungselm/multimoda-rs"
|
|
8
|
+
url: "https://multimoda-rs.readthedocs.io"
|
|
9
|
+
|
|
10
|
+
authors:
|
|
11
|
+
- family-names: Stark
|
|
12
|
+
given-names: Anselm W.
|
|
13
|
+
orcid: "https://orcid.org/0000-0002-5861-3753"
|
|
14
|
+
affiliation: "Department of Cardiology, Inselspital, Bern University Hospital, University of Bern, Switzerland"
|
|
15
|
+
- family-names: Ilic
|
|
16
|
+
given-names: Marc
|
|
17
|
+
orcid: "https://orcid.org/0009-0000-6443-8739"
|
|
18
|
+
affiliation: "Department of Cardiology, Inselspital, Bern University Hospital, University of Bern, Switzerland"
|
|
19
|
+
- family-names: Mokhtari
|
|
20
|
+
given-names: Ali
|
|
21
|
+
orcid: "https://orcid.org/0009-0004-8864-6988"
|
|
22
|
+
affiliation: "Department of Cardiology, Inselspital, Bern University Hospital, University of Bern, Switzerland"
|
|
23
|
+
- family-names: Mohammadi Kazaj
|
|
24
|
+
given-names: Pooya
|
|
25
|
+
orcid: "https://orcid.org/0000-0003-2747-1489"
|
|
26
|
+
affiliation: "Department of Cardiology, Inselspital, Bern University Hospital, University of Bern, Switzerland"
|
|
27
|
+
- family-names: Gräni
|
|
28
|
+
given-names: Christoph
|
|
29
|
+
orcid: "https://orcid.org/0000-0001-6029-0597"
|
|
30
|
+
affiliation: "Department of Cardiology, Inselspital, Bern University Hospital, University of Bern, Switzerland"
|
|
31
|
+
- family-names: Shiri
|
|
32
|
+
given-names: Isaac
|
|
33
|
+
orcid: "https://orcid.org/0000-0002-5735-0736"
|
|
34
|
+
affiliation: "Department of Cardiology, Inselspital, Bern University Hospital, University of Bern, Switzerland"
|
|
35
|
+
|
|
36
|
+
abstract: >
|
|
37
|
+
multimodars is a high-performance Python/Rust toolkit that registers and fuses
|
|
38
|
+
intravascular imaging (IVUS, OCT) with coronary CT angiography (CCTA) into unified
|
|
39
|
+
high-resolution 3D vessel models. It implements four alignment modes (full,
|
|
40
|
+
double-pair, single-pair, single) to support multi-state comparisons such as rest
|
|
41
|
+
vs. pharmacologic stress and pre- vs. post-intervention. The Rust backend (via
|
|
42
|
+
PyO3/maturin) parallelizes the multiscale angular search across all available cores,
|
|
43
|
+
achieving a combined 38.5x speedup over a Python brute-force baseline. Output
|
|
44
|
+
includes Wavefront OBJ/STL meshes, per-frame lumen metrics (minimum lumen area,
|
|
45
|
+
stenosis fraction, elliptic ratio), and optional interpolated deformation animations.
|
|
46
|
+
|
|
47
|
+
keywords:
|
|
48
|
+
- Rust
|
|
49
|
+
- Python
|
|
50
|
+
- Cardiology
|
|
51
|
+
- Intravascular imaging
|
|
52
|
+
- CCTA
|
|
53
|
+
- Image Fusion
|
|
54
|
+
- Image registration
|
|
55
|
+
- IVUS
|
|
56
|
+
- OCT
|
|
57
|
+
- Coronary artery anomalies
|
|
58
|
+
|
|
59
|
+
preferred-citation:
|
|
60
|
+
type: article
|
|
61
|
+
title: "multimodars: A Rust-powered toolkit for multi-modality cardiac image fusion and registration"
|
|
62
|
+
authors:
|
|
63
|
+
- family-names: Stark
|
|
64
|
+
given-names: Anselm W.
|
|
65
|
+
orcid: "https://orcid.org/0000-0002-5861-3753"
|
|
66
|
+
- family-names: Ilic
|
|
67
|
+
given-names: Marc
|
|
68
|
+
orcid: "https://orcid.org/0009-0000-6443-8739"
|
|
69
|
+
- family-names: Mokhtari
|
|
70
|
+
given-names: Ali
|
|
71
|
+
orcid: "https://orcid.org/0009-0004-8864-6988"
|
|
72
|
+
- family-names: Mohammadi Kazaj
|
|
73
|
+
given-names: Pooya
|
|
74
|
+
orcid: "https://orcid.org/0000-0003-2747-1489"
|
|
75
|
+
- family-names: Gräni
|
|
76
|
+
given-names: Christoph
|
|
77
|
+
orcid: "https://orcid.org/0000-0001-6029-0597"
|
|
78
|
+
- family-names: Shiri
|
|
79
|
+
given-names: Isaac
|
|
80
|
+
orcid: "https://orcid.org/0000-0002-5735-0736"
|
|
81
|
+
journal: "Journal of Open Source Software"
|
|
82
|
+
year: 2026
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: multimodars
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
6
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
@@ -36,7 +36,7 @@ Project-URL: Issues, https://github.com/yungselm/multimoda-rs/issues
|
|
|
36
36
|
[](https://multimoda-rs.readthedocs.io)
|
|
37
37
|
[](https://github.com/yungselm/multimoda-rs/actions/workflows/CI.yml)
|
|
38
38
|
[](https://joss.theoj.org/papers/24992cb138d85b0bf08f01bc2b384a80)
|
|
39
|
-
|
|
39
|
+
[](https://doi.org/10.5281/zenodo.20051997)
|
|
40
40
|
|
|
41
41
|
<figure class="epigraph" style="text-align: center; font-style: italic;">
|
|
42
42
|
<blockquote>
|
|
@@ -79,7 +79,7 @@ For building from source or the full developer setup (tests, linting, docs), see
|
|
|
79
79
|
|
|
80
80
|
## Quick Example
|
|
81
81
|
|
|
82
|
-
Checkout our **[Documentation](https://
|
|
82
|
+
Checkout our **[Documentation](https://multimoda-rs.readthedocs.io/en/latest)** for in-depth explanations of the functionality and preloaded notebooks including 3D plots. Otherwise clone the repo to follow along the different examples:
|
|
83
83
|
|
|
84
84
|
```python
|
|
85
85
|
import multimodars as mm
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
[](https://multimoda-rs.readthedocs.io)
|
|
13
13
|
[](https://github.com/yungselm/multimoda-rs/actions/workflows/CI.yml)
|
|
14
14
|
[](https://joss.theoj.org/papers/24992cb138d85b0bf08f01bc2b384a80)
|
|
15
|
-
|
|
15
|
+
[](https://doi.org/10.5281/zenodo.20051997)
|
|
16
16
|
|
|
17
17
|
<figure class="epigraph" style="text-align: center; font-style: italic;">
|
|
18
18
|
<blockquote>
|
|
@@ -55,7 +55,7 @@ For building from source or the full developer setup (tests, linting, docs), see
|
|
|
55
55
|
|
|
56
56
|
## Quick Example
|
|
57
57
|
|
|
58
|
-
Checkout our **[Documentation](https://
|
|
58
|
+
Checkout our **[Documentation](https://multimoda-rs.readthedocs.io/en/latest)** for in-depth explanations of the functionality and preloaded notebooks including 3D plots. Otherwise clone the repo to follow along the different examples:
|
|
59
59
|
|
|
60
60
|
```python
|
|
61
61
|
import multimodars as mm
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Python package that has one purpose: Combining any intravascular image modality with CCTA meshes.
|
|
2
|
+
|
|
3
|
+
# Hot fixes
|
|
4
|
+
- [] Labeling doesn't work as expected especially for `from_file` variants
|
|
5
|
+
|
|
6
|
+
# Optimizations
|
|
7
|
+
- [] Optimize the ray intersection algorithm
|
|
8
|
+
- [] Better parallelization strategy for frames alignment by processing in batches (frames only have to be aligned relative to each other not sequentially)
|
|
9
|
+
|
|
10
|
+
# Mesh combination (v0.3.0)
|
|
11
|
+
## New/improved features
|
|
12
|
+
- [] "Smooth" the aortic ring of boundary points first, by projecting on plane to shift outliers
|
|
13
|
+
- [] Write a more stable function to find intramural points on aorta (for now works with excluded RCA points)
|
|
14
|
+
- [] Create an aortic and coronary wall with a consistent connection
|
|
15
|
+
- [] Find bifurcation reference points automatically
|
|
16
|
+
- [] `numpy_to_report` function that allows to read in lists for the four variables of interest (frame, phase, measurement_1, measurement_2)
|
|
17
|
+
- [] write metadata to .obj files so they can be reconstructed completely to geometry/geometrypair
|
|
18
|
+
|
|
19
|
+
## Fixes/Improvements
|
|
20
|
+
- [] Write a more stable function to find intramural points on aorta (for now works with excluded RCA points)
|
|
21
|
+
- [] Align algorithms only work with geometry pair not only geometry
|
|
22
|
+
- [] fix centerline resampling
|
|
23
|
+
|
|
24
|
+
## Investigate/check logic
|
|
25
|
+
- [] When aligning geometry with wall has a rotation in the wall -> logic error for centerline alignment?
|
|
26
|
+
|
|
27
|
+
# CCTA clinical assessment (v0.4.0)
|
|
28
|
+
- [] automatically find the required reference points for anomalous alignment
|
|
29
|
+
- [] automatically assess lumen area, minor-, major axis, mla, intramural length etc. from the CCTA mesh.
|
|
30
|
+
- [] return hausdorff distances per point between aligned ivus mesh and ccta mesh.
|
|
31
|
+
- [] function to automatically assign wall thickness to ccta and combined ivus/ccta mesh
|
|
32
|
+
|
|
33
|
+
# Miscellaneous
|
|
34
|
+
- [] Integrate coverage measurement (`pytest-cov`) and upload reports to Coveralls or Codecov.
|
|
35
|
+
- [] Add a coverage status badge to the README.
|
|
36
|
+
|
|
37
|
+
# Centerline alignment module (v1.0.0)
|
|
38
|
+
- [] Create Centerline directly from mesh
|
|
39
|
+
|
|
40
|
+
# Myocardial Fusion v2.0.0
|
|
41
|
+
- [] Read in LGE/T2 etc. from MRI and map onto CCTA myocardium
|
|
42
|
+
- [] Read in Perfusion map from PET/SPECT and load onto CCTA myocardium
|
|
43
|
+
- [] Calculate perfusion territories based on coronary anatomy
|
|
@@ -6,18 +6,18 @@ Two benchmarks to characterise the alignment performance of `multimodars`.
|
|
|
6
6
|
|
|
7
7
|
| # | What | Function | Dataset |
|
|
8
8
|
|---|------|----------|---------|
|
|
9
|
-
| 1 | Bruteforce vs.
|
|
9
|
+
| 1 | Bruteforce vs. optimized at varying step sizes | `from_file_full` | IVUS rest/stress |
|
|
10
10
|
| 2 | CPU-core scaling (parallelism) | `from_array_single` | OCT (280 frames) |
|
|
11
11
|
|
|
12
12
|
Results land in `benchmarks/results/`.
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
## Benchmark 1 — Bruteforce vs.
|
|
16
|
+
## Benchmark 1 — Bruteforce vs. optimized: Step-Size Scaling
|
|
17
17
|
|
|
18
18
|
### Goal
|
|
19
19
|
|
|
20
|
-
Quantify how bruteforce and
|
|
20
|
+
Quantify how bruteforce and optimized alignment scale as `step_rotation_deg` decreases
|
|
21
21
|
(i.e. as the rotation-search grid becomes finer).
|
|
22
22
|
|
|
23
23
|
### Setup
|
|
@@ -51,7 +51,7 @@ Corresponding number of rotation steps n = 180 / step (using the full ±90° ran
|
|
|
51
51
|
|
|
52
52
|
- **Bruteforce**: exhaustively scores every rotation angle for every frame pair →
|
|
53
53
|
wall time grows linearly with n\_steps (O(n)).
|
|
54
|
-
- **
|
|
54
|
+
- **optimized**: coarse-to-fine hierarchical search → sub-linear in n\_steps,
|
|
55
55
|
asymptotically O(log n).
|
|
56
56
|
|
|
57
57
|
The plot axes are logarithmic so the two complexity classes appear as straight lines
|
|
@@ -64,8 +64,8 @@ with clearly different slopes.
|
|
|
64
64
|
- x-axis (bottom): number of rotation steps; (top): step size in degrees
|
|
65
65
|
- y-axis: median wall time (seconds)
|
|
66
66
|
- Line 1: bruteforce (red ○–) + O(n) reference dashed line
|
|
67
|
-
- Line 2:
|
|
68
|
-
- Right y-axis: speedup factor (bruteforce /
|
|
67
|
+
- Line 2: optimized (blue ■ --)
|
|
68
|
+
- Right y-axis: speedup factor (bruteforce / optimized)
|
|
69
69
|
|
|
70
70
|
---
|
|
71
71
|
|
|
@@ -110,7 +110,7 @@ a **separate subprocess** with the variable set before any import of `multimodar
|
|
|
110
110
|
|
|
111
111
|
### Expected behaviour
|
|
112
112
|
|
|
113
|
-
- **
|
|
113
|
+
- **optimized**: near-linear speedup up to ~8 cores, then diminishing returns
|
|
114
114
|
(Amdahl's law — serial fraction of the pipeline limits gains beyond that).
|
|
115
115
|
- **Bruteforce**: also benefits from rayon frame-level parallelism, but expected
|
|
116
116
|
to saturate earlier because the per-step work is less cache-friendly.
|
|
@@ -119,7 +119,7 @@ a **separate subprocess** with the variable set before any import of `multimodar
|
|
|
119
119
|
|
|
120
120
|
`benchmarks/results/cpu_scaling.png` — two-panel figure:
|
|
121
121
|
|
|
122
|
-
- Left panel: wall time (s) vs. CPU cores, two lines (bruteforce,
|
|
122
|
+
- Left panel: wall time (s) vs. CPU cores, two lines (bruteforce, optimized)
|
|
123
123
|
- Right panel: speedup relative to 1 core vs. CPU cores +
|
|
124
124
|
ideal linear-speedup reference; same two lines
|
|
125
125
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Benchmark 1: bruteforce vs.
|
|
1
|
+
"""Benchmark 1: bruteforce vs. optimized alignment at varying step sizes.
|
|
2
2
|
|
|
3
3
|
Tests from_file_full on the bundled IVUS rest/stress example data with
|
|
4
4
|
step_rotation_deg in {5.0, 2.5, 1.0, 0.5, 0.1} degrees.
|
|
@@ -27,7 +27,7 @@ INPUT_AB = str(DATA_ROOT / "ivus_rest")
|
|
|
27
27
|
INPUT_CD = str(DATA_ROOT / "ivus_stress")
|
|
28
28
|
RESULTS_DIR = Path(__file__).parent / "results"
|
|
29
29
|
|
|
30
|
-
STEP_SIZES: list[float] = [5.0, 2.5, 1.0, 0.5, 0.1, 0.05]
|
|
30
|
+
STEP_SIZES: list[float] = [5.0, 2.5, 1.0, 0.5, 0.25, 0.1, 0.05]
|
|
31
31
|
RANGE_DEG: float = 90.0
|
|
32
32
|
REPEATS: int = 3
|
|
33
33
|
|
|
@@ -63,7 +63,7 @@ def _run_once(step: float, bruteforce: bool) -> float:
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
def run_benchmark() -> dict[str, list[float]]:
|
|
66
|
-
results: dict[str, list[float]] = {"bruteforce": [], "
|
|
66
|
+
results: dict[str, list[float]] = {"bruteforce": [], "optimized": []}
|
|
67
67
|
|
|
68
68
|
for step in STEP_SIZES:
|
|
69
69
|
print(f"step={step:4.1f}° bruteforce=True ", end="", flush=True)
|
|
@@ -75,7 +75,7 @@ def run_benchmark() -> dict[str, list[float]]:
|
|
|
75
75
|
print(f"step={step:4.1f}° bruteforce=False ", end="", flush=True)
|
|
76
76
|
times_opt = [_run_once(step, bruteforce=False) for _ in range(REPEATS)]
|
|
77
77
|
med_opt = statistics.median(times_opt)
|
|
78
|
-
results["
|
|
78
|
+
results["optimized"].append(med_opt)
|
|
79
79
|
speedup = med_bf / med_opt if med_opt > 0 else float("inf")
|
|
80
80
|
print(f"{med_opt:.2f} s (speedup {speedup:.1f}x)")
|
|
81
81
|
|
|
@@ -92,7 +92,7 @@ def plot_results(results: dict[str, list[float]]) -> None:
|
|
|
92
92
|
|
|
93
93
|
n_steps_list = [_n_steps(s) for s in STEP_SIZES]
|
|
94
94
|
bf_times = results["bruteforce"]
|
|
95
|
-
opt_times = results["
|
|
95
|
+
opt_times = results["optimized"]
|
|
96
96
|
speedups = [bf / opt for bf, opt in zip(bf_times, opt_times)]
|
|
97
97
|
|
|
98
98
|
fig, (ax_main, ax_speedup) = plt.subplots(
|
|
@@ -116,7 +116,7 @@ def plot_results(results: dict[str, list[float]]) -> None:
|
|
|
116
116
|
color="tab:blue",
|
|
117
117
|
linewidth=2,
|
|
118
118
|
markersize=7,
|
|
119
|
-
label="
|
|
119
|
+
label="optimized",
|
|
120
120
|
)
|
|
121
121
|
|
|
122
122
|
# O(n) reference anchored at the first bruteforce point
|
|
@@ -137,7 +137,7 @@ def plot_results(results: dict[str, list[float]]) -> None:
|
|
|
137
137
|
ax_main.set_xlabel("Number of rotation steps (2 × range / step)", fontsize=12)
|
|
138
138
|
ax_main.set_ylabel("Median wall time (s)", fontsize=12)
|
|
139
139
|
ax_main.set_title(
|
|
140
|
-
"Bruteforce vs.
|
|
140
|
+
"Bruteforce vs. optimized — Step-Size Scaling\n"
|
|
141
141
|
"from_file_full · IVUS rest/stress · range = 90°",
|
|
142
142
|
fontsize=11,
|
|
143
143
|
)
|
|
@@ -164,9 +164,9 @@ def plot_results(results: dict[str, list[float]]) -> None:
|
|
|
164
164
|
ax_speedup.axhline(1.0, color="gray", linewidth=1, linestyle=":")
|
|
165
165
|
ax_speedup.set_xscale("log")
|
|
166
166
|
ax_speedup.set_xlabel("Number of rotation steps (2 × range / step)", fontsize=12)
|
|
167
|
-
ax_speedup.set_ylabel("Speedup (bruteforce /
|
|
167
|
+
ax_speedup.set_ylabel("Speedup (bruteforce / optimized)", fontsize=12)
|
|
168
168
|
ax_speedup.set_title(
|
|
169
|
-
"Speedup of
|
|
169
|
+
"Speedup of optimized over Bruteforce\n"
|
|
170
170
|
"from_file_full · IVUS rest/stress · range = 90°",
|
|
171
171
|
fontsize=11,
|
|
172
172
|
)
|
|
@@ -190,7 +190,7 @@ def plot_results(results: dict[str, list[float]]) -> None:
|
|
|
190
190
|
# ---------------------------------------------------------------------------
|
|
191
191
|
|
|
192
192
|
if __name__ == "__main__":
|
|
193
|
-
print("=== Benchmark 1: Bruteforce vs.
|
|
193
|
+
print("=== Benchmark 1: Bruteforce vs. optimized — Step-Size Scaling ===\n")
|
|
194
194
|
results = run_benchmark()
|
|
195
195
|
print("\nPlotting results …")
|
|
196
196
|
plot_results(results)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Benchmark 2: CPU-core scaling for
|
|
1
|
+
"""Benchmark 2: CPU-core scaling for optimized vs. bruteforce alignment.
|
|
2
2
|
|
|
3
3
|
Tests from_array_single on the bundled OCT example dataset (280 frames) at
|
|
4
4
|
varying RAYON_NUM_THREADS values (1 → 16 cores).
|
|
@@ -103,6 +103,7 @@ def _run_worker(n_cores: int, bruteforce: bool) -> list[float]:
|
|
|
103
103
|
[sys.executable, "-c", script],
|
|
104
104
|
capture_output=True,
|
|
105
105
|
text=True,
|
|
106
|
+
encoding="utf-8",
|
|
106
107
|
)
|
|
107
108
|
if proc.returncode != 0 or not proc.stdout.strip():
|
|
108
109
|
print(f"\n [stdout] {proc.stdout.strip()[:600]}", file=sys.stderr)
|
|
@@ -122,7 +123,7 @@ def _run_worker(n_cores: int, bruteforce: bool) -> list[float]:
|
|
|
122
123
|
|
|
123
124
|
|
|
124
125
|
def run_benchmark() -> dict[str, list[float]]:
|
|
125
|
-
results: dict[str, list[float]] = {"bruteforce": [], "
|
|
126
|
+
results: dict[str, list[float]] = {"bruteforce": [], "optimized": []}
|
|
126
127
|
|
|
127
128
|
for n_cores in CORE_COUNTS:
|
|
128
129
|
label_width = len(str(max(CORE_COUNTS)))
|
|
@@ -144,7 +145,7 @@ def run_benchmark() -> dict[str, list[float]]:
|
|
|
144
145
|
)
|
|
145
146
|
times_opt = _run_worker(n_cores, bruteforce=False)
|
|
146
147
|
med_opt = statistics.median(times_opt)
|
|
147
|
-
results["
|
|
148
|
+
results["optimized"].append(med_opt)
|
|
148
149
|
print(f"{med_opt:.2f} s")
|
|
149
150
|
|
|
150
151
|
return results
|
|
@@ -159,7 +160,7 @@ def plot_results(results: dict[str, list[float]]) -> None:
|
|
|
159
160
|
RESULTS_DIR.mkdir(parents=True, exist_ok=True)
|
|
160
161
|
|
|
161
162
|
bf_times = results["bruteforce"]
|
|
162
|
-
opt_times = results["
|
|
163
|
+
opt_times = results["optimized"]
|
|
163
164
|
|
|
164
165
|
bf_speedup = [bf_times[0] / t for t in bf_times]
|
|
165
166
|
opt_speedup = [opt_times[0] / t for t in opt_times]
|
|
@@ -186,7 +187,7 @@ def plot_results(results: dict[str, list[float]]) -> None:
|
|
|
186
187
|
color="tab:blue",
|
|
187
188
|
linewidth=2,
|
|
188
189
|
markersize=7,
|
|
189
|
-
label="
|
|
190
|
+
label="optimized",
|
|
190
191
|
)
|
|
191
192
|
ax_time.set_xlabel("CPU cores (RAYON_NUM_THREADS)", fontsize=12)
|
|
192
193
|
ax_time.set_ylabel("Median wall time (s)", fontsize=12)
|
|
@@ -224,7 +225,7 @@ def plot_results(results: dict[str, list[float]]) -> None:
|
|
|
224
225
|
color="tab:blue",
|
|
225
226
|
linewidth=2,
|
|
226
227
|
markersize=7,
|
|
227
|
-
label="
|
|
228
|
+
label="optimized",
|
|
228
229
|
)
|
|
229
230
|
ax_speedup.set_xlabel("CPU cores (RAYON_NUM_THREADS)", fontsize=12)
|
|
230
231
|
ax_speedup.set_ylabel("Speedup (relative to 1 core)", fontsize=12)
|
|
Binary file
|
|
@@ -1005,7 +1005,7 @@ def from_array_single(
|
|
|
1005
1005
|
|
|
1006
1006
|
def align_three_point(
|
|
1007
1007
|
centerline: PyCenterline,
|
|
1008
|
-
|
|
1008
|
+
geometry: PyGeometryPair | PyGeometry,
|
|
1009
1009
|
aortic_ref_pt: tuple[float, float, float],
|
|
1010
1010
|
upper_ref_pt: tuple[float, float, float],
|
|
1011
1011
|
lower_ref_pt: tuple[float, float, float],
|
|
@@ -1016,20 +1016,19 @@ def align_three_point(
|
|
|
1016
1016
|
output_dir: str = "output/aligned",
|
|
1017
1017
|
contour_types: list[PyContourType] | None = None,
|
|
1018
1018
|
case_name: str = "None",
|
|
1019
|
-
) -> tuple[PyGeometryPair, PyCenterline]:
|
|
1020
|
-
"""Align
|
|
1019
|
+
) -> tuple[PyGeometryPair | PyGeometry, PyCenterline]:
|
|
1020
|
+
"""Align a geometry (or geometry pair) to the centerline using three reference points.
|
|
1021
1021
|
|
|
1022
|
-
Creates centerline-aligned meshes
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
anomalies.
|
|
1022
|
+
Creates centerline-aligned meshes based on three anatomical reference points
|
|
1023
|
+
(aorta, upper section, lower section). Only works for elliptic vessels such
|
|
1024
|
+
as coronary artery anomalies.
|
|
1026
1025
|
|
|
1027
1026
|
Parameters
|
|
1028
1027
|
----------
|
|
1029
1028
|
centerline : PyCenterline
|
|
1030
1029
|
Centerline of the vessel.
|
|
1031
|
-
|
|
1032
|
-
|
|
1030
|
+
geometry : PyGeometryPair or PyGeometry
|
|
1031
|
+
Single geometry or diastolic/systolic geometry pair to align.
|
|
1033
1032
|
aortic_ref_pt : tuple of float
|
|
1034
1033
|
``(x, y, z)`` reference point at the aortic ostium.
|
|
1035
1034
|
upper_ref_pt : tuple of float
|
|
@@ -1043,7 +1042,8 @@ def align_three_point(
|
|
|
1043
1042
|
watertight : bool, optional
|
|
1044
1043
|
Whether to write a watertight or shell mesh. Default is ``True``.
|
|
1045
1044
|
interpolation_steps : int, optional
|
|
1046
|
-
Number of interpolation steps between phases.
|
|
1045
|
+
Number of interpolation steps between phases. Only used when *geometry*
|
|
1046
|
+
is a ``PyGeometryPair``. Default is ``0``.
|
|
1047
1047
|
output_dir : str, optional
|
|
1048
1048
|
Output directory for aligned meshes. Default is ``"output/aligned"``.
|
|
1049
1049
|
contour_types : list of PyContourType, optional
|
|
@@ -1054,15 +1054,15 @@ def align_three_point(
|
|
|
1054
1054
|
|
|
1055
1055
|
Returns
|
|
1056
1056
|
-------
|
|
1057
|
-
|
|
1058
|
-
Aligned geometry
|
|
1057
|
+
geometry : PyGeometryPair or PyGeometry
|
|
1058
|
+
Aligned geometry, matching the type of the input.
|
|
1059
1059
|
centerline : PyCenterline
|
|
1060
1060
|
Resampled centerline.
|
|
1061
1061
|
|
|
1062
1062
|
Examples
|
|
1063
1063
|
--------
|
|
1064
1064
|
>>> import multimodars as mm
|
|
1065
|
-
>>>
|
|
1065
|
+
>>> result, cl = mm.align_three_point(
|
|
1066
1066
|
... centerline,
|
|
1067
1067
|
... geometry_pair,
|
|
1068
1068
|
... (12.2605, -201.3643, 1751.0554),
|
|
@@ -1074,7 +1074,7 @@ def align_three_point(
|
|
|
1074
1074
|
contour_types = _default_contour_types()
|
|
1075
1075
|
return _align_three_point(
|
|
1076
1076
|
centerline,
|
|
1077
|
-
|
|
1077
|
+
geometry,
|
|
1078
1078
|
aortic_ref_pt,
|
|
1079
1079
|
upper_ref_pt,
|
|
1080
1080
|
lower_ref_pt,
|
|
@@ -1090,7 +1090,7 @@ def align_three_point(
|
|
|
1090
1090
|
|
|
1091
1091
|
def align_manual(
|
|
1092
1092
|
centerline: PyCenterline,
|
|
1093
|
-
|
|
1093
|
+
geometry: PyGeometryPair | PyGeometry,
|
|
1094
1094
|
rotation_angle: float,
|
|
1095
1095
|
ref_point: tuple[float, float, float],
|
|
1096
1096
|
write: bool = False,
|
|
@@ -1099,20 +1099,19 @@ def align_manual(
|
|
|
1099
1099
|
output_dir: str = "output/aligned",
|
|
1100
1100
|
contour_types: list[PyContourType] | None = None,
|
|
1101
1101
|
case_name: str = "None",
|
|
1102
|
-
) -> tuple[PyGeometryPair, PyCenterline]:
|
|
1103
|
-
"""Align
|
|
1102
|
+
) -> tuple[PyGeometryPair | PyGeometry, PyCenterline]:
|
|
1103
|
+
"""Align a geometry (or geometry pair) to the centerline using a manual rotation angle.
|
|
1104
1104
|
|
|
1105
|
-
Creates centerline-aligned meshes
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
anomalies.
|
|
1105
|
+
Creates centerline-aligned meshes using an explicit rotation angle and a
|
|
1106
|
+
single reference point on the centerline. Only works for elliptic vessels
|
|
1107
|
+
such as coronary artery anomalies.
|
|
1109
1108
|
|
|
1110
1109
|
Parameters
|
|
1111
1110
|
----------
|
|
1112
1111
|
centerline : PyCenterline
|
|
1113
1112
|
Centerline of the vessel.
|
|
1114
|
-
|
|
1115
|
-
|
|
1113
|
+
geometry : PyGeometryPair or PyGeometry
|
|
1114
|
+
Single geometry or diastolic/systolic geometry pair to align.
|
|
1116
1115
|
rotation_angle : float
|
|
1117
1116
|
Rotation angle in radians to apply.
|
|
1118
1117
|
ref_point : tuple of float
|
|
@@ -1122,7 +1121,8 @@ def align_manual(
|
|
|
1122
1121
|
watertight : bool, optional
|
|
1123
1122
|
Whether to write a watertight or shell mesh. Default is ``True``.
|
|
1124
1123
|
interpolation_steps : int, optional
|
|
1125
|
-
Number of interpolation steps between phases.
|
|
1124
|
+
Number of interpolation steps between phases. Only used when *geometry*
|
|
1125
|
+
is a ``PyGeometryPair``. Default is ``0``.
|
|
1126
1126
|
output_dir : str, optional
|
|
1127
1127
|
Output directory for aligned meshes. Default is ``"output/aligned"``.
|
|
1128
1128
|
contour_types : list of PyContourType, optional
|
|
@@ -1133,15 +1133,15 @@ def align_manual(
|
|
|
1133
1133
|
|
|
1134
1134
|
Returns
|
|
1135
1135
|
-------
|
|
1136
|
-
|
|
1137
|
-
Aligned geometry
|
|
1136
|
+
geometry : PyGeometryPair or PyGeometry
|
|
1137
|
+
Aligned geometry, matching the type of the input.
|
|
1138
1138
|
centerline : PyCenterline
|
|
1139
1139
|
Resampled centerline.
|
|
1140
1140
|
|
|
1141
1141
|
Examples
|
|
1142
1142
|
--------
|
|
1143
1143
|
>>> import multimodars as mm
|
|
1144
|
-
>>>
|
|
1144
|
+
>>> result, cl = mm.align_manual(
|
|
1145
1145
|
... centerline, geometry_pair, rotation_angle=1.57, ref_point=(1.0, 2.0, 3.0)
|
|
1146
1146
|
... )
|
|
1147
1147
|
"""
|
|
@@ -1149,7 +1149,7 @@ def align_manual(
|
|
|
1149
1149
|
contour_types = _default_contour_types()
|
|
1150
1150
|
return _align_manual(
|
|
1151
1151
|
centerline,
|
|
1152
|
-
|
|
1152
|
+
geometry,
|
|
1153
1153
|
rotation_angle,
|
|
1154
1154
|
ref_point,
|
|
1155
1155
|
write,
|
|
@@ -1163,7 +1163,7 @@ def align_manual(
|
|
|
1163
1163
|
|
|
1164
1164
|
def align_combined(
|
|
1165
1165
|
centerline: PyCenterline,
|
|
1166
|
-
|
|
1166
|
+
geometry: PyGeometryPair | PyGeometry,
|
|
1167
1167
|
aortic_ref_pt: tuple[float, float, float],
|
|
1168
1168
|
upper_ref_pt: tuple[float, float, float],
|
|
1169
1169
|
lower_ref_pt: tuple[float, float, float],
|
|
@@ -1177,20 +1177,19 @@ def align_combined(
|
|
|
1177
1177
|
output_dir: str = "output/aligned",
|
|
1178
1178
|
contour_types: list[PyContourType] | None = None,
|
|
1179
1179
|
case_name: str = "None",
|
|
1180
|
-
) -> tuple[PyGeometryPair, PyCenterline]:
|
|
1181
|
-
"""Align
|
|
1180
|
+
) -> tuple[PyGeometryPair | PyGeometry, PyCenterline]:
|
|
1181
|
+
"""Align a geometry (or geometry pair) using three reference points and Hausdorff refinement.
|
|
1182
1182
|
|
|
1183
|
-
Creates centerline-aligned meshes
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
rotation.
|
|
1183
|
+
Creates centerline-aligned meshes using three anatomical reference points
|
|
1184
|
+
for an initial orientation and a set of additional points for
|
|
1185
|
+
Hausdorff distance-based fine-tuning of the rotation.
|
|
1187
1186
|
|
|
1188
1187
|
Parameters
|
|
1189
1188
|
----------
|
|
1190
1189
|
centerline : PyCenterline
|
|
1191
1190
|
Centerline of the vessel.
|
|
1192
|
-
|
|
1193
|
-
|
|
1191
|
+
geometry : PyGeometryPair or PyGeometry
|
|
1192
|
+
Single geometry or diastolic/systolic geometry pair to align.
|
|
1194
1193
|
aortic_ref_pt : tuple of float
|
|
1195
1194
|
``(x, y, z)`` reference point at the aortic ostium.
|
|
1196
1195
|
upper_ref_pt : tuple of float
|
|
@@ -1212,7 +1211,8 @@ def align_combined(
|
|
|
1212
1211
|
watertight : bool, optional
|
|
1213
1212
|
Whether to write a watertight or shell mesh. Default is ``True``.
|
|
1214
1213
|
interpolation_steps : int, optional
|
|
1215
|
-
Number of interpolation steps between phases.
|
|
1214
|
+
Number of interpolation steps between phases. Only used when *geometry*
|
|
1215
|
+
is a ``PyGeometryPair``. Default is ``0``.
|
|
1216
1216
|
output_dir : str, optional
|
|
1217
1217
|
Output directory for aligned meshes. Default is ``"output/aligned"``.
|
|
1218
1218
|
contour_types : list of PyContourType, optional
|
|
@@ -1223,15 +1223,15 @@ def align_combined(
|
|
|
1223
1223
|
|
|
1224
1224
|
Returns
|
|
1225
1225
|
-------
|
|
1226
|
-
|
|
1227
|
-
Aligned geometry
|
|
1226
|
+
geometry : PyGeometryPair or PyGeometry
|
|
1227
|
+
Aligned geometry, matching the type of the input.
|
|
1228
1228
|
centerline : PyCenterline
|
|
1229
1229
|
Resampled centerline.
|
|
1230
1230
|
|
|
1231
1231
|
Examples
|
|
1232
1232
|
--------
|
|
1233
1233
|
>>> import multimodars as mm
|
|
1234
|
-
>>>
|
|
1234
|
+
>>> result, cl = mm.align_combined(
|
|
1235
1235
|
... centerline,
|
|
1236
1236
|
... geometry_pair,
|
|
1237
1237
|
... (12.2605, -201.3643, 1751.0554),
|
|
@@ -1244,7 +1244,7 @@ def align_combined(
|
|
|
1244
1244
|
contour_types = _default_contour_types()
|
|
1245
1245
|
return _align_combined(
|
|
1246
1246
|
centerline,
|
|
1247
|
-
|
|
1247
|
+
geometry,
|
|
1248
1248
|
aortic_ref_pt,
|
|
1249
1249
|
upper_ref_pt,
|
|
1250
1250
|
lower_ref_pt,
|