Add batch export of DC/RF/Velocity map images
Add Export -> Batch Export Images..., which renders and saves one PNG per angle for whichever of CH1 (RF), CH3 (DC-A), CH4 (DC-B), and Velocity the user selects, for the currently open file. Each channel gets its own fixed min/max colorbar range, entered once in the dialog and held constant across every exported angle, so the resulting images are directly comparable to each other instead of each auto-scaling to its own data (today's live-view default). BatchExportDialog (sras_viewer.py) collects the output folder, file prefix, and per-channel checkbox + range, seeded from whatever's already cached (session DC/FFT caches, or the file's own v7 cache blocks) so opening the dialog never triggers a fresh compute. Export runs on a background thread via a new BatchExportWorker (sras_workers.py), which computes each angle's channel image with the existing dc_image_mv/compute_rf_image helpers (reusing one FFT per angle for both RF and Velocity) and renders it with a headless matplotlib Agg canvas. Also includes several small correctness/robustness fixes that were already staged in the working tree: an int16-vs-float32 accumulator mismatch in sras_average.py's row averaging, a DC4-mask cache reuse and atomic sidecar write in sras_compute.py, a dc3/dc4 pairing guard in sras_format.py's v7 cache writer, and matching updates to the tools/ test fixtures. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+9
-4
@@ -54,8 +54,13 @@ def average_rows(block: np.ndarray, n: int, discard_remainder: bool) -> np.ndarr
|
||||
"""Average every N frames of one angle's (n_rows, n_ch, n_frames, spf)
|
||||
block. Returns int16 of shape (n_rows, n_ch, n_out, spf).
|
||||
|
||||
Averaging is done in float32 and rounded on cast, matching numpy's mean
|
||||
followed by an int16 cast in the original implementation.
|
||||
Cast to native int16 (not float32) before calling .mean(): numpy's mean()
|
||||
uses a float64 accumulator by default for integer input, matching the
|
||||
original implementation exactly (which kept the whole file as int16 and
|
||||
called .mean() directly). A float32 cast here would use a float32
|
||||
accumulator instead — for large group sizes that can round the sum
|
||||
differently than float64 and, after the int16 cast below, occasionally
|
||||
land on a value 1 ADC count away from the original tool's output.
|
||||
"""
|
||||
n_frames = block.shape[2]
|
||||
n_full = n_frames // n
|
||||
@@ -63,11 +68,11 @@ def average_rows(block: np.ndarray, n: int, discard_remainder: bool) -> np.ndarr
|
||||
|
||||
parts = []
|
||||
if n_full:
|
||||
full = block[:, :, :n_full * n, :].astype(np.float32)
|
||||
full = block[:, :, :n_full * n, :].astype(np.int16)
|
||||
full = full.reshape(block.shape[0], block.shape[1], n_full, n, block.shape[3])
|
||||
parts.append(full.mean(axis=3).astype(np.int16))
|
||||
if remainder and not discard_remainder:
|
||||
tail = block[:, :, n_full * n:, :].astype(np.float32)
|
||||
tail = block[:, :, n_full * n:, :].astype(np.int16)
|
||||
parts.append(tail.mean(axis=2, keepdims=True).astype(np.int16))
|
||||
|
||||
if not parts:
|
||||
|
||||
Reference in New Issue
Block a user