Skip to content

FFT physics review: window-envelope compensation, odd-size soft-border centring#29

Merged
jacobson30-bot merged 1 commit into
mainfrom
fft-physics-review
Jun 11, 2026
Merged

FFT physics review: window-envelope compensation, odd-size soft-border centring#29
jacobson30-bot merged 1 commit into
mainfrom
fft-physics-review

Conversation

@jacobson30-bot

Copy link
Copy Markdown
Contributor

Summary

Dedicated physics review of the FFT stack (kernels + viewer axis handling).

Findings

[CRITICAL] processing/filters.py fourier_filter — fixed
What: data windowed before the FFT (GUI default Hann) but never divided back out after the inverse.
Why: the window envelope stayed baked into every radial-FFT-filter result — output vignetted toward the mean at the borders (measured: edge std 0.03 vs centre 0.77 on unit-variance noise). Existing near-identity tests used cutoff exactly 1.0/0.0, which early-return before windowing.
Fix: divide the window back out post-inverse (the approach fft_soft_border always used), gain capped at 20×; window="none" pinned bit-identical.

[INFO] fft_soft_border — fixed
Radial mask centre was half a pixel off the fftshift DC bin for odd sizes (index arithmetic at N/2.0). Now uses the fftfreq cycles/pixel convention shared with fourier_filter; even sizes pinned bit-identical to the old mask.

Disproved by test: the soft-border "half-axis ellipse" suspicion — Δx/cx = 2f makes it exactly the cycles/pixel convention; both stripe orientations filter identically on a 64×256 image.

Verified clean: fft_magnitude (axes/coherent gain/ROI mean), inverse_fft (raw transform, conjugate-exact masks, imag-residual reporting), mains q-axes, viewer q-axis + radial profile (per-axis physical d=), fft_points (honest unit labels), line_periodicity (physical rfftfreq, gain-normalised power).

Replay note

Saved states using a window now replay with the corrected (envelope-free) output — same correctness-over-replay precedent as the gradient-slope and qPlus-setpoint fixes.

Test plan

  • 5 new tests: spatial flatness of near-identity low-pass and uniform high-pass response (fail before the fix), window="none" bit-equality, odd-size DC preservation, even-size mask-rewrite bit-equality.
  • Full suite green locally: 2371 passed, 3 skipped.

🤖 Generated with Claude Code

… for odd sizes

FFT physics review pass (2026-06-12), pinned by new tests in
tests/test_fft_physics.py:

- fourier_filter windowed the data before the transform (GUI default:
  Hann) but never divided the window back out after the inverse, so every
  radial FFT filter result carried the window envelope — vignetted toward
  the mean at the borders (edge std 0.03 vs centre 0.77 on unit-variance
  noise). The pre-existing near-identity tests used cutoff exactly 1.0/0.0,
  which early-return before any windowing and could not see it.
  fft_soft_border has always compensated its taper; fourier_filter now does
  the same, with the compensation gain capped at 20x so near-zero edge
  weights cannot amplify residual spectral leakage into spikes. The
  window="none" path is bit-identical to before (pinned). Note: replaying
  saved states that used a window now produces the corrected (envelope-free)
  output — same correctness-over-replay precedent as the gradient-slope and
  qPlus-setpoint fixes.

- fft_soft_border's radial mask used index arithmetic centred at N/2.0,
  which is identical to the fftfreq convention for even sizes (pinned by a
  bit-equality test) but sat half a pixel off the fftshift DC bin for odd
  ones — a tiny low-pass could miss DC and shift the image mean. The mask
  now uses the same fftfreq-based cycles/pixel convention as fourier_filter.

Verified clean in the same pass: fft_magnitude (axes, coherent gain, ROI
mean handling), inverse_fft (raw unwindowed transform, conjugate-exact
masks, imaginary-residual reporting), mains q-axes, the FFT viewer's q-axis
construction and radial profile (per-axis physical d=, isotropic q-map),
measurements/fft_points (explicit d= with honest cycles/pixel vs
cycles/unit labels), and line_periodicity (rfftfreq with physical spacing,
window-gain-normalised power, DC excluded). One suspicion was disproved by
test: the soft-border half-axis normalisation is exactly equivalent to
cycles/pixel (Δx/cx = 2f), not elliptical.

Co-Authored-By: Claude Fable 5 <[email protected]>
@jacobson30-bot jacobson30-bot merged commit 4e6c0e9 into main Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant