mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-10-14 13:30:55 +02:00
Add paid-service captures, aggregator attribution, and provenance vetoes
Collect provider samples from Higgsfield, Runway and Dreamina plus Google Photos, Gemini, Grok and YouTube re-encodes into data/captures/2026-09 with a hash-checked manifest, and use them to fix attribution and false positives: - Attribute aggregator outputs: Higgsfield job metadata, Dreamina's re-signed manifests, Runway's own C2PA signer, TC260 video producers. - Veto any visible video mark when a valid C2PA claim names another vendor (a Gemini Omni clip read as Sora on steam), and gate Kling and Hailuo matches on edge, anchor and white-label evidence. - Veto the Gemini sparkle when trusted provenance names another vendor. - Report SynthID as present for Google Photos AI edits: Google's checker found it on all four measured edits, though manifests record no action. - Add Vidu video and Wan image marks, TC260 audio placements, and record the AIGC key in metadata records. - Document the research sweep, the Dreamina badge decision, and verified Google statements on SynthID in Photos. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6f629a4ea6
commit
7ba07869d2
192 files changed
+2582
-194
No files matched your search
@@ -297,7 +297,9 @@ def _pixel_fingerprint(path: Path) -> tuple[str, int, int, str]:
|
||||
"""Return a bounded-memory RGBA pixel hash, width, height, and format."""
|
||||
with Image.open(path) as image:
|
||||
image.load()
|
||||
image_format = image.format
|
||||
# iPhone JPEGs are MPO: a JPEG primary image followed by an HDR gain map.
|
||||
# PIL decodes the primary frame, which is what a provider checks.
|
||||
image_format = "JPEG" if image.format == "MPO" else image.format
|
||||
if image_format not in {"JPEG", "PNG", "WEBP"}:
|
||||
raise ValueError(f"unsupported image format: {image_format or 'unknown'}")
|
||||
digest = hashlib.sha256()
|
||||
|
||||
@@ -81,6 +81,9 @@ MARKS = {
|
||||
"chromastudio_alpha.png": "ChromaStudio.ai",
|
||||
"digenai_alpha.png": "DIGENAI",
|
||||
"gendo_alpha.png": "GendoAI",
|
||||
# Alibaba Wan (通义万相) bottom-right logo plus "Wan" wordmark. Sentinel:
|
||||
# drawn by draw_wan(), not font-rendered alone.
|
||||
"wan_alpha.png": "Wan logo + Wan",
|
||||
# Jianying / 剪映, the China product in the CapCut family, stamps 剪映AI
|
||||
# bottom-right (the international CapCut pill sits top-left).
|
||||
"jianying_alpha.png": "剪映AI",
|
||||
@@ -300,10 +303,65 @@ def draw_samsung_en(width: int = 335) -> np.ndarray:
|
||||
return arr[ys.min() : ys.max() + 1, xs.min() : xs.max() + 1]
|
||||
|
||||
|
||||
def draw_wan(width: int = 335) -> np.ndarray:
|
||||
"""Alibaba Wan logo plus "Wan" wordmark, bottom-right, light-glyph class.
|
||||
|
||||
Geometry measured on the one real Wan 2.7 Pro export (2048 px square,
|
||||
2026-09-23): logo 102 x 103 px, a 19 px gap, then "Wan" 147 px wide with a
|
||||
51 px cap height centered on the logo; 268 x 103 overall. The logo is
|
||||
approximated as a hexagon with a central triangular hole and three pinwheel
|
||||
cuts; the wordmark is Avenir Next Demi Bold, the nearest installed geometric sans.
|
||||
Hole size, cut width and weight were chosen by the best match to the real mark
|
||||
over a small grid (0.66 vs 0.60-0.65 for the neighbors).
|
||||
"""
|
||||
import math
|
||||
|
||||
k = width / 268
|
||||
h = round(103 * k)
|
||||
im = Image.new("L", (width, h), 0)
|
||||
d = ImageDraw.Draw(im)
|
||||
logo = round(102 * k)
|
||||
cx, cy, radius = logo / 2, h / 2, logo / 2
|
||||
d.polygon(
|
||||
[
|
||||
(cx + radius * math.cos(math.radians(60 * i)), cy + radius * 0.98 * math.sin(math.radians(60 * i)))
|
||||
for i in range(6)
|
||||
],
|
||||
fill=255,
|
||||
)
|
||||
inner = [
|
||||
(
|
||||
cx + radius * 0.42 * math.cos(math.radians(90 + 120 * i)),
|
||||
cy + radius * 0.42 * math.sin(math.radians(90 + 120 * i)),
|
||||
)
|
||||
for i in range(3)
|
||||
]
|
||||
d.polygon(inner, fill=0)
|
||||
cut = max(2, round(logo * 0.05))
|
||||
for i, (px, py) in enumerate(inner):
|
||||
angle = math.radians(90 + 120 * i + 90)
|
||||
d.line(
|
||||
[(px, py), (px + radius * 1.2 * math.cos(angle), py + radius * 1.2 * math.sin(angle))], fill=0, width=cut
|
||||
)
|
||||
text = "Wan"
|
||||
probe = ImageDraw.Draw(Image.new("L", (10, 10)))
|
||||
font = _fit_font(
|
||||
"/System/Library/Fonts/Avenir Next.ttc",
|
||||
lambda f: probe.textbbox((0, 0), text, font=f)[3] - probe.textbbox((0, 0), text, font=f)[1] >= round(51 * k),
|
||||
index=2,
|
||||
)
|
||||
bb = probe.textbbox((0, 0), text, font=font)
|
||||
d.text((round((102 + 19) * k) - bb[0], round(h / 2 - (bb[3] - bb[1]) / 2) - bb[1]), text, font=font, fill=255)
|
||||
arr = np.array(im)
|
||||
ys, xs = np.where(arr > 0)
|
||||
return arr[ys.min() : ys.max() + 1, xs.min() : xs.max() + 1]
|
||||
|
||||
|
||||
_CUSTOM_RENDERERS = {
|
||||
"catlogo_alpha.png": draw_catlogo,
|
||||
"microsoft_alpha.png": draw_msbadge,
|
||||
"samsung_en_alpha.png": draw_samsung_en,
|
||||
"wan_alpha.png": draw_wan,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -283,6 +283,14 @@ def _video_mark_frame(key: str, w: int, h: int) -> np.ndarray:
|
||||
tmpl, scale, x, y = templates["dola"], 0.036, int(w * 0.70), int(h * 0.88)
|
||||
elif key == "hailuo":
|
||||
tmpl, scale, x, y = templates["hailuo"], 0.052, int(w * 0.34), int(h * 0.82)
|
||||
elif key == "vidu":
|
||||
# Logo plus wordmark bottom-right, at the measured 1080p geometry: 4.6% of
|
||||
# the short side tall, ~3% off the right and bottom edges.
|
||||
tmpl = templates["vidu"]
|
||||
th = max(8, round(short * 0.046))
|
||||
tw = max(1, round(tmpl.shape[1] * th / tmpl.shape[0]))
|
||||
x, y = w - tw - round(short * 0.029), h - th - round(short * 0.033)
|
||||
return _composite_light(base, cv2.resize(tmpl, (tw, th)).astype(np.float32) / 255.0, x, y, 250)
|
||||
elif key == "kling":
|
||||
# The FULL mark: swirl logo left of the text run, flush against the
|
||||
# bottom-right EDGE. The font arm is edge-gated (region must reach
|
||||
|
||||
@@ -74,7 +74,7 @@ _LEGACY_FAMILIES: tuple[tuple[str, tuple[str, ...]], ...] = (
|
||||
("samsung_genai", ("genaitype", "photoeditor_re_edit")),
|
||||
("soft_binding", ("soft binding", "soft-binding", "invismark")),
|
||||
("c2pa_cloud", ("cloud manifest",)),
|
||||
("hf_job", ("hf-job", "hugging face job")),
|
||||
("hf_job", ("hf-job", "higgsfield job", "hugging face job")),
|
||||
("exif_generator", ("exif", "software tag", "png text")),
|
||||
)
|
||||
|
||||
|
||||
Reference in new issue
Block a user