You want to upload that clip to YouTube — but a passerby’s face, a parked car’s license plate, and a doorplate with a name on it are all in the frame. In the social-media era, the ability to mosaic or blur a specific region of a video is an essential privacy skill. This guide walks through, step by step, how to reliably hide faces, plates, and other personal information using FFmpeg alone — from static regions to moving subjects.
What You’ll Learn
- The difference between mosaic (pixelization) and the various blur filters, and when to use each
- The
crop+overlay“sandwich” pattern for blurring a single static region - How to hide multiple regions in one pass
- Animating a region over time with
t-based expressions and key-frame–style switching - Limiting a blur to a specific time range with
enable='between(t,...)' - The OpenCV / Python workflow needed when you must auto-track a moving face
- Tips for preserving output quality and fixing common failures
Tested with: FFmpeg 8.1
Platform: Windows / macOS / Linux
Mosaic vs Blur — Which to Choose
“Mosaic” and “blur” get used interchangeably, but they’re technically distinct. Pick based on privacy strength, file size, and aesthetics.
| Method | Mechanism | Privacy Strength | Speed | Look |
|---|---|---|---|---|
| Mosaic (pixelize) | scale down then up, or pixelize | Strong (information destroyed) | Fast | Hard pixel blocks |
| boxblur | Box-kernel averaging | Medium (depends on strength) | Fastest | Uniform soft focus |
| gblur | Gaussian distribution | Medium | Slightly slower | Smooth, natural |
| avgblur | Plain averaging | Medium | Fast | Slightly coarser |
Privacy rule of thumb:
- Faces, license plates, addresses — info that must NEVER be recoverable → mosaic with large blocks
- Background figures, incidental objects — visual cleanup → blur (
boxblur/gblur)
Weak blurs can be partially reversed by AI super-resolution. If you actually need privacy, use large pixel blocks — that’s the unbreakable choice.
Mosaic a Single Static Region (Pixelize)
The most common use case: block out a face or plate at a fixed location.
Method 1 — scale down then scale up
ffmpeg -i input.mp4 -filter_complex "[0:v]crop=80:60:100:80,scale=10:8,scale=80:60:flags=neighbor[fg];[0:v][fg]overlay=100:80[out]" -map "[out]" output.mp4
What each step does:
crop=80:60:100:80— cut an 80×60 region starting at (100, 80)scale=10:8— shrink it down to 10×8 (destroying detail)scale=80:60:flags=neighbor— scale back up using nearest-neighbor (sharp blocks)overlay=100:80— paste it back over the original at the same position
For a stronger effect, shrink to scale=5:4 instead. The bigger the blocks, the less recoverable the original.
Method 2 — the pixelize filter (FFmpeg 5.1+)
ffmpeg -i input.mp4 -filter_complex "[0:v]crop=80:60:100:80,pixelize=w=10:h=10[fg];[0:v][fg]overlay=100:80[out]" -map "[out]" output.mp4
pixelize is a dedicated mosaic filter; w and h set the block size directly.
Gaussian Blur a Static Region
Blur is the classic privacy choice — soft and natural-looking, ideal for streamed content.
The crop + overlay sandwich pattern
ffmpeg -i input.mp4 -filter_complex "[0:v]crop=80:60:100:80,gblur=sigma=20[fg];[0:v][fg]overlay=100:80[out]" -map "[out]" output.mp4
Visually, the “sandwich” looks like this:
Source [0:v]
│
├─────────────► used as the base layer
│
└─► crop ─► gblur ─► [fg] ──┐
▼
overlay at the same coordinates
│
▼
[out] result
The key insight: you only need one input. [0:v] can be referenced multiple times inside a filtergraph. You don’t need split — referencing the same label twice is enough.
Faster boxblur version
ffmpeg -i input.mp4 -filter_complex "[0:v]crop=80:60:100:80,boxblur=10[fg];[0:v][fg]overlay=100:80[out]" -map "[out]" output.mp4
For batch processing where CPU time matters, boxblur wins. See the boxblur deep dive for strength tuning.
Hiding Multiple Regions Simultaneously
To blur multiple faces or several cars in one pass, chain overlay calls.
ffmpeg -i input.mp4 -filter_complex "[0:v]crop=60:50:40:40,boxblur=10[a];[0:v][a]overlay=40:40[v1];[0:v]crop=60:50:200:140,boxblur=10[b];[v1][b]overlay=200:140[out]" -map "[out]" output.mp4
The pipeline:
- Region A (top-left):
crop→ blur → overlay onto base, label[v1] - Region B (bottom-right):
crop→ blur → overlay onto[v1] - Final result
[out]
You can extend this to 3, 4, or more regions — just keep adding intermediate labels ([v1]→[v2]→[v3]→...→[out]).
When the Region Moves Over Time
A walking person or driving car won’t stay put. Use t (time in seconds) inside the position expressions to make the box follow the subject.
Constant horizontal motion
ffmpeg -i input.mp4 -filter_complex "[0:v]crop=60:50:40+t*5:40,boxblur=10[fg];[0:v][fg]overlay=40+t*5:40[out]" -map "[out]" output.mp4
40+t*5 means “start at X=40, move 5 px right per second.” The crop and overlay expressions must match exactly.
Key-frame–style position switching
If the subject jumps to a new position at the 5-second mark:
ffmpeg -i input.mp4 -filter_complex "[0:v]crop=60:50:if(lt(t\,5)\,40\,150):40,boxblur=10[fg];[0:v][fg]overlay=if(lt(t\,5)\,40\,150):40[out]" -map "[out]" output.mp4
if(lt(t,5),40,150) reads as “if t<5 then 40 else 150”. For three or more segments, nest: if(lt(t,2),A,if(lt(t,5),B,C)). Inside a filter expression, escape commas as \,.
Auto-Tracking Face Blur
FFmpeg alone cannot detect faces. To follow a moving face automatically, you need an external pipeline:
- Detect faces with OpenCV / dlib / MediaPipe — in Python, run a detector on each frame and write
(time, x, y, w, h)to CSV - Convert detections into
t-based expressions — emit chunkedif(between(t,a,b),x1,...)strings - Apply with FFmpeg — same syntax as the key-frame example above
A minimal detection sketch:
# OpenCV face detection -> FFmpeg expression
import cv2
cap = cv2.VideoCapture("input.mp4")
detector = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
boxes = [] # (time, x, y, w, h)
fps = cap.get(cv2.CAP_PROP_FPS)
i = 0
while True:
ok, frame = cap.read()
if not ok: break
faces = detector.detectMultiScale(frame, 1.3, 5)
if len(faces):
x, y, w, h = faces[0]
boxes.append((i/fps, x, y, w, h))
i += 1
# Build the FFmpeg `if()` expression from boxes and pass it to -filter_complex
For production-grade results, swap the Haar cascade for a modern model (YOLOv8, MediaPipe Face Detection).
Time-Limited Blur
To blur only between, say, 2s and 5s, use the enable expression on overlay.
ffmpeg -i input.mp4 -filter_complex "[0:v]crop=60:50:40:40,boxblur=10[fg];[0:v][fg]overlay=x=40:y=40:enable='between(t,2,5)'[out]" -map "[out]" output.mp4
between(t,2,5)— only enable the overlay between 2s and 5s- Outside that window the original frame passes through untouched
For multiple intervals, OR them with +: enable='between(t,2,5)+between(t,10,12)'.
Preserving Output Quality
Blurring is computationally expensive and the default settings can produce visible quality loss or banding.
Lower the CRF
ffmpeg -i input.mp4 -filter_complex "[0:v]crop=80:60:100:80,boxblur=10[fg];[0:v][fg]overlay=100:80[out]" -map "[out]" -c:v libx264 -crf 18 -pix_fmt yuv420p output.mp4
-crf 18 is visually near-lossless (the default is 23). For stable blur output, 18–20 is the safe range.
Lock down the pixel format
Always include:
-pix_fmt yuv420p
This guarantees broad player compatibility and stable color reproduction. For 10-bit sources, use yuv420p10le instead.
Troubleshooting
Output is completely black
Cause: missing -map "[out]", or the filtergraph label name has a typo.
# Wrong
ffmpeg -i input.mp4 -filter_complex "[0:v]crop=80:60:100:80,boxblur=10[fg];[0:v][fg]overlay=100:80[result]" output.mp4
If you don’t -map the final label [result], FFmpeg falls back to [0:v] and the blur silently disappears (or the size mismatch produces black). Always include -map "[out]".
”Invalid too big or non positive size”
crop=W:H:X:Y requires X+W ≤ input width and Y+H ≤ input height. Run ffprobe -i input.mp4 first to read the resolution.
Processing is extremely slow
- A
gblursigmaabove ~30 explodes CPU usage. Switch toboxblur, or lowersigmato ~10 - For 4K sources, scale your crop coordinates accordingly (faces are realistically 200–400 px)
Slight color shift
Blur filters may trigger an internal color-space conversion. If you see a shift, prepend format=yuv420p to the chain to lock it down.
Quick Comparison — Which Filter to Use
| Filter | Strength control | Speed | Resistance to recovery | Best for |
|---|---|---|---|---|
scale,scale (mosaic) | Shrink size | Fastest | Strong (larger blocks → stronger) | Faces, license plates |
pixelize | w, h | Fastest | Strong | Faces, license plates |
boxblur | luma_radius:luma_power | Fast | Medium | Background, batch jobs |
gblur | sigma | Slightly slower | Medium | Polished output |
avgblur | Kernel size | Fast | Medium | Lightweight batch |
FAQ
Q1. The blur looks too weak — how do I make it stronger?
For boxblur, raise both radius and power: boxblur=20:3. For gblur, raise sigma: gblur=sigma=30. For mosaic, shrink to a tiny size like 5×4 — that’s the strongest option.
Q2. Is mosaic enough to fully hide personal info?
With sufficiently large blocks, yes — pixelization is effectively unrecoverable. Weak blurs, on the other hand, have been partially defeated by AI super-resolution. The rule is “mosaic + large blocks” for sensitive data. For text (addresses, phone numbers), a solid black box (drawbox) is sometimes safer than any blur.
Q3. How do I track a moving subject (person, car)?
FFmpeg can’t track on its own. The standard pipeline is OpenCV / MediaPipe / YOLO to extract per-frame coordinates, then feed those into the “key-frame–style” expression shown above. Python preprocesses, FFmpeg renders.
Q4. The output is all black
Almost always a missing -map "[out]". Whenever you use -filter_complex, you must explicitly -map the final label.
Q5. How do I blur exactly one frame?
Use n (frame number): enable='between(n,30,30)'. In time terms, enable='between(t,1.0,1.04)' (about one frame at 30 fps). Single-frame blurs are useful when reusing footage as a thumbnail.
Q6. Can the blurred area be reverted?
Mosaic and blur destroy information, so reverting is impossible without the original file (weak blurs may be guess-reconstructed by AI). Conversely, if you accidentally over-blurred a region, unsharp can sharpen slightly but can’t recover detail. Always keep a backup of the original.
Related Articles
- boxblur Filter — Box Blur Basics — full-frame blur foundation
- Overlay a Watermark — overlay basics
- Crop Black Bars — crop syntax in detail
Tested with ffmpeg 8.1 / Windows 11
Primary source: ffmpeg.org/ffmpeg-filters.html#boxblur