diff --git a/head_extension_app.py b/head_extension_app.py index 83ceff0..4743011 100644 --- a/head_extension_app.py +++ b/head_extension_app.py @@ -119,6 +119,28 @@ def crop_head_neck(image): 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): scale = min(width / image.width, height / image.height) 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 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( state_images["original"], state_images["soft_transition"], Path(output_dir), angle_degrees, + vol_info.coordinates_cutoff, ) 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 -def sitk_sagittal_panel(image): +def sitk_sagittal_panel(image, coordinates_cutoff=None): 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): - before = sitk_sagittal_panel(original_image) - after = sitk_sagittal_panel(deformed_image) +def make_output_preview_from_images(original_image, deformed_image, output_dir, angle_degrees, coordinates_cutoff=None): + before = sitk_sagittal_panel(original_image, coordinates_cutoff) + after = sitk_sagittal_panel(deformed_image, coordinates_cutoff) slide = Image.new("RGB", (2560, 1440), (0, 0, 0)) draw = ImageDraw.Draw(slide) @@ -408,14 +439,14 @@ def make_output_preview_from_images(original_image, deformed_image, output_dir, 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) screenshot_dir = output_dir / "process_screenshots" reset_folder(screenshot_dir) panels = [] 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.save(panel_path, quality=95) panels.append((label, panel)) @@ -573,6 +604,7 @@ class HeadExtensionApp: self.status.set("正在读取 DICOM 生成预览...") self.cached_volume = load_dicom_volume(self.input_dir.get()) 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())) canvas = Image.new("RGB", (1120, 610), (0, 0, 0)) diff --git a/web_backend.py b/web_backend.py index ec3e8de..e6e97a0 100644 --- a/web_backend.py +++ b/web_backend.py @@ -25,6 +25,7 @@ from head_extension_app import ( APP_DIR, crop_head_neck, ct_window, + draw_cutoff_line, fit_image, load_dicom_volume, 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): volume = load_dicom_volume(input_dir) before = crop_head_neck(sagittal_mip(volume)) + before = draw_cutoff_line(before, volume.shape[0]) after = preview_deform_2d(before, float(angle_degrees)) canvas = BytesIO()