multimoda-rs: Intravascular Module – Notebook Tutorial
This notebook follows the Intravascular Alignment Tutorial step-by-step:
General note on segmentation preparation
Workflow from CSV files (AIVUS-CAA format)
Workflow from numpy arrays — including parameter fine-tuning
Alignment with a CCTA centerline
Saving geometries as
.objfilesUtility functions:
to_arrayandnumpy_to_geometryClass-level methods for
PyContour,PyFrame, andPyGeometry
Performance note: Alignment is compute-intensive. Running the notebook via the console is faster than interactive cell-by-cell execution.
1. General note on segmentation preparation
The package works with segmented intravascular images from IVUS or OCT. Segmentation must follow specific conventions for optimal results:
Files follow AIVUS-CAA convention:
diastolic_contours.csv,systolic_contours.csv,diastolic_reference_points.csv,systolic_reference_points.csv.Each file has columns
(frame_index, x_mm, y_mm, z_mm)— no header row.A correct anatomical reference point is essential for downstream centerline co-registration.
For CAD a bifurcation serves as reference; for AAOCA the reference is placed at the aortic side of the most proximal fully closed lumen contour (see tutorial for details).
Four processing modes
Mode |
Inputs |
Typical use-case |
|---|---|---|
|
4 states (rest-dia, rest-sys, stress-dia, stress-sys) |
AAOCA pulsatile lumen analysis |
|
2 independent pullbacks, each with dia+sys |
Pre/post-stenting across both phases |
|
1 pullback with dia+sys |
CAD: diastole vs. systole within one pullback |
|
1 pullback (dia or sys only) |
Standalone reconstruction (CAD, OCT) |
import os
from pathlib import Path
import numpy as np
import multimodars as mm
cwd = Path.cwd()
for candidate in [cwd, cwd.parent, cwd.parent.parent]:
if (candidate / "examples" / "data").exists():
os.chdir(candidate / "examples" / "data")
break
elif (candidate / "data").exists():
os.chdir(candidate / "data")
break
print(f"Working directory: {os.getcwd()}")
Working directory: /mnt/c/WorkingData/Documents/2_Coding/Rust/multimodars/examples/data
Note: you may need to restart the kernel to use updated packages.
2. Workflow from CSV files
This section uses AIVUS-CAA CSV files.
from_file_full processes all four states simultaneously (rest-diastole, rest-systole,
stress-diastole, stress-systole) and writes aligned meshes to four output directories.
See the tutorial for a detailed description of the CSV format.
# Full 4-state workflow: rest vs. stress, diastole vs. systole
rest, stress, dia, sys, _ = mm.from_file_full(
input_path_ab="ivus_rest",
input_path_cd="ivus_stress",
labels=["rest_dia", "rest_sys", "stress_dia", "stress_sys"],
step_rotation_deg=0.1,
range_rotation_deg=90,
image_center=(4.5, 4.5),
radius=0.5,
n_points=20,
write_obj=True,
watertight=False,
contour_types=[mm.PyContourType.Lumen, mm.PyContourType.Catheter, mm.PyContourType.Wall],
output_path_ab="output/rest",
output_path_cd="output/stress",
output_path_ac="output/diastole",
output_path_bd="output/systole",
interpolation_steps=28,
)
✅ Successfully built geometry from path
-----------------------------------------
✅ Lumen
❌ Eem
❌ Calcification
❌ Sidebranch
✅ Catheter
-----------------------------------------
Label: rest_dia
Diastole phase: Yes
✅ Successfully built geometry from path
-----------------------------------------
✅ Lumen
❌ Eem
❌ Calcification
❌ Sidebranch
✅ Catheter
-----------------------------------------
Label: rest_sys
Diastole phase: No
✅ Successfully built geometry from path
-----------------------------------------
✅ Lumen
❌ Eem
❌ Calcification
❌ Sidebranch
✅ Catheter
-----------------------------------------
Label: stress_dia
Diastole phase: Yes
✅ Successfully built geometry from path
-----------------------------------------
✅ Lumen
❌ Eem
❌ Calcification
❌ Sidebranch
✅ Catheter
-----------------------------------------
Label: stress_sys
Diastole phase: No
+--------------------------------------------------------------------+
| ✅ Finished aligning 'rest_sys' (anomalous: true) |
+---------+------------+---------------+-------+-------+-------------+
| Contour | Matched To | Rotation (°) | Tx | Ty | Centroid |
+---------+------------+---------------+-------+-------+-------------+
| 1 | 0 | -2.30 | -0.19 | -0.01 | (3.63,5.12) |
| 2 | 1 | 12.20 | -0.54 | 0.15 | (3.63,5.12) |
| 3 | 2 | -10.30 | -0.44 | 0.53 | (3.63,5.12) |
| 4 | 3 | -4.10 | -0.72 | 0.94 | (3.63,5.12) |
| 5 | 4 | 0.80 | -0.75 | 0.26 | (3.63,5.12) |
| 6 | 5 | 5.10 | -0.79 | 0.65 | (3.63,5.12) |
| 7 | 6 | -6.50 | -1.21 | 0.69 | (3.63,5.12) |
| 8 | 7 | 14.70 | -1.08 | 0.81 | (3.63,5.12) |
| 9 | 8 | -35.30 | -1.03 | 0.24 | (3.63,5.12) |
| 10 | 9 | 25.50 | -1.46 | 0.28 | (3.63,5.12) |
| 11 | 10 | 41.30 | -2.40 | 0.73 | (3.63,5.12) |
| 12 | 11 | -21.20 | -1.95 | 0.68 | (3.63,5.12) |
| 13 | 12 | 4.80 | -2.06 | 0.66 | (3.63,5.12) |
| 14 | 13 | -6.90 | -2.14 | 0.52 | (3.63,5.12) |
| 15 | 14 | 23.40 | -2.27 | 0.46 | (3.63,5.12) |
| 16 | 15 | 7.30 | -2.28 | 0.53 | (3.63,5.12) |
+---------+------------+---------------+-------+-------+-------------+
+--------------------------------------------------------------------+
| ✅ Finished aligning 'stress_dia' (anomalous: true) |
+---------+------------+---------------+-------+-------+-------------+
| Contour | Matched To | Rotation (°) | Tx | Ty | Centroid |
+---------+------------+---------------+-------+-------+-------------+
| 1 | 0 | 3.80 | -0.10 | 0.02 | (3.60,4.59) |
| 2 | 1 | 6.20 | -0.38 | 0.40 | (3.60,4.59) |
| 3 | 2 | 1.90 | -0.24 | 0.42 | (3.60,4.59) |
| 4 | 3 | -1.60 | -0.11 | 0.49 | (3.60,4.59) |
| 5 | 4 | -1.90 | -0.22 | 0.50 | (3.60,4.59) |
| 6 | 5 | 0.70 | -0.03 | 0.10 | (3.60,4.59) |
| 7 | 6 | 3.20 | -0.05 | 0.09 | (3.60,4.59) |
| 8 | 7 | 0.80 | -0.11 | -0.09 | (3.60,4.59) |
| 9 | 8 | 2.40 | -0.14 | -0.13 | (3.60,4.59) |
| 10 | 9 | 14.60 | -0.75 | 0.01 | (3.60,4.59) |
| 11 | 10 | 0.00 | -1.08 | 0.08 | (3.60,4.59) |
| 12 | 11 | 8.80 | -1.21 | 0.08 | (3.60,4.59) |
| 13 | 12 | 11.80 | -1.28 | 0.20 | (3.60,4.59) |
| 14 | 13 | -14.20 | -1.46 | -0.02 | (3.60,4.59) |
| 15 | 14 | 22.50 | -1.55 | 0.29 | (3.60,4.59) |
| 16 | 15 | -13.20 | -1.72 | 0.16 | (3.60,4.59) |
| 17 | 16 | -18.90 | -1.61 | -0.27 | (3.60,4.59) |
| 18 | 17 | -2.70 | -1.41 | -0.44 | (3.60,4.59) |
| 19 | 18 | 7.50 | -1.74 | -0.31 | (3.60,4.59) |
| 20 | 19 | 10.70 | -1.77 | -0.55 | (3.60,4.59) |
| 21 | 20 | 18.00 | -2.30 | -0.37 | (3.60,4.59) |
| 22 | 21 | 0.00 | -2.04 | -0.25 | (3.60,4.59) |
| 23 | 22 | 24.70 | -2.34 | 0.36 | (3.60,4.59) |
| 24 | 23 | 4.10 | -2.29 | 0.78 | (3.60,4.59) |
+---------+------------+---------------+-------+-------+-------------+
+--------------------------------------------------------------------+
| ✅ Finished aligning 'rest_dia' (anomalous: true) |
+---------+------------+---------------+-------+-------+-------------+
| Contour | Matched To | Rotation (°) | Tx | Ty | Centroid |
+---------+------------+---------------+-------+-------+-------------+
| 1 | 0 | 7.70 | 0.12 | -0.36 | (3.72,5.25) |
| 2 | 1 | 0.70 | 0.04 | -0.16 | (3.72,5.25) |
| 3 | 2 | 6.20 | -0.28 | 0.03 | (3.72,5.25) |
| 4 | 3 | -13.10 | -0.16 | 0.45 | (3.72,5.25) |
| 5 | 4 | -23.90 | -0.32 | 0.89 | (3.72,5.25) |
| 6 | 5 | -5.10 | -0.48 | 1.10 | (3.72,5.25) |
| 7 | 6 | -29.80 | -0.94 | 0.82 | (3.72,5.25) |
| 8 | 7 | -18.00 | -1.29 | 0.87 | (3.72,5.25) |
| 9 | 8 | 1.50 | -1.36 | 0.86 | (3.72,5.25) |
| 10 | 9 | -1.20 | -1.68 | 1.27 | (3.72,5.25) |
| 11 | 10 | 47.40 | -1.54 | 1.87 | (3.72,5.25) |
| 12 | 11 | 3.30 | -1.45 | 1.95 | (3.72,5.25) |
| 13 | 12 | 1.00 | -1.51 | 2.08 | (3.72,5.25) |
| 14 | 13 | 30.60 | -0.96 | 2.12 | (3.72,5.25) |
| 15 | 14 | -7.00 | -1.11 | 1.93 | (3.72,5.25) |
| 16 | 15 | 19.40 | -0.68 | 2
sidebranch file not found, skipping: "ivus_rest/branch_diastolic_contours.csv"
process_directory: unknown mapping name 'catheter', skipping
eem file not found, skipping: "ivus_rest/eem_diastolic_contours.csv"
calcification file not found, skipping: "ivus_rest/calcium_diastolic_contours.csv"
process_directory: unknown mapping name 'catheter', skipping
calcification file not found, skipping: "ivus_rest/calcium_systolic_contours.csv"
eem file not found, skipping: "ivus_rest/eem_systolic_contours.csv"
sidebranch file not found, skipping: "ivus_rest/branch_systolic_contours.csv"
sidebranch file not found, skipping: "ivus_stress/branch_diastolic_contours.csv"
calcification file not found, skipping: "ivus_stress/calcium_diastolic_contours.csv"
eem file not found, skipping: "ivus_stress/eem_diastolic_contours.csv"
process_directory: unknown mapping name 'catheter', skipping
sidebranch file not found, skipping: "ivus_stress/branch_systolic_contours.csv"
calcification file not found, skipping: "ivus_stress/calcium_systolic_contours.csv"
process_directory: unknown mapping name 'catheter', skipping
eem file not found, skipping: "ivus_stress/eem_systolic_contours.csv"
.47 | (3.72,5.25) |
| 17 | 16 | 3.40 | -0.50 | 2.35 | (3.72,5.25) |
| 18 | 17 | -0.90 | -0.58 | 2.24 | (3.72,5.25) |
| 19 | 18 | -11.00 | -0.95 | 2.37 | (3.72,5.25) |
+---------+------------+---------------+-------+-------+-------------+
+--------------------------------------------------------------------+
| ✅ Finished aligning 'stress_sys' (anomalous: true) |
+---------+------------+---------------+-------+-------+-------------+
| Contour | Matched To | Rotation (°) | Tx | Ty | Centroid |
+---------+------------+---------------+-------+-------+-------------+
| 1 | 0 | 0.00 | -0.22 | 0.10 | (3.80,4.64) |
| 2 | 1 | 1.00 | -0.19 | 0.14 | (3.80,4.64) |
| 3 | 2 | 0.00 | -0.04 | 0.17 | (3.80,4.64) |
| 4 | 3 | -1.20 | 0.08 | 0.15 | (3.80,4.64) |
| 5 | 4 | -1.10 | 0.03 | 0.09 | (3.80,4.64) |
| 6 | 5 | 0.20 | 0.06 | 0.06 | (3.80,4.64) |
| 7 | 6 | 7.30 | 0.06 | 0.24 | (3.80,4.64) |
| 8 | 7 | 2.60 | -0.30 | 0.00 | (3.80,4.64) |
| 9 | 8 | 11.50 | -0.32 | -0.18 | (3.80,4.64) |
| 10 | 9 | 9.50 | -0.57 | -0.32 | (3.80,4.64) |
| 11 | 10 | -1.00 | -0.53 | -0.36 | (3.80,4.64) |
| 12 | 11 | 3.40 | -0.49 | -0.39 | (3.80,4.64) |
| 13 | 12 | -4.10 | -0.52 | -0.46 | (3.80,4.64) |
| 14 | 13 | 8.40 | -0.63 | -0.37 | (3.80,4.64) |
| 15 | 14 | -2.10 | -0.76 | -0.43 | (3.80,4.64) |
| 16 | 15 | 8.50 | -0.79 | -0.41 | (3.80,4.64) |
| 17 | 16 | -6.00 | -0.81 | -0.81 | (3.80,4.64) |
| 18 | 17 | 0.00 | -0.83 | -1.10 | (3.80,4.64) |
| 19 | 18 | 0.40 | -0.82 | -1.12 | (3.80,4.64) |
| 20 | 19 | 3.20 | -0.89 | -1.13 | (3.80,4.64) |
| 21 | 20 | 9.50 | -0.96 | -1.14 | (3.80,4.64) |
+---------+------------+---------------+-------+-------+-------------+
✅ Aligned geometry 'stress_sys' to 'stress_dia'
-----------------------------------------
Applied initial translation: (-0.20, -0.05, 0.00) mm
Found best rotation of 2.70° with parameters:
range: 90.00°
step size: 0.1°
Applied final translation: ( 0, 0.00, 0.00) mm
-----------------------------------------
✅ Aligned geometry 'rest_sys' to 'rest_dia'
-----------------------------------------
Applied initial translation: (0.09, 0.13, -3.94) mm
Found best rotation of 3.00° with parameters:
range: 90.00°
step size: 0.1°
Applied final translation: ( 0, 0.00, 0.00) mm
-----------------------------------------
✅ Aligned geometry 'stress_sys' to 'rest_sys'
-----------------------------------------
Applied initial translation: (0.12, 0.66, 0.00) mm
Found best rotation of 1.80° with parameters:
range: 90.00°
step size: 0.1°
Applied final translation: ( 0, 0.00, 0.00) mm
-----------------------------------------
✅ Aligned geometry 'stress_dia' to 'rest_dia'
-----------------------------------------
Applied initial translation: (0.12, 0.66, 0.00) mm
Found best rotation of 17.80° with parameters:
range: 90.00°
step size: 0.1°
Applied final translation: ( 0, 0.00, 0.00) mm
-----------------------------------------
Saving files for 'rest_dia - rest_sys' to 'output/rest'
LUMEN .obj files: 30/30 written successfully
CATHETER .obj files: 30/30 written successfully
WALL .obj files: 30/30 written successfully
Saving files for 'stress_dia - stress_sys' to 'output/stress'
LUMEN .obj files: 30/30 written successfully
CATHETER .obj files: 30/30 written successfully
WALL .obj files: 30/30 written successfully
Saving files for 'rest_dia - stress_dia' to 'output/diastole'
LUMEN .obj files: 30/30 written successfully
CATHETER .obj files: 30/30 written successfully
WALL .obj files: 30/30 written successfully
Saving files for 'rest_sys - stress_sys' to 'output/systole'
LUMEN .obj files: 30/30 written successfully
CATHETER .obj files: 30/30 written successfully
WALL .obj files: 30/30 written successfully
# Build "before" meshes from raw CSV, then write explicit .obj files for comparison
rest_dia_raw = np.genfromtxt("ivus_rest/diastolic_contours.csv")
rest_dia_ref = np.genfromtxt("ivus_rest/diastolic_reference_points.csv")
rest_sys_raw = np.genfromtxt("ivus_rest/systolic_contours.csv")
rest_sys_ref = np.genfromtxt("ivus_rest/systolic_reference_points.csv")
rest_dia_before = mm.numpy_to_geometry(
lumen_arr=rest_dia_raw, eem_arr=np.array([]), catheter_arr=np.array([]),
wall_arr=np.array([]), reference_arr=rest_dia_ref, label="dia_before",
)
rest_sys_before = mm.numpy_to_geometry(
lumen_arr=rest_sys_raw, eem_arr=np.array([]), catheter_arr=np.array([]),
wall_arr=np.array([]), reference_arr=rest_sys_ref, label="sys_before",
)
rest_dia_before.frames = np.array([f.sort_frame_points() for f in rest_dia_before.frames])
rest_sys_before.frames = np.array([f.sort_frame_points() for f in rest_sys_before.frames])
# Write before (unprocessed) and after (aligned) meshes with explicit names
mm.to_obj(rest_dia_before, "output/before", watertight=False,
contour_types=[mm.PyContourType.Lumen], filename_prefix="dia")
mm.to_obj(rest_sys_before, "output/before", watertight=False,
contour_types=[mm.PyContourType.Lumen], filename_prefix="sys")
mm.to_obj(rest.geom_a, "output/after", watertight=False,
contour_types=[mm.PyContourType.Lumen], filename_prefix="dia")
mm.to_obj(rest.geom_b, "output/after", watertight=False,
contour_types=[mm.PyContourType.Lumen], filename_prefix="sys")
Successfully wrote lumen to output/before/dia_lumen.obj
Successfully wrote lumen to output/before/sys_lumen.obj
Successfully wrote lumen to output/after/dia_lumen.obj
Successfully wrote lumen to output/after/sys_lumen.obj
plot_pair(
before_paths=["output/before/dia_lumen.obj", "output/before/sys_lumen.obj"],
after_paths=["output/after/dia_lumen.obj", "output/after/sys_lumen.obj"],
colors=["royalblue", "firebrick"],
titles=["Before Processing", "After Processing"],
)
The data is now neatly ordered in pairs (diastolic and systolic geometry). Every geometry contains
contours for lumen, wall, and a synthetic catheter. The reference point is used for centerline
co-registration. All points belonging to a contour are stored in a PyContour struct.
print(f"PyGeometryPair:\n{rest}")
print(f"PyGeometry:\n{rest.geom_a}")
print(f"PyFrame:\n{rest.geom_a.frames[0]}")
print(f"PyContour:\n{rest.geom_a.frames[0].lumen}")
print(f"Extras:\n{rest.geom_a.frames[0].extras}")
print(f"PyContourPoint:\n{rest.geom_a.frames[0].lumen.points[0]}")
PyGeometryPair:
GeometryPair rest_dia - rest_sys (diastolic: 14 frames, systolic: 14 frames)
PyGeometry:
Geometry(14 frames, label='rest_dia')
PyFrame:
Frame(id=0, centroid=(3.72, 5.25, 0.00), lumen=Contour(id=0, frame=385, points=501, centroid=(3.72, 5.25, 0.00), kind=Lumen), extras=2)
PyContour:
Contour(id=0, frame=385, points=501, centroid=(3.72, 5.25, 0.00), kind=Lumen)
Extras:
{'Catheter': Contour(id=0, frame=385, points=20, centroid=(3.84, 6.33, 0.00), kind=Catheter), 'Wall': Contour(id=0, frame=385, points=501, centroid=(3.72, 5.25, 0.00), kind=Wall)}
PyContourPoint:
Point(frame_id=19, pt_id=0, x=3.80, y=7.90, z=0.00, aortic=false)
Different stenosis measurements can be obtained directly from the objects:
# Summary over PyGeometryPair
summary_pair, deform_table = rest.get_summary()
print(f"PyGeometryPair summary (dia_geom: (mla [mm²], max. stenosis, stenosis length [mm])):\n{summary_pair}")
print(f"Deformation table:\n{np.array(deform_table)}")
# Summary over PyGeometry
print(f"\nPyGeometry summary:\n{rest.geom_a.get_summary()[0]}")
# Per-contour measurements
print(f"\nContour area [mm²]: {rest.geom_a.frames[0].lumen.get_area():.4f}")
print(f"Elliptic ratio: {rest.geom_a.frames[-1].lumen.get_elliptic_ratio():.4f}")
+----+----------+-----------+----------+-----------+-------+
| id | area_dia | ellip_dia | area_sys | ellip_sys | z |
+----+----------+-----------+----------+-----------+-------+
PyGeometryPair summary (dia_geom: (mla [mm²], max. stenosis, stenosis length [mm])):
((5.559206007496853, 0.6780775439844883, 10.364606842105268), (6.111481670202526, 0.663038559502465, 7.7734551315789515))
Deformation table:
[[ 0. 5.63662776 4.58651358 6.11148167 4.35376487 0. ]
[ 1. 5.79662471 4.75220339 6.25936689 3.38956113 1.29557586]
[ 2. 5.70440311 3.69759593 6.49106359 2.85326248 2.59115171]
[ 3. 5.55920601 2.56932308 6.84671006 2.17905332 3.88672757]
[ 4. 5.5883072 1.63583386 7.59416089 1.90836576 5.18230342]
[ 5. 6.41743783 1.50724622 7.57484592 1.67549107 6.47787928]
[ 6. 7.25510456 1.36697234 7.3074273 1.63078837 7.77345513]
[ 7. 7.74398201 1.24613112 10.23430197 1.32956583 9.06903099]
[ 8. 7.77347285 1.43038361 11.90101937 1.19922679 10.36460684]
[ 9. 9.05387612 1.32700846 14.15414345 1.08910895 11.6601827 ]
[10. 11.91520903 1.18319293 11.7040278 1.17176063 12.95575855]
[11. 15.27166328 1.08294811 13.56639023 1.14517785 14.25133441]
[12. 17.26877359 1.05840795 15.42409292 1.11280753 15.54691026]
[13. 16.34175131 1.0407399 18.13703568 1.06629346 16.84248612]]
| 0 | 5.64 | 4.59 | 6.11 | 4.35 | 0.00 |
| 1 | 5.80 | 4.75 | 6.26 | 3.39 | 1.30 |
| 2 | 5.70 | 3.70 | 6.49 | 2.85 | 2.59 |
| 3 | 5.56 | 2.57 | 6.85 | 2.18 | 3.89 |
| 4 | 5.59 | 1.64 | 7.59 | 1.91 | 5.18 |
| 5 | 6.42 | 1.51 | 7.57 | 1.68 | 6.48 |
| 6 | 7.26 | 1.37 | 7.31 | 1.63 | 7.77 |
| 7 | 7.74 | 1.25 | 10.23 | 1.33 | 9.07 |
| 8 | 7.77 | 1.43 | 11.90 | 1.20 | 10.36 |
| 9 | 9.05 | 1.33 | 14.15 | 1.09 | 11.66 |
| 10 | 11.92 | 1.18 | 11.70 | 1.17 | 12.96 |
| 11 | 15.27 | 1.08 | 13.57 | 1.15 | 14.25 |
| 12 | 17.27 | 1.06 | 15.42 | 1.11 | 15.55 |
| 13 | 16.34 | 1.04 | 18.14 | 1.07 | 16.84 |
+----+----------+-----------+----------+-----------+-------+
PyGeometry summary:
5.559206007496853
Contour area [mm²]: 5.6366
Elliptic ratio: 1.0407
The four pairs represent all four possible comparisons in gated images — particularly relevant for coronary artery anomalies (AAOCA): rest pulsatile, stress pulsatile, stress-induced diastolic, and stress-induced systolic lumen deformation.
The interpolation_steps parameter generates intermediate meshes between paired states, useful for
deformation animations in Blender. Set interpolation_steps=0 to skip interpolation.
Coronary Artery Disease
from_file_single reconstructs a single pullback with all contour layers (lumen, EEM, wall, catheter):
cad, _ = mm.from_file_single(
input_path="ivus_full",
diastole=True,
step_rotation_deg=0.1,
range_rotation_deg=90,
image_center=(4.5, 4.5),
radius=0.5,
n_points=20,
write_obj=True,
watertight=False,
contour_types=[
mm.PyContourType.Lumen, mm.PyContourType.Eem,
mm.PyContourType.Catheter, mm.PyContourType.Wall,
],
output_path="output/cad",
)
# Centre coordinate system on EEM and write both versions
cad_aligned = cad.center_to_contour(mm.PyContourType.Eem)
mm.to_obj(cad_aligned, "output/cad", watertight=False,
contour_types=[mm.PyContourType.Lumen, mm.PyContourType.Eem, mm.PyContourType.Wall],
filename_prefix="aligned")
# from_file_single writes files as {contour_type}_{input_path_name}.obj
lumen = trimesh.load("output/cad/lumen_ivus_full.obj")
eem = trimesh.load("output/cad/eem_ivus_full.obj")
wall = trimesh.load("output/cad/wall_ivus_full.obj")
lumen_a = trimesh.load("output/cad/aligned_lumen.obj")
eem_a = trimesh.load("output/cad/aligned_eem.obj")
wall_a = trimesh.load("output/cad/aligned_wall.obj")
fig = make_subplots(
rows=1, cols=2,
specs=[[{"type": "scene"}, {"type": "scene"}]],
subplot_titles=("CAD (lumen-centred)", "CAD (EEM-centred)"),
)
camera = dict(eye=dict(x=1.5, y=1.5, z=1.0))
for t in [trimesh_to_mesh3d(lumen, "firebrick", "Lumen", 1.0),
trimesh_to_mesh3d(eem, "royalblue", "EEM", 0.6),
trimesh_to_mesh3d(wall, "white", "Wall", 0.5)]:
fig.add_trace(t, row=1, col=1)
for t in [trimesh_to_mesh3d(lumen_a, "firebrick", "Lumen", 1.0),
trimesh_to_mesh3d(eem_a, "royalblue", "EEM", 0.6),
trimesh_to_mesh3d(wall_a, "white", "Wall", 0.5)]:
fig.add_trace(t, row=1, col=2)
fig.update_layout(
width=900, height=450,
scene=dict(camera=camera, aspectmode="data"),
scene2=dict(camera=camera, aspectmode="data"),
margin=dict(l=0, r=0, t=40, b=0),
)
fig.show()
✅ Successfully built geometry from path
-----------------------------------------
✅ Lumen
✅ Eem
✅ Calcification
✅ Sidebranch
✅ Catheter
-----------------------------------------
Label: ivus_full
Diastole phase: Yes
+--------------------------------------------------------------------+
| ✅ Finished aligning 'ivus_full' (anomalous: false) |
+---------+------------+---------------+-------+-------+-------------+
| Contour | Matched To | Rotation (°) | Tx | Ty | Centroid |
+---------+------------+---------------+-------+-------+-------------+
| 1 | 0 | 0.00 | 0.04 | -0.07 | (4.38,4.06) |
| 2 | 1 | -0.60 | 0.11 | -0.15 | (4.38,4.06) |
| 3 | 2 | 19.20 | 0.04 | -0.24 | (4.38,4.06) |
| 4 | 3 | 90.00 | -0.12 | -0.46 | (4.38,4.06) |
| 5 | 4 | 5.30 | -0.24 | -0.36 | (4.38,4.06) |
| 6 | 5 | -36.00 | -0.16 | -0.44 | (4.38,4.06) |
| 7 | 6 | 18.00 | -0.21 | -0.50 | (4.38,4.06) |
| 8 | 7 | 2.70 | -0.12 | -0.52 | (4.38,4.06) |
| 9 | 8 | -18.00 | -0.08 | -0.46 | (4.38,4.06) |
| 10 | 9 | 0.00 | 0.02 | -0.39 | (4.38,4.06) |
| 11 | 10 | 0.00 | -0.06 | -0.42 | (4.38,4.06) |
| 12 | 11 | -90.00 | -0.14 | -0.34 | (4.38,4.06) |
| 13 | 12 | 69.40 | 0.05 | -0.52 | (4.38,4.06) |
| 14 | 13 | 0.00 | 0.29 | -0.53 | (4.38,4.06) |
| 15 | 14 | -5.90 | 0.32 | -0.51 | (4.38,4.06) |
| 16 | 15 | -12.30 | 0.36 | -0.50 | (4.38,4.06) |
| 17 | 16 | -6.90 | 0.35 | -0.36 | (4.38,4.06) |
| 18 | 17 | 0.00 | 0.38 | -0.40 | (4.38,4.06) |
| 19 | 18 | -8.60 | 0.35 | -0.35 | (4.38,4.06) |
| 20 | 19 | -0.80 | 0.43 | -0.35 | (4.38,4.06) |
| 21 | 20 | -19.60 | 0.35 | -0.23 | (4.38,4.06) |
| 22 | 21 | -10.50 | 0.39 | -0.05 | (4.38,4.06) |
| 23 | 22 | -3.70 | 0.31 | 0.03 | (4.38,4.06) |
| 24
process_directory: unknown mapping name 'catheter', skipping
| 23 | -2.20 | 0.23 | 0.04 | (4.38,4.06) |
| 25 | 24 | 0.00 | 0.14 | -0.00 | (4.38,4.06) |
| 26 | 25 | 0.00 | 0.03 | -0.09 | (4.38,4.06) |
| 27 | 26 | -18.00 | -0.06 | -0.16 | (4.38,4.06) |
| 28 | 27 | 17.00 | -0.05 | -0.14 | (4.38,4.06) |
| 29 | 28 | 0.00 | -0.01 | -0.06 | (4.38,4.06) |
| 30 | 29 | 18.00 | 0.08 | -0.02 | (4.38,4.06) |
| 31 | 30 | 89.40 | 0.20 | -0.59 | (4.38,4.06) |
+---------+------------+---------------+-------+-------+-------------+
Successfully wrote OBJ files for geometry ivus_full to output/cad
Successfully wrote lumen to output/cad/aligned_lumen.obj
Successfully wrote eem to output/cad/aligned_eem.obj
Successfully wrote wall to output/cad/aligned_wall.obj
Pre- vs. Post-stenting
For pre/post-stenting comparison across both cardiac phases, from_file_full is used with the
pre-stent pullback as input_path_ab and the post-stent pullback as input_path_cd.
prestent, poststent, dia_comp, sys_comp, _ = mm.from_file_full(
input_path_ab="ivus_prestent",
input_path_cd="ivus_poststent",
step_rotation_deg=0.1,
range_rotation_deg=45,
watertight=False,
output_path_ab="output/stent_rest",
output_path_cd="output/stent_stress",
output_path_ac="output/stent_diastole",
output_path_bd="output/stent_systole",
interpolation_steps=0,
)
# Write comparison geometries with explicit names
mm.to_obj(dia_comp.geom_a, "output/stent_vis", watertight=False,
contour_types=[mm.PyContourType.Lumen], filename_prefix="prestent")
mm.to_obj(dia_comp.geom_b, "output/stent_vis", watertight=False,
contour_types=[mm.PyContourType.Lumen], filename_prefix="poststent")
mesh_pre = trimesh.load("output/stent_vis/prestent_lumen.obj")
mesh_post = trimesh.load("output/stent_vis/poststent_lumen.obj")
fig = go.Figure(data=[
trimesh_to_mesh3d(mesh_pre, "royalblue", "Pre-stent (diastole)"),
trimesh_to_mesh3d(mesh_post, "firebrick", "Post-stent (diastole)"),
])
fig.update_layout(
title="Pre- vs. Post-stenting: diastolic lumen comparison",
scene=dict(aspectmode="data"),
margin=dict(l=0, r=0, t=40, b=0),
)
fig.show()
eem file not found, skipping: "ivus_prestent/eem_diastolic_contours.csv"
sidebranch file not found, skipping: "ivus_prestent/branch_diastolic_contours.csv"
calcification file not found, skipping: "ivus_prestent/calcium_diastolic_contours.csv"
process_directory: unknown mapping name 'catheter', skipping
sidebranch file not found, skipping: "ivus_prestent/branch_systolic_contours.csv"
process_directory: unknown mapping name 'catheter', skipping
calcification file not found, skipping: "ivus_prestent/calcium_systolic_contours.csv"
eem file not found, skipping: "ivus_prestent/eem_systolic_contours.csv"
sidebranch file not found, skipping: "ivus_poststent/branch_diastolic_contours.csv"
eem file not found, skipping: "ivus_poststent/eem_diastolic_contours.csv"
calcification file not found, skipping: "ivus_poststent/calcium_diastolic_contours.csv"
process_directory: unknown mapping name 'catheter', skipping
eem file not found, skipping: "ivus_poststent/eem_systolic_contours.csv"
sidebranch file not found, skipping: "ivus_poststent/branch_systolic_contours.csv"
calcification file not found, skipping: "ivus_poststent/calcium_systolic_contours.csv"
process_directory: unknown mapping name 'catheter', skipping
✅ Successfully built geometry from path
-----------------------------------------
✅ Lumen
❌ Eem
❌ Calcification
❌ Sidebranch
✅ Catheter
-----------------------------------------
Label: ivus_prestent
Diastole phase: Yes
✅ Successfully built geometry from path
-----------------------------------------
✅ Lumen
❌ Eem
❌ Calcification
❌ Sidebranch
✅ Catheter
-----------------------------------------
Label: ivus_prestent
Diastole phase: No
✅ Successfully built geometry from path
-----------------------------------------
✅ Lumen
❌ Eem
❌ Calcification
❌ Sidebranch
✅ Catheter
-----------------------------------------
Label: ivus_poststent
Diastole phase: Yes
✅ Successfully built geometry from path
-----------------------------------------
✅ Lumen
❌ Eem
❌ Calcification
❌ Sidebranch
✅ Catheter
-----------------------------------------
Label: ivus_poststent
Diastole phase: No
+--------------------------------------------------------------------+
| ✅ Finished aligning 'ivus_prestent' (anomalous: true) |
+---------+------------+---------------+-------+-------+-------------+
| Contour | Matched To | Rotation (°) | Tx | Ty | Centroid |
+---------+------------+---------------+-------+-------+-------------+
| 1 | 0 | -11.70 | 0.08 | 0.17 | (4.32,5.28) |
| 2 | 1 | -2.80 | 0.23 | 0.35 | (4.32,5.28) |
| 3 | 2 | -8.00 | 0.14 | 0.69 | (4.32,5.28) |
| 4 | 3 | 1.40 | 0.09 | 0.86 | (4.32,5.28) |
| 5 | 4 | 39.20 | -0.72 | -0.77 | (4.32,5.28) |
| 6 | 5 | -19.80 | -0.23 | -0.57 | (4.32,5.28) |
| 7 | 6 | 13.90 | -0.38 | -0.86 | (4.32,5.28) |
| 8 | 7 | 3.20 | -0.30 | -0.49 | (4.32,5.28) |
| 9 | 8 | -4.00 | -0.10 | -0.61 | (4.32,5.28) |
| 10 | 9 | 5.30 | -0.20 | -0.51 | (4.32,5.28) |
| 11 | 10 | -3.40 | -0.21 | -0.08 | (4.32,5.28) |
| 12 | 11 | -45.00 | 0.07 | 0.90 | (4.32,5.28) |
| 13 | 12 | 0.00 | -0.11 | 0.78 | (4.32,5.28) |
| 14 | 13 | 36.00 | -0.10 | 0.99 | (4.32,5.28) |
| 15 | 14 | -17.70 | -0.10 | 1.01 | (4.32,5.28) |
| 16 | 15 | -45.00 | -0.29 | 0.49 | (4.32,5.28) |
| 17 | 16 | 8.00 | -0.30 | 0.48 | (4.32,5.28) |
| 18 | 17 | 0.00 | -0.35 | 0.23 | (4.32,5.28) |
| 19 | 18 | -6.80 | -0.36 | 0.22 | (4.32,5.28) |
| 20 | 19 | -9.80 | -0.12 | 0.24 | (4.32,5.28) |
| 21 | 20 | -14.70 | 0.04 | 0.26 | (4.32,5.28) |
| 22 | 21 | -2.80 | 0.07 | 0.37 | (4.32,5.28) |
| 23 | 22 | -45.00 | 0.22 | 1.06 | (4.32,5.28) |
| 24 | 23 | -18.00 | 0.11 | 1.11 | (4.32,5.28) |
| 25 | 24 | 45.00 | 0.09 | 0.78 | (4.32,5.28) |
| 26 | 25 | 45.00 | -0.24 | 0.67 | (4.32,5.28) |
| 27 | 26 | 39.70 | -0.25 | 0.70 | (4.32,5.28) |
| 28 | 27 | 36.00 | -0.09 | 0.97 | (4.32,5.28) |
| 29 | 28 | -36.10 | -0.38 | 1.48 | (4.32,5.28) |
+---------+------------+---------------+-------+-------+-------------+
⚠️ Hole detected! Attempting to fix using Geometry::insert_frame(...) (baseline spacing = 0.559)
✅ Fixed one-frame hole between Frame 24 and Frame 25 (dz = 0.909, ratio = 1.627)
✅ Fixed one-frame hole between Frame 31 and Frame 32 (dz = 0.876, ratio = 1.568)
✅ Fixed one-frame hole between Frame 33 and Frame 34 (dz = 0.842, ratio = 1.508)
⚠️ Hole detected! Attempting to fix using Geometry::insert_frame(...) (baseline spacing = 0.533)
✅ Fixed one-frame hole between Frame 19 and Frame 20 (dz = 1.099, ratio = 2.061)
+-------------------------------------------------------------------+
| ✅ Finished aligning 'ivus_poststent' (anomalous: true) |
+---------+------------+---------------+------+-------+-------------+
| Contour | Matched To | Rotation (°) | Tx | Ty | Centroid |
+---------+------------+---------------+------+-------+-------------+
| 1 | 0 | 12.40 | 0.18 | -0.01 | (4.69,3.78) |
| 2 | 1 | 7.50 | 0.29 | 0.00 | (4.69,3.78) |
| 3 | 2 | -6.70 | 0.20 | 0.31 | (4.69,3.78) |
| 4 | 3 | 3.30 | 0.33 | 0.39 | (4.69,3.78) |
| 5 | 4 | 0.00 | 0.25 | 0.06 | (4.69,3.78) |
| 6 | 5 | 2.20 | 0.34 | 0.32 | (4.69,3.78) |
| 7 | 6 | 8.70 | 0.50 | 0.31 | (4.69,3.78) |
| 8 | 7 | -7.90 | 0.46 | 0.32 | (4.69,3.78) |
| 9 | 8 | 7.30 | 0.54 | 0.22 | (4.69,3.78) |
| 10 | 9 | 7.90 | 0.65 | 0.09 | (4.69,3.78) |
| 11 | 10 | 0.00 | 0.62 | 0.00 | (4.69,3.78) |
| 12 | 11 | 5.30 | 0.66 | -0.11 | (4.69,3.78) |
| 13 | 12 | -11.40 | 0.60 | -0.18 | (4.69,3.78) |
| 14 | 13 | -2.90 | 0.56 | -0.21 | (4.69,3.78) |
| 15 | 14 | 0.00 | 0.53 | -0.35 | (4.69,3.78) |
| 16 | 15 | 18.00 | 0.55 | -0.46 | (4.69,3.78) |
| 17 | 16 | 12.40 | 0.59 | -0.58 | (4.69,3.78) |
| 18 | 17 | 5.80 | 0.61 | -0.61 | (4.69,3.78) |
| 19 | 18 | 13.20 | 0.63 | -0.72 | (4.69,3.78) |
| 20 | 19 | 0.70 | 0.61 | -0.73 | (4.69,3.78) |
| 21 | 20 | 0.00 | 0.57 | -0.75 | (4.69,3.78) |
| 22 | 21 | -18.00 | 0.89 | -0.63 | (4.69,3.78) |
| 23 | 22 | -0.70 | 0.85 | -0.69 | (4.69,3.78) |
| 24 | 23 | -15.90 | 0.83 | -0.50 | (4.69,3.78) |
| 25 | 24 | 3.60 | 0.83 | -0.49 | (4.69,3.78) |
| 26 | 25 | 15.70 | 0.90 | -0.63 | (4.69,3.78) |
| 27 | 26 | 16.20 | 0.85 | -0.78 | (4.69,3.78) |
| 28 | 27 | -5.40 | 0.91 | -0.70 | (4.69,3.78) |
| 29 | 28 | 6.40 | 0.89 | -0.73 | (4.69,3.78) |
| 30 | 29 | 24.20 | 0.78 | -0.99 | (4.69,3.78) |
| 31 | 30 | -1.30 | 0.72 | -0.91 | (4.69,3.78) |
| 32 | 31 | 19.40 | 0.65 | -0.94 | (4.69,3.78) |
| 33 | 32 | 0.00 | 0.86 | -1.01 | (4.69,3.78) |
| 34 | 33 | 9.90 | 0.74 | -1.06 | (4.69,3.78) |
+---------+------------+---------------+------+-------+-------------+
+--------------------------------------------------------------------+
| ✅ Finished aligning 'ivus_prestent' (anomalous: true) |
+---------+------------+---------------+-------+-------+-------------+
| Contour | Matched To | Rotation (°) | Tx | Ty | Centroid |
+---------+------------+---------------+-------+-------+-------------+
| 1 | 0 | 0.10 | 0.01 | 0.12 | (4.20,4.59) |
| 2 | 1 | -8.10 | -0.03 | 0.07 | (4.20,4.59) |
| 3 | 2 | 9.20 | -0.05 | -0.14 | (4.20,4.59) |
| 4 | 3 | -10.90 | 0.27 | -0.75 | (4.20,4.59) |
| 5 | 4 | 9.90 | 0.11 | -1.14 | (4.20,4.59) |
| 6 | 5 | -1.10 | 0.03 | -1.17 | (4.20,4.59) |
| 7 | 6 | 8.00 | -0.13 | -1.28 | (4.20,4.59) |
| 8 | 7 | -4.10 | -0.08 | -1.25 | (4.20,4.59) |
| 9 | 8 | -0.80 | -0.06 | -1.36 | (4.20,4.59) |
| 10 | 9 | -0.70 | 0.02 | -1.22 | (4.20,4.59) |
| 11 | 10 | 6.00 | -0.26 | -1.22 | (4.20,4.59) |
| 12 | 11 | -0.30 | -0.35 | -1.21 | (4.20,4.59) |
| 13 | 12 | -4.70 | -0.13 | -0.88 | (4.20,4.59) |
| 14 | 13 | -4.30 | -0.08 | -0.87 | (4.20,4.59) |
| 15 | 14 | 4.20 | -0.14 | -0.77 | (4.20,4.59) |
| 16 | 15 | -3.60 | -0.13 | -0.74 | (4.20,4.59) |
| 17 | 16 | 0.00 | -0.17 | -0.52 | (4.20,4.59) |
| 18 | 17 | -45.00 | 0.06 | 0.32 | (4.20,4.59) |
| 19 | 18 | -3.50 | 0.04 | 0.28 | (4.20,4.59) |
| 20 | 19 | 45.00 | -0.42 | -0.17 | (4.20,4.59) |
| 21 | 20 | -11.60 | -0.29 | -0.06 | (4.20,4.59) |
| 22 | 21 | 45.00 | -0.72 | -0.15 | (4.20,4.59) |
+---------+------------+---------------+-------+-------+-------------+
⚠️ Hole detected! Attempting to fix using Geometry::insert_frame(...) (baseline spacing = 0.569)
✅ Fixed one-frame hole between Frame 30 and Frame 31 (dz = 0.876, ratio = 1.539)
+-------------------------------------------------------------------+
| ✅ Finished aligning 'ivus_poststent' (anomalous: true) |
+---------+------------+---------------+------+-------+-------------+
| Contour | Matched To | Rotation (°) | Tx | Ty | Centroid |
+---------+------------+---------------+------+-------+-------------+
| 1 | 0 | 3.90 | 0.16 | 0.16 | (4.84,3.84) |
| 2 | 1 | -10.70 | 0.08 | 0.12 | (4.84,3.84) |
| 3 | 2 | 17.50 | 0.40 | 0.34 | (4.84,3.84) |
| 4 | 3 | -12.70 | 0.19 | 0.38 | (4.84,3.84) |
| 5 | 4 | 3.90 | 0.34 | 0.38 | (4.84,3.84) |
| 6 | 5 | 6.00 | 0.47 | 0.39 | (4.84,3.84) |
| 7 | 6 | 13.90 | 0.65 | 0.30 | (4.84,3.84) |
| 8 | 7 | -18.00 | 0.37 | 0.43 | (4.84,3.84) |
| 9 | 8 | 15.80 | 0.57 | 0.20 | (4.84,3.84) |
| 10 | 9 | 3.00 | 0.57 | 0.18 | (4.84,3.84) |
| 11 | 10 | 0.30 | 0.68 | 0.18 | (4.84,3.84) |
| 12 | 11 | 8.80 | 0.72 | 0.02 | (4.84,3.84) |
| 13 | 12 | 7.10 | 0.81 | -0.06 | (4.84,3.84) |
| 14 | 13 | 0.00 | 0.76 | -0.14 | (4.84,3.84) |
| 15 | 14 | 5.40 | 0.78 | -0.23 | (4.84,3.84) |
| 16 | 15 | -0.80 | 0.85 | -0.25 | (4.84,3.84) |
| 17 | 16 | 10.50 | 0.88 | -0.35 | (4.84,3.84) |
| 18 | 17 | 13.70 | 0.94 | -0.43 | (4.84,3.84) |
| 19 | 18 | 0.20 | 0.92 | -0.50 | (4.84,3.84) |
| 20 | 19 | 23.30 | 0.92 | -0.72 | (4.84,3.84) |
| 21 | 20 | -12.40 | 0.86 | -0.68 | (4.84,3.84) |
| 22 | 21 | 32.40 | 0.82 | -0.85 | (4.84,3.84) |
| 23 | 22 | 18.10 | 0.78 | -0.89 | (4.84,3.84) |
| 24 | 23 | -18.00 | 0.74 | -0.75 | (4.84,3.84) |
| 25 | 24 | 18.00 | 0.79 | -0.94 | (4.84,3.84) |
| 26 | 25 | 11.30 | 0.70 | -0.95 | (4.84,3.84) |
| 27 | 26 | 0.00 | 0.77 | -0.91 | (4.84,3.84) |
| 28 | 27 | 0.00 | 0.70 | -0.88 | (4.84,3.84) |
| 29 | 28 | 12.70 | 0.74 | -0.64 | (4.84,3.84) |
| 30 | 29 | 0.00 | 1.10 | -0.63 | (4.84,3.84) |
| 31 | 30 | 11.40 | 0.82 | -0.72 | (4.84,3.84) |
| 32 | 31 | -9.60 | 1.10 | -0.64 | (4.84,3.84) |
| 33 | 32 | 4.70 | 1.09 | -0.64 | (4.84,3.84) |
+---------+------------+---------------+------+-------+-------------+
✅ Aligned geometry 'ivus_poststent' to 'ivus_poststent'
-----------------------------------------
Applied initial translation: (-0.16, -0.05, 0.00) mm
Found best rotation of -34.90° with parameters:
range: 45.00°
step size: 0.1°
Applied final translation: ( 0, 0.00, 0.00) mm
-----------------------------------------
✅ Aligned geometry 'ivus_prestent' to 'ivus_prestent'
-----------------------------------------
Applied initial translation: (0.12, 0.70, 0.00) mm
Found best rotation of -0.50° with parameters:
range: 45.00°
step size: 0.1°
Applied final translation: ( 0, 0.00, 0.00) mm
-----------------------------------------
✅ Aligned geometry 'ivus_poststent' to 'ivus_prestent'
-----------------------------------------
Applied initial translation: (-0.37, 1.50, 0.00) mm
Found best rotation of 30.20° with parameters:
range: 45.00°
step size: 0.1°
Applied final translation: ( 0, 0.00, 0.00) mm
-----------------------------------------
✅ Aligned geometry 'ivus_poststent' to 'ivus_prestent'
-----------------------------------------
Applied initial translation: (-0.37, 1.50, 0.00) mm
Found best rotation of -27.40° with parameters:
range: 45.00°
step size: 0.1°
Applied final translation: ( 0, 0.00, 0.00) mm
-----------------------------------------
Saving files for 'ivus_prestent - ivus_prestent' to 'output/stent_rest'
LUMEN .obj files: 2/2 written successfully
CATHETER .obj files: 2/2 written successfully
WALL .obj files: 2/2 written successfully
Saving files for 'ivus_poststent - ivus_poststent' to 'output/stent_stress'
LUMEN .obj files: 2/2 written successfully
CATHETER .obj files: 2/2 written successfully
WALL .obj files: 2/2 written successfully
Saving files for 'ivus_prestent - ivus_poststent' to 'output/stent_diastole'
LUMEN .obj files: 2/2 written successfully
CATHETER .obj files: 2/2 written successfully
WALL .obj files: 2/2 written successfully
Saving files for 'ivus_prestent - ivus_poststent' to 'output/stent_systole'
LUMEN .obj files: 2/2 written successfully
CATHETER .obj files: 2/2 written successfully
WALL .obj files: 2/2 written successfully
Successfully wrote lumen to output/stent_vis/prestent_lumen.obj
Successfully wrote lumen to output/stent_vis/poststent_lumen.obj
3. Workflow from numpy arrays
The numpy workflow accepts data from any source: you build PyInputData objects manually and
then call the same alignment functions. Use this workflow when your segmentation software uses
different file formats, when you need pre-processing steps, or when embedding multimodars into a
larger pipeline.
For stent comparison, both pre- and post-stent geometries are packed into PyInputData objects:
before_arr = np.genfromtxt("ivus_prestent/diastolic_contours.csv", delimiter='\t')
before_ref = np.genfromtxt("ivus_prestent/diastolic_reference_points.csv", delimiter='\t')
after_arr = np.genfromtxt("ivus_poststent/diastolic_contours.csv", delimiter='\t')
after_ref = np.genfromtxt("ivus_poststent/diastolic_reference_points.csv", delimiter='\t')
before_input = mm.numpy_to_inputdata(
lumen_arr=before_arr, ref_point=before_ref,
record=None, diastole=True, label="prestent",
)
after_input = mm.numpy_to_inputdata(
lumen_arr=after_arr, ref_point=after_ref,
record=None, diastole=True, label="poststent",
)
pair, _ = mm.from_array_singlepair(
input_data_a=before_input,
input_data_b=after_input,
step_rotation_deg=0.01,
range_rotation_deg=30,
output_path="output/stent_comparison",
)
✅ Successfully built geometry from input data
-----------------------------------------
✅ Lumen
❌ Eem
❌ Calcification
❌ Sidebranch
✅ Catheter
-----------------------------------------
Label: prestent
Diastole phase: Yes
✅ Successfully built geometry from input data
-----------------------------------------
✅ Lumen
❌ Eem
❌ Calcification
❌ Sidebranch
✅ Catheter
-----------------------------------------
Label: poststent
Diastole phase: Yes
+--------------------------------------------------------------------+
| ✅ Finished aligning 'prestent' (anomalous: true) |
+---------+------------+---------------+-------+-------+-------------+
| Contour | Matched To | Rotation (°) | Tx | Ty | Centroid |
+---------+------------+---------------+-------+-------+-------------+
| 1 | 0 | -19.43 | 0.23 | 0.35 | (4.32,5.28) |
| 2 | 1 | 2.77 | 0.08 | 0.17 | (4.32,5.28) |
| 3 | 2 | -8.61 | 0.14 | 0.69 | (4.32,5.28) |
| 4 | 3 | 1.44 | 0.09 | 0.86 | (4.32,5.28) |
| 5 | 4 | -10.88 | -0.10 | -0.61 | (4.32,5.28) |
| 6 | 5 | 5.32 | -0.20 | -0.51 | (4.32,5.28) |
| 7 | 6 | -0.54 | -0.30 | -0.49 | (4.32,5.28) |
| 8 | 7 | -3.14 | -0.38 | -0.86 | (4.32,5.28) |
| 9 | 8 | 7.40 | -0.72 | -0.77 | (4.32,5.28) |
| 10 | 9 | -19.75 | -0.23 | -0.57 | (4.32,5.28) |
| 11 | 10 | 0.00 | -0.21 | -0.08 | (4.32,5.28) |
| 12 | 11 | 16.53 | -0.35 | 0.23 | (4.32,5.28) |
| 13 | 12 | -6.85 | -0.36 | 0.22 | (4.32,5.28) |
| 14 | 13 | 0.00 | -0.30 | 0.48 | (4.32,5.28) |
| 15 | 14 | -8.01 | -0.29 | 0.49 | (4.32,5.28) |
| 16 | 15 | -17.73 | -0.12 | 0.24 | (4.32,5.28) |
| 17 | 16 | -14.66 | 0.04 | 0.26 | (4.32,5.28) |
| 18 | 17 | -30.00 | 0.07 | 0.90 | (4.32,5.28) |
| 19 | 18 | 16.52 | -0.09 | 0.97 | (4.32,5.28) |
| 20 | 19 | -14.64 | -0.10 | 1.01 | (4.32,5.28) |
| 21 | 20 | 17.73 | -0.10 | 0.99 | (4.32,5.28) |
| 22 | 21 | -26.19 | -0.11 | 0.78 | (4.32,5.28) |
| 23 | 22 | 30.00 | 0.07 | 0.37 | (4.32,5.28) |
| 24 | 23 | -6.49 | 0.09 | 0.78 | (4.32,5.28) |
| 25 | 24 | 29.51 | -0.25 | 0.70 | (4.32,5.28) |
| 26 | 25 | -29.88 | -0.24 | 0.67 | (4.32,5.28) |
| 27 | 26 | 30.00 | -0.38 | 1.48 | (4.32,5.28) |
| 28 | 27 | 30.00 | 0.11 | 1.11 | (4.32,5.28) |
| 29 | 28 | 18.00 | 0.22 | 1.06 | (4.32,5.28) |
+---------+------------+---------------+-------+-------+-------------+
⚠️ Hole detected! Attempting to fix using Geometry::insert_frame(...) (baseline spacing = 0.559)
✅ Fixed one-frame hole between Frame 24 and Frame 25 (dz = 0.909, ratio = 1.627)
✅ Fixed one-frame hole between Frame 31 and Frame 32 (dz = 0.876, ratio = 1.568)
✅ Fixed one-frame hole between Frame 33 and Frame 34 (dz = 0.842, ratio = 1.508)
+-------------------------------------------------------------------+
| ✅ Finished aligning 'poststent' (anomalous: false) |
+---------+------------+---------------+------+-------+-------------+
| Contour | Matched To | Rotation (°) | Tx | Ty | Centroid |
+---------+------------+---------------+------+-------+-------------+
| 1 | 0 | 12.38 | 0.18 | -0.01 | (4.69,3.78) |
| 2 | 1 | 7.45 | 0.29 | 0.00 | (4.69,3.78) |
| 3 | 2 | -12.68 | 0.25 | 0.06 | (4.69,3.78) |
| 4 | 3 | -1.86 | 0.20 | 0.31 | (4.69,3.78) |
| 5 | 4 | 3.30 | 0.33 | 0.39 | (4.69,3.78) |
| 6 | 5 | 6.94 | 0.34 | 0.32 | (4.69,3.78) |
| 7 | 6 | 8.74 | 0.50 | 0.31 | (4.69,3.78) |
| 8 | 7 | -7.95 | 0.46 | 0.32 | (4.69,3.78) |
| 9 | 8 | 7.24 | 0.54 | 0.22 | (4.69,3.78) |
| 10 | 9 | 7.93 | 0.65 | 0.09 | (4.69,3.78) |
| 11 | 10 | 0.00 | 0.62 | 0.00 | (4.69,3.78) |
| 12 | 11 | 5.25 | 0.66 | -0.11 | (4.69,3.78) |
| 13 | 12 | -11.45 | 0.60 | -0.18 | (4.69,3.78) |
| 14 | 13 | -2.92 | 0.56 | -0.21 | (4.69,3.78) |
| 15 | 14 | 0.00 | 0.53 | -0.35 | (4.69,3.78) |
| 16 | 15 | 18.00 | 0.55 | -0.46 | (4.69,3.78) |
| 17 | 16 | 12.37 | 0.59 | -0.58 | (4.69,3.78) |
| 18 | 17 | 5.81 | 0.61 | -0.61 | (4.69,3.78) |
| 19 | 18 | 13.24 | 0.63 | -0.72 | (4.69,3.78) |
| 20 | 19 | 0.67 | 0.61 | -0.73 | (4.69,3.78) |
| 21 | 20 | -18.00 | 0.83 | -0.50 | (4.69,3.78) |
| 22 | 21 | 18.00 | 0.57 | -0.75 | (4.69,3.78) |
| 23 | 22 | -23.65 | 0.83 | -0.49 | (4.69,3.78) |
| 24 | 23 | 15.70 | 0.90 | -0.63 | (4.69,3.78) |
| 25 | 24 | -3.16 | 0.89 | -0.63 | (4.69,3.78) |
| 26 | 25 | -0.74 | 0.85 | -0.69 | (4.69,3.78) |
| 27 | 26 | 0.00 | 0.89 | -0.73 | (4.69,3.78) |
| 28 | 27 | -6.41 | 0.91 | -0.70 | (4.69,3.78) |
| 29 | 28 | 5.45 | 0.85 | -0.78 | (4.69,3.78) |
| 30 | 29 | -0.54 | 0.72 | -0.91 | (4.69,3.78) |
| 31 | 30 | 19.47 | 0.65 | -0.94 | (4.69,3.78) |
| 32 | 31 | 6.12 | 0.74 | -1.06 | (4.69,3.78) |
| 33 | 32 | -9.93 | 0.86 | -1.01 | (4.69,3.78) |
| 34 | 33 | -1.94 | 0.78 | -0.99 | (4.69,3.78) |
+---------+------------+---------------+------+-------+-------------+
✅ Aligned geometry 'poststent' to 'prestent'
-----------------------------------------
Applied initial translation: (-0.37, 1.50, 0.00) mm
Found best rotation of 18.55° with parameters:
range: 30.00°
step size: 0.01°
Applied final translation: ( 0, 0.00, 0.00) mm
-----------------------------------------
Saving files for 'prestent - poststent' to 'output/stent_comparison'
LUMEN .obj files: 2/2 written successfully
CATHETER .obj files: 2/2 written successfully
WALL .obj files: 2/2 written successfully
A single 3D geometry can also be reconstructed from OCT. from_array_single returns a
PyGeometry from a single-state contour array. The replace_frame method demonstrates
in-place editing before writing:
oct_raw = np.genfromtxt("oct_single/oct_contours_raw.csv", delimiter=',')
oct_ref = np.genfromtxt("oct_single/oct_ref.csv", delimiter=',')
oct_input = mm.numpy_to_inputdata(
lumen_arr=oct_raw, ref_point=oct_ref,
record=None, diastole=True, label="oct",
)
oct_recon, _ = mm.from_array_single(
input_data=oct_input,
step_rotation_deg=0.01,
range_rotation_deg=6,
image_center=(5.0, 5.0),
radius=0.5,
n_points=40,
write_obj=False,
watertight=False,
output_path="output/oct",
smooth=False,
)
# Replace an outlier frame and write both versions for comparison
frame = oct_recon.get_frame_at_z(34.8)
oct_replaced = oct_recon.replace_frame(frame.id + 1, frame)
mm.to_obj(oct_recon, "output/oct", watertight=False,
contour_types=[mm.PyContourType.Lumen], filename_prefix="oct")
mm.to_obj(oct_replaced, "output/oct", watertight=False,
contour_types=[mm.PyContourType.Lumen], filename_prefix="oct_replaced")
oct_mesh = trimesh.load("output/oct/oct_lumen.obj")
oct_mesh_fixed = trimesh.load("output/oct/oct_replaced_lumen.obj")
fig = make_subplots(
rows=1, cols=2,
specs=[[{"type": "scene"}, {"type": "scene"}]],
subplot_titles=["OCT original", "OCT with replaced frame"],
)
scene_cfg = dict(aspectmode="data", camera=dict(eye=dict(x=1.5, y=1.5, z=1.0)))
for t in [trimesh_to_mesh3d(oct_mesh, "royalblue", "OCT Lumen")]:
fig.add_trace(t, row=1, col=1)
for t in [trimesh_to_mesh3d(oct_mesh_fixed, "tomato", "OCT Replaced")]:
fig.add_trace(t, row=1, col=2)
fig.update_layout(
width=1200, height=600,
scene=scene_cfg, scene2=scene_cfg,
margin=dict(l=0, r=0, t=40, b=0),
)
fig.show()
✅ Successfully built geometry from input data
-----------------------------------------
✅ Lumen
❌ Eem
❌ Calcification
❌ Sidebranch
✅ Catheter
-----------------------------------------
Label: oct
Diastole phase: Yes
+--------------------------------------------------------------------+
| ✅ Finished aligning 'oct' (anomalous: false) |
+---------+------------+---------------+-------+-------+-------------+
| Contour | Matched To | Rotation (°) | Tx | Ty | Centroid |
+---------+------------+---------------+-------+-------+-------------+
| 1 | 0 | -1.75 | 0.00 | 0.05 | (6.30,5.97) |
| 2 | 1 | 1.40 | 0.02 | 0.12 | (6.30,5.97) |
| 3 | 2 | 0.47 | -0.03 | 0.17 | (6.30,5.97) |
| 4 | 3 | 3.10 | -0.04 | 0.25 | (6.30,5.97) |
| 5 | 4 | -1.86 | -0.06 | 0.29 | (6.30,5.97) |
| 6 | 5 | 4.01 | -0.10 | 0.42 | (6.30,5.97) |
| 7 | 6 | 5.88 | -0.13 | 0.59 | (6.30,5.97) |
| 8 | 7 | 5.99 | -0.14 | 0.78 | (6.30,5.97) |
| 9 | 8 | 0.71 | -0.13 | 0.90 | (6.30,5.97) |
| 10 | 9 | 2.19 | -0.10 | 1.04 | (6.30,5.97) |
| 11 | 10 | 2.24 | -0.09 | 1.14 | (6.30,5.97) |
| 12 | 11 | 2.62 | -0.05 | 1.27 | (6.30,5.97) |
| 13 | 12 | 0.92 | 0.00 | 1.37 | (6.30,5.97) |
| 14 | 13 | 5.28 | 0.05 | 1.50 | (6.30,5.97) |
| 15 | 14 | 1.82 | 0.09 | 1.59 | (6.30,5.97) |
| 16 | 15 | 2.20 | 0.15 | 1.68 | (6.30,5.97) |
| 17 | 16 | 0.01 | 0.22 | 1.78 | (6.30,5.97) |
| 18 | 17 | -0.59 | 0.29 | 1.83 | (6.30,5.97) |
| 19 | 18 | 4.33 | 0.36 | 1.91 | (6.30,5.97) |
| 20 | 19 | 2.80 | 0.42 | 1.95 | (6.30,5.97) |
| 21 | 20 | -0.80 | 0.51 | 1.97 | (6.30,5.97) |
| 22 | 21 | -6.00 | 0.67 | 1.95 | (6.30,5.97) |
| 23 | 22 | 2.37 | 0.76 | 1.90 | (6.30,5.97) |
| 24 | 23 | 0.83 | 0.82 | 1.88 | (6.30,5.97) |
| 25 | 24 | -0.62 | 0.78 | 1.86 | (6.30,5.97) |
| 26 | 25 | -0.87 | 0.82 | 1.84 | (6.30,5.97) |
| 27 | 26 | 3.52 | 0.82 | 1.84 | (6.30,5.97) |
| 28 | 27 | 4.63 | 0.86 | 1.87 | (6.30,5.97) |
| 29 | 28 | 5.46 | 0.88 | 1.92 | (6.30,5.97) |
| 30 | 29 | 1.04 | 0.90 | 1.95 | (6.30,5.97) |
| 31 | 30 | 1.64 | 0.91 | 2.00 | (6.30,5.97) |
| 32 | 31 | 0.20 | 0.90 | 2.06 | (6.30,5.97) |
| 33 | 32 | 0.88 | 0.93 | 2.13 | (6.30,5.97) |
| 34 | 33 | 1.35 | 0.96 | 2.22 | (6.30,5.97) |
| 35 | 34 | 3.43 | 0.99 | 2.28 | (6.30,5.97) |
| 36 | 35 | 3.22 | 1.04 | 2.32 | (6.30,5.97) |
| 37 | 36 | 5.27 | 1.14 | 2.39 | (6.30,5.97) |
| 38 | 37 | 5.99 | 1.25 | 2.48 | (6.30,5.97) |
| 39 | 38 | 5.99 | 1.43 | 2.53 | (6.30,5.97) |
| 40 | 39 | 4.19 | 1.56 | 2.58 | (6.30,5.97) |
| 41 | 40 | 4.06 | 1.69 | 2.59 | (6.30,5.97) |
| 42 | 41 | 3.93 | 1.79 | 2.60 | (6.30,5.97) |
| 43 | 42 | 0.38 | 1.84 | 2.61 | (6.30,5.97) |
| 44 | 43 | 3.33 | 1.92 | 2.61 | (6.30,5.97) |
| 45 | 44 | 2.85 | 1.97 | 2.61 | (6.30,5.97) |
| 46 | 45 | -0.62 | 2.00 | 2.59 | (6.30,5.97) |
| 47 | 46 | 0.62 | 2.02 | 2.57 | (6.30,5.97) |
| 48 | 47 | 2.22 | 2.05 | 2.52 | (6.30,5.97) |
| 49 | 48 | 0.99 | 2.04 | 2.53 | (6.30,5.97) |
| 50 | 49 | 5.96 | 2.09 | 2.51 | (6.30,5.97) |
| 51 | 50 | -4.71 | 2.08 | 2.54 | (6.30,5.97) |
| 52 | 51 | 1.17 | 2.09 | 2.52 | (6.30,5.97) |
| 53 | 52 | 0.70 | 2.14 | 2.51 | (6.30,5.97) |
| 54 | 53 | -0.97 | 2.19 | 2.45 | (6.30,5.97) |
| 55 | 54 | 2.69 | 2.23 | 2.40 | (6.30,5.97) |
| 56 | 55 | -4.29 | 2.19 | 2.38 | (6.30,5.97) |
| 57 | 56 | 2.00 | 2.22 | 2.29 | (6.30,5.97) |
| 58 | 57 | 1.90 | 2.25 | 2.22 | (6.30,5.97) |
| 59 | 58 | -1.62 | 2.28 | 2.14 | (6.30,5.97) |
| 60 | 59 | 1.61 | 2.26 | 2.08 | (6.30,5.97) |
| 61 | 60 | 1.23 | 2.23 | 2.03 | (6.30,5.97) |
| 62 | 61 | 1.38 | 2.18 | 1.99 | (6.30,5.97) |
| 63 | 62 | 2.23 | 2.16 | 1.96 | (6.30,5.97) |
| 64 | 63 | 1.43 | 2.16 | 1.91 | (6.30,5.97) |
| 65 | 64 | -0.70 | 2.14 | 1.88 | (6.30,5.97) |
| 66 | 65 | 2.59 | 2.14 | 1.85 | (6.30,5.97) |
| 67 | 66 | 0.93 | 2.13 | 1.83 | (6.30,5.97) |
| 68 | 67 | 1.79 | 2.13 | 1.81 | (6.30,5.97) |
| 69 | 68 | 1.58 | 2.12 | 1.80 | (6.30,5.97) |
| 70 | 69 | 0.97 | 2.10 | 1.82 | (6.30,5.97) |
| 71 | 70 | 1.95 | 2.07 | 1.81 | (6.30,5.97) |
| 72 | 71 | 0.56 | 2.04 | 1.80 | (6.30,5.97) |
| 73 | 72 | 1.54 | 2.02 | 1.78 | (6.30,5.97) |
| 74 | 73 | -0.07 | 2.00 | 1.77 | (6.30,5.97) |
| 75 | 74 | 1.18 | 1.97 | 1.78 | (6.30,5.97) |
| 76 | 75 | 0.83 | 1.96 | 1.75 | (6.30,5.97) |
| 77 | 76 | 1.11 | 1.94 | 1.71 | (6.30,5.97) |
| 78 | 77 | 1.73 | 1.95 | 1.69 | (6.30,5.97) |
| 79 | 78 | 0.16 | 1.94 | 1.66 | (6.30,5.97) |
| 80 | 79 | 2.62 | 1.94 | 1.64 | (6.30,5.97) |
| 81 | 80 | 2.81 | 1.95 | 1.63 | (6.30,5.97) |
| 82 | 81 | -1.60 | 1.97 | 1.62 | (6.30,5.97) |
| 83 | 82 | 4.01 | 1.95 | 1.63 | (6.30,5.97) |
| 84 | 83 | 5.99 | 1.96 | 1.69 | (6.30,5.97) |
| 85 | 84 | 5.99 | 2.03 | 1.60 | (6.30,5.97) |
| 86 | 85 | 4.49 | 2.06 | 1.53 | (6.30,5.97) |
| 87 | 86 | -0.62 | 2.09 | 1.45 | (6.30,5.97) |
| 88 | 87 | 1.30 | 2.12 | 1.41 | (6.30,5.97) |
| 89 | 88 | -0.70 | 2.15 | 1.37 | (6.30,5.97) |
| 90 | 89 | 2.85 | 2.19 | 1.33 | (6.30,5.97) |
| 91 | 90 | 0.63 | 2.23 | 1.28 | (6.30,5.97) |
| 92 | 91 | -0.24 | 2.27 | 1.25 | (6.30,5.97) |
| 93 | 92 | -0.26 | 2.31 | 1.23 | (6.30,5.97) |
| 94 | 93 | 3.01 | 2.35 | 1.15 | (6.30,5.97) |
| 95 | 94 | 2.36 | 2.37 | 1.08 | (6.30,5.97) |
| 96 | 95 | 3.39 | 2.36 | 1.05 | (6.30,5.97) |
| 97 | 96 | 3.71 | 2.37 | 1.04 | (6.30,5.97) |
| 98 | 97 | 5.99 | 2.38 | 1.00 | (6.30,5.97) |
| 99 | 98 | 5.99 | 2.43 | 0.99 | (6.30,5.97) |
| 100 | 99 | 5.69 | 2.44 | 0.99 | (6.30,5.97) |
| 101 | 100 | 0.35 | 2.46 | 0.90 | (6.30,5.97) |
| 102 | 101 | 0.00 | 2.46 | 0.79 | (6.30,5.97) |
| 103 | 102 | 2.10 | 2.45 | 0.66 | (6.30,5.97) |
| 104 | 103 | 3.25 | 2.43 | 0.57 | (6.30,5.97) |
| 105 | 104 | 3.92 | 2.42 | 0.47 | (6.30,5.97) |
| 106 | 105 | 0.01 | 2.41 | 0.41 | (6.30,5.97) |
| 107 | 106 | 3.23 | 2.42 | 0.33 | (6.30,5.97) |
| 108 | 107 | 0.98 | 2.41 | 0.28 | (6.30,5.97) |
| 109 | 108 | 1.37 | 2.40 | 0.25 | (6.30,5.97) |
| 110 | 109 | -0.05 | 2.41 | 0.22 | (6.30,5.97) |
| 111 | 110 | 1.56 | 2.38 | 0.20 | (6.30,5.97) |
| 112 | 111 | 2.63 | 2.35 | 0.16 | (6.30,5.97) |
| 113 | 112 | 0.12 | 2.32 | 0.15 | (6.30,5.97) |
| 114 | 113 | -1.41 | 2.32 | 0.14 | (6.30,5.97) |
| 115 | 114 | 1.38 | 2.32 | 0.14 | (6.30,5.97) |
| 116 | 115 | 0.00 | 2.29 | 0.15 | (6.30,5.97) |
| 117 | 116 | 3.14 | 2.27 | 0.13 | (6.30,5.97) |
| 118 | 117 | -0.86 | 2.25 | 0.12 | (6.30,5.97) |
| 119 | 118 | 4.18 | 2.20 | 0.14 | (6.30,5.97) |
| 120 | 119 | -1.10 | 2.19 | 0.15 | (6.30,5.97) |
| 121 | 120 | -0.71 | 2.17 | 0.20 | (6.30,5.97) |
| 122 | 121 | 0.67 | 2.13 | 0.24 | (6.30,5.97) |
| 123 | 122 | 1.49 | 2.12 | 0.27 | (6.30,5.97) |
| 124 | 123 | 1.96 | 2.09 | 0.32 | (6.30,5.97) |
| 125 | 124 | -0.32 | 2.08 | 0.34 | (6.30,5.97) |
| 126 | 125 | 2.68 | 2.04 | 0.36 | (6.30,5.97) |
| 127 | 126 | 0.64 | 2.02 | 0.33 | (6.30,5.97) |
| 128 | 127 | -1.77 | 1.99 | 0.33 | (6.30,5.97) |
| 129 | 128 | -1.93 | 1.98 | 0.31 | (6.30,5.97) |
| 130 | 129 | -1.08 | 1.99 | 0.23 | (6.30,5.97) |
| 131 | 130 | 5.99 | 1.94 | 0.28 | (6.30,5.97) |
| 132 | 131 | -4.71 | 1.95 | 0.28 | (6.30,5.97) |
| 133 | 132 | -4.51 | 1.92 | 0.35 | (6.30,5.97) |
| 134 | 133 | 1.89 | 1.86 | 0.42 | (6.30,5.97) |
| 135 | 134 | -2.08 | 1.82 | 0.46 | (6.30,5.97) |
| 136 | 135 | -0.74 | 1.76 | 0.50 | (6.30,5.97) |
| 137 | 136 | -3.69 | 1.70 | 0.57 | (6.30,5.97) |
| 138 | 137 | 2.54 | 1.58 | 0.67 | (6.30,5.97) |
| 139 | 138 | 5.99 | 1.54 | 0.66 | (6.30,5.97) |
| 140 | 139 | 5.99 | 1.41 | 0.72 | (6.30,5.97) |
| 141 | 140 | 0.62 | 1.35 | 0.73 | (6.30,5.97) |
| 142 | 141 | 3.40 | 1.35 | 0.76 | (6.30,5.97) |
| 143 | 142 | 1.15 | 1.35 | 0.80 | (6.30,5.97) |
| 144 | 143 | 0.27 | 1.31 | 0.84 | (6.30,5.97) |
| 145 | 144 | -1.53 | 1.31 | 0.82 | (6.30,5.97) |
| 146 | 145 | -6.00 | 1.32 | 0.82 | (6.30,5.97) |
| 147 | 146 | 0.00 | 1.33 | 0.90 | (6.30,5.97) |
| 148 | 147 | 0.00 | 1.33 | 1.02 | (6.30,5.97) |
| 149 | 148 | 5.99 | 1.38 | 1.05 | (6.30,5.97) |
| 150 | 149 | -5.73 | 1.42 | 0.98 | (6.30,5.97) |
| 151 | 150 | 1.77 | 1.50 | 0.93 | (6.30,5.97) |
| 152 | 151 | -6.00 | 1.64 | 0.81 | (6.30,5.97) |
| 153 | 152 | -5.97 | 1.72 | 0.76 | (6.30,5.97) |
| 154 | 153 | 2.13 | 1.81 | 0.72 | (6.30,5.97) |
| 155 | 154 | 0.97 | 1.88 | 0.70 | (6.30,5.97) |
| 156 | 155 | -3.46 | 1.94 | 0.69 | (6.30,5.97) |
| 157 | 156 | -2.27 | 1.98 | 0.69 | (6.30,5.97) |
| 158 | 157 | 3.29 | 2.00 | 0.68 | (6.30,5.97) |
| 159 | 158 | 0.53 | 2.02 | 0.70 | (6.30,5.97) |
| 160 | 159 | -3.84 | 2.04 | 0.72 | (6.30,5.97) |
| 161 | 160 | 0.88 | 2.05 | 0.75 | (6.30,5.97) |
| 162 | 161 | 1.50 | 2.06 | 0.78 | (6.30,5.97) |
| 163 | 162 | 0.86 | 2.07 | 0.80 | (6.30,5.97) |
| 164 | 163 | -1.45 | 2.09 | 0.83 | (6.30,5.97) |
| 165 | 164 | -5.89 | 2.12 | 0.87 | (6.30,5.97) |
| 166 | 165 | 1.19 | 2.15 | 0.90 | (6.30,5.97) |
| 167 | 166 | 1.00 | 2.15 | 0.90 | (6.30,5.97) |
| 168 | 167 | 1.81 | 2.14 | 0.90 | (6.30,5.97) |
| 169 | 168 | -0.80 | 2.14 | 0.90 | (6.30,5.97) |
| 170 | 169 | 0.67 | 2.15 | 0.89 | (6.30,5.97) |
| 171 | 170 | 0.61 | 2.16 | 0.89 | (6.30,5.97) |
| 172 | 171 | 1.12 | 2.15 | 0.89 | (6.30,5.97) |
| 173 | 172 | -1.80 | 2.13 | 0.91 | (6.30,5.97) |
| 174 | 173 | -2.57 | 2.13 | 0.95 | (6.30,5.97) |
| 175 | 174 | -6.00 | 0.76 | -0.29 | (6.30,5.97) |
| 176 | 175 | 5.74 | 2.09 | 1.06 | (6.30,5.97) |
| 177 | 176 | -1.61 | 2.11 | 1.08 | (6.30,5.97) |
| 178 | 177 | -0.99 | 2.11 | 1.05 | (6.30,5.97) |
| 179 | 178 | 1.95 | 2.12 | 1.02 | (6.30,5.97) |
| 180 | 179 | 1.87 | 2.11 | 0.99 | (6.30,5.97) |
| 181 | 180 | -0.88 | 2.12 | 0.99 | (6.30,5.97) |
| 182 | 181 | 0.48 | 2.13 | 1.01 | (6.30,5.97) |
| 183 | 182 | -1.57 | 2.14 | 1.04 | (6.30,5.97) |
| 184 | 183 | -0.01 | 2.17 | 1.08 | (6.30,5.97) |
| 185 | 184 | -0.61 | 2.19 | 1.11 | (6.30,5.97) |
| 186 | 185 | -1.26 | 2.22 | 1.16 | (6.30,5.97) |
| 187 | 186 | -0.16 | 2.25 | 1.19 | (6.30,5.97) |
| 188 | 187 | -0.22 | 2.28 | 1.23 | (6.30,5.97) |
| 189 | 188 | 0.00 | 2.32 | 1.25 | (6.30,5.97) |
| 190 | 189 | -0.38 | 2.38 | 1.32 | (6.30,5.97) |
| 191 | 190 | 0.00 | 2.44 | 1.35 | (6.30,5.97) |
| 192 | 191 | 0.00 | 2.50 | 1.38 | (6.30,5.97) |
| 193 | 192 | 0.00 | 2.54 | 1.41 | (6.30,5.97) |
| 194 | 193 | -0.44 | 2.59 | 1.47 | (6.30,5.97) |
| 195 | 194 | -0.67 | 2.63 | 1.53 | (6.30,5.97) |
| 196 | 195 | 0.69 | 2.67 | 1.60 | (6.30,5.97) |
| 197 | 196 | -0.31 | 2.72 | 1.66 | (6.30,5.97) |
| 198 | 197 | -0.27 | 2.76 | 1.72 | (6.30,5.97) |
| 199 | 198 | -0.46 | 2.79 | 1.77 | (6.30,5.97) |
| 200 | 199 | -1.27 | 2.82 | 1.84 | (6.30,5.97) |
| 201 | 200 | 0.03 | 2.84 | 1.89 | (6.30,5.97) |
| 202 | 201 | 0.24 | 2.86 | 1.92 | (6.30,5.97) |
| 203 | 202 | 0.47 | 2.87 | 1.94 | (6.30,5.97) |
| 204 | 203 | 0.76 | 2.88 | 1.95 | (6.30,5.97) |
| 205 | 204 | -2.53 | 2.85 | 1.96 | (6.30,5.97) |
| 206 | 205 | -3.82 | 2.78 | 1.92 | (6.30,5.97) |
| 207 | 206 | -2.64 | 2.66 | 1.87 | (6.30,5.97) |
| 208 | 207 | 1.72 | 2.54 | 1.79 | (6.30,5.97) |
| 209 | 208 | 5.02 | 2.37 | 1.71 | (6.30,5.97) |
| 210 | 209 | -0.41 | 2.27 | 1.68 | (6.30,5.97) |
| 211 | 210 | 1.44 | 2.15 | 1.67 | (6.30,5.97) |
| 212 | 211 | -5.68 | 1.99 | 1.62 | (6.30,5.97) |
| 213 | 212 | 0.00 | 1.91 | 1.59 | (6.30,5.97) |
| 214 | 213 | -2.03 | 1.84 | 1.60 | (6.30,5.97) |
| 215 | 214 | -5.80 | 1.72 | 1.61 | (6.30,5.97) |
| 216 | 215 | -2.52 | 1.64 | 1.61 | (6.30,5.97) |
| 217 | 216 | -3.02 | 1.56 | 1.60 | (6.30,5.97) |
| 218 | 217 | -0.79 | 1.52 | 1.61 | (6.30,5.97) |
| 219 | 218 | -0.27 | 1.49 | 1.60 | (6.30,5.97) |
| 220 | 219 | -2.81 | 1.47 | 1.60 | (6.30,5.97) |
| 221 | 220 | -1.28 | 1.44 | 1.61 | (6.30,5.97) |
| 222 | 221 | -2.63 | 1.41 | 1.63 | (6.30,5.97) |
| 223 | 222 | -1.33 | 1.39 | 1.64 | (6.30,5.97) |
| 224 | 223 | -1.16 | 1.37 | 1.64 | (6.30,5.97) |
| 225 | 224 | -2.10 | 1.34 | 1.65 | (6.30,5.97) |
| 226 | 225 | -2.43 | 1.31 | 1.65 | (6.30,5.97) |
| 227 | 226 | -1.13 | 1.28 | 1.66 | (6.30,5.97) |
| 228 | 227 | -1.61 | 1.26 | 1.67 | (6.30,5.97) |
| 229 | 228 | 0.23 | 1.24 | 1.67 | (6.30,5.97) |
| 230 | 229 | -1.46 | 1.21 | 1.68 | (6.30,5.97) |
| 231 | 230 | -1.54 | 1.19 | 1.69 | (6.30,5.97) |
| 232 | 231 | 0.00 | 1.17 | 1.71 | (6.30,5.97) |
| 233 | 232 | -1.35 | 1.15 | 1.72 | (6.30,5.97) |
| 234 | 233 | 1.73 | 1.15 | 1.73 | (6.30,5.97) |
| 235 | 234 | -0.12 | 1.14 | 1.73 | (6.30,5.97) |
| 236 | 235 | -1.14 | 1.12 | 1.73 | (6.30,5.97) |
| 237 | 236 | -0.49 | 1.09 | 1.75 | (6.30,5.97) |
| 238 | 237 | -2.95 | 1.07 | 1.76 | (6.30,5.97) |
| 239 | 238 | -0.12 | 1.06 | 1.76 | (6.30,5.97) |
| 240 | 239 | -1.87 | 1.03 | 1.74 | (6.30,5.97) |
| 241 | 240 | -0.03 | 1.00 | 1.73 | (6.30,5.97) |
| 242 | 241 | -1.49 | 0.99 | 1.73 | (6.30,5.97) |
| 243 | 242 | 1.90 | 1.00 | 1.70 | (6.30,5.97) |
| 244 | 243 | 0.24 | 0.96 | 1.70 | (6.30,5.97) |
| 245 | 244 | 0.51 | 0.95 | 1.69 | (6.30,5.97) |
| 246 | 245 | -0.80 | 0.92 | 1.68 | (6.30,5.97) |
| 247 | 246 | -0.32 | 0.91 | 1.67 | (6.30,5.97) |
| 248 | 247 | -0.20 | 0.90 | 1.66 | (6.30,5.97) |
| 249 | 248 | -2.02 | 0.89 | 1.66 | (6.30,5.97) |
| 250 | 249 | -1.93 | 0.90 | 1.66 | (6.30,5.97) |
| 251 | 250 | 1.96 | 0.89 | 1.66 | (6.30,5.97) |
| 252 | 251 | 0.94 | 0.89 | 1.66 | (6.30,5.97) |
| 253 | 252 | -0.72 | 0.89 | 1.65 | (6.30,5.97) |
| 254 | 253 | -0.04 | 0.89 | 1.66 | (6.30,5.97) |
| 255 | 254 | -0.53 | 0.90 | 1.67 | (6.30,5.97) |
| 256 | 255 | 0.46 | 0.88 | 1.65 | (6.30,5.97) |
| 257 | 256 | -1.66 | 0.86 | 1.63 | (6.30,5.97) |
| 258 | 257 | -1.58 | 0.85 | 1.63 | (6.30,5.97) |
| 259 | 258 | 1.73 | 0.86 | 1.62 | (6.30,5.97) |
| 260 | 259 | -1.50 | 0.87 | 1.62 | (6.30,5.97) |
| 261 | 260 | -0.70 | 0.84 | 1.59 | (6.30,5.97) |
| 262 | 261 | -1.45 | 0.84 | 1.59 | (6.30,5.97) |
| 263 | 262 | 1.30 | 0.83 | 1.58 | (6.30,5.97) |
| 264 | 263 | -1.63 | 0.84 | 1.59 | (6.30,5.97) |
| 265 | 264 | 1.35 | 0.83 | 1.55 | (6.30,5.97) |
| 266 | 265 | -1.02 | 0.83 | 1.56 | (6.30,5.97) |
| 267 | 266 | 0.18 | 0.83 | 1.54 | (6.30,5.97) |
| 268 | 267 | 0.24 | 0.84 | 1.52 | (6.30,5.97) |
| 269 | 268 | -0.84 | 0.84 | 1.50 | (6.30,5.97) |
| 270 | 269 | -0.83 | 0.85 | 1.49 | (6.30,5.97) |
| 271 | 270 | 0.00 | 0.85 | 1.47 | (6.30,5.97) |
| 272 | 271 | 0.84 | 0.84 | 1.45 | (6.30,5.97) |
| 273 | 272 | 0.00 | 0.85 | 1.41 | (6.30,5.97) |
| 274 | 273 | 0.00 | 0.85 | 1.38 | (6.30,5.97) |
| 275 | 274 | -1.67 | 0.85 | 1.36 | (6.30,5.97) |
| 276 | 275 | -0.12 | 0.85 | 1.33 | (6.30,5.97) |
| 277 | 276 | -0.86 | 0.86 | 1.30 | (6.30,5.97) |
| 278 | 277 | -0.06 | 0.87 | 1.27 | (6.30,5.97) |
| 279 | 278 | -2.59 | 0.87 | 1.24 | (6.30,5.97) |
| 280 | 279 | -0.78 | 0.87 | 1.24 | (6.30,5.97) |
+---------+------------+---------------+-------+-------+-------------+
Successfully wrote lumen to output/oct/oct_lumen.obj
Successfully wrote lumen to output/oct/oct_replaced_lumen.obj