Exercise 02 is the moment the piscine clicks: you discover that a photograph is nothing but a 3D array of integers. Once you see an image as numbers, every later exercise — zoom, rotate, color filters — becomes plain array manipulation.
The shape of an image
Load a JPEG and print its shape and you get something like (257, 450, 3). Read it the same way as any array:
- 257 — axis 0, the height (number of pixel rows).
- 450 — axis 1, the width (pixels per row).
- 3 — axis 2, the color channels: Red, Green, Blue.
So a single pixel is a triple like [19, 42, 83] — a little red, more green, a lot of blue. Each channel is usually an 8-bit integer from 0 to 255 (dtype uint8).
Note the order: it is (height, width), rows before columns — the same axis-0-is-rows convention from the 2D-array exercise. This trips up everyone at least once, because we say "width × height" in everyday language but arrays are indexed the other way.
Loading the file
The subject allows any image library; Pillow (PIL) is the standard choice, and NumPy turns the image object into an array:
import numpy as np
from PIL import Image
def ft_load(path: str):
"""Load an image, print its format, return its RGB pixel array."""
img = Image.open(path)
img = img.convert("RGB") # force 3 channels
array = np.array(img)
print("The shape of image is:", array.shape)
return array
Two details matter:
convert("RGB")normalizes the channel count. A PNG might carry a 4th alpha channel (RGBA); a scan might be grayscale (1 channel). Converting guarantees the(H, W, 3)shape the exercise expects.np.array(img)is the bridge from "image object" to "pixel matrix". From here it is all NumPy.
Handle JPG and JPEG (and errors)
The subject explicitly requires JPG/JPEG support and a clear message on failure. The single most common error is "file not found", and a piscine rule says an uncaught exception fails the exercise. So wrap it:
def ft_load(path: str):
try:
img = Image.open(path).convert("RGB")
except FileNotFoundError:
print("Error: file not found:", path)
return None
except Exception as e:
print("Error:", e)
return None
array = np.array(img)
print("The shape of image is:", array.shape)
return array
Pillow already recognizes JPG and JPEG (same format, two extensions) out of the box, plus PNG, BMP and more — you do not branch on the extension yourself.
Why this representation is so powerful
Because the image is "just numbers", every transformation is arithmetic:
- Crop / zoom → slice the array (next exercise).
- Rotate / flip → transpose the axes.
- Brighten → add a constant to every pixel.
- Invert → compute 255 - pixel.
- Grayscale → average the three channels.
That is the whole roadmap of the Array module, and it all rests on the idea you just unlocked here.
Takeaways
- An RGB image is a
(height, width, 3)array ofuint8values 0–255. - Axis order is rows (height) first, columns (width) second — not the spoken "width × height".
- Use Pillow to open and
convert("RGB"), thennp.arrayto get pixels. - Always guard file loading with a clear error message.