¿Qué es OpenEXR?
OpenEXR es un formato de imagen de alto rango dinámico (HDR) desarrollado por Industrial Light & Magic (ILM) — la empresa de efectos visuales de George Lucas responsable de los efectos originales de Star Wars — y publicado como código abierto en 2003. En 2021, la Academy Software Foundation (ASWF) adoptó OpenEXR como estándar ISO/IEC (ISO/IEC 23082), consolidando su posición como el formato de imagen fundamental para los efectos visuales profesionales, el renderizado 3D y la cinematografía con gradación de color.
La característica definitoria del formato es su capacidad para almacenar datos de imagen con precisión de coma flotante lineal en un rango dinámico efectivamente ilimitado — desde la sombra más oscura hasta un valor que representa la superficie del sol en la misma imagen, sin recorte ni artefactos de cuantización. Un JPEG o PNG estándar mapea el mundo en 256 niveles de brillo; una imagen OpenEXR puede representar 65.504 valores distintos por canal usando flotante de media precisión de 16 bits, y más de 3.000 millones con flotante de 32 bits.
Por Qué el Cine VFX Usa OpenEXR
El pipeline de VFX cinematográfico impone requisitos que los formatos de imagen para consumidor no pueden satisfacer:
- Composición: requiere que los valores de píxel preserven las relaciones energéticas físicas — añadir fuentes de luz matemáticamente debe producir resultados correctos, no valores blancos recortados
- Flexibilidad de espacio de color: los archivos OpenEXR almacenan valores lineales de escena; las tablas de búsqueda (LUT) y las configuraciones OpenColorIO (OCIO) se aplican posteriormente en el momento de la visualización
- Renderizado multipaso: un único archivo OpenEXR puede contener docenas de pases de renderizado (belleza, difuso, especular, sombra, oclusión ambiental, profundidad, vectores de movimiento, IDs de cryptomatte) como canales con nombre separados
- Ida y vuelta sin pérdida: cada ciclo de codificación/decodificación debe ser numéricamente idéntico; la compresión con pérdida de JPEG es incompatible con las matemáticas de composición
Cada gran estudio cinematográfico — Disney, Pixar, DreamWorks, Weta Digital, DNEG, ILM — exige OpenEXR como formato de intercambio principal para los activos de VFX.
Tipos de Datos en OpenEXR
Cada canal de un archivo OpenEXR puede usar independientemente uno de tres tipos de datos:
| Tipo | Bits | Rango | Caso de uso |
|---|---|---|---|
HALF |
16 | ±65.504; ~3 dígitos decimales | Canales de color (R, G, B, A) — mejor equilibrio tamaño/precisión |
FLOAT |
32 | ±3,4×10³⁸; ~7 dígitos decimales | Profundidad, vectores de movimiento, datos técnicos precisos |
UINT |
32 | 0–4.294.967.295 | IDs de objeto, recuentos de muestras, máscaras de cryptomatte |
El tipo HALF (flotante de media precisión IEEE 754 de 16 bits) se usa de forma abrumadora para los canales RGB. Fue inventado específicamente para OpenEXR — este formato es anterior al soporte generalizado de GPU para fp16.
Nomenclatura de Canales y EXR Multipaso
OpenEXR usa una convención de nomenclatura separada por puntos para organizar los datos de renderizado multipaso:
Canales estándar:
R, G, B, A — pase de belleza RGBA
Capas de renderizado arbitrarias (formato parte.canal):
diffuse.R, diffuse.G, diffuse.B — sombreado difuso
specular.R, specular.G, specular.B — reflejos especulares
shadow.R, shadow.G, shadow.B — pase de sombras
AO.R, AO.G, AO.B — oclusión ambiental
Z — profundidad (distancia de la cámara, float32)
Pworld.X, Pworld.Y, Pworld.Z — posición 3D en espacio mundial por píxel
Nworld.X, Nworld.Y, Nworld.Z — normales de superficie en espacio mundial
motionvector.U, motionvector.V — vectores de movimiento 2D en espacio de pantalla
cryptomatte00.R, cryptomatte00.G — IDs de cryptomatte para máscaras de objeto
Un único EXR multipaso de un renderizado de V-Ray o Arnold puede contener más de 30 canales, todos en un solo archivo.
Modos de Compresión de OpenEXR
| Método | Tipo | Ratio | Caso de uso |
|---|---|---|---|
NONE |
Sin pérdida | 1× | Máxima compatibilidad, archivado |
RLE |
Sin pérdida | ~1,5× | Codificación por longitud de ejecución; bueno para áreas planas |
ZIPS |
Sin pérdida | ~3× | Compresión ZIP por línea de escaneado |
ZIP |
Sin pérdida | ~3–4× | ZIP en bloques de 16 líneas de escaneado |
PIZ |
Sin pérdida | ~3–5× | Variante Huffman ajustada para datos visuales — el más común en producción |
DWAA |
Con pérdida | ~10–30× | Wavelets DreamWorks; pérdida casi imperceptible a calidad predeterminada |
DWAB |
Con pérdida | ~10–30× | DWAA con bloques de 256 líneas (mejor paralelismo) |
PIZ es el estándar para la producción VFX — sin pérdida, razonablemente rápido y con excelente compresión para contenido fotográfico. DWAA/DWAB se usa cuando el almacenamiento es crítico y la diferencia perceptual menor es aceptable.
Lectura de OpenEXR con Python
Bindings Python de OpenEXR (biblioteca oficial):
import OpenEXR
import Imath
import numpy as np
# Abrir un archivo EXR
exr = OpenEXR.InputFile("render_belleza.exr")
cabecera = exr.header()
print(f"Canales: {list(cabecera['channels'].keys())}")
print(f"Ventana de datos: {cabecera['dataWindow']}")
# Leer canales RGB (tipo half-float)
HALF = Imath.PixelType(Imath.PixelType.HALF)
vd = cabecera["dataWindow"]
anchura = vd.max.x - vd.min.x + 1
altura = vd.max.y - vd.min.y + 1
r_bytes = exr.channel("R", HALF)
g_bytes = exr.channel("G", HALF)
b_bytes = exr.channel("B", HALF)
R = np.frombuffer(r_bytes, dtype=np.float16).reshape((altura, anchura))
G = np.frombuffer(g_bytes, dtype=np.float16).reshape((altura, anchura))
B = np.frombuffer(b_bytes, dtype=np.float16).reshape((altura, anchura))
rgb = np.stack([R, G, B], axis=-1) # forma (H, W, 3), float16
print(f"Tamaño de imagen: {anchura}×{altura}")
print(f"Rango del canal R: {R.min():.4f} – {R.max():.4f}")
imageio (más simple, para RGB básico):
import imageio, numpy as np
# Leer EXR como array numpy (requiere imageio[exr])
img = imageio.imread("render.exr", format="exr")
print(img.dtype) # float32
print(img.shape) # (H, W, 4) para RGBA
# Aplicar ajuste de exposición simple
img_exp = img * 2.0 # +1 stop de exposición
# Mapeo tonal Reinhard simple para visualización en 8 bits
img_tm = img_exp / (1.0 + img_exp)
img_8bit = (np.clip(img_tm, 0, 1) * 255).astype(np.uint8)
imageio.imwrite("vista_previa.png", img_8bit)
EXR Multiparte (OpenEXR 2.0+)
OpenEXR 2.0 introdujo los archivos multiparte — múltiples conjuntos de imágenes independientes dentro de un único archivo .exr, cada uno con su propia cabecera, canales y compresión:
render_completo.exr
├── Parte 0: "beauty" — RGBA, línea de escaneado, compresión PIZ
├── Parte 1: "depth" — Z float32, línea de escaneado, ZIP
├── Parte 2: "normals" — XYZ float16, línea de escaneado, PIZ
└── Parte 3: "cryptomatte" — RGBA uint32, línea de escaneado, NONE
Esto elimina la necesidad de gestionar docenas de archivos EXR separados por fotograma.
Datos Profundos: Composición Volumétrica
OpenEXR 2.0 también introdujo la composición profunda (tipo DEEP_IMAGE): en lugar de un valor RGBA por píxel, el EXR profundo almacena una lista de longitud variable de muestras por píxel, cada una con un rango de profundidad y valor de color. Esto permite componer objetos semitransparentes (lluvia, humo, cristal) sin errores de alfa pre-multiplicado, y la inserción de renderizados volumétricos en escenas de acción real.
OpenEXR en Software de Renderizado
Todos los principales renderizadores 3D usan OpenEXR como formato de salida principal:
- Pixar RenderMan: salida EXR nativa desde 2002
- Arnold (Autodesk/Solid Angle): EXR con AOVs arbitrarios
- V-Ray (Chaos): EXR multicapa con elementos de renderizado separados
- Blender Cycles / EEVEE: EXR vía nodo Salida de Archivo en el compositor
- Unreal Engine: el renderizado cinematográfico produce secuencias EXR
- Redshift: EXR comprimido con DWAA como predeterminado
- Octane: EXR multipaso desde la configuración de renderizado
El estándar OpenEXR se mantiene en openexr.com y la biblioteca C++ forma parte de VFX Platform, la especificación de dependencias coordinada por la industria que usan todos los pipelines de los estudios de VFX.
Conversiones relacionadas
Lo más habitual al trabajar con imágenes son estas direcciones de conversión: