aida-p0 / scripts /predict.py
AIDAON's picture
AIDA P0 — codigo, metodologia, metricas e limitacoes (sem os pesos do modelo)
1352e55 verified
Raw History Blame Contribute Delete
4.12 kB
#!/usr/bin/env python
"""AIDA P0 — CLI de inferência.
python predict.py imagem.jpg
python predict.py imagem.jpg --json
python predict.py *.jpg --json # uma linha JSON por imagem
python predict.py imagem.jpg --models-dir /outro/models
Códigos de saída:
0 todas as imagens foram analisadas
2 erro de entrada (arquivo, formato, imagem)
3 erro de ambiente (dependência, artefato, memória)
Leia o MODEL_CARD.md antes de interpretar qualquer número. Resumo do que
importa: o score é probabilístico, o FPR depende fortemente do tipo de
fotografia, e o resultado não é prova de origem.
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
def _raiz_do_pacote() -> Path:
"""Acha o diretorio que contem `aida_p0/`, sem assumir profundidade.
O CLI e distribuido em dois arranjos: `release/predict.py` no repositorio de
pesquisa e `scripts/predict.py` no pacote do Hugging Face. Um caminho fixo
so funcionaria num dos dois.
"""
aqui = Path(__file__).resolve()
for base in (aqui.parent, *aqui.parents):
for candidato in (base, base / "src"):
if (candidato / "aida_p0" / "__init__.py").exists():
return candidato
raise SystemExit(
"ERRO: nao encontrei o pacote `aida_p0`. Rode este script de dentro do "
"pacote do AIDA (o diretorio que contem `aida_p0/` e `models/`).")
sys.path.insert(0, str(_raiz_do_pacote()))
from aida_p0 import AidaP0, erros # noqa: E402
AVISO = ("AIDA e um detector estatistico. O resultado NAO e prova de origem "
"nem de fraude, e o FPR varia muito por dominio (ver MODEL_CARD).")
def texto(r) -> str:
marca = "!" if r.decisao_politica == "INCONCLUSIVO" else " "
linhas = [
f"arquivo : {Path(r.arquivo).name}",
f"score_ia : {r.score_ia:.6f}",
f"classe : {r.classe}",
f"threshold : {r.threshold!r}",
f"decisao {marca} : {r.decisao_politica}",
f"sensores : clip {r.score_clip:.6f} | forense {r.score_forense:.6f} "
f"| rigid {r.score_rigid:.6f}",
f"desacordo : {r.desacordo_entre_sensores:.6f} "
f"qualidade: {r.qualidade:.4f}",
f"tempo : {r.segundos:.2f} s",
]
if r.motivos:
linhas.append("motivos : " + "; ".join(r.motivos))
return "\n".join(linhas)
def main(argv: list[str] | None = None) -> int:
ap = argparse.ArgumentParser(
description="AIDA P0 — deteccao de imagens geradas por IA",
epilog=AVISO)
ap.add_argument("imagens", nargs="+", help="caminho(s) de imagem")
ap.add_argument("--json", action="store_true",
help="uma linha JSON por imagem (JSONL)")
ap.add_argument("--models-dir", default=None,
help="diretorio dos artefatos (padrao: ./models)")
ap.add_argument("--sem-verificar-hashes", action="store_true",
help="pula a conferencia SHA-256 dos artefatos")
ap.add_argument("--verbose", action="store_true")
args = ap.parse_args(argv)
try:
aida = AidaP0(models_dir=args.models_dir,
verificar_hashes=not args.sem_verificar_hashes,
verbose=args.verbose)
except erros.AidaError as e:
print(f"ERRO: {e}", file=sys.stderr)
return e.codigo_saida
pior = 0
for i, caminho in enumerate(args.imagens):
try:
r = aida.analisar(caminho)
except erros.AidaError as e:
print(f"ERRO [{caminho}]: {e}", file=sys.stderr)
pior = max(pior, e.codigo_saida)
continue
if args.json:
print(json.dumps(r.to_dict(), ensure_ascii=False))
else:
if i:
print()
print(texto(r))
if not args.json:
print(f"\n{AVISO}", file=sys.stderr)
return pior
if __name__ == "__main__":
raise SystemExit(main())