Stop seeding Manual Alignment translation from the automatic result
The dialog was seeding rotation_deg/shift_mm from self._alignment_result whenever it was non-None, with no way to tell whether that result came from a user's own manual Save or from the automatic Fusion -> Angle Alignment run. The automatic path's translation comes from FFT phase correlation - the exact thing manual mode exists to work around - so opening Manual Alignment after running the automatic pass silently inherited its unreliable shifts on top of the (correct) analytic rotation, reproducing the same scattered-scan symptom under a "manual" label. Manual mode should always start from identity (each angle's centroid already coincides with every other's, per the prior pivot fix) unless the user previously used this same dialog to save their own deliberate translation - which is exactly what the sidecar file already records. Seed exclusively from load_manual_alignment() and drop the self._alignment_result branch entirely; the automatic result is never an appropriate seed for a manual editing session. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+13
-10
@@ -2540,16 +2540,19 @@ class SrasViewerWindow(QMainWindow):
|
||||
ref_idx = 0
|
||||
threshold_mv = self.spin_threshold_mv.value()
|
||||
seed: dict[int, ManualAngleParams] = {}
|
||||
if (self._alignment_result is not None
|
||||
and self._alignment_result.ref_angle_idx == ref_idx):
|
||||
seed = {a: ManualAngleParams(t.rotation_deg, t.shift_mm)
|
||||
for a, t in self._alignment_result.per_angle.items()}
|
||||
threshold_mv = self._alignment_result.dc_threshold_mv
|
||||
else:
|
||||
sidecar = load_manual_alignment(self._sras)
|
||||
if sidecar is not None and sidecar.ref_angle_idx == ref_idx:
|
||||
seed = dict(sidecar.per_angle)
|
||||
threshold_mv = sidecar.dc_threshold_mv
|
||||
# Seed only from a previously *saved manual* alignment (this dialog's
|
||||
# own Save also writes this sidecar) -- never from self._alignment_result
|
||||
# when it holds the automatic Fusion -> Angle Alignment's output. That
|
||||
# path's translation comes from FFT phase correlation, which is the
|
||||
# very thing manual mode exists to work around; inheriting it here
|
||||
# would silently reintroduce the same bad translations under a
|
||||
# "manual" label, on top of the (correct) analytic rotation, which is
|
||||
# exactly what makes manual mode look like it "still does the same
|
||||
# thing" the automatic one does.
|
||||
sidecar = load_manual_alignment(self._sras)
|
||||
if sidecar is not None and sidecar.ref_angle_idx == ref_idx:
|
||||
seed = dict(sidecar.per_angle)
|
||||
threshold_mv = sidecar.dc_threshold_mv
|
||||
|
||||
cached_dc4 = {a: img for (a, ch), img in self._dc_cache.items() if ch == CH4_IDX}
|
||||
dlg = ManualAlignmentDialog(
|
||||
|
||||
+10
-9
@@ -278,20 +278,21 @@ def main():
|
||||
-(float(s.angles_deg[a]) - float(s.angles_deg[0])))
|
||||
for a in range(s.n_angles)))
|
||||
|
||||
# --- Open, seeding from the still-live automatic AlignmentResult --------
|
||||
# --- Open: must NOT seed from the still-live automatic AlignmentResult --
|
||||
# The automatic result's translation comes from FFT phase correlation --
|
||||
# the very thing manual mode exists to work around -- so manual mode
|
||||
# must start from identity (centroids coincide, zero shift) regardless
|
||||
# of whatever the automatic run last computed. Only a previously *saved
|
||||
# manual* alignment (sidecar) should ever seed this dialog.
|
||||
win._on_manual_alignment()
|
||||
check("dialog opened", win._manual_align_dialog is not None)
|
||||
dlg = win._manual_align_dialog
|
||||
check("mask prep needed no background worker (already DC-cached)",
|
||||
not win._job_running("manual_align_masks"))
|
||||
r = win._alignment_result # still the automatic result from the block above
|
||||
if r is not None:
|
||||
check("seeded rotation matches the automatic result",
|
||||
all(dlg._angle_params[a].rotation_deg == r.per_angle[a].rotation_deg
|
||||
for a in range(s.n_angles)))
|
||||
check("seeded shift matches the automatic result",
|
||||
all(dlg._angle_params[a].shift_mm == r.per_angle[a].shift_mm
|
||||
for a in range(s.n_angles)))
|
||||
check("no manual sidecar yet -> dialog starts at identity, not the "
|
||||
"automatic result",
|
||||
all(dlg._angle_params[a] == compute.ManualAngleParams()
|
||||
for a in range(s.n_angles)))
|
||||
|
||||
# --- Reference angle is locked -------------------------------------------
|
||||
dlg.combo_active_angle.setCurrentIndex(dlg._ref_angle_idx)
|
||||
|
||||
Reference in New Issue
Block a user