To a computer an image is just a grid of numbers. Here is how OpenCV and NumPy store, read and change them.
A digital image is a 2-D array (a matrix) of pixels.
(H, W)
A 2-D array: each pixel is one intensity value, 0 to 255.
(H, W, 3)
A 3-D array with three channels, one each for blue, green and red.
uint8
Usually an unsigned 8-bit integer, so every value lies in 0 to 255.
Move over the photo to read any pixel, in B, G, R order.
The origin (0, 0) is the top-left corner.
First index = row (y), second = column (x).
Write it as image[row, col] or image[y, x].
Grayscale: one number per pixel.
Colour (BGR in OpenCV): three numbers per pixel.
import cv2
import numpy as np
# images are numpy arrays
img = np.array([[0, 50, 100],
[150, 200, 255]], dtype=np.uint8)
# ans: a 2x3 grayscale image
print(img.shape)
# ans: (2, 3): 2 rows, 3 columns
print(img.dtype)
# ans: uint8: values 0-255
2 rows × 3 columns
Press → and the numbers become pixels.
# load a colour image
img = cv2.imread('image.jpg')
# ans: loads in BGR order (not RGB!)
print(img.shape)
# ans: (height, width, channels)
height, width, channels = img.shape
# ans: height = rows, width = cols, channels = 3
# load as grayscale
gray = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# ans: loads a single-channel image
print(gray.shape)
# ans: (height, width): no channel dimension
# accessing individual pixels
img = cv2.imread('image.jpg')
pixel = img[100, 50]
# ans: the pixel at row 100, col 50
print(pixel)
# ans: [B, G, R] values (3 numbers)
# modify a pixel
img[100, 50] = [255, 0, 0]
# ans: sets the pixel to blue (BGR order)
Row first, then column:img[y, x]
# create a blank black image
black = np.zeros((480, 640, 3), dtype=np.uint8)
# ans: 480x640 black image (all zeros)
# create a white image
white = np.ones((480, 640, 3), dtype=np.uint8) * 255
# ans: 480x640 white image (all 255s)
# create a coloured image
red = np.zeros((480, 640, 3), dtype=np.uint8)
red[:, :, 2] = 255
# ans: red image (3rd channel = red in BGR)
height × width × channels × bytes per value
1080 × 1920 × 3 × 1 = 6,220,800 bytes
≈ 6.2 MB for one uncompressed Full-HD frame.
1 byte per value instead of 4 for int32.
The eye can't tell apart more than about 256 intensity levels.
It is what most image formats (JPEG, PNG) store.
Answer first, then press → to check.
What is the shape?
img = np.zeros((300, 400, 3))
(300, 400, 3): 300 rows, 400 columns, 3 channels.
How many pixels in total?
300 × 400 = 120,000 pixels.
How many values in total?
300 × 400 × 3 = 360,000 values.
What colour is the pixel [255, 255, 0] in BGR?
Cyan: blue = 255, green = 255, red = 0. (Yellow would be [0, 255, 255].)
Why use uint8 instead of int?
It saves memory, and 0 to 255 is enough range for images.
What is the difference?
gray = np.zeros((100, 100))
color = np.zeros((100, 100, 3))
gray is 2-D; color is 3-D with 3 channels.
img.shape returns (480, 640, 3). Which number is the height?
480: the first dimension is the rows.
What happens with the value 300 in uint8?
img = np.array([[300]], dtype=np.uint8)
It overflows: 300 % 256 = 44 (older NumPy wraps it; NumPy 2 raises an OverflowError instead).
Create a 100×100 grayscale image.
gray = np.zeros((100, 100), dtype=np.uint8) # ans:
# ans: a black grayscale image
Create a 100×100 colour image.
color = np.zeros((100, 100, 3), dtype=np.uint8) # ans:
# ans: a black colour image with 3 channels
Access the pixel at row 10, column 20.
img = np.random.randint(0, 256, (50, 50, 3),
dtype=np.uint8)
pixel = img[10, 20] # ans:
# ans: an array of 3 BGR values
Create a green image (BGR).
green = np.zeros((100, 100, 3), dtype=np.uint8)
green[:, :, 1] = 255 # ans:
# ans: the green channel (index 1) set to 255
Create a half-white, half-black image.
img = np.zeros((100, 100), dtype=np.uint8)
img[:50, :] = 255 # ans:
# ans: top 50 rows white, bottom 50 rows black
Create a solid cyan image (blue + green).
cyan = np.zeros((100, 100, 3), dtype=np.uint8)
cyan[:, :, 0] = 255 # ans:
cyan[:, :, 1] = 255 # ans:
# ans: blue + green: every pixel is [255, 255, 0]
How do you get the image dimensions?
height = img.shape[0]
width = img.shape[1]
channels = img.shape[2]
# ans: shape is (height, width, channels)
What is the memory size of a 1024×768 RGB image?
1024 × 768 × 3 = 2,359,296 bytes ≈ 2.4 MB.
What colour is this image?
img = np.full((100, 100, 3), [255, 0, 0], dtype=np.uint8)
# ans: blue: every pixel is [255, 0, 0] in BGR
What is img.ndim for a grayscale image?
gray = np.zeros((100, 100))
# ans: 2 (a 2-dimensional array)