? CONTANDO OBJETOS (Aplicando Umbralización/Thersholding) en Python – OpenCV
En el blog de hoy, vamos a realizar una pequeña aplicación basada en el conteo de objetos usando umbralización simple, para poder enumerar monedas en una imagen usando OpenCV y Python.
El proceso para realizar esta aplicación será el siguiente:
- Leer la imagen de entrada.
- Transformarla a escala de grises.
- Aplicación de umbralización simple (thresholding).
- Buscar los contornos correspondientes a los objetos.
- Contar/Enumerar cada uno de los objetos (monedas) encontradas.
Leer la imagen de entrada
Al momento de leer una imagen en OpenCV, esta es leída en BGR (Blue, Green, Red). Para poder leer la imagen de la figura 1 importaremos previamente OpenCV luego usaremos la función cv2.imread
y tendremos las siguientes líneas de código:
import cv2 imagen = cv2.imread('monedas.jpg')
Transformar una imagen de BGR a escala de grises
Para la transformación de nuestra imagen a escala de grises, usaremos la función cv2.cvtColor
, en donde debemos especificar que transformaremos de BGR a escala de grises con cv2.COLOR_BGR2GRAY
.
grises = cv2.cvtColor(imagen, cv2.COLOR_BGR2GRAY)
De este modo obtendremos la siguiente imagen:
Aplicación de umbralización simple (thresholding) a la imagen
Ahora es necesario que los objetos (monedas) de nuestro interés se muestren en color blanco y el resto de la imagen en color negro, para ello usaremos umbralización simple. Si no recuerdas esta función, puedes revisar este link.
Ya que el fondo de la imagen es blanco, usaremos cv2.THRESH_BINARY_INV
, para obtener el fondo negro, de este modo obtendremos una imagen binaria, que nos servirá para encontrar los contornos de la imagen.
_,th = cv2.threshold(grises, 240, 255, cv2.THRESH_BINARY_INV)
Aplicando esta función, si visualizamos, obtendríamos a siguiente imagen:
Buscar los contornos de las regiones en blanco de la imagen binaria
Una vez que se tiene la imagen binaria en donde la región en blanco representa a los objetos de nuestro interés, mientras que la región en negro el fondo de la imagen. Se podrá aplicar la función cv2.findContours
(Puedes obtener más información de esta función aquí).
Ya que únicamente necesitamos obtener los contornos externos, usaremos cv2.RETR_EXTERNAL
, como segundo argumento de la función.
Dependiendo la versión de OpenCV que tengas instalada, puedes usar lo siguiente:
#Para OpenCV 3 _,cnts,_ = cv2.findContours(th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#Para OpenCV 4 cnts,_ = cv2.findContours(th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
Si deseas dibujar todos los contornos encontrados puedes usar la siguiente línea:
cv2.drawContours(imagen, cnts, -1, (255,0,0),2)
Si aplicas esta y visualizas, tendrás lo siguiente:
Aquí para determinar la totalidad de objetos en la imagen bastaría con usar len(cnts)
, del cual podemos obtener el número de contornos encontrados. Sin embargo vamos a ir más allá, enumerando cada moneda.
print('Contornos: ', len(cnts))
Contar / enumerar cada uno de los objetos (monedas) encontradas
Para poder enumerar cada una de las monedas, vamos a usar las siguientes líneas de código:
font = cv2.FONT_HERSHEY_SIMPLEX i=0 for c in cnts: M=cv2.moments(c) if (M["m00"]==0): M["m00"]=1 x=int(M["m10"]/M["m00"]) y=int(M['m01']/M['m00']) mensaje = 'Num :' + str(i+1) cv2.putText(imagen,mensaje,(x-40,y),font,0.75, (255,0,0),2,cv2.LINE_AA) cv2.drawContours(imagen, [c], 0, (255,0,0),2) cv2.imshow('Imagen', imagen) cv2.waitKey(0) i = i+1 cv2.destroyAllWindows()
Línea 16: Se está declarando la fuente del texto que se visualizará para cada moneda.
Línea 17: Declaramos un contador i en 0, para poder visualizar la enumeración de cada moneda.
Línea 18: Vamos a ir analizando cada contorno, para ello se ha utilizado un for, en donde c
será cada contorno encontrado.
Línea 19 a 22: Estás líneas nos ayudarán a determinar los puntos x e y centrales del contorno. Estas ya las habíamos usado también en un post anterior.
Línea 24: Aquí es donde estamos añadiendo el número que se asignará a cada moneda para enumerarla, más un string. Como para visualizar estos datos, deben ser ambos string, estamos sumando 'Num :'
e i+1
, a este último le sometemos a un cambio de tipo de dato para poder visualizar en la imagen.
Línea 25: Aquí usamos la función cv2.putText
, para que se visualice en color azul (BGR) la enumeración de cada moneda ya que el texto que se irá mostrando es el de la línea 24. Puedes darle un vistazo a esta función aquí.
Línea 27: En esta se va a dibujar cada contorno encontrado, toma en cuenta que he comentado previamente la línea 13 que dibujaba todos los contornos a la vez. En este caso en cambio se visualizarán los contornos uno a uno.
Línea 28 y 29: Se está realizando la visualización de la imagen, además de que cada dibujo del contorno como el texto que usamos para enumerar se mostrarán al presionar una tecla, ya que estamos usando cv2.waitKey(0)
.
Línea 30: En esta se aumentará en 1 el contador, que nos ayudará a contar cada objeto.
Línea 31: Se van a cerrar todas las ventanas de visualización con cv2.destroyAllWindows()
.
El resultado sería el siguiente:
Por último te dejo todo el código empleado para este post (Recuerda que también tengo mi repositorio en github):
import cv2 imagen = cv2.imread('monedas.jpg') grises = cv2.cvtColor(imagen, cv2.COLOR_BGR2GRAY) _,th = cv2.threshold(grises, 240, 255, cv2.THRESH_BINARY_INV) #Para OpenCV 3 _,cnts,_ = cv2.findContours(th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) #Para OpenCV 4 #cnts,_ = cv2.findContours(th, cv2.RETR_EXTERNAL, # cv2.CHAIN_APPROX_SIMPLE) #cv2.drawContours(imagen, cnts, -1, (255,0,0),2) #print('Contornos: ', len(cnts)) font = cv2.FONT_HERSHEY_SIMPLEX i=0 for c in cnts: M=cv2.moments(c) if (M["m00"]==0): M["m00"]=1 x=int(M["m10"]/M["m00"]) y=int(M['m01']/M['m00']) mensaje = 'Num :' + str(i+1) cv2.putText(imagen,mensaje,(x-40,y),font,0.75, (255,0,0),2,cv2.LINE_AA) cv2.drawContours(imagen, [c], 0, (255,0,0),2) cv2.imshow('Imagen', imagen) cv2.waitKey(0) i = i+1 cv2.destroyAllWindows()
Esto fue todo por el post de hoy, nos vemos en el siguiente. Pero, antes de despedirme totalmente, recuerda que tengo videos de tutoriales como este en youtube acerca de visión por computador, así que te espero por allá también. Ahora sí, nos vemos en el siguiente blog/video.
Se podría saber que tipo de valor tiene cada moneda y su sumatoria, muchas gracias por tus conocimientos
Hola Juan, claro que sí! 😀 Puedes basarte en el tamaño de las monedas para identificar su valor.
Consulta, en mis imagenes tengo mucho ruido en el fondo y cuenta de mas… como puedo elimianr el ruido? Recomendas algun tutorial?
Hola Federico, puedes usar transformaciones morfológicas en las imágenes binarias, también podrías aplicar suavizado a las imágenes. Otra cosa que podrías hacer es basarte en el tamaño de los contornos para poder descartar los que no son de tu interés. 🙂
Hola ¿puede aplicarse esta tecnica con videostreaming o el streaming debe pasarse a imágenes?
Hola Abel, si podrías realizarlo. Tendrías que usar en es caso cv2.VideoCapture.
Muy buena idea. Gracias