v 1.0.1
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Application Port
|
||||||
|
PORT=3000
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Stage 1: Build the application
|
||||||
|
FROM swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/node:20-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Enable pnpm
|
||||||
|
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||||
|
|
||||||
|
# Copy package management files
|
||||||
|
COPY package.json pnpm-lock.yaml ./
|
||||||
|
|
||||||
|
# Install dependencies (ignoring scripts to speed up and safer)
|
||||||
|
# Removing frozen-lockfile restriction in case of version mismatch, ensuring install works
|
||||||
|
RUN pnpm install --no-frozen-lockfile
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build the project
|
||||||
|
RUN pnpm build
|
||||||
|
|
||||||
|
# Stage 2: Serve with Nginx
|
||||||
|
FROM swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:alpine
|
||||||
|
|
||||||
|
# Copy built assets from builder stage
|
||||||
|
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
# Expose port 80 (internal container port)
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
# Start Nginx
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
259
README.md
|
|
@ -1,221 +1,92 @@
|
||||||
[中文文档](README_zh.md)
|
[中文文档](README_zh.md)
|
||||||
|
|
||||||
# Gemini Lossless Watermark Remover - [banana.ovo.re](https://banana.ovo.re)
|
# Gemini & NotebookLM Watermark Cleaner
|
||||||
|
|
||||||
A high-performance, 100% client-side tool for removing Gemini AI watermarks. Built with pure JavaScript, it leverages a mathematically precise **Reverse Alpha Blending** algorithm rather than unpredictable AI inpainting.
|
A privacy-first, browser-based tool for removing watermarks from Gemini and NotebookLM generated content (Images & PDFs). Built with vanilla JavaScript, featuring mathematical precision for lossless restoration and robust fallback cloning for complex document formats.
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://count.getloli.com/@gemini-watermark-remover?name=gemini-watermark-remover&theme=minecraft&padding=7&offset=0&align=top&scale=1&pixelated=1&darkmode=auto" width="400">
|
<img src="https://count.getloli.com/@gemini-watermark-remover?name=gemini-watermark-remover&theme=minecraft&padding=7&offset=0&align=top&scale=1&pixelated=1&darkmode=auto" width="400">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Features
|
## ✨ Features
|
||||||
|
|
||||||
- ✅ **100% Client-side** - No backend, no server-side processing. Your data stays in your browser.
|
- **Multi-Format Support**: Handles standard Gemini Images (`.jpg`, `.png`, `.webp`) and NotebookLM documents (`.pdf`).
|
||||||
- ✅ **Privacy-First** - Images are never uploaded to any server. Period.
|
- **Lossless Restoration**: Uses **Reverse Alpha Blending** for standard Gemini images to perfectly restore original pixels without blurring.
|
||||||
- ✅ **Mathematical Precision** - Based on the Reverse Alpha Blending formula, not "hallucinating" AI models.
|
- **Smart Cloning**: Applies intelligent **Simple Clone** (patching) for NotebookLM headers/footers and PDF slides where alpha maps don't apply.
|
||||||
- ✅ **Auto-Detection** - Intelligent recognition of 48×48 or 96×96 watermark variants.
|
- **Privacy-First**: 100% client-side processing. No files are ever uploaded to any server.
|
||||||
- ✅ **User Friendly** - Simple drag-and-drop interface with instant processing.
|
- **Batch Processing**: Process multiple files at once and download them as a ZIP archive.
|
||||||
- ✅ **Cross-Platform** - Runs smoothly on all modern web browsers.
|
- **Modern UI**: Dark/Light mode support with a clean, responsive interface.
|
||||||
|
- **PDF Preview**: Real-time progress tracking and preview for PDF file cleaning.
|
||||||
|
|
||||||
## Examples
|
## 🛠️ Technology Stack
|
||||||
|
|
||||||
<details open>
|
- **Core Logic**: Vanilla JavaScript (ES Modules)
|
||||||
<summary>Click to Expand/Collapse Examples</summary>
|
- **PDF Processing**: `pdfjs-dist` (Parsing) & `jspdf` (Re-generation)
|
||||||
|
- **UI Styling**: Tailwind CSS (CDN)
|
||||||
<p>lossless diff example</p>
|
- **Packaging**: `esbuild` for bundling
|
||||||
<p><img src="docs/lossless_diff.webp"></p>
|
|
||||||
|
|
||||||
|
## 🧩 How It Works
|
||||||
|
|
||||||
<p>example images</p>
|
This tool employs two distinct strategies depending on the content type:
|
||||||
|
|
||||||
| Original Image | Watermark Removed |
|
### 1. Reverse Alpha Blending (for Gemini Images)
|
||||||
| :---: | :----: |
|
Gemini applies watermarks using a standard alpha compositing formula:
|
||||||
| <img src="docs/1.webp" width="400"> | <img src="docs/unwatermarked_1.webp" width="400"> |
|
$$Pixel_{watermarked} = \alpha \cdot Pixel_{logo} + (1 - \alpha) \cdot Pixel_{original}$$
|
||||||
| <img src="docs/2.webp" width="400"> | <img src="docs/unwatermarked_2.webp" width="400"> |
|
|
||||||
| <img src="docs/3.webp" width="400"> | <img src="docs/unwatermarked_3.webp" width="400"> |
|
|
||||||
| <img src="docs/4.webp" width="400"> | <img src="docs/unwatermarked_4.webp" width="400"> |
|
|
||||||
| <img src="docs/5.webp" width="400"> | <img src="docs/unwatermarked_5.webp" width="400"> |
|
|
||||||
|
|
||||||
</details>
|
We reverse this to recover the original pixel:
|
||||||
|
$$Pixel_{original} = \frac{Pixel_{watermarked} - \alpha \cdot Pixel_{logo}}{1 - \alpha}$$
|
||||||
|
|
||||||
## ⚠️ Disclaimer
|
By using pre-calibrated Alpha Maps (48px/96px), we can mathematically restore the original pixel values with near-zero loss.
|
||||||
|
|
||||||
> [!WARNING]
|
### 2. Smart Cloning (for NotebookLM & PDF)
|
||||||
> **USE AT YOUR OWN RISK**
|
For NotebookLM's strip-like watermarks or PDF slides where exact alpha maps vary, the tool uses a **Simple Clone** technique. It identifies the watermark region and intelligently "clones" a patch of pixels from the immediate neighborhood (e.g., just above the watermark) to cover it, blending the edges for a seamless look.
|
||||||
>
|
|
||||||
> This tool modifies image files. While it is designed to work reliably, unexpected results may occur due to:
|
|
||||||
> - Variations in Gemini's watermark implementation
|
|
||||||
> - Corrupted or unusual image formats
|
|
||||||
> - Edge cases not covered by testing
|
|
||||||
>
|
|
||||||
> The author assumes no responsibility for any data loss, image corruption, or unintended modifications. By using this tool, you acknowledge that you understand these risks.
|
|
||||||
|
|
||||||
> [!NOTE]
|
## 🔗 Attribution & References
|
||||||
> **Note**: Disabling any fingerprint defender extensions (e.g., Canvas Fingerprint Defender) to avoid processing errors. https://github.com/journey-ad/gemini-watermark-remover/issues/3
|
|
||||||
|
|
||||||
## Usage
|
This project is built upon the excellent work of the open-source community. Special thanks to:
|
||||||
|
|
||||||
### Online Website
|
- **Original Project**: [gemini-watermark-remover](https://github.com/journey-ad/gemini-watermark-remover) by [journey-ad](https://github.com/journey-ad).
|
||||||
|
- *Base architecture, alpha map logic, and core "Reverse Alpha Blending" implementation.*
|
||||||
|
|
||||||
1. Open [banana.ovo.re](https://banana.ovo.re).
|
- **Algorithm Origin**: [Gemini Watermark Tool](https://github.com/allenk/GeminiWatermarkTool) by [Allen Kuo](https://github.com/allenk).
|
||||||
2. Drag and drop or click to select your Gemini-generated image.
|
- *Original discovery of the math and watermark masks.*
|
||||||
3. The engine will automatically process and remove the watermark.
|
|
||||||
4. Download the cleaned image.
|
|
||||||
|
|
||||||
### Userscript for Gemini Conversation Pages
|
## 🚀 Usage
|
||||||
|
|
||||||
1. Install a userscript manager (e.g., Tampermonkey or Greasemonkey).
|
### Development
|
||||||
2. Open [gemini-watermark-remover.user.js](https://banana.ovo.re/userscript/gemini-watermark-remover.user.js).
|
1. Clone the repository:
|
||||||
3. The script will install automatically.
|
```bash
|
||||||
4. Navigate to Gemini conversation pages.
|
git clone http://git.unissense.tech/mula/gemini_watermark_cleaner.git
|
||||||
5. Click "Copy Image" or "Download Image" to remove the watermark.
|
```
|
||||||
|
2. Install dependencies:
|
||||||
|
```bash
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
3. Start development mode (rebuilds on changes):
|
||||||
|
```bash
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
4. Start a local preview server:
|
||||||
|
```bash
|
||||||
|
pnpm serve
|
||||||
|
```
|
||||||
|
5. Build for production:
|
||||||
|
```bash
|
||||||
|
pnpm build
|
||||||
|
```
|
||||||
|
|
||||||
## Development
|
### Docker Deployment
|
||||||
|
1. Build and run the container:
|
||||||
|
```bash
|
||||||
|
docker-compose up -d --build
|
||||||
|
```
|
||||||
|
2. The application will be available at `http://localhost:3000` (or the port specified in `.env`).
|
||||||
|
|
||||||
```bash
|
### Browser
|
||||||
# Install dependencies
|
Simply open the `dist/index.html` (after building) or the deployed site. Drag and drop your files.
|
||||||
pnpm install
|
- **Images**: Instant preview and cleaning.
|
||||||
|
- **PDFs**: Shows progress per page, then displays the cleaned first page.
|
||||||
|
|
||||||
# Development build
|
## 📄 License
|
||||||
pnpm dev
|
|
||||||
|
|
||||||
# Production build
|
[MIT License](./LICENSE)
|
||||||
pnpm build
|
|
||||||
|
|
||||||
# Local preview
|
|
||||||
pnpm serve
|
|
||||||
```
|
|
||||||
|
|
||||||
## How it Works
|
|
||||||
|
|
||||||
### The Gemini Watermarking Process
|
|
||||||
|
|
||||||
Gemini applies watermarks using standard alpha compositing:
|
|
||||||
|
|
||||||
$$watermarked = \alpha \cdot logo + (1 - \alpha) \cdot original$$
|
|
||||||
|
|
||||||
Where:
|
|
||||||
- `watermarked`: The pixel value with the watermark.
|
|
||||||
- `α`: The Alpha channel value (0.0 - 1.0).
|
|
||||||
- `logo`: The watermark logo color value (White = 255).
|
|
||||||
- `original`: The raw, original pixel value we want to recover.
|
|
||||||
|
|
||||||
### The Reverse Solution
|
|
||||||
|
|
||||||
To remove the watermark, we solve for `original`:
|
|
||||||
|
|
||||||
$$original = \frac{watermarked - \alpha \cdot logo}{1 - \alpha}$$
|
|
||||||
|
|
||||||
By capturing the watermark on a known solid background, we reconstruct the exact Alpha map and apply the inverse formula to restore the original pixels with zero loss.
|
|
||||||
|
|
||||||
## Detection Rules
|
|
||||||
|
|
||||||
| Image Dimension Condition | Watermark Size | Right Margin | Bottom Margin |
|
|
||||||
| :--- | :--- | :--- | :--- |
|
|
||||||
| Width > 1024 **AND** Height > 1024 | 96×96 | 64px | 64px |
|
|
||||||
| Otherwise | 48×48 | 32px | 32px |
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
|
|
||||||
```text
|
|
||||||
gemini-watermark-remover/
|
|
||||||
├── public/
|
|
||||||
│ ├── index.html # Main page
|
|
||||||
│ └── terms.html # Terms of Service page
|
|
||||||
├── src/
|
|
||||||
│ ├── core/
|
|
||||||
│ │ ├── alphaMap.js # Alpha map calculation logic
|
|
||||||
│ │ ├── blendModes.js # Implementation of Reverse Alpha Blending
|
|
||||||
│ │ └── watermarkEngine.js # Main engine coordinator
|
|
||||||
│ ├── assets/
|
|
||||||
│ │ ├── bg_48.png # Pre-captured 48×48 watermark map
|
|
||||||
│ │ └── bg_96.png # Pre-captured 96×96 watermark map
|
|
||||||
│ ├── i18n/ # Internationalization language files
|
|
||||||
│ ├── userscript/ # Userscript for Gemini
|
|
||||||
│ ├── app.js # Website application entry point
|
|
||||||
│ └── i18n.js # Internationalization utilities
|
|
||||||
├── dist/ # Build output directory
|
|
||||||
├── build.js # Build script
|
|
||||||
└── package.json
|
|
||||||
```
|
|
||||||
|
|
||||||
## Core Modules
|
|
||||||
|
|
||||||
### alphaMap.js
|
|
||||||
|
|
||||||
Calculates the Alpha channel by comparing captured watermark assets:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
export function calculateAlphaMap(bgCaptureImageData) {
|
|
||||||
// Extract max RGB channel and normalize to [0, 1]
|
|
||||||
const alphaMap = new Float32Array(width * height);
|
|
||||||
for (let i = 0; i < alphaMap.length; i++) {
|
|
||||||
const maxChannel = Math.max(r, g, b);
|
|
||||||
alphaMap[i] = maxChannel / 255.0;
|
|
||||||
}
|
|
||||||
return alphaMap;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### blendModes.js
|
|
||||||
|
|
||||||
The mathematical core of the tool:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
export function removeWatermark(imageData, alphaMap, position) {
|
|
||||||
// Formula: original = (watermarked - α × 255) / (1 - α)
|
|
||||||
for (let row = 0; row < height; row++) {
|
|
||||||
for (let col = 0; col < width; col++) {
|
|
||||||
const alpha = Math.min(alphaMap[idx], MAX_ALPHA);
|
|
||||||
const original = (watermarked - alpha * 255) / (1.0 - alpha);
|
|
||||||
imageData.data[idx] = Math.max(0, Math.min(255, original));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Browser Compatibility
|
|
||||||
|
|
||||||
- ✅ Chrome 90+
|
|
||||||
- ✅ Firefox 88+
|
|
||||||
- ✅ Safari 14+
|
|
||||||
- ✅ Edge 90+
|
|
||||||
|
|
||||||
Required APIs:
|
|
||||||
- ES6 Modules
|
|
||||||
- Canvas API
|
|
||||||
- Async/Await
|
|
||||||
- TypedArray (Float32Array, Uint8ClampedArray)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Limitations
|
|
||||||
|
|
||||||
- Only removes **Gemini visible watermarks** <small>(the semi-transparent logo in bottom-right)</small>
|
|
||||||
- Does not remove invisible/steganographic watermarks. <small>[(Learn more about SynthID)](https://support.google.com/gemini/answer/16722517)</small>
|
|
||||||
- Designed for Gemini's current watermark pattern <small>(as of 2025)</small>
|
|
||||||
|
|
||||||
## Legal Disclaimer
|
|
||||||
|
|
||||||
This tool is provided for **personal and educational use only**.
|
|
||||||
|
|
||||||
The removal of watermarks may have legal implications depending on your jurisdiction and the intended use of the images. Users are solely responsible for ensuring their use of this tool complies with applicable laws, terms of service, and intellectual property rights.
|
|
||||||
|
|
||||||
The author does not condone or encourage the misuse of this tool for copyright infringement, misrepresentation, or any other unlawful purposes.
|
|
||||||
|
|
||||||
**THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM THE USE OF THIS SOFTWARE.**
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
|
|
||||||
This project is a JavaScript port of the [Gemini Watermark Tool](https://github.com/allenk/GeminiWatermarkTool) by Allen Kuo ([@allenk](https://github.com/allenk)).
|
|
||||||
|
|
||||||
The Reverse Alpha Blending method and calibrated watermark masks are based on the original work © 2024 AllenK (Kwyshell), licensed under MIT License.
|
|
||||||
|
|
||||||
## Related Links
|
|
||||||
|
|
||||||
- [Gemini Watermark Tool](https://github.com/allenk/GeminiWatermarkTool)
|
|
||||||
- [Removing Gemini AI Watermarks: A Deep Dive into Reverse Alpha Blending](https://allenkuo.medium.com/removing-gemini-ai-watermarks-a-deep-dive-into-reverse-alpha-blending-bbbd83af2a3f)
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[MIT License](./LICENSE)
|
|
||||||
279
README_zh.md
|
|
@ -1,241 +1,92 @@
|
||||||
[English](README.md)
|
[English](README.md)
|
||||||
|
|
||||||
# Gemini 无损去水印工具 - [banana.ovo.re](https://banana.ovo.re)
|
# Gemini & NotebookLM 无损去水印工具
|
||||||
|
|
||||||
基于 Javascript 的纯浏览器端 Gemini AI 图像无损去水印工具,使用数学精确的反向 Alpha 混合算法
|
基于浏览器的隐私优先去水印工具,专为处理 Gemini 和 NotebookLM 生成的内容(图片及 PDF)而设计。项目采用原生 JavaScript 构建,结合了数学精确的无损还原算法与鲁棒的智能修补技术。
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://count.getloli.com/@gemini-watermark-remover?name=gemini-watermark-remover&theme=minecraft&padding=7&offset=0&align=top&scale=1&pixelated=1&darkmode=auto" width="400">
|
<img src="https://count.getloli.com/@gemini-watermark-remover?name=gemini-watermark-remover&theme=minecraft&padding=7&offset=0&align=top&scale=1&pixelated=1&darkmode=auto" width="400">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## 特性
|
## ✨ 功能特性
|
||||||
|
|
||||||
- ✅ **纯浏览器端处理** - 无需后端服务器,所有处理在本地完成
|
- **多格式支持**:完美支持 Gemini 生成的标准图片 (`.jpg`, `.png`, `.webp`) 以及 NotebookLM 文档 (`.pdf`)。
|
||||||
- ✅ **隐私保护** - 图片不会上传到任何服务器
|
- **无损还原**:针对 Gemini 标准图片,采用 **反向 Alpha 混合 (Reverse Alpha Blending)** 算法,像素级还原原图,拒绝模糊。
|
||||||
- ✅ **数学精确** - 基于反向 Alpha 混合算法,非 AI 模型
|
- **智能修补**:针对 NotebookLM 的条状水印或 PDF 幻灯片,采用 **智能克隆 (Simple Clone)** 技术,自动采样邻近像素进行修补融合。
|
||||||
- ✅ **自动检测** - 自动识别 48×48 或 96×96 水印尺寸
|
- **隐私优先**:100% 纯浏览器本地处理,任何文件都不会上传至服务器。
|
||||||
- ✅ **易于使用** - 拖拽选择图片,一键处理
|
- **批量处理**:支持多文件拖拽上传,处理完成后可打包为 ZIP 一键下载。
|
||||||
- ✅ **跨平台** - 支持所有现代浏览器
|
- **现代 UI**:支持深色/浅色模式切换,界面简洁响应快。
|
||||||
|
- **PDF 预览**:PDF 处理时实时显示页码进度,完成后可预览处理好的首页。
|
||||||
|
|
||||||
## 效果示例
|
## 🛠️ 技术栈
|
||||||
|
|
||||||
<details open>
|
- **核心逻辑**: Vanilla JavaScript (ES Modules)
|
||||||
<summary>点击查看/收起示例</summary>
|
- **PDF 处理**: `pdfjs-dist` (解析) & `jspdf` (生成)
|
||||||
|
- **UI 样式**: Tailwind CSS (CDN)
|
||||||
<p>无损 diff 示例</p>
|
- **构建工具**: `esbuild`
|
||||||
<p><img src="docs/lossless_diff.webp"></p>
|
|
||||||
|
|
||||||
|
## 🧩 原理说明
|
||||||
|
|
||||||
<p>示例图片</p>
|
本工具根据文件类型自动选择两种不同的处理策略:
|
||||||
|
|
||||||
| 原图 | 去水印后 |
|
### 1. 反向 Alpha 混合 (适用于 Gemini 图片)
|
||||||
| :---: | :----: |
|
Gemini 添加水印使用的是标准的 Alpha 合成公式:
|
||||||
| <img src="docs/1.webp" width="400"> | <img src="docs/unwatermarked_1.webp" width="400"> |
|
$$Pixel_{watermarked} = \alpha \cdot Pixel_{logo} + (1 - \alpha) \cdot Pixel_{original}$$
|
||||||
| <img src="docs/2.webp" width="400"> | <img src="docs/unwatermarked_2.webp" width="400"> |
|
|
||||||
| <img src="docs/3.webp" width="400"> | <img src="docs/unwatermarked_3.webp" width="400"> |
|
|
||||||
| <img src="docs/4.webp" width="400"> | <img src="docs/unwatermarked_4.webp" width="400"> |
|
|
||||||
| <img src="docs/5.webp" width="400"> | <img src="docs/unwatermarked_5.webp" width="400"> |
|
|
||||||
|
|
||||||
</details>
|
我们通过反向推导来恢复原始像素:
|
||||||
|
$$Pixel_{original} = \frac{Pixel_{watermarked} - \alpha \cdot Pixel_{logo}}{1 - \alpha}$$
|
||||||
|
|
||||||
## ⚠️ 使用需注意
|
利用预先提取的 Alpha Map(48px/96px),我们可以精确计算出原始像素值,实现近乎零损失的还原。
|
||||||
|
|
||||||
> [!WARNING]
|
### 2. 智能克隆修补 (适用于 NotebookLM & PDF)
|
||||||
> **使用此工具产生的风险由用户自行承担**
|
对于 NotebookLM 的条状水印或某些 PDF 页面,由于缺乏固定的 Alpha Map,工具采用 **Simple Clone** 技术。它自动定位水印区域,从紧邻水印上方或周围的区域“克隆”像素块覆盖水印,并对边缘进行羽化融合,从而达到视觉上的消除效果。
|
||||||
>
|
|
||||||
> 本工具涉及对图像数据的修改。尽管在设计上力求处理结果的可靠性,但由于以下因素,仍可能产生非预期的处理结果:
|
|
||||||
> - Gemini 水印实现方式的更新或变动
|
|
||||||
> - 图像文件损坏或使用了非标准格式
|
|
||||||
> - 测试案例未能覆盖的边界情况
|
|
||||||
>
|
|
||||||
> 作者对任何形式的数据丢失、图像损坏或非预期的修改结果不承担法律责任。使用本工具即代表您已了解并接受上述风险。
|
|
||||||
|
|
||||||
> [!NOTE]
|
## 🔗 致谢与参考
|
||||||
> 另请注意:使用此工具需禁用 Canvas 指纹防护扩展(如 Canvas Fingerprint Defender),否则可能会导致处理结果错误。 https://github.com/journey-ad/gemini-watermark-remover/issues/3
|
|
||||||
|
|
||||||
## 使用方法
|
本项目站在巨人的肩膀上,特别感谢开源社区的杰出贡献:
|
||||||
|
|
||||||
### 在线使用
|
- **原项目**: [gemini-watermark-remover](https://github.com/journey-ad/gemini-watermark-remover) by [journey-ad](https://github.com/journey-ad)。
|
||||||
|
- *提供了项目的基础架构、Alpha Map 核心逻辑及反向混合算法的初始实现。*
|
||||||
|
|
||||||
1. 浏览器打开 [banana.ovo.re](https://banana.ovo.re)
|
- **算法起源**: [Gemini Watermark Tool](https://github.com/allenk/GeminiWatermarkTool) by [Allen Kuo](https://github.com/allenk)。
|
||||||
2. 拖拽或点击选择带水印的 Gemini 图片
|
- *最早发现并公开了该数学原理及水印掩膜数据。*
|
||||||
3. 图片会自动开始处理,移除水印
|
|
||||||
4. 下载处理后的图片
|
|
||||||
|
|
||||||
### 油猴脚本
|
## 🚀 使用指南
|
||||||
|
|
||||||
1. 安装油猴插件(如 Tampermonkey 或 Greasemonkey)
|
### 本地开发
|
||||||
2. 打开 [gemini-watermark-remover.user.js](https://banana.ovo.re/userscript/gemini-watermark-remover.user.js)
|
1. 克隆代码仓库:
|
||||||
3. 脚本会自动安装到浏览器中
|
```bash
|
||||||
4. Gemini 对话页面点击复制或者下载图片时,会自动移除水印
|
git clone http://git.unissense.tech/mula/gemini_watermark_cleaner.git
|
||||||
|
```
|
||||||
|
2. 安装依赖:
|
||||||
|
```bash
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
3. 启动开发模式(持续监听变化):
|
||||||
|
```bash
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
4. 启动本地预览服务:
|
||||||
|
```bash
|
||||||
|
pnpm serve
|
||||||
|
```
|
||||||
|
5. 构建生产版本:
|
||||||
|
```bash
|
||||||
|
pnpm build
|
||||||
|
```
|
||||||
|
|
||||||
## 开发
|
### Docker 部署
|
||||||
|
1. 构建并启动容器:
|
||||||
|
```bash
|
||||||
|
docker-compose up -d --build
|
||||||
|
```
|
||||||
|
2. 访问 `http://localhost:3000`(或在 `.env` 中配置的端口)即可使用应用。
|
||||||
|
|
||||||
```bash
|
### 浏览器使用
|
||||||
# 安装依赖
|
构建完成后打开 `dist/index.html`,或直接访问部署好的站点。将文件拖入上传区域即可:
|
||||||
pnpm install
|
- **图片**:即时预览去水印结果。
|
||||||
|
- **PDF**:显示处理进度(页码),完成后预览并支持下载。
|
||||||
|
|
||||||
# 开发构建
|
## 📄 许可证
|
||||||
pnpm dev
|
|
||||||
|
|
||||||
# 生产构建
|
[MIT License](./LICENSE)
|
||||||
pnpm build
|
|
||||||
|
|
||||||
# 本地预览
|
|
||||||
pnpm serve
|
|
||||||
```
|
|
||||||
|
|
||||||
## 算法原理
|
|
||||||
|
|
||||||
### Gemini 添加水印的方式
|
|
||||||
|
|
||||||
Gemini 通过以下方式添加水印:
|
|
||||||
|
|
||||||
$$watermarked = \alpha \cdot logo + (1 - \alpha) \cdot original$$
|
|
||||||
|
|
||||||
其中:
|
|
||||||
- `watermarked`: 带水印的像素值
|
|
||||||
- `α`: Alpha 通道值 (0.0-1.0)
|
|
||||||
- `logo`: 水印 logo 的颜色值(白色 = 255)
|
|
||||||
- `original`: 原始像素值
|
|
||||||
|
|
||||||
### 反向求解移除水印
|
|
||||||
|
|
||||||
为了去除水印,可以反向求解如下:
|
|
||||||
|
|
||||||
$$original = \frac{watermarked - \alpha \cdot logo}{1 - \alpha}$$
|
|
||||||
|
|
||||||
通过在纯色背景上捕获水印,我们可以重建 Alpha 通道,然后应用反向公式恢复原始图像
|
|
||||||
|
|
||||||
## 水印检测规则
|
|
||||||
|
|
||||||
| 图像尺寸条件 | 水印尺寸 | 右边距 | 下边距 |
|
|
||||||
|------------|---------|--------|--------|
|
|
||||||
| 宽 > 1024 **且** 高 > 1024 | 96×96 | 64px | 64px |
|
|
||||||
| 其他情况 | 48×48 | 32px | 32px |
|
|
||||||
|
|
||||||
## 项目结构
|
|
||||||
|
|
||||||
```
|
|
||||||
gemini-watermark-remover/
|
|
||||||
├── public/
|
|
||||||
│ ├── index.html # 主页面
|
|
||||||
│ └── terms.html # 使用条款页面
|
|
||||||
├── src/
|
|
||||||
│ ├── core/
|
|
||||||
│ │ ├── alphaMap.js # Alpha map 计算
|
|
||||||
│ │ ├── blendModes.js # 反向 alpha 混合算法
|
|
||||||
│ │ └── watermarkEngine.js # 主引擎
|
|
||||||
│ ├── assets/
|
|
||||||
│ │ ├── bg_48.png # 48×48 水印背景
|
|
||||||
│ │ └── bg_96.png # 96×96 水印背景
|
|
||||||
│ ├── i18n/ # 国际化语言文件
|
|
||||||
│ ├── userscript/ # 用户脚本
|
|
||||||
│ ├── app.js # 网站应用入口
|
|
||||||
│ └── i18n.js # 国际化工具
|
|
||||||
├── dist/ # 构建输出目录
|
|
||||||
├── build.js # 构建脚本
|
|
||||||
└── package.json
|
|
||||||
```
|
|
||||||
|
|
||||||
## 核心模块
|
|
||||||
|
|
||||||
### alphaMap.js
|
|
||||||
|
|
||||||
从背景捕获图像计算 Alpha 通道:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
export function calculateAlphaMap(bgCaptureImageData) {
|
|
||||||
// 提取 RGB 通道最大值并归一化到 [0, 1]
|
|
||||||
const alphaMap = new Float32Array(width * height);
|
|
||||||
for (let i = 0; i < alphaMap.length; i++) {
|
|
||||||
const maxChannel = Math.max(r, g, b);
|
|
||||||
alphaMap[i] = maxChannel / 255.0;
|
|
||||||
}
|
|
||||||
return alphaMap;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### blendModes.js
|
|
||||||
|
|
||||||
实现反向 Alpha 混合算法:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
export function removeWatermark(imageData, alphaMap, position) {
|
|
||||||
// 对每个像素应用公式:original = (watermarked - α × 255) / (1 - α)
|
|
||||||
for (let row = 0; row < height; row++) {
|
|
||||||
for (let col = 0; col < width; col++) {
|
|
||||||
const alpha = Math.min(alphaMap[idx], MAX_ALPHA);
|
|
||||||
const original = (watermarked - alpha * 255) / (1.0 - alpha);
|
|
||||||
imageData.data[idx] = Math.max(0, Math.min(255, original));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### watermarkEngine.js
|
|
||||||
|
|
||||||
主引擎类,协调整个处理流程:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
export class WatermarkEngine {
|
|
||||||
async removeWatermarkFromImage(image) {
|
|
||||||
// 1. 检测水印尺寸
|
|
||||||
const config = detectWatermarkConfig(width, height);
|
|
||||||
|
|
||||||
// 2. 获取 alpha map
|
|
||||||
const alphaMap = await this.getAlphaMap(config.logoSize);
|
|
||||||
|
|
||||||
// 3. 移除水印
|
|
||||||
removeWatermark(imageData, alphaMap, position);
|
|
||||||
|
|
||||||
return canvas;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 浏览器兼容性
|
|
||||||
|
|
||||||
- ✅ Chrome 90+
|
|
||||||
- ✅ Firefox 88+
|
|
||||||
- ✅ Safari 14+
|
|
||||||
- ✅ Edge 90+
|
|
||||||
|
|
||||||
需要支持:
|
|
||||||
- ES6 Modules
|
|
||||||
- Canvas API
|
|
||||||
- Async/Await
|
|
||||||
- TypedArray (Float32Array, Uint8ClampedArray)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 局限性
|
|
||||||
|
|
||||||
- 只去除了 **Gemini 可见的水印**<small>(即右下角的半透明 Logo)</small>
|
|
||||||
- 无法去除隐形或隐写水印。<small>[(了解更多关于 SynthID 的信息)](https://support.google.com/gemini/answer/16722517)</small>
|
|
||||||
- 针对 Gemini 当前的水印模式设计<small>(截至 2025 年)</small>
|
|
||||||
|
|
||||||
## 免责声明
|
|
||||||
|
|
||||||
本工具仅限**个人学习研究**所用,不得用于商业用途。
|
|
||||||
|
|
||||||
根据您所在的司法管辖区及图像的实际用途,移除水印的行为可能具有潜在的法律影响。用户需自行确保其使用行为符合适用法律、相关服务条款以及知识产权规定,并对此承担全部责任。
|
|
||||||
|
|
||||||
作者不纵容也不鼓励将本工具用于侵犯版权、虚假陈述或任何其他非法用途。
|
|
||||||
|
|
||||||
**本软件按“原样”提供,不提供任何形式(无论是明示或暗示)的保证。在任何情况下,作者均不对因使用本软件而产生的任何索赔、损害或其他责任承担任何义务。**
|
|
||||||
|
|
||||||
## 致谢
|
|
||||||
|
|
||||||
本项目是 [Gemini Watermark Tool](https://github.com/allenk/GeminiWatermarkTool) 的 JavaScript 移植版本,原作者 Allen Kuo ([@allenk](https://github.com/allenk))
|
|
||||||
|
|
||||||
反向 Alpha 混合算法和用于校准的水印图像基于原作者的工作 © 2024 AllenK (Kwyshell),采用 MIT 许可证
|
|
||||||
|
|
||||||
## 相关链接
|
|
||||||
|
|
||||||
- [Gemini Watermark Tool](https://github.com/allenk/GeminiWatermarkTool)
|
|
||||||
- [算法原理说明](https://allenkuo.medium.com/removing-gemini-ai-watermarks-a-deep-dive-into-reverse-alpha-blending-bbbd83af2a3f)
|
|
||||||
|
|
||||||
## 许可证
|
|
||||||
|
|
||||||
[MIT License](./LICENSE)
|
|
||||||
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 2.5 MiB |
|
Before Width: | Height: | Size: 8.9 MiB After Width: | Height: | Size: 8.9 MiB |
|
Before Width: | Height: | Size: 586 KiB After Width: | Height: | Size: 586 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 5.8 MiB |
|
Before Width: | Height: | Size: 5.8 MiB After Width: | Height: | Size: 5.8 MiB |
|
Before Width: | Height: | Size: 5.9 MiB After Width: | Height: | Size: 5.9 MiB |
|
Before Width: | Height: | Size: 6.3 MiB After Width: | Height: | Size: 6.3 MiB |
|
Before Width: | Height: | Size: 6.2 MiB After Width: | Height: | Size: 6.2 MiB |
|
Before Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 4.1 MiB |
BIN
data/天王星.png
|
Before Width: | Height: | Size: 5.8 MiB |
|
|
@ -0,0 +1,14 @@
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
container_name: gemini-watermark-cleaner
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
# Maps Host Port (from .env) to Container Port (80)
|
||||||
|
- "${PORT:-3000}:80"
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
|
@ -2,10 +2,10 @@
|
||||||
"name": "gemini-watermark-remover",
|
"name": "gemini-watermark-remover",
|
||||||
"description": "Automatically removes watermarks from Gemini AI generated images",
|
"description": "Automatically removes watermarks from Gemini AI generated images",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "journey-ad",
|
"author": "mula.liu",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/journey-ad/gemini-watermark-remover.git"
|
"url": "http://git.unissense.tech/mula/gemini_watermark_cleaner"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
|
||||||
16
src/app.js
|
|
@ -247,7 +247,17 @@ async function processSingle(item) {
|
||||||
let previewUrl;
|
let previewUrl;
|
||||||
|
|
||||||
if (item.isPdf) {
|
if (item.isPdf) {
|
||||||
const result = await processPdf(item.file);
|
// Show result section immediately for progress
|
||||||
|
processedSection.style.display = 'block';
|
||||||
|
processedImage.src = item.originalUrl || img.src;
|
||||||
|
processedImage.style.opacity = '0.5'; // Dim original while processing
|
||||||
|
processedInfo.innerHTML = `<span class="text-accent">${i18n.t('status.processing')}</span>`;
|
||||||
|
|
||||||
|
const result = await processPdf(item.file, (page, total) => {
|
||||||
|
processedInfo.innerHTML = `<span class="text-accent">${i18n.t('status.processing')} ${page}/${total}</span>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
processedImage.style.opacity = '1';
|
||||||
blob = result.pdfBlob;
|
blob = result.pdfBlob;
|
||||||
previewUrl = result.previewDataUrl;
|
previewUrl = result.previewDataUrl;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -476,9 +486,9 @@ async function processPdf(file, onProgress) {
|
||||||
// So I should force clone for PDF to use `NOTEBOOK_CLONE_CONFIG`?
|
// So I should force clone for PDF to use `NOTEBOOK_CLONE_CONFIG`?
|
||||||
// Let's assume PDF pages are NotebookLM slides.
|
// Let's assume PDF pages are NotebookLM slides.
|
||||||
|
|
||||||
await engine.removeWatermarkFromImage(canvas, { forceSimpleClone: true });
|
const cleanedCanvas = await engine.removeWatermarkFromImage(canvas, { forceSimpleClone: true });
|
||||||
|
|
||||||
const imgData = canvas.toDataURL('image/jpeg', 0.9);
|
const imgData = cleanedCanvas.toDataURL('image/jpeg', 0.9);
|
||||||
|
|
||||||
// Capture first page as preview
|
// Capture first page as preview
|
||||||
if (i === 1) {
|
if (i === 1) {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,14 @@ import BG_96_PATH from '../assets/bg_96.png';
|
||||||
import BG_NOTEBOOK_PATH from '../assets/bg_notebook.png';
|
import BG_NOTEBOOK_PATH from '../assets/bg_notebook.png';
|
||||||
import BG_NOTEBOOK_LARGE_PATH from '../assets/bg_notebook_large.png';
|
import BG_NOTEBOOK_LARGE_PATH from '../assets/bg_notebook_large.png';
|
||||||
|
|
||||||
|
const GEMINI_CLONE_CONFIG = {
|
||||||
|
wRatio: 0.06, wRef: 'long',
|
||||||
|
hRatio: 0.06, hRef: 'long',
|
||||||
|
mrRatio: 0.04, mrRef: 'long',
|
||||||
|
mbRatio: 0.04, mbRef: 'long',
|
||||||
|
featherSize: 12
|
||||||
|
};
|
||||||
|
|
||||||
const NOTEBOOK_CLONE_CONFIG = {
|
const NOTEBOOK_CLONE_CONFIG = {
|
||||||
wRatio: 0.0825, wRef: 'long',
|
wRatio: 0.0825, wRef: 'long',
|
||||||
hRatio: 0.0375, hRef: 'short',
|
hRatio: 0.0375, hRef: 'short',
|
||||||
|
|
@ -293,17 +301,18 @@ export class WatermarkEngine {
|
||||||
// Draw original image onto canvas
|
// Draw original image onto canvas
|
||||||
ctx.drawImage(image, 0, 0);
|
ctx.drawImage(image, 0, 0);
|
||||||
|
|
||||||
|
// Detect watermark configuration
|
||||||
|
const config = detectWatermarkConfig(canvas.width, canvas.height);
|
||||||
|
|
||||||
// Use simple clone if forced (e.g. for PDFs or explicit override)
|
// Use simple clone if forced (e.g. for PDFs or explicit override)
|
||||||
if (options.forceSimpleClone) {
|
if (options.forceSimpleClone) {
|
||||||
this.removeWatermarkSimpleClone(ctx, canvas.width, canvas.height, NOTEBOOK_CLONE_CONFIG);
|
const cloneConfig = config.type === 'notebooklm' ? NOTEBOOK_CLONE_CONFIG : GEMINI_CLONE_CONFIG;
|
||||||
|
this.removeWatermarkSimpleClone(ctx, canvas.width, canvas.height, cloneConfig);
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get image data
|
// Get image data
|
||||||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
// Detect watermark configuration
|
|
||||||
const config = detectWatermarkConfig(canvas.width, canvas.height);
|
|
||||||
const position = calculateWatermarkPosition(canvas.width, canvas.height, config);
|
const position = calculateWatermarkPosition(canvas.width, canvas.height, config);
|
||||||
|
|
||||||
if (config.type === 'notebooklm') {
|
if (config.type === 'notebooklm') {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
"preview.result": "Processed Result",
|
"preview.result": "Processed Result",
|
||||||
"panel.title": "Control Panel",
|
"panel.title": "Control Panel",
|
||||||
"btn.download": "Download",
|
"btn.download": "Download",
|
||||||
"btn.reset": "Reset / Change Image",
|
"btn.reset": "Reset / Change File",
|
||||||
"progress.text": "Progress",
|
"progress.text": "Progress",
|
||||||
"btn.downloadAll": "Download All",
|
"btn.downloadAll": "Download All",
|
||||||
"feature.title": "Features",
|
"feature.title": "Features",
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
"preview.result": "处理结果",
|
"preview.result": "处理结果",
|
||||||
"panel.title": "操作面板",
|
"panel.title": "操作面板",
|
||||||
"btn.download": "下载结果",
|
"btn.download": "下载结果",
|
||||||
"btn.reset": "重置 / 更换图片",
|
"btn.reset": "重置 / 更换文件",
|
||||||
"progress.text": "处理进度",
|
"progress.text": "处理进度",
|
||||||
"btn.downloadAll": "全部下载",
|
"btn.downloadAll": "全部下载",
|
||||||
"feature.title": "功能特点",
|
"feature.title": "功能特点",
|
||||||
|
|
|
||||||