? CONTANDO OBJETOS (Aplicando detección de bordes con CANNY) en Python-OpenCV

Por Administrador

En el post anterior habíamos hablado de como contar objetos usando  threshold o umbralización simple, ahora vamos a hacer algo similar con la diferencia que estaremos usando detección de bordes para encontrar los objetos (cartas o barajas) que deseamos contar.

El proceso será el siguiente:

  1. Leer la imagen de entrada.
  2. Transformarla a escala de grises.
  3. Aplicar detección de bordes con cv2.Canny.
  4. Buscar los contornos correspondientes a los objetos (cartas).
  5. Contar/Enumerar cada uno de los objetos (cartas) encontradas.

Leer la imagen de entrada

En esta ocasión vamos a usar la siguiente imagen, de la cual debemos enumerar las 4 cartas o barajas que aparecen en ella.

Figura 1: Imagen de entrada

import cv2

imagen = cv2.imread('cartas.png')

Y empezamos importando OpenCV con import cv2, ahora leeremos la imagen que vamos a analizar, en este caso cartas.png, y procedemos a visualizarla, todo este proceso ya lo hemos hecho anteriormente en los videos. 

Transformar una imagen de BGR a escala de grises

Para la transformación de BGR a escala de grises usaremos cv2.cvtColor, de la siguiente manera:

grises = cv2.cvtColor(imagen, cv2.COLOR_BGR2GRAY)

Figura 2: Imagen resultante luego de aplicar escala de grises, a la imagen de la figura 1.

Aplicar detección de bordes con cv2.Canny

Para poder realizar la detección de bordes, vamos a usar la función cv2.Canny. Esta función nos pide:

  • Imagen de entrada
  • Primer umbral (minVal)
  • Segundo umbral (maxVal)

Puedes ingresar otros datos, para saber más sobre ellos puedes visitar este enlace, y si deseas estudiar el proceso que realiza esta función te dejo este otro.

Para que los bordes se dibujen o no, se necesita de dos umbrales, minVal y maxVal. De este modo, todos los bordes superiores a maxVal se dibujarán. Los bordes que estén dentro del maxVal y minVal se dibujarán solo si se encuentran conectados a los que superan maxVal. Por el contrario todos los bordes por debajo de minVal no serán dibujados. Veamos la siguiente imagen:

Figura 3: Ejemplo de bordes que se dibujarían o no, según los valores minVal y maxVal. (Imagen original)

La figura 3 muestra una imagen que tomé de la documentación de OpenCV, la cual he separado en 3 colores. El color verde correspondiente a valores mayores a maxVal indica todos los bordes que serán dibujados, en este caso A. Luego en el color naranja están los bordes que se encuentran dentro de minVal y maxVal, en donde se dibujarán los bordes que se encuentren conectados a los que superen maxVal, por ello C se dibujará, mientras que el borde B no se dibujará. Por último el área roja indica los valores menores a minVal, los cuales no serán dibujados.

Una vez que tengamos claro el uso de estos dos umbrales podemos hacer pruebas para analizar como se van dibujando los distintos bordes dada una imagen. Para el caso de enumeración de cartas o barajas que estamos viendo hoy, se ha especificado:

bordes = cv2.Canny(grises, 100, 200)

De este modo obtendremos la siguiente imagen:

Figura 4: Detección de bordes aplicado a la imagen de entrada (Figura 1).

NOTA: Puedes cambiar los valores de los umbrales de tal manera que puedas conseguir los que mejor se adapten a tu proyecto.

Encontrar y dibujar los contornos de la imagen binaria

Una vez que tenemos la imagen binaria, correspondiente a la detección de bordes, podemos aplicar la función cv2.findContours. De este modo podremos encontrar las 4 cartas/barajas que se encuentran en la imagen. Este procedimiento también se lo realizó en el post anterior.

#Para OpenCV3
_, ctns, _ = cv2.findContours(bordes, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
#Para OpenCV4
ctns, _ = cv2.findContours(bordes, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

Una vez obtenidos los contornos, procedemos a dibujarlos con cv2.drawContours (Si quieres más información de esta función, visita este post).

cv2.drawContours(imagen, ctns, -1, (0,0,255), 2)

Gracias a esta función se dibujarán todos los contornos encontrados de color rojo con un grosor de 2 pixeles en imagen. Veamos:

Figura 5: Contornos correspondientes a las cartas/barajas dibujados.

Contar / enumerar cada uno de los objetos (cartas) encontradas

Para poder contar cada uno de los objetos encontrados, podríamos imprimir la cantidad de elementos almacenados en cnts, que corresponderán al número total de cartas encontradas:

print('Número de contornos encontrados: ', len(ctns))

En este caso se imprimirá el número de contornos encontrados:

Figura 6: Resultado de imprimir el número total de contornos encontrados.

Ahora con esta información no nos queda más que imprimirla en la imagen.

texto = 'Contornos encontrados: '+ str(len(ctns))
cv2.putText(imagen, texto, (10,20), cv2.FONT_HERSHEY_SIMPLEX, 0.7,
  (255, 0, 0), 1)

cv2.imshow('Imagen', imagen)
cv2.waitKey(0)
cv2.destroyAllWindows()

En la línea 16 estoy convirtiendo el número de contornos (len(cnts)) a string y uniéndolo a un mensaje ('Contornos encontrados: '), esto es necesario para que se pueda visualizar en la función cv2.putText. Si tienes un poco de problemas con esta función te recomiendo este post.

Y este sería el resultado final:

Figura 7: Imagen final obtenida.

A continuación te dejo todo el programa. ¡Espero que te sirva!.

import cv2

imagen = cv2.imread('cartas.png')
grises = cv2.cvtColor(imagen, cv2.COLOR_BGR2GRAY)
bordes = cv2.Canny(grises, 100, 200)

#Para OpenCV3
#_, ctns, _ = cv2.findContours(bordes, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

#Para OpenCV4
ctns, _ = cv2.findContours(bordes, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

cv2.drawContours(imagen, ctns, -1, (0,0,255), 2)
print('Número de contornos encontrados: ', len(ctns))
texto = 'Contornos encontrados: '+ str(len(ctns))

cv2.putText(imagen, texto, (10,20), cv2.FONT_HERSHEY_SIMPLEX, 0.7,
  (255, 0, 0), 1)

cv2.imshow('Imagen', imagen)
cv2.waitKey(0)
cv2.destroyAllWindows()

Bien, esto es todo por este post. Recuerda que puedes experimentar con cada una de las funciones que he utilizado aquí. Y cualquier pregunta que tengas la puedes dejar en la sección de los comentarios, así nos podemos ayudar entre todos a aprender más y más. ¡Lindo día!

Referencias

  • https://docs.opencv.org/3.1.0/da/d22/tutorial_py_canny.html