Inpainting es una técnica de computer vision ideada para recuperar imágenes que se hayan visto alteradas por daños, desgaste o información faltante.
Si bien este es un tema que ya tocamos cuando hablamos sobre cómo llevar a cabo inapinting con OpenCV, me pareció una buena oportunidad para revisitar esta técnica, esta vez usando scikit-image, otra de las herramientas por excelencia en computer vision.Al final de este post sabrás:
¿Preparado? Genial, pongámonos manos a la obra.
¿Cómo Funciona el Inpainting?
Como bien mencionamos en el este artículo sobre inpainting en OpenCV, no existe un único mecanismo o un método universal de restauración de imágenes.
No obstante, en general, todos los algoritmos llevan a cabo variaciones de estos pasos:
- 1Las secciones dañadas se rellean usando información de los píxeles que las rodean.
- 2La estructura de color que rodea una zona afectada se extiende para ocupar el borde de la región faltante haciéndola, en efecto, más pequeña (imagina que las regiones dañadas se “rellenan” con el color de los píxeles vecinos).
- 3Se pintan los pequeños detalles en aras de preservar la uniformidad.
En scikit-image contamos únicamente con un método basado en ecuaciones biarmónicas, pero por fortuna, no necesitamos matricularnos en cálculo avanzado para poder hacer uso del mismo ?.
Sin nada más que agregar en lo que a teoría respecta, procedamos a implementar nuestra solución.
Creación del Entorno de Desarrollo con Anaconda
Echémosle un vistazo rápido a la estructura del proyecto:
Como notarás, el único archivo de interés es datasmarts/inpaint.py, pues allí vive nuestra implementación.
Las imágenes de prueba que usaremos para evaluar nuestra solución se encuentran en la carpeta resources.
Para crear el ambiente de Anaconda, corre este comando en tu terminal:
conda env create -f env.yml
Esta instrucción creará un ambiente llamado skimage-inpainting, correspondiente al siguiente archivo de configuración:
Lo que resta es activar el ambiente de la siguiente manera:
conda activate skimage-inpainting
Excelente, podemos continuar.
Cómo Restaurar Imágenes con Inpainting en scikit-image
Abre el archivo datasmarts/inpaint.py, e inserta las siguientes líneas para importar las dependencias:
Nuestro script es bastante ligero en lo que a dependencias se refiere, ya que únicamente nos valremos en OpenCV y scikit-image, aparte de ArgumentParser para, como de costumbre, definir el menú del programa.
El módulo inpaint ubicado en skimage.restoration contiene la función que implementa el método de inpainting biarmónico que veremos en breve.
En el siguiente extracto configuramos los argumentos de entrada de nuestro script:
Cargamos la imagen a reparar:
Y la máscara que nos servirá para ubicar las regiones de la imagen dañada a restaurar:
Nota que la máscara tuvimos que pasarla a escala de grises.
También vale mencionar que aunque usaremos scikit-image para aplicar el inpainting per se, nos valemos de OpenCV para cargar y mostrar las imágenes.
¿Por qué, te preguntas? Pues, honestamente, me parece más fácil trabajar con OpenCV en lo que a I/O de imágenes respecta, aunque bien podrías usar únicamente scikit-image de principio a fin si así lo prefieres.
Para llevar a cabo la restauración de la imagen como tal, llamamos a la función inpaint.inpaint_biharmonic(), teniendo especial cuidado de pasarle multichannel=True, puesto que estamos trabajando con imágenes a color:
Transformamos el resultado a un formato entendible por OpenCV (esto es, un arreglo multidimensional de enteros sin signo) mediante la función img_as_byte():
Por último, mostramos la imagen dañada, la máscara, y el resultado del inpainting:
Podemos correr el programa como sigue:
python ./datasmarts/inpaint.py -i ./resources/damaged.jpg -m ./resources/mask.jpg
Al cabo de unos pocos segundos veremos varias imágenes en pantalla. La primera es la foto que deseamos restaurar:
A mano derecha observamos varios trazos en blanco que claramente no tienen nada que ver con el paisaje de la foto.
Luego, nos toparemos con la máscara:
No es más que una imagen binaria en la que las zonas en blanco indican los píxeles en la imagen original que queremos recuperar.
Si te preguntas cómo obtuve esta máscara, simplemente usé Paint. Es un proceso bien tedioso, la verdad.
También se podría hacer de forma programática, aunque lo más probable es que me hubiera consumido más tiempo.
La última imagen que veremos es el resultado de aplicar inpainting:Si bien se taparon en su mayoría los trazos blancos que hice adrede, un vistazo más cercano revela que el resultado podría mejorar.
Para empezar, aún se nota un manchón blanco en la derecha. Este puede deberse a una limitación del algoritmo de inpainting, o a una mala máscara (en cuyo caso puedes echarme la culpa ?).
Luego, en el cielo y en las olas vemos que a pesar de haberse rellenado los daños con colores similares a los de los píxeles vecinos, no se alcanzó un resultado uniforme, homogéneo.
La conclusión es que el inpainting, al menos basado en algoritmos más artesanales como este, es una herramienta útil, aunque limitada.
Necesitamos más poder, ¿pero cómo?
¡Deep learning, por supuesto! Aunque eso ya es tema para otro artículo.
Resumen
El día de hoy aprendimos a aplicar inpainting o, en español, restauración de imágenes, usando scikit-image.
Siempre que queramos aplicar inpainting necesitaremos dos ingredientes:- 1Una imagen dañada que queramos restaurar.
- 2Una máscara binaria que indica las regiones de la imagen que se deben reparar.
Luego es cuestión de escoger entre los diversos métodos a nuestra disposición para llevar a cabo la tarea. No obstante, cabe mencionar que, al momento de escribir este artículo, scikit-image solo implementa inpainting basado en ecuaciones biarmónicas.
Si quieres más opciones, te recomiendo leer este artículo sobre inpainting en OpenCV, puesto que éste ofrece dos alternativas aparte de las ecuaciones biarmónicas.
Si deseas descargar el código del proyecto, puedes hacerlo en el formulario de abajo:
¡Hasta pronto!