Face Masking#
Implemented in facemask.masking — see Python Analysis API for the
full API reference. The three detection backends themselves
(facemask.detectors) are pretrained models producing plain
(x1, y1, x2, y2) boxes; this page covers the geometry applied to those
boxes afterward.
Box padding and clamping#
expand_and_clip() pads a detected box by a
fraction of its own size (so hairline, ears, and chin are covered, not
just the tight detector box), then clips the result to the frame bounds.
For a box \((x_1, y_1, x_2, y_2)\) with width \(w = x_2-x_1\) and
height \(h = y_2-y_1\), and a margin fraction \(m\):
where \(W, H\) are the frame’s width/height. Padding is applied before clipping, so a face near the frame edge is padded first and only then clamped — the result is always a valid, non-negative box even at the frame boundary, never an out-of-bounds region a caller could mis-slice.
Blur-kernel sizing#
apply_mask()’s "blur" style applies a Gaussian
blur whose kernel size scales with the box’s own size, so small/distant
and large/close faces both get proportionally strong blur:
where \(\vert\) is bitwise OR, used here purely to force the result
odd (OpenCV’s Gaussian blur requires an odd kernel size), and the
\(\max(3, \ldots)\) floor guarantees a valid minimum kernel even for a
very small box. The computed kernel is additionally clamped to the box’s
own actual width/height (never larger than the region being blurred) — a
region too small for any valid odd kernel falls back to a solid fill
instead, so a detected face is never left completely unmasked purely
because cv2.GaussianBlur rejected an oversized kernel for a thin,
edge-clipped box.
Practical recommendations#
MediaPipe (default) has the best recall across angles. YOLOv8 is a community checkpoint — verify it’s finding faces you’d expect before trusting it for a privacy-critical run. OpenCV DNN avoids an extra ML framework but is noticeably weaker at extreme angles — only use it when the other two aren’t viable options.
This tool’s entire purpose is anonymization — a missed detection means an unmasked face in the output, silently. Spot-check the anonymized output on a few frames per camera before treating it as safe to share externally, especially for a backend/session combination you haven’t validated before.
Unlike Pose/Expression’s frame-skip (which only thins out recorded data), Face Masking’s skip control reuses the last detected box on skipped frames — a fast-moving face between detected frames can leave a real gap in coverage. Keep it at 1 (every frame) unless you’ve confirmed the subject’s motion is slow enough for a higher value to still cover every frame adequately.
Blur is visually softer and preserves general context (hair, approximate shape); a solid box is a harder, more obviously irreversible guarantee against any residual leakage through a partially-transparent or thin blur kernel — prefer solid box when the anonymization requirement is strict, not just visual.