Quick start#
Estimate the CNR of a noisy 1-D profile in a few lines:
import numpy as np
from fft_cnr import fft_cnr
# Simulate a 1-D intensity profile (e.g., from a microscopy line scan)
# with additive detector noise
rng = np.random.default_rng(0)
x = np.arange(256, dtype=float)
signal = 10.0 * np.exp(-0.5 * ((x - 127) / 20) ** 2)
noisy = signal + rng.normal(0, 1.0, 256)
result = fft_cnr(noisy)
print(f"CNR: {result.cnr:.1f}")
print(f"CNR 95%CI: ({result.cnr_ci95[0]:.1f}, {result.cnr_ci95[1]:.1f})")
print(f"Amplitude: {result.amplitude:.2f}")
print(f"Noise RMS: {result.noise_rms:.3f}")
The output is deterministic given the seed, so this snippet reproduces the same numbers on each run:
CNR: 9.8
CNR 95%CI: (6.9, 12.7)
Amplitude: 9.74
Noise RMS: 0.991
Amplitude estimation#
By default fft_cnr reads the peak of the smoothed profile above an
edge-estimated baseline, which works for any profile shape. Two alternatives are
available through fft_cnr():
# With a known noise-free template (matched filter, most precise)
result = fft_cnr(noisy, template=signal)
# With a generalized Gaussian fit (when shape parameters are also wanted)
result = fft_cnr(noisy, fit_model="generalized_gaussian")
print(result.diagnostics["gaussian_fit_params"])
Noise model detection#
Setting estimate_noise_model=True tests whether the noise grows with the
signal (shot noise) and attaches a NoiseModel to the result:
result = fft_cnr(noisy, estimate_noise_model=True)
model = result.noise_model
if model.signal_dependent:
# Noise grows with the signal; result.cnr overestimates the peak SNR.
print(f"Peak SNR: {model.peak_snr(result.amplitude):.1f}")
For the full set of parameters, return fields, accuracy characterization, and the treatment of structured background, see the README and the API reference.