<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: KatySwift</title>
    <description>The latest articles on DEV Community by KatySwift (@katyswift).</description>
    <link>https://dev.to/katyswift</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4015899%2F6193a8bd-546a-405e-96bf-e8ed4531c7a9.png</url>
      <title>DEV Community: KatySwift</title>
      <link>https://dev.to/katyswift</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/katyswift"/>
    <language>en</language>
    <item>
      <title>Watermark removal isn't lossy — you've been using the wrong tool</title>
      <dc:creator>KatySwift</dc:creator>
      <pubDate>Sun, 05 Jul 2026 09:36:57 +0000</pubDate>
      <link>https://dev.to/katyswift/watermark-removal-isnt-lossy-youve-been-using-the-wrong-tool-1hpg</link>
      <guid>https://dev.to/katyswift/watermark-removal-isnt-lossy-youve-been-using-the-wrong-tool-1hpg</guid>
      <description>&lt;p&gt;Ask anyone who has tried to remove a watermark from an image and they'll tell you the same thing: you don't get the original back. You get something close. There's a soft, smeared patch where the mark used to be, and if you look at it straight on, you can always tell.&lt;/p&gt;

&lt;p&gt;That belief is correct almost every time. It's also the reason a whole category of watermark removal gets done the wrong way.&lt;/p&gt;

&lt;p&gt;For one specific kind of watermark — the visible logo Gemini stamps into the corner of its images — you can get the original back. Not a close approximation but the actual pixel values, the ones they held before the mark was ever applied, recovered without a model, without an upload, and without guessing at anything.&lt;/p&gt;

&lt;p&gt;The trick is that "removing a watermark" turns out to be two completely different problems wearing the same name.&lt;/p&gt;

&lt;h2&gt;
  
  
  The word "lossy" is hiding an assumption
&lt;/h2&gt;

&lt;p&gt;When people say watermark removal is lossy, here's the unstated logic underneath:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The watermark covers up part of the photo. Whatever was under it is gone. So removing the watermark means &lt;em&gt;reconstructing&lt;/em&gt; the missing part — filling the hole with a best guess.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That logic is airtight, and for most watermarks it's exactly right. If a mark truly destroyed the pixels underneath, then yes, your only option is to invent replacements that look plausible. That's a guess, and a guess is lossy by definition.&lt;/p&gt;

&lt;p&gt;But notice what the whole argument rests on: the idea that the pixels underneath are &lt;em&gt;gone&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For a Gemini watermark, they're not gone. They're just mixed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nobody's AI painted that logo
&lt;/h2&gt;

&lt;p&gt;Here's the part that trips people up. The image came out of an AI model, so the instinct is that everything about it — including the watermark — is some inscrutable AI thing that needs an equally inscrutable AI thing to undo.&lt;/p&gt;

&lt;p&gt;The watermark isn't AI. After the model finishes generating the picture, a plain, deterministic step pastes the logo on top. It's the same alpha compositing that has been in every graphics stack since the 1980s:&lt;/p&gt;

&lt;p&gt;watermarked = α · logo + (1 − α) · original&lt;/p&gt;

&lt;p&gt;For each pixel the logo touches, the final value is a weighted blend of the logo's color and the original photo's color. &lt;code&gt;α&lt;/code&gt; (alpha) is how opaque the logo is at that pixel — 0 means fully transparent, 1 means fully opaque.&lt;/p&gt;

&lt;p&gt;Read that formula again with the "lossy" assumption in mind. The original pixel is still in there. It wasn't painted over and thrown away. It was &lt;em&gt;added to&lt;/em&gt; the logo, with a known weight. Addition has an inverse.&lt;/p&gt;

&lt;h2&gt;
  
  
  If it was added, subtract it
&lt;/h2&gt;

&lt;p&gt;Solve the same equation for the thing you actually want:&lt;/p&gt;

&lt;p&gt;original = (watermarked − α · logo) / (1 − α)&lt;/p&gt;

&lt;p&gt;That's it. That's the whole idea. If you know three things — the final watermarked pixel (you have it), the logo's color (fixed and knowable), and the alpha at that pixel — you can recover the original exactly — no reconstruction, no plausible guess, just arithmetic.&lt;/p&gt;

&lt;p&gt;This is why the "close but smeared" outcome doesn't apply here. A smear happens when a tool paints over the region because it thinks the data is gone. This tool doesn't paint. It reverses a calculation that was run in the forward direction a moment earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  The catch is the alpha map
&lt;/h2&gt;

&lt;p&gt;There is a catch, though — two of them — and skipping over them would turn this into a sales pitch.&lt;/p&gt;

&lt;p&gt;The formula needs &lt;code&gt;α&lt;/code&gt; for every pixel, and the logo doesn't ship with its alpha values attached. So you have to recover them first.&lt;/p&gt;

&lt;p&gt;The way to do it is almost embarrassingly direct: take the watermark as it appears over a &lt;em&gt;known&lt;/em&gt; solid background — a flat color you fully control — and work backward from the blend to figure out how opaque the logo is at each point. Once you've built that alpha map once, it's the same map every time, because Gemini applies the same logo the same way. You measure it once and reuse it forever.&lt;/p&gt;

&lt;p&gt;The second catch is the one the "lossless" claims tend to skip over.&lt;/p&gt;

&lt;p&gt;Look at the denominator: &lt;code&gt;(1 − α)&lt;/code&gt;. When the logo is nearly opaque — α close to 1 — you're dividing by a number close to zero. In those pixels the original barely contributed to the blend in the first place, so there's almost nothing left to recover, and the division amplifies whatever tiny errors exist. Where the logo is fully opaque, the original really is gone, and no formula brings it back. Honesty demands saying that out loud.&lt;/p&gt;

&lt;p&gt;Add to that the fact that these images are stored in 8 bits per channel. The forward blend already rounded to whole numbers once, which means a sub-pixel of precision was thrown away before you ever got the file. Reversing the math can't un-round it.&lt;/p&gt;

&lt;p&gt;So the accurate claim isn't "flawless, always." It's this: &lt;strong&gt;across the vast majority of the watermarked region, where the logo is semi-transparent, you get the original pixel back with zero meaningful loss. Only in the small, fully-opaque core is anything actually unrecoverable.&lt;/strong&gt; That is a categorically different result from a smear across the whole patch — and it's why the "removal is lossy" rule of thumb quietly breaks here.&lt;/p&gt;

&lt;h2&gt;
  
  
  You have to find the watermark before you touch a pixel
&lt;/h2&gt;

&lt;p&gt;One more piece before the math can run: you need to know &lt;em&gt;where&lt;/em&gt; the logo sits, and you can't just trust that it's in the corner. Get the position wrong and you'll happily subtract a logo from a patch of sky that never had one.&lt;/p&gt;

&lt;p&gt;A reasonable pipeline does this in stages, cheap checks first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Catalog lookup — Gemini emits a small set of known sizes,&lt;/span&gt;
&lt;span class="c1"&gt;//    each with a fixed watermark size and margin.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sizeCatalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;applied&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// not a shape we recognize&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Local anchor search — scan a small region around the&lt;/span&gt;
&lt;span class="c1"&gt;//    expected spot to lock onto the logo's exact offset.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;findAnchor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expectedRegion&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Validation — actually run the reverse blend on that region&lt;/span&gt;
&lt;span class="c1"&gt;//    and check the result is sane before trusting it. This is&lt;/span&gt;
&lt;span class="c1"&gt;//    what stops false positives on images that only look similar.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;restored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;reverseAlphaBlend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alphaMap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;looksRestored&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;restored&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;applied&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;applied&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;restored&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The validation step is the one that matters most for not lying to the user. It's the difference between "I removed the watermark" and "I mangled a corner of your photo because the dimensions happened to match." If the reverse blend doesn't produce something coherent, the honest move is to report that nothing was done.&lt;/p&gt;

&lt;p&gt;It runs in the browser, and nothing leaves it&lt;/p&gt;

&lt;p&gt;Because this is arithmetic over a pixel array and not a model inference, there's no reason to send the image anywhere. It decodes into an ImageData buffer via the Canvas API, the math runs over a typed array right there on the page, and a PNG comes back out. No server, no upload, no queue. The whole thing is a few hundred kilobytes of JavaScript.&lt;/p&gt;

&lt;p&gt;That's a nice side effect of picking the right tool. The "AI must undo AI" framing would have pushed you toward a hosted model and an upload, for a job that a formula does locally and better.&lt;/p&gt;

&lt;p&gt;When you genuinely do need the model&lt;/p&gt;

&lt;p&gt;This is the boundary, and it's worth being precise about it, because the point of this article is not "AI is overkill for everything." It's "match the method to the mark."&lt;/p&gt;

&lt;p&gt;Reverse alpha blending works when the watermark was blended on top — a known logo, a known compositing step, pixels that were mixed rather than destroyed. Push outside that and it stops applying:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fully opaque marks over the content — nothing was mixed, so there's nothing to unmix.&lt;/li&gt;
&lt;li&gt;Invisible / steganographic watermarks like SynthID, which are woven into the image statistics rather than pasted on top. Subtraction has nothing to grab onto.&lt;/li&gt;
&lt;li&gt;Arbitrary objects, text, or logos on a real photo, where there is no known blend formula and the stuff underneath truly is missing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last case is the one everyone pictures when they hear "watermark removal," and it is genuinely lossy — because there, you really are reconstructing missing content, and reconstruction means an educated guess. That's the job diffusion-based inpainting was built for, and if you want the counterpart to this article — how a model fills a hole when the pixels really are gone — I wrote a visual walkthrough of &lt;a href="https://erasio.app/blog/how-image-inpainting-works-explainer" rel="noopener noreferrer"&gt;how AI inpainting actually works&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The two approaches aren't competitors. They answer different questions. One reverses a known operation. The other invents a plausible one. Using the guessing tool on a problem that has an exact answer is how you end up believing all removal is lossy.&lt;/p&gt;

&lt;p&gt;Try it, and read the source&lt;/p&gt;

&lt;p&gt;The reverse-alpha approach for Gemini is open source — the algorithm, the size catalog, the detection stages, all of it: &lt;a href="https://github.com/GargantuaX/gemini-watermark-remover" rel="noopener noreferrer"&gt;gemini-watermark-remover on GitHub&lt;/a&gt; (itself a JavaScript port of Allen Kuo's original tool).&lt;/p&gt;

&lt;p&gt;If you just want to run it on an image without touching code, there's &lt;a href="https://erasio.app/gemini-watermark-remover" rel="noopener noreferrer"&gt;a browser-based Gemini watermark remover&lt;/a&gt; that does exactly the pipeline above — local, no upload, and it tells you when the mark isn't one it can reverse instead of quietly smearing it.&lt;/p&gt;

&lt;p&gt;The next time someone tells you watermark removal is always lossy, they're right about the general case and wrong about this one. The mark made of math comes off with math.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>imageprocessing</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
