septiembre 29, 2021 10:00 am

Jesús

Una de las claves para crear soluciones de computer vision efectivas y eficientes, es controla el entorno, en especial la iluminación.

¿A qué me refiero con esto? 

Pues, a que es mucho más sencillo desarrollar un producto o software cuando sabemos con certeza cuáles son las condiciones iniciales de, por ejemplo, iluminación, entorno, entre otras variables.

La alternativa a controlar el entorno, es crear un algoritmo que maneje todos los posibles casos borde, tales como:

  • Imágenes tomadas en diferntes condiciones de iluminación.
  • Imágenes tomadas en exteriores versus imágenes tomadas en interiores.
  • Imágenes borrosas o desenfocadas.
  • Imágenes descentradas o con oclusión.
  • Imágenes con diferentes niveles de contraste.

¿Sabes qué es lo peor? Que puedes dedicarte días, semanas, e incluso meses a crear el algoritmo más robusto posible, y te garantizo que, de cualquier manera, existirá alguna condición en la que no habrás pensado.

¿Moraleja?

Controla. La. Iluminación.

Un buen primer paso para saber cuándo una imagen presenta problemas de iluminación, es determinar si su nivel de contraste es alto o bajo.

Para ello, podemos crear un detector de bajo contraste con la ayuda de OpenCV y scikit-image en apenas un puñado de líneas de código.

¿Te interesa? ¡Sigue leyendo!

Al final de este artículo habrás aprendido:

  • Cómo el bajo contraste puede influir en el procesamiento de imágenes.
  • Cómo implementar un detector de contraste bajo con OpenCV y scikit-image.
  • Cómo detectar bajo contraste en tus imágenes.

¡Comencemos!

¿Qué es el Contraste y Por Qué Debería Importarte?

Según Wikipedia, “el contraste se define como la diferencia relativa entre un punto de una imagen.”

En pocas palabras, se trata de la diferencia de intensidad o color entre dos píxeles de una imagen.

Ejemplos de contraste alto, medio y bajo.

Ejemplos de contraste alto, medio y bajo.

Como vemos en la imagen de arriba, cuando el contraste es bajo, nos cuesta distinguir los objetos en primer plano, del fondo y otros elementos en la fotografía. 

¿Qué algoritmos o procesos pueden verse afectados por el bajo contraste?

Para empezar, la detección de bordes mediante herramientas como Canny se vuelve una tarea cuesta arriba debido a que, precisamente, los límites entre objetos se difuminan, se píerden a causa del poco contraste.

La segmentación de objetos, la detección de contornos y, en general, cualquier proceso dependiente de encontrar los bordes de los elementos de una imagen se ven negativamente impactados por una foto con bajo contraste, en especial si los parámetros pasados a estos algoritmos son estáticos.

Ahora que sabemos lo nocivo que puede llegar a ser el bajo contraste, dispongámonos a crear un detector específico para este tipo de situaciones.

Creación del Entorno de Programación con Anaconda

Echémosle un vistazo a la estructura del proyecto:

Estructura del proyecto

Estructura del proyecto

Como podemos notar, es bastante sencillo, ya que se compone de un único script, datasmarts/detect_low_contrast.py, mientras que en la carpeta resources contamos con varias imágenes de prueba que usaremos para evaluar nuestra solución.

Para crear el ambiente de Anaconda, corre este comando:

conda env create -f env.yml

Con esta instrucción habremos creado un ambiente llamado opencv-low-contrast-detector, configurado así:

Finalmente, para activar el ambiente, ejecuta:

conda activate opencv-low-contrast-detector

¡Chévere! Sigamos adelante.

Detección de Bajo Contraste con OpenCV y scikit-image

Abre el archivo datasmarts/detect_low_contrast.py e inserta estas líneas para traerte las dependencias del script:

Fíjate que entre las importaciones nos traemos la función is_low_contrast(), la cual, como su nombre indica, nos dirá si el contraste en una imagen es bajo o no. Dentro de poco veremos en detalle cómo funciona.

A continuación, definimos el menú del programa, compuesto de los siguientes parámetros:

  • -i/--input: Ruta a la carpeta donde se encuentran las imágenes de entrada.
  • -t/--threshold: Umbral para determinar si una foto tiene bajo contraste.

El siguiente paso es cargar la lista de imágenes a procesar:

Iteraremos sobre cada imagen en el directorio de entrada; una vez cargada la imagen, la redimensionaremos para que tenga un ancho de 450 pixeles, preservando la relación de aspecto:

Convertimos la imagen a escala de grises, la difuminamos usando Gaussian blur y luego corremos el detector de bordes de Canny sobre la misma. 

¿Por qué hacemos esto? Para ver que, en efecto, cuando una imagen tiene bajo contraste, somos incapaces de detectar la mayoría de los bordes presentes en ella:

Dependiendo del resultado de is_low_contrast(), etiquetaremos la imagen original, indicando si es de bajo contraste o no.

is_low_contrast() determina si la imagen es de bajo contraste con base a un umbral proporcionado por nosotros, en este caso, a través del parámetro -t/--threshold:

Creamos un mosaico comparativo de la imagen original etiquetada con el resultado de is_low_contrast(), versus los bordes hallados por Canny:

Por último, destruimos todas las ventanas creadas por OpenCV:

Podemos probar nuestro programa fácilmente con este comando:

python datasmarts/detect_low_contrast.py -i ./resources

La primera imagen que veremos es la siguiente:

Según nuestro programa, no padece de bajo contraste, lo cual queda patente en el hecho de que Canny fue capaz de detectar un gran número de bordes como se ve en la imagen binaria de la derecha.

De hecho, si quisiéramos procesar correctamente esta imagen, posiblemente tendríamos que difuminarla y suavizarla para reducir la cantidad de texturas y relieves que contribuyen a bordes innecesarios (por ejemplo, los ubicados en el suelo).

La siguiente imagen es a color, tomada en una bodega de vino:

En primera instancia, podríamos pensar que por tratarse de una fotgrafía tomada en una locación oscura, el contraste no sería el mejor, pero nuestro algoritmo parece estar conforme con la iluminación de esta imagen.

No obstante, al analizar la imagen binaria, concluimos que el contraste podría mejorar, ya que los bordes en algunas zonas (por ejemplo, donde se encuentran las botellas en el piso) fueron ignorados por Canny, precisamente por la poca diferenciación entre los objetos y el fondo.

Por último, tenemos la imagen de una modelo en blanco y negro:

En este caso, resulta evidente que la imagen adolece de bajo contraste, y efectivamente nuestro script opina lo mismo. Adicionalmente, la imagen binaria nos muestra apenas unos cuantos bordes correspondientes al contorno del sombrero y a algunos rasgos faciales de la chica. 

Sin embargo, detalles como los labios, la nariz, la cabellera de la mujer y su reflejo en el espejo han sido ignorados por el pobre contraste en esta fotografía.

Resumen

Hoy aprendimos cómo el bajo contraste, una situación que se da cuando la diferencia entre la densidad lumínica entre píxeles adyacentes no es lo suficientemente pronunciada, impacta negativamente en nuestra capacidad de analizar, procesar y extraer información de las imágenes, particularmente cuando nuestras herramientas dependen de la detección de los bordes de los objetos en una foto.

Uno de los algoritmos que se ve más afectado por un pobre contraste es el detector de bordes de Canny, ya que, por definición, un contraste bajo se traduce en dificultad para diferenciar entre un objeto y el fondo en una imagen, una condición fundamental para que Canny funcione adecuadamente.

Con el fin de determinar cuando una imagen adolece de bajo contraste, creamos un pequeño pero efectivo programa capaz de detectar esta situación.

Con la ayuda de OpenCV, pero sobre todo, de scikit-image, a través de la útil función is_low_contrast() logramos determinar fácilmente si una imagen es de bajo contraste o no.

Afortunadamente, contamos con cierto grado de libertad al momento de usar el detector, ya que podemos pasarle un valor que sirve como umbral para decidir si una imagen tiene bajo contraste. Por defecto, usamos el valor 0.35, aunque posiblemente debamos cambiarlo para obtener mejores resultados en otros contextos.

¿Por qué no te descargas el código y experimentas un poco más por tu cuenta? Por ejemplo, puedes probar con diferentes valores de -t/--threshold para ver cómo varían los resultados.

¡Adiós!

Sobre el Autor

Jesús Martínez es el creador de DataSmarts, un lugar para los apasionados por computer vision y machine learning. Cuando no se encuentra bloggeando, jugando con algún algoritmo o trabajando en un proyecto (muy) cool, disfruta escuchar a The Beatles, leer o viajar por carretera.