feat(metrics): add smooth and baseline-corrected spectral weights#34
feat(metrics): add smooth and baseline-corrected spectral weights#34alxndrkalinin wants to merge 17 commits into
Conversation
- smooth_spectral_weights: SG-smoothed Wiener weights for spectral PCC - estimate_noise_baseline: frequency-dependent noise floor from running low-quantile of SG-smoothed log-power spectrum - baseline_snr2_weights: SNR²-based weights against freq-dependent baseline - spectral_pcc_baseline: new spectral PCC variant using baseline approach - spectral_pcc: add smooth/sg_window/sg_polyorder params for optional SG-smoothed weights Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reviewer's GuideAdds Savitzky–Golay-smoothed spectral weights and a new baseline-corrected spectral PCC metric, plus an option for spectral_pcc to use the smoothed weights instead of the existing ones. File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Implement random binomial splitting (Rieger et al., Optics Express 2024) as an alternative to checkerboard splitting for single-image FRC/FSC. Each pixel's photon count is split into n1 ~ Binomial(n, 0.5) and n2 = n - n1, producing two noise-independent images with preserved Poisson statistics and no spatial subsampling. New `split_type="binomial"` parameter threads through calculate_frc, frc_resolution, fsc_resolution, and calculate_sectioned_fsc. Supports counts mode (raw camera data with gain/offset/readout noise correction) and poisson_thinning mode (fallback for float/deconvolved images). Multi-repeat averaging via `n_repeats` produces correlation-std and resolution-std for uncertainty quantification. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add nbins_low parameter to zero out the lowest radial frequency bins before computing spectrally-weighted PCC, suppressing DC/illumination/ background contributions that can dominate the correlation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add frc_weights() to derive per-frequency reliability weights from single-image FRC, and spectral_pcc_frcw() which uses these weights for spectral PCC computation. Export both from cubic.metrics. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds new spectral weighting options and a binomial (photon-statistics-preserving) splitting path to improve robustness/interpretability of Fourier-domain correlation metrics (Spectral PCC, FRC/FSC), including uncertainty estimation via repeated splits.
Changes:
- Introduces SG-smoothed and baseline-corrected spectral weighting utilities, plus new spectral PCC variants (baseline-based and FRC-weight-based).
- Extends FRC/FSC preprocessing to support
split_type="binomial"with optional read-noise correction andn_repeatsaggregation (std estimates). - Adds binomial split implementation + unit tests, and updates spectral correlation data structures/docs accordingly.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
cubic/metrics/bandlimited.py |
Adds smoothed/baseline/FRC-derived spectral weighting functions and extends spectral_pcc with optional smoothing + low-k exclusion. |
cubic/metrics/spectral/frc.py |
Adds binomial split support to FRC/FSC workflows, including repeat RNG handling and optional std outputs. |
cubic/image_utils.py |
Implements binomial_split (counts + poisson thinning modes) used by single-image FRC/FSC. |
cubic/metrics/spectral/analysis.py |
Extends result containers to include correlation-std and resolution-std fields. |
cubic/metrics/spectral/README.md |
Documents checkerboard vs binomial splitting and recommended usage patterns. |
cubic/metrics/__init__.py |
Exposes new bandlimited metrics helpers (frc_weights, spectral_pcc_frcw). |
tests/test_image_utils.py |
Adds unit tests covering statistical/behavioral properties of binomial_split. |
tests/metrics/spectral/test_frc.py |
Adds integration tests for binomial-split FRC/FSC behavior and repeat/stability expectations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
- Use cubic.cuda abstraction (to_same_device/asnumpy) in binomial_split (C1) - Derive frequency scaling factor from image dimensions in frc_weights (C2) - Use nanmean/nanstd for multi-repeat FRC averaging (S3) - Add xy_std/z_std keys to single-pass binomial fsc_resolution (S4) - Make internal baseline functions private (_estimate_noise_baseline, etc) (S5) - Document resolution averaging asymmetry in FRC (S7) - Add ndim, gain, readout_noise_rms validation to binomial_split (N1) - Fix "1-bit" → "1/7" in frc_weights docstring (N2) - Replace assert with raise ValueError in frc_weights (N3) - Extract duplicate binomial log message to module constant (N4) - Fix checkerboard uncertainty in README table (N6) - Fix bin_delta type annotation to int in spectral_pcc_frcw (N7) - Add __all__ to bandlimited.py (N8) - Fix SG window fallback for tiny arrays in smooth_spectral_weights (Copilot) - Add frozen_weights length validation in spectral PCC functions (Copilot) - Fix gain convention docstring in binomial_split (Copilot) - Add nbins_low to spectral_pcc docstring (Copilot) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace xp/get_array_module pattern with to_same_device in all three spectral PCC functions (CLAUDE.md: prefer cubic.cuda abstractions) - Use cubic.scipy proxy for savgol_filter and median_filter instead of direct scipy imports (CLAUDE.md: device-agnostic wrappers) - Add xy_std/z_std keys to fsc_resolution mask backend return path to match the documented return contract - Warn when n_repeats>1 is silently ignored (checkerboard or two-image) - Clarify n_repeats docstring: requires single-image + binomial mode - Add tests for spectral_pcc(smooth=True) and nbins_low parameter Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a procedural commit workflow checklist (list → group by concept → plan N commits → execute) to prevent grouping changes by workflow step. Simplify CLAUDE.md to just reference AGENTS.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add bandpass_spectral_pcc — a spectral PCC that operates within a frequency band defined by cumulative weight-mass percentiles. Uses w_i * n_i (weight × Fourier pixel count per ring) as the mass definition, with soft cosine tapers at band edges to avoid ringing. New public API: - percentile_band_taper: reusable band selection + taper helper that returns tapered weights and band diagnostics (k_low, k_high, k50, k90 in both normalized and physical units) - bandpass_spectral_pcc: full metric supporting three weight methods (simple, smooth_wiener, baseline_snr2) or pre-computed weights via frozen_weights. Returns float by default, (float, dict) with return_diagnostics=True. Also removes unused spacing parameter from spectral_pcc_frcw (FRC weights are inherently in index-frequency units). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After mean subtraction, the DC bin power is ~0, mapping to log(1e-30) ≈ -69 — an extreme outlier that distorts the first few Savitzky-Golay fitted values. Replace DC with its neighbor before smoothing in both smooth_spectral_weights and _estimate_noise_baseline. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extend spectral_pcc with a weighting parameter ("simple",
"smooth_wiener", "snr2") to select the per-bin weight law, and
taper_low for soft cosine low-frequency exclusion. The existing
smooth=True is kept as deprecated sugar mapping to "smooth_wiener".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Review of latest changes (ec58ec1, 12dc6df)Found 3 issues in the new `weighting`/`taper_low` additions to `spectral_pcc`:
All items resolved in 7386979. `weighting` now uses `Literal[...] | None`. |
Resolve conflicts: - image_utils.py: keep binomial_split + np.ndarray return type on label() - frc.py: keep feature branch refactored code, drop stale main-branch reverse-split block that was moved to _calculate_frc_single_pass Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ames - Fix taper_low ramp to use arange(1, n+1) matching percentile_band_taper (DC already excluded by bid=-1, bin 0 shouldn't be hard-zeroed) - Fix weighting default: None sentinel instead of "simple" so smooth=True backward compat works correctly - Add ValueError for unknown weighting values - Unify weight method names: "baseline_snr2" → "snr2" across both spectral_pcc and bandpass_spectral_pcc (one _WEIGHT_METHODS constant) - Add tests for weighting="snr2", unknown weighting, taper_low, and smooth=True backward compatibility - Fix bandpass_spectral_pcc test type narrowing for mypy Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Delete unused _spectral_pcc_baseline (~120 lines of dead code) - Change rng default from 42 to None in frc_weights and spectral_pcc_frcw (aligns with calculate_frc/frc_resolution) - Use Literal["checkerboard", "binomial"] for split_type parameter - Document n_repeats=3 default vs calculate_frc's n_repeats=1 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace assert with RuntimeError in _calculate_frc_single_pass for original_image1 None check (assert stripped by python -O) - Warn when n_repeats>1 is silently ignored on deprecated mask backend in fsc_resolution Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 6 tests: - frc_weights: weight range [0,1], monotone envelope after nbins_low, square image requirement - spectral_pcc_frcw: identical images ~1.0, range [-1,1], shape mismatch All tests use explicit rng=42 for reproducibility. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add smooth_spectral_weights to cubic/metrics/__init__.py (was in bandlimited.__all__ but missing from the package export) - Remove "(default)" from README splitting table headers — checkerboard is the default for calculate_frc/frc_resolution/fsc_resolution but frc_weights/spectral_pcc_frcw default to binomial Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add weighting="snr2" using global noise floor + SNR² formula (max(0, SNR-1)², matching the dynacell-validated Spectral_PCC_SNR2). Rename the existing frequency-dependent baseline variant to "snr2_baseline". Both available in spectral_pcc and bandpass_spectral_pcc. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary by Sourcery
Introduce smoothed and baseline-corrected spectral weighting options for spectral PCC metrics.
New Features:
Enhancements: