draw deformation cutoff line in previews

This commit is contained in:
2026-05-03 01:57:57 +08:00
parent 2559522e3d
commit ea71713b0a
2 changed files with 42 additions and 8 deletions

View File

@@ -119,6 +119,28 @@ def crop_head_neck(image):
return image.crop((left, top, right, bottom)) return image.crop((left, top, right, bottom))
def cutoff_center_z(coordinates_cutoff):
return float(np.mean([point[0] for point in coordinates_cutoff]))
def draw_cutoff_line(panel, image_depth, coordinates_cutoff=DEFAULT_COORDINATES_CUTOFF):
panel = panel.copy()
crop_height = int(image_depth * 0.72)
if crop_height <= 0:
return panel
line_y = int(round((image_depth - 1 - cutoff_center_z(coordinates_cutoff)) * panel.height / crop_height))
if line_y < 0 or line_y >= panel.height:
return panel
draw = ImageDraw.Draw(panel)
shadow = (0, 0, 0)
line_color = (255, 215, 60)
draw.line((0, line_y, panel.width, line_y), fill=shadow, width=6)
draw.line((0, line_y, panel.width, line_y), fill=line_color, width=3)
return panel
def fit_image(image, width, height): def fit_image(image, width, height):
scale = min(width / image.width, height / image.height) scale = min(width / image.width, height / image.height)
resized = image.resize( resized = image.resize(
@@ -333,12 +355,18 @@ def run_deformation(input_dir, output_dir, angle_degrees, transition_width, prog
output_paths["legacy_soft"] = legacy_soft_dir output_paths["legacy_soft"] = legacy_soft_dir
progress("正在生成四状态过程对比图...") progress("正在生成四状态过程对比图...")
preview_paths = make_four_state_preview(state_images, Path(output_dir), angle_degrees) preview_paths = make_four_state_preview(
state_images,
Path(output_dir),
angle_degrees,
vol_info.coordinates_cutoff,
)
make_output_preview_from_images( make_output_preview_from_images(
state_images["original"], state_images["original"],
state_images["soft_transition"], state_images["soft_transition"],
Path(output_dir), Path(output_dir),
angle_degrees, angle_degrees,
vol_info.coordinates_cutoff,
) )
return output_paths, preview_paths return output_paths, preview_paths
@@ -375,14 +403,17 @@ def make_output_preview(original_dir, deformed_dicom_dir, output_dir, angle_degr
return preview_path return preview_path
def sitk_sagittal_panel(image): def sitk_sagittal_panel(image, coordinates_cutoff=None):
volume = sitk.GetArrayFromImage(image)[::-1] volume = sitk.GetArrayFromImage(image)[::-1]
return crop_head_neck(sagittal_mip(volume)) panel = crop_head_neck(sagittal_mip(volume))
if coordinates_cutoff is not None:
panel = draw_cutoff_line(panel, volume.shape[0], coordinates_cutoff)
return panel
def make_output_preview_from_images(original_image, deformed_image, output_dir, angle_degrees): def make_output_preview_from_images(original_image, deformed_image, output_dir, angle_degrees, coordinates_cutoff=None):
before = sitk_sagittal_panel(original_image) before = sitk_sagittal_panel(original_image, coordinates_cutoff)
after = sitk_sagittal_panel(deformed_image) after = sitk_sagittal_panel(deformed_image, coordinates_cutoff)
slide = Image.new("RGB", (2560, 1440), (0, 0, 0)) slide = Image.new("RGB", (2560, 1440), (0, 0, 0))
draw = ImageDraw.Draw(slide) draw = ImageDraw.Draw(slide)
@@ -408,14 +439,14 @@ def make_output_preview_from_images(original_image, deformed_image, output_dir,
return preview_path return preview_path
def make_four_state_preview(state_images, output_dir, angle_degrees): def make_four_state_preview(state_images, output_dir, angle_degrees, coordinates_cutoff=None):
output_dir = Path(output_dir) output_dir = Path(output_dir)
screenshot_dir = output_dir / "process_screenshots" screenshot_dir = output_dir / "process_screenshots"
reset_folder(screenshot_dir) reset_folder(screenshot_dir)
panels = [] panels = []
for state_key, label, _ in STATE_LABELS: for state_key, label, _ in STATE_LABELS:
panel = sitk_sagittal_panel(state_images[state_key]) panel = sitk_sagittal_panel(state_images[state_key], coordinates_cutoff)
panel_path = screenshot_dir / f"{state_key}.png" panel_path = screenshot_dir / f"{state_key}.png"
panel.save(panel_path, quality=95) panel.save(panel_path, quality=95)
panels.append((label, panel)) panels.append((label, panel))
@@ -573,6 +604,7 @@ class HeadExtensionApp:
self.status.set("正在读取 DICOM 生成预览...") self.status.set("正在读取 DICOM 生成预览...")
self.cached_volume = load_dicom_volume(self.input_dir.get()) self.cached_volume = load_dicom_volume(self.input_dir.get())
before = crop_head_neck(sagittal_mip(self.cached_volume)) before = crop_head_neck(sagittal_mip(self.cached_volume))
before = draw_cutoff_line(before, self.cached_volume.shape[0])
after = preview_deform_2d(before, float(self.angle.get())) after = preview_deform_2d(before, float(self.angle.get()))
canvas = Image.new("RGB", (1120, 610), (0, 0, 0)) canvas = Image.new("RGB", (1120, 610), (0, 0, 0))

View File

@@ -25,6 +25,7 @@ from head_extension_app import (
APP_DIR, APP_DIR,
crop_head_neck, crop_head_neck,
ct_window, ct_window,
draw_cutoff_line,
fit_image, fit_image,
load_dicom_volume, load_dicom_volume,
preview_deform_2d, preview_deform_2d,
@@ -549,6 +550,7 @@ def start_job(kind, worker, owner=None, params=None, remember_user_task=True):
def make_preview(input_dir, angle_degrees): def make_preview(input_dir, angle_degrees):
volume = load_dicom_volume(input_dir) volume = load_dicom_volume(input_dir)
before = crop_head_neck(sagittal_mip(volume)) before = crop_head_neck(sagittal_mip(volume))
before = draw_cutoff_line(before, volume.shape[0])
after = preview_deform_2d(before, float(angle_degrees)) after = preview_deform_2d(before, float(angle_degrees))
canvas = BytesIO() canvas = BytesIO()