? RESALTANDO UN COLOR de un fondo en grises | OpenCV con Python

Por Administrador

¡Qué grato tenerte aquí en un nuevo tutorial!. Hace unos días una lectora de este blog dejó el siguiente comentario:

«Hola, una pregunta como puedo hacer que el fondo se vea en escala de grises y solo se visualice el color rojo?» – Alondra

En primer lugar agradezco mucho a los lectores del blog y a los suscriptores del canal que siempre tienen ideas, preguntas y demás para colaborar con el contenido que desarrollo sobre visión por computador.

Pues bien, el planteamiento de Alondra me pareció muy interesante de realizar, ya que he visto algunas aplicaciones que llevan a cabo este tipo de efecto sobre imágenes, agregándoles gran atractivo y un toque especial. Por ello, en esta ocasión te mostraré como resaltar un color de una imagen, mientras que el fondo de la misma se presenta en escala de grises, para este procedimiento estaré usando la detección de colores que habíamos empleado en tutoriales anteriores.

CONTENIDO

  • ¿En qué consiste nuestro problema a resolver?
  • Determinar los rangos del color a detectar en el espacio de color HSV
  • Leer la imagen y transformarla a otros espacios de color
  • Detección del color rojo en la imagen
  • Fondo en escala de grises
  • Sumando imágenes para obtener el efecto de resaltar el color rojo sobre grises
  • Pero, ¿este programa puede servir para otros colores?

       – Programa completo

       – Programa para realizar el mismo procedimiento sobre un video

¿En qué consiste nuestro problema a resolver?

Lo que necesitamos hacer es, dada cierta imagen detectar el color rojo dentro de ella. Una vez que obtengamos la detección, podremos dejar este área a color, mientras que transformaremos el resto de la imagen a escala de grises.

Este proceso necesitará también de la suma de dos imágenes, para fusionarlas y lograr este efecto, a continuación veremos como realizarlo.

Determinar los rangos del color a detectar en el espacio de color HSV

Voy a crear un programa llamado redGrayImage.py, allí especificaremos las librerías que usaremos y además los rangos en donde se encuentra el color rojo a detectar. De ser necesario, puedes dirigirte a mis posts sobre detección de colores para una explicación más detallada: Detección de colores en OpenCV – Python (En 4 pasos) y DETECCIÓN DE COLORES Y Tracking en OpenCV – Parte2.

import cv2
import numpy as np
import imutils

rojoBajo1 = np.array([0, 140, 90], np.uint8)
rojoAlto1 = np.array([8, 255, 255], np.uint8)
rojoBajo2 = np.array([160, 140, 90], np.uint8)
rojoAlto2 = np.array([180, 255, 255], np.uint8)

Línea 1 a 3: Importamos OpenCV, numpy e imutils.

Línea 5 a 8: Establecemos los rangos en donde está presente el color rojo en el espacio de color HSV.

Leer la imagen y transformarla a otros espacios de color

Para la realización de este ejemplo voy a usar la siguiente imagen:

Figura 1: Imagen de entrada.

Vamos a continuar con nuestro programa:

# Leer la imagen
image = cv2.imread('img_00.jpeg')
image = imutils.resize(image, width=640)

# Pasamos las imágenes de BGR a: GRAY (esta a BGR nuevamente) y a HSV
imageGray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
imageGray = cv2.cvtColor(imageGray, cv2.COLOR_GRAY2BGR)
imageHSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

Línea 11: Leemos la imagen de entrada correspondiente a la figura 1.

Línea 12: En esta línea estoy redimensionando la imagen a un ancho de 640. Podrías omitir este paso.

Línea 15 y 16: Transformamos la imagen de entrada a escala de grises, y esta a su vez la volvemos a transformar a BGR, con esto conseguiremos tener una imagen en 3 canales que mostrará niveles de grises. 

NOTA: Si hubiéramos mantenido solo la imagen de la línea 15 en escala de grises, obtendríamos errores en pasos posteriores, específicamente con la suma de imágenes de 3 canales.

Línea 17: Transformamos la imagen de entrada al espacio de color HSV.

Detección del color rojo en la imagen

Una vez que tenemos la imagen en HSV, podemos proceder a la detección del color rojo, veamos:

# Detectamos el color rojo
maskRojo1 = cv2.inRange(imageHSV, rojoBajo1, rojoAlto1)
maskRojo2 = cv2.inRange(imageHSV, rojoBajo2, rojoAlto2)
mask = cv2.add(maskRojo1,maskRojo2)
mask = cv2.medianBlur(mask, 7)

Línea 20 y 21: De imageHSV vamos a encontrar los rangos del color rojo contenidos en dicha imagen, estos se almacenarán en maskRojo1 y maskRojo2 en forma de imagen binaria, en donde el color blanco representará la presencia del color rojo, mientras que en negro la no presencia del mismo

Línea 22 y 23: Con cv2.add sumamos las dos imágenes binarias maskRojo1 y maskRojo2 para obtener una sola que se almacenará en mask, a esta a su vez le aplicaré cv2.medianBlur para mejorar los bordes del área en blanco. Veamos como se vería la imagen contenida en mask:

Figura 2: Detección del color rojo de la imagen de entrada.

En la figura 2 podemos apreciar como se ha detectado el color rojo que se representa en blanco, pero ¿qué te parece si visualizamos el color real de este área en la imagen?, para ello emplearemos la siguiente línea:

redDetected = cv2.bitwise_and(image,image,mask=mask)

Línea 24: Vamos a ayudarnos de cv2.bitwise_and, para tomar el color rojo detectado de la imagen de entrada y visualizarlo, mientras que el resto de la imagen se mantendrá en negro. Si quieres saber un poco más sobre esta función te dejo este post: Operadores BITWISE (AND-OR-NOT-XOR) con OpenCV y Python :).

Figura 3: Visualización de redDetected.

Ahora que ya tenemos los objetos, en este caso las rosas rojas en su color original, procedemos a trabajar en el fondo en grises de la imagen.

Fondo en escala de grises

En este paso necesitamos de la imagen de la línea 16, en ella en donde está ubicado el color rojo deberá mostrarse en negro, mientras que el resto de la imagen debe estar en grises. Con ello podremos sumar dicha imagen con la que tenemos en redDetected para realizar el efecto del color rojo sobre un fondo en grises.

# Fondo en grises
invMask = cv2.bitwise_not(mask)

Línea 27: Vamos a invertir la imagen binaria con cv2.bitwise_not. Y obtendremos lo siguiente:

Figura 4: Visualización de InvMask.

Prosigamos con la programación, ¡ya falta poco!

bgGray = cv2.bitwise_and(imageGray,imageGray,mask=invMask)

Línea 28: Ahora al igual que en la línea 24 usamos cv2.bitwise_and. En el área blanca se mostrará la imagen en grises, veamos:

Figura 5: Visualización de bgGray.

Ahora solo nos queda sumar las dos imágenes contenidas en las figuras 3 y 5.

Sumando imágenes para obtener el efecto de resaltar el color rojo sobre grises

Ya estamos en la parte final de este tutorial. Procedemos a realizar el siguiente planteamiento:

Figura 6: Sumar redDetected y bgGray para obtener el efecto deseado.

Como puedes apreciar en la figura 6, vamos a sumar dos imágenes que ya habíamos obtenido anteriormente para finalmente obtener el efecto plateado, veamos el resto del programa:

# Sumamos bgGray y redDetectedCreamos
finalImage = cv2.add(bgGray,redDetected)

# Visualización
cv2.imshow('Image',image)
cv2.imshow('finalImage', finalImage)
cv2.waitKey(0)
cv2.destroyAllWindows()

Línea 31: Con cv2.add sumamos las dos imágenes bgGray y redDetected. Para más información sobre esta función por favor visita el siguiente tutorial: ADICIÓN y SUSTRACCIÓN de imágenes con OpenCV y Python.

Línea 34 a 37: Visualizamos la imagen de entrada y la imagen resultante. Esta visualización se presentará hasta que se presione una tecla y finalmente se cerrarán las ventanas creadas.

Figura 7: Visualización de la imagen de entrada y la imagen de salida con el efecto de rojo sobre grises.

¡Lo hemos logrado, ya hemos finalizado con la programación! En la figura 7 podemos ver los resultados y en el inicio de este post también puedes ver otros ejemplos de imágenes a las cueles se les ha aplicado el mismo procedimiento.

Pero, ¿este programa puede servir para otros colores?

La respuesta es si, podrías usarlo para cualquier otro color. Para resaltar el color amarillo por ejemplo, tendrías que modificar los rangos en HSV para detectar dicho color. Mira a continuación como se vería:

Figura 8: Mismo efecto ahora detectando el color amarillo.

Programa completo

import cv2
import numpy as np
import imutils

rojoBajo1 = np.array([0, 140, 90], np.uint8)
rojoAlto1 = np.array([8, 255, 255], np.uint8)
rojoBajo2 = np.array([160, 140, 90], np.uint8)
rojoAlto2 = np.array([180, 255, 255], np.uint8)

# Leer la imagen
image = cv2.imread('img_00.jpeg')
image = imutils.resize(image, width=640)

# Pasamos las imágenes de BGR a: GRAY (esta a BGR nuevamente) y a HSV
imageGray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
imageGray = cv2.cvtColor(imageGray, cv2.COLOR_GRAY2BGR)
imageHSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

# Detectamos el color rojo
maskRojo1 = cv2.inRange(imageHSV, rojoBajo1, rojoAlto1)
maskRojo2 = cv2.inRange(imageHSV, rojoBajo2, rojoAlto2)
mask = cv2.add(maskRojo1,maskRojo2)
mask = cv2.medianBlur(mask, 7)
redDetected = cv2.bitwise_and(image,image,mask=mask)

# Fondo en grises
invMask = cv2.bitwise_not(mask)
bgGray = cv2.bitwise_and(imageGray,imageGray,mask=invMask)

# Sumamos bgGray y redDetected
finalImage = cv2.add(bgGray,redDetected)

# Visualización
cv2.imshow('Image',image)
cv2.imshow('finalImage', finalImage)
cv2.waitKey(0)
cv2.destroyAllWindows()

Programa para realizar el mismo procedimiento sobre un video

import cv2
import numpy as np
import imutils

cap = cv2.VideoCapture('video1.mp4')

rojoBajo1 = np.array([0, 140, 90], np.uint8)
rojoAlto1 = np.array([8, 255, 255], np.uint8)
rojoBajo2 = np.array([160, 140, 90], np.uint8)
rojoAlto2 = np.array([180, 255, 255], np.uint8)

while True:

    ret, frame = cap.read()
    if ret == False: break
    frame = imutils.resize(frame,width=640)
    
    # Pasamos las imágenes de BGR a: GRAY (esta a BGR nuevamente) y a HSV
    frameGray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    frameGray = cv2.cvtColor(frameGray, cv2.COLOR_GRAY2BGR)
    frameHSV = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

    # Detectamos el color rojo
    maskRojo1 = cv2.inRange(frameHSV, rojoBajo1, rojoAlto1)
    maskRojo2 = cv2.inRange(frameHSV, rojoBajo2, rojoAlto2)
    mask = cv2.add(maskRojo1,maskRojo2)
    mask = cv2.medianBlur(mask, 7)
    redDetected = cv2.bitwise_and(frame,frame,mask=mask)

    # Fondo en grises
    invMask = cv2.bitwise_not(mask)
    bgGray = cv2.bitwise_and(frameGray,frameGray,mask=invMask)
    
    # Sumamos bgGray y redDetected
    finalframe = cv2.add(bgGray,redDetected)

    # Visualización
    cv2.imshow('Frame',frame)
    cv2.imshow('finalframe', finalframe)

    if cv2.waitKey(1) & 0xFF == 27:
        break

cap.release()
cv2.destroyAllWindows()

En la línea 5 puedes establecer el uso de un video en directo, o puedes leer un video almacenado.

Y esto fue todo por este tutorial, espero que te haya gustado y que lo encuentres útil. Nos vemos en un próximo post/video.