Qu

medfilt

Builtin index · Noise

result = medfilt(x, [window=3])

Input: x, window · Output: result

What it is

Replaces each sample by the median of window samples centred on it. x is a length-N number vector or an Image (filtered channel by channel); window is a positional integer, odd, default 3.

Overloads

x can be number, vector, or Image.

2 forms. Each carries its own inputs, outputs and worked example — pick the tab for the one you are calling.

CodeInputOutputDescription
result = medfilt(x, [window=3])
x number or vectorx a length-N number vector.
window optional (default 3)Slides a window-wide 1-D neighbourhood and keeps the median.
result —
—

Examples

Written for this form and run by the build -- the output below is what the engine actually produced.

v = [5, 3, 40, 6, 5, 4, 3]
print(medfilt(v, 3))   # [3, 5, 6, 6, 5, 4, 4] -- the spike at index 2 is gone

output
[3, 5, 6, 6, 5, 4, 4]
CodeInputOutputDescription
result = medfilt(x, [window=3])
x Imagex an Image.
window optional (default 3)Runs the true 2-D median over a window×window square, channel by channel — not a row-by-row 1-D median, which would smear vertical structure it never looks at.
result —
Only possible because the Image carries its own width and height. (medfilt2/median_filter are the exact same builtin under other names, so this case applies to them too.).

Examples

Written for this form and run by the build -- the output below is what the engine actually produced.

m = [128,128,128,128,128; 128,128,255,128,128; 128,128,128,128,128; 128,128,128,128,128; 128,128,128,128,128]
img = image_from_matrix(m)
cleaned = medfilt(img, 3)
print(imhist(img, 4))       # [0, 0, 24, 1] -- one bright pixel

print(imhist(cleaned, 4))   # [0, 0, 25, 0] -- gone, over a 2-D neighbourhood this time

output
[0, 0, 24, 1]
[0, 0, 25, 0]

The paragraph and table above describe the removal tools individually; the example below chains several of them into one cleanup pipeline. First, impulse(n, [index=], [amplitude=]) builds the spike to inject in the first place — a plain length-n zero vector with one sample set to amplitude at position index. n is a required positional integer, the output length; index is a named integer sample position, default 0; amplitude is a named number, default 1. It returns a length-n number vector, and is the natural way to add a single, controlled transient rather than reaching for add_noise's random placement.

A clean tone corrupted by both a single spike and a slow linear drift needs two different tools, applied in the right order — a median filter cannot see through a ramp, and a polynomial detrend cannot see through a spike, so the spike must go first:

fs = 1000
n = 500
t = (0 to n - 1) / fs
clean = sin(2 * pi * 7 * t)

spike = 5 * impulse(n, index = 200)
corrupted = clean + spike + 0.01 * (0 to n - 1)   # a spike, plus baseline drift


fixed = medfilt(corrupted, 5)     # the spike

flat = detrend(fixed, 1)          # the ramp

sg = savgol(flat, 11, 2)          # a final polish, shape-preserving

sm = smooth(flat, 11)             # same thing, one word (default method is savgol)

print("after cleanup: {measure_snr(clean, sg):.2f} dB (savgol), {measure_snr(clean, sm):.2f} dB (smooth)")
output
after cleanup: 17.81 dB (savgol), 17.81 dB (smooth)

How it works

medfilt(x, n) on a vector, medfilt(img, n) on an image. The answer to salt-and-pepper, and the reason is worth knowing: an average is pulled by an outlier in proportion to how far out it is, while a median ignores it entirely as long as fewer than half the window is corrupt. Which is also why it keeps an edge that an average smears -- a step is not an outlier to a median.

From the note above this builtin in the interpreter, where the reasoning was written down as it was built.