Una descripción general de los métodos de segmentación de imágenes en la biblioteca de imágenes scikit

Umbral


Esta es la forma más fácil de separar objetos del fondo seleccionando píxeles por encima o por debajo de un cierto umbral. Esto suele ser útil cuando vamos a segmentar objetos por su fondo. Puedes leer más sobre el umbral aquí .

Las personas familiarizadas con la película Terminator probablemente estarán de acuerdo en que fue la mejor película de ciencia ficción de la época. En la película, James Cameron introdujo un concepto interesante de efectos visuales, que permitió a los espectadores esconderse detrás de los ojos de un cyborg llamado Terminator. Este efecto se conoce como "Terminator Vision" (Inglés Terminator Vision). En cierto sentido, separó las siluetas de personas del fondo. Esto puede sonar completamente inapropiado entonces, pero la segmentación de imágenes es una parte importante de muchas técnicas de procesamiento de imágenes en la actualidad.

Segmentación de imagen


Hay varias bibliotecas escritas para el análisis de imágenes. En este artículo, analizaremos en detalle scikit-image, una biblioteca de procesamiento de imágenes de Python.

Scikit-image


imagen

Scikit-image es una biblioteca de Python para el procesamiento de imágenes.

Instalación


scikit-image se instala de la siguiente manera:

pip install -U scikit-image(Linux and OSX) pip install scikit-image(Windows) # For Conda-based distributions conda install scikit-image 



Descripción general de la imagen de Python


Antes de pasar a los aspectos técnicos de la segmentación de imágenes, es importante familiarizarse un poco con el ecosistema de imágenes Scikit y cómo procesa las imágenes.

Importar imagen GrayScale de la biblioteca de skimage


El módulo de datos de skimage contiene varios ejemplos integrados de conjuntos de datos, que generalmente se almacenan en formato jpeg o png.

 from skimage import data import numpy as np import matplotlib.pyplot as plt image = data.binary_blobs() plt.imshow(image, cmap='gray') 

Importar imagen en color de la biblioteca de skimage


 from skimage import data import numpy as np import matplotlib.pyplot as plt image = data.astronaut() plt.imshow(image) 



Importar una imagen desde una fuente externa


 # The I/O module is used for importing the image from skimage import data import numpy as np import matplotlib.pyplot as plt from skimage import io image = io.imread('skimage_logo.png') plt.imshow(image); 



Descargar múltiples imágenes


 images = io.ImageCollection('../images/*.png:../images/*.jpg') print('Type:', type(images)) images.files Out[]: Type: <class 'skimage.io.collection.ImageCollection'> 

Guardar imágenes


 #Saving file as 'logo.png' io.imsave('logo.png', logo) 

Segmentación de imagen


Ahora que tenemos una idea de scikit-image, ofrecemos considerar los detalles de la segmentación de imágenes. La segmentación de imágenes es el proceso de dividir una imagen digital en varios segmentos para simplificar y / o cambiar la representación de la imagen a algo más significativo y más fácil de analizar.

En este artículo, consideraremos algoritmos para modelos enseñados tanto con un maestro (supervisado) como sin un maestro (sin supervisión).


Algunos de los algoritmos de segmentación están disponibles en la biblioteca de imágenes scikit.

Segmentación con un profesor: algunos conocimientos preliminares, posiblemente a partir de aportes humanos, se utilizan para guiar el algoritmo.

Segmentación sin profesor: no se requieren conocimientos previos. Estos algoritmos intentan dividir automáticamente las imágenes en áreas significativas. El usuario aún puede configurar ciertos parámetros para obtener los resultados deseados.

Probemos esto en una imagen tutorial que viene con un conjunto de datos de imagen scikit predefinido.

Importación regular


 import numpy as np import matplotlib.pyplot as plt import skimage.data as data import skimage.segmentation as seg import skimage.filters as filters import skimage.draw as draw import skimage.color as color 

Característica de imagen simple

 def image_show(image, nrows=1, ncols=1, cmap='gray'): fig, ax = plt.subplots(nrows=nrows, ncols=ncols, figsize=(14, 14)) ax.imshow(image, cmap='gray') ax.axis('off') return fig, ax 

Imagen


 text = data.page() image_show(text) 



Esta imagen es un poco más oscura, pero quizás aún podamos elegir un valor que nos dé una segmentación razonable sin algoritmos complicados. Ahora, para ayudarnos a elegir este valor, utilizaremos un histograma.

En este caso, el histograma muestra el número de píxeles en la imagen con diferentes intensidades encontradas en esta imagen. En pocas palabras, un histograma es un gráfico en el que el eje X muestra todos los valores que están en la imagen, y el eje Y muestra la frecuencia de estos valores.

 fig, ax = plt.subplots(1, 1) ax.hist(text.ravel(), bins=32, range=[0, 256]) ax.set_xlim(0, 256); 



Nuestro ejemplo resultó ser una imagen de 8 bits, por lo que tenemos 256 valores posibles a lo largo del eje X. El histograma muestra que hay una concentración de píxeles bastante brillantes (0: negro, 255: blanco). Este es probablemente nuestro fondo de texto bastante claro, pero el resto es un poco borroso. Un histograma de segmentación ideal sería bimodal para que podamos seleccionar un número justo en el medio. Ahora intentemos crear algunas imágenes segmentadas basadas en un valor umbral simple.

Umbral controlado


Como nosotros mismos elegimos un valor umbral, lo llamamos un valor umbral controlado.

 text_segmented = text > (value concluded from histogram ie 50,70,120 ) image_show(text_segmented); 


Izquierda: texto> 50 | Mediados: texto> 70 | Derecha: texto> 120

No obtuvimos resultados perfectos, ya que la sombra de la izquierda crea problemas. Probemos con el umbral desatendido ahora.

Umbral no controlado


Umbral no controlado Scikit-image tiene una serie de métodos automáticos de determinación de umbral que no requieren entrada al elegir el umbral óptimo. Estos son algunos de los métodos: otsu, li, local.

 text_threshold = filters.threshold_ # Hit tab with the cursor after the underscore to get all the methods. image_show(text < text_threshold); 


Izquierda otsu || Derecha: li

En el caso de local, también necesitamos especificar block_size. La compensación ayuda a ajustar la imagen para obtener mejores resultados.

 text_threshold = filters.threshold_local(text,block_size=51, offset=10) image_show(text > text_threshold); 



Este método da un muy buen efecto. En gran medida, uno puede deshacerse de las regiones ruidosas.

Segmentación con un algoritmo para un modelo con un profesor.


El umbral es un proceso de segmentación muy simple, y no funcionará correctamente en una imagen de alto contraste, para lo cual necesitaremos herramientas más avanzadas.

En esta sección, utilizaremos una imagen de ejemplo que está disponible gratuitamente y trataremos de segmentar la parte de la cabeza utilizando métodos con el maestro.

 # import the image from skimage import io image = io.imread('girl.jpg') plt.imshow(image); 



Antes de hacer cualquier segmentación de imagen, se recomienda eliminar el ruido utilizando algunos filtros.

Sin embargo, en nuestro caso, la imagen no tiene un ruido significativo, por lo que la aceptaremos tal cual. El siguiente paso es convertir la imagen a escala de grises usando rgb2gray.

 image_gray = color.rgb2gray(image) image_show(image_gray); 



Utilizaremos dos métodos de segmentación que funcionan con principios completamente diferentes.

Segmentación activa del contorno


La segmentación de la ruta activa también se llama serpiente y se inicializa usando una ruta o línea definida por el usuario alrededor de una región de interés, y luego esta ruta se comprime lentamente y es atraída o repelida por la luz y los bordes.

Para nuestra imagen de ejemplo, dibujemos un círculo alrededor de la cabeza humana para inicializar la serpiente.

 def circle_points(resolution, center, radius): """ Generate points which define a circle on an image.Centre refers to the centre of the circle """ radians = np.linspace(0, 2*np.pi, resolution) c = center[1] + radius*np.cos(radians)#polar co-ordinates r = center[0] + radius*np.sin(radians) return np.array([c, r]).T # Exclude last point because a closed path should not have duplicate points points = circle_points(200, [80, 250], 80)[:-1] 

Los cálculos anteriores calculan las coordenadas xey de los puntos en la periferia del círculo. Como le dimos una resolución de 200, calculará 200 de esos puntos.

 fig, ax = image_show(image) ax.plot(points[:, 0], points[:, 1], '--r', lw=3) 



Luego, el algoritmo segmenta la cara de la persona del resto de la imagen, ajustando una curva cerrada a los bordes de la cara.



Podemos configurar parámetros llamados alfa y beta. Los valores alfa más altos hacen que la curva se contraiga más rápido, mientras que la beta hace que la curva sea más suave.

 snake = seg.active_contour(image_gray, points,alpha=0.06,beta=0.3) fig, ax = image_show(image) ax.plot(points[:, 0], points[:, 1], '--r', lw=3) ax.plot(snake[:, 0], snake[:, 1], '-b', lw=3); 



Segmentación aleatoria del caminante


En este método, la segmentación se lleva a cabo mediante el etiquetado interactivo, que se denomina etiquetas. Al dibujar cada píxel en la etiqueta para la que se calcula la mayor probabilidad, puede obtener una segmentación de imagen de alta calidad. Se pueden encontrar más detalles sobre este método en este trabajo.

A continuación, utilizaremos nuevamente los valores anteriores de nuestro ejemplo. Podríamos haber hecho diferentes inicializaciones, pero por simplicidad, ceñámonos al principio de los círculos.

 image_labels = np.zeros(image_gray.shape, dtype=np.uint8) 

El algoritmo de paso aleatorio acepta etiquetas como entrada. Por lo tanto, tendremos un círculo grande que cubra toda la cara de la persona, y otro círculo más pequeño cerca del centro de la cara.

 indices = draw.circle_perimeter(80, 250,20)#from here image_labels[indices] = 1 image_labels[points[:, 1].astype(np.int), points[:, 0].astype(np.int)] = 2 image_show(image_labels); 


Ahora usemos Random Walker y veamos qué sucede.

 image_segmented = seg.random_walker(image_gray, image_labels) # Check our results fig, ax = image_show(image_gray) ax.imshow(image_segmented == 1, alpha=0.3); 



El resultado no es el mejor, los bordes de la cara permanecieron inalcanzados. Para corregir esta situación, podemos ajustar el parámetro de pasaje hasta obtener el resultado deseado. Después de varios intentos, establecemos el valor en 3000, que funciona bastante bien.

 image_segmented = seg.random_walker(image_gray, image_labels, beta = 3000) # Check our results fig, ax = image_show(image_gray) ax.imshow(image_segmented == 1, alpha=0.3); 



Todo esto es para la segmentación con el profesor, donde tuvimos que proporcionar ciertos datos de entrada, así como configurar algunos parámetros. Sin embargo, no siempre es posible que una persona mire la imagen y luego decida qué contribución dar y por dónde comenzar. Afortunadamente, para tales situaciones, tenemos métodos de segmentación no controlados.

Segmentación sin profesor


La segmentación sin un maestro no requiere conocimiento previo. Considere una imagen que es tan grande que es imposible ver todos los píxeles a la vez. Por lo tanto, en tales casos, la segmentación sin un maestro puede dividir la imagen en varias subregiones, por lo que en lugar de millones de píxeles, tiene decenas o cientos de áreas. Veamos dos de estos algoritmos:

Agrupación iterativa lineal simple


El método (inglés Simple Linear Iterative Clustering o SLIC) utiliza un algoritmo de aprendizaje automático llamado K-Means. Toma todos los valores de píxeles de la imagen e intenta dividirlos en un número determinado de subdominios. Lea este trabajo para obtener información detallada.

SLIC funciona con diferentes colores, por lo que utilizaremos la imagen original.

 image_slic = seg.slic(image,n_segments=155) 

Todo lo que necesitamos hacer es simplemente establecer un valor promedio para cada segmento que encontremos, lo que hace que se parezca más a una imagen.

 # label2rgb replaces each discrete label with the average interior color image_show(color.label2rgb(image_slic, image, kind='avg')); 



Redujimos esta imagen de 512 * 512 = 262,000 píxeles a 155 segmentos.

Felzenszwalb


Este método también utiliza un algoritmo de aprendizaje automático denominado agrupamiento de árbol de mínima extensión. Felzenszwaib no nos dice el número exacto de grupos en los que se dividirá la imagen. Generará todos los grupos que considere adecuados para esto.

 image_felzenszwalb = seg.felzenszwalb(image) image_show(image_felzenszwalb); 



Hay demasiadas regiones en la imagen. Vamos a contar la cantidad de segmentos únicos.

 np.unique(image_felzenszwalb).size 3368 

Ahora volvamos a colorearlos usando el valor promedio sobre el segmento, como lo hicimos en el algoritmo SLIC.

 image_felzenszwalb_colored = color.label2rgb(image_felzenszwalb, image, kind='avg') image_show(image_felzenszwalb_colored); 

Ahora tenemos menos segmentos. Si quisiéramos incluso menos segmentos, podríamos cambiar el parámetro de escala. Este enfoque a veces se llama sobre segmentación.



Esto es más como una imagen posterizada, que es esencialmente solo una reducción en la cantidad de colores. Para combinarlos de nuevo (RAG).

Conclusión


La segmentación de imágenes es un paso muy importante en el procesamiento de imágenes. Esta es un área activa de investigación con una variedad de aplicaciones, que van desde visión por computadora hasta imágenes médicas, tráfico y video vigilancia. Python proporciona una biblioteca robusta de imágenes scikit con una gran cantidad de algoritmos de procesamiento de imágenes. Está disponible de forma gratuita y sin restricciones, respaldado por una comunidad activa. Le recomiendo que lea su documentación. El artículo original se puede encontrar aquí .

Source: https://habr.com/ru/post/441006/


All Articles