# Denoise

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

Noise is random variation from the sensor, film grain, analog transfer or earlier compression. An encoder cannot predict it, so noisy video eats bitrate that would otherwise go to real detail. Denoising before encoding either lowers the bitrate at the same look or gives you a cleaner picture for the same bitrate.

Whether to do it depends on what the noise is. Ugly digital noise, low-light sensor noise and compression residue are worth removing. Film grain is part of the image: leave it, or remove it and have the encoder [synthesise it back](/tools/filtering/graining/), but don't denoise it away and leave nothing. Denoising always costs some detail, so use the least that gets the result you want. Clean sources need none.

## FFmpeg

`hqdn3d` is fast and does spatial plus temporal smoothing; `nlmeans` is far slower and more precise.

```sh
ffmpeg -i input.mkv -vf hqdn3d -c:v libx264 -crf 18 output.mkv
ffmpeg -i input.mkv -vf nlmeans -c:v libx264 -crf 18 output.mkv
```

The defaults of `hqdn3d` are 4 for `luma_spatial`, with the other three strengths (chroma spatial, luma temporal, chroma temporal) derived from it as 3, 6 and 4.5. If there is still too much noise, raise `luma_spatial` (`hqdn3d=8` is the same as `hqdn3d=8:6:12:9`). Strong settings cause ghosting behind moving objects, [banding](/start-here/video-artifacts/#banding) and blocking.

`nlmeans` compares each pixel's neighbourhood with others nearby and averages the similar ones. It is slow, so reserve it for very noisy sources or when you need high-quality denoising; below that, `hqdn3d` is more efficient. Its artefacts are less obvious than `hqdn3d`'s; on very noisy input the typical one is a flat, cartoonish look.

| Option | Meaning                                                                           | Default |
| ------ | --------------------------------------------------------------------------------- | ------- |
| `s`    | Denoising strength, from 1.0 to 30.0.                                             | 1.0     |
| `p`    | Patch size: the neighbourhood compared. Odd number.                               | 7       |
| `r`    | Research size: how far to look for similar patches. Odd number; larger is slower. | 15      |

`nlmeans=s=3:r=31:p=15` is a stronger, much slower setting for heavy noise at high resolutions, and `nlmeans=s=1:r=5:p=3` trades quality for speed.

## VapourSynth

`vsdenoise` in [vs-jetpack](/tools/filtering/vapoursynth/) offers the denoisers most people use:

- **`DFTTest`** is a frequency-domain denoiser. It is fast, handles most noise well, and lets you set a different strength for each frequency, so you can leave coarse detail alone and hit fine noise.
- **`mc_degrain`** is motion-compensated temporal denoising built on MVTools. It averages a pixel with its matching positions in neighbouring frames, so it removes noise from static and slowly moving areas without smearing the way plain temporal averaging does.
- **`bm3d`** is a state-of-the-art algorithm and very slow; it has CUDA, HIP, SYCL and Metal backends as well as a CPU one.

```python
from vsdenoise import DFTTest, mc_degrain

dn = DFTTest().denoise(clip, {0: 0, 0.25: 4, 0.5: 8, 0.75: 16, 1.0: 32}, tr=1)
# or
dn = mc_degrain(clip, tr=2)
```

In the DFTTest example, the dictionary maps frequency (0 is the coarsest detail, 1 the finest) to strength, and `tr` is the temporal radius in frames. In `mc_degrain`, `tr` is likewise the number of frames used on each side (default 1). Its strength control is `thsad` (default 400), how different a matching block may be before it is rejected: a noisier source needs a higher value or nothing gets averaged.

To check that you removed noise and not detail, look at what the filter took out. With the 16-bit clip from the [starting script](/tools/filtering/basics/#starting-script):

```python
diff = core.std.MakeDiff(clip, dn)
diff = core.std.Expr(diff, ["x 32768 - 8 * 32768 +", ""])   # amplify 8x, grey = no change
```

The difference should look like random noise. If you can see outlines of objects in it, the filter is removing detail.

## Checking the result

Look at faces, foliage, dark gradients and scene cuts. Over-denoising shows as waxy skin, flattened texture, trails behind moving objects and banding in gradients (which you can then repair with [debanding](/tools/filtering/deband/)). Watch for grain that pulses between frames when temporal denoising only partly matches. Full-reference metrics compare against the noisy original, so they penalise a denoise that looks better; decide by eye.
