python + opencv – ¿Cómo trazar el rango de hsv?

Para extraer el color, tenemos esta función.

# define range of blue color in HSV lower_blue = np.array([110,50,50]) upper_blue = np.array([130,255,255]) # Threshold the HSV image to get only blue colors mask = cv2.inRange(hsv, lower_blue, upper_blue) 

¿Cómo visualizamos realmente el rango (lower_blue, upper_blue) que defino en el espacio hsv? Además, ¿cómo puedo realmente trazar un color hsv, pero no está funcionando …? Tengo este codigo

 upper = np.array([60, 255, 255]) upper = cv2.cvtColor(upper, cv2.COLOR_HSV2BGR) upper = totuple(upper/-255) print(upper) plt.imshow([[upper]]) 

¿Qué son los colores HSV?

HSV, como HSL (o en OpenCV, HLS), es uno de los espacios de color cilíndricos.

espacios de colores cilíndricos

El nombre es un tanto descriptivo de cómo se hace referencia a sus valores.

El tono se representa como grados de 0 a 360 (en OpenCV, para ajustarse al formato de entero sin signo de 8 bits, los grados se dividen entre dos para obtener un número de 0 a 179; por lo tanto, 110 en OpenCV es de 220 grados). Si tuviera que tomar un “rango” de valores de matiz, es como cortar una rebanada de un pastel. Solo estás tomando un poco de ángulo de la torta.

El canal de saturación es la distancia a la que se encuentra del centro: el radio en el que se encuentra. El centro no tiene ninguna saturación, solo colores grises de negro a blanco. Si tomaste un rango de estos valores, es similar a rasurar el exterior del cilindro, o cortar un círculo desde el centro. Por ejemplo, si el rango es de 0 a 255, entonces el rango de 0 a 127 sería un cilindro que solo se extiende a la mitad del radio; El rango de 127 a 255 sería cortar un cilindro interno con la mitad del radio hacia afuera.

El canal de valor es un nombre ligeramente confuso; no es exactamente de oscuridad a brillo porque el valor más alto representa el color directo, mientras que el valor más bajo es negro. Esta es la altura del cilindro. No es tan difícil imaginarse cortando verticalmente una rebanada del cilindro.

Rangos de valores de HSV

La función cv2.inRange(image, lower_bound, upper_bound) encuentra todos los valores de la imagen entre lower_bound y upper_bound . Por ejemplo, si su imagen era una imagen de 3×3 (solo para fines de demostración simples) con 3 canales, podría verse algo como esto:

 # h channel # s channel # v channel 100 150 250 150 150 100 50 75 225 50 100 125 75 25 50 255 100 50 0 255 125 100 200 250 50 75 100 

Si quisiéramos seleccionar tonos entre 100 y 200, entonces nuestra lower_b debería ser [100, 0, 0] y upper_b debería ser [200, 255, 255] . De esa manera, nuestra máscara solo tomaría en cuenta los valores en el canal de tono y no se vería afectada por la saturación y el valor. Es por eso que el HSV es tan popular: puede seleccionar colores por matiz independientemente de su brillo u oscuridad, por lo que se puede seleccionar un rojo oscuro y un rojo shiny con solo especificar el mínimo y máximo del canal de matiz.

Pero digamos que solo queríamos seleccionar colores blancos shinys. Observe el modelo del cilindro: vemos que el blanco se da en la parte superior central del cilindro, por lo que s valores de s son bajos y los valores de v son altos, y el ángulo de color no importa. Entonces, lower_b se vería algo como [0, 0, 200] y upper_b se vería algo como [255, 50, 255] . Eso significa que todos los valores de H se incluirán y no afectarán nuestra máscara. Pero entonces solo se incluirían valores S entre 0 y 50 (hacia el centro del cilindro) y solo se incluirán valores V de 200 a 255 (hacia la parte superior del cilindro).

Visualizando una gama de colores de HSV.

Una forma de visualizar todos los colores en un rango es crear gradientes que vayan a lo largo de ambas direcciones para cada uno de los dos canales, y luego animar sobre el tercer canal cambiante.

Por ejemplo, podría crear un gradiente de valores de izquierda a derecha para el rango de valores S , de arriba a abajo para el rango de valores V , y luego hacer un bucle sobre cada valor H Todo este progtwig podría verse así:

 import numpy as np import cv2 lower_b = np.array([110,50,50]) upper_b = np.array([130,255,255]) s_gradient = np.ones((500,1), dtype=np.uint8)*np.linspace(lower_b[1], upper_b[1], 500, dtype=np.uint8) v_gradient = np.rot90(np.ones((500,1), dtype=np.uint8)*np.linspace(lower_b[1], upper_b[1], 500, dtype=np.uint8)) h_array = np.arange(lower_b[0], upper_b[0]+1) for hue in h_array: h = hue*np.ones((500,500), dtype=np.uint8) hsv_color = cv2.merge((h, s_gradient, v_gradient)) rgb_color = cv2.cvtColor(hsv_color, cv2.COLOR_HSV2BGR) cv2.imshow('', rgb_color) cv2.waitKey(250) cv2.destroyAllWindows() 

Gif de valores de rango

Ahora este gif muestra un nuevo valor H cada cuadro. Y de izquierda a derecha tenemos los valores mínimos a máximos de S , y de arriba a abajo tenemos los valores mínimos a máximos de V Cada uno de los colores que se muestran en esta animación se seleccionarán de su imagen para formar parte de su mask .

Haga su propia función inRange ()

Para comprender completamente la función OpenCV, la forma más sencilla es hacer su propia función para completar la tarea. No es nada difícil, y no es mucho código.

La idea detrás de la función es simple: encuentre dónde se encuentran los valores de cada canal entre min y max , y luego & todos los canales juntos.

 def inRange(img, lower_b, upper_b): ch1, ch2, ch3 = cv2.split(img) ch1m = (lower_b[0] <= ch1) & (ch1 <= upper_b[0]) ch2m = (lower_b[1] <= ch2) & (ch2 <= upper_b[1]) ch3m = (lower_b[2] <= ch3) & (ch3 <= upper_b[2]) mask = ch1m & ch2m & ch3m return mask.astype(np.uint8)*255 

Puede leer los documentos de OpenCV para ver que esta es la fórmula utilizada. Y podemos verificarlo también.

 lower_b = np.array([200,200,200]) upper_b = np.array([255,255,255]) mask = cv2.inRange(img, lower_b, upper_b) # OpenCV function mask2 = inRange(img, lower_b, upper_b) # above defined function print((mask==mask2).all()) # checks that the masks agree on all values # True 

Cómo encontrar los colores adecuados.

Puede ser un poco difícil encontrar los valores correctos para usar para una imagen en particular. Sin embargo, hay una manera fácil de experimentar. Puedes crear barras de seguimiento en OpenCV y usarlas para controlar el mínimo y máximo de cada canal y hacer que el progtwig Python actualice tu máscara cada vez que cambies los valores. Hice un progtwig para esto que puedes usar en GitHub aquí . Aquí hay un .gif animado de que se está utilizando, para demostrar:

Gif del programa cspaceThresh