Por qué funciona: Unidad 1.3: La imagen como datos
Crear una máscara de color con cv2.inRange en OpenCV
Convierte un rango HSV en una máscara en blanco y negro con cv2.inRange, inviértela con bitwise_not y aplícala con bitwise_and. En la mesa de billar una llamada marca el 79,3% de la foto como paño.
Traducción asistida por máquina del original en inglés. Los números, el código y las referencias se comprobaron contra él; el texto no fue revisado por un hablante nativo. Leer el original. Los labs interactivos y el código están en inglés.
Una máscara es una imagen en blanco y negro que marca qué píxeles pasan una prueba, y cv2.inRange crea una a partir de un rango de color en una sola llamada.

Dónde ya lo has visto: el filtro de chroma key de OBS y de los editores de video, que es una máscara sobre el verde; Seleccionar › Por color en GIMP y Selección › Gama de colores en Photoshop.
TL;DR. Una llamada convierte el rango de color de la lección anterior en un mapa de dónde está ese color, e invertir el mapa da todo lo demás. En la foto del billar el 79,3% de los píxeles cae en el rango del paño; el resto, el “no paño”, contiene todas las bolas, pero también las bandas, la sala y el taco.
El problema
La lección 1 encontró el rango del paño en HSV. En el proyecto las bolas son lo que no es paño, así que el siguiente paso es marcar cada píxel como paño o no, y luego quedarse con los otros.
Un rango, una máscara
import cv2
import numpy as np
img = cv2.imread("pool.webp")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower = np.array([75, 80, 40])
upper = np.array([100, 255, 255])
cloth = cv2.inRange(hsv, lower, upper)
print(cloth.shape, cloth.dtype, np.unique(cloth))
print("cloth:", round(100 * np.mean(cloth > 0), 1), "% of pixels")
cv2.imshow("cloth", cloth)(720, 960) uint8 [ 0 255] cloth: 79.3 % of pixels
| Llamada | Argumentos | Qué hace |
|---|---|---|
cv2.inRange(hsv, lower, upper) | una imagen, y el valor mínimo y máximo permitidos en cada canal | 255 donde todos los canales están dentro de su rango, 0 en el resto |
np.unique(cloth) | un array | los valores distintos que contiene: aquí solo 0 y 255 |
El resultado tiene un canal, no tres, y solo dos valores. Un píxel queda blanco solo si pasa las tres pruebas: tono entre 75 y 100, y saturación entre 80 y 255, y valor entre 40 y 255 [1].
Inviértela y luego aplícala
Las bolas son los agujeros del paño. bitwise_not intercambia negro y blanco, y bitwise_and con mask= conserva los píxeles de la foto solo donde la máscara es blanca:
not_cloth = cv2.bitwise_not(cloth)
# conserva los colores de la foto donde la máscara es blanca
cut = cv2.bitwise_and(img, img, mask=not_cloth)
cv2.imshow("not cloth", cut)
| Llamada | Qué hace |
|---|---|
cv2.bitwise_not(mask) | 255 pasa a 0 y 0 pasa a 255 |
cv2.bitwise_and(img, img, mask=m) | img donde m es blanca, negro en el resto |
Todas las bolas están ahí. También está todo lo que no es la mesa: las bandas, las troneras, la sala oscura y el taco. La máscara responde “¿este píxel es paño?”, y la sala no es paño. La lección 4 usa la máscara del paño por segunda vez para encontrar el contorno de la mesa, y así se puede descartar la sala. Antes de eso, la lección 3 limpia las motas.
Mueve el rango tú mismo
Los mismos seis números, en controles deslizantes, aplicados a la misma foto en un tamaño menor. Sube H min de 75 hacia 85 y la máscara empieza a romperse a lo largo del borde cercano, donde el tono del paño es más bajo; baja V min a 0 y trozos de la sala oscura se unen al paño, el fallo que mide la lección 5.
Rómpelo
bitwise_and espera dos imágenes de la misma forma. La foto tiene tres canales y la máscara uno, así que pasar la máscara como segunda imagen, en lugar de como mask=, falla:
# la máscara pasada como segunda imagen, no como mask=
cut = cv2.bitwise_and(img, not_cloth)Traceback (most recent call last): File "<exec>", line 2, in <module> cv2.error: OpenCV(4.11.0) /home/runner/work/pyodide-recipes/pyodide-recipes/packages/opencv-python/build/opencv-python-4.11.0.86/opencv/modules/core/src/arithm.cpp:214: error: (-209:Sizes of input arguments do not match) The operation is neither 'array op array' (where arrays have the same size and type), nor 'array op scalar', nor 'scalar op array' in function 'binary_op'
OpenCV avisa que los tamaños de las entradas no coinciden. Las máscaras van en el argumento mask=.
Tu turno
El blanco tiene cualquier tono, muy poca saturación y un valor alto. Crea una máscara llamada white que seleccione la bola blanca en x = 791, y = 311 y deje el paño en negro.
white = cv2.inRange(hsv, (0, 0, 0), (179, 255, 255)) # estrecha esto
cv2.imshow("white", white)Por qué funciona
Una máscara es una imagen binaria: cada píxel tiene uno de dos valores, y los dos significan “sí” y “no”. inRange hace la misma pregunta de sí o no a cada píxel: si sus tres números caen dentro de una caja en el espacio HSV [1] [2]. Gonzalez y Woods tratan la segmentación por color en HSI, un espacio cercano a HSV que también separa el tono del brillo, en su capítulo sobre el color [3]. La sección final de Colour spaces: choosing axes you can threshold (en inglés) hace lo mismo con el rojo, cuyo tono da la vuelta en 0.
Errores comunes
- Inferior y superior intercambiados.
inRangeno los ordena. Un límite inferior por encima del superior no coincide con nada, y no da error. - El rojo en un solo rango. El rojo está cerca de 0 y también de 179. Haz dos máscaras y únelas con
cv2.bitwise_or. - La máscara como argumento de imagen. Pásala como
mask=, como muestra la celda Rómpelo. Tiene que ser un arrayuint8de un canal con el mismo alto y ancho que la imagen. - Esperar que “no paño” signifique “bolas”. Invertir una máscara selecciona todo lo demás, y en una foto eso incluye el fondo.
Referencias
[1] OpenCV. Thresholding Operations using inRange (tutorial, doc/tutorials/imgproc/threshold_inRange/threshold_inRange.markdown). OpenCV 4.11.0, commit 31b0eee. github.com/opencv/opencv
[2] OpenCV. Changing Colorspaces (Python tutorial, doc/py_tutorials/py_imgproc/py_colorspaces/py_colorspaces.markdown). OpenCV 4.11.0, commit 31b0eee. github.com/opencv/opencv
[3] Gonzalez, R. C., & Woods, R. E. (2018). Digital Image Processing (4th ed.), section 7.7 “Using Color in Image Segmentation”, Segmentation in HSI Color Space, p. 576. Pearson.
[4] MarkBuckawicki. Billiards table 2.JPG (27 Feb 2014). Wikimedia Commons, CC0 1.0. commons.wikimedia.org