Blog
Jerarquía de contornos
- Publicado por: Alberto Sosa-Costa
- Categoría: Blog OpenCV Procesamiento de Imagenes
Entender la jerarquía de contornos, es decir, la relación padre-hijo en Contornos.
Comprender el argumento modo de recuperación de contorno, de la función cv2.findContours() y sus posibles valores: RETR_LIST, RETR_EXTERNAL, RETR_CCOMP y RETR_TREE.
Jerarquía
Como se ha señalado anteriormente, el objetivo principal de la función cv2.findContours() es detectar objetos en una imagen. Aunque muchas veces los objetos están en lugares diferentes, en algunos casos, algunas formas están anidadas dentro de otras formas. En este último escenario, se llama padres (parents) a los contornos más externos e hijos (children) a los contornos contenidos en los anteriores. De esta manera, los contornos de una imagen tienen cierta relación entre sí. Utilizando esta definición se puede especificar cómo los contornos están conectados entre sí, por ejemplo, especificando si un contorno es hijo de otro, o por el contrario su padre. Es precisamente la representación de esta relación a lo que se llama Jerarquía.
A continuación se muestra un ejemplo donde esta idea queda más clara:
En esta imagen hay algunas formas, numeradas de 0 a 5. 2 y 2a denotan los contornos externo e interno de la caja más externa, respectivamente.
Aquí, los contornos 0,1,2 son externos o ultraperiféricos. Podemos decir que están en jerarquía-0 o simplemente que están en el mismo nivel de jerarquía.
Luego viene el contorno-2a, que puede considerarse como un hijo del contorno-2 (o en sentido opuesto, el contorno-2 es el padre del contorno-2a). Estos definen la jerarquía-1.
Del mismo modo el contorno-3 es hijo de contorno-2 y pertenece a la siguiente jerarquía, 2.
Finalmente, los contornos 4 y 5 son los hijos del contorno-3a, y forman el último nivel de jerarquía, 3.
Debido a la manera en que están eumeradas las cajas, se podría estar tentado a pensar que el contorno-4 es el primer hijo del contorno-3a. Sin emabrgo, el contorno-5 también podría ser el primer hijo.
Con este ejemplo quedan claros conceptos tales como: mismo nivel de jerarquía, contorno externo, contorno hijo, contorno padre, primer hijo, etc. Ahora estamos listos para ver estos conceptos en OpenCV.
Representación de Jerarquías en OpenCV
Como se ha visto, cada contorno tiene su propia información sobre a qué jerarquía pertenece, quién es su hijo, quién es su padre, etc. OpenCV representa esta información como una matriz de cuatro valores: [Next, Previous, First_Child, Parent]. Esta matriz es la tercera salida de la función cv2.findContours().
Next (siguiente)
Next indica el siguiente contorno en el mismo nivel jerárquico. Por ejemplo, consideremos el contorno-0 de la imagen anterior. ¿Cuál es el siguiente contorno en su mismo nivel? Es el contorno-1. Por lo tanto Next = 1. De manera similar, para contorno-1, el siguiente es contorno-2. Por consiguiente Next = 2.
En el caso del contorno-2, como no hay siguiente contorno en el mismo nivel, Next = -1. Por otra parte, el contorno-4, está en el mismo nivel del contorno-5. Así que su siguiente contorno es contorno-5, luego Next = 5.
Previous (anterior)
Previous indica el contorno anterior en el mismo nivel jerárquico. Por ejemplo, el contorno anterior del contorno-1 es el contorno-0 en el mismo nivel. Del mismo modo para el contorno-2, es el contorno-1. Para el contorno-0, no hay anterior, así que Previous=-1.
First_Child (primer hijo)
First_Child denota el primer contorno hijo. Por ejemplo, para contorno-2, el hijo es contorno-2a. Así que First_Child tendrá el valor de índice correspondiente del contorno-2a. Por otro lado, el contorno-3a tiene dos hijos. Sin embargo, sólo se toma el primer hijo, que es el contorno-4. Así que First_Child = 4 para el contorno-3a. Por último, si no hay hijos, como en el caso del contorno-4 o 5, First_Child=-1.
Parent (padre)
Parent indica el índice del controno padre. Es justo lo opuesto de First_Child. Tanto para el contorno-4 como para el contorno-5, el contorno padre es el contorno-3a. Para contorno-3a, es contorno-3 y así sucesivamente. Si no hay contorno padre, como en el caso del contorno-0, Parent=-1.
Modos de recuperación de contorno:
Ahora que ya sabemos sobre el estilo de jerarquía utilizado en OpenCV, podemos entender los modos de recuperación de controno en OpenCV, con la ayuda de la misma imagen anteriormente utilizada. Veamos el significado de las banderas cv2.RETR_LIST, cv2.RETR_TREE, cv2.RETR_CCOMP y cv2.RETR_EXTERNAL.
RETR_LIST
Esta es la más simple de las cuatro banderas (desde el punto de vista de la explicación). Simplemente, recupera todos los contornos, pero no crea ninguna relación padre-hijo. Los padres y los hijos son iguales bajo esta regla, y son sólo contornos. Es decir, todos ellos pertenecen al mismo nivel jerárquico.
Así que el 3er y 4to término en la matriz de jerarquía, correspondientes a First_Child y Parent, respectivamente, serán siempre -1. Obviamente, los términos Next y Previous tendrán sus valores correspondientes.
A continuación se muestra el resultado obtenido al utilizar la bandera RETR_LIST. En cada fila están los detalles de la jerarquía del contorno correspondiente. Por ejemplo, la primera fila corresponde al contorno-0. El siguiente contorno es el contorno-1. Así que Next = 1. No hay contorno anterior, así que Previous = -1. Los dos términos restantes, como se explicó anteriormente, serán -1.
print(hierarchy)
([[[ 1, -1, -1, -1], [ 2, 0, -1, -1], [ 3, 1, -1, -1], [ 4, 2, -1, -1], [ 5, 3, -1, -1], [ 6, 4, -1, -1], [ 7, 5, -1, -1], [-1, 6, -1, -1]]])
Esta es una buena opción, si no se está usando ninguna característica de jerarquía.
RETR_EXTERNAL
Si utiliza este indicador, sólo se devuelven indicadores externos extremos. Los contornos hijos no se tendrán en cuenta. Podemos decir, bajo esta ley, que sólo el mayor en cada familia es conservado.
Por lo tanto, en nuestra imagen, sólo tendremos 3 contornos, corrspondientes al nivel de jerarquía 0. Es decir, sólo se tendrán en cuenta los contornos 0,1,2. A continuación se muestra el resultado de utilizar esta bandera a la imagen de prueba:
print(hierarchy)
([[[ 1, -1, -1, -1], [ 2, 0, -1, -1], [-1, 1, -1, -1]]])
Este indicador es útil si se desea extraer sólo los contornos exteriores.
RETR_CCOMP
Este indicador recupera todos los contornos y los organiza en una jerarquía de dos niveles. Es decir, los contornos externos del objeto (es decir, su frontera) se colocan en la jerarquía-1. Y los contornos de los agujeros dentro del objeto (si los hay) se colocan en la jerarquía-2.
Basta con considerar la imagen de un “gran cero blanco” sobre un fondo negro. El círculo exterior al cero pertenece a la primera jerarquía, y el círculo interno del cero pertenece a la segunda jerarquía.
Para qe quede más clara la idea, se utilizará la siguiente imagen, donde se ha etiquetado el orden de los contornos en color rojo y la jerarquía a la que pertenece, en color verde (1 o 2). El orden es el mismo que el orden en el que OpenCV detecta los contornos.
Por ejemplo, en la imagen se puede ver que el contorno-0 es de jerarquía-1, ya que tiene dos agujeros, los contornos 1 y 2, que por lo tanto son de jerarquía-2. Bajo esta estructura, el contorno siguiente al contorno-0, en el mismo nivel de jerarquía, es el contorno-3, por tanto Next=3. Por otro lado, Previous=-1, ya que no existe un contorno anterior. Finalmente, su primer hijo es contorno-1 en jerarquía-2. No tiene padre, porque está en jerarquía-1. Así que su matriz jerárquica sería [3, -1,1, -1].
El contorno-1, por otra parte, está en jerarquía-2. El siguiente en la misma jerarquía (bajo la paternidad del contorno-1) es contorno-2. No tiene contorno anterior ni hijos, pero el padre es contorno-0. Así que la matriz de jerarquía será [2, -1, -1,0].
Del mismo modo para el contorno-2 se tendrá: jerarquía-2, no contorno siguiente, contorno anterior el contorno-1, ningún hijo y contorno padre el contorno-0. Por tanto la matriz será [-1,1, -1,0].
Contorno-3: Siguiente en jerarquía-1 es contorno-5. Anterior es el contorno-0. El hijo es contorno-4 y ningún padre. Así que la matriz será [5,0,4, -1].
Contorno-4: Está en jerarquía-2 bajo contorno-3 y no tiene hermano. Así que no hay siguiente, no hay anterior, no hay hijo y el padre es el contorno-3. Así que la matriz de jerarquía para este contorno será [-1, -1, -1,3].
Aplicando este indicador a la imagen de arriba, el resultado que se obtiene es el siguiente:
print(hierarchy)
([[[ 3, -1, 1, -1], [ 2, -1, -1, 0], [-1, 1, -1, 0], [ 5, 0, 4, -1], [-1, -1, -1, 3], [ 7, 3, 6, -1], [-1, -1, -1, 5], [ 8, 5, -1, -1], [-1, 7, -1, -1]]])
RETR_TREE
Este indicador recupera todos los contornos y crea una lista completa de jerarquías familiares. Incluso cuenta, quién es el abuelo, padre, hijo, nieto,etc.
Por ejemplo,utilicemos la misma imagen anterior pero ahora indicando la jerarquía que devuelve la bandera cv2.RETR_TREE. De nuevo, las letras rojas dan el número de contorno y las letras verdes dan el orden de la jerarquía.
Veamos algunos ejemplos:
contorno-0: Está en jerarquía-0. El siguiente contorno en la misma jerarquía es el contorno-7. No hay contornos anteriores. El hijo es el contorno-1 y no hay padre. Entonces la matriz de jerarquía es [7, -1,1, -1].
contorno-2: Está en jerarquía-1. No hay contornos en el mismo nivel. No anterior. El hijo es el contorno-2 y el padre es el contorno-0. Así que la matri para este contorno es [-1, -1,2,0].
Intenta obtener los restantes por tu cuenta y compara tus resultados con la respuesta correcta, que se muestra a continuación:
print(hierarchy)
([[[ 7, -1, 1, -1], [-1, -1, 2, 0], [-1, -1, 3, 1], [-1, -1, 4, 2], [-1, -1, 5, 3], [ 6, -1, -1, 4], [-1, 5, -1, 4], [ 8, 0, -1, -1], [-1, 7, -1, -1]]])
Contornos, histogramas, procesamientos, detección de rostros
y mucho más en nuestro curso Python de OpenCV:
Curso de Procesamiento de Imágenes y Visión Artificial
[…] ➡ Jerarquía de contornos […]