Discuss project

Removing watermarks with AI: lessons from 9319 photos

A real project on removing watermarks with a neural network: why the model refuses, where compositing breaks, what to verify and how much it costs.

Vladislav KrivorutskoJuly 23, 202611 min read
Contents

TL;DR - key points

  • The model doesn't refuse because it can't: the word «watermark» in the request trips a filter, rephrasing to «inpaint overlaid graphic elements» gave 0 refusals across 14 frames
  • A generative model physically cannot hand your frame back — it re-encodes the whole thing at its own resolution and alters object details
  • The fix is to paste back only the watermark region from the result, but the model shifts the frame invisibly, so the paste must be aligned to the untouched border
  • The weak link is detection, not generation: the cheapest stage of the pipeline sets the quality of the whole result
  • Real cost on my corpus: $0.09 per frame for a small mark and $0.23–0.48 for a large one — the gap is in quality, not in the API price

Short answer

Removing watermarks with a neural network is not a single request to a model but a four-stage pipeline: detect the mark, generate a clean fragment, composite it with the original, and check the result automatically. On a real-estate catalogue project with 9319 photos I spent about $12 on research and learned the main thing: almost all visible defects came not from the model but from my own compositing code. Below are the concrete mistakes, the numbers, and what follows from them for any similar task.

The model doesn't refuse because it can't

The first thing almost everyone trips over: the model responds with a refusal. Of the four Google generative models I tested, three refused to work on the direct wording.

ModelReaction to «remove all watermarks»
Gemini 2.5 Flash Imagetextual refusal: «I'm unable to remove watermarks from images»
Gemini 3.1 Flash Lite Imageempty response, finishReason: OTHER
Gemini 3.1 Flash Imageworks
Gemini 3 Pro Imageworks

I nearly crossed the Lite model off as incapable — and I was wrong. It came down to the wording. That same Lite worked on the same frames without a single refusal when the task was phrased differently:

Inpaint the overlaid graphic elements in this photograph so the underlying scene is continuous. Preserve every real object exactly as it appears.

Zero refusals across 14 frames against steady refusals before. Two other phrasings gave one refusal each out of 14. The word «watermark» in the request trips a filter — the action itself is not blocked.

The practical takeaway is broader than this task: if the model refuses, check the wording before you swap the model. The price gap between Lite and the full version was 2×, and I almost paid it for my own bad phrase.

A generative model won't hand your frame back

The second misconception to drop immediately: asking the model to «remove the mark and return the rest as it was» is impossible. It's an architectural limit, not a prompt question.

Generative models encode the image into a latent space and decode it anew — the whole thing, on their fixed 1K/2K/4K grid. Even untouched areas are reassembled. In practice it looks like this: on a bedroom shot the model removed the logo but also changed the bedspread pattern — blue poppies became paisley. On another frame the glazing structure changed: one sliding panel in the original, a different frame division in the result.

For a real-estate catalogue that's unacceptable. The buyer looks at the windows and the layout, not at the artistic merit of the shot.

I tested the workarounds; all three fail:

  • Ask for a lower resolution — the API doesn't accept 0.5K, and 512 returned 592 pixels and burned 3359 tokens, twice as expensive as an honest 1K.
  • Feed the model a crop — the price is tied to the output resolution, not the input.
  • Ask in the prompt to «change nothing but the mark» — it helps, but doesn't guarantee.

The only reliable way to preserve the frame is to take nothing from the model's result except the watermark region.

Compositing: the right idea and three wrong implementations

The idea is simple: generate a clean frame but paste only the rectangle with the mark back into the original. Then 90% of the pixels stay bit-for-bit original. Implementing it turned out harder than it sounds — I spent more time debugging it than on everything else combined.

Mistake one: feather wider than the inset. The paste's transparency ramped inward from the border edge over 10 pixels, with an inset of 8. The outer band blended clean generation with the white logo that was still there, and a light rectangle — a «ghost» — appeared where the mark had been. The rule I eventually baked into the code: inset = max(inset, feather + 12). The alpha ramp must finish before it reaches the mark itself.

Mistake two: pasting by coordinates. The model doesn't return the frame pixel-for-pixel — it re-crops it invisibly. I pasted pixels at the original coordinates, but they corresponded to a slightly different part of the scene. Invisible on texture, a visible seam on a smooth wall. The fix: search for the shift by scanning a ring of untouched pixels around the border, and only then paste.

Mistake three: fixing symptoms. I adjusted feather, tone and alignment three times instead of doubting the scheme itself. The diagnosis came only when I looked at the model's raw output apart from the compositing: the wall there was perfectly clean. The defect was created by my code, not the neural network.

If the result is bad — inspect the intermediate artefact before post-processing. I lost hours optimizing parameters around a bug that wasn't where I was looking for it.

Intuition says save on the cheap stages and invest in generation. It turned out to be exactly the opposite.

Detecting the mark's coordinates costs about $0.0006 per frame — a hundred times cheaper than generation. I put the cheapest model, gemini-3.5-flash-lite, on it, and it found one mark out of two: it saw the corner logo but missed the red signature at the bottom of the frame. The mark stayed on the image while the pipeline reported success.

Switching to gemini-3.5-flash — plus $3 for the whole nine-thousand-image corpus — closed the problem. Saving on detection devalued generation, which costs a hundred times more.

A separate finding about structured output: the model with thinking enabled produced broken JSON. Reasoning tokens ate into the maxOutputTokens limit and the response was cut off mid-array. A measurement on one frame:

SettingOutput tokensResult
maxOutputTokens: 4096115 + 284 on thinkingJSON broken
maxOutputTokens: 8192100 + 232valid
thinkingBudget: 063valid, half the price

Listing coordinates needs no reasoning. Turning it off gave both correct output and savings.

A silent refusal is the most dangerous error

The worst failure mode I hit: the cheap model returns a nice image with the mark not cut out. Not an error, not an empty response — a valid JPEG that looks like a work result. Without a check, such a frame slips into production unnoticed.

Hence the rule: the generation result must be re-checked with a detector, not taken on faith. Re-running detection on the finished image raised one approach's success rate from 2 of 5 to 4 of 5 — simply because it began catching these silent no-ops and sending the frame back for a retry.

An important implementation detail: the check must not look only at the regions the first detector found. If the mark was missed on input, it isn't in the region list, and the check reports «clean». You have to re-scan the whole frame.

What really decides success — the mark's size

The main quality conclusion turned out counterintuitive. I assumed the model removes large marks worse. Measurement showed otherwise: the model removes the mark at any size; what degrades is not removal but faithfulness.

A run with one mark at different frame coverage:

Mark coversWhat happens
28% of the fragmentreconstruction is faithful
44%the model invented a nonexistent nightstand
71%it rewrote the headboard texture

The less real context remains, the more the model invents. Hence the working strategy — routing by the mark's area:

  • mark under 3% of the frame — clean the fragment around it and paste it back. On a random sample of 40 frames this path gave 88% pass rate, with 6 of 6 clean on visual spot-check;
  • mark over 3% — split the region into overlapping tiles and process each with its own context. On the five hardest frames (marks 7–21% of area) — 4 of 5, with a measured guarantee: outside the mark region 0.000–0.105% of pixels changed, on average below one brightness level. That's JPEG re-encoding noise, not a change of scene.

Worth knowing separately is the analytical path. A semi-transparent mark is not lost information: it's overlaid by the formula result = α·mark + (1−α)·background. If you have hundreds of frames with the same mark, α and its colour recover statistically and the background by the inverse formula, for free and without a single distortion. For me it weakened the mark roughly by half but didn't remove it: in bright areas the signal hits the 255 ceiling and there's nothing to recover. As a standalone solution it didn't work; as a pre-stage it's promising.

How much it costs

Numbers for my corpus, measured rather than from a price list. An important correction: real spend is above the tabulated one. The price list promises 1120 tokens for a 1K image; in practice it came to about 1770. Going from 1K to 2K should cost +50% per the list; it actually gave +12%.

ClassShare of corpusMethodPrice per frame
No mark15%detection only$0.0006
Mark under 3%38%fragment + composite$0.089
Mark over 3%47%tiles + check$0.23–0.48

For all 9319 images — $660–1200 using the Batch API, which gives an honest 50% discount and fits this task perfectly: offline processing, result within a day.

Note the structure: large marks are 47% of frames but 70–80% of the budget. The decision on this class is the decision on the money. If you leave large marks alone, the whole rest of the corpus costs $158.

A checklist if you take on this task

  1. Clarify the rights to the images before, not after, processing. Removing the mark grants no licence to the photo.
  2. Check the prompt wording before swapping the model for a pricier one. A refusal is often caused by a word, not the task.
  3. Don't save on detection. It's the cheapest stage and it sets the quality ceiling of the whole pipeline.
  4. Turn off thinking for structured coordinate output — it breaks the JSON and makes the call more expensive.
  5. Paste back only the mark region and align the paste to the untouched border: the model re-crops its output.
  6. Keep the inset wider than the feather, or the original mark leaks through the alpha ramp.
  7. Re-check the result with a detector — a silent return of the unprocessed frame happens regularly.
  8. Route by the mark's area instead of processing everything the same way.
  9. Look with your own eyes at what the client sees. I reported success twice, checking files on disk, while the client saw browser-cached old versions through the report, with a day-long Cache-Control — exactly the class of bug I unpack in the article on technical SEO optimization. Two different sources of truth are a guaranteed conflict.
  10. Validate on a random sample, not on hand-picked examples. Five good frames say nothing about nine thousand.

The last one matters most. In this project the automatic check erred both ways: it missed remaining marks and rejected clean frames. An independent check with someone else's eyes caught what I, as the author of the solution, missed — including a case where the approach invented a nonexistent lamp in a shot, and that approach's own metric was tuned so as not to notice it.

If you have a similar task at scale — catalogue processing, content migration, any routine over thousands of objects — I tackle these things under AI automation. Not «we'll deploy a neural network», but a concrete pipeline with a measured unit cost and an honest defect rate.

Frequently asked questions

Why does a neural network refuse to remove a watermark?
A filter fires on the wording, not on the action itself. In my tests Gemini 2.5 Flash Image replied with the text «I'm unable to remove watermarks from images», while 3.1 Flash Lite Image simply returned an empty response with finishReason OTHER. The same model on the same image worked fine when the task was described as inpainting an overlaid graphic element without the word watermark.
Can a watermark be removed without losing image quality?
Only if you don't hand the whole frame to the model. Generative models encode the image into a latent space and decode it anew on their 1K/2K/4K grid, so the original resolution is lost and details are reassembled. Quality can be preserved in exactly one way: take only the pixels inside the watermark from the model's result and paste them into the original with alignment.
Which model removes watermarks best?
On my material the gap between a cheap and an expensive model turned out smaller than the gap between compositing methods. Nano Banana 2 Lite at $0.042 per call produced a result comparable to the full version at $0.101, and every visible defect I blamed on the model turned out to be a bug in my own code. Debug the pipeline first, compare models second.
How much does it cost to remove watermarks from a thousand photos?
By my measurements through the Gemini API — $90 to $480 per thousand images depending on mark size, plus a 50% discount when using the Batch API. Small corner logos cost about $0.09 per frame; large semi-transparent full-frame overlays cost $0.23–0.48 because they need several calls and repeated checks.
Is it legal to remove watermarks from someone else's photos?
Removing the mark does not grant rights to the image. A watermark is the rights holder's marking, not the source of rights: removing it does not give you a licence to the photo. If the images are taken from someone else's platforms, the question is settled by a contract with the rights holder, not technically. Clarify this before you start processing, not after publishing.

Conclusion

Removing watermarks in 2026 is not a «just ask the neural network» task but an engineering one with detection, compositing and quality control. Generation is at most a quarter of the effort; the rest is verifying that the model actually did what you think it did. If you need a similar task tackled at scale, see how I build these pipelines under AI automation.

About the author

Vladislav Krivorutsko — founder of ADLAB
Vladislav Krivorutsko

Founder of ADLAB OÜ · SEO and Google Ads

Over 20 years in search traffic and monetization, and on the Estonian market since 2017. I work solo: I run the audit, build the strategy and deliver the project myself — no subcontractors, no templates. I only write about what I have tested on my own and client sites.

  • 20+ years in search traffic
  • 50+ end-to-end projects
  • Own sites in competitive niches
  • SEO for ru/et/en in one market
More about me

Read next