Every OpenCV call takes and returns NumPy arrays, so knowing the layout lets you mix cv2 with plain NumPy freely.
OpenCV's Python package is imported as cv2. Unlike the C++ API with its cv::Mat class, the Python bindings use plain NumPy arrays for images. There is no special image type to learn.
The layout is fixed by convention:
- A colour image has shape
(height, width, 3): rows first, then columns, then channels. - A grayscale image has shape
(height, width), with no channel axis. - The usual dtype is
uint8, values 0 to 255. Some operations producefloat32orint16, for example gradients. - Channels are in BGR order, not RGB. This is a historical choice that every newcomer trips over; pixel
img[y, x]returns[blue, green, red].
Because indexing is img[row, col], the y coordinate comes first when you index. But OpenCV functions that take points, sizes or rectangles use (x, y) and (width, height). Keep both conventions in mind: NumPy indexing is (y, x), cv2 arguments are (x, y).
Since an image is an array, you can create one with np.zeros, crop with slicing and adjust brightness with arithmetic. Watch the dtype, though: NumPy uint8 arithmetic wraps around, while cv2.add saturates (clamps at 255).
Example
import cv2
import numpy as np
img = np.zeros((120, 200, 3), dtype=np.uint8) # black, 200 wide x 120 tall
img[:, :100] = (255, 0, 0) # left half BLUE (BGR!)
print(img.shape, img.dtype) # (120, 200, 3) uint8
print(img[10, 10]) # [255 0 0]
h, w = img.shape[:2]
print("width", w, "height", h) # width 200 height 120
a = np.array([[250]], dtype=np.uint8)
print(a + np.uint8(10)) # [[4]] NumPy wraps
print(cv2.add(a, np.array([[10]], np.uint8))) # [[255]] OpenCV saturatesRemember
- An image is an ndarray of shape (h, w) or (h, w, channels), usually uint8
- Colour channels are BGR, not RGB
- Index as img[y, x]; cv2 functions take points as (x, y)
- cv2 arithmetic saturates, NumPy uint8 arithmetic wraps
Watch out for
- Passing
img.shape[:2](h, w) where cv2 expects a size(w, h), which swaps width and height.