Detección de colores en OpenCV – Python (En 4 pasos)
CONTENIDO
- HSV (Hue, Saturation, Value) en OpenCV
- Detección de colores con HSV en OpenCV
- Paso 1: Imagen a procesar
- Paso 2: Transformar de BGR a HSV
- Paso 3: Determinar los rangos en donde se encuentra el color a detectar
- Paso 4: Visualización
- Si has pensado en usar la detección de colores, ten en cuenta que..
El tema de este post es la detección de colores usando OpenCV en Python, para ello se han usado las versiones de 3.4.4 y 3.6 respectivamente. Trataremos brevemente el espacio de color HSV, para luego explicar los 4 pasos para detectar colores. ¡Empecemos!
HSV (Hue, Saturation, Value) en OpenCV
El espacio de color HSV (Hue, Saturation, Value / Matiz, Saturación, Brillo), posee 3 componentes, similar al espacio de color RGB que tratamos anteriormente en un post. Vamos a utilizar este espacio de color debido a que podremos determinar de forma más sencilla los rangos de los colores que deseamos detectar.
Para determinar un color nos vamos a centrar principalmente en el componente H que corresponde al matiz. En la figura 1 podemos ver como este componente va cambiando de rojo, amarillo, verde, violeta hasta llegar nuevamente a rojo.
Es necesario conocer que valores pueden tomar cada uno de los canales de HSV en OpenCV, veamos:
- H: 0 a 179
- S: 0 a 255
- V: 0 a 255
Esta breve información acerca de este espacio de color te va a ayudar a comprender especialmente el tercer paso para la detección de colores.
Detección de colores con HSV en OpenCV
Para detectar un color en OpenCV usando el espacio de color HSV, he determinado 4 pasos, vamos a desglosarlos uno por uno como se ha hecho en el video.
Paso 1: Imagen a procesar
Lo primero que necesitamos es una imagen o fotograma para sobre este detectar los colores. Podemos conseguir nuestra «materia prima» de dos formas, leyendo una imagen o a través de un video. En esta ocasión lo haremos a través de un video streaming:
import cv2 import numpy as np cap = cv2.VideoCapture(0) while True: ret,frame=cap.read() if ret==True: cv2.imshow('frame', frame) if cv2.waitKey(1) & 0xFF == ord('s'): break cap.release() cv2.destroyAllWindows()
En la línea 1 y 2 importamos OpenCV y numpy, luego inicializamos el proceso de captura del video streaming. Luego en la línea 7 obtenemos los fotogramas con los que vamos a trabajar. Estoy pasando rápidamente esta explicación debido a que esto ya lo he explicado de forma más detallada anteriormente (Capturar, guardar y leer un video en OpenCV y Python).
Paso 2: Transformar de BGR a HSV
Por defecto OpenCV lee a las imágenes o fotogramas en BGR, por ello es necesario transformarlas al espacio de color HSV. Para ello nos ayudaremos de la función cv2.cvtColor
, como primer argumento le daremos la imagen a transformar, y luego cv2.COLOR_BGR2HSV
, para indicar que transformaremos de BGR a HSV. Veamos como quedaría nuestro código hasta aquí:
import cv2 import numpy as np cap = cv2.VideoCapture(0) while True: ret,frame = cap.read() if ret==True: frameHSV = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) cv2.imshow('frame', frame) if cv2.waitKey(1) & 0xFF == ord('s'): break cap.release() cv2.destroyAllWindows()
En la línea 9 podemos ver como se ha aplicado la función antes mencionada, entonces esta transformación queda guardada en la variable frameHSV
.
Paso 3: Determinar los rangos en donde se encuentra el color a detectar
En este post vamos a detectar el color rojo, que se encuentra al principio y al final del componente H:
Como vemos en la figura 2 el componente en H va de 0 a179, y en este pasan colores de rojo, naranja, amarillo, verde, azul, violeta y nuevamente rojo, entonces vamos a determinar 2 rangos para el color rojo que está presente al principio y al final, el primer rango lo determinaremos de 0 a 8, y el segundo de 175 a 179 en H, mientras que para los componente S de 100 a 255, y V de 20 a 255, para ambos rangos.
import cv2 import numpy as np cap = cv2.VideoCapture(0) redBajo1 = np.array([0, 100, 20], np.uint8) redAlto1 = np.array([8, 255, 255], np.uint8) redBajo2=np.array([175, 100, 20], np.uint8) redAlto2=np.array([179, 255, 255], np.uint8) while True: ret,frame = cap.read() if ret==True: frameHSV = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) maskRed1 = cv2.inRange(frameHSV, redBajo1, redAlto1) maskRed2 = cv2.inRange(frameHSV, redBajo2, redAlto2) maskRed = cv2.add(maskRed1, maskRed2) cv2.imshow('frame', frame) if cv2.waitKey(1) & 0xFF == ord('s'): break cap.release() cv2.destroyAllWindows()
En la línea 6 y 7 se especifica el primer rango, para ello debemos crear un array con numpy, allí se especificarán los componentes en H, S y V, seguido de np.uint8.
El mismo procedimiento se realiza en las líneas 9 y 10, ahora para el segundo rango. Estas están ubicadas previo al while, debido a que solo será necesario declararlas una vez.
¿Te fijaste en las líneas 16, 17 y 18? Estas nuevas líneas son necesarias para buscar los rangos, en este caso para detectar el color rojo, por ello en la línea 16 se usa la función cv2.inRange
, el primer argumento es la imagen en la cual se buscará el rango, en este caso frameHSV
, luego el límite inicial y final del primer rango, el mismo proceso se realizará en la línea 17, para el segundo rango.
De aquí se obtendrán las imágenes maskRed1
y maskRed2
, que son binarias en donde el color blanco refleja el lugar en donde está presente el primer y segundo rango respectivamente, mientras que el color negro mostrará el lugar donde no están presentes estos rangos.
Como deseamos detectar el color rojo, y tenemos 2 rangos establecidos, debemos unirlos, para ello usaremos la línea 18. Aquí estamos sumando ambas imágenes para mostrar en una sola (maskRed
), la presencia del color rojo. maskRed
por lo tanto también será binaria.
Paso 4: Visualización
Entonces tenemos una imagen binaria, en donde el color blanco representa el color rojo detectado según los rangos que hemos dado en el paso 3. Es hora de visualizar este resultado.
import cv2 import numpy as np cap = cv2.VideoCapture(0) redBajo1 = np.array([0, 100, 20], np.uint8) redAlto1 = np.array([8, 255, 255], np.uint8) redBajo2=np.array([175, 100, 20], np.uint8) redAlto2=np.array([179, 255, 255], np.uint8) while True: ret,frame = cap.read() if ret==True: frameHSV = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) maskRed1 = cv2.inRange(frameHSV, redBajo1, redAlto1) maskRed2 = cv2.inRange(frameHSV, redBajo2, redAlto2) maskRed = cv2.add(maskRed1, maskRed2) maskRedvis = cv2.bitwise_and(frame, frame, mask= maskRed) cv2.imshow('frame', frame) cv2.imshow('maskRed', maskRed) cv2.imshow('maskRedvis', maskRedvis) if cv2.waitKey(1) & 0xFF == ord('s'): break cap.release() cv2.destroyAllWindows()
En la línea 20 visualizamos el video streming, en mi caso se vería algo así:
Ahora bien, vamos a visualizar la imagen binaria que tenemos en maskRed
, para ello usamos cv2.imshow
, como se aprecia en la línea 21, tal y como lo habíamos hecho para frame
.
Como vemos en la figura 4, la región de color blanco corresponde a la ubicación de la pelota roja. También podemos ver unas pequeñas regiones de color blanco, esto vendría a ser ruido en nuestra detección. En el siguiente post veremos como mejorar la detección (O puedes ver este video, que contiene la información que vendrá en el siguiente post). Sin embargo a pesar de ello ya hemos detectado el color rojo en una imagen. ¡Felicitaciones!
Tal vez te estés preguntando, sobre la línea 19, pues bien, esta es una visualización adicional, en donde podremos ver el color rojo detectado, y en las regiones de la imagen donde no se presente este color se visualiza en negro. Veamos:
Para poder realizar esta visualización necesitamos de la línea 19. Aquí usamos la función cv2.bitwise_and
, a esta le entregamos como primer y segundo argumento la imagen o fotograma en BGR (frame
), luego, mask = maskRed
. Esto lo que hace es que la región blanca demaskRed
permita visualizar los colores de frame
, mientras que la región en negro se mantenga.
Si has pensado en usar la detección de colores, ten en cuenta que..
La detección de colores puede ayudarte a realizar un montón de aplicaciones, sin embargo hay dos aspectos importantes a considerar:
- Iluminación. Es de importancia tener controlado este aspecto, ya que a más o menos iluminación sobre el objeto o la región que deseamos detectar cierto color, puede alterar el resultado de dicha detección.
- Fondo. El fondo de la imagen también podría irrumpir en el proceso de detección, ya que si en este están presentes colores que deseamos detectar, no solo se detectaría el objeto de interés, sino también parte del fondo.
Y bien, por ahora hemos acabado. Espero te haya servido este pequeño tutorial de como detectar el color rojo, recuerda que puedes practicar detectando distintos colores. En el próximo post veremos como mejorar la detección, nos vemos pronto.
Por cierto, no te vayas sin ver las hojitas del contenido, u hojas de resumen, te podrían ayudar a entender mejor el proceso.
Muy interesante y bien explicado.
Muchas gracias Jose, seguiré subiendo más contenido por aquí pronto 🙂
Está muy bien explicando aunque la parte de como poner los Colores está algo general, o sea se puede entender más por ejemplo yo quiero poner el color verde, azul, y amarillo y allí es muy confundo ya que no se explica como sacar los valores de los demás datos solamente el de la H.
Hola Cristian, gracias por tu comentario.Tengo un video que te puede servir: https://youtu.be/CFuyXw-pfPY
Allí estoy detectando distintos colores.
buen día, un código como el tuyo puede servir con un selector?, pensando en que al momento de que la cámara detecte azul, rojo, amarillo, sea true y al momento de que detecte verde sea false y esto encienda un pistón para desviar la pieza?
Podrías probarlo con una raspberry pi, para que puedas usar las salidas GPIO y combinarlo con detección de colores.
infaltable el varón con el valioso aporte!
Muy bueno tu vídeo, felicitaciones.
tengo una duda y ojala me puedas ayudar, como puedo hacer que el código arroje los valores de un color.
por ejemplo tengo un fondo blanco y solo hay un objeto azul, pero en realidad no se que color es. entonces quiero que el programa me arroje los valores del color, como podría hacer eso.
de antemano gracias por la ayuda.
Hola Jhojan, se me ocurre que primero tendrías que establecer el color del fondo para que sea fácil de diferenciar con respecto al objeto. Luego cuando tengas el objeto únicamente, podrías ayudarte de numpy, para obtener los valores que más se repiten, o los límites bajos y altos que se encuentren en ese objeto. Previamente pienso que deberías especificar los rangos de los posibles colores que pueden aparecer, para que puedan ser comparados con los valores que obtengas al usar numpy. Creo que esa sería una opción, espero que te haya ayudado 🙂 saludos!
Hola, excelentes tus videos, muy bien explicados, tengo una duda, como hago para detectar el color negro en un fondo blanco? muchas gracias un abrazo.
Hola Diego, muchas gracias. Lo que podrías usar es thresholding, te dejo el link a este tutorial: http://omes-va.com/simple-thresholding/
muy claro! gracias
Hola, una pregunta como puedo hacer que el fondo se vea en escala de grises y solo de visualice el color rojo?
Hola Alondra, puedes emplear la funcion cv2.bitwise_and, ( http://omes-va.com/operadores-bitwise/ ), allí puedes usar la imagen en escala de grises, y la imagen binaria que obtengas de la detección, puedes ver un ejemplo aquí:http://omes-va.com/marcador-o-lapiz-virtual-opencv-python/
Hola, gracias. Quería anexar una imagen para que me explicara mejor pero no se puede jiji. Me resulta un poco difícil de aplicar en mi trabajo. Pero aun asi muchas gracias.
Ohh, si deseas puedes explicarme un poquito más :).
Hola , me podria suministrar el link para descargar openCV 3.4.4 , es qe el qe tengo es version 3.0.4 y la funcion cv2.findContours() no me funciona con los 3 parametros requeridos, gracias, muy buena tus explicaciones
Hola José, si tienes problemas con los 3 valores puedes reemplazar por 2 valores por ejemplo: cnts,_ = cv2.findContours. Esto depende de la versión de OpenCV. También podrías usar help(cv2.findContours), para conocer cuantos valores necesita la cv2.findContours dependiendo de la versión que tengas instalada.
Puedes instalar opencv via pip: https://pypi.org/project/opencv-contrib-python/
Eres genial, muchas gracias por tus aportes
Quisiera poder profundizar mucho mas en este tema.
me gustaría poder crear un a cámara capaz de seguir objetos y colores tamaños y formas y de alguna manera que sea capaz de realizar acciones conforme a los valores obtenidos .
Tus vídeos me ayudan muchísimo como guía y claros ejemplos de lo que podría llegar a hacer.
Hola Kevin, muchas gracias :D! Me pone muy feliz que te sea de ayuda el contenido y además que sea útil para realizar tus proyectos. ¡Muchos éxitos!
Que tal, habría forma de hacer este procedimiento de manera inversa? Es decir que por ejemplo en lugar de mostrar únicamente el rojo y lo demás en negro, que el rojo se vea negro pero todo lo demás se vea normal. Espero haberme dado a entender. Muchas gracias por la explicación.
Hola Andrés, puedes usar las funciones bitwise para invertir la imagen binaria. Échale un ojo a este tutorial: http://omes-va.com/operadores-bitwise/
Me encantan tus tutoriales, son faciles de entender.
Tenia una duda, soy nuevo en python y no se diga en opencv, me gustaria saber si es posible que una variable(0 o 1 por ejemplo) me regrese si en la imagen se encontro el color que especifique
Hola Ivan, muchas gracias :). Si es posible, podrías usar cv2.findContours y contar cuantos contornos del color detectado existen. Puedes darle un vistazo por ejemplo a este video: https://youtu.be/DwPug0V8pcI
muchas gracias, ya lo logré 🙂
Buenas noches,
Necesito realizar una programación en Python para identificar colores en ROBODK me podrías asesorar con eso.
Muchas gracias.
Hola Roberto, qué espacio de color emplea?
Gracias por tus videos, me parecen excelentes…
Solo que no me queda muy claro como es que funcionan los parámetros de la saturación y el brillo… Comentas que pondrás un link para stackOverFlow, pero solo pusiste 2 veces el link para una imagen…
?Pregunta en stackoverflow: https://i.stack.imgur.com/gyuw4.png
?Imagen de componentes en HSV: https://i.stack.imgur.com/gyuw4.png
Te agradecería mucho si corriges el link a stackOverFlow
Hola Oscar, muchas gracais. Se me ha pasado por alto lo del link, aquí esta: https://stackoverflow.com/questions/10948589/choosing-the-correct-upper-and-lower-hsv-boundaries-for-color-detection-withcv?lq=1 🙂 Espero que te sea de utilidad.
Hola me gustaria saber si puedo usar la camara del pc y como la configuro para que deje tomar el video
Hola Ivan, te refieres a la webcam integrada? Si puedes usarla, no deberías tener que configurar nada solo seguir los tutoriales con OpenCV: http://omes-va.com/basicvideo/ 🙂
GENTE PROGRAMADORA BUEN DIA!
DESEO SUS COMENTARIOS EN APOYO A UNA INVESTIGACION QUE QUIERO HACER PARA DETECTAR A TRABAJADORES A TRAVES DE SUS CASCOS DE COLORES; SUS MOVIMIENTOS Y TODO;A TRAVES DE LAS CAMARAS DE VIDEOVIGILANCIA; COMO PODRIA EMPEZAR A AVERIGUAR O INDAGAR EN ESO; EXISTEN YA SOFTWARE QUE HACEN ESO ?? QUE AL GRABAR DETECTEN EL MOVIMIENTO DE LOS CASCOS DE LAS PERSONAS Y SUS MOVIMIENTOS Y LOS REGISTRE EN UN SOFTWARE
LES AGRADECERIA MUCHISIMO CUALQUIER APOYO
ESTARE ATENTO A CUALQUIER CUALQUIER SUGERENCIA
GRACIAS
Hola Alexander, estaba buscando en internet. Tal vez le puedas echar un ojo a estas webs: https://issivs.com/securos-helmet-detection/ y https://www.acti.com/es/applications/helmet-violation-detection
Será posible que en video maskred que se muestra en blanco, se le pueda dar otro color que no sea blanco?
Hola Pablo en la imagen binaria, el blanco representa la presencia del color a detectar y en negro la no presencia del mismo. Si quisieras tomar la región en blanco para que se muestre de otro color, podrías primero transformarla a bgr y luego asignar el color que desees en los pixeles blancos.
hola disculpa me aparece esta leyenda a la hora de correrlo C:\Users\Aguayo\AppData\Local\Programs\Python\Python39\python.exe: can’t find ‘__main__’ module in »
Arreglado