Extracción de segmentos de una lista de 8 píxeles conectados


situación Actual : estoy tratando de extraer segmentos de una imagen. Gracias al método findContours() de OpenCV, ahora tengo una lista de 8 puntos conectados para cada contorno. Sin embargo, estas listas no son directamente utilizables, porque contienen una gran cantidad de duplicados.

El problema : Dada una lista de 8 puntos conectados, que pueden contener duplicados, extraiga segmentos de ella.

Posibles soluciones:

  • Al principio, usé el {[4] de OpenCV]} método. Sin embargo, los resultados son bastante malos... Aquí están los contornos ampliados:

introduzca la descripción de la imagen aquí

Aquí está el resultado de approxPolyDP() : (9 segmentos! Algunas superposiciones)

introduzca la descripción de la imagen aquí

Pero lo que quiero es más como : {[16]]}

introduzca la descripción de la imagen aquí

Es malo porque approxPolyDP() puede convertir algo que "parece varios segmentos" en "varios segmentos". Sin embargo, lo que tengo es una lista de puntos que tienden a repetirse varias veces sobre sí mismos.

Por ejemplo, si mis puntos son:

0 1 2 3 4 5 6 7 8 
  9   

Entonces, la lista de puntos será 0 1 2 3 4 5 6 7 8 7 6 5 4 3 2 1 9... Y si el número de puntos se hace grande (>100) entonces los segmentos extraídos por approxPolyDP() desafortunadamente no son duplicados (es decir : se superponen entre sí, pero no son estrictamente iguales, por lo que no puedo decir simplemente "eliminar duplicados", en lugar de píxeles, por ejemplo)

  • Tal vez, tengo una solución, pero es bastante largo (aunque interesante). En primer lugar, para toda la lista de 8 conectados, yo crear un matrix (para eficiencia) y establezca los valores de la matriz en 1 si el píxel pertenece a la lista. Luego, creo un gráfico , con nodos correspondientes a píxeles, y bordes entre píxeles vecinos. Esto también significa que yo añado todos los bordes faltantes entre píxeles (complejidad pequeña, posible debido a la matriz dispersa). Entonces yo remove all possible "squares" (4 neighouring nodes), y esto es posible porque ya estoy trabajando en contornos bastante finos. Entonces puedo inicie un algoritmo minimal spanning tree. Y finalmente, puedo aproximar cada rama del árbol con approxPolyDP()

Segmentación http://img197.imageshack.us/img197/4488/segmentation.png

Aquí hay fotos fantásticas (gracias Paint!) de la lista original, y el gráfico asociado. Luego, cuando agrego bordes entre vecinos. Y finalmente, cuando elimino bordes y hago un árbol de expansión mínimo (no es útil aquí)

Para resumir: Tengo un método tedioso, que aún no he implementado, ya que parece propenso a errores. Sin embargo, les pregunto a ustedes , la gente de StackOverflow: ¿hay otros métodos existentes, posiblemente con buenas implementaciones?


Editar: Para aclarar, una vez que tengo un árbol, puedo extraer "ramas" (las ramas comienzan en hojas o nodos vinculados a 3 o más nodos) Entonces, el algoritmo en el approxPolyDP() de OpenCV es el algoritmo Ramer–Douglas–Peucker, y aquí está la imagen de Wikipedia de lo que hace :

introduzca la descripción de la imagen aquí

Con esta imagen, es fácil entender por qué falla cuando los puntos pueden ser duplicados entre sí{[16]]}


Otra edición : En mi método, hay algo que puede ser interesante notar. Cuando se consideran los puntos ubicados en una cuadrícula (como píxeles), entonces generalmente, el algoritmo de árbol de expansión mínima no es útil porque hay muchos árboles mínimos posibles

X-X-X-X
|
X-X-X-X

Es fundammentally muy diferente de{[16]]}

X-X-X-X
| | | |
X X X X

Pero ambos son árboles de expansión mínima

Sin embargo, en mi caso, mis nodos rara vez forman clústeres porque se supone que son contornos, y ya hay un algoritmo de adelgazamiento que se ejecuta de antemano en el findContours().


Respuesta al comentario de Tomalak:

introduzca la descripción de la imagen aquí

Si el algoritmo DP devuelve 4 segmentos (el segmento desde el punto 2 al centro está allí dos veces) ¡estaría feliz! Por supuesto, con buenos parámetros, puedo llegar a un estado donde" por casualidad " tengo segmentos idénticos, y puedo eliminar duplicados. Sin embargo, claramente, el algoritmo no está diseñado para ello.

Aquí hay un ejemplo real con demasiados segmentos:

introduzca la descripción de la imagen aquí

Author: Fezvez, 2011-06-20

4 answers

Usando Mathematica 8, creé un gráfico morfológico de la lista de píxeles blancos en la imagen. Está funcionando bien en su primera imagen:

introduzca la descripción de la imagen aquí

introduzca la descripción de la imagen aquí

Crear el gráfico morfológico:

graph = MorphologicalGraph[binaryimage];

Luego puede consultar las propiedades del gráfico que le interesan.

Esto da los nombres del vértice en el gráfico:

vertex = VertexList[graph]

La lista de los bordes:

EdgeList[graph]

Y eso da las posiciones del vértice:

pos = PropertyValue[{graph, #}, VertexCoordinates] & /@ vertex

Esto es cómo se ven los resultados de la primera imagen:

In[21]:= vertex = VertexList[graph]

Out[21]= {1, 3, 2, 4, 5, 6, 7, 9, 8, 10}

In[22]:= EdgeList[graph]

Out[22]= {1 \[UndirectedEdge] 3, 2 \[UndirectedEdge] 4,  3 \[UndirectedEdge] 4, 
          3 \[UndirectedEdge] 5, 4 \[UndirectedEdge] 6,  6 \[UndirectedEdge] 7, 
          6 \[UndirectedEdge] 9, 8 \[UndirectedEdge] 9,  9 \[UndirectedEdge] 10}

In[26]:= pos = PropertyValue[{graph, #}, VertexCoordinates] & /@ vertex

Out[26]= {{54.5, 191.5}, {98.5, 149.5},  {42.5, 185.5}, 
          {91.5, 138.5}, {132.5, 119.5}, {157.5, 72.5},
          {168.5, 65.5}, {125.5, 52.5},  {114.5, 53.5}, 
          {120.5, 29.5}}

Dada la documentación, http://reference.wolfram.com/mathematica/ref/MorphologicalGraph.html , el comando MorphologicalGraph primero calcula el esqueleto por adelgazamiento morfológico:

skeleton = Thinning[binaryimage, Method -> "Morphological"]

Entonces se detectan los vértices; son los puntos de ramificación y los puntos finales:

verteximage = ImageAdd[
                  MorphologicalTransform[skeleton, "SkeletonEndPoints"],   
                  MorphologicalTransform[skeleton, "SkeletonBranchPoints"]]

introduzca la descripción de la imagen aquí

Y luego los vértices se enlazan después del análisis de su conectividad.

Por ejemplo, uno podría comenzar al romper la estructura alrededor del vértice y luego buscar los componentes conectados, revelando los bordes del gráfico:

comp = MorphologicalComponents[
           ImageSubtract[
               skeleton, 
               Dilation[vertices, CrossMatrix[1]]]];
Colorize[comp] 

introduzca la descripción de la imagen aquí

El diablo está en los detalles, pero eso suena como un punto de partida sólido si desea desarrollar su propia implementación.

 16
Author: Matthias Odisio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-28 17:03:30

Pruebe la morfología matemática. Primero necesitas dilate o close tu imagen para rellenar agujeros.

cvDilate(pimg, pimg, NULL, 3);
cvErode(pimg, pimg, NULL);

Tengo esta imagen

introduzca la descripción de la imagen aquí

El siguiente paso debería ser aplicar algoritmo de adelgazamiento. Desafortunadamente no está implementado en OpenCV (MATLAB tiene bwmorph con el argumento thin). Por ejemplo, con MATLAB refiné la imagen a esta:

introduzca la descripción de la imagen aquí

Sin embargo, OpenCV tiene todas las operaciones morfológicas básicas necesarias para implementar el adelgazamiento (cvMorphologyEx, cvCreateStructuringElementEx, sucesivamente).

Otra idea.

Dicen que la transformación a distancia parece ser muy útil en tales tareas. Puede ser. Considere la función cvDistTransform. Crea una imagen así:

introduzca la descripción de la imagen aquí

Luego usando algo como cvAdaptiveThreshold:

introduzca la descripción de la imagen aquí

Eso es esqueleto. Supongo que puedes iterar sobre todos los píxeles blancos conectados, encontrar curvas y filtrar segmentos pequeños.

 10
Author: Andrey Sboev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-20 13:46:42

He implementado un algoritmo similar antes, y lo hice de una manera de mínimos cuadrados incrementales. Funcionó bastante bien. El pseudocódigo es algo así como:

L = empty set of line segments
for each white pixel p
  line = new line containing only p
  C = empty set of points
  P = set of all neighboring pixels of p
  while P is not empty
    n = first point in P
    add n to C
    remove n from P
    line' = line with n added to it
    perform a least squares fit of line'
    if MSE(line) < max_mse and d(line, n) < max_distance
      line = line'
      add all neighbors of n that are not in C to P
  if size(line) > min_num_points
    add line to L

Donde MSE(línea) es la media-cuadrado-error de la línea (suma sobre todos los puntos en la línea de la distancia cuadrada a la línea que mejor encaja) y d(línea,n) es la distancia desde el punto n a la línea. Los buenos valores para max_distance parecen ser un píxel o algo así y max_mse parece ser mucho menor, y dependerá de la media tamaño de los segmentos de línea en la imagen. 0.1 o 0.2 píxeles han funcionado en imágenes bastante grandes para mí.

Había estado usando esto en imágenes reales preprocesadas con el operador Canny, por lo que los únicos resultados que tengo son de eso. Aquí está el resultado del algoritmo anterior en una imagen: Imagen RawSegmentos detectados

También es posible hacer que el algoritmo sea rápido. La implementación de C++ que tengo (código cerrado impuesto por mi trabajo, lo siento, de lo contrario te lo daría) procesó la imagen de arriba en aproximadamente 20 milisegundo. Eso incluye la aplicación del operador Canny para la detección de bordes, por lo que debería ser aún más rápido en su caso.

 6
Author: Sean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-30 21:03:04

Puede comenzar extrayendo líneas rectas de su imagen de contornos usando HoughLinesP que se proporciona con OpenCV:

HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength = 0, double maxLineGap = 0)  

Si elige threshold = 1 y minLineLenght small, incluso puede obtener todos los elementos individuales. Sin embargo, tenga cuidado, ya que produce muchos resultados en caso de que tenga muchos píxeles de borde.

 0
Author: Sebastian-CV-ML,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-13 16:43:14