# Deband

Canonical URL: https://encode.wiki/tools/filtering/deband/
Content version: ca42e61ede0a24f789d7287a0001244ad22f4abf
Status: current
License: CC BY-SA 4.0
Checksum (SHA-256): b16c6f6816c04897c5bab9cc06708cecc5d7b43bf692b9c67ad4edf765b79556

Banding is visible stepping in what should be a smooth gradient: skies, fades, dark rooms, flat-shaded animation backgrounds. It comes from too few code values to describe the gradient (8-bit sources and dark scenes are the worst), from earlier lossy compression that flattened the noise which hid the steps, or from an 8-bit conversion without dithering. A debander finds those flat steps, smooths across them and adds a little dither so the new values don't re-band.

It only helps when the banding is in what the encoder receives. Banding that appears only in your encode is the encoder removing noise and flattening the gradient; encode at 10-bit (even from an 8-bit source), use a lower CRF, and consider [adding grain](/tools/filtering/graining/) or letting the codec synthesise it.

## FFmpeg

FFmpeg has two debanders. `deband` is the one to use before encoding:

```sh
ffmpeg -i input.mkv -vf "format=yuv420p10le,deband" -c:v libsvtav1 -crf 30 -pix_fmt yuv420p10le output.mkv
```

`deband` replaces pixels that look banded with the average of randomly chosen pixels around them. It runs at any bit depth, but an 8-bit frame has no values between two adjacent steps, so the filter can only scatter pixels across each step edge. Converting to 10-bit first gives it room to produce the in-between values of a smooth gradient, and the encode should be 10-bit anyway. Keep `-pix_fmt yuv420p10le` on the output: without it, FFmpeg may negotiate `deband`'s output back down to 8-bit for the encoder.

`gradfun` fits a gradient and dithers the result back to 8-bit. It only accepts 8-bit formats (FFmpeg silently converts higher bit depths down before it), and its documentation says it is designed for playback: lossy compression tends to remove the dither and bring the bands back. Keep it out of encoding chains.

| Option                 | What it does                                                                                                                                                                                                                    |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `1thr`, `2thr`, `3thr` | Per-plane threshold, 0.00003 to 0.5, default 0.02. A pixel counts as banded when its difference from its reference pixels is below the threshold, so raising it deals with stronger banding and also flattens more real detail. |
| `range`                | How far away the reference pixels are picked, in pixels (randomly up to this value). Default 16.                                                                                                                                                                     |
| `blur`                 | On by default: compare against the average of the surrounding pixels. Turning it off requires all four neighbours to be within the threshold, which touches less real detail.                                                   |

## VapourSynth

For anything beyond a quick pass, `vsdeband` in [vs-jetpack](/tools/filtering/vapoursynth/) is more flexible. `f3k_deband` implements f3kdb-style debanding through the vszip plugin; `placebo_deband` uses libplacebo's GPU-based debander. Both take a radius and a threshold:

```python
from vsdeband import f3k_deband, deband_detail_mask, Grainer

deband = f3k_deband(clip, radius=16, thr=96)
mask = deband_detail_mask(clip)
deband = core.std.MaskedMerge(deband, clip, mask)   # keep detailed areas untouched
deband = Grainer.GAUSS(deband, strength=0.5, luma_scaling=10)
```

Deband at high bit depth (the [starting script](/tools/filtering/basics/#starting-script) works in 16-bit) and let the final dither to 10-bit carry the result. `thr` uses the old neo_f3kdb scale (default 96); raise it for stronger banding, and use `grain` or a separate `Grainer` pass to add dither-like noise afterwards. A higher `luma_scaling` reduces the grain in brighter areas, which suits banding because it shows up in the darks. `mdb_bilateral` is a multi-stage option for extreme banding, and `pfdeband` blurs first and limits how far the result may move from the original.

The detail mask keeps the debander away from edges and texture. Without it, a threshold high enough to fix the sky also smooths faces and fabric. View the mask (`mask.set_output()`): it should be white on edges and texture, which keep the original pixels, and black in the flat gradients you want debanded.

## Pitfalls

- **Too strong a threshold** blurs low-contrast detail: dark-scene texture, film grain, skin. Start low and raise it only until the steps vanish.
- **Debanding the whole video** when only a few scenes band. Apply it to those shots, or at least check the others for damage.
- **No grain.** A perfectly smooth gradient is exactly what an encoder will quantise back into bands. Fine noise added after debanding survives better.
- **HDR.** Thresholds are in code values, and a step of a given size looks different in PQ or HLG than in SDR, so re-tune and judge on an HDR display.
