En este post vamos a contar objetos (en este caso círculos) de acuerdo a su color. El programa completo estará al final de este post.
El proceso que vamos a realizar es el siguiente:
- Leer la imagen de entrada.
- Transformar de BGR a HSV y determinar los rangos en donde se encuentren los colores a detectar
- Encontrar y dibujar cada contorno encontrado por cada color
- Enumerar cada uno de los círculos de acuerdo al color que poseen
- Elaborar una imagen resumen
Leer la imagen de entrada
import cv2 import numpy as np imagen = cv2.imread('lunares.png')
Empezamos importando opencv y numpy con un alias np, luego leemos la imagen con la que vamos a trabajar, esta lleva el nombre de lunares con su extensión png. La imagen presenta círculos de colores amarrillo, violeta, verde y rojo, que son los que contaremos para cada color.
Transformar de BGR a HSV y determinar los límites en donde se encuentren los colores a detectar
Transformamos la imagen de BGR que es por defecto como lee OpenCV a las imágenes, a HSV con la función cv2.cvtColor
:
imagenHSV = cv2.cvtColor(imagen, cv2.COLOR_BGR2HSV)
Determinar los límites en donde se encuentren los colores a detectar
Necesitamos determinar los límites alto y bajo en donde esté presente cada uno de los colores que deseamos detectar, para ello construiremos un vector con la ayuda de numpy, y para poder encontrar los colores usaremos cv2.inRange
. Veamos este proceso para cada color.
Recuerda que puedes visitar el post Detección de colores en OpenCV – Python (En 4 pasos) y DETECCIÓN DE COLORES Y Tracking en OpenCV – Parte2 si quieres los pasos más detallados de como detectar distintos colores.
Detectando el color amarillo
Lo primero que vamos a hacer es determinar los límites altos y bajos en donde se encuentra este color en HSV. Luego usaremos cv2.inRange
(como hemos hecho anteriormente).
amarilloBajo = np.array([20, 100, 20], np.uint8) amarilloAlto = np.array([32, 255, 255], np.uint8) maskAmarillo = cv2.inRange(imagenHSV, amarilloBajo, amarilloAlto)
Si visualizamos maskAmarillo
, obtendremos la siguiente imagen binarizada (derecha).
La región en blanco representará el color amarillo detectado, mientras que en negro la no presencia del mismo.
Detectando el color violeta
Vamos a seguir con el mismo procedimiento que hicimos recientemente.
violetaBajo = np.array([130, 100, 20], np.uint8) violetaAlto = np.array([145, 255, 255], np.uint8) maskVioleta = cv2.inRange(imagenHSV, violetaBajo, violetaAlto)
Veamos la imagen binarizada maskVioleta
obtenida:
La región en blanco representará el color violeta detectado, mientras que en negro la no presencia del mismo.
Detectando el color verde
verdeBajo = np.array([36, 100, 20], np.uint8) verdeAlto = np.array([70, 255, 255], np.uint8) maskVerde = cv2.inRange(imagenHSV, verdeBajo, verdeAlto)
La región en blanco representará el color verde detectado, mientras que en negro la no presencia del mismo.
Detectando el color rojo
rojoBajo1 = np.array([0, 100, 20], np.uint8) rojoAlto1 = np.array([10, 255, 255], np.uint8) rojoBajo2 = np.array([175, 100, 20], np.uint8) rojoAlto2 = np.array([180, 255, 255], np.uint8) maskRojo1 = cv2.inRange(imagenHSV, rojoBajo1, rojoAlto1) maskRojo2 = cv2.inRange(imagenHSV, rojoBajo2, rojoAlto2) maskRojo = cv2.add(maskRojo1, maskRojo2)
La imagen de entrada con la imagen binarizada para el color rojo serán:
La región en blanco representará el color rojo detectado, mientras que en negro la no presencia del mismo.
Encontrar y dibujar cada contorno encontrado por cada color
Ahora que tenemos las imágenes binarias del amarillo, violeta, verde y rojo, podemos encontrar cada uno de sus contornos con cv2.findContours
.
NOTA: Utiliza el código para encontrar los contornos que se muestra a continuación de acuerdo a tu versión de OpenCV.
#Encontrando contornos #OpenCV 3 #contornosAmarillo = cv2.findContours(maskAmarillo, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1] #contornosVioleta = cv2.findContours(maskVioleta, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1] #contornosVerde = cv2.findContours(maskVerde, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1] #contornosRojo = cv2.findContours(maskRojo, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1] #OpenCV 4 contornosAmarillo = cv2.findContours(maskAmarillo, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0] contornosVioleta = cv2.findContours(maskVioleta, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0] contornosVerde = cv2.findContours(maskVerde, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0] contornosRojo = cv2.findContours(maskRojo, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
Enumerar cada uno de los círculos de acuerdo al color
Vamos a enumerar cada círculo por su color, además que rodearemos a este, por ello vamos a crear una pequeña función:
def dibujarContorno(contornos, color): for (i, c) in enumerate(contornos): M = cv2.moments(c) if (M["m00"]==0): M["m00"]==1 x = int(M["m10"]/M["m00"]) y = int(M["m01"]/M["m00"]) cv2.drawContours(imagen, [c], 0, color, 2) cv2.putText(imagen, str(i+1), (x-10,y+10), 1, 2,(0,0,0),2)
La función dibujarContorno
necesita de contornos (encontrados por cada color) y un color en BGR para que sean rodeados dichos contornos. Luego vamos a tratar cada contorno encontrado dentro de contornos
, por lo que estamos usando un for. Para poder enumerar cada círculo, es decir para poner un número dentro del círculo nos ayudaremos de cv2.moments
, con este vamos a encontrar los puntos centrales x e y del contorno. Finalmente dibujamos los contornos con cv2.drawContours
del color dado a la función, y añadimos el texto con cv2.putText
que se visualizará en color negro.
Para una explicación más extensa de como trabajar con contornos y como dibujarlos haz clic aquí. Mientras que si necesitas información de como funciona cv2.putText
, da clic aquí.
Usaremos esta función creada para cada color, veamos:
Enumerando el color amarillo
dibujarContorno(contornosAmarillo, (0, 255,255))
En este caso se está rodeando cada círculo amarillo del mismo color, y en el centro de los mismo se ha asignado un número con lo que se puede enumerarlos.
Enumerando el color violeta
dibujarContorno(contornosVioleta, (140, 40, 120))
Se está dibujando el contorno de color violeta, además se puede visualizar un número dentro de cada círculo con este color.
Enumerando el color verde
dibujarContorno(contornosVerde, (0, 255, 0))
Los contornos se van a dibujar de color verde, y los números dentro de los círculos de negro.
Enumerando el color rojo
dibujarContorno(contornosRojo, (0, 0, 255))
En este caso se va a dibujar cada contorno de color rojo, mientras que los números dentro de los círculos de color negro.
Todos los círculos enumerados
En la figura 10 podemos ver cada uno de los círculos enumerados de acuerdo al color que poseen, sin embargo aquí no se puede distinguir la cantidad de círculos por cada color, es por ello que es necesario construir una pequeña imagen resumen que veremos a continuación.
Imagen Resumen
Para construir esta imagen necesitaremos usar numpy. Esta imagen tendrá 210 pixeles de alto y 100 de ancho, la realizaremos de la siguiente manera:
imgResumen = 255 * np.ones((210,100,3), dtype = np.uint8)
Luego vamos a dibujar pequeños círculos de cada color a la izquierda de la imagen, uno bajo el otro, para ello usaremos la función cv2.circle
.
cv2.circle(imgResumen, (30,30), 15, (0,255,255), -1) cv2.circle(imgResumen, (30,70), 15, (140,40,120), -1) cv2.circle(imgResumen, (30,110), 15, (0,255,0), -1) cv2.circle(imgResumen, (30,150), 15, (0,0,255), -1)
Recuerda que especificamos -1
al final para formar un círculo y no una circunferencia.
Seguimos, asignando la cantidad de círculos encontrados por cada color a la derecha de los círculos recién creados, para ello empleamos las siguientes líneas:
cv2.putText(imgResumen,str(len(contornosAmarillo)),(65,40), 1, 2,(0,0,0),2) cv2.putText(imgResumen,str(len(contornosVioleta)),(65,80), 1, 2,(0,0,0),2) cv2.putText(imgResumen,str(len(contornosVerde)),(65,120), 1, 2,(0,0,0),2) cv2.putText(imgResumen,str(len(contornosRojo)),(65,160), 1, 2,(0,0,0),2)
Finalmente vamos a sumar todos los contornos encontrados para mostrarle al usuario la totalidad de círculos encontrados sin importar su color.
totalCnts = len(contornosAmarillo) + len(contornosVioleta) + len(contornosVerde) + len(contornosRojo) cv2.putText(imgResumen,str(totalCnts),(55,200), 1, 2,(0,0,0),2)
Si visualizamos esta imagen con cv2.imshow
veremos la siguiente imagen:
Hasta aquí llega la aplicación realizada en este post. Es hora de que te deje todo el código usado:
import cv2 import numpy as np def dibujarContorno(contornos, color): for (i, c) in enumerate(contornos): M = cv2.moments(c) if (M["m00"]==0): M["m00"]==1 x = int(M["m10"]/M["m00"]) y = int(M["m01"]/M["m00"]) cv2.drawContours(imagen, [c], 0, color, 2) cv2.putText(imagen, str(i+1), (x-10,y+10), 1, 2,(0,0,0),2) amarilloBajo = np.array([20, 100, 20], np.uint8) amarilloAlto = np.array([32, 255, 255], np.uint8) violetaBajo = np.array([130, 100, 20], np.uint8) violetaAlto = np.array([145, 255, 255], np.uint8) verdeBajo = np.array([36, 100, 20], np.uint8) verdeAlto = np.array([70, 255, 255], np.uint8) rojoBajo1 = np.array([0, 100, 20], np.uint8) rojoAlto1 = np.array([10, 255, 255], np.uint8) rojoBajo2 = np.array([175, 100, 20], np.uint8) rojoAlto2 = np.array([180, 255, 255], np.uint8) imagen = cv2.imread('lunares.png') imagenHSV = cv2.cvtColor(imagen, cv2.COLOR_BGR2HSV) #Detectando colores maskAmarillo = cv2.inRange(imagenHSV, amarilloBajo, amarilloAlto) maskVioleta = cv2.inRange(imagenHSV, violetaBajo, violetaAlto) maskVerde = cv2.inRange(imagenHSV, verdeBajo, verdeAlto) maskRojo1 = cv2.inRange(imagenHSV, rojoBajo1, rojoAlto1) maskRojo2 = cv2.inRange(imagenHSV, rojoBajo2, rojoAlto2) maskRojo = cv2.add(maskRojo1, maskRojo2) #Encontrando contornos #OpenCV 3 #contornosAmarillo = cv2.findContours(maskAmarillo, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1] #contornosVioleta = cv2.findContours(maskVioleta, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1] #contornosVerde = cv2.findContours(maskVerde, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1] #contornosRojo = cv2.findContours(maskRojo, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1] #OpenCV 4 contornosAmarillo = cv2.findContours(maskAmarillo, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0] contornosVioleta = cv2.findContours(maskVioleta, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0] contornosVerde = cv2.findContours(maskVerde, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0] contornosRojo = cv2.findContours(maskRojo, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0] dibujarContorno(contornosAmarillo, (0, 255,255)) dibujarContorno(contornosVioleta, (140, 40, 120)) dibujarContorno(contornosVerde, (0, 255, 0)) dibujarContorno(contornosRojo, (0, 0, 255)) #Imagen Resumen imgResumen = 255 * np.ones((210,100,3), dtype = np.uint8) cv2.circle(imgResumen, (30,30), 15, (0,255,255), -1) cv2.circle(imgResumen, (30,70), 15, (140,40,120), -1) cv2.circle(imgResumen, (30,110), 15, (0,255,0), -1) cv2.circle(imgResumen, (30,150), 15, (0,0,255), -1) cv2.putText(imgResumen,str(len(contornosAmarillo)),(65,40), 1, 2,(0,0,0),2) cv2.putText(imgResumen,str(len(contornosVioleta)),(65,80), 1, 2,(0,0,0),2) cv2.putText(imgResumen,str(len(contornosVerde)),(65,120), 1, 2,(0,0,0),2) cv2.putText(imgResumen,str(len(contornosRojo)),(65,160), 1, 2,(0,0,0),2) totalCnts = len(contornosAmarillo) + len(contornosVioleta) + len(contornosVerde) + len(contornosRojo) cv2.putText(imgResumen,str(totalCnts),(55,200), 1, 2,(0,0,0),2) cv2.imshow('Resumen', imgResumen) #cv2.imshow('maskAmarillo', maskAmarillo) #cv2.imshow('maskVioleta', maskVioleta) #cv2.imshow('maskVerde', maskVerde) #cv2.imshow('maskRojo', maskRojo) cv2.imshow('Imagen', imagen) cv2.imwrite('conteo.png', imagen) cv2.waitKey(0) cv2.destroyAllWindows()
Y hemos llegado al final del post. No olvides visitar mi canal en YOUTUBE, mi repositorio en gitHub, así como ver los otros posts. ¡Cuídate mucho!