Brillo y contraste. Ajuste automático.

El ajuste del brillo y del contraste se usa para mejorar la visibilidad de la imagen. El brillo añade o reduce luminosidad a la imagen y el contraste aumenta o disminuye la distancia entre los valores. No vamos a complicarnos la vida. La fórmula para ajustar el brillo y el contraste de una imagen es muy sencilla, es la unión de dos formulas. Como ya os imaginareis, la de ajuste del brillo y la de ajuste del contraste

Formula para ajustar el brillo de la imagen:

newPixelValue = pixelValue+b;

Sumando a cada pixel un valor lo hacemos más brillante, si el valor es positivo, o más oscuro, si es negativo.

Formula para ajuste del contraste:

newPixelValue = c * (pixelValue-128) + 128

Combinando ambas tenemos una formaula que nos permite ajusta ambos valores:

newPixelValue = c * (pixelValue-128) + 128 + b

Siendo el parámetro c el contraste y b el brillo. Sólo hay que tener en cuenta las diferencias entre la estructura de datos de color y de escala de grises y tenemos el código listo.

Pero como nos encanta complicarnos la vida vamos a intentar que el ajuste del brillo y el contraste sea automático. ¿Como?. Supongamos que una imagen correctamente balanceada «ocupa» todo el rango de valores de 0 a 255. Vamos a «estirar» los valores de la imagen para que el mínimo sea el 0 y el máximo el 255. Es tan «sencillo» como buscar el valor más bajo y más alto que hay en la imagen. Ahora seleccionamos los valores contraste y brillo que hacen que el mínimo valor sea 0 y el máximo 255. Suena lógico, ¿cómo lo hacemos?. ¡Resolviendo ecuaciones!. Contened vuestra emoción.

Vamos a hacer que el mínimo sea la variable low y el máximo high. Tenemos estas dos ecuaciones:

(c*(high-128))+128+b = 255

(c*(low-128))+128+b = 0

Resolvemos el valor de b en la que es igual a 0. Podríamos despejarlo de cualquier otra forma pero esta me ha parecido la más fácil.

b = 128*c - low*c - 128

Sustituimos la b en la segunda ecuación:

c = 255/(high-low)

Bueno, ¿de donde sacamos estos valores low y high? Es tan sencillo como recorrer la imagen comparando los valores de sus pixels para  encontrar el mínimo y el máximo.

Con las imágenes en escala de grises es sencillo pues solo hay un valor maximo y uno minimo. Pero con las de color hay que tomar una decisión. ¿Ajustamos los tres canales de color de forma independiente calculando los valores b y c para cada uno o usamos los mismos para todos los canales?.

Si ajustamos todos los canales con los mismos valores de b y c hay dos opciones:

  • Convertir cada pixel a escala de grises, calcular el mínimo y el máximo y luego los correspondientes b y c
  • Calcular el máximo y mínimo de cada canal y calcular el b y c de aquel que tenga mayor distancia entre el valor máximo y mínimo (high -low). Asi nos aseguramos que coger el canal que «menos hay que estirar».

¿Cual es el problema ajustar cada color por separado? Que al estirar cada canal por separado la relación entre los colores cambia cambiando el color y no sólo el contraste y la intensidad. Por ejemplo si el rango de cada canal es:

Rojo: 0-255

Verde: 10-230

Azul: 100-150

Tras aplicar nuestro ajuste todos los canales tendrán un rango de 0-255. Así que mientras que el canal rojo no ha cambiado el azul ha variado de forma desproporcionada de tal forma que un píxel RGB con valores (230, 230, 100) pasa a ser tras el ajuste (230, 255, 0). Mientras el canal R y G casi no varía el B pasa de 100 a 0. Estos cambios alteran el color. Puede dar resultados muy artísticos pero poco naturales.

Todos estos cálculos se pueden realizar usando tablas de consulta calculando el valor para cada uno de los posibles valores de cada pixel (0…255)

Evitar el ruido

Vamos a la siguiente vuelta de tuerca. ¿Qué pasa si el valor mínimo y el máximo sólo corresponden a unos pocos píxeles no representativos de la imagen? Al aplicar el ajuste de brillo y contraste podríamos hacer más mal que bien y dejar la imagen peor. ¿Pero ese caso se puede dar? Resulta que «píxeles con valores extremos no representativos de la imagen» describe muy bien cierto tipo de ruidos como el ruido de sal y pimienta. Como siempre la realidad resulta ser menos bonita que la bella teoría. Por suerte hay solución. En lugar de coger los valores mínimos y máximos vamos a descartar una porcentaje pequeño de los valores más altos y más bajos. Por ejemplos vamos a descartar el 5% de los pixeles con los valores más bajos y el 5% con los valores más altos. En caso de que la iamgen esté muy deteriorada por el ruido estos valores pueden ser mayores

Así que hemos de sumar cuantos pixeles de cada valor hay en la imagen. Un momento, eso me suena, huy si, es el histograma y ya lo tenemos hecho. Sólo hemos de ir sumando el número de píxeles de cada entrada del array del histograma hasta que el acumulado supere los valores deseados y usar esos valores como mínimo y máximo. Parece facil y lo es hasta que vemos el histograma a color, tenemos tres canales de color ¿Como elegimos los valores high y low para calcular los ajustes de brillo y contraste?. En el caso anterior hemos ignorado de forma poco elegante este punto y el resultado a veces se resiente. La solución más simple es modificar la función de ajuste de brillo y contraste para que permita modificar cada canal por separado.