Skip to content

athm793/Gemini-Bulk-Studio

 
 

Repository files navigation

Gemini Bulk Studio

A Chrome extension for working with Google Gemini images at scale, entirely on your own machine:

  • Generate in bulk — hand it a CSV of prompts and it drives Gemini for you.
  • Strip watermarks in bulk — remove Gemini's corner watermark with the LaMa inpainting model, running locally in WebAssembly.

The two compose: generated images are cleaned as they arrive, so one CSV gives you a zip of finished images. No server, no API key, no account, and no image ever leaves your computer.

A fork of dinoBOLT/Gemini-Watermark-Remover, substantially extended. See Changes from upstream.

image

Generating images from a CSV

Drop in a CSV of prompts and the extension drives the Gemini web UI in your own browser — new chat, type, send, collect the image — then optionally runs each result straight through the watermark cleaner.

prompt,name
"A friendly cartoon lion reading a book, soft pastel colours",lion-reading
"A paper-craft origami mosque at sunrise, warm light",origami-mosque

One prompt per line also works, with no header at all. The optional name column sets the output filename; otherwise names are derived from the prompt. A ready-made example lives at examples/prompts-example.csv.

Warning

This automates gemini.google.com, which Google's terms prohibit. The account you run it under can be rate-limited or suspended. It also breaks whenever Google changes their page. For anything you depend on, use the official API instead — it is supported, stable, and its images carry no visible watermark at all, so the cleaning half becomes unnecessary. It costs roughly $0.02–0.039/image with a free tier of ~500/day.

The macro fails stop, not stealthy: on any rate limit, refusal, or human check it halts and tells you. There is deliberately no fingerprint spoofing, CAPTCHA solving, or account rotation.

Cleaning images

Gemini stamps a visible watermark into the bottom-right corner of generated images. This tool masks that corner and asks the LaMa (Large Mask Inpainting) model to reconstruct what belongs there, then composites the repaired patch back onto the original at full resolution. Everything outside the watermark corner is left byte-for-byte untouched.

  • Bulk by default — queue any number of images; each is processed in turn with a live per-image status, and one failure never aborts the run
  • Download one or all — click any finished card to save that image, or take the whole run as a .zip. Single images also get a before/after slider
  • Original filenames preservedsunset.png becomes sunset-clean.png
  • Off the main thread — inference runs in a Web Worker, so the tab stays responsive during a long batch
  • Local and private — verifiable in DevTools: no network requests are made after the page loads

Scope and limitations — read this first

It removes the visible watermark only. Gemini images also carry SynthID, an invisible watermark encoded into the pixels themselves. Inpainting a corner does not touch it, and these images remain identifiable as AI-generated. Do not treat the output as untraceable.

It is slow: expect a few seconds per image. A 168-image batch takes tens of minutes. It runs unattended, but it is not fast — see Why it is slow for the specifics.

The watermark must be in the bottom-right. The masked region is fixed (CONFIG.WATERMARK in src/js/config.js). Watermarks elsewhere are not detected.

Keep the tab open. All work happens in that tab and results live in memory until you download them. Closing it cancels the batch and discards anything you have not saved.

Install

Requires Google Chrome 90+ (or any Chromium browser) and about 250MB of disk.

1. Get the code

git clone https://github.com/athm793/Gemini-Bulk-Studio.git

2. Download the AI model — the extension will not work without it

The model is ~198MB, which is over GitHub's 100MB file limit, and this project does not use Git LFS. So it is a manual download:

👉 Download lama_fp32.onnx

Put it in src/assets/:

Gemini-Bulk-Studio/
├── src/assets/lama_fp32.onnx   <-- here (~198MB)
├── manifest.json
└── ...

3. Load it in Chrome

  1. Go to chrome://extensions/
  2. Turn on Developer mode (top-right)
  3. Click Load unpacked and select the folder containing manifest.json

Use

  1. Click the extension icon — it opens the app in a new tab
  2. Drop in one image or hundreds
  3. Wait. Progress and a per-image log are shown as it goes
  4. Download all as .zip, or Download Image for a single result

Files land in your Chrome download folder (usually ~/Downloads).

Why it is slow

The model runs single-threaded on the CPU, and that is a hard constraint rather than an oversight. Both escape routes are closed:

Multi-threading is impossible. ONNX Runtime spawns its WASM threads as workers loaded from blob: URLs. Manifest V3 pins extension pages to script-src 'self' 'wasm-unsafe-eval' and forbids relaxing it, so those workers are blocked outright:

Loading the script 'blob:...' violates the following Content Security Policy
directive: "script-src 'self' 'wasm-unsafe-eval'"

This holds even though the page is cross-origin isolated and SharedArrayBuffer is available (see the COOP/COEP keys in manifest.json) — the blocker is the CSP on blob: scripts, which SAB availability does not change.

WebGPU does not work with this model. It was tried on ONNX Runtime 1.17 and 1.27 and fails identically at run time, on every image:

[WebGPU] Kernel "[Add] /generator/model/model.5/conv1/ffc/convg2g/Add" failed.
Error: Can't perform binary op on the given tensors

The WebGPU EP cannot broadcast the Add inside LaMa's Fourier convolutions. The session builds successfully and only fails once inference runs, which makes it look supported when it is not. The GPU path was removed rather than left in as dead weight.

If you want to revisit this, the blocker is that one kernel — the question is whether a newer ONNX Runtime can broadcast it, not whether WebGPU is "supported".

The realistic remaining lever is an int8-quantized model, worth roughly 2–4x on CPU. The current lama_fp32.onnx is full fp32.

How it works

index.html          App shell and UI
styles.css          Styles
manifest.json       MV3 manifest (+ COOP/COEP, gemini.google.com host permission)
background.js       Opens the app when the toolbar icon is clicked
BRAIN.html          Full architecture, build log, dead ends, risks
examples/           Example prompt CSV
src/
  lib/              ONNX Runtime 1.17.1 + its WASM binaries
  assets/           lama_fp32.onnx (you download this)
  js/
    app.js              Orchestration: both queues, tabs, downloads
    config.js           All tunables and constants, with the reasoning
    ui-manager.js       DOM, batch grid, progress, comparison slider
    model-manager.js    Owns the inference worker and talks to it
    inference-worker.js Runs ONNX Runtime off the main thread
    image-processor.js  Pre/post-processing and final composition
    macro.js            Prompt queue; talks to the content script
    gemini-content.js   Content script: drives the Gemini page
    csv.js              RFC 4180 parser + prompt extraction
    zip.js              Minimal store-only ZIP writer
    utils.js            Validation, logging, helpers

BRAIN.html is the deep reference — architecture, build log, the dead ends below in full, and the risk register. Open it in a browser.

The pipeline per image: decode → resize to 512×512 → build an RGB tensor plus a mask over the watermark corner → run LaMa in the worker → convert the output back to pixels → draw only the repaired corner onto the untouched original → encode to PNG.

Images are processed sequentially. They share one InferenceSession, so concurrent runs would contend for the same WASM heap rather than overlap.

Changes from upstream

What this was when I forked it

I forked dinoBOLT/Gemini-Watermark-Remover at v3.0.0. At that point it was a capable but strictly single-image watermark remover:

  • Drop one image → it masks the corner, inpaints with LaMa, shows a before/after slider, and downloads the result.
  • Selecting multiple files silently kept only the first (files[0]) and discarded the rest.
  • Inference ran inline on the main thread, so a large image froze the tab.
  • No way to generate images — you made them by hand in Gemini first.
  • Six source modules (app, config, image-processor, model-manager, ui-manager, utils) totalling ~356 lines of the parts I later changed.

It worked, and the core inpainting approach is still upstream's. Everything below is what I added or changed on top of it.

What I changed and added

Roughly +3,300 lines across 19 files, including six new modules. The headline is that it went from a one-at-a-time cleaner to a bulk studio — generate from a CSV and clean at scale — hence the rename to Gemini Bulk Studio.

New: generate images from a CSV (entirely mine — no upstream equivalent)

  • A second mode that drives the Gemini web UI from a list of prompts and collects the images, cleaning each one as it arrives.
  • csv.js — a proper RFC 4180 parser (prompts are prose full of commas and quotes), with 13 unit tests including Excel BOM/CRLF and non-Latin scripts.
  • macro.js — the sequential prompt queue, fail-stop on any rate limit or refusal, configurable pacing.
  • gemini-content.js — the content script that types, sends, and extracts the rendered image (the extraction had to work around Gemini revoking its own blob: URLs — see How it works).
  • In-app help explaining exactly how to build the CSV, plus a bundled example.

New: bulk cleaning (upstream was single-image only)

  • Multi-file select and drag-drop, a sequential queue, and a live results grid with per-image status. One failure no longer aborts the run.
  • zip.js — a hand-rolled store-only ZIP writer to download a whole batch at once (CSP blocks CDN libraries; PNGs are already compressed).
  • Click any finished card to download that one image.
  • Output keeps the source filename (x.pngx-clean.png) instead of gemini-clean-<timestamp>.png.

Security and correctness fixes (bugs I found in the upstream code)

  • Fixed an XSS hole: the log and error toast rendered messages via innerHTML, and messages embed user-supplied filenames — so a file named <img src=x onerror=...>.png would execute inside a privileged extension page. Everything builds DOM nodes now.
  • Fixed an unbounded DOM leak: LOG_MAX_LINES trimmed the log array but not the log element, so the on-page log grew without limit.
  • Fixed the progress bar jumping backwards from 50% → 0% when a batch followed a model load.

Performance and memory (rearchitected upstream's approach)

  • Moved inference off the main thread into a Web Worker (inference-worker.js). Upstream ran it inline, which triggered Chrome's "Page unresponsive" dialog on long runs.
  • Results are Blobs behind object URLs instead of base64 data URLs (~33% smaller, revocable); decoded bitmaps are close()d per image, so peak memory stays flat across a batch instead of growing linearly.
  • Tensor buffers are transferred to the worker rather than copied.
  • Raised graphOptimizationLevel from basic to all.

Documentation

  • Added BRAIN.html — full architecture, a build log, the dead ends below, and a risk register.
  • Investigated and rejected two speed routes, documented in src/js/config.js so they are not rediscovered: WASM multi-threading (blocked by MV3's CSP on blob: workers) and WebGPU (a broken kernel on this model, on both ORT 1.17 and 1.27). See Why it is slow.

What I kept from upstream: the LaMa inpainting model and the core mask-and-composite approach, the Apache-2.0 license, and the general UI shape of the cleaning flow. Credit for those is upstream's.

Privacy

Everything runs locally. Image processing makes no network requests at all — the model is read from disk and inpainting happens in WebAssembly. You can verify this in DevTools → Network while cleaning.

The only host permission is gemini.google.com, used solely by the generate feature to drive the page you are already signed into. Nothing is sent anywhere else, and the cleaning side needs no network access whatsoever.

Credits

License

Apache License 2.0 — see LICENSE. Modifications from the original are listed under Changes from upstream.

About

Chrome extension: bulk-generate Gemini images from a CSV of prompts and strip their watermarks with LaMa inpainting. Runs entirely on your machine - no server, no API key.

Topics

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages