AI 圖片取證
什麼時候用
用戶問「這張圖是不是 AI 生成的 / 是不是 P 的 / 是不是別人用過的圖」,或者要批量審核一堆圖(簽收照、評價圖、素材圖)時用。
一條命令
python3 scripts/probe.py 圖片路徑... # 只跑元數據層
python3 scripts/probe.py --dir 目錄 --lib 圖庫目錄 # 加查重
python3 scripts/probe.py --dir 目錄 --model <權重目錄> # 加像素層,可選
輸出四檔結論,不要把 NO-EVIDENCE 說成「真圖」:
| 結論 | 含義 | 能做什麼 |
|---|---|---|
AI-SIGNED | 有 AIGC 國標標識或 C2PA 憑證 | 可以直接採信,這是生成方自證 |
AI-LIKELY | 有生成器指紋,或像素模型給高分 | 只當線索,需要人工複核 |
DUPLICATE | 與圖庫中某張 pHash 距離 ≤10 | 可以直接判重 |
NO-EVIDENCE | 三層都沒找到 | 不能說真,只能說「沒證據」 |
三層的實際能力(本地實測,不要憑印象許諾)
第一層 元數據——最準,也最便宜(毫秒級,純 Python)。
- 國內合規生成的圖會帶 TC260 AIGC 隱式標識:
{"AIGC":{"Label":"1","ContentProducer":...,"ProduceID":...,"ReservedCode1":"...sm3...sm2..."}}。常藏在 PNG 的eXIfchunk 的 UserComment(0x9286)裡,不在 tEXt 裡,所以只掃文字塊的工具會漏。 - 但重新儲存一次就沒了:Pillow 另存 JPG 或 PNG,欄位都消失。所以這一層只能抓「原圖和原樣轉發的圖」,抓不到「截過圖、修過圖」的。
- 相機的
Make/Model/DateTimeOriginal在,說明是相機直接出的;全空通常是社交平台洗過或截圖。
第二層 查重——pHash + dHash,20 毫秒一張。
- 平台壓縮、縮放、截圖:距離 0~2,幾乎無感。
- 裁掉 7.5% 邊緣:距離 8~24,會漏一部分。
- 不同圖之間最小距離 24,所以閾值定 10 是安全的。
第三層 像素模型——慢、且不穩定,只在必要時開。
- Community-Forensics ViT-S/384(CVPR 2025):CPU 單張 0.2
0.6 秒。對訓練分佈內的老生成器很靈(DALL·E 2 的 5 張官方測試圖 5/5 判出),對新生成器完全失效(同一人測 7 張 2026 年的生成圖,7 張全判成真,置信度 0.00000.0019)。 - 所以像素層的可信範圍是「命中才有意義,沒命中什麼都說明不了」。
- 權重不在倉庫裡,要先下
OwensLab/commfor-model-384(HF,87MB)放到models/commfor384/。
三個模型的分工(別用錯)
| 腳本 | 模型 | 該餵什麼 | 絕不能餵什麼 |
|---|---|---|---|
scripts/run_commfor.py | ViT-S/16 384(CVPR'25 通用生成圖判別) | 通用生成圖,預設 --size 384 | 別用 224 版權重,實測同一批圖只有 3/5 |
scripts/run_raiw.py | RAIW source-classify(10 廠商 + unknown) | 想知道「是哪家生成的」 | 別當判別器,實測 17/17 全棄權 |
scripts/run_xai.py | EfficientNetV2-B0 五分類(FF++ 換臉) | 只餵人臉圖 | 商品圖/簽收圖,實測 10 張真圖誤判 5 張 |
python3 scripts/run_commfor.py 圖.jpg --size 384 --weights /path/model.safetensors
python3 scripts/run_raiw.py 圖.jpg --weights /path/multi-provider-source-v3.npz
python3 scripts/run_xai.py 人臉.jpg --weights /path/ff_attribution.ckpt
三個腳本都支援 --weights 指定權重、--out 落盤(不給就只列印)。權重查找順序統一為:
--weights > 環境變數 > scripts/../models/<模型名>/。權重不在倉庫裡,得自己下:
| 模型 | 來源 | 大小 |
|---|---|---|
| commfor384 | HF OwensLab/commfor-model-384 | 87 MB |
| commfor224 | HF OwensLab/commfor-model-224 | 86 MB |
| RAIW | HF wiltodelta/raiw-source-classify | 2.03 GB |
| XAI-Deepfakes | 倉庫 model/checkpoint/ff_attribution.ckpt | 70 MB |
國內下載加鏡像:HF_ENDPOINT=https://hf-mirror.com;大檔案用 aria2c -x16 -s16。
別讓用戶自己拼下載命令,有現成腳本(預設走 hf-mirror、aria2c 多執行緒、下完核體積和 SHA256):
python3 scripts/fetch_weights.py --list # 清單 + 當前狀態
python3 scripts/fetch_weights.py commfor384 # 87 MB,只跑像素層下這個就夠
python3 scripts/fetch_weights.py --all --jobs 16 # 四個全下,合計 2.2 GB
python3 scripts/fetch_weights.py --all --check # 只校驗已有檔案
腳本支援斷點續傳;被代理截斷的檔案會被體積 + SHA256 攔下來(本機真踩過:GitHub
tarball 少了 70 MB 的 ckpt,解壓才報 failed finding central directory)。
下完落在 models/<模型名>/,正好是三個 run_*.py 的預設查找路徑,不用再傳 --weights。
下載不成就直說卡在哪一步,不要用「理論上可以」替代實測結果。
輸出長什麼樣(照這個形狀回答)
probe.py 的真實輸出,三種檔位各一例:
samples/ai/手機拍的快遞驛站貨架.png -> AI-SIGNED
理由:檔案裡有 AIGC 合規標識(Label=1),這是生成方的自證
元數據:aigc_label | 格式 PNG 1024x1536
samples/variants/real01__p75.jpg -> DUPLICATE
理由:與 real01.jpg 的感知哈希距離 0,閾值 10
元數據:無正向標記 | 格式 JPEG 1280x853
重複:最近 real01.jpg 距離 0 ≤10 判重複
samples/real/real02.jpg -> NO-EVIDENCE
理由:三層都沒找到標識;這不等於真圖,需要業務側另找證據
元數據:無正向標記 | 格式 JPEG 4500x3000
向用戶轉述時:結論檔位大寫原樣給,不要翻譯成「疑似 AI」這類模糊說法;理由那行不要
改寫,它是判定的依據;NO-EVIDENCE 後面緊跟一句「不等於真圖」,別讓用戶誤會。
閾值速查
| 判據 | 閾值 | 依據 |
|---|---|---|
| pHash 判重 | ≤10 | 跨圖最小距離 24,留足餘量 |
| 像素 p_fake | >0.6 才算線索 | 0.1~0.6 區間基本是雜訊 |
| AIGC 標識 | 存在即命中 | 標準欄位,不設閾值 |
三條邊界(必須告訴用戶)
- 沒有標識 ≠ 真圖。 加一道另存就洗掉了,剪貼簿、微信轉發、截圖全都會洗。
- 像素模型不能單獨定罪。 換個沒見過的生成器就歸零,誤判真圖也常見(實測 iPhone 拍的紙箱照被 384 版判成 0.896)。
- 要可追溯就得用業務手段:上傳時存原圖位元組哈希、記錄裝置指紋、比對同一訂單的歷史圖,比任何檢測模型都可靠。
反面教材
image-provenance(客戶端網頁工具):介面漂亮、頻域分析完整,但源碼裡沒有任何 AIGC/TC260 關鍵詞,對中國合規標識完全無感;且元數據解析依賴 CDN 載入 exifr,離線就退化為位元組啟發式。實測一張帶完整 AIGC 標識的圖,它顯示「未發現 AI 來源憑證」。XAI-Deepfakes:換臉(FaceForensics++)多分類器,只認人臉。餵快遞簽收照會讓 10 張真圖裡 5 張被判成 Face2Face/DeepFakes/NeuralTextures。LAID:架構很輕(ShuffleNet 0.34M 參數、CPU 20ms),但權重在 Google Drive,國內下不到。