Task 3 of 5

Trade the Ringing for a Softer Edge

The ringing came from the cliff. A rectangle in frequency has a discontinuity, and a discontinuity in one domain is a long, slowly-decaying tail in the other. Round the cliff off and the tail goes with it.

The most satisfying way to round it off is a Gaussian, because the Fourier transform of a Gaussian is a Gaussian. The impulse response of this filter is therefore a Gaussian too — strictly positive, with no negative lobes anywhere. A filter that never subtracts cannot overshoot. The ringing does not shrink here; it vanishes, from about 9.2% to about 0.00%.

You pay for it twice. A Gaussian never reaches zero, so it never removes anything completely: at σ = 12 the bin at k = 26 is not deleted, it is scaled by exp(−26²/(2·12²)) ≈ 0.096. And it starts attenuating immediately — the harmonic at k = 18, which the brick wall passed at full strength, comes through at 0.325. The edge that swung in 7 samples now takes 13. That is the whole trade, and it is the same one Windowing & Spectral Leakage makes from the other end, one module back: a smooth taper buys clean tails at the cost of sharpness.

Convolution & Filters convolved a Gaussian blur directly, tap by tap. This is that same filter, arriving from the other side.

Goal: finish rollOff so every bin is scaled by exp(−dist²/(2σ²)), where dist is the folded distance from DC — and watch the overshoot collapse.

Requirements

Hint 1 — the response, then the scaling

Compute h once, before you look at which plane you are on. Every thread of a bin must agree on it:

const k = this.thread.x;
const dist = Math.min(k, this.constants.n - k);
const s = this.constants.sigma;
const h = Math.exp(-(dist * dist) / (2 * s * s));
Hint 2 — the finish

No branch on the cutoff at all — there is no cutoff. Every bin is scaled, most of them by almost nothing:

if (this.thread.y === 0) return spec[0][k] * h;
return spec[1][k] * h;

The 2 under the exponent is not decoration. Leave it out and you have a filter √2 narrower than the one you asked for.

Same idea elsewhere

Choosing a window is the oldest trade in the field: rectangular, Hann, Hamming, Blackman, Kaiser — each one moves ringing into resolution or back at a different exchange rate. cuFFT and rocFFT will happily transform anything; the window is always yours to pick. And the Gaussian's other half is why cv::GaussianBlur and every game engine's bloom pass use a Gaussian rather than a box: the box is a brick wall in time, and it rings in frequency.

All tasks in Filtering in the Frequency Domain

  1. Convolution Becomes Multiplication
  2. The Brick Wall Rings
  3. Trade the Ringing for a Softer Edge
  4. A Tone Buried in Noise
  5. Multiplying Spectra Convolves in a Circle

This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.