TL;DR
FFmpeg can upscale video with -vf "scale=1920:1080:flags=lanczos". Lanczos is the best built-in scaling algorithm for upscaling. Use hqdn3d to reduce grain while preserving edges, and use vidstab for camera shake stabilization with a two-pass workflow. For a full enhancement pipeline, chain denoising, scaling, and stabilization in one FFmpeg filter graph.
Introduction
Video quality enhancement with FFmpeg is more than changing resolution. A practical enhancement workflow usually targets three separate problems:
-
Low resolution or pixelation: upscale with
scale -
Noise or grain: denoise with
hqdn3d -
Camera shake: stabilize with
vidstab
This guide shows each step independently, then combines them into one implementation-focused pipeline.
Scaling algorithms
When FFmpeg upscales video, it has to create new pixels. The scaling algorithm controls how those pixels are estimated.
| Algorithm | Speed | Quality | Best for |
|---|---|---|---|
neighbor |
Fastest | Lowest | Pixel art |
bilinear |
Fast | Low | Speed-critical jobs |
bicubic |
Medium | Good | General downscaling |
lanczos |
Slower | Best | Upscaling |
For most video upscaling tasks, start with lanczos.
Upscale 720p to 1080p
ffmpeg -i input_720p.mp4 \
-vf "scale=1920:1080:flags=lanczos" \
-c:v libx264 -crf 20 \
output_1080p.mp4
Maintain aspect ratio
Use -2 for the height so FFmpeg calculates it automatically while keeping the value divisible by 2.
ffmpeg -i input.mp4 \
-vf "scale=1920:-2:flags=lanczos" \
-c:v libx264 -crf 20 \
output.mp4
Scale to 4K
ffmpeg -i input.mp4 \
-vf "scale=3840:-2:flags=lanczos" \
-c:v libx264 -crf 18 -preset slow \
output_4k.mp4
-preset slow tells x264 to spend more time optimizing compression. This is useful for higher-resolution output where file size and quality matter more.
Denoising with hqdn3d
The hqdn3d filter performs high-quality 3D denoising. It reduces grain and noise while preserving edge detail better than simple blur filters.
ffmpeg -i noisy_video.mp4 \
-vf "hqdn3d=4:3:6:4.5" \
-c:v libx264 -crf 20 \
denoised.mp4
The parameter format is:
hqdn3d=luma_spatial:chroma_spatial:luma_temporal:chroma_temporal
| Parameter | Range | Purpose | Default |
|---|---|---|---|
luma_spatial |
0-16 |
Spatial noise in brightness channel | 4 |
chroma_spatial |
0-16 |
Spatial noise in color channels | 3 |
luma_temporal |
0-16 |
Temporal smoothing in brightness | 6 |
chroma_temporal |
0-16 |
Temporal smoothing in color | 4.5 |
Stronger denoising
Use stronger values for visibly grainy footage:
ffmpeg -i grainy.mp4 \
-vf "hqdn3d=10:8:15:10" \
-c:v libx264 -crf 20 \
clean.mp4
Higher values remove more noise, but they can blur fine detail. Test on a short clip before processing a full video.
Light denoising
Use lighter values when you want to preserve detail:
ffmpeg -i video.mp4 \
-vf "hqdn3d=2:1.5:3:2.5" \
-c:v libx264 -crf 20 \
output.mp4
Stabilization with vidstab
The vidstab filter uses a two-pass workflow:
- Analyze motion and write transform data.
- Apply stabilization using that transform data.
Check whether your FFmpeg build includes vidstab:
ffmpeg -filters | grep vidstab
On macOS, the Homebrew FFmpeg package includes it:
brew install ffmpeg
Pass 1: Analyze motion
ffmpeg -i shaky_video.mp4 \
-vf "vidstabdetect=stepsize=6:shakiness=8:accuracy=9:result=transform.trf" \
-f null -
Key options:
-
shakiness=8: expected camera shake from1-10 -
accuracy=9: motion detection accuracy from1-15 -
result=transform.trf: output transform file -
-f null -: discard video output because this pass only creates the transform file
Pass 2: Apply stabilization
ffmpeg -i shaky_video.mp4 \
-vf "vidstabtransform=input=transform.trf:zoom=1:smoothing=10" \
-c:v libx264 -crf 20 \
stabilized.mp4
Key options:
-
zoom=1: adds 1% zoom to compensate for edge cropping -
smoothing=10: controls how smooth the camera path becomes
If black borders appear, increase zoom.
More aggressive stabilization
ffmpeg -i video.mp4 \
-vf "vidstabtransform=input=transform.trf:zoom=3:smoothing=30:optzoom=1" \
-c:v libx264 -crf 20 \
stable.mp4
optzoom=1 lets FFmpeg optimize zoom to avoid borders.
Combined quality enhancement pipeline
After running the vidstabdetect pass, combine denoising, scaling, and stabilization in one filter chain:
ffmpeg -i source.mp4 \
-vf "hqdn3d=4:3:6:4.5,scale=1920:-2:flags=lanczos,vidstabtransform=input=transform.trf:zoom=1:smoothing=10" \
-c:v libx264 -crf 18 -preset slow \
-c:a copy \
enhanced.mp4
Recommended order:
- Denoise first to reduce artifacts before scaling.
- Scale second so noise is not enlarged.
-
Stabilize last using the transform file from
vidstabdetect.
Before running the combined command, create the transform file:
ffmpeg -i source.mp4 \
-vf "vidstabdetect=stepsize=6:shakiness=8:accuracy=9:result=transform.trf" \
-f null -
Sharpening filter
If footage looks soft rather than noisy, use unsharp.
ffmpeg -i video.mp4 \
-vf "unsharp=5:5:1.5:5:5:0.5" \
-c:v libx264 -crf 20 \
sharpened.mp4
Parameter format:
unsharp=lx:ly:la:cx:cy:ca
Where:
-
lx:ly: luma matrix size in pixels -
la: luma amount, where positive values sharpen and negative values blur -
cx:cy:ca: chroma matrix size and amount
Light sharpening:
ffmpeg -i video.mp4 \
-vf "unsharp=3:3:0.5:3:3:0.0" \
-c:v libx264 -crf 20 \
output.mp4
Strong sharpening:
ffmpeg -i video.mp4 \
-vf "unsharp=5:5:2.5:5:5:0.0" \
-c:v libx264 -crf 20 \
output.mp4
Performance considerations
Enhancement filters are compute-heavy. Approximate processing times for a 10-minute 1080p video:
| Pipeline | Estimated time |
|---|---|
| Scale only | 2-5 minutes |
Scale + hqdn3d
|
5-10 minutes |
Scale + hqdn3d + vidstab
|
15-25 minutes |
Use -preset to balance encoding speed and file size:
| Preset | Behavior |
|---|---|
ultrafast |
Fastest encoding, larger files |
fast |
Moderate speed and size |
slow |
Slower encoding, smaller files for a given CRF |
veryslow |
Usually not worth the extra time compared with slow
|
For batch processing, you can use GNU parallel:
ls *.mp4 | parallel ffmpeg -i {} \
-vf "scale=1920:-2:flags=lanczos" \
-c:v libx264 -crf 20 \
enhanced_{/}
Connecting to AI video upscaling APIs
FFmpeg filters are fast and free, but AI-powered upscaling can produce better results for low-quality or damaged footage. AI upscaling uses neural models instead of algorithmic interpolation.
WaveSpeedAI offers AI upscaling models through an API:
POST https://api.wavespeed.ai/api/v2/wavespeed-ai/video-enhance
Authorization: Bearer {{WAVESPEED_API_KEY}}
Content-Type: application/json
{
"video_url": "https://storage.example.com/source-video.mp4",
"scale": 2,
"enhance": true
}
Test the API request before integrating it into your app.
Add basic assertions:
- Status code is
200 - Response body contains an
idfield
Then poll the status endpoint for completion and compare the AI-upscaled result against FFmpeg’s Lanczos output.
Use FFmpeg for standard enhancement work. Use API-based AI upscaling when output quality matters more than processing cost or runtime.
FAQ
Is Lanczos better than bicubic for all cases?
For upscaling, yes. For downscaling, bicubic is often faster with comparable quality. Lanczos is more computationally expensive.
Does vidstab work on phone footage?
Yes. Phone footage often benefits from stabilization. For handheld phone video, set shakiness high, usually between 8 and 10.
How much zoom is needed to hide stabilization borders?
Typically 3-8%, depending on how shaky the source is. Use optzoom=1 to let FFmpeg calculate it automatically.
Can FFmpeg enhance low-resolution historical footage?
FFmpeg filters can help, but they have limits. AI-based upscaling tools, such as ESRGAN or specialized video enhancement APIs, usually produce better results on severely degraded footage.
Does denoising slow down playback?
No. Denoising happens during conversion. The exported video plays normally.
Top comments (0)