update web workflow and preview behavior
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user