Análisis Forense de Imágenes con IA
Cuándo usarlo
Cuando el usuario pregunte «¿Esta imagen es generada por IA / está editada / es una imagen ya usada por otros?», o cuando necesite revisar un lote de imágenes (fotos de confirmación, imágenes de reseñas, imágenes de activos).
Un solo comando
python3 scripts/probe.py ruta_de_imagen... # Solo capa de metadatos
python3 scripts/probe.py --dir directorio --lib directorio_de_galeria # Con detección de duplicados
python3 scripts/probe.py --dir directorio --model <directorio_de_pesos> # Con capa de píxeles, opcional
Salida con cuatro niveles de conclusión, NO digas que NO-EVIDENCE significa 'imagen real':
| Conclusión | Significado | Qué se puede hacer |
|---|---|---|
AI-SIGNED | Tiene marca AIGC nacional o credencial C2PA | Se puede aceptar directamente, es una autodeclaración del generador |
AI-LIKELY | Tiene huella del generador, o el modelo de píxeles da una puntuación alta | Solo como pista, requiere revisión manual |
DUPLICATE | Distancia pHash con alguna imagen de la galería ≤10 | Se puede declarar duplicado directamente |
NO-EVIDENCE | No se encontró nada en las tres capas | No se puede decir que es real, solo 'no hay evidencia' |
Capacidad real de las tres capas (basada en pruebas locales, no prometas basándote en impresiones)
Primera capa: Metadatos — la más precisa y la más barata (milisegundos, Python puro).
- Las imágenes generadas de forma conforme en China suelen llevar la marca implícita AIGC TC260:
{"AIGC":{"Label":"1","ContentProducer":...,"ProduceID":...,"ReservedCode1":"...sm3...sm2..."}}. A menudo se oculta en el chunkeXIfde PNG, en UserComment (0x9286), no en tEXt, por lo que las herramientas que solo escanean bloques de texto la pasan por alto. - Pero se pierde al guardar de nuevo: si Pillow guarda como JPG o PNG, los campos desaparecen. Por lo tanto, esta capa solo captura imágenes originales y las reenviadas sin modificar, no las que han sido recortadas o editadas.
- Si están presentes
Make/Model/DateTimeOriginalde la cámara, indica que la imagen salió directamente de la cámara; si están todos vacíos, suele ser porque la plataforma social la ha lavado o es una captura de pantalla.
Segunda capa: Detección de duplicados — pHash + dHash, 20 milisegundos por imagen.
- Compresión, escalado y capturas de pantalla de la plataforma: distancia 0~2, casi imperceptible.
- Recortar 7.5% del borde: distancia 8~24, se pierde una parte.
- La distancia mínima entre imágenes diferentes es 24, por lo que establecer el umbral en 10 es seguro.
Tercera capa: Modelo de píxeles — lento e inestable, solo encenderlo cuando sea necesario.
- Community-Forensics ViT-S/384 (CVPR 2025): CPU 0.2
0.6 segundos por imagen. Muy efectivo para generadores antiguos dentro de la distribución de entrenamiento (5/5 imágenes de prueba oficiales de DALL·E 2), completamente ineficaz para generadores nuevos (7 imágenes de prueba de 2026 del mismo usuario, las 7 clasificadas como reales, con confianza 0.00000.0019). - Por lo tanto, el rango de confiabilidad de la capa de píxeles es: 'solo significativo cuando acierta, no dice nada cuando falla'.
- Los pesos no están en el repositorio, primero hay que descargar
OwensLab/commfor-model-384(HF, 87 MB) y colocarlos enmodels/commfor384/.
División del trabajo de los tres modelos (no los uses incorrectamente)
| Script | Modelo | Qué alimentar | Qué NO alimentar |
|---|---|---|---|
scripts/run_commfor.py | ViT-S/16 384 (CVPR'25 discriminación de imágenes generadas genéricas) | Imágenes generadas genéricas, por defecto --size 384 | No uses pesos de 224, en pruebas solo 3/5 de las mismas imágenes |
scripts/run_raiw.py | RAIW source-classify (10 fabricantes + unknown) | Para saber 'quién lo generó' | No lo uses como discriminador, en pruebas 17/17 se abstuvieron |
scripts/run_xai.py | EfficientNetV2-B0 clasificación en 5 clases (FF++ caras intercambiadas) | Solo imágenes de rostros | Imágenes de productos/confirmación, en pruebas 5/10 imágenes reales clasificadas erróneamente |
python3 scripts/run_commfor.py imagen.jpg --size 384 --weights /ruta/model.safetensors
python3 scripts/run_raiw.py imagen.jpg --weights /ruta/multi-provider-source-v3.npz
python3 scripts/run_xai.py rostro.jpg --weights /ruta/ff_attribution.ckpt
Los tres scripts admiten --weights para especificar pesos, --out para guardar en disco (si no se proporciona, solo imprime). El orden de búsqueda de pesos es uniforme:
--weights > variable de entorno > scripts/../models/<nombre_del_modelo>/. Los pesos no están en el repositorio, hay que descargarlos:
| Modelo | Fuente | Tamaño |
|---|---|---|
| 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 | Repositorio model/checkpoint/ff_attribution.ckpt | 70 MB |
Para descargar desde China, añade espejo: HF_ENDPOINT=https://hf-mirror.com; para archivos grandes, usa aria2c -x16 -s16.
No le hagas al usuario construir el comando de descarga manualmente, hay un script listo (por defecto usa hf-mirror, aria2c multihilo, verifica volumen y SHA256 al finalizar):
python3 scripts/fetch_weights.py --list # Lista + estado actual
python3 scripts/fetch_weights.py commfor384 # 87 MB, solo para la capa de píxeles, esto es suficiente
python3 scripts/fetch_weights.py --all --jobs 16 # Los cuatro, total 2.2 GB
python3 scripts/fetch_weights.py --all --check # Solo verifica archivos existentes
El script soporta reanudación de descargas interrumpidas; los archivos truncados por proxies serán interceptados por volumen + SHA256 (se probó localmente: un tarball de GitHub faltaba 70 MB de ckpt, y al descomprimir fallaba con failed finding central directory).
Una vez descargados, se colocan en models/<nombre_del_modelo>/, que es la ruta de búsqueda predeterminada de los tres run_*.py, no es necesario pasar --weights.
Si la descarga falla, di exactamente en qué paso falló, no uses 'teóricamente funciona' para reemplazar resultados de pruebas.
Cómo se ve la salida (responde con esta forma)
Salida real de probe.py, un ejemplo para cada uno de los tres niveles:
samples/ai/foto_de_estanteria_de_paqueteria_tomada_con_movil.png -> AI-SIGNED
Razón: El archivo contiene una marca AIGC conforme (Label=1), es una autodeclaración del generador
Metadatos: aigc_label | Formato PNG 1024x1536
samples/variants/real01__p75.jpg -> DUPLICATE
Razón: Distancia de hash perceptual con real01.jpg = 0, umbral 10
Metadatos: Sin marca positiva | Formato JPEG 1280x853
Duplicado: Más cercano real01.jpg distancia 0 ≤10 se declara duplicado
samples/real/real02.jpg -> NO-EVIDENCE
Razón: No se encontraron marcas en las tres capas; esto no equivale a imagen real, el negocio debe buscar otras pruebas
Metadatos: Sin marca positiva | Formato JPEG 4500x3000
Al informar al usuario: el nivel de conclusión en mayúsculas tal cual, no lo traduzcas a 'posible IA' o frases vagas; la línea de 'Razón' no la modifiques, es la base del juicio; después de NO-EVIDENCE, añade inmediatamente 'no equivale a imagen real', para que el usuario no malinterprete.
Referencia rápida de umbrales
| Criterio | Umbral | Fundamento |
|---|---|---|
| pHash duplicación | ≤10 | Distancia mínima entre imágenes diferentes 24, margen suficiente |
| p_fake de píxeles | >0.6 solo es pista | Intervalo 0.1~0.6 es básicamente ruido |
| Marca AIGC | Existe = acierto | Campo estándar, no se establece umbral |
Tres límites (debes informar al usuario)
- Sin marca no significa imagen real. Con solo guardar de nuevo se pierde; portapapeles, reenvío por WeChat, capturas de pantalla, todo lo elimina.
- El modelo de píxeles no puede condenar por sí solo. Con un generador no visto antes se queda a cero, y es común que clasifique erróneamente imágenes reales (en pruebas, una foto de caja de cartón tomada con iPhone fue clasificada como 0.896 por la versión 384).
- Para trazabilidad real, hay que usar medidas de negocio: almacenar el hash de bytes de la imagen original al subir, registrar la huella del dispositivo, comparar con imágenes históricas del mismo pedido; esto es más fiable que cualquier modelo de detección.
Material de referencia negativo
image-provenance(herramienta web del lado del cliente): Interfaz bonita, análisis de dominio de frecuencia completo, pero el código fuente no contiene ninguna palabra clave AIGC/TC260, es completamente insensible a las marcas de cumplimiento chinas; además, el análisis de metadatos depende de la carga de exifr desde CDN, por lo que sin conexión se degrada a heurística de bytes. En pruebas, una imagen con marca AIGC completa mostraba 'No se encontró credencial de fuente AI'.XAI-Deepfakes: Clasificador multiclase de intercambio de caras (FaceForensics++), solo reconoce rostros. Alimentar una foto de confirmación de paquete haría que 5 de cada 10 imágenes reales fueran clasificadas como Face2Face/DeepFakes/NeuralTextures.LAID: Arquitectura ligera (ShuffleNet 0.34M parámetros, CPU 20ms), pero los pesos están en Google Drive, inaccesibles desde China.