Lawrence D'Oliveiro wrote:
> On Wed, 29 Jul 2026 09:31:42 -0700, Maria Sophia wrote:
>
>> Python Pillow was used to try to foil AI image detection
>> https://arstechnica.com/ai/2026/07/tested-google-synthid-works-great-but-labeling-ai-content-may-be-a-losing-game/
>>
>> I'm surprised it's so hard, but where it matters is for years, we've
>> been "destroying" images so that they can't be correlated by their
>> sensor defects, but if it's this difficult to destroy the
>> cryptography Google AI adds, that means it's not easy.
>
> One thing the article didn't mention, is trying to mix multiple
> AI-generated images in various ways, to see what that does to their
> respective SynthID markers. And applying various bandwidth-filtering
> techniques (because the SynthID marker apparently takes the form of an
> "audio waveform").
Hi Lawrence,
I had never heard of SynthID before this article popped up in my feed
today, so anything I say can be wrong (and likely is wrong), but it seems
to me that your instinct is spot on that if SynthID really behaves like a
kind of embedded "audio-like waveform" distributed across pixel space, then
mixing multiple AI-generated images or applying frequency-domain filtering
is perhaps just the kind of stress test that could reveal how fragile (or
how robust) the watermark actually is.
The Ars Technica piece we're reading confirms the robustness under
compression, resizing, and moderate cropping, but, as you noted, it doesn't
explore multi-image blending or frequency-domain attacks at all.
If it's genuinely difficult to destroy SynthID without destroying the
image, then watermarking is much more robust than traditional sensor-noise
fingerprinting.
To their credit, the article makes clear that SynthID is not designed to
resist adversarial attacks and will eventually be bypassed by determined
attackers, of which I am not one, but I did update the script today.
# crush.py
# Use model: python crush.py
# Simulates image being shared on the net to see if the
# AI watermarks can still be found by Gemini testing.
# ---------------------------------------------------------
# v1p3 20260729
# Further slowed degradation to about gen_100 or so
# v1p2 20260729
# It worked but by gen_12 the image was unusable
# JPEG quality 10-40 is extremely destructive
# Resize 80-100% shrinks the image every single generation
# Crop 20% every 50 gens removes huge chunks
# Changed quality from (10, 40) to (60, 85)
# Changed scale from (0.80, 1.00) to (0.95, 1.00)
# Changed crop from every 50 generations to every 100
# Changed crop amount from 20% to 5%
# v1p1 20260729
# It worked but it barfed when the image got too small
# Added a clamp so width/height never drop below 1px
# v1p0 20260729
# Simulate an image being shared over the Internet
# for the purpose of testing if it can be crushed
# enough such that it can no longer be identified.
# ---------------------------------------------------------
# crush_slow.py
import random
import os
from PIL import Image
INPUT_IMAGE = "input.jpg"
GENERATIONS = 300
OUTPUT_DIR = "output"
os.makedirs(OUTPUT_DIR, exist_ok=True)
img = Image.open(INPUT_IMAGE)
for i in range(1, GENERATIONS + 1):
# Very gentle JPEG compression
quality = random.randint(75, 95)
# Only resize occasionally (every 10 generations)
if i % 10 == 0:
scale = random.uniform(0.97, 1.00)
new_w = max(1, int(img.width * scale))
new_h = max(1, int(img.height * scale))
img = img.resize((new_w, new_h), Image.LANCZOS)
# Save this generation
out_path = f"{OUTPUT_DIR}/gen_{i}.jpg"
img.save(out_path, "JPEG", quality=quality)
# Reload it
img = Image.open(out_path)
# Crop very rarely and very lightly
if i % 100 == 0:
w, h = img.size
if w > 40 and h > 40:
crop_amount = int(w * 0.03) # 3% crop
img = img.crop((crop_amount, crop_amount,
w - crop_amount, h - crop_amount))
img.save(out_path)
print("Done! Check the output/ folder.")
# end of crush.py
# Craiyon, Nano Banana 2 in Gemini, ChatGPG (3 per day)
--
Usenet allows people with shared interests to discuss topics of import.
--
https://mail.python.org/mailman3//lists/python-list.python.org