From a937583400fd13e5ae6192aade05a31e4bcd6fea Mon Sep 17 00:00:00 2001 From: admin <572701190@qq.com> Date: Sun, 3 May 2026 02:11:14 +0800 Subject: [PATCH] toggle cutoff line only in quick preview --- WebSite/src/App.tsx | 19 +++++++++++++++++-- head_extension_app.py | 14 ++++---------- web_backend.py | 21 +++++++++++++++------ 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/WebSite/src/App.tsx b/WebSite/src/App.tsx index 28813d1..2a3c119 100644 --- a/WebSite/src/App.tsx +++ b/WebSite/src/App.tsx @@ -31,6 +31,8 @@ import { Server, AlertCircle, Info, + Eye, + EyeOff, X } from 'lucide-react'; @@ -239,6 +241,7 @@ export default function App() { // --- Simulation State (Workspace) --- const [cervicalRotation, setCervicalRotation] = useState(14.5); const [transitionWidth, setTransitionWidth] = useState(90); + const [showPreviewCutoffLine, setShowPreviewCutoffLine] = useState(true); const [isSimulating, setIsSimulating] = useState(restoredDeformationJob?.job.status === 'running'); const [progress, setProgress] = useState(restoredDeformationJob ? progressFromJob(restoredDeformationJob.job, restoredDeformationJob.progress) : 0); const [toastMessage, setToastMessage] = useState(""); @@ -665,7 +668,8 @@ export default function App() { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ inputDir: selectedInputDir, - angleDegrees: cervicalRotation + angleDegrees: cervicalRotation, + showCutoffLine: showPreviewCutoffLine }), signal: controller.signal }); @@ -688,7 +692,7 @@ export default function App() { controller.abort(); window.clearTimeout(timer); }; - }, [currentPage, selectedInputDir, cervicalRotation]); + }, [currentPage, selectedInputDir, cervicalRotation, showPreviewCutoffLine]); const handleRunSimulation = async () => { if (isSimulating) return; @@ -901,6 +905,17 @@ export default function App() {
过渡平滑宽度{transitionWidth}
setTransitionWidth(parseInt(e.target.value, 10))} className="w-full h-1.5 bg-slate-100 rounded-lg appearance-none cursor-pointer accent-blue-600 opacity-80 hover:opacity-100 transition-opacity" /> +
diff --git a/head_extension_app.py b/head_extension_app.py index 4b2085f..5a127f7 100644 --- a/head_extension_app.py +++ b/head_extension_app.py @@ -355,18 +355,12 @@ 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, - vol_info.coordinates_cutoff, - ) + preview_paths = make_four_state_preview(state_images, Path(output_dir), angle_degrees) 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 @@ -604,12 +598,12 @@ 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)) - after = preview_deform_2d(before, float(self.angle.get())) - after = draw_cutoff_line(after, self.cached_volume.shape[0]) + before_with_line = draw_cutoff_line(before, self.cached_volume.shape[0]) + after = preview_deform_2d(before_with_line, float(self.angle.get())) canvas = Image.new("RGB", (1120, 610), (0, 0, 0)) draw = ImageDraw.Draw(canvas) - before_panel = fit_image(before, 520, 500) + before_panel = fit_image(before_with_line, 520, 500) after_panel = fit_image(after, 520, 500) canvas.paste(before_panel, (30, 80)) canvas.paste(after_panel, (570, 80)) diff --git a/web_backend.py b/web_backend.py index c978f9f..bc5f178 100644 --- a/web_backend.py +++ b/web_backend.py @@ -547,15 +547,18 @@ def start_job(kind, worker, owner=None, params=None, remember_user_task=True): return get_job(job_id) -def make_preview(input_dir, angle_degrees): +def make_preview(input_dir, angle_degrees, show_cutoff_line=True): volume = load_dicom_volume(input_dir) before = crop_head_neck(sagittal_mip(volume)) - after = preview_deform_2d(before, float(angle_degrees)) - after = draw_cutoff_line(after, volume.shape[0]) + before_display = draw_cutoff_line(before, volume.shape[0]) if show_cutoff_line else before + after = preview_deform_2d(before_display, float(angle_degrees)) + + canvas_image = Image.new("RGB", (1440, 520), (0, 0, 0)) + canvas_image.paste(fit_image(before_display, 700, 520), (0, 0)) + canvas_image.paste(fit_image(after, 700, 520), (740, 0)) canvas = BytesIO() - preview = fit_image(after, 720, 520) - preview.save(canvas, format="PNG") + canvas_image.save(canvas, format="PNG") encoded = base64.b64encode(canvas.getvalue()).decode("ascii") return { "image": f"data:image/png;base64,{encoded}", @@ -721,7 +724,13 @@ class Handler(BaseHTTPRequestHandler): body = self.read_json() if parsed.path == "/api/preview": - self.send_json(make_preview(body["inputDir"], body.get("angleDegrees", 12))) + self.send_json( + make_preview( + body["inputDir"], + body.get("angleDegrees", 12), + bool(body.get("showCutoffLine", True)), + ) + ) return if parsed.path == "/api/deformation/package":