miércoles, 18 de julio de 2018

Fusión HDR de imágenes con R

En ocasiones los fotógrafos se enfrentan a escenas cuya diferencia de luminosidad entre las partes más oscuras y las más claras supera el rango dinámico que el sensor de la cámara es capaz de capturar. Esto significa que con una sola fotografía resulta imposible obtener toda la información: si se salvan las altas luces las sombras tendrán un ruido excesivo que arruinará sus texturas, y si por el contrario se fotografían correctamente las sombras las luces aparecerán quemadas.

Una de las técnicas usadas para resolver este problema es la fusión HDR (High Dynamic Range o Alto Rango Dinámico), que probablemente puedas configurar en tu móvil, y que consiste en hacer varias fotografías de diferente nivel de exposición (luminosidad) con intención de combinarlas para obtener una única imagen final con toda la información: las capturas menos expuestas suministrarán la información de las altas luces sin saturar, mientras que las más expuestas proporcionarán las sombras libres de ruido.

Vamos a estudiar una escena de rango dinámico superior al de la cámara usada para fotografiarla, una Canon 350D. Para abordarla se tomaron dos fotografías con una diferencia en exposición de cuatro pasos, es decir, niveles separados por un factor lineal 24 = 16.


Fuente: Seu Universitària, La Nucía (Alicante). Estudio crystalzoo


Las imágenes se ven planas porque se han revelado a partir de los archivos RAW sin aplicar ningún procesado. Pero lo importante en este punto es darnos cuenta de que ninguna de ellas por sí sola basta para obtener un resultado válido: la primera presenta excesivo ruido en las sombras y la segunda tiene las luces quemadas.



Necesitamos conocer la diferencia de exposición entre las dos tomas porque habremos de igualar la exposición de las imágenes previamente a su fusión. Aunque se ajustó una diferencia nominal de cuatro pasos, las cámaras presentan tolerancias así que calcularemos numéricamente un valor más exacto.

Amparándonos en la alta linealidad del sensor, comparamos los niveles RGB lineales (obtenidos con DCRAW) de cada par de píxeles homónimos de una imagen y la otra haciendo un promedio de los ratios. Pero no todos los emparejamientos serán válidos, tomaremos solo aquellos niveles RGB que se encuentren en ambas imágenes entre unos valores mínimo (para evitar un ruido excesivo) y máximo (descartando posibles alinealidades del sensor). Representamos gráficamente qué píxeles finalmente participaron en el cálculo.



Esta imagen tan psicodélica requiere cierta explicación pero es interesante de interpretar. Las zonas en negro corresponden a píxeles que no se emplearon para calcular la exposición relativa entre las fotografías, porque sus niveles en alguna de las dos imágenes no respetaban los umbrales. El resto sí participaron, indicando su color qué canales RGB entraron en el cálculo: en blanco si contribuyeron los tres canales, en amarillo los canales R y G, en azul si solo participó el canal B,...

Del total de niveles RGB disponibles entró en el cómputo el 20%, lo que en una imagen de 8Mpx es una cantidad de información más que suficiente para tener una estimación muy precisa. Como era de esperar los píxeles escogidos resultaron ser los correspondientes a las luminosidades intermedias.

La exposición relativa calculada fue de 15,69 en escala lineal, lo que en este caso supone una diferencia muy sutil respecto al valor 16 ajustado en la cámara. En la siguiente imagen se muestra la distribución de exposiciones relativas, indicando en rojo el valor calculado por mediana.



La distribución debe su forma a la definición como cociente entre dos señales afectadas por ruido fotónico, dominante en los niveles de exposición participantes en el cálculo. El ruido fotónico se modela como un proceso de Poisson con alta tasa de llegadas (fotones) y es por tanto aproximable por una normal, así que lo que tenemos es un cociente de distribuciones normales.

Gracias al cálculo preciso de exposición relativa nos permitiremos hacer una fusión "dura", que consiste en tomar de la captura más expuesta todos los niveles RGB que cumplan una condición de no saturación y el resto de niveles de la otra fotografía, sin transición progresiva en las regiones frontera. Los valores RGB de la captura más expuesta se corregirán previamente a la baja por el factor calculado con el fin de igualar la exposición.

La siguiente imagen es el mapa de fusión, gráfico que muestra en blanco los píxeles que se tomarán de la captura menos expuesta, en negro los de la más expuesta, y en tonos parciales aquellos píxeles con una contribución mixta de ambas capturas. La selección es muy óptima obteniendo el 87% del total de la información de la toma más expuesta, la que proporciona más calidad de imagen por tener mejor relación S/N.



El resultado de la fusión es una imagen con el mismo nivel de exposición que la captura menos expuesta, pero con toda la información de luces y sombras disponible.



Para obtener una imagen final válida falta aplicar sobre la fusión "oscura" y sin ruido un procesado conocido como mapeo de tonos, consistente en el aumento de la luminosidad general, pero preservando la información de altas luces a la vez que se logra un aceptable nivel de contraste local, para que la imagen no resulte anodina.

En el archivo fusionmapeotonos.tif se tiene tanto la fusión de las dos capturas (podemos ver la contribución de cada una deseleccionando la otra), como el proceso de mapeo tonal realizado mediante curvas con máscaras de capa en Photoshop. Con mayor resolución aquí.



Pese a que la fusión se ha hecho sin ninguna progresividad, se hace imposible detectar saltos en las "costuras" del mapa de fusión gracias a la linealidad del sensor y a la precisión con que se ha hecho la corrección de exposición.

Aunque el proceso pueda parecer complejo, recomiendo echar un ojo al código en hdr.R para comprobar lo concisa que resulta su implementación, una buena demostración de la notación matricial de R. No es solo que podamos almacenar de forma nativa imágenes en color en variables de tipo array, sino la potencia que brinda poder acceder y manipular los elementos de dichos arrays con índices lineales.

En concreto la función which(), de la que soy fan, nos ha permitido vectorizar la selección y manipulación de extensos e irregulares conjuntos de píxeles en estas fotografías con una sintaxis compacta, intuitiva, y de ejecución ultrarrápida al ser funciones compiladas que evitan tener que usar bucles.

Para replicar el proceso íntegro con la escena del ejemplo, los archivos RAW pueden descargarse en raw1.cr2 y raw2.cr2.

No hay comentarios:

Publicar un comentario

Por claridad del blog, por favor trata de utilizar una sintaxis lo más correcta posible y no abusar del uso de emoticonos, mayúsculas y similares.