Median Denoiser

Rendering & Graphics

A median filter is the classic cure for "salt and pepper" speckle: replace each pixel with the middle value of its 3×3 neighbourhood, and stray black/white dots vanish while edges stay sharp. Finding that middle value fast means a sorting network of min and max operations. Here a sweeping wipe shows the cleaned image on one side and the raw noise on the other. Runs on its own; no joypad needed. Written in C and compiled with the llvm-mos-based 65816 toolchain (+mos-a16, 16-bit accumulator mode).

loading core…

Self-running demo — the wipe sweeps between noisy and denoised.

Click the screen to focus, then watch. Tab away and it pauses.

What it is

The median of nine numbers is the fifth-largest. You don't have to fully sort them — a fixed sequence of compare-and-swap steps (a sorting network) leaves the median in the middle slot:

// one compare-exchange: branchless min then max
lo = (a < b) ? a : b;   // min
hi = (a < b) ? b : a;   // max
// 19 of these sort just enough to leave the MEDIAN in slot 4:
median = network9(pixels)[4];

Nineteen min/max pairs do it. On a processor with no conditional-move instruction each becomes a compare and a branch, so the whole filter is compares, loads and stores — no multiply, no divide. The picture stays bit-exact across every codegen mode.

Compiler stress-test

ItemWhat it exercises
min / max as the hot opA median filter needs the middle of nine values. A sorting network does it with 19 compare-exchanges — each one a min and a max, written as (a<b)?a:b. Those are the generic min/max opcodes the compiler lowers directly, no library call.
no conditional moveThe 65816 has no "move-if" instruction, so each min/max becomes a compare and a short branch rather than a branchless select. "Branchless" describes the source — no data-dependent control flow — which the compiler realises with compares. Measuring that is the point.
absThe difference between the noisy pixel and the cleaned one uses absolute value — another small opcode the backend expands inline (compare-to-zero and negate).
cross-checkedThe build gate runs the network median against an independent insertion-sort median over thousands of random 9-tuples; they must agree exactly. Verified with zero mismatches over 200,000 tuples.

Written in C with the llvm-mos 65816 toolchain and verified against bsnes-jg (and MAME where the SPC700 IPL is present). Hit Verify fidelity to reproduce the build gate's WRAM assert (gate CRC 0x87FE — a fold of median-filtered windows, cross-checked against an insertion-sort median) live in this tab. No far pointers — the same source passes every way: host == default == +mos-a16 == +mos-xy16 == bsnes-jg, -verify clean.