multimodars 0.3.2__tar.gz → 0.3.5__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.
Files changed (125) hide show
  1. multimodars-0.3.5/.gitattributes +9 -0
  2. {multimodars-0.3.2 → multimodars-0.3.5}/.gitignore +2 -0
  3. {multimodars-0.3.2 → multimodars-0.3.5}/CHANGELOG.md +33 -4
  4. multimodars-0.3.5/CITATION.cff +82 -0
  5. {multimodars-0.3.2 → multimodars-0.3.5}/Cargo.lock +1 -1
  6. {multimodars-0.3.2 → multimodars-0.3.5}/Cargo.toml +1 -1
  7. {multimodars-0.3.2 → multimodars-0.3.5}/PKG-INFO +3 -3
  8. {multimodars-0.3.2 → multimodars-0.3.5}/README.md +2 -2
  9. multimodars-0.3.5/ROADMAP.MD +39 -0
  10. {multimodars-0.3.2 → multimodars-0.3.5}/benchmarks/BENCHMARK_PLAN.md +8 -8
  11. {multimodars-0.3.2 → multimodars-0.3.5}/benchmarks/benchmark_bruteforce_stepsize.py +10 -10
  12. {multimodars-0.3.2 → multimodars-0.3.5}/benchmarks/benchmark_cpu_scaling.py +7 -6
  13. multimodars-0.3.5/benchmarks/results/bruteforce_stepsize.png +0 -0
  14. {multimodars-0.3.2 → multimodars-0.3.5}/multimodars/_converters.py +5 -1
  15. {multimodars-0.3.2 → multimodars-0.3.5}/multimodars/_processing.py +42 -42
  16. {multimodars-0.3.2 → multimodars-0.3.5}/multimodars/ccta/debug_plots.py +26 -5
  17. {multimodars-0.3.2 → multimodars-0.3.5}/multimodars/ccta/manipulating.py +276 -10
  18. multimodars-0.3.5/multimodars/io/__init__.py +2 -0
  19. {multimodars-0.3.2 → multimodars-0.3.5}/multimodars/io/read_geometrical.py +5 -2
  20. {multimodars-0.3.2 → multimodars-0.3.5}/multimodars/io/write_geometries.py +6 -5
  21. {multimodars-0.3.2 → multimodars-0.3.5}/multimodars/multimodars.pyi +6 -6
  22. multimodars-0.3.5/paper/REVIEWER2.md +48 -0
  23. {multimodars-0.3.2 → multimodars-0.3.5}/paper/bibliography.bib +48 -12
  24. multimodars-0.3.5/paper/paper.md +126 -0
  25. {multimodars-0.3.2 → multimodars-0.3.5}/pyproject.toml +1 -0
  26. {multimodars-0.3.2 → multimodars-0.3.5}/src/ccta/adjust_mesh/label_coronary.rs +52 -45
  27. {multimodars-0.3.2 → multimodars-0.3.5}/src/ccta/binding/ccta_py.rs +0 -1
  28. multimodars-0.3.5/src/intravascular/binding/align.rs +397 -0
  29. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/binding/classes.rs +2 -3
  30. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/binding/entry.rs +8 -11
  31. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/binding/mod.rs +145 -34
  32. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/centerline_align/align_algorithms.rs +75 -62
  33. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/centerline_align/mod.rs +139 -94
  34. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/io/geometry.rs +10 -12
  35. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/io/input.rs +11 -16
  36. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/io/integrity_check.rs +3 -8
  37. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/io/mod.rs +3 -8
  38. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/io/output.rs +13 -16
  39. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/processing/align_between.rs +2 -4
  40. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/processing/align_within.rs +4 -4
  41. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/processing/postprocessing.rs +1 -1
  42. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/processing/preprocessing.rs +3 -7
  43. multimodars-0.3.5/src/intravascular/to_object/mod.rs +124 -0
  44. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/to_object/write_mtl.rs +15 -24
  45. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/utils/general_utils.rs +3 -3
  46. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/utils/test_utils.rs +1 -1
  47. {multimodars-0.3.2 → multimodars-0.3.5}/tests/test_ccta.py +406 -21
  48. {multimodars-0.3.2 → multimodars-0.3.5}/tests/test_converters.py +3 -5
  49. {multimodars-0.3.2 → multimodars-0.3.5}/tests/test_core.py +0 -3
  50. multimodars-0.3.5/tests/test_intravascular.py +256 -0
  51. {multimodars-0.3.2 → multimodars-0.3.5}/tests/test_wrappers.py +0 -1
  52. multimodars-0.3.2/ROADMAP.MD +0 -93
  53. multimodars-0.3.2/benchmarks/results/bruteforce_stepsize.png +0 -0
  54. multimodars-0.3.2/multimodars/io/__init__.py +0 -2
  55. multimodars-0.3.2/paper/paper.md +0 -128
  56. multimodars-0.3.2/src/intravascular/binding/align.rs +0 -335
  57. multimodars-0.3.2/src/intravascular/to_object/mod.rs +0 -66
  58. {multimodars-0.3.2 → multimodars-0.3.5}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  59. {multimodars-0.3.2 → multimodars-0.3.5}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  60. {multimodars-0.3.2 → multimodars-0.3.5}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  61. {multimodars-0.3.2 → multimodars-0.3.5}/.github/workflows/CI.yml +0 -0
  62. {multimodars-0.3.2 → multimodars-0.3.5}/.github/workflows/draft-pdf.yml +0 -0
  63. {multimodars-0.3.2 → multimodars-0.3.5}/.pre-commit-config.yaml +0 -0
  64. {multimodars-0.3.2 → multimodars-0.3.5}/.readthedocs.yaml +0 -0
  65. {multimodars-0.3.2 → multimodars-0.3.5}/CONTRIBUTING.md +0 -0
  66. {multimodars-0.3.2 → multimodars-0.3.5}/LICENSE +0 -0
  67. {multimodars-0.3.2 → multimodars-0.3.5}/benchmarks/results/cpu_scaling.png +0 -0
  68. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/idealized_geometry/branch_diastolic_contours.csv +0 -0
  69. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/idealized_geometry/branch_systolic_contours.csv +0 -0
  70. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/idealized_geometry/calcium_diastolic_contours.csv +0 -0
  71. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/idealized_geometry/calcium_systolic_contours.csv +0 -0
  72. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/idealized_geometry/diastolic_contours.csv +0 -0
  73. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/idealized_geometry/diastolic_reference_points.csv +0 -0
  74. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/idealized_geometry/eem_diastolic_contours.csv +0 -0
  75. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/idealized_geometry/eem_systolic_contours.csv +0 -0
  76. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/idealized_geometry/systolic_contours.csv +0 -0
  77. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/idealized_geometry/systolic_reference_points.csv +0 -0
  78. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_full/branch_diastolic_contours.csv +0 -0
  79. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_full/branch_systolic_contours.csv +0 -0
  80. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_full/calcium_diastolic_contours.csv +0 -0
  81. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_full/calcium_systolic_contours.csv +0 -0
  82. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_full/diastolic_contours.csv +0 -0
  83. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_full/diastolic_reference_points.csv +0 -0
  84. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_full/eem_diastolic_contours.csv +0 -0
  85. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_full/eem_systolic_contours.csv +0 -0
  86. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_full/systolic_contours.csv +0 -0
  87. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_full/systolic_reference_points.csv +0 -0
  88. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_rest/combined_sorted_manual.csv +0 -0
  89. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_rest/diastolic_contours.csv +0 -0
  90. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_rest/diastolic_reference_points.csv +0 -0
  91. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_rest/systolic_contours.csv +0 -0
  92. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_rest/systolic_reference_points.csv +0 -0
  93. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_stress/combined_sorted_manual.csv +0 -0
  94. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_stress/diastolic_contours.csv +0 -0
  95. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_stress/diastolic_reference_points.csv +0 -0
  96. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_stress/systolic_contours.csv +0 -0
  97. {multimodars-0.3.2 → multimodars-0.3.5}/data/fixtures/ivus_stress/systolic_reference_points.csv +0 -0
  98. {multimodars-0.3.2 → multimodars-0.3.5}/media/coronary_artery_disease.jpg +0 -0
  99. {multimodars-0.3.2 → multimodars-0.3.5}/media/dynamic_lumen_changes.png +0 -0
  100. {multimodars-0.3.2 → multimodars-0.3.5}/media/multimoda-rs.jpg +0 -0
  101. {multimodars-0.3.2 → multimodars-0.3.5}/multimodars/__init__.py +0 -0
  102. {multimodars-0.3.2 → multimodars-0.3.5}/multimodars/ccta/__init__.py +0 -0
  103. {multimodars-0.3.2 → multimodars-0.3.5}/multimodars/ccta/fixing_functions.py +0 -0
  104. {multimodars-0.3.2 → multimodars-0.3.5}/multimodars/ccta/labeling.py +0 -0
  105. {multimodars-0.3.2 → multimodars-0.3.5}/multimodars/py.typed +0 -0
  106. {multimodars-0.3.2 → multimodars-0.3.5}/paper/REVIEWER1.md +0 -0
  107. {multimodars-0.3.2 → multimodars-0.3.5}/paper/figures/Figure1.jpg +0 -0
  108. {multimodars-0.3.2 → multimodars-0.3.5}/paper/figures/Figure2.jpg +0 -0
  109. {multimodars-0.3.2 → multimodars-0.3.5}/paper/figures/Figure3.png +0 -0
  110. {multimodars-0.3.2 → multimodars-0.3.5}/paper/figures/Figure4.jpg +0 -0
  111. {multimodars-0.3.2 → multimodars-0.3.5}/src/ccta/adjust_mesh/mod.rs +0 -0
  112. {multimodars-0.3.2 → multimodars-0.3.5}/src/ccta/adjust_mesh/scale_coronary.rs +0 -0
  113. {multimodars-0.3.2 → multimodars-0.3.5}/src/ccta/binding/mod.rs +0 -0
  114. {multimodars-0.3.2 → multimodars-0.3.5}/src/ccta/mod.rs +0 -0
  115. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/centerline_align/preprocessing.rs +0 -0
  116. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/mod.rs +0 -0
  117. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/processing/mod.rs +0 -0
  118. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/processing/process_utils.rs +0 -0
  119. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/processing/wall.rs +0 -0
  120. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/to_object/interpolation.rs +0 -0
  121. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/to_object/texture.rs +0 -0
  122. {multimodars-0.3.2 → multimodars-0.3.5}/src/intravascular/utils/mod.rs +0 -0
  123. {multimodars-0.3.2 → multimodars-0.3.5}/src/lib.rs +0 -0
  124. {multimodars-0.3.2 → multimodars-0.3.5}/tests/conftest.py +0 -0
  125. {multimodars-0.3.2 → multimodars-0.3.5}/tests/tests_todo.md +0 -0
@@ -0,0 +1,9 @@
1
+ # Exclude CSV files
2
+ *.csv linguist-generated=true
3
+
4
+ # Exclude Jupyter Notebook outputs
5
+ *.ipynb linguist-generated=true
6
+
7
+ # Exclude documentation files
8
+ *.md linguist-documentation=true
9
+ *.rst linguist-documentation=true
@@ -5,6 +5,8 @@
5
5
  /paper/old
6
6
  .VSCodeCounter
7
7
  /test
8
+ .mypy_cache/
9
+ .ruff_cache/
8
10
  examples/__pycache__/
9
11
  examples/logs/
10
12
  examples/data/output/
@@ -3,6 +3,35 @@
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.5] - 2026-05-15
7
+
8
+ ### Added
9
+ - `clamp_overshoot` parameter in `stitch_ccta_to_intravascular` (default 0.5 mm): for anomalous
10
+ ostia where the boundary-ring plane and the IV plane diverge by ≥ 45°, every proximal boundary
11
+ point is projected onto the IV plane and then pushed at least `clamp_overshoot` mm away from it,
12
+ creating an inward step that softens the stitching angle.
13
+ - `_enforce_layer_gap_from_plane`: the two mesh rings adjacent to the clamped boundary are
14
+ pushed radially outward within the IV plane (ring 1: 0.1 mm, ring 2: 0.2 mm) to eliminate
15
+ ridges at the clamping zone.
16
+ - Tests for `_clamp_to_plane`, `_enforce_layer_gap_from_plane`, and `_prepare_prox_dist_boundary_pts`
17
+ with idealized geometries (`test_ccta.py`, 17 tests).
18
+
19
+ ### Performance
20
+ - Ray-intersection labeling step parallelized via `par_iter()`, reducing wall time on large meshes.
21
+
22
+ ## [0.3.4] - 2026-05-13
23
+
24
+ ### Changed
25
+ - `align_three_point`, `align_manual` and `align_combined` are now all generic, taking either `PyGeometryPair` or `PyGeometry` as input
26
+ - Test file added `test_intravascular.py` to demonstrate that same results with both
27
+
28
+ ## [0.3.3] - 2026-05-06 Release JOSS paper
29
+
30
+ ### Added
31
+ - `CITATION.cff` with full author list, ORCIDs, and preferred citation for JOSS/Zenodo archival.
32
+ - Zenodo DOI badge added to README.
33
+ - Created a release coupled to JOSS publication
34
+
6
35
  ## [0.3.2] - 2026-05-05
7
36
 
8
37
  ### Added
@@ -14,14 +43,14 @@ This project adheres to [Semantic Versioning](https://semver.org/).
14
43
 
15
44
  ### Changed
16
45
  - `paper/paper.md` Performance section updated with quantified benchmark results and
17
- corrected parallelisation description.
46
+ corrected parallelization description.
18
47
 
19
48
  ### Performance
20
- - Intra-pullback alignment (`search_range`) now parallelises over candidate rotation
49
+ - Intra-pullback alignment (`search_range`) now parallelizes over candidate rotation
21
50
  angles via `par_iter` instead of the point-rotation loop. The inner cost-function
22
51
  loop is now sequential. This provides enough rayon tasks per frame step to saturate
23
- all cores: bruteforce scales 6.5× and optimised 4.2× from 2 to 16 cores (previously
24
- ~1.4× for both). Combined algorithmic + parallelisation gain reaches **38.5×** vs.
52
+ all cores: bruteforce scales 6.5× and optimized 4.2× from 2 to 16 cores (previously
53
+ ~1.4× for both). Combined algorithmic + parallelization gain reaches **38.5×** vs.
25
54
  brute-force at 2 cores.
26
55
 
27
56
  ## [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
@@ -753,7 +753,7 @@ dependencies = [
753
753
 
754
754
  [[package]]
755
755
  name = "multimodars"
756
- version = "0.3.2"
756
+ version = "0.3.5"
757
757
  dependencies = [
758
758
  "anyhow",
759
759
  "approx",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "multimodars"
3
- version = "0.3.2"
3
+ version = "0.3.5"
4
4
  edition = "2021"
5
5
  license = "MIT"
6
6
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multimodars
3
- Version: 0.3.2
3
+ Version: 0.3.5
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
  [![Docs](https://img.shields.io/readthedocs/multimoda-rs)](https://multimoda-rs.readthedocs.io)
37
37
  [![Tests and Build](https://github.com/yungselm/multimoda-rs/actions/workflows/CI.yml/badge.svg)](https://github.com/yungselm/multimoda-rs/actions/workflows/CI.yml)
38
38
  [![status](https://joss.theoj.org/papers/24992cb138d85b0bf08f01bc2b384a80/status.svg)](https://joss.theoj.org/papers/24992cb138d85b0bf08f01bc2b384a80)
39
-
39
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20051997.svg)](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://multimod-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:
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
  [![Docs](https://img.shields.io/readthedocs/multimoda-rs)](https://multimoda-rs.readthedocs.io)
13
13
  [![Tests and Build](https://github.com/yungselm/multimoda-rs/actions/workflows/CI.yml/badge.svg)](https://github.com/yungselm/multimoda-rs/actions/workflows/CI.yml)
14
14
  [![status](https://joss.theoj.org/papers/24992cb138d85b0bf08f01bc2b384a80/status.svg)](https://joss.theoj.org/papers/24992cb138d85b0bf08f01bc2b384a80)
15
-
15
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20051997.svg)](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://multimod-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:
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,39 @@
1
+ # Python package that has one purpose: Combining any intravascular image modality with CCTA meshes.
2
+
3
+ # Optimizations
4
+ - [] Optimize the ray intersection algorithm
5
+ - [] Better parallelization strategy for frames alignment by processing in batches (frames only have to be aligned relative to each other not sequentially)
6
+
7
+ # Mesh combination (v0.3.0)
8
+ ## New/improved features
9
+ - [] "Smooth" the aortic ring of boundary points first, by projecting on plane to shift outliers
10
+ - [] Write a more stable function to find intramural points on aorta (for now works with excluded RCA points)
11
+ - [] Create an aortic and coronary wall with a consistent connection
12
+ - [] Find bifurcation reference points automatically
13
+ - [] `numpy_to_report` function that allows to read in lists for the four variables of interest (frame, phase, measurement_1, measurement_2)
14
+ - [] write metadata to .obj files so they can be reconstructed completely to geometry/geometrypair
15
+
16
+ ## Fixes/Improvements
17
+ - [] Write a more stable function to find intramural points on aorta (for now works with excluded RCA points)
18
+ - [] fix centerline resampling
19
+
20
+ ## Investigate/check logic
21
+ - [] When aligning geometry with wall has a rotation in the wall -> logic error for centerline alignment?
22
+
23
+ # CCTA clinical assessment (v0.4.0)
24
+ - [] automatically find the required reference points for anomalous alignment
25
+ - [] automatically assess lumen area, minor-, major axis, mla, intramural length etc. from the CCTA mesh.
26
+ - [] return hausdorff distances per point between aligned ivus mesh and ccta mesh.
27
+ - [] function to automatically assign wall thickness to ccta and combined ivus/ccta mesh
28
+
29
+ # Miscellaneous
30
+ - [] Integrate coverage measurement (`pytest-cov`) and upload reports to Coveralls or Codecov.
31
+ - [] Add a coverage status badge to the README.
32
+
33
+ # Centerline alignment module (v1.0.0)
34
+ - [] Create Centerline directly from mesh
35
+
36
+ # Myocardial Fusion v2.0.0
37
+ - [] Read in LGE/T2 etc. from MRI and map onto CCTA myocardium
38
+ - [] Read in Perfusion map from PET/SPECT and load onto CCTA myocardium
39
+ - [] 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. optimised at varying step sizes | `from_file_full` | IVUS rest/stress |
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. Optimised: Step-Size Scaling
16
+ ## Benchmark 1 — Bruteforce vs. optimized: Step-Size Scaling
17
17
 
18
18
  ### Goal
19
19
 
20
- Quantify how bruteforce and optimised alignment scale as `step_rotation_deg` decreases
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
- - **Optimised**: coarse-to-fine hierarchical search → sub-linear in n\_steps,
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: optimised (blue ■ --)
68
- - Right y-axis: speedup factor (bruteforce / optimised)
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
- - **Optimised**: near-linear speedup up to ~8 cores, then diminishing returns
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, optimised)
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. optimised alignment at varying step sizes.
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": [], "optimised": []}
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["optimised"].append(med_opt)
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["optimised"]
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="Optimised",
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. Optimised — Step-Size Scaling\n"
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 / optimised)", fontsize=12)
167
+ ax_speedup.set_ylabel("Speedup (bruteforce / optimized)", fontsize=12)
168
168
  ax_speedup.set_title(
169
- "Speedup of Optimised over Bruteforce\n"
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. Optimised — Step-Size Scaling ===\n")
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 optimised vs. bruteforce alignment.
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": [], "optimised": []}
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["optimised"].append(med_opt)
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["optimised"]
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="Optimised",
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="Optimised",
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)
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing import cast
4
+
3
5
  import numpy as np
4
6
  import trimesh
5
7
  from .multimodars import (
@@ -139,7 +141,9 @@ def _geometry_to_numpy(geom) -> dict[str, np.ndarray]:
139
141
  if len(result[key]) == 0:
140
142
  result[key] = frame_data[key]
141
143
  else:
142
- result[key] = np.vstack([result[key], frame_data[key]])
144
+ result[key] = cast(
145
+ np.ndarray, np.vstack([result[key], frame_data[key]])
146
+ )
143
147
 
144
148
  return result
145
149