enero 17, 2022 10:00 am

Jesús

Las imágenes constituyen uno de los medios de transmisión de información más densos que existen.

Pensémoslo por un momento: ¿Cuántas palabras te tomaría describir la siguiente foto?

¿Podríamos capturar en texto los matices, tonalidades y ubicación de cada elemento? 

¿Cómo expresaríamos la posición exacta de la palmera, así como su inclinación

¿Tenemos la capacidad de describir a tal nivel los colores involucrados en la foto, de manera que el lector, sin importar de quién se trate, dibuje la misma escena mental que estamos visualizando?

Difícilmente.

Teniendo esto en cuenta, el dicho “Una imagen vale más que mil palabras” cobra mucho más sentido, ¿no lo crees? 

Un ejemplo de cómo una simple imagen puede sustituir millares de palabras se encuentra en las adaptaciones al cine de libros o novelas. 

Recuerdo que al leer por primera vez Harry Potter y la Piedra Filosofal, la autora dedicó un número importante de páginas a la descripción de Hogwarts, el castillo medieval donde se imparten las clases de magia y, a su vez, donde ocurre la mayor parte de la acción en la saga. 

No obstante, al asistir al estreno de la película, noté que éstos párrafos ricos en detalle fueron reemplazados por tan solo unos cuantos cuadros en el largometraje

Ahora, cabe hacernos la siguiente interrogante: ¿Es toda la información contenida en una imagen útil?

La respuesta varía fuertemente con base a la aplicación o proyecto de computer vision en el que estemos trabajando.

Por ejemplo, si estamos construyendo un reconocedor de rostros en imágenes, todo lo que no constituya una cara es ruido que, muy probablemente, distraiga o dificulte la tarea que queremos llevar a cabo. 

¿No sería genial contar con un mecanismo para enfocarnos en regiones específicas de una fotografía? 

Sí, y el día de hoy centraremos nuestra atención en una herramienta para tal propósito, llamada enmascarado.

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

  • Qué es una máscara y para qué sirve.
  • Cómo crear una máscara en NumPy.
  • Cómo aplicar una máscara en OpenCV.

¿Preparado? ¡Comencemos!

Enmascarado

El enmascarado es un mecanismo que nos facilita especificar cuáles píxeles de una imagen queremos conservar. De hecho, puede ser visto como un método de recorte avanzado.

Ahora, la pregunta del millón es: ¿Cómo la llevamos a cabo?

Mediante operaciones a nivel de bits. Afortunadamente, en este artículo hablamos al respecto sobre tales operaciones, por lo que mi recomendación es que lo leas para que te familiarices con el concepto antes de seguir adelante.

¿Qué es una máscara?

Una máscara es, en pocas palabras, un mapa donde indicamos qué pixeles queremos conservar y cuáles no.

Una analogía que me ayuda a entender el concepto es que la máscara es un tablero de control, con tantos interruptores como pixeles tiene la imagen original (por esta razón, las máscaras deben tener el mismo ancho y alto que éstas. Su profundidad es unidimensional, ya que se encuentran en escala de grises).

Un píxel en la máscara con valor igual a cero indica que este píxel está apagado o debe ser desechado en la imagen original

Un píxel en la máscara con valor mayor a cero indica que éste píxel está prendido o debe conservarse en la imagen original.

Creación del Entorno de Desarrollo con Anaconda

Antes de continuar, veamos la estructura del proyecto:

El archivo más importante es, por supuesto, datasmarts/masking.py, pues ahí vive la demostración que analizaremos más adelante.

Para crear el ambiente de Anaconda, ejecuta esta instrucción desde la raíz del proyecto:
conda env create -f opencv-masks

El ambiente creado, llamado opencv-masks, está configurado así:

Para activar el ambiente, ejecuta:

conda activate opencv-masks

Encontrarás el código para descarga en el formulario de abajo:

Pongámonos manos a la obra en la siguiente sección.

Máscaras en OpenCV

Como de costumbre, las ideas suelen cimentarse cuando las vemos reflejadas en código, así que abre el archivo datasmarts/masking.py, e inserta estas líneas para importar las dependencias:

Cargamos en memoria la imagen de prueba, y la desplegamos en pantalla:

A continuación, instanciamos nuestra máscara, la cual será una matriz de NumPy con el mismo ancho y alto de la imagen original. Como podemos ver, esta matriz contendrá sólo ceros, lo cual en RGB significa que es meramente un fondo negro.

Esta máscara es un rectángulo sólido de color blanco. 

Como el blanco está codificado por el valor 255, y 255 > 0, los píxeles contenidos en su área serán los únicos que conservaremos en la imagen original después de aplicar la máscara. 

Aprovechamos para mostrar la máscara en pantalla:

En el próximo extracto es donde, propiamente, enmascaramos la imagen original

En el artículo anterior estudiamos el uso de la función cv2.bitwise_and() para calcular el AND entre dos imágenes, píxel a píxel. 

Sin embargo, si usamos el parámetro mask, sólo consideraremos la región resultante de aplicar la máscara. 

Por tanto, si calculamos el AND de la imagen consigo misma, pero sólo dentro del área permitida por la máscara, el resultado será el deseado, el cual es conservar sólo aquellos píxeles cuyo valor es mayor a 0 en la máscara.

Reconozco que es una manera un poco truculenta de aplicar máscaras, pero da resultados:

Ahora repetimos el proceso, esta vez creando un nuevo lienzo de sólo ceros en el que trazamos un círculo blanco, implicando que queremos conservar los píxeles de la imagen original que están contenidos en esta circunferencia:

Aplicamos la máscara y mostramos el resultado:

¡ATENTO!

Antes de ejecutar el código, asegúrate de hallarte en la raíz del proyecto para que los comandos funcionen correctamente.

Para correr el código, primero colócate en la raíz del proyecto, y luego ejecuta este comando:

python datasmarts/masking.py

Primero veremos la imagen original:

Imagen original

Imagen original

Después, la máscara rectangular usada para aislar el carro en la foto:

Máscara rectangular

Máscara rectangular

El resultado es este:

Imagen después de usar la máscara para aislar el carro de la foto

Imagen después de usar la máscara rectangular para aislar el carro de la foto

Luego, aparecerá en pantalla la máscara circular destinada a extraer la porción de la imagen correspondiente al logo de Volkswagen:

Máscara circular

Máscara circular

He aquí el resultado:

Resultado después de usar la máscara circular en la imagen aislada

Resultado después de usar la máscara circular en la imagen aislada

Resumen

El enmascarado es una técnica muy poderosa, más incluso que el recorte. Aunque resulte abstracta en un principio, sus aplicaciones en la visión computarizada son innumerables. 

Estos son los puntos claves que debes recordar:

  • Una máscara es una imagen binaria.
  • La máscara debe tener las mismas dimensiones que la imagen sobre la que se va a aplicar.
  • Puedes crear una máscara con la función np.zeros().
  • Para aplicar una máscara, debes usar la función cv2.bitwise_and() así: cv2.bitwise_and(image, image, mask=mask).

¿Qué te pareció el artículo? Espero que haya sido de utilidad para ti. 

Puedes descargar el proyecto a continuación:

Hasta la próxima.

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.