Ffmpeg denoise restore
Denoising and restoration with ffmpeg: nlmeans (non-local means), bm3d, hqdn3d, atadenoise, fftdnoiz, dctdnoiz, removegrain, nnedi, derain, dnn_processing with ESPCN/EDSR/SRCNN super-resolution, afftdn, anlmdn, arnndn (RNN audio denoise). Use when the user asks to denoise video, clean up grain, remove noise, restore old footage, fix a noisy webcam, remove film grain, upscale with AI super-resolution, enhance video quality, or run RNNoise-style audio denoise.From its SKILL.md
npx -y skills add damionrashford/media-os --skill ffmpeg-denoise-restoreAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 17 stars17 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
9.8 KB, as published. Nobody here has run it
Ffmpeg Denoise Restore
Context: $ARGUMENTS
Quick start
- Fast/mild video denoise (real-time):
hqdn3d→ Step 2 - Good quality / medium speed:
nlmeans→ Step 2 - Archival/highest quality (slow):
bm3d→ Step 2 - Temporally-adaptive (static backgrounds):
atadenoise→ Step 2 - Film grain removal (single-frame):
removegrain=mode=11→ Step 2 - AI super-resolution (2x/4x):
dnn_processing→ Step 2 - Audio broadband hiss:
afftdn→ Step 4 - Audio non-local means (preserves speech):
anlmdn→ Step 4 - Audio RNN-based (RNNoise model):
arnndn→ Step 4
When to use
- Cleaning up noisy webcams, low-light or high-ISO footage, old VHS/DVD rips, 8-mm film scans.
- Preparing noisy sources for encoding (denoise BEFORE encoding — post-compression noise is mostly irrecoverable).
- Upscaling small sources with DNN super-resolution (ESPCN / EDSR / SRCNN).
- Broadband hiss / fan noise / hum on lavalier, conference, or field audio recordings.
- Restoring rain streaks or chromatic aberration with
derain,chromaber_vulkan.
Step 1 — Identify the noise type
Before picking a filter, decide what kind of noise you have — the right filter changes drastically.
| Symptom | Likely type | Best filter |
|---|---|---|
| Random luma speckle, uniform distribution | Gaussian / thermal | hqdn3d, nlmeans, bm3d |
| Color blotches on flat areas | Chroma noise | hqdn3d with strong chroma params; nlmeans |
| 8×8 / 16×16 blocks on flats | Compression block noise | deblock, owdenoise (cannot fully recover) |
| Horizontal/vertical banding on gradients | Banding (bit-depth) | gradfun, dither to 10-bit (format=yuv420p10le) |
| Static speckle that never moves | Fixed-pattern / sensor | removegrain=mode=11, atadenoise |
| Visible film grain to preserve | Organic grain | Do not denoise; or bm3d with low sigma + add-grain back |
| Rain streaks | Weather artifact | derain |
| Rolling colored fringes on edges | Chromatic aberration | chromaber_vulkan |
Use the ffmpeg-probe skill (ffprobe -show_frames) to confirm bit depth and pixel format before denoising.
Step 2 — Pick filter + strength
All recipes produce MP4 / H.264 CRF 18. Replace encoder / container as needed.
# LIGHT — fast, real-time-ish on CPU. hqdn3d=luma_spatial:chroma_spatial:luma_tmp:chroma_tmp
ffmpeg -i in.mp4 -vf "hqdn3d=4:3:6:4.5" -c:v libx264 -crf 18 out.mp4
# MEDIUM — better quality, non-local means (s=strength, p=patch, r=research window)
ffmpeg -i in.mp4 -vf "nlmeans=s=1.0:p=7:r=15" -c:v libx264 -crf 18 out.mp4
# HEAVY — archival; BM3D, can run at ~1 fps
ffmpeg -i in.mp4 -vf "bm3d=sigma=10:block=4:bstep=2:group=1" -c:v libx264 -crf 18 out.mp4
# TEMPORAL ADAPTIVE — only changes static pixels, preserves motion
ffmpeg -i in.mp4 -vf "atadenoise=0a=0.02:0b=0.04:1a=0.02:1b=0.04:2a=0.02:2b=0.04:s=9" -c:v libx264 -crf 18 out.mp4
# GRAIN REMOVAL — single-frame mode 11 (smooth 3x3)
ffmpeg -i in.mp4 -vf "removegrain=mode=11" -c:v libx264 -crf 18 out.mp4
# FFT-BASED — good on DCT / compression noise
ffmpeg -i in.mp4 -vf "fftdnoiz=sigma=8:amount=0.9" -c:v libx264 -crf 18 out.mp4
# DNN SUPER-RESOLUTION (2x ESPCN, TensorFlow backend)
ffmpeg -i in.mp4 -vf "dnn_processing=dnn_backend=tensorflow:model=espcn.pb:input=x:output=y" -c:v libx264 -crf 18 out.mp4
Strength guidance: start at the mildest setting; every denoise trades detail for noise. Bump sigma/strength one step at a time and A/B compare.
Step 3 — Apply + verify
- Render a 5–10 second sample first (
-ss 0 -t 10) — faster iteration. - Compare to source with showspectrum / side-by-side (
ffmpeg-playbackskill). - Check for smearing on motion, loss of texture on skin/fabric, halos around edges.
- If detail loss is unacceptable, lower sigma or switch to a preserving filter (
nlmeanswith p=5, r=11).
Step 4 — Audio (parallel workflow)
Audio denoise is generally SAFE to do FIRST, before any other cleanup.
# Broadband (hiss, fan, tape) — nf is noise floor in dB, -20 to -40 typical
ffmpeg -i in.wav -af "afftdn=nf=-25" out.wav
# Non-local means (preserves speech transients)
ffmpeg -i in.wav -af "anlmdn=s=7:p=0.002:r=0.002" out.wav
# RNNoise — needs an .rnnn model file (download from xiph/rnnoise repo)
ffmpeg -i in.wav -af "arnndn=m=bd.rnnn" out.wav
# Band-limit to voice range before other processing
ffmpeg -i in.wav -af "highpass=f=80,lowpass=f=12000" out.wav
Available scripts
scripts/denoise.py— opinionated wrapper, subcommands:video,grain,audio,sr(super-resolution). Supports--dry-run,--verbose.
Workflow
uv run ${CLAUDE_SKILL_DIR}/scripts/denoise.py video --input in.mp4 --output out.mp4 --strength medium
uv run ${CLAUDE_SKILL_DIR}/scripts/denoise.py grain --input in.mp4 --output out.mp4
uv run ${CLAUDE_SKILL_DIR}/scripts/denoise.py audio --input in.wav --output out.wav --method afftdn
uv run ${CLAUDE_SKILL_DIR}/scripts/denoise.py sr --input in.mp4 --output out.mp4 --scale 2 --model espcn.pb
Reference docs
- Read
references/filters.mdfor full option tables, noise-type diagnosis, strength/speed/quality matrix, DNN model sources, and end-to-end recipe book (VHS, webcam, DSLR, film grain).
Gotchas
- Every denoise filter TRADES DETAIL FOR NOISE. Start mild and tune up — you cannot un-denoise.
- hqdn3d full form is
hqdn3d=luma_spatial:chroma_spatial:luma_tmp:chroma_tmp. With no args it uses mild defaults (4:3:6:4.5). - nlmeans params:
s=strength, p=patch_size, r=research_window. p=7, r=15 is a good default; p=3 r=9 is faster; p=9 r=21 is heavy. - bm3d is extremely slow — can be ~1 fps. Only for archival work. Also requires an even number of frames when
group>1. - atadenoise is temporal — preserves static detail beautifully but smears motion trails; bad for fast action.
dnn_processingrequires ffmpeg built with TensorFlow OR OpenVINO OR NativeDN support. Most distro packages DO NOT include it. Checkffmpeg -filters | grep dnn_processing.- SR model files (.pb / .onnx) must be downloaded separately. FFmpeg does not ship them. See
references/filters.md. srfilter is deprecated — usednn_processinginstead.- Super-resolution output width/height are BAKED INTO THE MODEL — you cannot pass an arbitrary scale factor at runtime; pick the model that matches your target.
arnndnneeds an.rnnnmodel file — ffmpeg doesn't ship one. Grabbd.rnnnorstd.rnnnfrom thexiph/rnnoiserepo.afftdn nfis in dB. -25 is moderate; -20 is aggressive; -40 is gentle.- Always verify with A/B spectrograms (use ffmpeg-playback skill's
showspectrum). - Denoise BEFORE encoding, not after — post-compression noise is entangled with block artifacts and is mostly irrecoverable.
- 10-bit sources stay 10-bit through the chain. Add
format=yuv420p10leoryuv422p10leafter the denoise; most denoise filters support high bit depths but will downconvert silently if the encoder is 8-bit.
Examples
Example 1: Noisy webcam, real-time
Input: 1080p30 webcam capture, uniform luma/chroma noise.
Steps: hqdn3d=4:3:6:4.5 → H.264 CRF 20. Done in one pass, CPU only.
Example 2: High-ISO DSLR at night
Input: 4K H.265, heavy chroma noise, some luma grain.
Steps: nlmeans=s=1.2:p=7:r=15,format=yuv420p10le → HEVC CRF 20 10-bit.
Example 3: VHS-rip restoration
Input: 480i interlaced, dot-crawl, tape hiss, color bleed.
Steps: deinterlace (bwdif) → hqdn3d=6:4:8:6 → removegrain=mode=17 → audio arnndn=m=bd.rnnn → H.264 CRF 18.
Example 4: 2x AI upscale of small archival clip
Input: 480p20 archival interview.
Steps: download ESPCN .pb → dnn_processing=dnn_backend=tensorflow:model=espcn.pb:input=x:output=y → H.264 CRF 18.
Troubleshooting
Error: Filter 'dnn_processing' not found
Cause: ffmpeg built without TF/OpenVINO support.
Solution: rebuild from source with --enable-libtensorflow or --enable-libopenvino, or install a build that includes it (e.g., BtbN's Windows builds, jellyfin-ffmpeg). macOS Homebrew ffmpeg does NOT include DNN backends by default.
Error: Invalid model file / Cannot load model
Cause: wrong backend vs. file format (TF expects .pb, OpenVINO expects .xml+.bin, NativeDN has its own format).
Solution: set dnn_backend= to match the model; verify the file by loading it in the originating framework.
Result: Video looks plasticky / skin lost texture
Cause: denoise too aggressive; detail thrown out.
Solution: lower sigma / strength; switch bm3d → nlmeans; or nlmeans s=1.5 → s=0.8; re-add a tiny noise=alls=3:allf=t grain.
Result: Motion smearing / ghost trails
Cause: temporal filter too aggressive (hqdn3d last two params, atadenoise over-wide).
Solution: reduce temporal terms (hqdn3d=4:3:2:1.5) or drop temporal entirely with single-frame filters (removegrain, fftdnoiz).
Error: arnndn: model file required
Cause: missing .rnnn model.
Solution: download bd.rnnn or std.rnnn from the xiph/rnnoise GitHub repo and pass with arnndn=m=/path/to/bd.rnnn.
Result: Audio sounds "underwater" after afftdn
Cause: noise floor too aggressive.
Solution: raise nf toward -40 (less aggressive), or switch to anlmdn which preserves transients better.