update web workflow and preview behavior

This commit is contained in:
2026-05-03 05:41:22 +08:00
parent f4bde6460a
commit 149cbc95d3
5 changed files with 339 additions and 78 deletions

View File

@@ -152,7 +152,37 @@ def fit_image(image, width, height):
return canvas
def preview_deform_2d(image, angle_degrees):
def preview_motion_weight(height, width, mode="soft_transition", transition_width=90, gaussian_sigma=3):
yy, xx = np.mgrid[0:height, 0:width]
boundary_y = height * 0.71
transition_span = height * np.clip(float(transition_width) / 90 * 0.42, 0.18, 0.56)
full_motion_y = boundary_y - transition_span * 0.5
fixed_y = boundary_y + transition_span * 0.5
if mode == "hard_boundary":
return (yy <= boundary_y).astype(np.float32)
if mode == "gaussian_smooth":
try:
from scipy.ndimage import gaussian_filter
except Exception:
gaussian_filter = None
hard = (yy <= boundary_y).astype(np.float32)
if gaussian_filter is None:
return hard
sigma = float(np.clip(float(gaussian_sigma), 1, 12))
return np.clip(gaussian_filter(hard, sigma=sigma), 0, 1).astype(np.float32)
t = np.clip((yy - full_motion_y) / (fixed_y - full_motion_y), 0, 1)
weight = 1 - (t * t * (3 - 2 * t))
x_soft = np.clip((xx - width * 0.15) / (width * 0.75), 0, 1)
x_soft = x_soft * x_soft * (3 - 2 * x_soft)
return np.clip(weight * (0.90 + 0.10 * x_soft), 0, 1).astype(np.float32)
def preview_deform_2d(image, angle_degrees, mode="soft_transition", transition_width=90, gaussian_sigma=3):
"""Fast visual preview only. The DICOM output uses the real 3D field."""
try:
from scipy.ndimage import map_coordinates
@@ -166,15 +196,7 @@ def preview_deform_2d(image, angle_degrees):
pivot_x = int(w * 0.55)
pivot_y = int(h * 0.62)
full_motion_y = h * 0.50
fixed_y = h * 0.92
t = np.clip((yy - full_motion_y) / (fixed_y - full_motion_y), 0, 1)
weight = 1 - (t * t * (3 - 2 * t))
x_soft = np.clip((xx - w * 0.15) / (w * 0.75), 0, 1)
x_soft = x_soft * x_soft * (3 - 2 * x_soft)
weight = np.clip(weight * (0.90 + 0.10 * x_soft), 0, 1)
weight = preview_motion_weight(h, w, mode, transition_width, gaussian_sigma)
theta = np.deg2rad(angle_degrees) * weight
cos_t = np.cos(theta)
sin_t = np.sin(theta)
@@ -359,7 +381,7 @@ 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, transition_width=transition_width)
make_output_preview_from_images(
state_images["original"],
state_images["soft_transition"],
@@ -437,14 +459,32 @@ 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, coordinates_cutoff=None):
def make_four_state_preview(state_images, output_dir, angle_degrees, coordinates_cutoff=None, transition_width=90):
output_dir = Path(output_dir)
screenshot_dir = output_dir / "process_screenshots"
reset_folder(screenshot_dir)
original_panel = sitk_sagittal_panel(state_images["original"], coordinates_cutoff)
preview_panels = {
"original": original_panel,
"hard_boundary": preview_deform_2d(original_panel, angle_degrees, "hard_boundary"),
"gaussian_smooth": preview_deform_2d(
original_panel,
angle_degrees,
"gaussian_smooth",
transition_width,
),
"soft_transition": preview_deform_2d(
original_panel,
angle_degrees,
"soft_transition",
transition_width,
),
}
panels = []
for state_key, label, _ in STATE_LABELS:
panel = sitk_sagittal_panel(state_images[state_key], coordinates_cutoff)
panel = preview_panels[state_key]
panel_path = screenshot_dir / f"{state_key}.png"
panel.save(panel_path, quality=95)
panels.append((label, panel))
@@ -517,7 +557,7 @@ class HeadExtensionApp:
self.angle = Scale(
controls,
from_=0,
to=20,
to=45,
orient=HORIZONTAL,
resolution=0.5,
length=420,
@@ -603,7 +643,12 @@ class HeadExtensionApp:
self.cached_volume = load_dicom_volume(self.input_dir.get())
before = crop_head_neck(sagittal_mip(self.cached_volume))
before_with_line = draw_cutoff_line(before, self.cached_volume.shape[0])
after = preview_deform_2d(before_with_line, float(self.angle.get()))
after = preview_deform_2d(
before_with_line,
float(self.angle.get()),
transition_width=float(self.transition.get()),
gaussian_sigma=3,
)
canvas = Image.new("RGB", (1120, 610), (0, 0, 0))
draw = ImageDraw.Draw(canvas)