implemented background referencing and subtraction if from FFT window and B-scan. Continous ref calculation can be toggled
This commit is contained in:
@ -1704,7 +1704,10 @@ def main():
|
||||
ymax_slider = None
|
||||
contrast_slider = None
|
||||
calib_enabled = False
|
||||
bg_compute_enabled = True
|
||||
bg_subtract_enabled = False
|
||||
fft_bg_subtract_enabled = False
|
||||
bg_spec_cache: Optional[np.ndarray] = None
|
||||
peak_calibrate_mode = bool(getattr(args, "calibrate", False))
|
||||
peak_search_enabled = bool(getattr(args, "peak_search", False))
|
||||
peak_ref_window = float(getattr(args, "peak_ref_window", 1.0))
|
||||
@ -1814,15 +1817,19 @@ def main():
|
||||
return _normalize_by_calib(raw, calib, norm_type=norm_type)
|
||||
|
||||
def _sync_checkbox_states():
|
||||
nonlocal calib_enabled, bg_subtract_enabled, peak_search_enabled, current_sweep_norm
|
||||
nonlocal calib_enabled, bg_compute_enabled, bg_subtract_enabled, fft_bg_subtract_enabled, peak_search_enabled, current_sweep_norm
|
||||
try:
|
||||
states = cb.get_status() if cb is not None else ()
|
||||
calib_enabled = bool(states[0]) if len(states) > 0 else False
|
||||
bg_subtract_enabled = bool(states[1]) if len(states) > 1 else False
|
||||
peak_search_enabled = bool(states[2]) if len(states) > 2 else False
|
||||
bg_compute_enabled = bool(states[1]) if len(states) > 1 else True
|
||||
bg_subtract_enabled = bool(states[2]) if len(states) > 2 else False
|
||||
fft_bg_subtract_enabled = bool(states[3]) if len(states) > 3 else False
|
||||
peak_search_enabled = bool(states[4]) if len(states) > 4 else False
|
||||
except Exception:
|
||||
calib_enabled = False
|
||||
bg_compute_enabled = True
|
||||
bg_subtract_enabled = False
|
||||
fft_bg_subtract_enabled = False
|
||||
peak_search_enabled = False
|
||||
if calib_enabled and current_sweep_raw is not None and last_calib_sweep is not None:
|
||||
current_sweep_norm = _normalize_sweep(current_sweep_raw, last_calib_sweep)
|
||||
@ -1834,14 +1841,14 @@ def main():
|
||||
ax_smin = fig.add_axes([0.92, 0.55, 0.02, 0.35])
|
||||
ax_smax = fig.add_axes([0.95, 0.55, 0.02, 0.35])
|
||||
ax_sctr = fig.add_axes([0.98, 0.55, 0.02, 0.35])
|
||||
ax_cb = fig.add_axes([0.90, 0.37, 0.10, 0.17])
|
||||
ax_cb = fig.add_axes([0.90, 0.30, 0.10, 0.24])
|
||||
ymin_slider = Slider(ax_smin, "R min", 0.0, 1.0, valinit=0.0, orientation="vertical")
|
||||
ymax_slider = Slider(ax_smax, "R max", 0.0, 1.0, valinit=1.0, orientation="vertical")
|
||||
contrast_slider = Slider(ax_sctr, "Int max", 0, 100, valinit=100, valstep=1, orientation="vertical")
|
||||
cb = CheckButtons(
|
||||
ax_cb,
|
||||
["нормировка", "вычет фона", "поиск пиков"],
|
||||
[False, False, peak_search_enabled],
|
||||
["нормировка", "расчет фона", "вычет фона", "FFT вычет фона", "поиск пиков"],
|
||||
[False, True, False, False, peak_search_enabled],
|
||||
)
|
||||
|
||||
def _on_ylim_change(_val):
|
||||
@ -2155,13 +2162,19 @@ def main():
|
||||
mean_spec = np.nan_to_num(mean_spec, nan=0.0)
|
||||
return disp_fft - mean_spec[:, None]
|
||||
|
||||
def _visible_bg_fft(disp_fft: np.ndarray) -> Optional[np.ndarray]:
|
||||
def _visible_bg_fft(disp_fft: np.ndarray, force: bool = False) -> Optional[np.ndarray]:
|
||||
"""Оценка фона по медиане в текущем видимом по времени окне B-scan."""
|
||||
if not bg_subtract_enabled or disp_fft.size == 0:
|
||||
nonlocal bg_spec_cache
|
||||
need_bg = bool(bg_subtract_enabled or force)
|
||||
if (not need_bg) or disp_fft.size == 0:
|
||||
return None
|
||||
ny, nx = disp_fft.shape
|
||||
if ny <= 0 or nx <= 0:
|
||||
return None
|
||||
return bg_spec_cache
|
||||
if bg_spec_cache is not None and bg_spec_cache.size != ny:
|
||||
bg_spec_cache = None
|
||||
if not bg_compute_enabled:
|
||||
return bg_spec_cache
|
||||
try:
|
||||
x0, x1 = ax_spec.get_xlim()
|
||||
except Exception:
|
||||
@ -2173,14 +2186,15 @@ def main():
|
||||
ix1 = ix0
|
||||
window = disp_fft[:, ix0 : ix1 + 1]
|
||||
if window.size == 0:
|
||||
return None
|
||||
return bg_spec_cache
|
||||
try:
|
||||
bg_spec = np.nanmedian(window, axis=1)
|
||||
except Exception:
|
||||
return None
|
||||
return bg_spec_cache
|
||||
if not np.any(np.isfinite(bg_spec)):
|
||||
return None
|
||||
return np.nan_to_num(bg_spec, nan=0.0).astype(np.float32, copy=False)
|
||||
return bg_spec_cache
|
||||
bg_spec_cache = np.nan_to_num(bg_spec, nan=0.0).astype(np.float32, copy=False)
|
||||
return bg_spec_cache
|
||||
|
||||
def make_display_ring_fft():
|
||||
if ring_fft is None:
|
||||
@ -2211,6 +2225,14 @@ def main():
|
||||
channel_text,
|
||||
)
|
||||
changed = drain_queue() > 0
|
||||
bg_fft_for_line = None
|
||||
if fft_bg_subtract_enabled and ring_fft is not None:
|
||||
try:
|
||||
disp_fft_for_bg = make_display_ring_fft()
|
||||
disp_fft_for_bg = _subtract_recent_mean_fft(disp_fft_for_bg)
|
||||
bg_fft_for_line = _visible_bg_fft(disp_fft_for_bg, force=True)
|
||||
except Exception:
|
||||
bg_fft_for_line = None
|
||||
|
||||
# Обновление линии последнего свипа
|
||||
if current_sweep_raw is not None:
|
||||
@ -2258,6 +2280,19 @@ def main():
|
||||
if fft_vals.size > xs_fft.size:
|
||||
fft_vals = fft_vals[: xs_fft.size]
|
||||
xs_fft = xs_fft[: fft_vals.size]
|
||||
if fft_bg_subtract_enabled and bg_fft_for_line is not None:
|
||||
n_bg = int(min(fft_vals.size, bg_fft_for_line.size))
|
||||
if n_bg > 0:
|
||||
num = np.maximum(
|
||||
np.power(10.0, np.asarray(fft_vals[:n_bg], dtype=np.float64) / 20.0),
|
||||
0.0,
|
||||
)
|
||||
den = np.maximum(np.asarray(bg_fft_for_line[:n_bg], dtype=np.float64), 0.0)
|
||||
fft_vals = (20.0 * np.log10((num + 1e-9) / (den + 1e-9))).astype(
|
||||
np.float32,
|
||||
copy=False,
|
||||
)
|
||||
xs_fft = xs_fft[:n_bg]
|
||||
fft_line_obj.set_data(xs_fft, fft_vals)
|
||||
|
||||
finite_fft = np.isfinite(xs_fft) & np.isfinite(fft_vals)
|
||||
@ -2291,7 +2326,12 @@ def main():
|
||||
box_obj.set_visible(False)
|
||||
|
||||
# Авто-диапазон по Y для спектра
|
||||
if np.any(finite_fft):
|
||||
if fft_bg_subtract_enabled and bg_fft_for_line is not None:
|
||||
finite_x = xs_fft[np.isfinite(xs_fft)]
|
||||
if finite_x.size > 0:
|
||||
ax_fft.set_xlim(float(np.min(finite_x)), float(np.max(finite_x)))
|
||||
ax_fft.set_ylim(-10.0, 30.0)
|
||||
elif np.any(finite_fft):
|
||||
finite_x = xs_fft[finite_fft]
|
||||
if finite_x.size > 0:
|
||||
ax_fft.set_xlim(float(np.min(finite_x)), float(np.max(finite_x)))
|
||||
@ -2633,7 +2673,9 @@ def run_pyqtgraph(args):
|
||||
|
||||
# Правая панель настроек внутри основного окна.
|
||||
calib_cb = QtWidgets.QCheckBox("нормировка")
|
||||
bg_compute_cb = QtWidgets.QCheckBox("расчет фона")
|
||||
bg_subtract_cb = QtWidgets.QCheckBox("вычет фона")
|
||||
fft_bg_subtract_cb = QtWidgets.QCheckBox("FFT вычет фона")
|
||||
peak_search_cb = QtWidgets.QCheckBox("поиск пиков")
|
||||
try:
|
||||
settings_title = QtWidgets.QLabel("Настройки")
|
||||
@ -2641,7 +2683,9 @@ def run_pyqtgraph(args):
|
||||
except Exception:
|
||||
pass
|
||||
settings_layout.addWidget(calib_cb)
|
||||
settings_layout.addWidget(bg_compute_cb)
|
||||
settings_layout.addWidget(bg_subtract_cb)
|
||||
settings_layout.addWidget(fft_bg_subtract_cb)
|
||||
settings_layout.addWidget(peak_search_cb)
|
||||
calib_window = None
|
||||
|
||||
@ -2673,7 +2717,10 @@ def run_pyqtgraph(args):
|
||||
spec_clip = _parse_spec_clip(getattr(args, "spec_clip", None))
|
||||
spec_mean_sec = float(getattr(args, "spec_mean_sec", 0.0))
|
||||
calib_enabled = False
|
||||
bg_compute_enabled = True
|
||||
bg_subtract_enabled = False
|
||||
fft_bg_subtract_enabled = False
|
||||
bg_spec_cache: Optional[np.ndarray] = None
|
||||
current_peak_width: Optional[float] = None
|
||||
current_peak_amplitude: Optional[float] = None
|
||||
peak_candidates: List[Dict[str, float]] = []
|
||||
@ -2721,14 +2768,44 @@ def run_pyqtgraph(args):
|
||||
bg_subtract_enabled = False
|
||||
plot_dirty = True
|
||||
|
||||
def _set_bg_compute_enabled():
|
||||
nonlocal bg_compute_enabled, plot_dirty
|
||||
try:
|
||||
bg_compute_enabled = bool(bg_compute_cb.isChecked())
|
||||
except Exception:
|
||||
bg_compute_enabled = False
|
||||
plot_dirty = True
|
||||
|
||||
def _set_fft_bg_subtract_enabled():
|
||||
nonlocal fft_bg_subtract_enabled, plot_dirty
|
||||
try:
|
||||
fft_bg_subtract_enabled = bool(fft_bg_subtract_cb.isChecked())
|
||||
except Exception:
|
||||
fft_bg_subtract_enabled = False
|
||||
plot_dirty = True
|
||||
|
||||
try:
|
||||
bg_compute_cb.setChecked(True)
|
||||
except Exception:
|
||||
pass
|
||||
_set_bg_compute_enabled()
|
||||
|
||||
try:
|
||||
calib_cb.stateChanged.connect(lambda _v: _set_calib_enabled())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
bg_compute_cb.stateChanged.connect(lambda _v: _set_bg_compute_enabled())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
bg_subtract_cb.stateChanged.connect(lambda _v: _set_bg_subtract_enabled())
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
fft_bg_subtract_cb.stateChanged.connect(lambda _v: _set_fft_bg_subtract_enabled())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _refresh_peak_params_label(peaks: List[Dict[str, float]]):
|
||||
if peak_params_label is None:
|
||||
@ -2959,13 +3036,19 @@ def run_pyqtgraph(args):
|
||||
return None
|
||||
return (vmin, vmax)
|
||||
|
||||
def _visible_bg_fft(disp_fft: np.ndarray) -> Optional[np.ndarray]:
|
||||
def _visible_bg_fft(disp_fft: np.ndarray, force: bool = False) -> Optional[np.ndarray]:
|
||||
"""Оценка фона по медиане в текущем видимом по времени окне B-scan."""
|
||||
if not bg_subtract_enabled or disp_fft.size == 0:
|
||||
nonlocal bg_spec_cache
|
||||
need_bg = bool(bg_subtract_enabled or force)
|
||||
if (not need_bg) or disp_fft.size == 0:
|
||||
return None
|
||||
ny, nx = disp_fft.shape
|
||||
if ny <= 0 or nx <= 0:
|
||||
return None
|
||||
return bg_spec_cache
|
||||
if bg_spec_cache is not None and bg_spec_cache.size != ny:
|
||||
bg_spec_cache = None
|
||||
if not bg_compute_enabled:
|
||||
return bg_spec_cache
|
||||
try:
|
||||
(x0, x1), _ = p_spec.viewRange()
|
||||
except Exception:
|
||||
@ -2977,14 +3060,15 @@ def run_pyqtgraph(args):
|
||||
ix1 = ix0
|
||||
window = disp_fft[:, ix0 : ix1 + 1]
|
||||
if window.size == 0:
|
||||
return None
|
||||
return bg_spec_cache
|
||||
try:
|
||||
bg_spec = np.nanmedian(window, axis=1)
|
||||
except Exception:
|
||||
return None
|
||||
return bg_spec_cache
|
||||
if not np.any(np.isfinite(bg_spec)):
|
||||
return None
|
||||
return np.nan_to_num(bg_spec, nan=0.0).astype(np.float32, copy=False)
|
||||
return bg_spec_cache
|
||||
bg_spec_cache = np.nan_to_num(bg_spec, nan=0.0).astype(np.float32, copy=False)
|
||||
return bg_spec_cache
|
||||
|
||||
def push_sweep(s: np.ndarray, freqs: Optional[np.ndarray] = None):
|
||||
nonlocal ring, ring_time, head, ring_fft, y_min_fft, y_max_fft, current_fft_db
|
||||
@ -3075,6 +3159,13 @@ def run_pyqtgraph(args):
|
||||
return
|
||||
changed = drain_queue() > 0
|
||||
redraw_needed = changed or plot_dirty
|
||||
bg_fft_for_line = None
|
||||
if redraw_needed and fft_bg_subtract_enabled and ring_fft is not None:
|
||||
try:
|
||||
disp_fft_for_bg = ring_fft if head == 0 else np.roll(ring_fft, -head, axis=0)
|
||||
bg_fft_for_line = _visible_bg_fft(disp_fft_for_bg.T, force=True)
|
||||
except Exception:
|
||||
bg_fft_for_line = None
|
||||
if redraw_needed and current_sweep_raw is not None and x_shared is not None:
|
||||
if current_freqs is not None and current_freqs.size == current_sweep_raw.size:
|
||||
xs = current_freqs
|
||||
@ -3124,6 +3215,19 @@ def run_pyqtgraph(args):
|
||||
if fft_vals.size > xs_fft.size:
|
||||
fft_vals = fft_vals[: xs_fft.size]
|
||||
xs_fft = xs_fft[: fft_vals.size]
|
||||
if fft_bg_subtract_enabled and bg_fft_for_line is not None:
|
||||
n_bg = int(min(fft_vals.size, bg_fft_for_line.size))
|
||||
if n_bg > 0:
|
||||
num = np.maximum(
|
||||
np.power(10.0, np.asarray(fft_vals[:n_bg], dtype=np.float64) / 20.0),
|
||||
0.0,
|
||||
)
|
||||
den = np.maximum(np.asarray(bg_fft_for_line[:n_bg], dtype=np.float64), 0.0)
|
||||
fft_vals = (20.0 * np.log10((num + 1e-9) / (den + 1e-9))).astype(
|
||||
np.float32,
|
||||
copy=False,
|
||||
)
|
||||
xs_fft = xs_fft[:n_bg]
|
||||
curve_fft.setData(xs_fft, fft_vals)
|
||||
finite_x = xs_fft[np.isfinite(xs_fft)]
|
||||
if finite_x.size > 0:
|
||||
@ -3159,13 +3263,16 @@ def run_pyqtgraph(args):
|
||||
for box in fft_peak_boxes:
|
||||
box.setVisible(False)
|
||||
|
||||
finite_y = y_for_range[np.isfinite(y_for_range)]
|
||||
if finite_y.size > 0:
|
||||
y0 = float(np.min(finite_y))
|
||||
y1 = float(np.max(finite_y))
|
||||
if y1 <= y0:
|
||||
y1 = y0 + 1e-3
|
||||
p_fft.setYRange(y0, y1, padding=0)
|
||||
if fft_bg_subtract_enabled and bg_fft_for_line is not None:
|
||||
p_fft.setYRange(-10.0, 30.0, padding=0)
|
||||
else:
|
||||
finite_y = y_for_range[np.isfinite(y_for_range)]
|
||||
if finite_y.size > 0:
|
||||
y0 = float(np.min(finite_y))
|
||||
y1 = float(np.max(finite_y))
|
||||
if y1 <= y0:
|
||||
y1 = y0 + 1e-3
|
||||
p_fft.setYRange(y0, y1, padding=0)
|
||||
if peak_calibrate_mode:
|
||||
markers = _find_peak_width_markers(xs_fft, fft_vals)
|
||||
if markers is not None:
|
||||
|
||||
Reference in New Issue
Block a user