import numpy as np
import matplotlib.pyplot as plt
print("NumPy version:", np.__version__)05 — NumPy for Computer Vision
NumPy for Computer Vision
NumPy is the core numerical array library used throughout Python computer vision. Images, coordinates, filters, features, and many geometric computations are naturally represented as NumPy arrays.
This notebook reviews the NumPy concepts that are especially useful in this course.
Before you begin
Open this notebook in VS Code and select the cv Conda environment as the notebook kernel.
The notebook is designed to be run from top to bottom. If variables become inconsistent because cells were executed out of order, restart the kernel and use Run All.
1. Arrays
A NumPy array stores values of a common data type in one or more dimensions.
a = np.array([1, 2, 3, 4, 5])
print(a)
print("shape:", a.shape)
print("dtype:", a.dtype)
print("ndim:", a.ndim)Creating arrays
Some of the most common constructors are:
np.array(...)np.zeros(...)np.ones(...)np.full(...)np.arange(...)np.linspace(...)
print(np.zeros((2, 3)))
print(np.ones((2, 3)))
print(np.full((2, 3), 7))
print(np.arange(0, 10, 2))
print(np.linspace(0, 1, 5))2. Images are arrays
A grayscale image is commonly represented as:
height × width
A color image is commonly represented as:
height × width × channels
For an RGB image, the channel dimension usually has size 3.
image = np.zeros((300, 400, 3), dtype=np.float32)
# First 100 columns: half-strength red
image[:, 0:100, 0] = 0.5
# Next 100 columns: full red
image[:, 100:200, 0] = 1.0
# Add a blue region in the top half
image[0:150, :, 2] += 0.35
plt.figure(figsize=(8, 5))
plt.imshow(np.clip(image, 0, 1))
plt.axis("off")
plt.show()
print("shape:", image.shape)
print("dtype:", image.dtype)3. Indexing and slicing
NumPy indexing is similar to Python list indexing, but it works across multiple dimensions.
For a color image:
image[row, column, channel]Useful slicing examples:
image[:, :, 0] # red channel
image[0:100, :, :] # first 100 rows
image[:, 50:150, :] # columns 50 through 149
image[::2, ::2, :] # every second row and columna = np.arange(1, 11)
print("array:", a)
print("first:", a[0])
print("last:", a[-1])
print("elements 2 through 5:", a[1:5])
print("every other element:", a[::2])Boolean indexing
A Boolean condition can select all elements that satisfy a criterion.
a = np.array([4, 17, 2, 25, 8, 31])
mask = a > 10
print("mask:", mask)
print("values > 10:", a[mask])
a[a > 10] = 100
print("modified:", a)Boolean indexing is extremely useful in image processing. For example, it can threshold every pixel without writing nested loops.
gray = np.linspace(0, 1, 400).reshape(20, 20)
binary = gray > 0.5
plt.figure(figsize=(4, 4))
plt.imshow(binary, cmap="gray")
plt.axis("off")
plt.show()4. Shape, transpose, reshape, and dimensions
a = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8]
])
print("a:")
print(a)
print("shape:", a.shape)
print("\na.T:")
print(a.T)
print("shape:", a.T.shape)a = np.arange(12)
print("original:", a.shape)
b = a.reshape(3, 4)
print("reshaped:")
print(b)
print("shape:", b.shape)
c = np.expand_dims(a, axis=0)
print("expanded shape:", c.shape)A dimension of size 1 is sometimes important because it controls how arrays interact during broadcasting or with machine-learning libraries.
5. Arithmetic and broadcasting
Arithmetic operators such as +, -, *, and / operate element-by-element.
a = np.array([1, 2, 3])
b = np.array([10, 20, 30])
print("a + b:", a + b)
print("a * b:", a * b)
print("a * 5:", a * 5)Broadcasting
Broadcasting lets NumPy combine arrays with compatible shapes without manually repeating data.
image = np.ones((4, 5, 3))
channel_scale = np.array([1.0, 0.5, 0.25])
scaled = image * channel_scale
print("image shape:", image.shape)
print("scale shape:", channel_scale.shape)
print("result shape:", scaled.shape)
print("pixel:", scaled[0, 0])6. Reductions and the axis argument
Functions such as sum, mean, min, and max can operate over the entire array or along a selected axis.
a = np.array([
[1, 2, 3],
[4, 5, 6]
])
print("sum all:", np.sum(a))
print("sum axis=0:", np.sum(a, axis=0))
print("sum axis=1:", np.sum(a, axis=1))
print("mean all:", np.mean(a))
print("mean axis=0:", np.mean(a, axis=0))For an image with shape (H, W, 3), averaging over axis=2 combines the color channels and produces an array with shape (H, W).
7. Matrix multiplication vs. elementwise multiplication
For arrays A and B:
A * Bperforms elementwise multiplication when the shapes are compatible.
A @ Bor
np.matmul(A, B)performs matrix multiplication.
A = np.array([
[1, 2],
[3, 4]
])
B = np.array([
[5, 6],
[7, 8]
])
print("Elementwise A * B:")
print(A * B)
print("\nMatrix multiplication A @ B:")
print(A @ B)np.dot has different behavior depending on the dimensionality of its inputs. For straightforward matrix multiplication in this course, @ is often the clearest notation.
u = np.array([1, 2, 3])
v = np.array([4, 5, 6])
print("vector dot product:", np.dot(u, v))
print("equivalently:", u @ v)8. Concatenating and stacking arrays
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
print("concatenate rows:")
print(np.concatenate([a, b], axis=0))
print("\nconcatenate columns:")
print(np.concatenate([a, b], axis=1))
print("\nstack new axis:")
print(np.stack([a, b], axis=0).shape)9. Vectorization: avoid unnecessary Python loops
NumPy operations are implemented efficiently in compiled code. Whenever possible, operate on entire arrays instead of iterating over individual pixels in Python.
import time
rng = np.random.default_rng(0)
A = rng.integers(0, 256, size=(800, 800), dtype=np.uint8)
# Python-loop version
loop_result = A.copy()
start = time.perf_counter()
for i in range(loop_result.shape[0]):
for j in range(loop_result.shape[1]):
if loop_result[i, j] > 100:
loop_result[i, j] = 255
loop_time = time.perf_counter() - start
# Vectorized version
vector_result = A.copy()
start = time.perf_counter()
vector_result[vector_result > 100] = 255
vector_time = time.perf_counter() - start
print(f"Loop: {loop_time:.4f} s")
print(f"Vectorized: {vector_time:.6f} s")
print("Same result:", np.array_equal(loop_result, vector_result))The exact timing depends on your computer, but the vectorized approach should normally be much faster and is also easier to read.
10. Preallocation
Repeatedly resizing an array is inefficient because data may need to be copied each time.
Prefer creating the final-size array first when the size is known.
# Preferred when the required size is known
a = np.empty(10, dtype=np.float32)
for i in range(10):
a[i] = i * 0.5
print(a)11. Views and copies
Slicing often creates a view into the original array rather than an independent copy. Changing the slice can therefore change the original array.
a = np.array([1, 2, 3, 4, 5])
view = a[1:4]
view[0] = 100
print("view:", view)
print("original:", a)Create an independent array with .copy() when you do not want later modifications to affect the original.
a = np.array([1, 2, 3, 4, 5])
independent = a[1:4].copy()
independent[0] = 100
print("copy:", independent)
print("original:", a)12. Data types and pixel ranges
NumPy arrays have an explicit dtype.
Common image types include:
uint8: integer values from 0 to 255;float32orfloat64: often normalized to the range 0 to 1.
Data type and value range both matter when performing image arithmetic or displaying images.
uint_image = np.array([0, 64, 128, 255], dtype=np.uint8)
float_image = uint_image.astype(np.float32) / 255.0
print(uint_image, uint_image.dtype)
print(float_image, float_image.dtype)Be careful with uint8 arithmetic
Unsigned 8-bit values cannot represent numbers below 0 or above 255. Arithmetic may wrap around.
x = np.array([250], dtype=np.uint8)
print("uint8:", x + np.array([10], dtype=np.uint8))
print("float:", x.astype(np.float32) + 10)For many image-processing calculations, convert to floating point before arithmetic and convert back only when needed.
13. NumPy and OpenCV
OpenCV images are NumPy arrays.
One important convention is that cv2.imread() loads color images in BGR channel order, while Matplotlib expects RGB.
import cv2
# Example for an image file:
#
# image_bgr = cv2.imread("image.jpg")
# image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
# plt.imshow(image_rgb)
# plt.axis("off")
# plt.show()
#
# print(image_bgr.shape)
# print(image_bgr.dtype)
print("OpenCV version:", cv2.__version__)14. Useful NumPy functions
A compact reference:
| Function | Purpose |
|---|---|
np.array |
Create an array |
np.zeros, np.ones, np.full |
Create initialized arrays |
np.arange, np.linspace |
Create numerical sequences |
arr.shape |
Array dimensions |
arr.dtype |
Data type |
arr.reshape(...) |
Change shape without changing data |
arr.T, np.transpose |
Reorder axes |
np.expand_dims |
Insert a dimension |
np.squeeze |
Remove dimensions of size 1 |
np.concatenate |
Join arrays along an existing axis |
np.stack |
Join arrays along a new axis |
np.sum, np.mean |
Reductions |
np.min, np.max |
Extrema |
np.argmin, np.argmax |
Index of extrema |
np.where |
Conditional selection |
np.clip |
Limit values to a range |
np.linalg.norm |
Vector/matrix norm |
@, np.matmul |
Matrix multiplication |
Practice Exercises
Try to solve these using NumPy operations. Prefer vectorized solutions when possible.
1. Mean
Calculate the mean value of all elements in arr.
arr = np.array([
[1, 2, 3],
[7, 8, 9],
[2, 7, 3]
])
# Your code here2. Row sums
Create an array containing the sum of each row.
arr = np.array([
[5, 6, 2, 5],
[4, 5, 2, 5],
[6, 7, 2, 5],
[1, 2, 8, 5],
[9, 1, 3, 2]
])
# Your code here3. Boolean indexing
Replace every even number in arr with 0.
arr = np.array([1, 4, 7, 8, 2, 5, 8, 1, 5, 7, 8, 9])
# Your code here4. Vectorize a loop
Replace the loop below with a NumPy expression.
arr = np.array([
[8, 1, 4],
[6, 2, 8]
])
result = np.zeros_like(arr)
for i in range(arr.shape[0]):
for j in range(arr.shape[1]):
result[i, j] = arr[i, j] ** 2
# Rewrite without explicit loops5. Combine columns
Create one array where each row contains [age, height, hours].
age = np.array([[14], [52], [24], [31]])
height = np.array([[65], [70], [68], [72]])
hours = np.array([[12], [40], [25], [35]])
# Your code here6. Thresholding
Set every element greater than 5 to 255 and every other element to 0, without loops.
arr = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
])
# Your code here7. Elementwise and matrix multiplication
Compute elementwise multiplication between m1 and m2.T, then compute matrix multiplication between m1 and m2.
m1 = np.array([
[4, 5, 2, 1],
[6, 8, 2, 1],
[1, 7, 9, 12]
])
m2 = np.array([
[2, 1, 3],
[4, 2, 5],
[6, 3, 1],
[2, 8, 4]
])
# Your code here8. Highest total hours
Find the person who worked the highest total number of hours.
people = np.array(["alice", "bob", "cindy", "david", "elyse"])
hours = np.array([
[8, 3, 6, 6, 7],
[5, 8, 8, 4, 6],
[9, 9, 5, 7, 8],
[8, 7, 6, 5, 4],
[6, 6, 7, 7, 6]
])
# Your code here9. Highest earnings
Using the arrays below, assume the pay rate is:
- day 1: $11/hour
- day 2: $12/hour
- day 3: $13/hour
- day 4: $14/hour
- day 5: $15/hour
Find the person who earned the most money.
people = np.array(["alice", "bob", "cindy", "david", "elyse"])
hours = np.array([
[8, 3, 6, 6, 7],
[5, 8, 8, 4, 6],
[9, 9, 5, 7, 8],
[8, 7, 6, 5, 4],
[6, 6, 7, 7, 6]
])
rates = np.array([11, 12, 13, 14, 15])
# Your code here10. Array pattern
Use m1 and m2 to create the following array without hard-coding the final values:
[0, 3, 1, 0, 0, 3, 1, 0, 0, 3, 1, 0]
Explore np.stack, np.column_stack, np.ravel, or reshaping.
m1 = np.array([0, 1, 0, 1, 0, 1])
m2 = np.array([3, 0, 3, 0, 3, 0])
# Your code hereAdditional reference
NumPy documentation:
When working with unfamiliar NumPy functions, reading the function documentation and checking the expected array shapes is often the fastest way to debug a problem.