Vocabulary for this course

The terms the chapters assume, in plain words. You can open this from any chapter.

anchor
A reference box of a fixed size and shape that a detector adjusts to fit an object.
augmentation
Random changes to training images that keep the correct answer intact, such as crops, flips and brightness shifts.
bounding box
Four numbers marking an object’s edges, either as two corners or as a centre, width and height.
channel
One grid of values per colour or per learned pattern; an RGB image has three such grids.
convolution
Sliding a small grid of weights across an image, multiplying and summing at each position.
feature map
The grid of responses one kernel produces when slid across an image.
IoU
The overlap area of two boxes divided by the area they cover together; one means identical, zero means no overlap.
kernel
The small grid of weights a convolution slides over an image.
normalisation
Rescaling values to a common range or spread, often using statistics from the training images.
padding
Extra values, usually zeros, added around an image’s border so a sliding window can reach the edge.
pixel
One number in an image grid, giving brightness or colour strength at a single position.
pooling
Summarising each small window of a feature map into one value, such as its maximum or average.
receptive field
The part of the input that can affect one output value.
stride
How far a sliding window moves between positions; larger steps shrink the output.

Computer vision from the ground up

Pixels and channels

An image is a grid of numbers with a colour model attached. Almost every vision bug starts with the shape, the range or the channel order.

Part 1 of 10

Intermediate13 min readBack to Courses

A grid of numbers

A pixel is one sample of light. A grayscale image stores one number per pixel. A colour image stores three, one for red, green and blue. A 224 by 224 colour image is therefore 150,528 numbers, and a batch of 32 of them is nearly five million. A batch is the group of images processed together. That count is why vision models are memory-bound long before they are compute-bound. Memory-bound means the cost is set by moving data, not by arithmetic.

Coordinates start at the top left. The x axis runs right and the y axis runs down. That matches how images are stored, not how graphs are drawn. A point is addressed as row then column, so the first index selects the height and the second selects the width. Swapping them gives a transposed image that still looks like an image.

Resolution is a sampling decision, not a property of the scene. Downsampling throws away detail permanently. Upsampling invents values that were never measured. A model trained on 224-pixel crops has no idea what the same object looks like at 4,000 pixels, unless the pipeline resizes consistently.

Channels and layout

An image library usually returns height, width and channels: (224, 224, 3). A channel is one colour plane, such as red or blue. A framework usually wants channels, height and width: (3, 224, 224), with a batch axis in front. The numbers are identical; only the order of the axes changes. A transpose at the boundary is the whole conversion. Forgetting it is the most common shape error in vision.

Channel order is a separate promise. PIL and most cameras give RGB; OpenCV gives BGR by default. A model trained on RGB and fed BGR sees red and blue swapped. That is not an error and does not crash. It performs worse, and the cause is invisible in the loss curve. Convert explicitly at the point where the image enters your code.

The batch axis adds a fourth dimension, (N, C, H, W). A single image is (C, H, W). Adding the batch axis with unsqueeze gives (1, C, H, W), and the model sees the same rank it saw during training. Most shape errors at inference are a missing batch axis, not a wrong model.

From a file to a tensor

from PIL import Image
import numpy as np

image = Image.open("cat.png").convert("RGB")
array = np.asarray(image)
print(array.shape, array.dtype)          # (height, width, 3) uint8

tensor = np.transpose(array, (2, 0, 1)) / 255.0
print(tensor.shape, tensor.min(), tensor.max())

An image you can print

Build a small grayscale image and render it with a character ramp. Then move the bright square and confirm that the ASCII picture follows the numbers.

Output
Click "Run" to execute your code

Range, dtype and the bugs they cause

An 8-bit image holds integers from 0 to 255. Adding ten to a white pixel does not clip to 255 in NumPy. It wraps to 9, because the result is stored in the same unsigned byte. A byte is the 8-bit storage unit, and unsigned means it cannot hold a negative number. Brightness adjustments, blends and normalisation all need a float copy first. Normalisation rescales values to a standard range. The wrap is silent and produces a plausible-looking but wrong image.

Frameworks expect float values, usually scaled to zero through one and then standardised by a mean and a standard deviation. The scaling is not cosmetic. A raw 0 to 255 input into a layer with small weights produces enormous activations and saturated nonlinearities. Know the range at every boundary, and assert it once at the entrance.

Dtype is the other half. Dtype means the numeric type of each value. A float64 array silently doubles memory and can slow a GPU path that expects float32. A bool mask is not an image, and an integer label array is not a feature map. A feature map is a grid of responses produced by a layer. Print dtype, shape, min and max once per batch in development. Four numbers catch most pipeline bugs before the model does.

A uint8 image plus a brightness offset wraps around: white plus ten becomes near-black. Convert to float before any arithmetic, then clip and convert back only for display.

Metadata is part of the image

A JPEG can carry an orientation flag that tells the viewer to rotate it. The pixels are stored sideways; the flag makes them look upright. A loader that ignores EXIF gives you a rotated image and a model that learned the wrong orientation. EXIF is the metadata stored inside the file. Most libraries expose the flag, and most pipelines forget it.

Colour profiles and alpha channels are the other two quiet inputs. An alpha channel stores transparency, so an image with one has four channels, not three. A model that expects three will either fail or silently take the first three. Resizing also changes the aspect ratio, which is width divided by height, unless you choose the crop deliberately. These are not edge cases. They are the normal state of real image files.

A loader is part of the model contract. The same resize interpolation, the same colour conversion and the same EXIF handling must run at training and inference. Otherwise the model sees a distribution it was never trained on. Interpolation is how new pixels are estimated when an image is resized. Save the preprocessing configuration next to the weights. It is as much a part of the model as the parameters.

Split the channels

Separate a two-by-two RGB image into its three channels, then convert each pixel to luminance. The red pixel becomes dark gray even though it is the brightest in the red channel.

Output
Click "Run" to execute your code

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

What does a 224 by 224 RGB image look like as a NumPy array from PIL?