Prove the Blur Landed
- #privacy
- #redaction
- #computer-vision
- #video
- #open-source
- #verification
A redaction tool that cannot check its own output is not a privacy tool. It is a privacy hope. The detector runs, boxes get drawn, pixels get smeared, and the file ships — and nothing anywhere in that chain ever asks the only question that matters: is the face still there?
Blur and hope
The failure mode is quiet, which is what makes it dangerous. A miss produces no error, no warning, no artifact. It produces a perfectly normal-looking video with a readable plate in it. You find out when someone tells you, and by then the file is on a CDN.
So the interesting part of a redaction library is not the detector. Detectors are commodity now. The interesting part is whether the thing can prove it worked.
redactcam is that pipeline, extracted from a production video service and open-sourced at github.com/Back-Road-Creative/redactcam. It finds faces, licence plates, whole people, and the cabin band of a vehicle where occupants sit — and then it goes back and audits its own render.
Finding the thing worth hiding
The detection stage is built around one observation: at 4K, the objects that matter are small. A face forty metres down the road is a dozen pixels. Downscale a whole frame to a square inference canvas and that face becomes noise.
- Tiled inference. The frame is split into an overlapping grid derived from a target tile edge, so each crop runs near native scale. A 4K frame splits; a 1080p frame mostly does not. Overlap means a seam-straddling object stays whole in at least one tile.
- Letterboxed, then mapped back. Each tile is resized into a fixed square canvas preserving aspect, with the scale and padding kept so every box maps back to native pixels exactly.
- Overlap dedupe. Neighbouring tiles emit near-duplicates of the same object, so boxes overlapping an already-kept box above an intersection-over-union threshold are dropped. The pass is order-stable, which keeps it deterministic.
- Independent thresholds per class. Faces and plates get their own confidence floors, because a small, clear pedestrian face can score far below the level a plate needs — and dragging the plate threshold down to catch it drags a pile of rectangular road signage back in with it.
The gates that follow are the part I did not expect to need. Confidence turned out to be the wrong axis for separating plates from junk. A squarish road sign scores above any usable threshold. So does a far-bank shoreline boxed at roughly thirteen to one. A real plate is wide and thin but bounded — so the filter is a width-to-height band with a floor and a ceiling, and shape does the work confidence could not. A second gate caps a plate’s area: a genuine plate is a fraction of a percent of a 4K frame, while the stone walls and verges the model misfires on span whole percentage points at a completely plausible aspect ratio.
Faces get the temporal version of the same idea. A real face re-detects across consecutive samples as the person persists; a shadow or a patch of foliage fires once and never again. So a face track is only emitted once it has accumulated enough associated detections — and when it qualifies, its earlier low-confidence frames are kept retroactively. That is what lets the face threshold chase a genuinely faint detection without also blurring every static texture that shares its score range.
Between the detections
Running a detector on every frame of a multi-hour file is not affordable. Running it sparsely and holding the last box is worse than it sounds: the blur lands on the object at the moment of detection, then sits on empty road while the object keeps moving. Early, then wrong.
So detection stays sparse and optical flow carries each box forward every single frame, with the boxes grown by a margin scaled to their own measured per-frame speed. Gaps between two detections of the same track are filled with the line between them rather than a frozen rectangle. A confirmed track can also be extrapolated backwards a few frames, covering the head of a pass where the object was present but not yet scored.
And when flow is not enough, detection comes back. While a plate track is moving fast — a close pass, where per-frame displacement outruns what flow can follow — the pipeline re-detects plates on every frame until the track slows or retires. The cost is bounded to the handful of frames where the plate is actually readable, rather than the whole file.
The result is a dense per-frame box timeline, dilated by a safety margin, written to a JSON sidecar keyed by a fingerprint of the exact file it was computed from. That is what makes a resume safe: a fingerprint mismatch recomputes rather than reusing a stale timeline.
The mask is a video, and it is checked
The timeline is rasterised into a lossless single-channel mask video — white boxes on black, feathered at the edges — at the source’s exact resolution, frame rate and frame count. Then the frame count is read back off the written file and asserted.
That assert is not defensive programming. A mask that is one frame short of its source blurs the wrong pixels for the entire remainder of the file, and it does so silently. A mismatch is fatal, never a warning.
The second pass
Here is the part worth open-sourcing.
After the render, an independent auditor takes the original and the blurred output and reads them in lockstep. It re-detects vehicles in the original using the same detector the blur used, corroborates each one with a plate inside the box — a roadside billboard the model reads as a car does not have a plate, and the blur deliberately leaves it readable — and then, for each corroborated vehicle, it measures two things about the cabin region in the rendered file:
- Coverage — the fraction of cabin pixels the blur actually changed, as an absolute difference against the original.
- Residual sharpness — the variance of a Laplacian over that same region. Blurred pixels are flat. Sharp pixels are a face.
A region that was barely changed and is still full of detail is an exposed driver. The tool prints the leak frames and exits non-zero.
Two design notes matter here. First, the auditor is deliberately independent of how the blur was produced — it reads the finished render, not the mask, not the intermediate state. Any redaction implementation can be pointed at it. Second, the coverage threshold is a half, not a whole. The auditor re-detects the vehicle from scratch, so its box lands a few percent off the one the blur used, and the difference trims edge pixels while the driver in the centre stays covered. On real footage the separation was clean without being total: exposed windshields measured in the zero to twenty percent band, covered ones in the sixty to seventy-six percent band. The threshold sits in the gap.
That measurement is the whole point of the exercise. Before it existed, the way you checked a redaction pass was to scrub through and eyeball a few frames — which is exactly how a cabin blur anchored to the wrong reference point shipped a wide-open windshield past a human reviewer. Four frames looked fine. Every car in between did not.
Fail closed, loudly
The surrounding contract follows the same rule: when the pipeline cannot prove a thing, it refuses rather than degrades.
- A missing plate model drops to faces-only and warns — visibly, in the log, every run.
- An occupant blur that is switched on but whose model cannot be resolved is a hard failure, not a silent disable. Turning it on is a statement that drivers must be covered.
- A cached render that carries no durable marker proving it was produced with redaction is refused on resume, because it may predate the day the blur was enabled.
None of this is clever. It is just the ordering: make the bad output impossible to construct where you can, gate it before the irreversible step where you cannot, and treat a check that only runs after the encode as a bug even on the days it passes.
What is in the box, and what is not
The repository ships the detection, tracking, mask and verification code, and the coverage auditor as a standalone command you can point at any pair of files. It does not ship model weights — those are fetched on demand from a URL you control, checksum-verified, and cached locally, so you can mirror them internally and never touch a public host. It also ships no sample footage. Fixtures are synthetic by policy, because a privacy library whose test data contains real faces and real plates has told you everything you need to know about it.
The defaults are shaped by dashcam footage, which is where every one of these gates was earned. They are all parameters.
If you take one thing from this: the detector is not the product. The auditor is. A redaction pipeline you cannot interrogate after the fact is a claim, and claims are not a privacy posture.
Related reading: Your AI Agent Governance Is Just a Suggestion — the same argument, applied to agent constraints instead of pixels.
Configuration details reflect a production environment at time of writing. Implementation specifics vary based on tooling versions, platform updates, and organizational requirements. Validate approaches against current documentation before deployment.