---
name: image_compare_side_by_side
kind: function
lang: py
domain: ml
version: "1.0.0"
purity: impure
signature: "def image_compare_side_by_side(a, b, label_a='A', label_b='B', gap_px=16, show_diff=True, show_phash=True) -> dict"
description: "Compara dos PIL Images lado a lado generando una imagen compuesta A | diff | B con gap configurable. Calcula MSE pixel-wise y perceptual hash (imagehash si disponible). Util para inspeccionar diferencias entre generaciones de imagen."
tags: [image, compare, diff, phash, mse, pil, pillow, visualization, ml, side-by-side, pendiente-usar]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [Pillow, numpy, imagehash]
tested: true
tests:
  - "grid es PIL.Image con dimensiones correctas show_diff=True"
  - "grid es PIL.Image sin diff show_diff=False"
  - "pixel_mse positivo para imagenes distintas"
  - "pixel_mse cero para imagen identica"
  - "phash None si imagehash no disponible"
test_file_path: "python/functions/ml/tests/test_image_compare_side_by_side.py"
file_path: "python/functions/ml/image_compare_side_by_side.py"
params:
  - name: a
    desc: "Primera imagen PIL (referencia). Se convierte a RGB internamente si es RGBA/L/etc."
  - name: b
    desc: "Segunda imagen PIL (comparacion). Se redimensiona a size de a si difieren."
  - name: label_a
    desc: "Etiqueta de texto para el panel A (default 'A')."
  - name: label_b
    desc: "Etiqueta de texto para el panel B (default 'B')."
  - name: gap_px
    desc: "Espacio en pixeles entre paneles y en los bordes del canvas (default 16)."
  - name: show_diff
    desc: "Si True (default), inserta panel central con PIL.ImageChops.difference + autocontrast."
  - name: show_phash
    desc: "Si True (default), calcula perceptual hash con imagehash. Si el paquete no esta instalado, retorna None silenciosamente."
output: "dict con: 'grid' (PIL.Image compuesta), 'phash_a' (str|None), 'phash_b' (str|None), 'phash_distance' (int|None, Hamming), 'pixel_mse' (float)."
---

## Ejemplo

```python
from PIL import Image
from ml.image_compare_side_by_side import image_compare_side_by_side

img_a = Image.open("outputs/gen_v1.png")
img_b = Image.open("outputs/gen_v2.png")

result = image_compare_side_by_side(img_a, img_b, label_a="v1", label_b="v2")

result["grid"].save("compare.png")
print(f"MSE:  {result['pixel_mse']:.2f}")
print(f"pHash distance: {result['phash_distance']}")
```

## Layout del grid

Con `show_diff=True` (default):

```
[gap] [A] [gap] [diff] [gap] [B] [gap]
```

Canvas width  = 3*w + 4*gap
Canvas height = h + 2*gap

Con `show_diff=False`:

```
[gap] [A] [gap] [B] [gap]
```

Canvas width  = 2*w + 3*gap

## Notas

- `pixel_mse` usa numpy si disponible; fallback a loop puro stdlib (mas lento).
- `phash_*` requiere `pip install imagehash`. Sin el paquete, los tres campos son `None`.
- Las imagenes se convierten a RGB antes de cualquier operacion para consistencia.
- Si `a` y `b` tienen distinto tamano, `b` se redimensiona con LANCZOS al tamano de `a`.
