Each channel is shown as a grayscale image: white = 255.
The same colour can be written as different sets of numbers. Picking the right set makes a task easy.
A colour space is a mathematical model for representing colours as numbers.
Why several? Different tasks need different representations.
Red, Green, Blue. Device-dependent.
Best for storage and transmission.
Hue, Saturation, Value. Perceptually intuitive.
Best for colour segmentation.
Lightness, A (green–red), B (blue–yellow).
Perceptually uniform.
COLOR_BGR2RGB swaps B and R
RGB is standard in most libraries. BGR is OpenCV's default.
import cv2
# opencv uses BGR by default
img_bgr = cv2.imread('image.jpg')
# ans: blue, green, red order
# convert to RGB
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
# ans: red, green, blue order
# channels are swapped
print(img_bgr[0, 0]) # [B, G, R]
print(img_rgb[0, 0]) # [R, G, B]
# ans: the same values, in a different order
# convert to grayscale
img = cv2.imread('image.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# ans: single-channel intensity
print(gray.shape)
# ans: (height, width): no channel dimension
# grayscale formula (weighted average)
# Gray = 0.299*R + 0.587*G + 0.114*B
# ans: green weighs most: the eye is most sensitive to it
# convert to HSV
img = cv2.imread('image.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# ans: hue, saturation, value
# split HSV channels
h, s, v = cv2.split(hsv)
# ans: three separate channels
print(h.shape)
# ans: (height, width)
# HSV ranges in OpenCV
# ans: H: 0-179, S: 0-255, V: 0-255
# detect red objects using HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define the red range (hue wraps around)
lower_red = np.array([0, 100, 100])
upper_red = np.array([10, 255, 255])
# ans: hue 0-10 is red, with high saturation and value
mask = cv2.inRange(hsv, lower_red, upper_red)
# ans: binary mask: red regions are 255
# apply the mask to extract red objects
result = cv2.bitwise_and(img, img, mask=mask)
# ans: keeps only the red regions
cv2.inRangeWhen H low > H high the range wraps through 179 → 0: two inRange masks joined with cv2.bitwise_or.
# split BGR channels
b, g, r = cv2.split(img)
# ans: separate B, G, R channels (each is grayscale)
# merge channels back
merged = cv2.merge([b, g, r])
# ans: reconstructs the original image
# swap red and blue channels
swapped = cv2.merge([r, g, b])
# ans: BGR becomes RGB order: reds and blues trade places
# convert to LAB
img = cv2.imread('image.jpg')
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
# ans: L (lightness), a, b (colour opponents)
l, a, b = cv2.split(lab)
# ans: L is a grayscale-like luminance
# LAB advantages
# ans: perceptually uniform (distance = perceived difference)
# ans: useful for colour correction and white balance
Each channel is shown as a grayscale image: white = 255.
Answer first, then press → to check.
What does cvtColor do?
rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
It converts between colour spaces.
Why use HSV for colour detection?
It separates colour (H) from lighting (V), so thresholds are easier.
What does this print?
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
print(gray.ndim) # ans: 2: a 2-D array, no channel dimension
Which colour order does cv2.imread give you by default?
BGR (blue, green, red).
Which channel matters most for grayscale? Gray = 0.299·R + 0.587·G + 0.114·B
Green (0.587 is the highest weight).
What is the hue range in OpenCV?
0 to 179: it stands for 0 to 360°, divided by 2.
When do you use the LAB colour space?
Colour correction, white balance, whenever you need perceptual uniformity.
What does inRange return?
mask = cv2.inRange(hsv, lower, upper)
A binary mask: 255 where the value is in range, 0 elsewhere.
Convert BGR to RGB.
img_bgr = cv2.imread('img.jpg')
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # ans:
# ans: swaps the red and blue channels
Extract the red channel from an RGB image.
r = img_rgb[:, :, 0] # ans:
# ans: red is the first channel in RGB
Make a red-only image.
red_only = img_rgb.copy()
red_only[:, :, 1:] = 0 # ans:
# ans: green and blue set to 0, red kept
Detect blue in HSV.
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_blue = np.array([100, 50, 50])
upper_blue = np.array([130, 255, 255])
mask = cv2.inRange(hsv, lower_blue, upper_blue) # ans:
# ans: a binary mask of the blue regions
Apply the mask to the image.
result = cv2.bitwise_and(img, img, mask=mask) # ans:
# ans: extracts only the blue regions
Invert the mask.
inv_mask = cv2.bitwise_not(mask) # ans:
# ans: 0 becomes 255 and 255 becomes 0
What does this produce?
b, g, r = cv2.split(img)
swapped = cv2.merge([r, g, b])
# ans: an RGB image made from a BGR one
Extract the hue channel.
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
hue = hsv[:, :, 0] # ans:
# ans: hue values, 0-179 in OpenCV
What happens here? Are img and back the same?
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
back = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
# ans: a grayscale image with 3 identical channels
# ans: not the same: the colour information is lost