Spaces:
Sleeping
Sleeping
Upload centerline_extraction.py
Browse files- centerline_extraction.py +23 -11
centerline_extraction.py
CHANGED
|
@@ -142,7 +142,7 @@ def compute_centerlines_network(
|
|
| 142 |
# Extract main aorta→iliacs centerline via Voronoi method
|
| 143 |
try:
|
| 144 |
print("Extracting main centerline...")
|
| 145 |
-
centerline_polydata = extract_centerline_from_surface(surface_polydata_ras, aorta_iliac_endpoints_ras, curve_sampling_distance, smooth_iterations=
|
| 146 |
print(f"Main centerline: {centerline_polydata.GetNumberOfCells()} cells, {centerline_polydata.GetNumberOfPoints()} points")
|
| 147 |
except Exception as e:
|
| 148 |
print(f"Main centerline extraction failed: {type(e).__name__}: {e}")
|
|
@@ -157,7 +157,7 @@ def compute_centerlines_network(
|
|
| 157 |
except Exception as e:
|
| 158 |
print(f"Branch extraction failed: {type(e).__name__}: {e}")
|
| 159 |
|
| 160 |
-
# Merge main + branch centerlines
|
| 161 |
if branch_centerlines:
|
| 162 |
try:
|
| 163 |
merged = merge_main_and_branch_centerlines(centerline_polydata, branch_centerlines)
|
|
@@ -726,7 +726,7 @@ def smooth_centerline_polydata(centerline_polydata, iterations, relaxation_facto
|
|
| 726 |
# Process all branches, generating their centerlines, computing their new bifurcation points on actual voronoi centerline (not skeleton)
|
| 727 |
def process_all_branches(branch_information, main_centerline_polydata, surface_polydata, curve_sampling_distance):
|
| 728 |
if not branch_information:
|
| 729 |
-
return []
|
| 730 |
|
| 731 |
# Convert branch info from LPS to RAS
|
| 732 |
branch_info_ras = []
|
|
@@ -768,7 +768,8 @@ def extract_branch_centerline(branch_info, main_centerline_polydata, surface_pol
|
|
| 768 |
|
| 769 |
# Extract the branch centerline
|
| 770 |
try:
|
| 771 |
-
branch_centerline = extract_centerline_from_surface(clipped_surface, branch_endpoints, curve_sampling_distance, smooth_iterations=
|
|
|
|
| 772 |
return branch_centerline, contact_point
|
| 773 |
except Exception as e:
|
| 774 |
print(f"\nWarning: branch centerline extraction failed: {e}")
|
|
@@ -812,11 +813,25 @@ def clip_surface_around_points(surface_polydata, point1, point2, radius_multipli
|
|
| 812 |
cleaner.Update()
|
| 813 |
return cleaner.GetOutput()
|
| 814 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 815 |
# Merge the main aorta→iliacs centerline with all branch centerlines into one polydata.
|
| 816 |
def merge_main_and_branch_centerlines(main_centerline_polydata, branch_centerlines):
|
| 817 |
if not branch_centerlines:
|
| 818 |
-
|
| 819 |
-
return smooth_centerline_polydata(main_centerline_polydata, iterations=20, relaxation_factor=0.1)
|
| 820 |
|
| 821 |
append_filter = vtk.vtkAppendPolyData()
|
| 822 |
append_filter.AddInputData(main_centerline_polydata)
|
|
@@ -828,14 +843,11 @@ def merge_main_and_branch_centerlines(main_centerline_polydata, branch_centerlin
|
|
| 828 |
cleaner = vtk.vtkCleanPolyData()
|
| 829 |
cleaner.SetInputData(append_filter.GetOutput())
|
| 830 |
cleaner.SetToleranceIsAbsolute(True)
|
| 831 |
-
cleaner.SetAbsoluteTolerance(0.
|
| 832 |
cleaner.Update()
|
| 833 |
|
| 834 |
-
# Smooth ONCE on the fully-connected network — endpoints and junction nodes are fixed by BoundarySmoothingOff
|
| 835 |
merged = cleaner.GetOutput()
|
| 836 |
-
|
| 837 |
-
print(f"Merged+smoothed: {smoothed.GetNumberOfCells()} cells, {smoothed.GetNumberOfPoints()} points")
|
| 838 |
-
return smoothed
|
| 839 |
|
| 840 |
# Main Pipeline for extracting centerline data, labeling bifurcations and arteries, computing ostium and reference point positions
|
| 841 |
def extract_centerline_data(centerlines, branch_junctions=None):
|
|
|
|
| 142 |
# Extract main aorta→iliacs centerline via Voronoi method
|
| 143 |
try:
|
| 144 |
print("Extracting main centerline...")
|
| 145 |
+
centerline_polydata = extract_centerline_from_surface(surface_polydata_ras, aorta_iliac_endpoints_ras, curve_sampling_distance, smooth_iterations=20)
|
| 146 |
print(f"Main centerline: {centerline_polydata.GetNumberOfCells()} cells, {centerline_polydata.GetNumberOfPoints()} points")
|
| 147 |
except Exception as e:
|
| 148 |
print(f"Main centerline extraction failed: {type(e).__name__}: {e}")
|
|
|
|
| 157 |
except Exception as e:
|
| 158 |
print(f"Branch extraction failed: {type(e).__name__}: {e}")
|
| 159 |
|
| 160 |
+
# Merge main + branch centerlines
|
| 161 |
if branch_centerlines:
|
| 162 |
try:
|
| 163 |
merged = merge_main_and_branch_centerlines(centerline_polydata, branch_centerlines)
|
|
|
|
| 726 |
# Process all branches, generating their centerlines, computing their new bifurcation points on actual voronoi centerline (not skeleton)
|
| 727 |
def process_all_branches(branch_information, main_centerline_polydata, surface_polydata, curve_sampling_distance):
|
| 728 |
if not branch_information:
|
| 729 |
+
return [], []
|
| 730 |
|
| 731 |
# Convert branch info from LPS to RAS
|
| 732 |
branch_info_ras = []
|
|
|
|
| 768 |
|
| 769 |
# Extract the branch centerline
|
| 770 |
try:
|
| 771 |
+
branch_centerline = extract_centerline_from_surface(clipped_surface, branch_endpoints, curve_sampling_distance, smooth_iterations=10)
|
| 772 |
+
branch_centerline = snap_branch_to_contact(branch_centerline, contact_point)
|
| 773 |
return branch_centerline, contact_point
|
| 774 |
except Exception as e:
|
| 775 |
print(f"\nWarning: branch centerline extraction failed: {e}")
|
|
|
|
| 813 |
cleaner.Update()
|
| 814 |
return cleaner.GetOutput()
|
| 815 |
|
| 816 |
+
# Forces the branch centerline endpoint nearest to contact_point to land at EXACTLY that coordinate, guaranteeing vtkCleanPolyData can merge it with the main centerline
|
| 817 |
+
def snap_branch_to_contact(branch_cl, contact_point):
|
| 818 |
+
pts = branch_cl.GetPoints()
|
| 819 |
+
n = pts.GetNumberOfPoints()
|
| 820 |
+
if n < 2:
|
| 821 |
+
return branch_cl
|
| 822 |
+
p_first = pts.GetPoint(0)
|
| 823 |
+
p_last = pts.GetPoint(n - 1)
|
| 824 |
+
d_first = vtk.vtkMath.Distance2BetweenPoints(p_first, contact_point)
|
| 825 |
+
d_last = vtk.vtkMath.Distance2BetweenPoints(p_last, contact_point)
|
| 826 |
+
snap_id = 0 if d_first < d_last else n - 1
|
| 827 |
+
pts.SetPoint(snap_id, contact_point[0], contact_point[1], contact_point[2])
|
| 828 |
+
pts.Modified()
|
| 829 |
+
return branch_cl
|
| 830 |
+
|
| 831 |
# Merge the main aorta→iliacs centerline with all branch centerlines into one polydata.
|
| 832 |
def merge_main_and_branch_centerlines(main_centerline_polydata, branch_centerlines):
|
| 833 |
if not branch_centerlines:
|
| 834 |
+
return main_centerline_polydata
|
|
|
|
| 835 |
|
| 836 |
append_filter = vtk.vtkAppendPolyData()
|
| 837 |
append_filter.AddInputData(main_centerline_polydata)
|
|
|
|
| 843 |
cleaner = vtk.vtkCleanPolyData()
|
| 844 |
cleaner.SetInputData(append_filter.GetOutput())
|
| 845 |
cleaner.SetToleranceIsAbsolute(True)
|
| 846 |
+
cleaner.SetAbsoluteTolerance(0.1) # 0.1mm tolerance to merge junction points from separately-extracted branch centerlines
|
| 847 |
cleaner.Update()
|
| 848 |
|
|
|
|
| 849 |
merged = cleaner.GetOutput()
|
| 850 |
+
return merged
|
|
|
|
|
|
|
| 851 |
|
| 852 |
# Main Pipeline for extracting centerline data, labeling bifurcations and arteries, computing ostium and reference point positions
|
| 853 |
def extract_centerline_data(centerlines, branch_junctions=None):
|