Replace the two Fusion alignment actions with a three-step wizard
Alignment was two disconnected menu actions. `Angle Alignment` ran the
registration with ref_angle_idx hard-coded to 0, no exposed parameters and
no way to retry; `Manual Alignment...` was a separate dialog that
deliberately refused to inherit the automatic result, so a bad fit meant
starting over by hand. Neither told you whether the alignment was any
good, and neither produced anything durable beyond an in-memory result.
Fusion -> Alignment Wizard... now covers all of it in three steps:
1. Correlate. Reference angle, DC threshold, source, rotation seed and
sign, search window, coarse step and fine grid are all on the page,
and Run/Re-run is repeatable. Angles start pre-rotated from the
stage angles in the file, so the page is informative before any
correlation runs. The verdict is a picture: every angle's DC mask
reprojected onto the shared canvas and summed, coloured by how many
angles cover each pixel, so a good alignment reads as one saturated
plateau and a bad one as a fringe of low-count halos. A per-angle
fit table flags angles that did not register or that disagree with
their stage angle by more than a degree.
2. Crop. An axis-aligned rectangle on that canvas, with numeric
canvas-pixel boxes synced both ways and a live size estimate.
"Fit to full overlap" uses a largest-rectangle sweep rather than a
bounding box: the overlap region of several rotated scans is roughly
a disc, whose bounding box has corners no angle covers.
3. Save. Writes the aligned, cropped stack to a new .sras.
Because the wizard replaces both actions it absorbs the old dialog's
by-eye nudge editor — without it, a scan the search cannot fit would
have no fallback at all. ManualAlignmentDialog is therefore deleted
rather than left orphaned, and its tests move to the wizard.
Two bugs found while driving it end to end and fixed here: QSpinBox
setRange clamps and emits valueChanged, which committed a 1x1 crop
before the default preset could run; and the mm round trip returns an
exact pixel boundary as 11.000000000000002, so a bare ceil() added a
spurious column on every rectangle edit.
Verified: 103 pass, the 6 test_stored_cache failures are pre-existing on
main, and tools/check_equivalence.py is byte-identical to main.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -173,6 +173,41 @@ sum over angles a of: n_rows[a] × 3 × n_frames[a] × samples_per_frame × byte
|
||||
> Table and check `file_size` against the running total before reshaping —
|
||||
> a fixed `(n_angles, n_rows, ...)` reshape (as in pre-v6 readers) will not
|
||||
> work since row/frame counts are no longer uniform across angles.
|
||||
>
|
||||
> A consequence worth stating explicitly: because a short file opens
|
||||
> *successfully* as a scan with fewer angles, a truncated file is not
|
||||
> detectably broken. Anything that writes a .sras must therefore stage to a
|
||||
> temporary name and rename on success — the viewer's aligned export writes
|
||||
> `<name>.part` and `os.replace`s it — or an interrupted write leaves behind
|
||||
> something that loads without complaint and silently has the wrong angle count.
|
||||
|
||||
---
|
||||
|
||||
## Files written by the viewer's Alignment Wizard
|
||||
|
||||
The acquisition app is not the only producer of this format. The viewer's
|
||||
`Fusion → Alignment Wizard…` writes a **v6** file holding the aligned, cropped
|
||||
stack, with these properties:
|
||||
|
||||
* Every angle shares one grid — the cropped alignment canvas — so the
|
||||
Per-Angle Geometry Table is `n_angles` identical records and the ragged Row
|
||||
Table is `n_angles` identical spans. The raggedness v6 exists for is still
|
||||
*expressible*, just unused, so any v6 reader works unchanged.
|
||||
* `x_delta` is the reference angle's own pitch, which is exactly
|
||||
`velocity_mm_s / laser_freq_hz`, so the derived X axis stays consistent with
|
||||
the header.
|
||||
* The `*_nominal` header fields describe the crop. Uniquely for these files they
|
||||
coincide with the actual per-angle geometry, since after alignment every angle
|
||||
really does scan the same box.
|
||||
* The **Angle Table is unchanged**. Alignment removes the sample's spatial
|
||||
rotation, not the acoustic propagation direction each angle measured — that
|
||||
direction is the point of a multi-angle scan, so it is preserved.
|
||||
* Output pixels with no corresponding source pixel (the canvas corners a rotated
|
||||
scan cannot reach) hold the per-channel ADC code nearest **0 mV**, not zero.
|
||||
Zero ADC decodes to roughly +100 mV on real calibration and would read as
|
||||
signal.
|
||||
* No Cache Tail is written: any cached DC/FFT is indexed by the source's grid
|
||||
and would be meaningless on the new one.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user