# Compara JPEG, WebP e AVIF nas fotos de uma pasta.
# Uso:  python comparar-formatos-de-imagem.py pasta_com_fotos [largura_final]
# Requer: pip install pillow numpy scikit-image   (Pillow 11.3 ou mais novo, que já grava AVIF)
#
# Para cada foto o script:
#   1. redimensiona para a largura final (padrão 1200 px), que é o que um site precisa
#   2. grava em JPEG, WebP e AVIF na qualidade padrão de cada formato
#   3. procura, em cada formato, o menor arquivo que ainda mantém SSIM >= 0,95 contra a foto redimensionada
#   4. escreve tudo em resultado.csv
# Modo Fluxo, setembro de 2026. Pode copiar, mudar e usar.

import csv, glob, io, os, sys, time
import numpy as np
from PIL import Image
from skimage.metrics import structural_similarity

PASTA = sys.argv[1] if len(sys.argv) > 1 else "fotos"
LARGURA = int(sys.argv[2]) if len(sys.argv) > 2 else 1200
ALVO_SSIM = 0.95
PADRAO = {"JPEG": 80, "WEBP": 80, "AVIF": 60}


def grava(img, fmt, q):
    buf = io.BytesIO()
    t0 = time.perf_counter()
    if fmt == "JPEG":
        img.save(buf, "JPEG", quality=q, optimize=True, progressive=True)
    elif fmt == "WEBP":
        img.save(buf, "WEBP", quality=q, method=6)
    elif fmt == "AVIF":
        img.save(buf, "AVIF", quality=q, speed=6)
    elif fmt == "PNG":
        img.save(buf, "PNG", optimize=True)
    ms = (time.perf_counter() - t0) * 1000
    return buf.getvalue(), ms


def ssim(ref, dados):
    out = Image.open(io.BytesIO(dados)).convert("RGB")
    a = np.asarray(ref.convert("L"), dtype=np.float64)
    b = np.asarray(out.convert("L"), dtype=np.float64)
    return structural_similarity(a, b, data_range=255)


def menor_com_qualidade(ref, fmt):
    """Busca binária: menor qualidade que ainda entrega SSIM >= alvo."""
    lo, hi, melhor = 5, 95, None
    while lo <= hi:
        q = (lo + hi) // 2
        dados, ms = grava(ref, fmt, q)
        s = ssim(ref, dados)
        if s >= ALVO_SSIM:
            melhor = (q, len(dados), s, ms, dados)
            hi = q - 1
        else:
            lo = q + 1
    return melhor


linhas = []
for caminho in sorted(glob.glob(os.path.join(PASTA, "*.jpg")) + glob.glob(os.path.join(PASTA, "*.jpeg")) + glob.glob(os.path.join(PASTA, "*.png"))):
    nome = os.path.basename(caminho)
    orig = Image.open(caminho).convert("RGB")
    bytes_orig = os.path.getsize(caminho)
    alt = round(orig.height * LARGURA / orig.width)
    ref = orig.resize((LARGURA, alt), Image.LANCZOS)
    png, _ = grava(ref, "PNG", 0)
    base = {"foto": nome, "largura_original": orig.width, "kb_original": round(bytes_orig / 1024), "kb_png_redimensionado": round(len(png) / 1024)}
    for fmt in ("JPEG", "WEBP", "AVIF"):
        dados, ms = grava(ref, fmt, PADRAO[fmt])
        base[f"{fmt}_padrao_q"] = PADRAO[fmt]
        base[f"{fmt}_padrao_kb"] = round(len(dados) / 1024, 1)
        base[f"{fmt}_padrao_ssim"] = round(ssim(ref, dados), 4)
        base[f"{fmt}_padrao_ms"] = round(ms)
        q, tam, s, ms2, d2 = menor_com_qualidade(ref, fmt)
        base[f"{fmt}_igual_q"] = q
        base[f"{fmt}_igual_kb"] = round(tam / 1024, 1)
        base[f"{fmt}_igual_ssim"] = round(s, 4)
        if os.environ.get("SALVAR"):
            os.makedirs("saida", exist_ok=True)
            open(os.path.join("saida", f"{os.path.splitext(nome)[0]}.{fmt.lower()}"), "wb").write(d2)
    linhas.append(base)
    print(nome, base["kb_original"], "KB ->", base["JPEG_igual_kb"], base["WEBP_igual_kb"], base["AVIF_igual_kb"], "KB (JPEG/WebP/AVIF com SSIM >= 0,95)")

with open("resultado.csv", "w", newline="", encoding="utf-8-sig") as f:
    w = csv.DictWriter(f, fieldnames=list(linhas[0].keys()), delimiter=";")
    w.writeheader()
    w.writerows(linhas)
print("resultado.csv gravado com", len(linhas), "fotos")
