mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-07-07 08:57:50 +02:00
0e5a4cbc54
- Add the Jimeng-basic top-left "AI生成" pill as a CAPTURE-LESS mark (pill_engine.py): synthetic-silhouette edge-NCC detect + inpaint-only removal. Gated in remove_auto_marks: kept only when Jimeng is confirmed (TC260 metadata OR the bottom-right "★ 即梦AI" wordmark fired -- the wordmark keeps recall on metadata-STRIPPED uploads) AND Doubao did not fire. - Add an inpaint-fallback removal path + MI-GAN ONNX backend (migan extra, MIT, ~28 MB / ~1 GB peak -- droplet-friendly) alongside big-LaMa. New --method auto|reverse-alpha|inpaint (shared across visible/all/batch) and erase --backend migan; footprint_mask on each engine. - auto is deterministic: reverse-alpha for capture marks (recovers exact pixels, lighter -- measured cleaner than MI-GAN on structured backgrounds) and inpaint only for the capture-less pill. - --mark auto now removes EVERY detected mark in one pass (remove_auto_marks), so a Jimeng-basic image's top-left pill AND bottom-right wordmark both clear. - Bump 0.12.1 -> 0.13.0. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
61 lines
2.3 KiB
Python
61 lines
2.3 KiB
Python
"""Render the SYNTHETIC 'AI生成' pill silhouette asset (data-safe, font-rendered).
|
|
|
|
The Jimeng-basic TC260 visible label is a rounded pill with 'AI生成' in the TOP-LEFT
|
|
corner (issue #54). Unlike the reverse-alpha marks, this is a CAPTURE-LESS mark:
|
|
the committed asset is a font-rendered binary SILHOUETTE (mark shape only, zero photo
|
|
content), used ONLY to (a) detect the pill by edge-NCC in the top-left corner and
|
|
(b) build the inpaint mask. It is NOT an alpha map -- removal quality comes from the
|
|
inpaint backend (MI-GAN/cv2), so the silhouette need not be pixel-accurate, and the
|
|
synthetic render keeps corpus/user content out of the tracked repo (data-safety).
|
|
|
|
Detection was calibrated on the retained local corpus (61 real positives + jimeng
|
|
negatives): edge-NCC threshold ~0.22 in the top-left ROI. Re-run to regenerate the
|
|
asset: uv run python scripts/render_pill_silhouette.py
|
|
|
|
Requires a CJK font (macOS STHeiti by default); the asset itself is committed, so this
|
|
script only runs when regenerating it (never in CI).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import numpy as np
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
_ASSET = Path(__file__).resolve().parents[1] / "src" / "remove_ai_watermarks" / "assets" / "jimeng_pill.png"
|
|
_FONT = "/System/Library/Fonts/STHeiti Medium.ttc"
|
|
|
|
|
|
def render_silhouette(w: int = 320) -> np.ndarray:
|
|
"""Rounded-pill outline + 'AI生成' text as a binary silhouette (255 = mark)."""
|
|
h = int(w * 0.5)
|
|
im = Image.new("L", (w, h), 0)
|
|
d = ImageDraw.Draw(im)
|
|
pad = int(w * 0.03)
|
|
r = (h - 2 * pad) // 3
|
|
d.rounded_rectangle([pad, pad, w - pad, h - pad], radius=r, outline=255, width=max(2, w // 90))
|
|
fsz = int(h * 0.42)
|
|
font = ImageFont.truetype(_FONT, fsz)
|
|
txt = "AI生成"
|
|
tb = d.textbbox((0, 0), txt, font=font)
|
|
tw, th = tb[2] - tb[0], tb[3] - tb[1]
|
|
d.text(((w - tw) // 2 - tb[0], (h - th) // 2 - tb[1]), txt, font=font, fill=255)
|
|
return np.array(im)
|
|
|
|
|
|
def main() -> None:
|
|
try:
|
|
sil = render_silhouette()
|
|
except OSError as e:
|
|
print(f"Font not found ({e}); install a CJK font or edit _FONT.", file=sys.stderr)
|
|
raise SystemExit(1) from e
|
|
_ASSET.parent.mkdir(parents=True, exist_ok=True)
|
|
Image.fromarray(sil).save(_ASSET)
|
|
print(f"wrote {_ASSET} ({sil.shape[1]}x{sil.shape[0]})")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|