Publicado el Deja un comentario

¿Cómo segmentar una imagen utilizando contornos activos con Scikit-Image en Python?

Los contornos activos son un grupo de algoritmos que permiten realizar la segmentación de objetos de interés dentro de una imagen. Esto se consigue por medio de una curva denominada spline, cuya representación matemática permite que tenga una enorme flexibilidad. Esta flexibilidad es muy interesante para aproximar contornos de objetos que sean muy complejos. A menudo, a estas curvas de contornos activos se les denomina snakes.

Descripción del proceso de segmentación

En primer lugar, se inicializa una curva paramétrica en un punto determinado (que se puede fijar manualmente) y se definen sus valores iniciales. Esto, básicamente, nos permitirá determinar dónde queremos que se sitúe el contorno activo y las características de forma que deseamos que tenga.

En segundo lugar, se determina cómo queremos que se comporte el snake. Por decirlo de alguna forma, vamos a coger el snake, vamos a «soltarlo», dejar que busque el contorno deseado y que poco a poco se vaya ajustando. Este proceso de ajuste será iterativo, lo cual quiere decir que, si los parámetros que hemos definido son adecuados para el problema, el snake se irá aproximando cada vez más al contorno del objeto de interés a lo largo de las iteraciones. Para definir ese comportamiento, debemos especificar dos funciones de lo que denominamos como energía.

  • Energía interna: se refiere a la flexibilidad que queremos darle a la curva.
  • Energía externa: se refiere a características de la imagen que son de interés. Por medio de este valor de energía, le decimos al snake dónde está el contorno para conseguir que se aproxime lo máximo posible a él. Un ejemplo de energía externa podría ser la información de bordes o líneas de la imagen.

Para ajustar el modelo al contorno deseado, se plantea un problema de optimización donde se busca minimizar los valores de energía. Para llevar a cabo esa minimización, primero se realiza una suma ponderada de ambos valores (lo que se conoce como energía total). Con esta suma ponderada, se controla si se desea otorgar un mayor peso a la energía externa o a la interna, según sea el caso. Lo más común, es que haya que probar varios valores y ver qué conjunto de ellos resuelve mejor el problema.

Snakes en Scikit-Image

Una vez que hemos visto un poco de la teoría sobre los snakes, vamos a pasar directamente a ver cómo se implementa su uso en un problema práctico utilizando, en este caso, el lenguaje Python con la ayuda de la librería de Scikit-Image. Para ejemplificarlo con un caso sencillo y que funcione relativamente bien de forma bastante directa, vamos a tomar como referencia la siguiente imagen.

Este ejemplo será fácil de segmentar, ya que es fácil distinguir el objeto de interés (la TIerra) del fondo de la imagen

A continuación, se encuentra todo el código para poder segmentar una imagen de ejemplo.

Vamos a explicar paso a paso qué es lo que se hace en este código.

En primer lugar, se importan las librerías necesarias

En segundo lugar, especificamos los parámetros para inicializar el snake

En este caso, hemos decidido inicializar el snake como un círculo, que debe contener totalmente el objeto de interés. La imagen en cuestión, tiene una resolución de 300 x 300, por lo que un círculo de 150 píxeles de radio es más que suficiente para conseguirlo. Por otra parte, el centro del snake coincidirá exactamente con el centro de la imagen, el punto (150, 150). Además, como otro parámetro del snake, la curva estará formada por 350 puntos. Para crear el círculo, se utiliza la función «linspace()» de Numpy. Esto se almacena en una variable «init» que podrá ser utilizada como forma inicial del snake para el proceso de segmentación.

En tercer lugar, se lee la imagen, se aplica la función de contornos activos y se muestra el resultado

Leemos la imagen con la función «imread()» de Scikit-Image y almacenamos el contenido de esa imagen en una matriz de Numpy. Se lo pasamos a la función «active_contour()» junto con el «init» (es decir, la forma inicial del snake) y otra serie de parámetros que resultará interesante ajustar para modificar el comportamiento del algoritmo.

  • alpha: determina la magnitud con la que queremos que el snake se contraiga.
  • beta: determina la suavidad de la curva.
  • w_line: determina el peso que se le quiere dar a la información de las líneas de la imagen en el valor de energía total.
  • w_edge: determina el peso que se le quiere dar a la información de bordes en el valor de energía total.
  • max_iterations: especifica el número máximo de iteraciones en las que el snake podrá ajustarse.

Por último, con las funciones de Matplotlib, se podrá mostrar el resultado. Con los parámetros elegidos, obtendremos algo como lo que se puede ver a continuación.

La curva verde es el contorno inicial del snake, mientras que la curva roja es el resultado final del proceso de segmentación con el modelo deformable. Como se puede observar, el contorno rojo se ajusta bastante bien al objeto de interés, lo cual es exactamente lo que estábamos buscando.

Eso sí, aunque en este caso los resultados sean muy buenos, no te esperes que siempre ocurra así. Esta imagen es muy fácil de segmentar (incluso con otras técnicas más básicas que los modelos deformables) pero lo más seguro es que, en el caso de los objetos de la vida real, no sea ni mucho menos tan sencillo de conseguir. De hecho, si no consigues buenos resultados probando con una gran cantidad de conjuntos de parámetros, lo más probable es que los modelos deformables no sean el mejor método para el problema particular que estás tratando.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *