Add manual angle alignment mode with overlay, keyboard nudge, and save/clear
Automatic alignment's phase-correlation translation search is unreliable, so add a Fusion -> Manual Alignment dialog: every angle's CH4 threshold mask overlaid at once with distinct colors/opacity, a selector for the active angle, arrow keys to nudge translation and Q/E to nudge rotation, an Auto De-rotate button that snaps to the known scan angle, and Save/Clear controls backed by a JSON sidecar that's restored automatically on reload. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -304,3 +304,45 @@ class AngleAlignmentWorker(QObject):
|
||||
self.finished.emit(result, "")
|
||||
except Exception as exc:
|
||||
self.finished.emit(None, str(exc))
|
||||
|
||||
|
||||
class Ch4MaskWorker(QObject):
|
||||
"""Fetches each requested angle's CH4 (Bias B) DC image in mV, for
|
||||
ManualAlignmentDialog's initial threshold-mask overlay.
|
||||
|
||||
Reuses dc_image_mv, which prefers a stored v5/v7 cache over recomputing
|
||||
from raw waveforms, so this only does real work for a file that hasn't
|
||||
gone through the v7 "Convert" batch step and for angles the main
|
||||
window's own DcPrecomputeWorker (which runs automatically right after
|
||||
every file load) hasn't reached yet. In the common case — the user opens
|
||||
Fusion -> Manual Alignment after DC precompute has already finished —
|
||||
*angle_indices* is empty and this worker is never even constructed (see
|
||||
ManualAlignmentDialog._start_mask_prep).
|
||||
"""
|
||||
angle_done = pyqtSignal(int, np.ndarray) # angle_idx, dc4_mv
|
||||
finished = pyqtSignal()
|
||||
error = pyqtSignal(str)
|
||||
|
||||
def __init__(self, sras: SrasFile, angle_indices: list[int]):
|
||||
super().__init__()
|
||||
self._sras = sras
|
||||
self._angles = angle_indices
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
n_workers, budget = compute.plan_angle_level(self._sras)
|
||||
pool = ThreadPoolExecutor(max_workers=n_workers)
|
||||
try:
|
||||
futures = {
|
||||
pool.submit(dc_image_mv, self._sras, a, CH4_IDX,
|
||||
max_workers=1, budget=budget): a
|
||||
for a in self._angles
|
||||
}
|
||||
for fut in as_completed(futures):
|
||||
a = futures[fut]
|
||||
self.angle_done.emit(a, fut.result())
|
||||
finally:
|
||||
pool.shutdown(wait=True)
|
||||
self.finished.emit()
|
||||
except Exception as exc:
|
||||
self.error.emit(str(exc))
|
||||
|
||||
Reference in New Issue
Block a user