Fix alignment rotation pivot and sign convention
Rotation was pivoting on each angle's raw scan-window bbox center, which ties alignment to wherever the window happened to sit in microscope/global XY space rather than to the sample itself, leaving a residual orbital drift between angles. Pivot on each angle's own CH4-threshold mask centroid instead (its own sample footprint), for both the automatic phase- correlation path and the manual dialog's Auto De-rotate/live preview. Also negate the rotation used everywhere (_theta_deg): the GR stage's reported angle increases in the opposite rotational sense from this module's CCW math convention, so de-rotating by the raw angles_deg delta was making misalignment worse rather than better. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -240,6 +240,35 @@ def main():
|
||||
print("\nmanual alignment (Fusion)")
|
||||
check("manual alignment action enabled", win._manual_align_act.isEnabled())
|
||||
|
||||
# --- Alignment pivot is the mask centroid, not the raw bbox center -----
|
||||
corner_mask = np.zeros(s.image_shape(0), dtype=bool)
|
||||
corner_mask[0, 0] = True
|
||||
expected_corner = (float(s.x_axis_mm(0)[0]), float(s.y_positions_mm(0)[0]))
|
||||
centroid = compute._mask_centroid_mm(s, 0, corner_mask)
|
||||
check("mask centroid of a single corner pixel is that corner exactly",
|
||||
np.allclose(centroid, expected_corner), f"{centroid} vs {expected_corner}")
|
||||
bbox_center = compute._bbox_center_mm(s, 0)
|
||||
check("mask centroid differs from the raw scan-window bbox center",
|
||||
not np.allclose(centroid, bbox_center),
|
||||
f"centroid {centroid} vs bbox center {bbox_center}")
|
||||
|
||||
# A threshold that would yield an *empty* mask if recomputed from scratch
|
||||
# (real DC values never reach 999 mV) — so this only matches expected_corner
|
||||
# if compute_pivot_points_mm actually reused the pre-computed masks dict
|
||||
# instead of silently recomputing (and falling back to the bbox center).
|
||||
reused_pivot = compute.compute_pivot_points_mm(
|
||||
s, 999.0, masks={0: corner_mask.astype(np.float32)})[0]
|
||||
check("compute_pivot_points_mm reuses a pre-computed masks dict",
|
||||
np.allclose(reused_pivot, expected_corner))
|
||||
|
||||
# --- Rotation sign convention: negative of the raw angles_deg delta ----
|
||||
check("_theta_deg negates the raw angles_deg delta (GR stage's positive "
|
||||
"angle is the opposite rotational sense from this module's CCW "
|
||||
"math convention)",
|
||||
all(np.isclose(compute._theta_deg(s, a, 0),
|
||||
-(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 --------
|
||||
win._on_manual_alignment()
|
||||
check("dialog opened", win._manual_align_dialog is not None)
|
||||
|
||||
Reference in New Issue
Block a user