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.

MethodMechanismPrivacy StrengthSpeedLook
Mosaic (pixelize)scale down then up, or pixelizeStrong (information destroyed)FastHard pixel blocks
boxblurBox-kernel averagingMedium (depends on strength)FastestUniform soft focus
gblurGaussian distributionMediumSlightly slowerSmooth, natural
avgblurPlain averagingMediumFastSlightly 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:

  1. crop=80:60:100:80 — cut an 80×60 region starting at (100, 80)
  2. scale=10:8 — shrink it down to 10×8 (destroying detail)
  3. scale=80:60:flags=neighbor — scale back up using nearest-neighbor (sharp blocks)
  4. 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:

  1. Region A (top-left): crop → blur → overlay onto base, label [v1]
  2. Region B (bottom-right): crop → blur → overlay onto [v1]
  3. 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:

  1. Detect faces with OpenCV / dlib / MediaPipe — in Python, run a detector on each frame and write (time, x, y, w, h) to CSV
  2. Convert detections into t-based expressions — emit chunked if(between(t,a,b),x1,...) strings
  3. 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 gblur sigma above ~30 explodes CPU usage. Switch to boxblur, or lower sigma to ~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

FilterStrength controlSpeedResistance to recoveryBest for
scale,scale (mosaic)Shrink sizeFastestStrong (larger blocks → stronger)Faces, license plates
pixelizew, hFastestStrongFaces, license plates
boxblurluma_radius:luma_powerFastMediumBackground, batch jobs
gblursigmaSlightly slowerMediumPolished output
avgblurKernel sizeFastMediumLightweight 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.



Tested with ffmpeg 8.1 / Windows 11
Primary source: ffmpeg.org/ffmpeg-filters.html#boxblur