Looking for AI-edit detection and tamper heatmaps? See Car Damage Photo Integrity v9 for image forgery localization in car-damage photos.
This repository retains its original checkpoint and inference instructions.
Car Damage Photo Integrity Detector
Research model and evaluation reports for checking whether car / automobile damage photos are real camera photos or AI-generated / AI-edited (localized inpainting that adds, removes, or exaggerates vehicle damage).
The repo id stays obuladinnesai/claim-photo-integrity-detector-v5 for continuity; the public focus is car damage photos.
v9 now has its own model page: obuladinnesai/car-damage-photo-integrity-v9 (same v9 weights as the
v9/folder here, with its own model card, code and report).
What's in this repo
- v9 weights (localization-first model):
v9/model.safetensors+v9/config.json. See v9 weights. - Code for v6, v7, v8 and v9 training / evaluation:
code/. See Code. - v5 weights (ViT image classifier, the original release): the root files
model.safetensors,config.json,preprocessor_config.json,training_args.bin. The rootmodel.safetensorsis v5, not v9. - v9 results (Oct 6, 2026): public eval report
reports/report_v7_vs_v9.html. - Earlier report comparing v5 and v6:
reports/report_v5_vs_v6.html.
v6, v7 and v8 weights are not released.
v9 results (Oct 6, 2026)
What "2% false alarms" means
Each model's alarm level is set so that only 2% of genuine photos are wrongly flagged: 12 of the 600 real car-damage test photos, or about 2 of every 100 real photos. The catch rate is the share of edited photos the model flags at that same level. So "v9 catches 85.3%" means it flags about 85 of every 100 edited photos while wrongly flagging about 2 of every 100 genuine ones.
Headline (catch rate at 2% false alarms on real photos)
| Model | Photo condition | Catch rate @ 2% false alarms | AUROC |
|---|---|---|---|
| v9 | All photos re-saved as JPEG (quality 90) | 85.3% | 0.948 |
| v9 | Upload setting (max side 1600 px + JPEG quality 85) | 81.6% | 0.933 |
| v7 | Original files | 56.0% | 0.888 |
| v7 | Upload setting | 52.9% | 0.870 |
| v6 | Original files | 43.5% | 0.862 |
| v6 | Upload setting | 32.8% | 0.842 |
Each row's threshold is set on the 600 real photos in that same condition (threshold = 12th-highest of the 600 real scores). v9 numbers use re-compressed photos so that edited and real photos are compared in the same file format.
By edit size (upload setting, catch rate @ 2% false alarms)
| Edit size (share of photo edited) | v6 | v7 | v9 |
|---|---|---|---|
| Large (12–35%), n=384 | 54.9% | 84.1% | 97.1% |
| Medium (3–12%), n=464 | 29.1% | 43.8% | 90.3% |
| Small (0.5–3%), n=292 | 5.5% | 10.6% | 61.3% |
The 545 edited photos v7 missed
| Detector | Caught (of 545) @ 2% false alarms |
|---|---|
| v9 (photos re-saved as JPEG q90) | 77.6% (423) |
| v9 (upload setting) | 71.4% (389) |
| TruFor (pretrained, public) | 4.2% |
| RADAR (pretrained, public) | 3.5% |
Method summary
- Localization-first model: a ConvNeXt-Tiny encoder plus a noise-residual stream predicts a pixel map of likely edited regions. The image score is the mean of the 256 strongest map values (the photo's most suspicious area).
- Training data: pixel masks of 3,298 diffusion and LaMa inpaints made on CarDD training photos, plus synthetic tiny paste-back edits. Trained on a 24 GB MacBook Pro.
- Evaluation set (eval_v6): 600 real CarDD val/test photos + 1,240 edited or generated photos (Stable Diffusion 1.5, SDXL, Kandinsky and LaMa inpaints, including context-window and paste-back edits; 100 SDXL text-to-image images). No source photos are shared between training and test.
Limitations
- Not yet at 90% catch at 2% false alarms.
- Small edits are the weakest case (61.3% in the upload setting).
- Fully AI-generated (text-to-image) scenes are a weak spot for v9 (41% in the upload setting, 45% with JPEG q90, vs 99% for v7); v9 is built to find local edits, so v7 is the better detector for fully generated scenes.
- Tested on these four inpainting tools (plus SDXL text-to-image) only; newer generators may behave differently.
- A triage signal for human review, not proof of editing, fraud, or authenticity.
v9 weights
v9/model.safetensors holds the weights only (no optimizer state), taken from training step 16,000 (the best.pt checkpoint), about 29.4M parameters.
v9/config.json describes the architecture, input handling, top-256 scoring rule, and the 2%-false-alarm thresholds for each photo condition.
The model needs the LocNet class from code/v9/model_v9.py, so it does not load with transformers.pipeline.
pip install torch timm safetensors huggingface_hub pillow numpy
git clone https://huggingface.co/obuladinnesai/claim-photo-integrity-detector-v5 && cd claim-photo-integrity-detector-v5/code/v9
python inference.py /path/to/car_damage_photo.jpg --heatmap heat.png
# from code/v9
from inference import load_model, score_image
net, cfg, dev = load_model() # downloads v9/model.safetensors + v9/config.json from this repo
r = score_image(net, cfg, dev, "car_damage_photo.jpg", condition="jpeg_q90")
print(r["score"], r["threshold"], r["flagged"]) # r["heatmap"]: H x W edit-probability map in [0, 1]
- Score is the mean of the 256 strongest stride-2 logits in the edit map (higher means more likely edited). The image is scored at full resolution in one pass.
- Thresholds (2% false alarms on the 600 real eval photos):
jpeg_q90is 4.191 (photo re-saved as JPEG q90 first; 85.3% catch) anduploadis 4.193 (max side 1600 + JPEG q85; 81.6% catch).score_imageapplies the matching preprocessing for the chosen condition. Anativethreshold is also in the config, but native results are not a fair comparison (see above). - The inference script reproduces the evaluation scores exactly (checked on eval images in all three conditions).
Code
code/ has the training and evaluation code for each version, each folder with its own README:
| Folder | Contents | Result (catch @ 2% false alarms) |
|---|---|---|
code/eval_v6 | Builds the shared test set (600 real CarDD val/test photos + 1,240 edited/generated) | n/a |
code/v6 | ConvNeXt-Tiny tile classifier | 43.5% |
code/v7 | Harder inpaints + multi-scale crops | 56.0% (original files) |
code/v8 | Miss-focused fine-tune of v7 (failed, not promoted) | 27.2% of v7's 545 misses |
code/v9 | Localization-first model + inference.py | 85.3% (JPEG q90), 81.6% (upload) |
All scripts read and write data under $PROJECT_ROOT; see code/README.md and code/requirements.txt.
Datasets, generated images, masks, logs and third-party repos (TruFor, RADAR) are not included.
Earlier results (v5 vs v6)
On a held-out automobile-damage test set (600 real CarDD photos, 1,140 AI-edited damage photos, 100 fully synthetic damaged-car scenes):
| Model | AUROC (native) | Catch rate @ ~2% false alarms |
|---|---|---|
| v5 (weights in this repo) | 0.51 (≈ chance on inpaints) | ~9% |
| v6 (not released) | 0.86 | ~45% |
Usage (v5 checkpoint, root files)
from transformers import pipeline
pipe = pipeline(
"image-classification",
model="obuladinnesai/claim-photo-integrity-detector-v5",
)
pipe("path/to/car_damage_photo.jpg")
The v5 checkpoint is close to chance on localized inpaints (see above); use it for research only.
General limitations
- Accuracy degrades on generators and edit types newer than the training data.
- Check dataset licenses before commercial use.