Day 8: OpenCV I: images as arrays; color spaces (RGB vs HSV)
Where Day 3's array model meets a real photo
OpenCV (cv2) is a NumPy user, not a replacement for it — every image you load is an ndarray, and everything from Days 3-4 (shape, dtype, vectorization, broadcasting) applies directly. Today starts the four-day OpenCV run that ends in the Catalog Tool.
The gotcha that will bite you at least once
OpenCV loads images with channels in BGR order (Blue-Green-Red), not the RGB order every other library (Matplotlib, PyTorch, PIL) expects. Forget to convert and every color-based operation is subtly wrong — most commonly, a photo displays or processes with red and blue swapped.
import cv2
img_bgr = cv2.imread("garment.jpg") # shape (H, W, 3), BGR order
img_bgr.shape, img_bgr.dtype # e.g. (1024, 768, 3), uint8
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # for display / other librariesRGB vs HSV
RGB encodes a pixel as how much red, green, and blue light it has — intuitive to build, but color and brightness are tangled together across all three channels. Dim the lights on a red shirt and *all three* RGB values drop, so 'is this pixel red?' becomes a moving target. HSV (Hue, Saturation, Value) separates them explicitly: Hue is the actual color (as an angle on a color wheel, independent of lighting), Saturation is how vivid vs washed-out it is, and Value is brightness. Filtering 'is this pixel red' becomes a check on Hue alone — robust to shadows, highlights, and lighting changes across a whole catalog shoot.
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)
# "red" hues wrap around 0/180 in OpenCV's 0-180 hue range — two ranges needed
lower_red1, upper_red1 = (0, 70, 50), (10, 255, 255)
lower_red2, upper_red2 = (170, 70, 50), (180, 255, 255)
mask1 = cv2.inRange(img_hsv, lower_red1, upper_red1)
mask2 = cv2.inRange(img_hsv, lower_red2, upper_red2)
red_mask = cv2.bitwise_or(mask1, mask2) # a boolean-style mask — Day 4's idea againKey terms
- BGR
- OpenCV's default channel order for loaded images — Blue, Green, Red — the reverse of the RGB every other library expects.
- HSV
- Hue-Saturation-Value color space that separates color identity (Hue) from lighting/brightness (Value).
- inRange
- OpenCV's function for producing a binary mask of pixels falling within given per-channel bounds.
Stage 0 exit criterion: you should now be able to
You load an image with cv2.imread and pass it straight to plt.imshow without converting it first. What happens?