Inspiration
Detecting AI generated image looks easy Models report 99% on clean benchmarks. But images don't stay clean: they get uploaded, re-encoded, screenshotted, cropped and reposted. We call that laundering.
What it does
Takes a directory of images and returns a probability that each was AI-generated, and keeps working after the image has been through the internet.
On the provided benchmark (COCO val2017 against DALL·E Advanced, a generator never seen in training): 0.9807 clean ROC-AUC, 0.970 averaged across sixteen single transformations. The worst single transform is a quarter-size downscale at 0.937. The worst composed chain is six-step heavy laundering at 0.953. Nothing in the battery drops below 0.93.
How we built it
DINOv3-L/16 frozen, adapted with LoRA or SVD with A100. Every training image is paired with a laundered copy under randomly composed transform chains that lengthen as training progresses. Both copies carry the same label, because laundering doesn't change whether an image was generated.
Because we apply the transforms, we know their exact parameters, so a severity head gets supervision at zero annotation cost, and that estimate conditions the classifier through FiLM. The severity head is gradient-detached. The consistency loss wants features blind to degradation while the severity head wants them sensitive to it. Detaching lets the trunk optimise for one while the head reads the other.
BCE(clean) + BCE(laundered) + 0.50·KL(p‖p̃) + 0.25·MSE(h,h̃) + 0.50·SmoothL1(ŝ,s)
Attack cost, instead of a robustness table. Asking "what's your AUC at JPEG-50?" measures performance against a menu someone else picked. We ask the adversary's question instead: for each correct prediction, what's the cheapest laundering that flips it? Cost is perceptual damage (1 − SSIM), which puts JPEG, blur, resize, noise and multi-step chains on one physical axis. 94.5% of correct predictions survive light damage, 87.6% survive heavy damage, and 72.9% are never flipped by any rung of any ladder we tried.
| AUC | AUC | |||
|---|---|---|---|---|
| clean | 0.9807 | jpeg_50 | 0.9704 | |
| jitter_0.2 | 0.9811 | noise_0.05 | 0.9688 | |
| cast_0.2 | 0.9800 | jpeg_30 | 0.9632 | |
| crop_0.8 | 0.9797 | noise_0.10 | 0.9525 | |
| blur_0.5 | 0.9785 | blur_2.0 | 0.9504 | |
| noise_0.02 | 0.9770 | resize_0.25 | 0.9371 | |
| jpeg_90 | 0.9765 | crop_repost | 0.9728 |
| model | survives 0.05 damage | survives 0.4 | never flipped |
|---|---|---|---|
| LoRA | 0.9451 | 0.8248 | 0.7292 |
| SVD | 0.9378 | 0.7904 | 0.7069 |
| frozen probe | 0.9384 | 0.7256 | 0.6402 |
| full fine-tune | 0.8914 | 0.6494 | 0.5630 |

Challenges we ran into
WaRPAD (NeurIPS 2025) was going to be our second, decorrelated cue. The intuition is appealing: DINO is trained on real images, so laundering a real image should barely move its embedding, while an AI image should move more. We implemented it against the paper. But the score direction turned out to be dataset-dependent
Accomplishments that we're proud of
The attack-cost framing, and what it revealed about adaptation: full fine-tuning is the easiest to evade at 56% never-flipped, the frozen probe 64%, LoRA 73%. The more of DINOv3's pretrained weights you overwrite, the cheaper it becomes to launder past you.
What we learned
AI generated image detector doesn't need advanced model, just creative pipeline
What's next for AIGC Detector
Mixture of experts, because one adapter is being asked to cover too much.
The space of AI-generated images is enormous and still growing: diffusion, GAN, and autoregressive families, each with its own artifacts, and within each family a wide range of styles from photoreal to illustration to 3D render. Right now a single set of LoRA weights has to cover all of it at once.
A mixture of experts fits this shape well. Several small LoRA experts sit on the same frozen DINOv3 trunk, with a router selecting a few per image.
Built With
- dino
- google-cloud
- gradio
- torch
- wandb
Log in or sign up for Devpost to join the conversation.