AI 이미지 포렌식
언제 사용하나요?
사용자가 "이 사진이 AI로 생성된 건가요? 혹은 편집된 건가요? 아니면 남이 쓴 사진인가요?"라고 묻거나, 대량의 이미지(배송 확인 사진, 리뷰 이미지, 콘텐츠 이미지)를 검증해야 할 때 사용합니다.
한 줄 명령어
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의eXIf청크 내 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장 판별). 새로운 생성기에는 완전히 무력함 (동일한 사람이 2026년 생성 이미지 7장 테스트, 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 5분류 (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에서 70MB 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 식별자 | 존재하면 적중 | 표준 필드, 임계값 불필요 |
세 가지 경계 조건 (사용자에게 반드시 알려야 함)
- 식별자가 없다고 해서 진짜 사진은 아님. 한 번만 다시 저장해도 제거되며, 클립보드, 위챗 전달, 스크린샷 모두 제거됩니다.
- 픽셀 모델만으로 유죄를 입증할 수 없음. 본 적 없는 생성기로 전환하면 결과가 0으로 떨어지며, 진짜 사진을 오판하는 경우도 흔함 (실제 테스트: 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에 있어 국내에서 다운로드 불가.