DEV Community

Umasou
Umasou

Posted on Originally published at clearpix.org

Automatic Watermark Detection: Cutting False Positives on Complex Backgrounds by Design

Our automatic watermark detector returned 125 text boxes on 24 real user photos, and every one of them was pre-selected. Ten were on a baby's face, a film still, a rock, a towel — never text at all. The other 39 we now drop are real text, but it sits on backgrounds an in-browser inpainter cannot rebuild without hallucinating. So we changed the rule: auto-detect now reports only what the model is genuinely good at — 76 boxes, no subject false positives, tiled watermarks untouched — and hands the rest to the user.

The numbers up front

Measurement Before After
Text boxes on a 24-photo real-user set 125 76
Boxes dropped by the new gate — 49 (10 by shape, 39 by background)
Subject false positives we could verify by eye 10 0
Tiled-watermark boxes kept (case-1 / case-2) 38 / 22 36 / 19
Site sample ("SAMPLE ONLY") detected still detected
Quality bench (13 synthetic cases) recall 89.2 %, precision 65.3 %, psnrRegion 24.8 dB bit-identical

Nothing in that table required a new model, a bigger model, or a server. It required deciding which detections we are willing to act on automatically.

What actually goes wrong: the detector fires on subjects

Our text detector is PP-OCRv5's DB head — the same 4.7 MB model described in part 6. It is a shape detector. It answers "where could text be", not "is this a watermark", and it fires on any structure that looks like strokes: high-contrast, roughly periodic, thin.

Natural photographs are full of such structures. Eyebrows, eyes and lips. Fingers and knuckles. The crumb structure of a dessert. Fabric folds and hair. Window mullions, balcony railings, a crowd. None of them are text, and none of them look like text to a shape detector.

The uncomfortable part is that these false positives are not weak. The boxes sitting on a baby's face and on a film still scored 0.81–0.94 mean detection probability — squarely inside the range of real watermarks on the same set (0.74–0.995). Every confidence gate we already had (mean score ≥ 0.6, minimum area, fill ratio, area cap) was blind to this, because the detector was not confused. It was confidently reporting a face.

That is the general lesson: "how confident" and "should we act" are different questions. No confidence threshold can separate "real watermark" from "real text-like texture", because the second one scores just as high.

The second half of the problem: the inpainter cannot rebuild texture

Detection is the ceiling on removal, but only one half of it. Our inpainting is MI-GAN by default and LaMa as an opt-in quality tier, both running at 512² tiles on the user's own GPU (or WASM on the CPU). That architecture reconstructs a hole from its surrounding context. On a homogeneous background it matches the statistics and the result is invisible. On a busy background it produces texture that is statistically plausible and spatially wrong.

We have measured both ends of this. On a city-texture case from our synthetic bench, leaving a faint watermark alone scored MAE 3.11 against the clean original; repainting the rectangle with the inpainter scored 24.79 — eight times worse than doing nothing. On real user photos the same effect shows up as a car-shaped hallucination on sand, a horizon-shaped smear band across a cloud layer, and a rock-like blob where a ridgeline used to be.

So the two failure modes compound. A false-positive box on a complex background is the worst possible combination: the model is asked to invent texture, and it invents it over the user's actual subject. A false-positive box on a plain wall is merely ugly.

Why we could not just make the model stronger

The usual fix for a noisy detector is a better detector. That was not available to us, and the constraints are worth stating plainly, because they are the reason this post is about product rules rather than about model work.

  • No server-side fallback. The product's promise is that your photo never leaves the tab. We cannot send a hard case to a bigger model and get an answer back; that would be an upload, and it is the one thing we refuse to do.
  • No elasticity. A hosted detector can be re-trained, re-scaled or replaced with a larger model when a bad case appears. A local model in the user's browser is whatever we shipped: a 4.7 MB OCR model, an 11 MB YOLO model, and a 102.8 MiB fp16 LaMa tier behind an opt-in. Making the detector meaningfully better means new weights, new hosting, and another download for every user.
  • The device is the budget. Phones and low-end laptops run the same 512² tile pipeline; the model's receptive field is fixed by its architecture, not by the request.
  • We have no training set for "watermark vs. scene text". Separating an overlay from a printed label on a product needs semantics and supervised data we do not have, and we cannot collect it from user photos without asking people to upload their images.

Given those constraints, the only lever left is deciding which detections we act on automatically. If the model cannot tell an overlay from a face, we can at least tell a simple background from a busy one, and we can tell a text line from a blob.

The decision: play to the model's strengths, hand the rest to the user

The rule we shipped is deliberately conservative: auto-detect only reports obvious text on a locally simple background. Everything else is the user's job — and the user's path for that is not worse than before. Drawing a box runs the same region-constrained detector inside the selection plus shape refinement, so a user who wants a watermark on a busy background can still get it, with the mask snapped to the actual strokes.

This is a change of philosophy, and we want to be explicit about it. Our detection pipeline had been running on "a miss is recoverable, a false positive is not" (the rule we stated in part 12): if we miss a faint watermark, the user brushes over it and the result is fine; if we eat their photo, there is no undo for trust. This change tightens that into: in auto mode, zero false positives first; a miss is handed to the user.

The gate, in two criteria plus one exemption

The gate runs after the existing box-level gates (score, area, fill) and before shapes are assembled. It applies to the image tool's auto-detect path only — the quality bench, the video pipeline and the manual refine path are untouched.

Criterion 1 — the box must look like a text line. A text box must be elongated: aspect ratio ≥ 1.5 (horizontal) or ≤ 0.4 (vertical). Small boxes (short side < 40 px) are exempt, because single characters in a tiled watermark are naturally squarish and tiny. Near-square large boxes are the classic OCR-on-a-subject shape, and this is what killed the baby face (111×85), the film-still boxes (66×54, 133×107, 197×109), the plate of food (46×100) and the cosplay portrait (277×344).

Criterion 2 — the background around the box must be simple. We measure a ring band around each box (pad = 35 % of the short side, clamped to 8–64 px) and compute two statistics over it: the standard deviation of luminance, and the mean absolute gradient. A box is accepted if the ring's std ≤ 35 and gradient ≤ 14.

Two details make that measurement honest rather than decorative. First, only ring pixels count — the pixels inside the box are the watermark's own strokes, and letting them into the statistic would make every watermark look like it sits on a busy background. Second, the gradient is only taken between ring pixels; a stroke edge is not background complexity.

Calibration came from the 24-photo set. Where a real watermark sat on a plain wall, water or sky, the ring measured std 7–30 and gradient 3–10. Where a false positive sat on a face, an arm, food or a crowd, it measured std 32–89 and gradient 4–27. The thresholds sit in the gap.

The exemption — repetition is evidence. A group of four or more text boxes with matching sizes (within ±30 % in width and height) is treated as a tiled watermark, and tiled watermarks are exempt from both criteria. Repetition is itself strong evidence that you are looking at an overlay rather than picture content, and tiled watermarks are small per instance, so a single instance is a local hole the inpainter can genuinely fill — on a mountainside if it has to. Our two tiled cases kept 36 of 38 and 19 of 22 boxes; what was dropped there is the low-confidence edge fragments plus, in one case, the single large central title that sits on the ridge.

One footnote on method, because we had been burned by it before. An earlier round concluded that "ring complexity has no discriminative power" — that result was about a different question (should this region use a rectangle mask or a glyph mask?). The label space matters: the same measurement separates nothing when it picks a mask strategy, and separates well when it picks whether to auto-select a detection. We have written that distinction into our tuning notes so the two results are not quoted as one.

What it costs and what it buys

Eliminated, verified by eye on the output overlays: a baby's face and a pair of legs in a family photo, three boxes on a film still, a rock formation in a mountain photo, a towel and a pillow in two selfies, a face-shaped box on a cosplay portrait, a plate of food.

Left to the user, deliberately: watermarks that sit on busy backgrounds — a large "Luma AI" title over a sunset ridge, a property agent's phone number across a fridge, "LIONSGATE" over clouds, a hand-written title over a cloud bank, a tattoo artist's name along a hand, a wordmark across a model's hair — plus in-screen text that is caption rather than overlay (meme captions, shop signage, a social-app username bar). Those detections are real, and we are choosing not to auto-erase them, because on that background the artifact is the inpainter's output, not the removal. A user who wants them can draw one box and get the same glyph-level mask.

The rest of the pipeline did not move. The synthetic bench — 13 cases, on average recall 89.2 %, precision 65.3 %, region PSNR 24.8 dB — is bit-identical, because the gate is off in the bench path. (Two bench gates were already failing before this change: one case at 12.3 dB region PSNR and the faint tiled case at 47.9 % recall. Those are pre-existing and unrelated.) The manual refine path is unchanged, and the gate is a single option on the detector call, so it can be turned on or off per surface.

We did turn it on for the video watermark remover, where a false positive is worse than in a still image: the mask is applied to the whole clip, so a hallucinated patch is replayed frame after frame. The subtitle remover keeps full auto-detection on purpose — a subtitle is the thing the user asked to delete, it can sit on a face or a street scene, and filtering by background would defeat the tool.

Try it

The detector described here runs in the free in-browser watermark remover, together with the glyph-level masking from part 6 and the mask-discipline fixes from part 12. Nothing is uploaded: detection, mask building and inpainting all happen in the tab. If you want the other half of the pipeline — upscale a photo 4× without uploading it — it runs on the same local models.


Part 15 of the ClearPix engineering series — how we build free, private, in-browser media tools at clearpix.org.

Top comments (0)