vmtk-centerline-api / endograft_selection.py
SitHuman's picture
restored original code
a018007 verified
Raw
History Blame Contribute Delete
21 kB
# COOK Zenith Alpha Thoracic Grafts
proximal_designs = [
{"graft_diameter": 18, "vessel_diameter_range": (15, 16), "graft_lengths": [105], "gap": 7},
{"graft_diameter": 20, "vessel_diameter_range": (17, 17), "graft_lengths": [105], "gap": 7},
{"graft_diameter": 22, "vessel_diameter_range": (18, 19), "graft_lengths": [105], "gap": 7},
{"graft_diameter": 24, "vessel_diameter_range": (20, 21), "graft_lengths": [105], "gap": 7},
{"graft_diameter": 26, "vessel_diameter_range": (22, 23), "graft_lengths": [105], "gap": 7},
{"graft_diameter": 28, "vessel_diameter_range": (24, 25), "graft_lengths": [109, 155, 201], "gap": 8},
{"graft_diameter": 30, "vessel_diameter_range": (26, 27), "graft_lengths": [109, 155, 201], "gap": 8},
{"graft_diameter": 32, "vessel_diameter_range": (28, 29), "graft_lengths": [109, 155, 201], "gap": 8},
{"graft_diameter": 34, "vessel_diameter_range": (30, 30), "graft_lengths": [113, 161, 209], "gap": 9},
{"graft_diameter": 36, "vessel_diameter_range": (31, 32), "graft_lengths": [113, 161, 209], "gap": 9},
{"graft_diameter": 38, "vessel_diameter_range": (33, 34), "graft_lengths": [117, 167, 217], "gap": 10},
{"graft_diameter": 40, "vessel_diameter_range": (35, 36), "graft_lengths": [117, 167, 217], "gap": 10},
{"graft_diameter": 42, "vessel_diameter_range": (37, 38), "graft_lengths": [121, 173, 225], "gap": 11},
{"graft_diameter": 44, "vessel_diameter_range": (39, 39), "graft_lengths": [125, 179, 233], "gap": 12},
{"graft_diameter": 46, "vessel_diameter_range": (40, 42), "graft_lengths": [125, 179, 233], "gap": 12},
]
proximal_tapered_designs = [
{"proximal_diameter": 32, "distal_diameter": 28, "vessel_range": (28, 29), "graft_lengths": [178, 201], "gap": 8},
{"proximal_diameter": 34, "distal_diameter": 30, "vessel_range": (30, 30), "graft_lengths": [161, 209], "gap": 9},
{"proximal_diameter": 36, "distal_diameter": 32, "vessel_range": (31, 32), "graft_lengths": [161, 209], "gap": 9},
{"proximal_diameter": 38, "distal_diameter": 34, "vessel_range": (33, 34), "graft_lengths": [167, 217], "gap": 10},
{"proximal_diameter": 40, "distal_diameter": 36, "vessel_range": (35, 36), "graft_lengths": [167, 217], "gap": 10},
{"proximal_diameter": 42, "distal_diameter": 38, "vessel_range": (37, 38), "graft_lengths": [173, 225], "gap": 11},
{"proximal_diameter": 44, "distal_diameter": 40, "vessel_range": (39, 39), "graft_lengths": [179, 233], "gap": 12},
{"proximal_diameter": 46, "distal_diameter": 42, "vessel_range": (40, 42), "graft_lengths": [179, 233], "gap": 12},
]
distal_designs = [
{"graft_diameter": 28, "vessel_diameter_range": (24, 25), "graft_lengths": [160], "gap": 8},
{"graft_diameter": 30, "vessel_diameter_range": (26, 27), "graft_lengths": [160], "gap": 8},
{"graft_diameter": 32, "vessel_diameter_range": (28, 29), "graft_lengths": [160], "gap": 8},
{"graft_diameter": 34, "vessel_diameter_range": (30, 30), "graft_lengths": [190], "gap": 8},
{"graft_diameter": 36, "vessel_diameter_range": (31, 32), "graft_lengths": [190], "gap": 9},
{"graft_diameter": 38, "vessel_diameter_range": (33, 34), "graft_lengths": [197], "gap": 10},
{"graft_diameter": 40, "vessel_diameter_range": (35, 36), "graft_lengths": [197], "gap": 10},
{"graft_diameter": 42, "vessel_diameter_range": (37, 38), "graft_lengths": [204], "gap": 11},
{"graft_diameter": 44, "vessel_diameter_range": (39, 39), "graft_lengths": [211], "gap": 12},
{"graft_diameter": 46, "vessel_diameter_range": (40, 41), "graft_lengths": [211], "gap": 12},
]
distal_extension_designs = [
{"graft_diameter": 22, "vessel_diameter_range": (18, 19), "graft_lengths": [104], "gap": 7},
{"graft_diameter": 26, "vessel_diameter_range": (22, 23), "graft_lengths": [104], "gap": 7},
{"graft_diameter": 30, "vessel_diameter_range": (26, 27), "graft_lengths": [108], "gap": 8},
{"graft_diameter": 34, "vessel_diameter_range": (30, 30), "graft_lengths": [112], "gap": 9},
{"graft_diameter": 38, "vessel_diameter_range": (33, 34), "graft_lengths": [91], "gap": 10},
{"graft_diameter": 42, "vessel_diameter_range": (37, 38), "graft_lengths": [94], "gap": 11},
{"graft_diameter": 46, "vessel_diameter_range": (40, 42), "graft_lengths": [97], "gap": 12},
]
def assign_selected_graft_parameters(proximal_code, distal_code, extension_code, proximal_generation_diameter,
distal_graft_diameter, proximal_length, extension_length, distal_length, proximal_gap,
extension_gap, distal_gap, distal_extension_overlap, proximal_distal_overlap):
return {
"is_custom": False,
"proximal": proximal_code,
"distal": distal_code,
"extension": extension_code,
"proximal_diameter": proximal_generation_diameter,
"distal_diameter": float(distal_graft_diameter),
"prox_length": float(proximal_length),
"extension_length": float(extension_length),
"distal_length": float(distal_length),
"prox_gap": float(proximal_gap),
"extension_gap": float(extension_gap),
"distal_gap": float(distal_gap),
"distal_extension_overlap": float(distal_extension_overlap),
"proximal_distal_overlap": float(proximal_distal_overlap),
}
def generate_custom_endograft(proximal_diameter, distal_diameter, length):
strut_amplitude = 15
gap = 10
proximal_distal_overlap = (3 * strut_amplitude) + (2 * gap)
total_graft_length = length - proximal_distal_overlap
proximal_length = total_graft_length // 2
distal_length = total_graft_length - proximal_length
custom_proximal_code = f"ZTA_P_{proximal_diameter}_{proximal_length}_{gap}"
custom_distal_code = f"ZTA_D_{distal_diameter}_{distal_length}_{gap}"
custom_graft = assign_selected_graft_parameters(
proximal_code=custom_proximal_code,
distal_code=custom_distal_code,
extension_code="ZTA_E_0_0_0",
proximal_generation_diameter=float(proximal_diameter),
distal_graft_diameter=float(distal_diameter),
proximal_length=float(proximal_length),
extension_length=0.0,
distal_length=float(distal_length),
proximal_gap=float(gap),
extension_gap=0.0,
distal_gap=float(gap),
distal_extension_overlap=0.0,
proximal_distal_overlap=float(proximal_distal_overlap)
)
custom_graft["is_custom"] = True
return custom_graft
def get_endograft_parameters(vessel_diameter, component_type):
lengths = None
diameter = None
gap = None
if component_type == "proximal":
designs = proximal_designs
elif component_type == "proximal_tapered":
designs = proximal_tapered_designs
elif component_type == "distal":
designs = distal_designs
else:
designs = distal_extension_designs
for item in designs:
if component_type == "proximal_tapered":
vessel_range = item.get("vessel_range") or item.get("vessel_diameter_range")
if vessel_range is None:
continue
min_vessel_diameter, max_vessel_diameter = vessel_range
if min_vessel_diameter <= vessel_diameter <= max_vessel_diameter:
lengths = item["graft_lengths"]
diameter = {
"proximal": item["proximal_diameter"], # We can return the proximal only
"distal": item["distal_diameter"],
}
gap = item["gap"]
break
continue
min_vessel_diameter, max_vessel_diameter = item["vessel_diameter_range"]
if (min_vessel_diameter <= vessel_diameter <= max_vessel_diameter):
lengths, diameter, gap = item["graft_lengths"], item["graft_diameter"], item["gap"]
break
return lengths, diameter, gap
def choose_endograft(proximal_diameter, distal_diameter, length, fenestrations_exist, distance_to_last_fenestration):
strut_amplitude = 15
is_tapered = (proximal_diameter - distal_diameter) == 4
if is_tapered:
proximal_lengths, proximal_graft_diameter, proximal_gap = get_endograft_parameters(proximal_diameter, "proximal_tapered") # notice here the proximal graft diameter is a dictionary with proximal and distal diameters
proximal_prefix = "ZTA_PT"
else:
proximal_lengths, proximal_graft_diameter, proximal_gap = get_endograft_parameters(proximal_diameter, "proximal")
proximal_prefix = "ZTA_P"
distal_lengths, distal_graft_diameter, distal_gap = get_endograft_parameters(distal_diameter, "distal")
distal_prefix = "ZTA_D"
# If no proximal component is found at all, we cannot use a standard Zenith Cook proximal component
if proximal_lengths is None:
return None
if is_tapered and isinstance(proximal_graft_diameter, dict):
proximal_code_diameter = f"{int(proximal_graft_diameter['proximal'])}_{int(proximal_graft_diameter['distal'])}"
proximal_generation_diameter = float(proximal_graft_diameter["proximal"])
else:
proximal_code_diameter = str(proximal_graft_diameter)
proximal_generation_diameter = float(proximal_graft_diameter)
# Only triggers if proximal is found, but NO distal components match
if distal_lengths is None:
# Exact match length using only Proximal
for prox_len in proximal_lengths:
if prox_len == length:
proximal_code = f"{proximal_prefix}_{proximal_code_diameter}_{prox_len}_{proximal_gap}"
graft = assign_selected_graft_parameters(
proximal_code=proximal_code, distal_code="None", extension_code="None",
proximal_generation_diameter=proximal_generation_diameter, distal_graft_diameter=float(distal_diameter),
proximal_length=float(prox_len), extension_length=0.0, distal_length=0.0,
proximal_gap=float(proximal_gap), extension_gap=0.0, distal_gap=0.0,
distal_extension_overlap=0.0, proximal_distal_overlap=0.0
)
graft["is_custom"] = False
return graft
# Biggest length smaller than total + Custom Distal
smaller_lengths = [p for p in proximal_lengths if p < length]
if smaller_lengths:
best_prox_len = max(smaller_lengths)
custom_distal_gap = 10
proximal_distal_overlap = (3 * strut_amplitude) + (2 * custom_distal_gap)
distal_length_needed = length - best_prox_len + proximal_distal_overlap
custom_distal_diameter = round(distal_diameter * 1.15)
proximal_code = f"{proximal_prefix}_{proximal_code_diameter}_{best_prox_len}_{proximal_gap}"
custom_distal_code = f"ZTA_D_CUSTOM_{custom_distal_diameter}_{int(distal_length_needed)}_{custom_distal_gap}"
custom_graft = assign_selected_graft_parameters(
proximal_code=proximal_code, distal_code=custom_distal_code, extension_code="None",
proximal_generation_diameter=proximal_generation_diameter, distal_graft_diameter=float(custom_distal_diameter),
proximal_length=float(best_prox_len), extension_length=0.0, distal_length=float(distal_length_needed),
proximal_gap=float(proximal_gap), extension_gap=0.0, distal_gap=float(custom_distal_gap),
distal_extension_overlap=0.0, proximal_distal_overlap=float(proximal_distal_overlap)
)
custom_graft["is_custom"] = False
return custom_graft
# If all standard proximals are longer than the total needed length, use the smallest one alone
best_prox_len = min(proximal_lengths)
proximal_code = f"{proximal_prefix}_{proximal_code_diameter}_{best_prox_len}_{proximal_gap}"
graft = assign_selected_graft_parameters(
proximal_code=proximal_code, distal_code="None", extension_code="None",
proximal_generation_diameter=proximal_generation_diameter, distal_graft_diameter=float(distal_diameter),
proximal_length=float(best_prox_len), extension_length=0.0, distal_length=0.0,
proximal_gap=float(proximal_gap), extension_gap=0.0, distal_gap=0.0,
distal_extension_overlap=0.0, proximal_distal_overlap=0.0
)
graft["is_custom"] = False
return graft
# Triggers when both Proximal and Distal components are found
base_overlap = (3 * strut_amplitude) + (2 * distal_gap)
selected_graft = None
fallback_graft = None
for proximal_length in proximal_lengths:
for distal_length in distal_lengths:
proximal_distal_overlap = base_overlap
# Check whether the last fenestration falls inside the overlap region.
fenestration_overlap = False
if fenestrations_exist:
overlap_start = proximal_length - proximal_distal_overlap
if overlap_start < distance_to_last_fenestration < proximal_length:
fenestration_overlap = True
total_graft_length = (proximal_length + distal_length - proximal_distal_overlap )
proximal_code = (
f"{proximal_prefix}_{proximal_code_diameter}_"
f"{proximal_length}_{proximal_gap}"
)
distal_code = (
f"{distal_prefix}_{distal_graft_diameter}_"
f"{distal_length}_{distal_gap}"
)
extension_code = "ZTA_E_0_0_0"
if abs(total_graft_length - length) < 1e-6:
selected_graft = assign_selected_graft_parameters(
proximal_code=proximal_code,
distal_code=distal_code,
extension_code=extension_code,
proximal_generation_diameter=proximal_generation_diameter,
distal_graft_diameter=distal_graft_diameter,
proximal_length=proximal_length,
extension_length=0.0,
distal_length=distal_length,
proximal_gap=proximal_gap,
extension_gap=0.0,
distal_gap=distal_gap,
distal_extension_overlap=0.0,
proximal_distal_overlap=proximal_distal_overlap)
if not fenestration_overlap:
return selected_graft
if fallback_graft is None:
fallback_graft = selected_graft
elif total_graft_length > length:
adjusted_overlap = (proximal_distal_overlap + (total_graft_length - length))
selected_graft = assign_selected_graft_parameters(
proximal_code=proximal_code,
distal_code=distal_code,
extension_code=extension_code,
proximal_generation_diameter=proximal_generation_diameter,
distal_graft_diameter=distal_graft_diameter,
proximal_length=proximal_length,
extension_length=0.0,
distal_length=distal_length,
proximal_gap=proximal_gap,
extension_gap=0.0,
distal_gap=distal_gap,
distal_extension_overlap=0.0,
proximal_distal_overlap=adjusted_overlap)
if not fenestration_overlap:
return selected_graft
if fallback_graft is None:
fallback_graft = selected_graft
# we didn't find a graft that avoids fenestration overlap, so return the option with fenestration overlap.
if fallback_graft is not None:
return fallback_graft
# This search is for an additional extension component if the all graft designs are too short.
extension_lengths, extension_graft_diameter, extension_gap = get_endograft_parameters(distal_diameter, "distal_extension")
extension_prefix = "ZTA_E"
if extension_lengths is None:
return None
for proximal_length in proximal_lengths:
for extension_length in extension_lengths:
for distal_length in distal_lengths:
proximal_distal_overlap = base_overlap
if extension_length > 97:
distal_extension_overlap = (3 * strut_amplitude) + (2 * extension_gap)
else:
distal_extension_overlap = (2 * strut_amplitude) + extension_gap
# Check whether the last fenestration falls inside the overlap region.
fenestration_overlap = False
if fenestrations_exist:
overlap_start = proximal_length - proximal_distal_overlap
if overlap_start < distance_to_last_fenestration < proximal_length:
fenestration_overlap = True
total_graft_length = (proximal_length + extension_length + distal_length - proximal_distal_overlap - distal_extension_overlap)
proximal_code = (
f"{proximal_prefix}_{proximal_code_diameter}_"
f"{proximal_length}_{proximal_gap}"
)
extension_code = (
f"{extension_prefix}_{extension_graft_diameter}_"
f"{extension_length}_{extension_gap}"
)
distal_code = (
f"{distal_prefix}_{distal_graft_diameter}_"
f"{distal_length}_{distal_gap}"
)
if abs(total_graft_length - length) < 1e-6:
selected_graft = assign_selected_graft_parameters(
proximal_code=proximal_code,
distal_code=distal_code,
extension_code=extension_code,
proximal_generation_diameter=proximal_generation_diameter,
distal_graft_diameter=distal_graft_diameter,
proximal_length=proximal_length,
extension_length=extension_length,
distal_length=distal_length,
proximal_gap=proximal_gap,
extension_gap=extension_gap,
distal_gap=distal_gap,
distal_extension_overlap=distal_extension_overlap,
proximal_distal_overlap=proximal_distal_overlap)
if not fenestration_overlap:
return selected_graft
if fallback_graft is None:
fallback_graft = selected_graft
elif total_graft_length > length:
adjusted_distal_extension_overlap = (distal_extension_overlap + (total_graft_length - length))
selected_graft = assign_selected_graft_parameters(
proximal_code=proximal_code,
distal_code=distal_code,
extension_code=extension_code,
proximal_generation_diameter=proximal_generation_diameter,
distal_graft_diameter=distal_graft_diameter,
proximal_length=proximal_length,
extension_length=extension_length,
distal_length=distal_length,
proximal_gap=proximal_gap,
extension_gap=extension_gap,
distal_gap=distal_gap,
distal_extension_overlap=adjusted_distal_extension_overlap,
proximal_distal_overlap=proximal_distal_overlap)
if not fenestration_overlap:
return selected_graft
if fallback_graft is None:
fallback_graft = selected_graft
if fallback_graft is not None:
return fallback_graft
custom_graft = generate_custom_endograft(proximal_diameter, distal_diameter, length)
return custom_graft
def start_endograft_pipeline(proximal_diameter, distal_diameter, proximal_start_point, distal_end_point, length, fenestrations_exist, distance_to_last_fenestration):
selected_graft = choose_endograft(round(proximal_diameter), round(distal_diameter), round(length), fenestrations_exist, round(distance_to_last_fenestration))
if selected_graft is None:
print("No supported graft; try another manufacturer")
else:
selected_graft["proximal_start_point"] = proximal_start_point
selected_graft["distal_end_point"] = distal_end_point
return selected_graft