Colour spaces and cvtColor

OpenCV · Essentials · 10 min

Most algorithms want grayscale, other libraries want RGB, and colour segmentation works far better in HSV.

cv2.cvtColor(img, code) converts an image between colour spaces. The codes read like COLOR_<from>2<to>:

  • cv2.COLOR_BGR2GRAY: one channel, a weighted sum 0.299R + 0.587G + 0.114B
  • cv2.COLOR_BGR2RGB: needed before handing images to matplotlib, Pillow or most deep learning models
  • cv2.COLOR_BGR2HSV: hue, saturation, value
  • cv2.COLOR_BGR2LAB: perceptually more uniform; good for colour distances and contrast tweaks

HSV separates which colour (hue) from how strong (saturation) and how bright (value). A red ball under shade and in sunlight has very different BGR values but a similar hue, which is why colour thresholding is done in HSV.

For 8-bit images OpenCV squeezes hue into 0 to 179 (degrees divided by 2) so it fits in a byte; S and V use 0 to 255. Red sits at both ends of the hue circle, around 0 and around 179, so selecting red needs two ranges.

Gray to BGR (COLOR_GRAY2BGR) does not invent colour; it copies the one channel three times so you can draw coloured annotations on it.

Flipping channel order with slicing, img[..., ::-1], also converts BGR to RGB, but it returns a non-contiguous view that some cv2 functions reject; cvtColor always returns a fresh contiguous array.

Example

import cv2
import numpy as np

img = np.zeros((1, 3, 3), np.uint8)
img[0] = [(0, 0, 255), (0, 255, 0), (255, 0, 0)]   # red, green, blue (BGR)

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
print(gray)                 # [[ 76 150  29]]

rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
print(rgb[0, 0])            # [255   0   0]  red is first now

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
print(hsv[0, :, 0])         # [  0  60 120]  hue in 0..179 (degrees / 2)
print(hsv[0, :, 1:])        # saturation and value are 255

Remember

  • cv2.cvtColor(img, cv2.COLOR_X2Y) converts colour spaces
  • Convert BGR to RGB before passing images to other libraries
  • HSV separates colour from brightness; 8-bit hue runs 0 to 179
  • Red wraps around hue 0/179, so it needs two ranges

Watch out for

  • Using hue thresholds in degrees (0 to 360) on 8-bit HSV images, where hue is halved.

Check yourself

  1. In an 8-bit HSV image from OpenCV, a pure green pixel has hue value...
  2. Why is colour segmentation usually done in HSV rather than BGR?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.opencv.org/4.x/df/d9d/tutorial_py_colorspaces.html