Spaces:
Sleeping
Sleeping
Update SynthScope Space from the GitHub repository
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- LEADERBOARD.md +2 -0
- benchmark/README.md +1 -1
- benchmark/bench_eval.py +4 -7
- benchmark/submissions/restrav.json +50 -9
- benchmark/submissions/stall.json +63 -0
- conf/dataset/image/arforensics.yaml +2 -1
- conf/model/image/adapted_dino.yaml +15 -0
- conf/model/image/aide.yaml +11 -0
- conf/model/image/bfree.yaml +5 -0
- conf/model/image/clip.yaml +15 -0
- conf/model/image/cnn.yaml +12 -0
- conf/model/image/co_spy.yaml +5 -0
- conf/model/image/cure.yaml +3 -0
- conf/model/image/d3qe.yaml +7 -0
- conf/model/image/dda.yaml +4 -0
- conf/model/image/dinov2.yaml +15 -0
- conf/model/image/dinov3.yaml +15 -0
- conf/model/image/effort.yaml +3 -0
- conf/model/image/fat_former.yaml +9 -0
- conf/model/image/moejepa.yaml +3 -0
- conf/model/image/npr.yaml +7 -0
- conf/model/image/omniaid.yaml +3 -0
- conf/model/image/omniaid_genimage.yaml +3 -0
- conf/model/image/omniaid_v1.yaml +3 -0
- conf/model/image/pgc.yaml +5 -0
- conf/model/image/safe.yaml +5 -0
- conf/model/image/spai.yaml +11 -1
- conf/model/image/vib_net.yaml +7 -0
- conf/model/image/vib_net_official.yaml +7 -0
- dataset.py +104 -53
- docs/ADD_DETECTOR.md +17 -2
- docs/BENCHMARKS.md +2 -1
- docs/CAPABILITIES.md +6 -4
- experiments/image/adapted_dinov2_vitl14.sh +1 -3
- experiments/image/aide.sh +4 -3
- experiments/image/clip_vitl14.sh +1 -3
- experiments/image/co_spy.sh +0 -2
- experiments/image/d3qe.sh +1 -3
- experiments/image/dinov2_vitl14.sh +1 -3
- experiments/image/dinov3_vitl14.sh +1 -3
- experiments/image/fat_former.sh +0 -6
- experiments/image/npr.sh +0 -2
- experiments/image/pgc.sh +0 -2
- experiments/image/safe.sh +1 -3
- experiments/image/spai.sh +0 -3
- experiments/image/vib_net.sh +0 -2
- experiments/video/d3.sh +1 -1
- experiments/video/restrav.sh +1 -1
- experiments/video/stall.sh +3 -3
- interface/backend/engine.py +9 -9
LEADERBOARD.md
CHANGED
|
@@ -34,3 +34,5 @@ Metrics are percentages. **mAP** = mean AP over generators. Each track is ranked
|
|
| 34 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
| 35 |
| 1 | SPLIT | training-free | 99.41 | 99.79 | — | 99.97 | ✓ | Training-free X-CLIP patch roughness x motion incoherence; Acc at the real-only FPR-1% calibration |
|
| 36 |
| 2 | D3 | training-free | 95.53 | 68.84 | — | 98.45 | ✓ | Training-free X-CLIP 2nd-order feature volatility (official preprocessing) |
|
|
|
|
|
|
|
|
|
| 34 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
| 35 |
| 1 | SPLIT | training-free | 99.41 | 99.79 | — | 99.97 | ✓ | Training-free X-CLIP patch roughness x motion incoherence; Acc at the real-only FPR-1% calibration |
|
| 36 |
| 2 | D3 | training-free | 95.53 | 68.84 | — | 98.45 | ✓ | Training-free X-CLIP 2nd-order feature volatility (official preprocessing) |
|
| 37 |
+
| 3 | STALL | training-free | 56.89 | 32.05 | — | 82.59 | ✓ | Training-free DINOv3 spatio-temporal likelihood under VATEX real-video statistics |
|
| 38 |
+
| 4 | ReStraV | frozen-probe | 30.01 | 33.60 | — | 43.06 | ✓ | Frozen DINOv2 trajectory geometry + MLP trained on GenVidBench; does not transfer to GenVideo-Val (AUROC below 50) |
|
benchmark/README.md
CHANGED
|
@@ -19,7 +19,7 @@ from the submission files in [`submissions/`](submissions/) — see [LEADERBOARD
|
|
| 19 |
| `genimage-sdv1.4` | image | GenImage SD-v1.4 real/fake | in-distribution diffusion | **AUROC** |
|
| 20 |
| `genvideo-val` | video | GenVideo-Val: all 10,000 MSR-VTT reals against every fake, per generator | mixed | **AUROC** (AP with fake as positive is unstable here: as few as 56 fakes against 10,000 reals) |
|
| 21 |
|
| 22 |
-
**These three tracks are only the start.** The repo supports
|
| 23 |
[README](../README.md#-benchmarks)), and the web page's Leaderboard tab already compares detectors on 30+ benchmark groups.
|
| 24 |
A dataset becomes a standardized track once its protocol (test set, grouping, metric) is fixed, so results stay comparable;
|
| 25 |
adding one means a schema entry, an `evaluate.py` track and verified submissions, described in
|
|
|
|
| 19 |
| `genimage-sdv1.4` | image | GenImage SD-v1.4 real/fake | in-distribution diffusion | **AUROC** |
|
| 20 |
| `genvideo-val` | video | GenVideo-Val: all 10,000 MSR-VTT reals against every fake, per generator | mixed | **AUROC** (AP with fake as positive is unstable here: as few as 56 fakes against 10,000 reals) |
|
| 21 |
|
| 22 |
+
**These three tracks are only the start.** The repo supports 20 image and 8 video benchmark datasets (see the table in the
|
| 23 |
[README](../README.md#-benchmarks)), and the web page's Leaderboard tab already compares detectors on 30+ benchmark groups.
|
| 24 |
A dataset becomes a standardized track once its protocol (test set, grouping, metric) is fixed, so results stay comparable;
|
| 25 |
adding one means a schema entry, an `evaluate.py` track and verified submissions, described in
|
benchmark/bench_eval.py
CHANGED
|
@@ -144,8 +144,9 @@ def discover(benchmark: str, root: Path, shared_reals: Path | None, select: set[
|
|
| 144 |
|
| 145 |
|
| 146 |
def native_res_transform(tf):
|
| 147 |
-
"""Drop the up-scaling
|
| 148 |
-
|
|
|
|
| 149 |
|
| 150 |
Some methods (e.g. NPR on GANGen-Detection/freqnet) detect up-sampling artifacts and
|
| 151 |
MUST NOT be fed a bilinearly up-scaled image — resizing a 128x128 GAN crop to 256x256
|
|
@@ -153,11 +154,7 @@ def native_res_transform(tf):
|
|
| 153 |
GANGen recipe runs with ``no_resize=True``; this reproduces it. Safe only when every
|
| 154 |
image in a per-generator batch shares one resolution (true for freqnet's per-gen sets),
|
| 155 |
since bench_eval stacks a batch into a single tensor."""
|
| 156 |
-
|
| 157 |
-
if not isinstance(tf, T.Compose):
|
| 158 |
-
return tf
|
| 159 |
-
kept = [t for t in tf.transforms if not isinstance(t, (T.Resize, T.CenterCrop))]
|
| 160 |
-
return T.Compose(kept)
|
| 161 |
|
| 162 |
|
| 163 |
@torch.no_grad()
|
|
|
|
| 144 |
|
| 145 |
|
| 146 |
def native_res_transform(tf):
|
| 147 |
+
"""Drop the up-scaling resize/center-crop steps from a model's eval transform, keeping
|
| 148 |
+
to_dtype + normalize, so images are scored at NATIVE resolution (ValueError for eval lists
|
| 149 |
+
whose own steps resize, see ``AIGITransformations.without_resize``).
|
| 150 |
|
| 151 |
Some methods (e.g. NPR on GANGen-Detection/freqnet) detect up-sampling artifacts and
|
| 152 |
MUST NOT be fed a bilinearly up-scaled image — resizing a 128x128 GAN crop to 256x256
|
|
|
|
| 154 |
GANGen recipe runs with ``no_resize=True``; this reproduces it. Safe only when every
|
| 155 |
image in a per-generator batch shares one resolution (true for freqnet's per-gen sets),
|
| 156 |
since bench_eval stacks a batch into a single tensor."""
|
| 157 |
+
return tf.without_resize()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
|
| 159 |
|
| 160 |
@torch.no_grad()
|
benchmark/submissions/restrav.json
CHANGED
|
@@ -3,20 +3,61 @@
|
|
| 3 |
"track": "genvideo-val",
|
| 4 |
"modality": "video",
|
| 5 |
"model_type": "restrav",
|
| 6 |
-
"paper": "2025",
|
| 7 |
"url": "https://github.com/SMSD75/SynthScope",
|
| 8 |
"evaluation": "frozen-probe",
|
| 9 |
"metrics": {
|
| 10 |
-
"acc":
|
| 11 |
-
"map":
|
| 12 |
-
"ap":
|
| 13 |
-
"auroc":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
},
|
| 15 |
-
"per_generator": null,
|
| 16 |
"submitted_by": "SynthScope",
|
| 17 |
-
"date": "2026-
|
| 18 |
"verified": true,
|
| 19 |
-
"source": "
|
| 20 |
-
"protocol_notes": "Frozen DINOv2 trajectory geometry + MLP",
|
| 21 |
"synthscope_version": "0.1.0"
|
| 22 |
}
|
|
|
|
| 3 |
"track": "genvideo-val",
|
| 4 |
"modality": "video",
|
| 5 |
"model_type": "restrav",
|
| 6 |
+
"paper": "Interno et al., 2025",
|
| 7 |
"url": "https://github.com/SMSD75/SynthScope",
|
| 8 |
"evaluation": "frozen-probe",
|
| 9 |
"metrics": {
|
| 10 |
+
"acc": 30.01,
|
| 11 |
+
"map": 33.6,
|
| 12 |
+
"ap": null,
|
| 13 |
+
"auroc": 43.06
|
| 14 |
+
},
|
| 15 |
+
"per_generator": {
|
| 16 |
+
"Crafter": {
|
| 17 |
+
"auroc": 89.15,
|
| 18 |
+
"ap": 68.75
|
| 19 |
+
},
|
| 20 |
+
"Gen2": {
|
| 21 |
+
"auroc": 2.76,
|
| 22 |
+
"ap": 6.37
|
| 23 |
+
},
|
| 24 |
+
"HotShot": {
|
| 25 |
+
"auroc": 0.1,
|
| 26 |
+
"ap": 3.35
|
| 27 |
+
},
|
| 28 |
+
"Lavie": {
|
| 29 |
+
"auroc": 34.51,
|
| 30 |
+
"ap": 25.86
|
| 31 |
+
},
|
| 32 |
+
"ModelScope": {
|
| 33 |
+
"auroc": 78.96,
|
| 34 |
+
"ap": 59.16
|
| 35 |
+
},
|
| 36 |
+
"MoonValley": {
|
| 37 |
+
"auroc": 2.07,
|
| 38 |
+
"ap": 3.02
|
| 39 |
+
},
|
| 40 |
+
"MorphStudio": {
|
| 41 |
+
"auroc": 92.58,
|
| 42 |
+
"ap": 74.66
|
| 43 |
+
},
|
| 44 |
+
"Show_1": {
|
| 45 |
+
"auroc": 91.56,
|
| 46 |
+
"ap": 66.49
|
| 47 |
+
},
|
| 48 |
+
"Sora": {
|
| 49 |
+
"auroc": 0.26,
|
| 50 |
+
"ap": 0.29
|
| 51 |
+
},
|
| 52 |
+
"WildScrape": {
|
| 53 |
+
"auroc": 38.7,
|
| 54 |
+
"ap": 28.08
|
| 55 |
+
}
|
| 56 |
},
|
|
|
|
| 57 |
"submitted_by": "SynthScope",
|
| 58 |
+
"date": "2026-11-02",
|
| 59 |
"verified": true,
|
| 60 |
+
"source": "repro/bench_results/restrav_genvideo_full.json (benchmark/bench_eval_video.py --dataset genvideo, no --limit)",
|
| 61 |
+
"protocol_notes": "Frozen DINOv2 trajectory geometry + MLP trained on GenVidBench; does not transfer to GenVideo-Val (AUROC below 50)",
|
| 62 |
"synthscope_version": "0.1.0"
|
| 63 |
}
|
benchmark/submissions/stall.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"method": "STALL",
|
| 3 |
+
"track": "genvideo-val",
|
| 4 |
+
"modality": "video",
|
| 5 |
+
"model_type": "stall",
|
| 6 |
+
"paper": "Ben Hayun et al., CVPR'26",
|
| 7 |
+
"url": "https://github.com/SMSD75/SynthScope",
|
| 8 |
+
"evaluation": "training-free",
|
| 9 |
+
"metrics": {
|
| 10 |
+
"acc": 56.89,
|
| 11 |
+
"map": 32.05,
|
| 12 |
+
"ap": null,
|
| 13 |
+
"auroc": 82.59
|
| 14 |
+
},
|
| 15 |
+
"per_generator": {
|
| 16 |
+
"Crafter": {
|
| 17 |
+
"auroc": 87.91,
|
| 18 |
+
"ap": 53.66
|
| 19 |
+
},
|
| 20 |
+
"Gen2": {
|
| 21 |
+
"auroc": 90.0,
|
| 22 |
+
"ap": 58.91
|
| 23 |
+
},
|
| 24 |
+
"HotShot": {
|
| 25 |
+
"auroc": 76.97,
|
| 26 |
+
"ap": 21.25
|
| 27 |
+
},
|
| 28 |
+
"Lavie": {
|
| 29 |
+
"auroc": 86.42,
|
| 30 |
+
"ap": 53.53
|
| 31 |
+
},
|
| 32 |
+
"ModelScope": {
|
| 33 |
+
"auroc": 78.21,
|
| 34 |
+
"ap": 20.92
|
| 35 |
+
},
|
| 36 |
+
"MoonValley": {
|
| 37 |
+
"auroc": 82.67,
|
| 38 |
+
"ap": 22.83
|
| 39 |
+
},
|
| 40 |
+
"MorphStudio": {
|
| 41 |
+
"auroc": 83.22,
|
| 42 |
+
"ap": 28.34
|
| 43 |
+
},
|
| 44 |
+
"Show_1": {
|
| 45 |
+
"auroc": 82.29,
|
| 46 |
+
"ap": 30.09
|
| 47 |
+
},
|
| 48 |
+
"Sora": {
|
| 49 |
+
"auroc": 80.73,
|
| 50 |
+
"ap": 2.8
|
| 51 |
+
},
|
| 52 |
+
"WildScrape": {
|
| 53 |
+
"auroc": 77.48,
|
| 54 |
+
"ap": 28.22
|
| 55 |
+
}
|
| 56 |
+
},
|
| 57 |
+
"submitted_by": "SynthScope",
|
| 58 |
+
"date": "2026-11-02",
|
| 59 |
+
"verified": true,
|
| 60 |
+
"source": "repro/bench_results/stall_genvideo_full.json (benchmark/bench_eval_video.py --dataset genvideo, no --limit)",
|
| 61 |
+
"protocol_notes": "Training-free DINOv3 spatio-temporal likelihood under VATEX real-video statistics",
|
| 62 |
+
"synthscope_version": "0.1.0"
|
| 63 |
+
}
|
conf/dataset/image/arforensics.yaml
CHANGED
|
@@ -3,7 +3,8 @@ name: arforensics
|
|
| 3 |
# Open-MAGVIT2), 6000 fakes + 6000 ImageNet reals each. (HF Yanran21/ARForensics, corrected test release, 21 GB, MIT)
|
| 4 |
root_dir: /hddstore/ssalehi/SynthScope/ARForensics
|
| 5 |
hf_repo_id: Yanran21/ARForensics
|
| 6 |
-
|
|
|
|
| 7 |
val_batch_size: 64
|
| 8 |
test_batch_size: 512
|
| 9 |
num_workers: 8
|
|
|
|
| 3 |
# Open-MAGVIT2), 6000 fakes + 6000 ImageNet reals each. (HF Yanran21/ARForensics, corrected test release, 21 GB, MIT)
|
| 4 |
root_dir: /hddstore/ssalehi/SynthScope/ARForensics
|
| 5 |
hf_repo_id: Yanran21/ARForensics
|
| 6 |
+
with_train_splits: false # true: also build (and fetch) the train/val splits, D3QE's training data (39 GB)
|
| 7 |
+
train_batch_size: 32
|
| 8 |
val_batch_size: 64
|
| 9 |
test_batch_size: 512
|
| 10 |
num_workers: 8
|
conf/model/image/adapted_dino.yaml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- /model/default
|
| 3 |
+
- _self_
|
| 4 |
+
|
| 5 |
+
# Adapter-tuned DINOv2: frozen DINOv2 ViT-L/14 with a trainable bottleneck adapter per block + a linear head.
|
| 6 |
+
model_type: adapted_dino
|
| 7 |
+
backbone_name: dinov2_vitl14
|
| 8 |
+
use_adapter: true
|
| 9 |
+
bottleneck_dim: 64
|
| 10 |
+
freeze_backbone: true
|
| 11 |
+
|
| 12 |
+
# Test-time preprocessing: inference, benchmarks and the end-of-training test.
|
| 13 |
+
eval_transform_list: [resize_short_256, center_crop_224, to_dtype, normalize_imagenet]
|
| 14 |
+
# Training augmentation.
|
| 15 |
+
train_transform_list: [resize_short_256, gaussian_blur, jpeg_compression, random_crop_224, random_horizontal_flip, to_dtype, normalize_imagenet]
|
conf/model/image/aide.yaml
CHANGED
|
@@ -19,3 +19,14 @@ dct:
|
|
| 19 |
stride: 16
|
| 20 |
output_size: 256
|
| 21 |
grade_N: 6
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
stride: 16
|
| 20 |
output_size: 256
|
| 21 |
grade_N: 6
|
| 22 |
+
|
| 23 |
+
# Test-time preprocessing (official): inference and benchmarks.
|
| 24 |
+
eval_transform_list: [aide_five_view]
|
| 25 |
+
# Training augmentation: the five native-resolution views, long side capped at 1024 (the recipe of the SynthScope_train
|
| 26 |
+
# checkpoint, repro/retrain_aide_native_v2.sbatch).
|
| 27 |
+
train_transform_list: [random_horizontal_flip, aide_five_view_train]
|
| 28 |
+
# Validation during training (differs from the test-time preprocessing above).
|
| 29 |
+
val_transform_list: [aide_five_view_train]
|
| 30 |
+
# Caps the long side at 1024 to bound dataloader-worker memory during the end-of-training test; inference and
|
| 31 |
+
# benchmarks use the uncapped eval list.
|
| 32 |
+
test_transform_list: [aide_five_view_train]
|
conf/model/image/bfree.yaml
CHANGED
|
@@ -9,3 +9,8 @@ model_type: bfree
|
|
| 9 |
timm_arch: vit_base_patch14_reg4_dinov2.lvd142m
|
| 10 |
crop_size: 504
|
| 11 |
pretrained_backbone: false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
timm_arch: vit_base_patch14_reg4_dinov2.lvd142m
|
| 10 |
crop_size: 504
|
| 11 |
pretrained_backbone: false
|
| 12 |
+
|
| 13 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 14 |
+
eval_transform_list: [to_dtype, normalize_imagenet]
|
| 15 |
+
# Native resolution (variable size): an end-of-training test through the training loaders needs
|
| 16 |
+
# DatasetConfig.test_batch_size=1 (no training recipe exists).
|
conf/model/image/clip.yaml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- /model/default
|
| 3 |
+
- _self_
|
| 4 |
+
|
| 5 |
+
# UnivFD (Ojha et al., CVPR'23): frozen CLIP ViT-L/14 image features + a linear real/fake head.
|
| 6 |
+
model_type: clip
|
| 7 |
+
backbone_name: CLIP:ViT-L/14
|
| 8 |
+
use_adapter: false
|
| 9 |
+
bottleneck_dim: 64
|
| 10 |
+
freeze_backbone: true
|
| 11 |
+
|
| 12 |
+
# Test-time preprocessing: inference, benchmarks and the end-of-training test.
|
| 13 |
+
eval_transform_list: [resize_short_256, center_crop_224, to_dtype, normalize_clip]
|
| 14 |
+
# Training augmentation.
|
| 15 |
+
train_transform_list: [resize_short_256, gaussian_blur, jpeg_compression, random_crop_224, random_horizontal_flip, to_dtype, normalize_clip]
|
conf/model/image/cnn.yaml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- /model/default
|
| 3 |
+
- _self_
|
| 4 |
+
|
| 5 |
+
# CNNDetection (Wang et al., CVPR'20): ImageNet ResNet-50 fine-tuned end to end as a real/fake classifier.
|
| 6 |
+
model_type: cnn
|
| 7 |
+
freeze_backbone: false
|
| 8 |
+
|
| 9 |
+
# Test-time preprocessing: inference, benchmarks and the end-of-training test.
|
| 10 |
+
eval_transform_list: [resize_short_256, center_crop_224, to_dtype, normalize_imagenet]
|
| 11 |
+
# Training augmentation.
|
| 12 |
+
train_transform_list: [resize_short_256, gaussian_blur, jpeg_compression, random_crop_224, random_horizontal_flip, to_dtype, normalize_imagenet]
|
conf/model/image/co_spy.yaml
CHANGED
|
@@ -19,3 +19,8 @@ feat_interp_alpha: 0.2
|
|
| 19 |
|
| 20 |
num_classes: 2
|
| 21 |
freeze_backbone: true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
num_classes: 2
|
| 21 |
freeze_backbone: true
|
| 22 |
+
|
| 23 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 24 |
+
eval_transform_list: [resize_short_384, center_crop_384, to_dtype]
|
| 25 |
+
# Training augmentation.
|
| 26 |
+
train_transform_list: [resize_short_384, gaussian_blur, jpeg_compression, random_crop_384, random_horizontal_flip, to_dtype]
|
conf/model/image/cure.yaml
CHANGED
|
@@ -13,3 +13,6 @@ base_filename: PE-Core-L14-336.pt
|
|
| 13 |
base_checkpoint: null # local PE-Core-L14-336.pt override (or env SYNTHSCOPE_PE_CORE_CKPT)
|
| 14 |
load_base_backbone: true # false = build the architecture only (smoke tests / key checks)
|
| 15 |
label_smoothing: 0.0
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
base_checkpoint: null # local PE-Core-L14-336.pt override (or env SYNTHSCOPE_PE_CORE_CKPT)
|
| 14 |
load_base_backbone: true # false = build the architecture only (smoke tests / key checks)
|
| 15 |
label_smoothing: 0.0
|
| 16 |
+
|
| 17 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 18 |
+
eval_transform_list: [cure_eval]
|
conf/model/image/d3qe.yaml
CHANGED
|
@@ -8,3 +8,10 @@ defaults:
|
|
| 8 |
# vq_ds16_c2i.pt) are fetched from their own releases, as in the official code.
|
| 9 |
model_type: d3qe
|
| 10 |
vq_checkpoint: null # local vq_ds16_c2i.pt; null -> $SYNTHSCOPE_LLAMAGEN_VQ, else download from the Hugging Face Hub
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
# vq_ds16_c2i.pt) are fetched from their own releases, as in the official code.
|
| 9 |
model_type: d3qe
|
| 10 |
vq_checkpoint: null # local vq_ds16_c2i.pt; null -> $SYNTHSCOPE_LLAMAGEN_VQ, else download from the Hugging Face Hub
|
| 11 |
+
|
| 12 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 13 |
+
eval_transform_list: [resize_short_256, center_crop_256, to_dtype]
|
| 14 |
+
# Training augmentation.
|
| 15 |
+
train_transform_list: [resize_short_256, blur_jpeg_cnndet, random_crop_256, random_horizontal_flip, to_dtype]
|
| 16 |
+
# Validation during training (differs from the test-time preprocessing above).
|
| 17 |
+
val_transform_list: [resize_short_256, blur_jpeg_cnndet_val, center_crop_256, to_dtype]
|
conf/model/image/dda.yaml
CHANGED
|
@@ -12,3 +12,7 @@ lora_rank: 8
|
|
| 12 |
lora_alpha: 1.0
|
| 13 |
lora_targets: [attn.qkv, attn.proj, mlp.fc1, mlp.fc2]
|
| 14 |
freeze_backbone: true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
lora_alpha: 1.0
|
| 13 |
lora_targets: [attn.qkv, attn.proj, mlp.fc1, mlp.fc2]
|
| 14 |
freeze_backbone: true
|
| 15 |
+
|
| 16 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 17 |
+
eval_transform_list: [center_crop_336, to_dtype, normalize_clip]
|
| 18 |
+
# No training lists: DDA trains on dataset image/dda_train, which builds its own official paired transforms.
|
conf/model/image/dinov2.yaml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- /model/default
|
| 3 |
+
- _self_
|
| 4 |
+
|
| 5 |
+
# DINOv2 probe: frozen DINOv2 ViT-L/14 + a linear real/fake head.
|
| 6 |
+
model_type: dinov2
|
| 7 |
+
backbone_name: dinov2_vitl14
|
| 8 |
+
use_adapter: false
|
| 9 |
+
bottleneck_dim: 64
|
| 10 |
+
freeze_backbone: true
|
| 11 |
+
|
| 12 |
+
# Test-time preprocessing: inference, benchmarks and the end-of-training test.
|
| 13 |
+
eval_transform_list: [resize_short_256, center_crop_224, to_dtype, normalize_imagenet]
|
| 14 |
+
# Training augmentation.
|
| 15 |
+
train_transform_list: [resize_short_256, gaussian_blur, jpeg_compression, random_crop_224, random_horizontal_flip, to_dtype, normalize_imagenet]
|
conf/model/image/dinov3.yaml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
defaults:
|
| 2 |
+
- /model/default
|
| 3 |
+
- _self_
|
| 4 |
+
|
| 5 |
+
# DINOv3 probe: frozen DINOv3 ViT-L/16 (gated weights) + a linear real/fake head.
|
| 6 |
+
model_type: dinov3
|
| 7 |
+
backbone_name: dinov3_vitl16
|
| 8 |
+
use_adapter: false
|
| 9 |
+
bottleneck_dim: 64
|
| 10 |
+
freeze_backbone: true
|
| 11 |
+
|
| 12 |
+
# Test-time preprocessing: inference, benchmarks and the end-of-training test.
|
| 13 |
+
eval_transform_list: [resize_short_256, center_crop_256, to_dtype, normalize_dinov3]
|
| 14 |
+
# Training augmentation.
|
| 15 |
+
train_transform_list: [resize_short_256, gaussian_blur, jpeg_compression, random_crop_256, random_horizontal_flip, to_dtype, normalize_dinov3]
|
conf/model/image/effort.yaml
CHANGED
|
@@ -7,3 +7,6 @@ defaults:
|
|
| 7 |
model_type: effort
|
| 8 |
clip_name: openai/clip-vit-large-patch14
|
| 9 |
svd_rank: 1023
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
model_type: effort
|
| 8 |
clip_name: openai/clip-vit-large-patch14
|
| 9 |
svd_rank: 1023
|
| 10 |
+
|
| 11 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 12 |
+
eval_transform_list: [effort_cv2_resize_224, to_dtype, normalize_clip]
|
conf/model/image/fat_former.yaml
CHANGED
|
@@ -11,3 +11,12 @@ num_classes: 2
|
|
| 11 |
classnames: [real, fake]
|
| 12 |
freeze_backbone: true
|
| 13 |
force_fp32: true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
classnames: [real, fake]
|
| 12 |
freeze_backbone: true
|
| 13 |
force_fp32: true
|
| 14 |
+
|
| 15 |
+
# Official FatFormer (utils/dataset.py) normalizes with ImageNet mean/std, not CLIP's, despite the CLIP backbone (the
|
| 16 |
+
# official-weights eval reproduces 98.39 acc / 99.70 mAP with it).
|
| 17 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 18 |
+
eval_transform_list: [resize_256, center_crop_224, to_dtype, normalize_imagenet]
|
| 19 |
+
# Training augmentation.
|
| 20 |
+
train_transform_list: [resize_short_256, gaussian_blur, jpeg_compression, random_crop_224, random_horizontal_flip, to_dtype, normalize_imagenet]
|
| 21 |
+
# Validation during training (differs from the test-time preprocessing above).
|
| 22 |
+
val_transform_list: [resize_short_256, center_crop_224, to_dtype, normalize_imagenet]
|
conf/model/image/moejepa.yaml
CHANGED
|
@@ -13,3 +13,6 @@ num_experts: 6
|
|
| 13 |
top_k: 2
|
| 14 |
router_temperature: 0.5
|
| 15 |
out_classes: 3
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
top_k: 2
|
| 14 |
router_temperature: 0.5
|
| 15 |
out_classes: 3
|
| 16 |
+
|
| 17 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 18 |
+
eval_transform_list: [vjepa2_processor]
|
conf/model/image/npr.yaml
CHANGED
|
@@ -5,3 +5,10 @@ defaults:
|
|
| 5 |
model_type: npr
|
| 6 |
num_classes: 2
|
| 7 |
freeze_backbone: false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
model_type: npr
|
| 6 |
num_classes: 2
|
| 7 |
freeze_backbone: false
|
| 8 |
+
|
| 9 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 10 |
+
eval_transform_list: [resize_256, to_dtype, normalize_imagenet]
|
| 11 |
+
# Training augmentation.
|
| 12 |
+
train_transform_list: [resize_256, random_crop_224, random_horizontal_flip, to_dtype, normalize_imagenet]
|
| 13 |
+
# Validation during training (differs from the test-time preprocessing above).
|
| 14 |
+
val_transform_list: [resize_256, center_crop_224, to_dtype, normalize_imagenet]
|
conf/model/image/omniaid.yaml
CHANGED
|
@@ -16,3 +16,6 @@ moe_lambda_gating_cls: 0.1
|
|
| 16 |
dropout: 0.0
|
| 17 |
gradient_checkpointing_enable: false
|
| 18 |
is_hybrid: true
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
dropout: 0.0
|
| 17 |
gradient_checkpointing_enable: false
|
| 18 |
is_hybrid: true
|
| 19 |
+
|
| 20 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 21 |
+
eval_transform_list: [resize_336, to_dtype, normalize_clip]
|
conf/model/image/omniaid_genimage.yaml
CHANGED
|
@@ -16,3 +16,6 @@ moe_lambda_gating_cls: 0.1
|
|
| 16 |
dropout: 0.0
|
| 17 |
gradient_checkpointing_enable: false
|
| 18 |
is_hybrid: true
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
dropout: 0.0
|
| 17 |
gradient_checkpointing_enable: false
|
| 18 |
is_hybrid: true
|
| 19 |
+
|
| 20 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 21 |
+
eval_transform_list: [resize_512, resize_336, to_dtype, normalize_clip]
|
conf/model/image/omniaid_v1.yaml
CHANGED
|
@@ -16,3 +16,6 @@ moe_lambda_gating_cls: 0.1
|
|
| 16 |
dropout: 0.0
|
| 17 |
gradient_checkpointing_enable: false
|
| 18 |
is_hybrid: true
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
dropout: 0.0
|
| 17 |
gradient_checkpointing_enable: false
|
| 18 |
is_hybrid: true
|
| 19 |
+
|
| 20 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 21 |
+
eval_transform_list: [resize_512, resize_336, to_dtype, normalize_clip]
|
conf/model/image/pgc.yaml
CHANGED
|
@@ -19,3 +19,8 @@ label_smoothing: 0.1
|
|
| 19 |
tau_rgb: 0.5
|
| 20 |
tau_res: 0.5
|
| 21 |
num_classes: 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
tau_rgb: 0.5
|
| 20 |
tau_res: 0.5
|
| 21 |
num_classes: 1
|
| 22 |
+
|
| 23 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 24 |
+
eval_transform_list: [center_crop_224, to_dtype, normalize_imagenet]
|
| 25 |
+
# Training augmentation.
|
| 26 |
+
train_transform_list: [random_horizontal_flip, color_jitter_pgc, pad_random_crop_224, to_dtype, normalize_imagenet]
|
conf/model/image/safe.yaml
CHANGED
|
@@ -6,3 +6,8 @@ defaults:
|
|
| 6 |
# it does NOT use backbone_name. model_type=safe is dispatched before the CLIP fallback.
|
| 7 |
model_type: safe
|
| 8 |
freeze_backbone: false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
# it does NOT use backbone_name. model_type=safe is dispatched before the CLIP fallback.
|
| 7 |
model_type: safe
|
| 8 |
freeze_backbone: false
|
| 9 |
+
|
| 10 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 11 |
+
eval_transform_list: [center_crop_256, to_dtype]
|
| 12 |
+
# Training augmentation.
|
| 13 |
+
train_transform_list: [random_crop_256_pad, random_horizontal_flip, random_rotation_180, color_jitter_safe, to_dtype, random_mask_safe]
|
conf/model/image/spai.yaml
CHANGED
|
@@ -15,4 +15,14 @@ use_sca: true
|
|
| 15 |
patch_size: 224
|
| 16 |
patch_stride: 224
|
| 17 |
num_heads: 12
|
| 18 |
-
attn_embed_dim: 1536
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
patch_size: 224
|
| 16 |
patch_stride: 224
|
| 17 |
num_heads: 12
|
| 18 |
+
attn_embed_dim: 1536
|
| 19 |
+
|
| 20 |
+
# Test-time preprocessing (official): inference and benchmarks.
|
| 21 |
+
eval_transform_list: [cap_long_side_1024, to_dtype]
|
| 22 |
+
# Training augmentation. SPAI takes [0, 1] images; ImageNet normalization is applied inside the model.
|
| 23 |
+
train_transform_list: [resize_short_256, gaussian_blur, jpeg_compression, random_crop_224, random_horizontal_flip, to_dtype]
|
| 24 |
+
# Validation during training (differs from the test-time preprocessing above).
|
| 25 |
+
val_transform_list: [resize_short_256, center_crop_224, to_dtype]
|
| 26 |
+
# Batchable stand-in for the end-of-training test (the native-resolution eval list varies in size); inference and
|
| 27 |
+
# benchmarks use the eval list.
|
| 28 |
+
test_transform_list: [resize_short_256, center_crop_224, to_dtype]
|
conf/model/image/vib_net.yaml
CHANGED
|
@@ -13,3 +13,10 @@ vib_latent_dim: 256
|
|
| 13 |
dropout: 0.5
|
| 14 |
std_shift: 5.0
|
| 15 |
freeze_backbone: true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
dropout: 0.5
|
| 14 |
std_shift: 5.0
|
| 15 |
freeze_backbone: true
|
| 16 |
+
|
| 17 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 18 |
+
eval_transform_list: [center_crop_224, to_dtype, normalize_clip]
|
| 19 |
+
# Training augmentation.
|
| 20 |
+
train_transform_list: [blur_jpeg_cnndet, pad_random_crop_224, random_horizontal_flip, to_dtype, normalize_clip]
|
| 21 |
+
# Validation during training (differs from the test-time preprocessing above).
|
| 22 |
+
val_transform_list: [blur_jpeg_cnndet_val, center_crop_224, to_dtype, normalize_clip]
|
conf/model/image/vib_net_official.yaml
CHANGED
|
@@ -17,3 +17,10 @@ std_floor: 0.0
|
|
| 17 |
clip_fp32: true
|
| 18 |
# The paper's trade-off parameter is not reported; 1e-3 is the standard VIB value (Alemi et al.).
|
| 19 |
vib_beta: 0.001
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
clip_fp32: true
|
| 18 |
# The paper's trade-off parameter is not reported; 1e-3 is the standard VIB value (Alemi et al.).
|
| 19 |
vib_beta: 0.001
|
| 20 |
+
|
| 21 |
+
# Test-time preprocessing (official): inference, benchmarks and the end-of-training test.
|
| 22 |
+
eval_transform_list: [center_crop_224, to_dtype, normalize_clip]
|
| 23 |
+
# Training augmentation.
|
| 24 |
+
train_transform_list: [blur_jpeg_cnndet, pad_random_crop_224, random_horizontal_flip, to_dtype, normalize_clip]
|
| 25 |
+
# Validation during training (differs from the test-time preprocessing above).
|
| 26 |
+
val_transform_list: [blur_jpeg_cnndet_val, center_crop_224, to_dtype, normalize_clip]
|
dataset.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
from torch.utils.data import Dataset
|
| 2 |
from typing import Literal
|
| 3 |
from pathlib import Path
|
|
|
|
| 4 |
import hashlib
|
| 5 |
import json
|
| 6 |
import os
|
|
@@ -22,6 +23,7 @@ import numpy as np
|
|
| 22 |
import torch
|
| 23 |
import torch.nn.functional as F
|
| 24 |
from decord import VideoReader, cpu
|
|
|
|
| 25 |
from torch.utils.data import DataLoader
|
| 26 |
from transformations import AIGITransformations
|
| 27 |
from torchvision.io import decode_image, ImageReadMode
|
|
@@ -71,6 +73,75 @@ def _has_videos(directory: Path) -> bool:
|
|
| 71 |
return directory.is_dir() and any(_iter_videos(directory))
|
| 72 |
|
| 73 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
class _PILFrames:
|
| 75 |
"""The part of decord's ``VideoReader`` API that ``sample_video_frames`` uses, for animated images decord cannot open
|
| 76 |
(GIFs: decord fails with "broken metadata" on them). Every frame is composited to RGB at the canvas size, and the
|
|
@@ -1128,16 +1199,13 @@ class CNNDetectionDataset(Dataset):
|
|
| 1128 |
class CNNDetectionDatasetModule:
|
| 1129 |
def __init__(self, config):
|
| 1130 |
self.config = config
|
| 1131 |
-
|
| 1132 |
-
eval_list = list(config.DatasetConfig.eval_transform_list)
|
| 1133 |
-
self.train_transform = AIGITransformations(train_list) if len(train_list) > 0 else None
|
| 1134 |
-
self.eval_transform = AIGITransformations(eval_list) if len(eval_list) > 0 else None
|
| 1135 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1136 |
classes = config.DatasetConfig.get("train_classes", None) # ProGAN classes for train/val (None: all 20)
|
| 1137 |
classes = list(classes) if classes else None
|
| 1138 |
self.train_dataset = CNNDetectionDataset(config.DatasetConfig.root_dir, "train", self.train_transform, prefetch=prefetch, classes=classes)
|
| 1139 |
-
self.val_dataset = CNNDetectionDataset(config.DatasetConfig.root_dir, "val", self.
|
| 1140 |
-
self.test_dataset = CNNDetectionDataset(config.DatasetConfig.root_dir, "test", self.
|
| 1141 |
|
| 1142 |
def train_dataloader(self):
|
| 1143 |
return DataLoader(self.train_dataset, batch_size=self.config.DatasetConfig.train_batch_size, shuffle=True, num_workers=self.config.DatasetConfig.num_workers, persistent_workers=self.config.DatasetConfig.persistent_workers, pin_memory=self.config.DatasetConfig.pin_memory)
|
|
@@ -1149,9 +1217,9 @@ class CNNDetectionDatasetModule:
|
|
| 1149 |
return DataLoader(self.test_dataset, batch_size=self.config.DatasetConfig.test_batch_size, shuffle=False, num_workers=self.config.DatasetConfig.num_workers, persistent_workers=self.config.DatasetConfig.persistent_workers, pin_memory=self.config.DatasetConfig.pin_memory)
|
| 1150 |
|
| 1151 |
|
| 1152 |
-
def _split_train_subset(dataset, per_class, val_per_class,
|
| 1153 |
"""Keep a seeded ``per_class`` images per label of ``dataset`` for training and return a disjoint ``val_per_class``
|
| 1154 |
-
per label as a validation copy with ``
|
| 1155 |
held-out training images (GenImage's ``val`` alias is its test split, which must not select the model)."""
|
| 1156 |
rng = random.Random(seed)
|
| 1157 |
train_idx, val_idx = [], []
|
|
@@ -1167,30 +1235,27 @@ def _split_train_subset(dataset, per_class, val_per_class, eval_transform, seed=
|
|
| 1167 |
ds.images = [dataset.images[i] for i in keep]
|
| 1168 |
ds.labels = [dataset.labels[i] for i in keep]
|
| 1169 |
ds.folder_names = [dataset.folder_names[i] for i in keep]
|
| 1170 |
-
val.transform =
|
| 1171 |
return val
|
| 1172 |
|
| 1173 |
|
| 1174 |
class GenImageDatasetModule:
|
| 1175 |
def __init__(self, config):
|
| 1176 |
self.config = config
|
| 1177 |
-
|
| 1178 |
-
eval_list = list(config.DatasetConfig.eval_transform_list)
|
| 1179 |
-
self.train_transform = AIGITransformations(train_list) if len(train_list) > 0 else None
|
| 1180 |
-
self.eval_transform = AIGITransformations(eval_list) if len(eval_list) > 0 else None
|
| 1181 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1182 |
source_url = config.DatasetConfig.get("source_url", GenImageDataset.google_drive_folder_url)
|
| 1183 |
GenImageDataset.google_drive_folder_url = source_url
|
| 1184 |
GenImageDataset.hf_repo_id = config.DatasetConfig.get("hf_repo_id", None)
|
| 1185 |
GenImageDataset.hf_subdir = config.DatasetConfig.get("hf_subdir", None)
|
| 1186 |
self.train_dataset = GenImageDataset(config.DatasetConfig.root_dir, "train", self.train_transform, prefetch=prefetch)
|
| 1187 |
-
self.test_dataset = GenImageDataset(config.DatasetConfig.root_dir, "test", self.
|
| 1188 |
per_class = config.DatasetConfig.get("train_per_class", None) # e.g. VIB-Net: 16k real + 16k SD v1.4
|
| 1189 |
if per_class:
|
| 1190 |
self.val_dataset = _split_train_subset(self.train_dataset, int(per_class),
|
| 1191 |
-
int(config.DatasetConfig.get("val_per_class", 1000)), self.
|
| 1192 |
else:
|
| 1193 |
-
self.val_dataset = GenImageDataset(config.DatasetConfig.root_dir, "val", self.
|
| 1194 |
|
| 1195 |
def train_dataloader(self):
|
| 1196 |
return DataLoader(self.train_dataset, batch_size=self.config.DatasetConfig.train_batch_size, shuffle=True, num_workers=self.config.DatasetConfig.num_workers, persistent_workers=self.config.DatasetConfig.persistent_workers, pin_memory=self.config.DatasetConfig.pin_memory)
|
|
@@ -1757,14 +1822,11 @@ class UniversialFakeDetectionDataset(Dataset):
|
|
| 1757 |
class AIGIBenchDatasetModule:
|
| 1758 |
def __init__(self, config):
|
| 1759 |
self.config = config
|
| 1760 |
-
|
| 1761 |
-
eval_list = list(config.DatasetConfig.eval_transform_list)
|
| 1762 |
-
self.train_transform = AIGITransformations(train_list) if len(train_list) > 0 else None
|
| 1763 |
-
self.eval_transform = AIGITransformations(eval_list) if len(eval_list) > 0 else None
|
| 1764 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1765 |
self.train_dataset = AIGIBenchDataset(config.DatasetConfig.root_dir, "train", config.DatasetConfig.setting, self.train_transform, prefetch=prefetch)
|
| 1766 |
-
self.val_dataset = AIGIBenchDataset(config.DatasetConfig.root_dir, "val", config.DatasetConfig.setting, self.
|
| 1767 |
-
self.test_dataset = AIGIBenchDataset(config.DatasetConfig.root_dir, "test", config.DatasetConfig.setting, self.
|
| 1768 |
|
| 1769 |
def train_dataloader(self):
|
| 1770 |
return DataLoader(self.train_dataset, batch_size=self.config.DatasetConfig.train_batch_size, shuffle=True, num_workers=self.config.DatasetConfig.num_workers, persistent_workers=self.config.DatasetConfig.persistent_workers, pin_memory=self.config.DatasetConfig.pin_memory)
|
|
@@ -1779,11 +1841,10 @@ class AIGIBenchDatasetModule:
|
|
| 1779 |
class DiTFakeDatasetModule:
|
| 1780 |
def __init__(self, config):
|
| 1781 |
self.config = config
|
| 1782 |
-
|
| 1783 |
-
self.eval_transform = AIGITransformations(eval_list) if len(eval_list) > 0 else None
|
| 1784 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1785 |
repo_id = config.DatasetConfig.get("repo_id", DiTFakeDataset.repo_id)
|
| 1786 |
-
self.test_dataset = DiTFakeDataset(config.DatasetConfig.root_dir, "test", self.
|
| 1787 |
|
| 1788 |
def test_dataloader(self):
|
| 1789 |
return DataLoader(self.test_dataset, batch_size=self.config.DatasetConfig.test_batch_size, shuffle=False, num_workers=self.config.DatasetConfig.num_workers, persistent_workers=self.config.DatasetConfig.persistent_workers, pin_memory=self.config.DatasetConfig.pin_memory)
|
|
@@ -1792,14 +1853,13 @@ class DiTFakeDatasetModule:
|
|
| 1792 |
class FreqNetDetectionDatasetModule:
|
| 1793 |
def __init__(self, config):
|
| 1794 |
self.config = config
|
| 1795 |
-
|
| 1796 |
-
self.eval_transform = AIGITransformations(eval_list) if len(eval_list) > 0 else None
|
| 1797 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1798 |
source_url = config.DatasetConfig.get("source_url", FreqNetDetectionDataset.google_drive_folder_url)
|
| 1799 |
self.test_dataset = FreqNetDetectionDataset(
|
| 1800 |
config.DatasetConfig.root_dir,
|
| 1801 |
"test",
|
| 1802 |
-
self.
|
| 1803 |
prefetch=prefetch,
|
| 1804 |
source_url=source_url,
|
| 1805 |
)
|
|
@@ -1817,14 +1877,13 @@ class FreqNetDetectionDatasetModule:
|
|
| 1817 |
class UniversialFakeDetectionDatasetModule:
|
| 1818 |
def __init__(self, config):
|
| 1819 |
self.config = config
|
| 1820 |
-
|
| 1821 |
-
self.eval_transform = AIGITransformations(eval_list) if len(eval_list) > 0 else None
|
| 1822 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1823 |
source_url = config.DatasetConfig.get("source_url", UniversialFakeDetectionDataset.source_url)
|
| 1824 |
self.test_dataset = UniversialFakeDetectionDataset(
|
| 1825 |
config.DatasetConfig.root_dir,
|
| 1826 |
"test",
|
| 1827 |
-
self.
|
| 1828 |
prefetch=prefetch,
|
| 1829 |
source_url=source_url,
|
| 1830 |
)
|
|
@@ -1842,8 +1901,7 @@ class UniversialFakeDetectionDatasetModule:
|
|
| 1842 |
class CoSpyDatasetModule:
|
| 1843 |
def __init__(self, config):
|
| 1844 |
self.config = config
|
| 1845 |
-
|
| 1846 |
-
self.eval_transform = AIGITransformations(eval_list) if len(eval_list) > 0 else None
|
| 1847 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1848 |
source_datasets = config.DatasetConfig.get("source_datasets", None)
|
| 1849 |
generator_models = config.DatasetConfig.get("generator_models", None)
|
|
@@ -1854,7 +1912,7 @@ class CoSpyDatasetModule:
|
|
| 1854 |
self.test_dataset = CoSpyDataset(
|
| 1855 |
config.DatasetConfig.root_dir,
|
| 1856 |
"test",
|
| 1857 |
-
self.
|
| 1858 |
prefetch=prefetch,
|
| 1859 |
source_datasets=list(source_datasets) if source_datasets else None,
|
| 1860 |
generator_models=list(generator_models) if generator_models else None,
|
|
@@ -2015,10 +2073,7 @@ class VideoFolderDatasetModule:
|
|
| 2015 |
def __init__(self, config):
|
| 2016 |
self.config = config
|
| 2017 |
cfg = config.DatasetConfig
|
| 2018 |
-
|
| 2019 |
-
eval_list = list(cfg.get("eval_transform_list", []))
|
| 2020 |
-
self.train_transform = AIGITransformations(train_list) if len(train_list) > 0 else None
|
| 2021 |
-
self.eval_transform = AIGITransformations(eval_list) if len(eval_list) > 0 else None
|
| 2022 |
prefetch = bool(cfg.get("prefetch", False))
|
| 2023 |
common = dict(
|
| 2024 |
num_frames=cfg.get("num_frames", 16),
|
|
@@ -2030,8 +2085,8 @@ class VideoFolderDatasetModule:
|
|
| 2030 |
resize_interp=cfg.get("resize_interp", "bilinear"),
|
| 2031 |
)
|
| 2032 |
self.train_dataset = VideoFolderDataset(cfg.root_dir, "train", self.train_transform, prefetch=prefetch, **common)
|
| 2033 |
-
self.val_dataset = VideoFolderDataset(cfg.root_dir, "val", self.
|
| 2034 |
-
self.test_dataset = VideoFolderDataset(cfg.root_dir, "test", self.
|
| 2035 |
|
| 2036 |
def train_dataloader(self):
|
| 2037 |
return DataLoader(self.train_dataset, batch_size=self.config.DatasetConfig.train_batch_size, shuffle=True, num_workers=self.config.DatasetConfig.num_workers, persistent_workers=self.config.DatasetConfig.persistent_workers, pin_memory=self.config.DatasetConfig.pin_memory)
|
|
@@ -2148,10 +2203,9 @@ class _TestOnlyVideoModule:
|
|
| 2148 |
def __init__(self, config):
|
| 2149 |
self.config = config
|
| 2150 |
cfg = config.DatasetConfig
|
| 2151 |
-
|
| 2152 |
-
self.eval_transform = AIGITransformations(eval_list) if len(eval_list) > 0 else None
|
| 2153 |
self.test_dataset = self.DATASET_CLASS(
|
| 2154 |
-
cfg.root_dir, "test", self.
|
| 2155 |
num_frames=cfg.get("num_frames", 16), fps=cfg.get("fps", None),
|
| 2156 |
window_seconds=cfg.get("window_seconds", 2.0), frame_size=cfg.get("frame_size", 224),
|
| 2157 |
hf_repo_id=cfg.get("hf_repo_id", self.DATASET_CLASS.hf_repo_id),
|
|
@@ -2922,10 +2976,9 @@ class _TestOnlyImageModule:
|
|
| 2922 |
def __init__(self, config):
|
| 2923 |
self.config = config
|
| 2924 |
cfg = config.DatasetConfig
|
| 2925 |
-
|
| 2926 |
-
self.eval_transform = AIGITransformations(eval_list) if len(eval_list) > 0 else None
|
| 2927 |
self.test_dataset = self.DATASET_CLASS(
|
| 2928 |
-
cfg.root_dir, "test", self.
|
| 2929 |
|
| 2930 |
def train_dataloader(self):
|
| 2931 |
return None
|
|
@@ -2993,20 +3046,18 @@ class SIDSetDatasetModule(_TestOnlyImageModule):
|
|
| 2993 |
|
| 2994 |
|
| 2995 |
class ARForensicsDatasetModule(_TestOnlyImageModule):
|
| 2996 |
-
"""The test split for benchmarking; with
|
| 2997 |
-
(D3QE's training data
|
| 2998 |
DATASET_CLASS = ARForensicsDataset
|
| 2999 |
|
| 3000 |
def __init__(self, config):
|
| 3001 |
super().__init__(config)
|
| 3002 |
cfg = config.DatasetConfig
|
| 3003 |
-
train_list = list(cfg.get("train_transform_list", []))
|
| 3004 |
self.train_dataset = self.val_dataset = None
|
| 3005 |
-
if
|
| 3006 |
prefetch = bool(cfg.get("prefetch", False))
|
| 3007 |
-
|
| 3008 |
-
self.
|
| 3009 |
-
self.val_dataset = ARForensicsDataset(cfg.root_dir, "val", AIGITransformations(val_list), prefetch=prefetch)
|
| 3010 |
|
| 3011 |
def _loader(self, dataset, batch_size, shuffle):
|
| 3012 |
cfg = self.config.DatasetConfig
|
|
|
|
| 1 |
from torch.utils.data import Dataset
|
| 2 |
from typing import Literal
|
| 3 |
from pathlib import Path
|
| 4 |
+
import functools
|
| 5 |
import hashlib
|
| 6 |
import json
|
| 7 |
import os
|
|
|
|
| 23 |
import torch
|
| 24 |
import torch.nn.functional as F
|
| 25 |
from decord import VideoReader, cpu
|
| 26 |
+
from omegaconf import OmegaConf
|
| 27 |
from torch.utils.data import DataLoader
|
| 28 |
from transformations import AIGITransformations
|
| 29 |
from torchvision.io import decode_image, ImageReadMode
|
|
|
|
| 73 |
return directory.is_dir() and any(_iter_videos(directory))
|
| 74 |
|
| 75 |
|
| 76 |
+
def _config_list(section, name):
|
| 77 |
+
"""``section.<name>`` as a list, or None when the section or the key is absent or null."""
|
| 78 |
+
value = None if section is None else section.get(name, None)
|
| 79 |
+
return None if value is None else list(value)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
DATASET_CONF_DIR = Path(__file__).resolve().parent / "conf" / "dataset"
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
@functools.lru_cache(maxsize=None)
|
| 86 |
+
def _dataset_yaml_lists(name: str) -> tuple[dict[str, list | None], ...]:
|
| 87 |
+
"""The ``train`` / ``val`` / ``eval`` transform lists shipped in the dataset yaml files of dataset ``name`` (matched
|
| 88 |
+
by file stem or ``name:``; several files may share a name), i.e. what DatasetConfig holds unless overridden."""
|
| 89 |
+
found = []
|
| 90 |
+
for path in sorted(DATASET_CONF_DIR.rglob("*.yaml")):
|
| 91 |
+
cfg = OmegaConf.load(path)
|
| 92 |
+
if name in (path.stem, cfg.get("name", None)):
|
| 93 |
+
found.append({key: _config_list(cfg, f"{key}_transform_list") for key in ("train", "val", "eval")})
|
| 94 |
+
return tuple(found)
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def _overridden(data, key: str, value: list) -> bool:
|
| 98 |
+
"""Whether DatasetConfig's ``<key>_transform_list`` was overridden (differs from its dataset yaml). False when the
|
| 99 |
+
yaml file or the key cannot be found."""
|
| 100 |
+
shipped = [lists[key] for lists in _dataset_yaml_lists(str(data.get("name", ""))) if lists[key] is not None]
|
| 101 |
+
return bool(shipped) and value not in shipped
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def _first_list(*candidates):
|
| 105 |
+
"""The first candidate list that is set (not None), else None. An explicit ``[]`` counts as set: an empty list in
|
| 106 |
+
the model config intentionally stops the fallback to the dataset config (that split gets no transform)."""
|
| 107 |
+
return next((c for c in candidates if c is not None), None)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def resolve_transforms(config) -> tuple[AIGITransformations | None, AIGITransformations | None, AIGITransformations | None]:
|
| 111 |
+
"""The ``(train, val, test)`` transforms of a run. Each comes from ``ModelConfig`` (the detector's own preprocessing)
|
| 112 |
+
and falls back to ``DatasetConfig`` for models whose config sets none. Precedence (first list set wins):
|
| 113 |
+
|
| 114 |
+
* train: model ``train_transform_list``, dataset ``train_transform_list``
|
| 115 |
+
* val: model ``val_transform_list``, model ``eval_transform_list``, dataset ``val_transform_list``, dataset
|
| 116 |
+
``eval_transform_list``
|
| 117 |
+
* test: model ``test_transform_list`` (a batchable stand-in for native-resolution eval lists), model
|
| 118 |
+
``eval_transform_list``, dataset ``eval_transform_list``
|
| 119 |
+
|
| 120 |
+
A DatasetConfig list that was overridden (differs from its dataset yaml, e.g. an old
|
| 121 |
+
``DatasetConfig.eval_transform_list=`` override) but loses to a model list is reported.
|
| 122 |
+
An empty list gives None."""
|
| 123 |
+
model = getattr(config, "ModelConfig", None)
|
| 124 |
+
data = getattr(config, "DatasetConfig", None)
|
| 125 |
+
model_train = _config_list(model, "train_transform_list")
|
| 126 |
+
model_val = _first_list(_config_list(model, "val_transform_list"), _config_list(model, "eval_transform_list"))
|
| 127 |
+
model_test = _first_list(_config_list(model, "test_transform_list"), _config_list(model, "eval_transform_list"))
|
| 128 |
+
data_lists = {key: _config_list(data, f"{key}_transform_list") for key in ("train", "val", "eval")}
|
| 129 |
+
for key, model_list in (("train", model_train), ("val", model_val), ("eval", model_test)):
|
| 130 |
+
if (data_lists[key] is not None and model_list is not None and data_lists[key] != model_list
|
| 131 |
+
and _overridden(data, key, data_lists[key])):
|
| 132 |
+
print(f"[transforms] DatasetConfig.{key}_transform_list is ignored: the model config's list is used "
|
| 133 |
+
f"({model_list})", flush=True)
|
| 134 |
+
train = _first_list(model_train, data_lists["train"])
|
| 135 |
+
val = _first_list(model_val, data_lists["val"], data_lists["eval"])
|
| 136 |
+
test = _first_list(model_test, data_lists["eval"])
|
| 137 |
+
# Val and test convert the decoded uint8 tensors to PIL first, so they run the exact inference code (benchmarks
|
| 138 |
+
# and the web UI feed PIL; tensor resizes differ by 1-2/255, and some eval steps take PIL only). Train stays on
|
| 139 |
+
# tensors for speed.
|
| 140 |
+
return (AIGITransformations(train) if train else None,
|
| 141 |
+
AIGITransformations(val, pil_input=True) if val else None,
|
| 142 |
+
AIGITransformations(test, pil_input=True) if test else None)
|
| 143 |
+
|
| 144 |
+
|
| 145 |
class _PILFrames:
|
| 146 |
"""The part of decord's ``VideoReader`` API that ``sample_video_frames`` uses, for animated images decord cannot open
|
| 147 |
(GIFs: decord fails with "broken metadata" on them). Every frame is composited to RGB at the canvas size, and the
|
|
|
|
| 1199 |
class CNNDetectionDatasetModule:
|
| 1200 |
def __init__(self, config):
|
| 1201 |
self.config = config
|
| 1202 |
+
self.train_transform, self.val_transform, self.test_transform = resolve_transforms(config)
|
|
|
|
|
|
|
|
|
|
| 1203 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1204 |
classes = config.DatasetConfig.get("train_classes", None) # ProGAN classes for train/val (None: all 20)
|
| 1205 |
classes = list(classes) if classes else None
|
| 1206 |
self.train_dataset = CNNDetectionDataset(config.DatasetConfig.root_dir, "train", self.train_transform, prefetch=prefetch, classes=classes)
|
| 1207 |
+
self.val_dataset = CNNDetectionDataset(config.DatasetConfig.root_dir, "val", self.val_transform, prefetch=prefetch, classes=classes)
|
| 1208 |
+
self.test_dataset = CNNDetectionDataset(config.DatasetConfig.root_dir, "test", self.test_transform, prefetch=prefetch)
|
| 1209 |
|
| 1210 |
def train_dataloader(self):
|
| 1211 |
return DataLoader(self.train_dataset, batch_size=self.config.DatasetConfig.train_batch_size, shuffle=True, num_workers=self.config.DatasetConfig.num_workers, persistent_workers=self.config.DatasetConfig.persistent_workers, pin_memory=self.config.DatasetConfig.pin_memory)
|
|
|
|
| 1217 |
return DataLoader(self.test_dataset, batch_size=self.config.DatasetConfig.test_batch_size, shuffle=False, num_workers=self.config.DatasetConfig.num_workers, persistent_workers=self.config.DatasetConfig.persistent_workers, pin_memory=self.config.DatasetConfig.pin_memory)
|
| 1218 |
|
| 1219 |
|
| 1220 |
+
def _split_train_subset(dataset, per_class, val_per_class, val_transform, seed=0):
|
| 1221 |
"""Keep a seeded ``per_class`` images per label of ``dataset`` for training and return a disjoint ``val_per_class``
|
| 1222 |
+
per label as a validation copy with ``val_transform``: for recipes trained on part of a train split, validated on
|
| 1223 |
held-out training images (GenImage's ``val`` alias is its test split, which must not select the model)."""
|
| 1224 |
rng = random.Random(seed)
|
| 1225 |
train_idx, val_idx = [], []
|
|
|
|
| 1235 |
ds.images = [dataset.images[i] for i in keep]
|
| 1236 |
ds.labels = [dataset.labels[i] for i in keep]
|
| 1237 |
ds.folder_names = [dataset.folder_names[i] for i in keep]
|
| 1238 |
+
val.transform = val_transform
|
| 1239 |
return val
|
| 1240 |
|
| 1241 |
|
| 1242 |
class GenImageDatasetModule:
|
| 1243 |
def __init__(self, config):
|
| 1244 |
self.config = config
|
| 1245 |
+
self.train_transform, self.val_transform, self.test_transform = resolve_transforms(config)
|
|
|
|
|
|
|
|
|
|
| 1246 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1247 |
source_url = config.DatasetConfig.get("source_url", GenImageDataset.google_drive_folder_url)
|
| 1248 |
GenImageDataset.google_drive_folder_url = source_url
|
| 1249 |
GenImageDataset.hf_repo_id = config.DatasetConfig.get("hf_repo_id", None)
|
| 1250 |
GenImageDataset.hf_subdir = config.DatasetConfig.get("hf_subdir", None)
|
| 1251 |
self.train_dataset = GenImageDataset(config.DatasetConfig.root_dir, "train", self.train_transform, prefetch=prefetch)
|
| 1252 |
+
self.test_dataset = GenImageDataset(config.DatasetConfig.root_dir, "test", self.test_transform, prefetch=prefetch)
|
| 1253 |
per_class = config.DatasetConfig.get("train_per_class", None) # e.g. VIB-Net: 16k real + 16k SD v1.4
|
| 1254 |
if per_class:
|
| 1255 |
self.val_dataset = _split_train_subset(self.train_dataset, int(per_class),
|
| 1256 |
+
int(config.DatasetConfig.get("val_per_class", 1000)), self.val_transform)
|
| 1257 |
else:
|
| 1258 |
+
self.val_dataset = GenImageDataset(config.DatasetConfig.root_dir, "val", self.val_transform, prefetch=prefetch)
|
| 1259 |
|
| 1260 |
def train_dataloader(self):
|
| 1261 |
return DataLoader(self.train_dataset, batch_size=self.config.DatasetConfig.train_batch_size, shuffle=True, num_workers=self.config.DatasetConfig.num_workers, persistent_workers=self.config.DatasetConfig.persistent_workers, pin_memory=self.config.DatasetConfig.pin_memory)
|
|
|
|
| 1822 |
class AIGIBenchDatasetModule:
|
| 1823 |
def __init__(self, config):
|
| 1824 |
self.config = config
|
| 1825 |
+
self.train_transform, self.val_transform, self.test_transform = resolve_transforms(config)
|
|
|
|
|
|
|
|
|
|
| 1826 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1827 |
self.train_dataset = AIGIBenchDataset(config.DatasetConfig.root_dir, "train", config.DatasetConfig.setting, self.train_transform, prefetch=prefetch)
|
| 1828 |
+
self.val_dataset = AIGIBenchDataset(config.DatasetConfig.root_dir, "val", config.DatasetConfig.setting, self.val_transform, prefetch=prefetch)
|
| 1829 |
+
self.test_dataset = AIGIBenchDataset(config.DatasetConfig.root_dir, "test", config.DatasetConfig.setting, self.test_transform, prefetch=prefetch)
|
| 1830 |
|
| 1831 |
def train_dataloader(self):
|
| 1832 |
return DataLoader(self.train_dataset, batch_size=self.config.DatasetConfig.train_batch_size, shuffle=True, num_workers=self.config.DatasetConfig.num_workers, persistent_workers=self.config.DatasetConfig.persistent_workers, pin_memory=self.config.DatasetConfig.pin_memory)
|
|
|
|
| 1841 |
class DiTFakeDatasetModule:
|
| 1842 |
def __init__(self, config):
|
| 1843 |
self.config = config
|
| 1844 |
+
self.test_transform = resolve_transforms(config)[2]
|
|
|
|
| 1845 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1846 |
repo_id = config.DatasetConfig.get("repo_id", DiTFakeDataset.repo_id)
|
| 1847 |
+
self.test_dataset = DiTFakeDataset(config.DatasetConfig.root_dir, "test", self.test_transform, prefetch=prefetch, repo_id=repo_id)
|
| 1848 |
|
| 1849 |
def test_dataloader(self):
|
| 1850 |
return DataLoader(self.test_dataset, batch_size=self.config.DatasetConfig.test_batch_size, shuffle=False, num_workers=self.config.DatasetConfig.num_workers, persistent_workers=self.config.DatasetConfig.persistent_workers, pin_memory=self.config.DatasetConfig.pin_memory)
|
|
|
|
| 1853 |
class FreqNetDetectionDatasetModule:
|
| 1854 |
def __init__(self, config):
|
| 1855 |
self.config = config
|
| 1856 |
+
self.test_transform = resolve_transforms(config)[2]
|
|
|
|
| 1857 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1858 |
source_url = config.DatasetConfig.get("source_url", FreqNetDetectionDataset.google_drive_folder_url)
|
| 1859 |
self.test_dataset = FreqNetDetectionDataset(
|
| 1860 |
config.DatasetConfig.root_dir,
|
| 1861 |
"test",
|
| 1862 |
+
self.test_transform,
|
| 1863 |
prefetch=prefetch,
|
| 1864 |
source_url=source_url,
|
| 1865 |
)
|
|
|
|
| 1877 |
class UniversialFakeDetectionDatasetModule:
|
| 1878 |
def __init__(self, config):
|
| 1879 |
self.config = config
|
| 1880 |
+
self.test_transform = resolve_transforms(config)[2]
|
|
|
|
| 1881 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1882 |
source_url = config.DatasetConfig.get("source_url", UniversialFakeDetectionDataset.source_url)
|
| 1883 |
self.test_dataset = UniversialFakeDetectionDataset(
|
| 1884 |
config.DatasetConfig.root_dir,
|
| 1885 |
"test",
|
| 1886 |
+
self.test_transform,
|
| 1887 |
prefetch=prefetch,
|
| 1888 |
source_url=source_url,
|
| 1889 |
)
|
|
|
|
| 1901 |
class CoSpyDatasetModule:
|
| 1902 |
def __init__(self, config):
|
| 1903 |
self.config = config
|
| 1904 |
+
self.test_transform = resolve_transforms(config)[2]
|
|
|
|
| 1905 |
prefetch = getattr(config.DatasetConfig, "prefetch", False)
|
| 1906 |
source_datasets = config.DatasetConfig.get("source_datasets", None)
|
| 1907 |
generator_models = config.DatasetConfig.get("generator_models", None)
|
|
|
|
| 1912 |
self.test_dataset = CoSpyDataset(
|
| 1913 |
config.DatasetConfig.root_dir,
|
| 1914 |
"test",
|
| 1915 |
+
self.test_transform,
|
| 1916 |
prefetch=prefetch,
|
| 1917 |
source_datasets=list(source_datasets) if source_datasets else None,
|
| 1918 |
generator_models=list(generator_models) if generator_models else None,
|
|
|
|
| 2073 |
def __init__(self, config):
|
| 2074 |
self.config = config
|
| 2075 |
cfg = config.DatasetConfig
|
| 2076 |
+
self.train_transform, self.val_transform, self.test_transform = resolve_transforms(config)
|
|
|
|
|
|
|
|
|
|
| 2077 |
prefetch = bool(cfg.get("prefetch", False))
|
| 2078 |
common = dict(
|
| 2079 |
num_frames=cfg.get("num_frames", 16),
|
|
|
|
| 2085 |
resize_interp=cfg.get("resize_interp", "bilinear"),
|
| 2086 |
)
|
| 2087 |
self.train_dataset = VideoFolderDataset(cfg.root_dir, "train", self.train_transform, prefetch=prefetch, **common)
|
| 2088 |
+
self.val_dataset = VideoFolderDataset(cfg.root_dir, "val", self.val_transform, prefetch=prefetch, **common)
|
| 2089 |
+
self.test_dataset = VideoFolderDataset(cfg.root_dir, "test", self.test_transform, prefetch=prefetch, **common)
|
| 2090 |
|
| 2091 |
def train_dataloader(self):
|
| 2092 |
return DataLoader(self.train_dataset, batch_size=self.config.DatasetConfig.train_batch_size, shuffle=True, num_workers=self.config.DatasetConfig.num_workers, persistent_workers=self.config.DatasetConfig.persistent_workers, pin_memory=self.config.DatasetConfig.pin_memory)
|
|
|
|
| 2203 |
def __init__(self, config):
|
| 2204 |
self.config = config
|
| 2205 |
cfg = config.DatasetConfig
|
| 2206 |
+
self.test_transform = resolve_transforms(config)[2]
|
|
|
|
| 2207 |
self.test_dataset = self.DATASET_CLASS(
|
| 2208 |
+
cfg.root_dir, "test", self.test_transform, prefetch=bool(cfg.get("prefetch", False)),
|
| 2209 |
num_frames=cfg.get("num_frames", 16), fps=cfg.get("fps", None),
|
| 2210 |
window_seconds=cfg.get("window_seconds", 2.0), frame_size=cfg.get("frame_size", 224),
|
| 2211 |
hf_repo_id=cfg.get("hf_repo_id", self.DATASET_CLASS.hf_repo_id),
|
|
|
|
| 2976 |
def __init__(self, config):
|
| 2977 |
self.config = config
|
| 2978 |
cfg = config.DatasetConfig
|
| 2979 |
+
self.train_transform, self.val_transform, self.test_transform = resolve_transforms(config)
|
|
|
|
| 2980 |
self.test_dataset = self.DATASET_CLASS(
|
| 2981 |
+
cfg.root_dir, "test", self.test_transform, prefetch=bool(cfg.get("prefetch", False)))
|
| 2982 |
|
| 2983 |
def train_dataloader(self):
|
| 2984 |
return None
|
|
|
|
| 3046 |
|
| 3047 |
|
| 3048 |
class ARForensicsDatasetModule(_TestOnlyImageModule):
|
| 3049 |
+
"""The test split for benchmarking; with ``DatasetConfig.with_train_splits`` (default false) also the train and val
|
| 3050 |
+
splits (D3QE's training data, 39 GB), with the run's train and val transforms (see ``resolve_transforms``)."""
|
| 3051 |
DATASET_CLASS = ARForensicsDataset
|
| 3052 |
|
| 3053 |
def __init__(self, config):
|
| 3054 |
super().__init__(config)
|
| 3055 |
cfg = config.DatasetConfig
|
|
|
|
| 3056 |
self.train_dataset = self.val_dataset = None
|
| 3057 |
+
if bool(cfg.get("with_train_splits", False)):
|
| 3058 |
prefetch = bool(cfg.get("prefetch", False))
|
| 3059 |
+
self.train_dataset = ARForensicsDataset(cfg.root_dir, "train", self.train_transform, prefetch=prefetch)
|
| 3060 |
+
self.val_dataset = ARForensicsDataset(cfg.root_dir, "val", self.val_transform, prefetch=prefetch)
|
|
|
|
| 3061 |
|
| 3062 |
def _loader(self, dataset, batch_size, shuffle):
|
| 3063 |
cfg = self.config.DatasetConfig
|
docs/ADD_DETECTOR.md
CHANGED
|
@@ -19,13 +19,28 @@ for the maintainers and stop.
|
|
| 19 |
- Imports + helper classes at module top-level (repo rule: no nested defs/imports).
|
| 20 |
- Add `models/<modality>/<k>/__init__.py` and a `README.md` (see structure below).
|
| 21 |
2. **`conf/model/image/<k>.yaml`** (or `video/`): `defaults: [/model/default, _self_]`, then `model_type: <k>`
|
| 22 |
-
plus any model knobs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
3. **Dispatch** — add a branch in `trainer.py` `_build_model` (the `if model_type == "<k>": return SynthScope<Name>Model(config)` chain, ~line 69+). The interface lazy-imports by `model_type` in `interface/backend/engine.py` — mirror it if needed.
|
| 24 |
4. **Interface registry** — `interface/backend/registry.py` `_add(ModelSpec(...))`:
|
| 25 |
- `key`, `name`, `paper`, `modality` (`image`|`video`), `overrides=["model@ModelConfig=image/<k>"]`,
|
| 26 |
- `predict_kind`: `ce` (logits→`softmax[:,1]`), `bce` (`sigmoid`), `bce_npr`, `vib`, or `video`,
|
| 27 |
- `checkpoint="<k>.pth"`, `ckpt_kind`: `synthscope` (our trained weights, strict load) **or** `official_<k>` + a `remap=remap_<k>` function (rename official keys → our module names) for the authors' released weights,
|
| 28 |
-
- `transform`: the eval preprocessing — match the **official** eval exactly (resize vs crop, norm vs none).
|
| 29 |
- `weights`: `"official"` | `"synthscope_trained"` | `"training_free"` (shown in the web UI badge).
|
| 30 |
5. **Per-model `README.md`** — mirror `models/image/pgc/README.md`: short method explanation + official-repo/paper links, the pipeline, "SynthScope adaptations", and a results table in the **Paper / SynthScope_official / SynthScope_train** schema.
|
| 31 |
|
|
|
|
| 19 |
- Imports + helper classes at module top-level (repo rule: no nested defs/imports).
|
| 20 |
- Add `models/<modality>/<k>/__init__.py` and a `README.md` (see structure below).
|
| 21 |
2. **`conf/model/image/<k>.yaml`** (or `video/`): `defaults: [/model/default, _self_]`, then `model_type: <k>`
|
| 22 |
+
plus any model knobs, and the model's preprocessing as named transform lists:
|
| 23 |
+
- `eval_transform_list` (required for image models): the **official** test-time preprocessing, used for
|
| 24 |
+
inference, benchmarks and the end-of-training test;
|
| 25 |
+
- `train_transform_list`: the training augmentation;
|
| 26 |
+
- `val_transform_list` (optional): validation during training, when it differs from the eval list;
|
| 27 |
+
- `test_transform_list` (optional): replaces the eval list for the end-of-training test only, e.g. a batchable
|
| 28 |
+
stand-in for SPAI's native-resolution eval, or AIDE's five views with the long side capped at 1024 to bound
|
| 29 |
+
dataloader-worker memory; benchmarks and inference still use `eval_transform_list`.
|
| 30 |
+
|
| 31 |
+
Each name is a step built by `AIGITransformations` in `transformations.py`; add new step names there.
|
| 32 |
+
The model's lists take precedence over the dataset's: the `train/eval/val_transform_list` of a
|
| 33 |
+
`conf/dataset/...` config are fallbacks, used only for models whose config sets none, and a warning is printed
|
| 34 |
+
when a dataset list is shadowed. Validation and test pipelines run on PIL images like inference, so an
|
| 35 |
+
end-of-training test with `eval_transform_list` preprocesses exactly as the benchmarks do.
|
| 36 |
+
Video models are out of scope for now: their preprocessing is the registry's video settings plus the dataset
|
| 37 |
+
lists. Add `conf/dataset/...` / `conf/trainer/...` only if a new recipe is needed.
|
| 38 |
3. **Dispatch** — add a branch in `trainer.py` `_build_model` (the `if model_type == "<k>": return SynthScope<Name>Model(config)` chain, ~line 69+). The interface lazy-imports by `model_type` in `interface/backend/engine.py` — mirror it if needed.
|
| 39 |
4. **Interface registry** — `interface/backend/registry.py` `_add(ModelSpec(...))`:
|
| 40 |
- `key`, `name`, `paper`, `modality` (`image`|`video`), `overrides=["model@ModelConfig=image/<k>"]`,
|
| 41 |
- `predict_kind`: `ce` (logits→`softmax[:,1]`), `bce` (`sigmoid`), `bce_npr`, `vib`, or `video`,
|
| 42 |
- `checkpoint="<k>.pth"`, `ckpt_kind`: `synthscope` (our trained weights, strict load) **or** `official_<k>` + a `remap=remap_<k>` function (rename official keys → our module names) for the authors' released weights,
|
| 43 |
+
- `transform=model_transform("image/<k>")`: builds the eval preprocessing from the model config's `eval_transform_list` — match the **official** eval exactly there (resize vs crop, norm vs none). (Getting this wrong silently tanks results — see SAFE: a resize destroyed the high-freq signal.)
|
| 44 |
- `weights`: `"official"` | `"synthscope_trained"` | `"training_free"` (shown in the web UI badge).
|
| 45 |
5. **Per-model `README.md`** — mirror `models/image/pgc/README.md`: short method explanation + official-repo/paper links, the pipeline, "SynthScope adaptations", and a results table in the **Paper / SynthScope_official / SynthScope_train** schema.
|
| 46 |
|
docs/BENCHMARKS.md
CHANGED
|
@@ -222,7 +222,8 @@ the GPU). Models are downloaded once into the Hugging Face cache (tens of GB eac
|
|
| 222 |
|
| 223 |
- **A detector**: follow [`docs/ADD_DETECTOR.md`](ADD_DETECTOR.md); then add it to the Leaderboard as in section 3.
|
| 224 |
- **A dataset**: add `conf/dataset/image/<name>.yaml` with the source (`hf_repo_id`, `source_url`, ...), make it follow the
|
| 225 |
-
layout in section 2, and add a profile to `bench_eval.py` if its grouping is new.
|
|
|
|
| 226 |
- **A generator**: add a function `call(image_png | None, prompt, size=None) -> png bytes` and one entry in `GENERATORS` in
|
| 227 |
`interface/backend/generators.py`. `image_png=None` means "create from the prompt alone". Then run section 4 with
|
| 228 |
`--generator <your key>`.
|
|
|
|
| 222 |
|
| 223 |
- **A detector**: follow [`docs/ADD_DETECTOR.md`](ADD_DETECTOR.md); then add it to the Leaderboard as in section 3.
|
| 224 |
- **A dataset**: add `conf/dataset/image/<name>.yaml` with the source (`hf_repo_id`, `source_url`, ...), make it follow the
|
| 225 |
+
layout in section 2, and add a profile to `bench_eval.py` if its grouping is new. Its transform lists are fallbacks,
|
| 226 |
+
used only for models whose config (`conf/model/...`) sets no lists of its own.
|
| 227 |
- **A generator**: add a function `call(image_png | None, prompt, size=None) -> png bytes` and one entry in `GENERATORS` in
|
| 228 |
`interface/backend/generators.py`. `image_png=None` means "create from the prompt alone". Then run section 4 with
|
| 229 |
`--generator <your key>`.
|
docs/CAPABILITIES.md
CHANGED
|
@@ -72,10 +72,12 @@ To wire it in: `conf/model/<modality>/<key>.yaml` (`model_type: <key>`), a branc
|
|
| 72 |
with groups `model/`, `dataset/`, `trainer/`. Multi-GPU via 🤗 `accelerate` (`accelerate_config.yaml`).
|
| 73 |
- **`dataset.py`** — image/video dataset loaders; `_decode_rgb` feeds uint8 `[3,H,W]` tensors;
|
| 74 |
`sample_video_frames(..., edge_crop=)` for video.
|
| 75 |
-
- **`transformations.py`** — composable named transforms / augmentations: `
|
| 76 |
-
`resize_short_{256,384,448}`, `center_crop_{224,256,384,448}`, `random_crop_*`, `pad_random_crop_224`
|
| 77 |
(PGC), `gaussian_blur`, `jpeg_compression`, `random_horizontal/vertical_flip`, `random_rotation_180`,
|
| 78 |
-
`color_jitter_safe`, `to_tensor`/`to_dtype`.
|
|
|
|
|
|
|
| 79 |
low-level detectors (SAFE/AIDE/SPAI/PGC/NPR) are wrecked by a wrong resize; match the official eval
|
| 80 |
exactly (see `repro/investigate/pgc_gap_investigation.md` and the SAFE crop-not-resize lesson).
|
| 81 |
- **`metrics.py`** — torchmetrics binary `Accuracy` (real/fake), `AveragePrecision` (AP),
|
|
@@ -170,6 +172,6 @@ New detectors are integrated **on a `detector/<key>` branch and pushed for revie
|
|
| 170 |
|
| 171 |
1. Confirm it passes both rules (public repo + strong results). Read `ADD_DETECTOR.md`.
|
| 172 |
2. `git checkout -b detector/<key>`; add the 5 files (model, conf, trainer dispatch, registry ModelSpec, README) + smoke test.
|
| 173 |
-
3. **Match the official eval transform exactly** in the `
|
| 174 |
4. Reproduce on the paper's benchmark with official weights via `bench_eval.py --ckpt`; fix `remap_<k>` for key mismatches; record per-gen AP/AUROC in the Paper/SynthScope_official/SynthScope_train schema.
|
| 175 |
5. (Optional) add an interpretability map. Push the branch; do **not** merge to `main`.
|
|
|
|
| 72 |
with groups `model/`, `dataset/`, `trainer/`. Multi-GPU via 🤗 `accelerate` (`accelerate_config.yaml`).
|
| 73 |
- **`dataset.py`** — image/video dataset loaders; `_decode_rgb` feeds uint8 `[3,H,W]` tensors;
|
| 74 |
`sample_video_frames(..., edge_crop=)` for video.
|
| 75 |
+
- **`transformations.py`** — composable named transforms / augmentations: `resize_{224,256,336,512}`,
|
| 76 |
+
`resize_short_{256,384,448}`, `center_crop_{224,256,336,384,448}`, `random_crop_*`, `pad_random_crop_224`
|
| 77 |
(PGC), `gaussian_blur`, `jpeg_compression`, `random_horizontal/vertical_flip`, `random_rotation_180`,
|
| 78 |
+
`color_jitter_safe`, `to_tensor`/`to_dtype`. Each model config names its pipelines (`eval_transform_list`,
|
| 79 |
+
`train_transform_list`, optional `val_transform_list` / `test_transform_list`); the web registry builds
|
| 80 |
+
`ModelSpec.transform` from `eval_transform_list`. **Eval-transform fidelity is critical** — artifact/
|
| 81 |
low-level detectors (SAFE/AIDE/SPAI/PGC/NPR) are wrecked by a wrong resize; match the official eval
|
| 82 |
exactly (see `repro/investigate/pgc_gap_investigation.md` and the SAFE crop-not-resize lesson).
|
| 83 |
- **`metrics.py`** — torchmetrics binary `Accuracy` (real/fake), `AveragePrecision` (AP),
|
|
|
|
| 172 |
|
| 173 |
1. Confirm it passes both rules (public repo + strong results). Read `ADD_DETECTOR.md`.
|
| 174 |
2. `git checkout -b detector/<key>`; add the 5 files (model, conf, trainer dispatch, registry ModelSpec, README) + smoke test.
|
| 175 |
+
3. **Match the official eval transform exactly** in the model config's `eval_transform_list` (resize vs crop, norm) — this is the #1 silent-failure source.
|
| 176 |
4. Reproduce on the paper's benchmark with official weights via `bench_eval.py --ckpt`; fix `remap_<k>` for key mismatches; record per-gen AP/AUROC in the Paper/SynthScope_official/SynthScope_train schema.
|
| 177 |
5. (Optional) add an interpretability map. Push the branch; do **not** merge to `main`.
|
experiments/image/adapted_dinov2_vitl14.sh
CHANGED
|
@@ -6,9 +6,7 @@ export CUDA_VISIBLE_DEVICES=1,2,3,4
|
|
| 6 |
export OMP_NUM_THREADS=1
|
| 7 |
|
| 8 |
accelerate launch --config_file accelerate_config.yaml --num_processes 4 main.py \
|
|
|
|
| 9 |
ModelConfig.bottleneck_dim=64 \
|
| 10 |
-
ModelConfig.model_type=dino \
|
| 11 |
ModelConfig.use_adapter=true \
|
| 12 |
-
DatasetConfig.train_transform_list=[resize_short_256,gaussian_blur,jpeg_compression,random_crop_224,random_horizontal_flip,to_dtype,normalize_dinov2] \
|
| 13 |
-
DatasetConfig.eval_transform_list=[resize_short_256,center_crop_224,to_dtype,normalize_dinov2] \
|
| 14 |
ModelConfig.backbone_name=dinov2_vitl14
|
|
|
|
| 6 |
export OMP_NUM_THREADS=1
|
| 7 |
|
| 8 |
accelerate launch --config_file accelerate_config.yaml --num_processes 4 main.py \
|
| 9 |
+
model@ModelConfig=image/adapted_dino \
|
| 10 |
ModelConfig.bottleneck_dim=64 \
|
|
|
|
| 11 |
ModelConfig.use_adapter=true \
|
|
|
|
|
|
|
| 12 |
ModelConfig.backbone_name=dinov2_vitl14
|
experiments/image/aide.sh
CHANGED
|
@@ -5,15 +5,16 @@ export TORCH_HOME=/nvmestore/ssalehi/.cache/torch
|
|
| 5 |
export CUDA_VISIBLE_DEVICES=1,2
|
| 6 |
export OMP_NUM_THREADS=1
|
| 7 |
|
|
|
|
|
|
|
| 8 |
/home/ssalehi/miniconda3/bin/conda run --no-capture-output -n SynthScope accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py \
|
| 9 |
trainer@TrainerConfig=image/aide \
|
| 10 |
model@ModelConfig=image/aide \
|
| 11 |
dataset@DatasetConfig=image/genimage \
|
| 12 |
-
DatasetConfig.train_batch_size=
|
|
|
|
| 13 |
DatasetConfig.val_batch_size=16 \
|
| 14 |
DatasetConfig.test_batch_size=16 \
|
| 15 |
-
DatasetConfig.train_transform_list=[resize_short_256,gaussian_blur,jpeg_compression,random_crop_256,random_horizontal_flip,to_dtype] \
|
| 16 |
-
DatasetConfig.eval_transform_list=[resize_short_256,center_crop_256,to_dtype] \
|
| 17 |
logger.name=aide \
|
| 18 |
logger.mode=online \
|
| 19 |
logger.tags=[SynthScope,AIDE,genimage]
|
|
|
|
| 5 |
export CUDA_VISIBLE_DEVICES=1,2
|
| 6 |
export OMP_NUM_THREADS=1
|
| 7 |
|
| 8 |
+
# Five native-resolution views per image (aide.yaml lists): batch 8 and 5 workers bound memory, as in the run behind
|
| 9 |
+
# the README's SynthScope_train checkpoint (repro/retrain_aide_native_v2.sbatch).
|
| 10 |
/home/ssalehi/miniconda3/bin/conda run --no-capture-output -n SynthScope accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py \
|
| 11 |
trainer@TrainerConfig=image/aide \
|
| 12 |
model@ModelConfig=image/aide \
|
| 13 |
dataset@DatasetConfig=image/genimage \
|
| 14 |
+
DatasetConfig.train_batch_size=8 \
|
| 15 |
+
DatasetConfig.num_workers=5 \
|
| 16 |
DatasetConfig.val_batch_size=16 \
|
| 17 |
DatasetConfig.test_batch_size=16 \
|
|
|
|
|
|
|
| 18 |
logger.name=aide \
|
| 19 |
logger.mode=online \
|
| 20 |
logger.tags=[SynthScope,AIDE,genimage]
|
experiments/image/clip_vitl14.sh
CHANGED
|
@@ -9,9 +9,7 @@ export OMP_NUM_THREADS=1
|
|
| 9 |
# accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py
|
| 10 |
|
| 11 |
accelerate launch --config_file accelerate_config.yaml --num_processes 4 main.py \
|
|
|
|
| 12 |
ModelConfig.bottleneck_dim=64 \
|
| 13 |
-
ModelConfig.model_type=clip \
|
| 14 |
ModelConfig.use_adapter=false \
|
| 15 |
-
DatasetConfig.train_transform_list=[resize_short_256,gaussian_blur,jpeg_compression,random_crop_224,random_horizontal_flip,to_dtype,normalize_clip] \
|
| 16 |
-
DatasetConfig.eval_transform_list=[resize_short_256,center_crop_224,to_dtype,normalize_clip] \
|
| 17 |
ModelConfig.backbone_name=CLIP:ViT-L/14
|
|
|
|
| 9 |
# accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py
|
| 10 |
|
| 11 |
accelerate launch --config_file accelerate_config.yaml --num_processes 4 main.py \
|
| 12 |
+
model@ModelConfig=image/clip \
|
| 13 |
ModelConfig.bottleneck_dim=64 \
|
|
|
|
| 14 |
ModelConfig.use_adapter=false \
|
|
|
|
|
|
|
| 15 |
ModelConfig.backbone_name=CLIP:ViT-L/14
|
experiments/image/co_spy.sh
CHANGED
|
@@ -11,8 +11,6 @@ export OMP_NUM_THREADS=1
|
|
| 11 |
DatasetConfig.train_batch_size=16 \
|
| 12 |
DatasetConfig.val_batch_size=16 \
|
| 13 |
DatasetConfig.test_batch_size=16 \
|
| 14 |
-
DatasetConfig.train_transform_list=[resize_short_384,gaussian_blur,jpeg_compression,random_crop_384,random_horizontal_flip,to_dtype] \
|
| 15 |
-
DatasetConfig.eval_transform_list=[resize_short_384,center_crop_384,to_dtype] \
|
| 16 |
logger.name=co_spy \
|
| 17 |
logger.mode=online \
|
| 18 |
logger.tags=[SynthScope,CoSpy,genimage]
|
|
|
|
| 11 |
DatasetConfig.train_batch_size=16 \
|
| 12 |
DatasetConfig.val_batch_size=16 \
|
| 13 |
DatasetConfig.test_batch_size=16 \
|
|
|
|
|
|
|
| 14 |
logger.name=co_spy \
|
| 15 |
logger.mode=online \
|
| 16 |
logger.tags=[SynthScope,CoSpy,genimage]
|
experiments/image/d3qe.sh
CHANGED
|
@@ -22,11 +22,9 @@ SAVE=${SAVE:-/nvmestore/ssalehi/SynthScope/models/d3qe}
|
|
| 22 |
model@ModelConfig=image/d3qe \
|
| 23 |
dataset@DatasetConfig=image/arforensics \
|
| 24 |
DatasetConfig.root_dir=$DATA \
|
|
|
|
| 25 |
DatasetConfig.train_batch_size=32 \
|
| 26 |
DatasetConfig.val_batch_size=64 \
|
| 27 |
-
"+DatasetConfig.train_transform_list=[resize_short_256,blur_jpeg_cnndet,random_crop_256,random_horizontal_flip,to_dtype]" \
|
| 28 |
-
"+DatasetConfig.val_transform_list=[resize_short_256,blur_jpeg_cnndet_val,center_crop_256,to_dtype]" \
|
| 29 |
-
"DatasetConfig.eval_transform_list=[resize_short_256,center_crop_256,to_dtype]" \
|
| 30 |
TrainerConfig.save_path=$SAVE \
|
| 31 |
"metrics=[real_accuracy,fake_accuracy]" \
|
| 32 |
logger.name=d3qe \
|
|
|
|
| 22 |
model@ModelConfig=image/d3qe \
|
| 23 |
dataset@DatasetConfig=image/arforensics \
|
| 24 |
DatasetConfig.root_dir=$DATA \
|
| 25 |
+
DatasetConfig.with_train_splits=true \
|
| 26 |
DatasetConfig.train_batch_size=32 \
|
| 27 |
DatasetConfig.val_batch_size=64 \
|
|
|
|
|
|
|
|
|
|
| 28 |
TrainerConfig.save_path=$SAVE \
|
| 29 |
"metrics=[real_accuracy,fake_accuracy]" \
|
| 30 |
logger.name=d3qe \
|
experiments/image/dinov2_vitl14.sh
CHANGED
|
@@ -9,9 +9,7 @@ export OMP_NUM_THREADS=1
|
|
| 9 |
# accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py
|
| 10 |
|
| 11 |
accelerate launch --config_file accelerate_config.yaml --num_processes 4 main.py \
|
|
|
|
| 12 |
ModelConfig.bottleneck_dim=64 \
|
| 13 |
-
ModelConfig.model_type=dinov2 \
|
| 14 |
ModelConfig.use_adapter=false \
|
| 15 |
-
DatasetConfig.train_transform_list=[resize_short_256,gaussian_blur,jpeg_compression,random_crop_224,random_horizontal_flip,to_dtype,normalize_dinov2] \
|
| 16 |
-
DatasetConfig.eval_transform_list=[resize_short_256,center_crop_224,to_dtype,normalize_dinov2] \
|
| 17 |
ModelConfig.backbone_name=dinov2_vitl14
|
|
|
|
| 9 |
# accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py
|
| 10 |
|
| 11 |
accelerate launch --config_file accelerate_config.yaml --num_processes 4 main.py \
|
| 12 |
+
model@ModelConfig=image/dinov2 \
|
| 13 |
ModelConfig.bottleneck_dim=64 \
|
|
|
|
| 14 |
ModelConfig.use_adapter=false \
|
|
|
|
|
|
|
| 15 |
ModelConfig.backbone_name=dinov2_vitl14
|
experiments/image/dinov3_vitl14.sh
CHANGED
|
@@ -9,9 +9,7 @@ export OMP_NUM_THREADS=1
|
|
| 9 |
# accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py
|
| 10 |
|
| 11 |
accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py \
|
|
|
|
| 12 |
ModelConfig.bottleneck_dim=64 \
|
| 13 |
-
ModelConfig.model_type=dinov3 \
|
| 14 |
ModelConfig.use_adapter=false \
|
| 15 |
-
DatasetConfig.train_transform_list=[resize_short_256,gaussian_blur,jpeg_compression,random_crop_256,random_horizontal_flip,to_dtype,normalize_dinov3] \
|
| 16 |
-
DatasetConfig.eval_transform_list=[resize_short_256,center_crop_256,to_dtype,normalize_dinov3] \
|
| 17 |
ModelConfig.backbone_name=dinov3_vitl16
|
|
|
|
| 9 |
# accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py
|
| 10 |
|
| 11 |
accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py \
|
| 12 |
+
model@ModelConfig=image/dinov3 \
|
| 13 |
ModelConfig.bottleneck_dim=64 \
|
|
|
|
| 14 |
ModelConfig.use_adapter=false \
|
|
|
|
|
|
|
| 15 |
ModelConfig.backbone_name=dinov3_vitl16
|
experiments/image/fat_former.sh
CHANGED
|
@@ -5,10 +5,6 @@ export TORCH_HOME=/nvmestore/ssalehi/.cache/torch
|
|
| 5 |
export CUDA_VISIBLE_DEVICES=1,2
|
| 6 |
export OMP_NUM_THREADS=1
|
| 7 |
|
| 8 |
-
# Official FatFormer (Michel-liu/FatFormer, utils/dataset.py) normalizes with **ImageNet**
|
| 9 |
-
# mean/std -- NOT CLIP stats -- despite the CLIP ViT-L/14 backbone. `normalize_dinov2` is the
|
| 10 |
-
# ImageNet Normalize in this repo, so we use it here to match the official preprocessing (and the
|
| 11 |
-
# deployed official-weights eval, which reproduces 98.39 acc / 99.70 mAP with ImageNet norm).
|
| 12 |
/home/ssalehi/miniconda3/bin/conda run --no-capture-output -n SynthScope accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py \
|
| 13 |
trainer@TrainerConfig=image/fat_former \
|
| 14 |
model@ModelConfig=image/fat_former \
|
|
@@ -16,8 +12,6 @@ export OMP_NUM_THREADS=1
|
|
| 16 |
DatasetConfig.train_batch_size=32 \
|
| 17 |
DatasetConfig.val_batch_size=32 \
|
| 18 |
DatasetConfig.test_batch_size=32 \
|
| 19 |
-
DatasetConfig.train_transform_list=[resize_short_256,gaussian_blur,jpeg_compression,random_crop_224,random_horizontal_flip,to_dtype,normalize_dinov2] \
|
| 20 |
-
DatasetConfig.eval_transform_list=[resize_short_256,center_crop_224,to_dtype,normalize_dinov2] \
|
| 21 |
logger.name=fat_former \
|
| 22 |
logger.mode=online \
|
| 23 |
logger.tags=[SynthScope,FatFormer,aigibench]
|
|
|
|
| 5 |
export CUDA_VISIBLE_DEVICES=1,2
|
| 6 |
export OMP_NUM_THREADS=1
|
| 7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
/home/ssalehi/miniconda3/bin/conda run --no-capture-output -n SynthScope accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py \
|
| 9 |
trainer@TrainerConfig=image/fat_former \
|
| 10 |
model@ModelConfig=image/fat_former \
|
|
|
|
| 12 |
DatasetConfig.train_batch_size=32 \
|
| 13 |
DatasetConfig.val_batch_size=32 \
|
| 14 |
DatasetConfig.test_batch_size=32 \
|
|
|
|
|
|
|
| 15 |
logger.name=fat_former \
|
| 16 |
logger.mode=online \
|
| 17 |
logger.tags=[SynthScope,FatFormer,aigibench]
|
experiments/image/npr.sh
CHANGED
|
@@ -25,8 +25,6 @@ SAVE=${SAVE:-/nvmestore/ssalehi/SynthScope/models/npr}
|
|
| 25 |
"+DatasetConfig.train_classes=[car,cat,chair,horse]" \
|
| 26 |
DatasetConfig.train_batch_size=32 \
|
| 27 |
DatasetConfig.val_batch_size=64 \
|
| 28 |
-
"DatasetConfig.train_transform_list=[resize_256,random_crop_224,random_horizontal_flip,to_dtype,normalize_dinov2]" \
|
| 29 |
-
"DatasetConfig.eval_transform_list=[resize_256,center_crop_224,to_dtype,normalize_dinov2]" \
|
| 30 |
TrainerConfig.save_path=$SAVE \
|
| 31 |
"metrics=[real_accuracy,fake_accuracy]" \
|
| 32 |
logger.name=npr \
|
|
|
|
| 25 |
"+DatasetConfig.train_classes=[car,cat,chair,horse]" \
|
| 26 |
DatasetConfig.train_batch_size=32 \
|
| 27 |
DatasetConfig.val_batch_size=64 \
|
|
|
|
|
|
|
| 28 |
TrainerConfig.save_path=$SAVE \
|
| 29 |
"metrics=[real_accuracy,fake_accuracy]" \
|
| 30 |
logger.name=npr \
|
experiments/image/pgc.sh
CHANGED
|
@@ -25,8 +25,6 @@ SAVE=${SAVE:-/nvmestore/ssalehi/SynthScope/models/pgc}
|
|
| 25 |
DatasetConfig.root_dir=$DATA \
|
| 26 |
DatasetConfig.train_batch_size=32 \
|
| 27 |
DatasetConfig.val_batch_size=64 \
|
| 28 |
-
"DatasetConfig.train_transform_list=[random_horizontal_flip,color_jitter_pgc,pad_random_crop_224,to_dtype,normalize_dinov2]" \
|
| 29 |
-
"DatasetConfig.eval_transform_list=[center_crop_224,to_dtype,normalize_dinov2]" \
|
| 30 |
TrainerConfig.save_path=$SAVE \
|
| 31 |
"metrics=[real_accuracy,fake_accuracy]" \
|
| 32 |
logger.name=pgc \
|
|
|
|
| 25 |
DatasetConfig.root_dir=$DATA \
|
| 26 |
DatasetConfig.train_batch_size=32 \
|
| 27 |
DatasetConfig.val_batch_size=64 \
|
|
|
|
|
|
|
| 28 |
TrainerConfig.save_path=$SAVE \
|
| 29 |
"metrics=[real_accuracy,fake_accuracy]" \
|
| 30 |
logger.name=pgc \
|
experiments/image/safe.sh
CHANGED
|
@@ -7,14 +7,12 @@ export OMP_NUM_THREADS=1
|
|
| 7 |
|
| 8 |
/home/ssalehi/miniconda3/bin/conda run --no-capture-output -n SynthScope accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py \
|
| 9 |
trainer@TrainerConfig=image/safe \
|
|
|
|
| 10 |
dataset@DatasetConfig=image/aigibench \
|
| 11 |
-
ModelConfig.model_type=safe \
|
| 12 |
ModelConfig.freeze_backbone=false \
|
| 13 |
DatasetConfig.train_batch_size=64 \
|
| 14 |
DatasetConfig.val_batch_size=64 \
|
| 15 |
DatasetConfig.test_batch_size=64 \
|
| 16 |
-
DatasetConfig.train_transform_list=[random_crop_256_pad,random_horizontal_flip,random_rotation_180,color_jitter_safe,to_dtype,random_mask_safe] \
|
| 17 |
-
DatasetConfig.eval_transform_list=[center_crop_256,to_dtype] \
|
| 18 |
TrainerConfig.epochs=100 \
|
| 19 |
TrainerConfig.lr=0.0005 \
|
| 20 |
TrainerConfig.weight_decay=0.05 \
|
|
|
|
| 7 |
|
| 8 |
/home/ssalehi/miniconda3/bin/conda run --no-capture-output -n SynthScope accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py \
|
| 9 |
trainer@TrainerConfig=image/safe \
|
| 10 |
+
model@ModelConfig=image/safe \
|
| 11 |
dataset@DatasetConfig=image/aigibench \
|
|
|
|
| 12 |
ModelConfig.freeze_backbone=false \
|
| 13 |
DatasetConfig.train_batch_size=64 \
|
| 14 |
DatasetConfig.val_batch_size=64 \
|
| 15 |
DatasetConfig.test_batch_size=64 \
|
|
|
|
|
|
|
| 16 |
TrainerConfig.epochs=100 \
|
| 17 |
TrainerConfig.lr=0.0005 \
|
| 18 |
TrainerConfig.weight_decay=0.05 \
|
experiments/image/spai.sh
CHANGED
|
@@ -5,15 +5,12 @@ export TORCH_HOME=/nvmestore/ssalehi/.cache/torch
|
|
| 5 |
export CUDA_VISIBLE_DEVICES=1,2
|
| 6 |
export OMP_NUM_THREADS=1
|
| 7 |
|
| 8 |
-
# SPAI expects [0,1] images (to_dtype only); ImageNet normalization is applied inside the model.
|
| 9 |
/home/ssalehi/miniconda3/bin/conda run --no-capture-output -n SynthScope accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py \
|
| 10 |
trainer@TrainerConfig=image/spai \
|
| 11 |
model@ModelConfig=image/spai \
|
| 12 |
DatasetConfig.train_batch_size=32 \
|
| 13 |
DatasetConfig.val_batch_size=32 \
|
| 14 |
DatasetConfig.test_batch_size=32 \
|
| 15 |
-
DatasetConfig.train_transform_list=[resize_short_256,gaussian_blur,jpeg_compression,random_crop_224,random_horizontal_flip,to_dtype] \
|
| 16 |
-
DatasetConfig.eval_transform_list=[resize_short_256,center_crop_224,to_dtype] \
|
| 17 |
logger.name=spai \
|
| 18 |
logger.mode=online \
|
| 19 |
logger.tags=[SynthScope,SPAI,genimage]
|
|
|
|
| 5 |
export CUDA_VISIBLE_DEVICES=1,2
|
| 6 |
export OMP_NUM_THREADS=1
|
| 7 |
|
|
|
|
| 8 |
/home/ssalehi/miniconda3/bin/conda run --no-capture-output -n SynthScope accelerate launch --config_file accelerate_config.yaml --num_processes 2 main.py \
|
| 9 |
trainer@TrainerConfig=image/spai \
|
| 10 |
model@ModelConfig=image/spai \
|
| 11 |
DatasetConfig.train_batch_size=32 \
|
| 12 |
DatasetConfig.val_batch_size=32 \
|
| 13 |
DatasetConfig.test_batch_size=32 \
|
|
|
|
|
|
|
| 14 |
logger.name=spai \
|
| 15 |
logger.mode=online \
|
| 16 |
logger.tags=[SynthScope,SPAI,genimage]
|
experiments/image/vib_net.sh
CHANGED
|
@@ -31,8 +31,6 @@ SAVE=${SAVE:-/nvmestore/ssalehi/SynthScope/models/vib_net}
|
|
| 31 |
DatasetConfig.prefetch=false \
|
| 32 |
DatasetConfig.train_batch_size=256 \
|
| 33 |
DatasetConfig.val_batch_size=256 \
|
| 34 |
-
"DatasetConfig.train_transform_list=[blur_jpeg_cnndet,pad_random_crop_224,random_horizontal_flip,to_dtype,normalize_clip]" \
|
| 35 |
-
"DatasetConfig.eval_transform_list=[blur_jpeg_cnndet_val,center_crop_224,to_dtype,normalize_clip]" \
|
| 36 |
TrainerConfig.save_path=$SAVE \
|
| 37 |
"metrics=[real_accuracy,fake_accuracy]" \
|
| 38 |
logger.name=vib_net \
|
|
|
|
| 31 |
DatasetConfig.prefetch=false \
|
| 32 |
DatasetConfig.train_batch_size=256 \
|
| 33 |
DatasetConfig.val_batch_size=256 \
|
|
|
|
|
|
|
| 34 |
TrainerConfig.save_path=$SAVE \
|
| 35 |
"metrics=[real_accuracy,fake_accuracy]" \
|
| 36 |
logger.name=vib_net \
|
experiments/video/d3.sh
CHANGED
|
@@ -8,7 +8,7 @@ export OMP_NUM_THREADS=1
|
|
| 8 |
# D3 is training-free: a frozen CLIP ViT-B/16 extracts per-frame features, and the per-video
|
| 9 |
# second-order std becomes the detection score. Frames are decoded to 224x224 in [0,1] and
|
| 10 |
# normalized by the dataset transform pipeline (normalize_clip here, matched to the CLIP
|
| 11 |
-
# backbone -- switch to
|
| 12 |
# meaningful metrics. The video benchmark must be present under DatasetConfig.root_dir;
|
| 13 |
# this is a template (data download is out of scope).
|
| 14 |
/home/ssalehi/miniconda3/bin/conda run --no-capture-output -n SynthScope accelerate launch --config_file accelerate_config.yaml --num_processes 1 main.py \
|
|
|
|
| 8 |
# D3 is training-free: a frozen CLIP ViT-B/16 extracts per-frame features, and the per-video
|
| 9 |
# second-order std becomes the detection score. Frames are decoded to 224x224 in [0,1] and
|
| 10 |
# normalized by the dataset transform pipeline (normalize_clip here, matched to the CLIP
|
| 11 |
+
# backbone -- switch to normalize_imagenet if you set a DINOv2/ResNet backbone). AP/AUROC are the
|
| 12 |
# meaningful metrics. The video benchmark must be present under DatasetConfig.root_dir;
|
| 13 |
# this is a template (data download is out of scope).
|
| 14 |
/home/ssalehi/miniconda3/bin/conda run --no-capture-output -n SynthScope accelerate launch --config_file accelerate_config.yaml --num_processes 1 main.py \
|
experiments/video/restrav.sh
CHANGED
|
@@ -10,7 +10,7 @@ export OMP_NUM_THREADS=1
|
|
| 10 |
# 224x224 over a CENTER 2 s window (24 frames, BICUBIC) and fed to DINOv2 as RAW [0,1] with
|
| 11 |
# *no* ImageNet normalization -- this matches the official ReStraV repo verbatim
|
| 12 |
# (dinov2_features.preprocess = frames/255 + BICUBIC resize, no Normalize) and the deployed
|
| 13 |
-
# serving spec (registry video_norm = identity). Do NOT add
|
| 14 |
# detector is served raw [0,1], so normalizing only at train time creates a train/serve
|
| 15 |
# mismatch that collapses cross-generator transfer. Official optimizer is plain Adam (no
|
| 16 |
# weight decay), 10 epochs -- matched below. The video benchmark must be present under
|
|
|
|
| 10 |
# 224x224 over a CENTER 2 s window (24 frames, BICUBIC) and fed to DINOv2 as RAW [0,1] with
|
| 11 |
# *no* ImageNet normalization -- this matches the official ReStraV repo verbatim
|
| 12 |
# (dinov2_features.preprocess = frames/255 + BICUBIC resize, no Normalize) and the deployed
|
| 13 |
+
# serving spec (registry video_norm = identity). Do NOT add normalize_imagenet here: the
|
| 14 |
# detector is served raw [0,1], so normalizing only at train time creates a train/serve
|
| 15 |
# mismatch that collapses cross-generator transfer. Official optimizer is plain Adam (no
|
| 16 |
# weight decay), 10 epochs -- matched below. The video benchmark must be present under
|
experiments/video/stall.sh
CHANGED
|
@@ -8,7 +8,7 @@ export OMP_NUM_THREADS=1
|
|
| 8 |
# STALL (Ben Hayun et al., CVPR'26) is training-free: a frozen DINOv3 ViT-L/16 extracts
|
| 9 |
# per-frame features, which are scored against a VATEX real-video calibration via two whitened
|
| 10 |
# isotropic-Gaussian likelihood branches (spatial + temporal). Frames are decoded square at 224
|
| 11 |
-
# in [0,1] and normalized with ImageNet stats --
|
| 12 |
# DINOv3 transform uses ImageNet mean/std (NOT normalize_dinov3). AP/AUROC are the meaningful
|
| 13 |
# metrics. The video benchmark must be present under DatasetConfig.root_dir.
|
| 14 |
#
|
|
@@ -24,8 +24,8 @@ export DINO_V3_WEIGHTS=${DINO_V3_WEIGHTS:-/hddstore/ssalehi/SynthScope/dinov3_ch
|
|
| 24 |
DatasetConfig.test_batch_size=8 \
|
| 25 |
DatasetConfig.num_frames=16 \
|
| 26 |
DatasetConfig.frame_size=224 \
|
| 27 |
-
DatasetConfig.train_transform_list=[
|
| 28 |
-
DatasetConfig.eval_transform_list=[
|
| 29 |
metrics=[real_accuracy,fake_accuracy,AP,AUC] \
|
| 30 |
logger.name=stall \
|
| 31 |
logger.mode=online \
|
|
|
|
| 8 |
# STALL (Ben Hayun et al., CVPR'26) is training-free: a frozen DINOv3 ViT-L/16 extracts
|
| 9 |
# per-frame features, which are scored against a VATEX real-video calibration via two whitened
|
| 10 |
# isotropic-Gaussian likelihood branches (spatial + temporal). Frames are decoded square at 224
|
| 11 |
+
# in [0,1] and normalized with ImageNet stats -- normalize_imagenet here, because the official
|
| 12 |
# DINOv3 transform uses ImageNet mean/std (NOT normalize_dinov3). AP/AUROC are the meaningful
|
| 13 |
# metrics. The video benchmark must be present under DatasetConfig.root_dir.
|
| 14 |
#
|
|
|
|
| 24 |
DatasetConfig.test_batch_size=8 \
|
| 25 |
DatasetConfig.num_frames=16 \
|
| 26 |
DatasetConfig.frame_size=224 \
|
| 27 |
+
DatasetConfig.train_transform_list=[normalize_imagenet] \
|
| 28 |
+
DatasetConfig.eval_transform_list=[normalize_imagenet] \
|
| 29 |
metrics=[real_accuracy,fake_accuracy,AP,AUC] \
|
| 30 |
logger.name=stall \
|
| 31 |
logger.mode=online \
|
interface/backend/engine.py
CHANGED
|
@@ -13,7 +13,6 @@ from collections import OrderedDict
|
|
| 13 |
|
| 14 |
import numpy as np
|
| 15 |
import torch
|
| 16 |
-
import torchvision.transforms as T
|
| 17 |
from PIL import Image
|
| 18 |
from safetensors.torch import load_file as load_safetensors
|
| 19 |
|
|
@@ -61,14 +60,12 @@ def _overlay_to_png_b64(sal: torch.Tensor, base: Image.Image, out_size: int = 28
|
|
| 61 |
|
| 62 |
|
| 63 |
def _geometric_base(spec, pil: Image.Image) -> Image.Image:
|
| 64 |
-
"""The model's input region in RGB (apply only the
|
| 65 |
-
transform, skipping
|
| 66 |
transforms (AIDE/SPAI native-res) fall back to the native image."""
|
| 67 |
-
import torchvision.transforms as T
|
| 68 |
base = pil.convert("RGB")
|
| 69 |
-
for t in
|
| 70 |
-
|
| 71 |
-
base = t(base)
|
| 72 |
return base
|
| 73 |
|
| 74 |
|
|
@@ -329,7 +326,10 @@ class Engine:
|
|
| 329 |
elif kind == "aide_dct": # AIDE: native-res DCT per-window energy (patch selection)
|
| 330 |
if pil is None:
|
| 331 |
return None
|
| 332 |
-
|
|
|
|
|
|
|
|
|
|
| 333 |
elif kind == "spai_patch": # SPAI: per-patch spectral fake-evidence over the 224-grid
|
| 334 |
sal = model.patch_fake_map(x) # [nH,nW] or None (five-crop/large grid)
|
| 335 |
if sal is None:
|
|
@@ -351,7 +351,7 @@ class Engine:
|
|
| 351 |
t = x[0].detach().float().cpu()
|
| 352 |
if t.dim() == 4:
|
| 353 |
t = torch.cat(list(t), dim=2)
|
| 354 |
-
norm =
|
| 355 |
if norm is not None:
|
| 356 |
t = t * torch.tensor(norm.std)[:, None, None] + torch.tensor(norm.mean)[:, None, None]
|
| 357 |
elif spec.key == "cure": # CuRe maps pixels to (x - 127.5) / 127.5
|
|
|
|
| 13 |
|
| 14 |
import numpy as np
|
| 15 |
import torch
|
|
|
|
| 16 |
from PIL import Image
|
| 17 |
from safetensors.torch import load_file as load_safetensors
|
| 18 |
|
|
|
|
| 60 |
|
| 61 |
|
| 62 |
def _geometric_base(spec, pil: Image.Image) -> Image.Image:
|
| 63 |
+
"""The model's input region in RGB (apply only the resize/center-crop steps of the eval
|
| 64 |
+
transform, skipping to_dtype/normalize) so the overlay aligns with the saliency. Custom
|
| 65 |
transforms (AIDE/SPAI native-res) fall back to the native image."""
|
|
|
|
| 66 |
base = pil.convert("RGB")
|
| 67 |
+
for t in spec.transform.geometric_steps():
|
| 68 |
+
base = t(base)
|
|
|
|
| 69 |
return base
|
| 70 |
|
| 71 |
|
|
|
|
| 326 |
elif kind == "aide_dct": # AIDE: native-res DCT per-window energy (patch selection)
|
| 327 |
if pil is None:
|
| 328 |
return None
|
| 329 |
+
five_view = next((t for t in spec.transform.transforms if hasattr(t, "dct_energy_map")), None)
|
| 330 |
+
if five_view is None:
|
| 331 |
+
return None
|
| 332 |
+
sal = five_view.dct_energy_map(pil) # [nH,Wn]
|
| 333 |
elif kind == "spai_patch": # SPAI: per-patch spectral fake-evidence over the 224-grid
|
| 334 |
sal = model.patch_fake_map(x) # [nH,nW] or None (five-crop/large grid)
|
| 335 |
if sal is None:
|
|
|
|
| 351 |
t = x[0].detach().float().cpu()
|
| 352 |
if t.dim() == 4:
|
| 353 |
t = torch.cat(list(t), dim=2)
|
| 354 |
+
norm = spec.transform.normalize_step()
|
| 355 |
if norm is not None:
|
| 356 |
t = t * torch.tensor(norm.std)[:, None, None] + torch.tensor(norm.mean)[:, None, None]
|
| 357 |
elif spec.key == "cure": # CuRe maps pixels to (x - 127.5) / 127.5
|