Detección de objetos por colores en imágenes con Python y OpenCV

Gastón Di Giuseppe
6 min readAug 29, 2019

--

En este posteo voy a tratar de explicar la detección de objetos por colores y para ello voy a usar Python y la librería más conocida para procesar imágenes y video que se llama OpenCV.

Primeros pasos: instalar las librerías

Instalamos las librerías que vamos a necesitar para el proyecto.

$ pip install opencv-python
$ pip install matplotlib

Además, vamos a trabajar con Jupyter Notebook. Si tienen Anaconda ya lo deben tener instalado, sino pueden googlear como instalarlo para su sistema operativo en su buscador favorito.

Entendiendo los colores de OpenCV

Los “espacios de color” son una forma de representar los canales de color presentes en la imagen, que le dan a la imagen ese matiz particular. Hay varios espacios de color diferentes y cada uno tiene su propio significado. Algunos de los espacios de color populares son RGB (rojo, verde, azul), CMYK (cian, magenta, amarillo, negro), HSV (tono, saturación, valor), etc.

Espacio de color BGR
El espacio de color predeterminado de OpenCV es RGB. Sin embargo, en realidad almacena color en formato BGR, intercambiando el canal Azul (B) por el Rojo (R). Es un modelo de color aditivo donde las diferentes intensidades de azul, verde y rojo dan diferentes tonos de color.

RGB describe un color como una tupla de tres componentes. Cada componente puede tomar un valor entre 0 y 255, donde la tupla (0, 0, 0) representa el negro y (255, 255, 255) representa el blanco.

Otro ejemplos de colores son:

Vemos como varían los enteros y el color obtenido

Espacio de color HSV
El espacio de color HSV (matiz, saturación, valor) es un modelo para representar el espacio de color similar al modelo de color RGB. Dado que el canal de matiz (H) modela el tipo de color, es muy útil en tareas de procesamiento de imágenes que necesitan segmentar objetos en función de su color. La variación de la saturación (S) puede entenderse como qué tan fuerte es el color que vemos; pasa de no saturada a representar sombras de gris y completamente saturada (sin componente blanco). El canal de valor (V) describe el brillo o la intensidad del color.

Rojo no saturado — Rojo saturado

En OpenCV, los canales de HSV varían así: [H:0–179, S:0–255, V:0–255]. Debemos entender la variación de H como “grados”, que en realidad varía de 0 a 360, como vemos en la imagen, pero en OpenCV lo simplifican en 180 grados. Vemos también que la saturación (S) varía desde el centro hacia afuera y el valor (V) desde lo más oscuro, la base, hasta lo más claro, el techo.

Espacio de color HSV

Manos a la obra

Trabajemos con la siguiente imagen:

Abrimos jupyter y creamos un archivo nuevo de python3:

Importamos las librerías que necesitamos:

import cv2
import matplotlib.pyplot as plt

Leemos la imagen con cv2 (OpenCV) y la mostramos con pyplot:

img = cv2.imread(“formas.jpg”)
plt.imshow(img)

Vemos como los colores no son los mismos que los que vimos en la original. Esto es porque OpenCV invierte los canales B y R. Para solucionar esto, tenemos una función para convertir la imagen a otro espacio de color:

img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img)
Ahora si!

Pero lo que nosotros realmente necesitamos para una mejor detección de color es la imagen en el espacio de color HSV, entonces la convertimos nuevamente:

img_hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)

Elegir y Detectar Colores

Ahora aprendamos a elegir y detectar colores en HSV.

Para ello necesitamos definir una máscara que funcionará como filtro del resto de los colores que no queremos detectar.

Nosotros necesitamos los valores de variación de cada canal, y abarcar un rango del espacio de colores en HSV. Es decir, por ejemplo, que H varíe de 0 a 10, que S varíe de 100 a 255, y V de 100 a 255, para detectar un color rojo/naranja. Si esto parece confuso, no te preocupes, lo vamos a ver en el ejemplo que sigue.

La mejor forma que encontré para hallar un color que necesito es a través de esta imagen, donde se muestra la variación de colores, con H en el eje “x”, y S en el eje “y”, y V = 255.

Variación de colores HSV en OpenCV, cuando V = 255

Comencemos por el verde. Definimos dos umbrales desde dónde hasta dónde abarcará el espacio de color en HSV, y aplicamos la máscara.

# Elegimos el umbral de verde en HSV
umbral_bajo = (70,100,100)
umbral_alto = (80,255,255)
# hacemos la mask y filtramos en la original
mask = cv2.inRange(img_hsv, umbral_bajo, umbral_alto)
res = cv2.bitwise_and(img, img, mask=mask)

La función inRange recibe 3 parámetros: la imagen, y los dos umbrales. Bien, pero ¿qué indican estos umbrales?. En este ejemplo indican que H agarrará el espacio de color de 70 a 80, S de 100 a 255 y V de 100 a 255 también.

Esto devuelve una máscara con valores 1 donde detectó el color elegido y 0 donde no lo encuentra. Filtramos la imagen original con la función bitwise_and, que básicamente hace una operación AND con los valores de la imagen original. Debemos entender que las imágenes están representadas en arreglos numpy y esta función nos devuelve la imagen original filtrada con la máscara.

Veamos cómo quedan ambas imágenes.

# imprimimos los resultados
plt.subplot(1, 2, 1)
plt.imshow(mask, cmap=”gray”)
plt.subplot(1, 2, 2)
plt.imshow(res)
plt.show()
IZQ: Máscara, DER: imagen original filtrada

Sigamos con el rojo.

# Elegimos el umbral de rojo en HSV
umbral_bajo = (170,100,100)
umbral_alto = (179,255,255)
# hacemos la mask y filtramos en la original
mask = cv2.inRange(img_hsv, umbral_bajo, umbral_alto)
res = cv2.bitwise_and(img, img, mask=mask)
# imprimimos los resultados
plt.subplot(1, 2, 1)
plt.imshow(mask, cmap=”gray”)
plt.subplot(1, 2, 2)
plt.imshow(res)
plt.show()
¿Y esos píxeles negros dentro de la máscara?

En este caso, vemos que tenemos algunos píxeles que no se llegaron a detectar con la máscara. Esto es porque en el rango del rojo en HSV, tenemos que contemplar tanto el inicio como el final del umbral de colores, con los valores de H.

¿Y eso qué significa y cómo se hace?

Bueno, hay que hacer dos máscaras que contemplen la parte más baja de H, y luego sumarlas. Si, podemos sumar las máscaras!. Sería algo así:

# Elegimos el umbral de rojo en HSV
umbral_bajo1 = (170,100,100)
umbral_alto1 = (179,255,255)
# Elegimos el segundo umbral de rojo en HSV
umbral_bajo2 = (0,100,100)
umbral_alto2 = (10,255,255)
# hacemos la mask y filtramos en la original
mask1 = cv2.inRange(img_hsv, umbral_bajo1, umbral_alto1)
mask2 = cv2.inRange(img_hsv, umbral_bajo2, umbral_alto2)
mask = mask1 + mask2
res = cv2.bitwise_and(img, img, mask=mask)
# imprimimos los resultados
plt.subplot(1, 2, 1)
plt.imshow(mask, cmap=”gray”)
plt.subplot(1, 2, 2)
plt.imshow(res)
plt.show()
Mucho mejor!

Vemos como desaparecieron esos píxeles negros que se veían dentro de la máscara, y cómo mejoró el filtro.

Conclusión

Aprendimos cómo identificar colores con OpenCV haciendo máscaras que funcionan como filtros si aplicamos la función AND que trae incorporada la librería. Les dejo como tarea el proceso de identificar el color amarillo y el azul, así pueden probar lo que aprendieron, si se animan ;).

Repositorio

Pueden clonar el tutorial desde acá: https://github.com/gastondg/Deteccion_color_OpenCV.git

Chau!

--

--