Spaces:
Sleeping
Sleeping
Upload centerline_extraction.py
Browse files- centerline_extraction.py +49 -17
centerline_extraction.py
CHANGED
|
@@ -2141,28 +2141,60 @@ def _deduplicate_bifurcation_points(bif_points, threshold_mm=5.0):
|
|
| 2141 |
def _orient_artery_segments(segments):
|
| 2142 |
"""Ensure artery (non-Aorta) segments are ordered bifurcation β endpoint.
|
| 2143 |
|
| 2144 |
-
|
| 2145 |
-
|
| 2146 |
-
the
|
| 2147 |
-
|
| 2148 |
-
|
| 2149 |
-
|
| 2150 |
-
|
| 2151 |
-
|
| 2152 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2153 |
"""
|
|
|
|
|
|
|
| 2154 |
for seg in segments:
|
| 2155 |
-
if seg
|
| 2156 |
continue
|
| 2157 |
-
pts = seg
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2158 |
if len(pts) < 2:
|
| 2159 |
continue
|
| 2160 |
-
|
| 2161 |
-
|
| 2162 |
-
|
| 2163 |
-
|
| 2164 |
-
|
| 2165 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2166 |
|
| 2167 |
|
| 2168 |
# Build Frontend response JSON
|
|
|
|
| 2141 |
def _orient_artery_segments(segments):
|
| 2142 |
"""Ensure artery (non-Aorta) segments are ordered bifurcation β endpoint.
|
| 2143 |
|
| 2144 |
+
Strategy (preferred): the bifurcation end of a branch is physically
|
| 2145 |
+
touching an Aorta segment endpoint (they share the same VMTK network
|
| 2146 |
+
node, so the distance is β 0 mm). We check both ends of each artery
|
| 2147 |
+
segment against all Aorta segment endpoints; whichever end is closest
|
| 2148 |
+
to any aorta endpoint is the bifurcation end and must come first.
|
| 2149 |
+
|
| 2150 |
+
Fallback: if no Aorta segments exist yet (very rare), fall back to the
|
| 2151 |
+
radius heuristic β the bifurcation end is typically thicker.
|
| 2152 |
+
|
| 2153 |
+
Why not radius alone? After 1 mm resampling the Voronoi radius at the
|
| 2154 |
+
resampled endpoints can be slightly different from the exact bifurcation
|
| 2155 |
+
radius, causing the heuristic to flip segments that should stay and
|
| 2156 |
+
vice-versa. Proximity to an Aorta endpoint is exact (same VTK point ID
|
| 2157 |
+
β same x,y,z coordinates) and therefore completely reliable.
|
| 2158 |
"""
|
| 2159 |
+
# Collect all Aorta segment endpoints (first and last point of each Aorta seg)
|
| 2160 |
+
aorta_eps = []
|
| 2161 |
for seg in segments:
|
| 2162 |
+
if seg.get('label') != 'Aorta':
|
| 2163 |
continue
|
| 2164 |
+
pts = seg.get('points', [])
|
| 2165 |
+
if not pts:
|
| 2166 |
+
continue
|
| 2167 |
+
aorta_eps.append(pts[0])
|
| 2168 |
+
if len(pts) > 1:
|
| 2169 |
+
aorta_eps.append(pts[-1])
|
| 2170 |
+
|
| 2171 |
+
for seg in segments:
|
| 2172 |
+
if seg.get('label') == 'Aorta':
|
| 2173 |
+
continue
|
| 2174 |
+
pts = seg.get('points', [])
|
| 2175 |
if len(pts) < 2:
|
| 2176 |
continue
|
| 2177 |
+
|
| 2178 |
+
if aorta_eps:
|
| 2179 |
+
# Proximity check: which end is closest to any Aorta endpoint?
|
| 2180 |
+
p0, p_last = pts[0], pts[-1]
|
| 2181 |
+
min_dist_start = min(
|
| 2182 |
+
(p0['x'] - ae['x']) ** 2 + (p0['y'] - ae['y']) ** 2 + (p0['z'] - ae['z']) ** 2
|
| 2183 |
+
for ae in aorta_eps
|
| 2184 |
+
)
|
| 2185 |
+
min_dist_end = min(
|
| 2186 |
+
(p_last['x'] - ae['x']) ** 2 + (p_last['y'] - ae['y']) ** 2 + (p_last['z'] - ae['z']) ** 2
|
| 2187 |
+
for ae in aorta_eps
|
| 2188 |
+
)
|
| 2189 |
+
# If the END is closer to the aorta, the segment is backwards β reverse it
|
| 2190 |
+
if min_dist_end < min_dist_start:
|
| 2191 |
+
pts.reverse()
|
| 2192 |
+
else:
|
| 2193 |
+
# Fallback: radius heuristic (bifurcation end is thicker)
|
| 2194 |
+
r_first = pts[0]['radius']
|
| 2195 |
+
r_last = pts[-1]['radius']
|
| 2196 |
+
if r_first < r_last:
|
| 2197 |
+
pts.reverse()
|
| 2198 |
|
| 2199 |
|
| 2200 |
# Build Frontend response JSON
|