Foundations
Linear Algebra
The mathematics of vectors, matrices and linear maps that computer vision is written in, from images stored as vectors and data matrices to camera projections, homographies, rank, null spaces, eigenvalues and the numerical care needed to solve linear systems.
beginner
Linear algebra is the mathematics of vectors, matrices and the linear maps between them. Computer vision is written in it: a digital image is an array that can be read as a vector or a matrix, a data set of images or descriptors is a matrix with one sample per row, colour conversions, blurs and the layers of a neural network are matrix products, and the geometry of cameras and views is expressed as and matrices acting on homogeneous coordinates. Estimating a camera, a homography or a fundamental matrix comes down to solving a linear system, and whether the answer can be trusted depends on the rank and conditioning of that system.
This article is an overview of the parts of linear algebra that vision uses, and it links the articles that develop each part. Least squares covers overdetermined systems and their numerical solution. Two factorizations do much of the practical work: the singular value decomposition, which writes any matrix as with orthogonal and and non-negative singular values on the diagonal of , and which underlies rank estimation, low-rank approximation and the solution of homogeneous systems; and the eigendecomposition of symmetric matrices, which underlies principal component analysis.
Definition
Linear algebra studies vector spaces, sets whose elements can be added and scaled, and linear maps between them, functions with . In finite dimensions every vector is a list of numbers, every linear map from to is multiplication by an matrix, and the subject becomes the study of matrices: how they combine, which vectors they send to zero, which outputs they can reach, how they change lengths and angles, and how to undo them.
Intuition
Think of a grayscale image of pixels as one point in a space with axes, one per pixel. Brightening the image scales the point; blending two images is a weighted sum of two points; the difference between two images is an arrow from one point to the other, and its length measures how different they are. Many image operations are linear in this sense: a blur, a colour conversion, a derivative filter or a downsampling replaces each output pixel with a fixed weighted sum of input pixels, so each is a matrix, however large.
Geometry adds a second picture. A or matrix moves points of the plane or of space: it rotates, scales, shears or projects them. The columns of the matrix are where the coordinate axes go, so a matrix can be read directly as a description of what it does to the grid (Boyd and Vandenberghe, 2018, §7.1).
Both pictures lead to the same questions. Can the map be undone, or does it lose information? Which inputs does it ignore entirely? If the outputs are known, can the inputs be recovered, and how sensitive is the answer to noise in the outputs? Rank, null space, determinant and condition number are the answers.
Formal Definition
Notation follows the rest of the site: bold lowercase letters are column vectors, uppercase letters are matrices, is the transpose, and a data set is with one sample per row, the layout scikit-learn uses. Textbooks often store samples as columns instead (Boyd and Vandenberghe’s feature matrix in §8.2 is ), so the transposes in formulas from different sources differ.
Vectors, matrices and images
An grayscale image is a vector of length once its pixels are listed in an agreed order, row by row or column by column; a colour image is a vector of length , and a video of frames one of length (Boyd and Vandenberghe, 2018, §1.1). The same image is also naturally an matrix, with the row index giving the vertical position and the column index the horizontal one (§6.1). NumPy’s reshape switches between the two views without moving any data, and the digital image article covers the indexing and memory-layout conventions.
Stacking samples gives a data matrix. The pixels of a colour image form an matrix, one pixel per row; a collection of images of pixels each forms an matrix, one image per row; descriptors such as SIFT vectors form an matrix. Statistics of the data are then matrix expressions. With the column means subtracted to give , the empirical covariance matrix is
with denominator ; the unbiased estimate divides by , which is NumPy’s np.cov default. A covariance matrix is symmetric and positive semidefinite (Deisenroth, Faisal and Ong, 2020, §6.4.2).
Matrix products as linear maps
The product of an matrix and an -vector is a linear map from to , and every such map has this form. The -th column of is the image of the -th unit vector, which gives a quick way to write down the matrix of any linear transformation (Boyd and Vandenberghe, 2018, §7.1). Composing maps multiplies matrices: applying and then is the single map , and the order matters because matrix multiplication is not commutative.
Three families of maps recur in vision.
- Per-pixel maps. A colour conversion applies the same matrix to every pixel’s colour vector. For a data matrix with one pixel per row, the whole image is converted by one product, . The color spaces article describes the conversions; linear sRGB to CIE XYZ is purely linear, while RGB to YCbCr in JPEG adds an offset and is affine.
- Spatial maps. A blur or a derivative filter computes each output pixel as a weighted sum of nearby input pixels, so with the image written as a vector it is a large, sparse matrix. For 2D convolution of an image with a kernel, for a structured matrix built from the kernel’s entries (Boyd and Vandenberghe, 2018, §7.4). Libraries never form , but the view explains why blurring is a linear inverse problem and why a convolutional neural network layer, being linear before its activation function, can be computed as one large matrix product.
- Geometric maps. Rotations, scalings, reflections and projections of points are or matrices (Boyd and Vandenberghe, 2018, §7.1). A rotation matrix is orthogonal, , so its inverse is its transpose and it preserves lengths and angles (Deisenroth, Faisal and Ong, 2020, §3.4); rotation representations covers the ways of parameterizing it.
A translation, , is not linear, since it moves the origin. An affine map becomes linear in homogeneous coordinates, where a 2D point is written and the map is a matrix with last row . Allowing a general last row gives a projective transformation, the homography, defined only up to scale. A pinhole camera is a matrix from homogeneous 3D points to homogeneous image points, with the intrinsic matrix and the pose described in camera parameters. The division by the last coordinate that turns a homogeneous vector back into a point is the only nonlinear step, and keeping it outside the matrix is what lets chains of warps and projections collapse into single products. Image warping lists the matrices from translation to homography, and projective geometry places them in a hierarchy by what each preserves.
Column space, null space and rank
Two subspaces describe what a matrix does (Deisenroth, Faisal and Ong, 2020, §2.7.3):
- The column space (image, range) is the set of all outputs , the span of the columns of , a subspace of .
- The null space (kernel) is the set of inputs that sends to zero, , a subspace of . It is the general solution of the homogeneous system .
The rank of is the number of linearly independent columns, which always equals the number of linearly independent rows, and it is the dimension of the column space. A matrix has full rank when its rank is and is rank deficient otherwise (§2.6.2). The rank–nullity theorem ties the two subspaces together:
(Deisenroth, Faisal and Ong, 2020, Theorem 2.24). Every dimension of the input either reaches the output or is lost in the null space.
In vision, rank is a statement about how much independent information a matrix holds. A stack of images that are all combinations of underlying patterns has a data matrix of rank at most , however many pixels and images there are; real image collections are often close to such a low-rank matrix, which principal component analysis and low-rank approximation exploit. A fundamental matrix has rank 2 by construction, and its estimators enforce that rank explicitly (fundamental matrix). The camera centre is the null vector of the camera matrix (camera parameters).
Linear systems
A system of linear equations in unknowns is . It is square if , overdetermined if (a tall ) and underdetermined if (a wide ); with it is homogeneous and always has the solution (Boyd and Vandenberghe, 2018, §8.3). A solution exists exactly when lies in the column space of , that is when appending as an extra column does not raise the rank, and if one exists, every vector of the null space can be added to it, so it is unique only when the null space is (Deisenroth, Faisal and Ong, 2020, §2.3.1 and §2.6.2).
- Square and full rank. There is exactly one solution, , for every (Boyd and Vandenberghe, 2018, §11.2). Small calibrated problems, such as converting a pixel to a ray with , are of this kind.
- Overdetermined. With noisy measurements, more equations than unknowns are generally inconsistent, and the usual answer minimizes ; the least squares article covers the normal equations, the geometric view as a projection, and QR- and SVD-based solvers. Fitting a line to edge points, a translation to a window of image gradients as in Lucas–Kanade, or a camera to calibration points all take this form.
- Underdetermined. Fewer equations than unknowns leave a null space of solutions, and a choice must be made, typically the solution of smallest norm or one favoured by a regularizer. Recovering a high-resolution image from a low-resolution one is underdetermined in this sense, since the downsampling matrix is wide, which is why super-resolution needs a prior on what images look like.
- Homogeneous with a constraint. Many geometric estimators produce equations that are linear in the unknown entries of a matrix and have zero right-hand side. The direct linear transform (DLT) for a homography stacks two equations per correspondence into , with the nine entries of ; the eight-point algorithm does the same for the fundamental matrix, one equation per match. The trivial solution is excluded by requiring , which matches the fact that is defined only up to scale. With exact data the answer spans the one-dimensional null space of ; with noisy data has full column rank, and the unit vector minimizing is the right singular vector of for its smallest singular value, as the homography and least squares articles show.
Norms and inner products
The inner product (dot product) of two vectors is , and the Euclidean norm is . They define distance, , and angle, (Deisenroth, Faisal and Ong, 2020, §3.1–§3.4). Other norms weight the coordinates differently: the norm sums absolute values and the norm takes the largest one. For matrices, the Frobenius norm treats the entries as one long vector, and the spectral norm is the largest factor by which stretches any vector, its largest singular value.
On images these are familiar quantities. The squared Euclidean distance between two images, divided by the number of pixels, is the mean squared error. The inner product of two mean-subtracted patches, divided by their norms, is normalized cross-correlation, the cosine of the angle between them. A linear classifier scores an input by an inner product with a weight vector. The similarity and distance measures article compares these measures and others used for descriptors and embeddings.
Orthogonality and projections
Two vectors are orthogonal when their inner product is zero. A set of mutually orthogonal unit vectors is orthonormal, and a square matrix with orthonormal columns is an orthogonal matrix, , whose inverse is its transpose (Deisenroth, Faisal and Ong, 2020, §3.4). Coordinates in an orthonormal basis are just inner products, , which is why orthonormal bases are the convenient ones: the columns of a rotation matrix and the principal axes of a covariance matrix are both of this kind.
A projection is a linear map with : applying it twice changes nothing. The orthogonal projection onto the column space of a matrix with independent columns is
which sends to the closest point of that subspace, leaving a residual orthogonal to every column of (Deisenroth, Faisal and Ong, 2020, §3.8). If the columns of are orthonormal it simplifies to . Least squares is exactly this projection of the measurements onto the column space of the design matrix, as the least squares article shows. Projecting an image onto the span of a few basis images, keeping what they can express and discarding the rest, is the idea behind subspace methods for faces and appearance.
Determinants and invertibility
The determinant of a square matrix is the signed volume of the parallelepiped spanned by its columns: the factor by which the map scales areas (in 2D) or volumes, with a negative sign when it reverses orientation (Deisenroth, Faisal and Ong, 2020, §4.1). A square matrix is invertible exactly when its determinant is non-zero, equivalently when it has full rank (Theorems 4.1 and 4.3). The determinant is multiplicative, , and equals the product of the eigenvalues (Theorem 4.16). A rotation has determinant , a reflection , an affine image warp scales every area by for its block, and the rank-2 constraint on a fundamental matrix is .
As a numerical test of invertibility the determinant is nearly useless, and Deisenroth, Faisal and Ong note that direct numerical methods have replaced its explicit computation (§4.1). The reason is scale: for the matrix , which is perfectly well behaved, the determinant is , while a matrix with determinant 1 can be close to singular. Whether a system can be solved reliably is measured by the condition number below, and whether a matrix is singular in practice by its rank with a tolerance.
Eigenvalues and eigenvectors
An eigenvector of a square matrix is a non-zero vector that only scales, ; the scale is its eigenvalue. Eigenvectors are the directions a linear map leaves unturned. A general matrix may have complex eigenvalues or too few independent eigenvectors, but symmetric matrices, which are the ones vision meets most, are well behaved: by the spectral theorem, a real symmetric matrix has real eigenvalues and an orthonormal basis of eigenvectors, so with orthogonal and diagonal (Deisenroth, Faisal and Ong, 2020, Theorem 4.15). Any matrix of the form is symmetric and positive semidefinite, with non-negative eigenvalues (Theorem 4.14). This factorization is the eigendecomposition.
Two symmetric positive semidefinite matrices recur throughout vision:
- Covariance matrices. The eigenvectors of the covariance of a data matrix are the principal axes of the data and the eigenvalues are the variances along them; principal component analysis keeps the axes with the largest eigenvalues. For image patches or face images this gives a low-dimensional basis, and for the pixel colours of a region it gives the dominant colour direction.
- The structure tensor. The matrix of summed gradient products over a window has eigenvalues that classify the window as flat, edge or corner. Both are small in a flat region, one is large on an edge, and both are large at a corner; the Harris corner detector scores corners from the determinant and trace, and the aperture problem is the case of one zero eigenvalue.
The singular value decomposition extends the spectral theorem to every rectangular matrix: the singular values of are the square roots of the eigenvalues of , and the right singular vectors are its eigenvectors (Deisenroth, Faisal and Ong, 2020, §4.5).
Properties
Conditioning
A system can be exactly solvable and still useless if a small change in the data causes a large change in the answer. For a square invertible , perturb the right-hand side of to . The solution moves by , so , and . Dividing gives the standard bound
where the last form holds for the spectral norm and uses the largest and smallest singular values; np.linalg.cond computes it this way by default. The condition number is at least 1, equals 1 for orthogonal matrices, and is infinite for singular ones. It is a property of the problem, not of the algorithm: for the worst perturbations the bound is attained, so no method can guarantee better relative accuracy than times the relative error in the data, and rounding errors alone, about in double precision, are data errors of this kind.
In vision, ill-conditioning has recognizable sources. Undoing a blur is ill conditioned because blurring nearly erases fine detail, so the matrix has tiny singular values for high-frequency patterns. A homography or fundamental-matrix system built from raw pixel coordinates is badly conditioned because its columns differ in scale by orders of magnitude; translating and scaling the coordinates first, as the normalized eight-point paper argued, fixes most of it, and the homography article measures the effect. Forming squares the condition number, the reason least-squares solvers avoid the normal equations on hard problems.
Solve, do not invert
Mathematically , but numerical code almost never forms . A library solver factors (LU with pivoting, QR or Cholesky) and solves the triangular systems that result. Boyd and Vandenberghe count about floating-point operations to solve a square system through a QR factorization and about to compute the inverse through the same factorization, and a factorization can be reused for many right-hand sides at little extra cost (2018, §11.3). Deisenroth, Faisal and Ong add that computing the inverse is generally not recommended for reasons of numerical precision (2020, §2.3.4).
The accuracy argument is subtler than often stated. Druinsky and Toledo (2012) showed that if the computed inverse is a good left inverse, with of order times machine precision, then is as accurate as the solution of a backward-stable solver; but the residual of is not small in general, so the computed answer does not carry the usual certificate of a small backward error, and an inverse computed one way can be a good right inverse without being a good left inverse. Solving directly with a backward-stable factorization gives both the accuracy and the small residual without depending on how an inverse was computed, costs less, and is what np.linalg.solve, scipy.linalg.solve and the backslash operator of other systems do.
Rank in floating point
Exact rank deficiency does not survive rounding or noise: the columns of a measured matrix are almost never exactly dependent, so in floating point nearly every matrix has full rank. Numerical rank is therefore defined with a tolerance, as the number of singular values above a threshold. NumPy’s matrix_rank uses by default, with the machine precision, and its documentation advises a tolerance near the measurement uncertainty when the data are noisier than that (NumPy 2.3 documentation). The same issue appears whenever an algorithm tests for singularity: a matrix whose smallest singular value is a millionth of its largest is singular for any practical purpose, whatever its determinant.
Examples
The examples use NumPy 2.3.5 and SciPy 1.16.3 on synthetic images.
An image as a data matrix, and colour conversion as a matrix
The JPEG File Interchange Format (JFIF) converts 8-bit RGB to YCbCr by fixed weights plus an offset of 128 on the two chroma channels, and gives the inverse coefficients as well (Hamilton, 1992). Written with one pixel per row, the conversion of a whole image is one matrix product.
import numpy as np
np.set_printoptions(suppress=True)
# A 4 x 6 synthetic RGB image: left half orange, right half sky blue, one white pixel.
img = np.zeros((4, 6, 3))
img[:, :3] = [230, 120, 30]
img[:, 3:] = [90, 160, 230]
img[0, 0] = [255, 255, 255]
H, W, _ = img.shape
X = img.reshape(-1, 3) # data matrix: one pixel per row, n = H*W
print("image", img.shape, "-> data matrix", X.shape)
# JFIF RGB -> YCbCr: a 3x3 matrix plus an offset, i.e. an affine map.
M = np.array([[ 0.299, 0.587, 0.114 ],
[-0.1687, -0.3313, 0.5 ],
[ 0.5, -0.4187, -0.0813]])
c = np.array([0.0, 128.0, 128.0])
Y = X @ M.T + c # every pixel converted by one matrix product
ycc = Y.reshape(H, W, 3)
print("YCbCr of the white, orange and blue pixels:")
print(np.round(ycc[0, 0], 1), np.round(ycc[1, 0], 1), np.round(ycc[1, 5], 1))
# The same map as one 3x4 matrix acting on homogeneous pixels [R, G, B, 1].
A = np.hstack([M, c[:, None]])
Xh = np.hstack([X, np.ones((X.shape[0], 1))])
print("homogeneous form agrees:", np.allclose(Xh @ A.T, Y))
# The map is invertible: det(M) != 0, and inv(M) reproduces JFIF's inverse coefficients.
print("det(M) =", round(np.linalg.det(M), 4))
print("inv(M) =\n", np.round(np.linalg.inv(M), 4))
X_back = np.linalg.solve(M, (Y - c).T).T
print("max round-trip error:", np.abs(X_back - X).max())
Output:
image (4, 6, 3) -> data matrix (24, 3)
YCbCr of the white, orange and blue pixels:
[255. 128. 128.] [142.6 64.4 190.3] [147. 174.8 87.3]
homogeneous form agrees: True
det(M) = 0.2363
inv(M) =
[[ 1. -0. 1.402 ]
[ 1. -0.3441 -0.7141]
[ 1. 1.772 -0.0001]]
max round-trip error: 4.263256414560601e-14
White maps to chroma , no colour, because each chroma row of sums to zero, so any grey has zero chroma before the offset. The orange pixel has a red-difference value above 128 and a blue-difference value below it, and the sky blue pixel the reverse. Appending a 1 to each pixel turns the affine map into a single matrix, the same device that homogeneous coordinates use for geometry. The determinant is non-zero, so the conversion loses nothing, and the inverse matrix reproduces JFIF’s published inverse coefficients 1.402, 0.34414, 0.71414 and 1.772 to four decimals; the where JFIF has 0 comes from the forward coefficients being rounded to four decimals in the standard. The round trip uses solve rather than the inverse, and returns the pixels to within .
Rank of an image collection
Two hundred images are generated as random mixtures of three patterns: a horizontal ramp, a vertical ramp and a bright disk. As a data matrix, one image per row, the collection has the rank of the patterns, not of its size.
import numpy as np
rng = np.random.default_rng(0)
s = 32
yy, xx = np.mgrid[0:s, 0:s] / (s - 1)
# Three basis patterns: horizontal ramp, vertical ramp, a bright disk.
ramp_x = xx
ramp_y = yy
disk = ((xx - 0.5) ** 2 + (yy - 0.5) ** 2 < 0.25 ** 2).astype(float)
B = np.stack([ramp_x, ramp_y, disk]).reshape(3, -1) # 3 x 1024
# 200 images, each a random mix of the three patterns: X = C B, one image per row.
C = rng.uniform(0, 1, size=(200, 3))
X = C @ B # 200 x 1024
print("data matrix:", X.shape, " rank:", np.linalg.matrix_rank(X))
# Null space: any image orthogonal to all three patterns is invisible to X.
null_dim = X.shape[1] - np.linalg.matrix_rank(X)
print("null space dimension:", null_dim)
# A new mixed image lies in the row space; an unrelated pattern does not.
def residual_outside(img, basis):
coef, *_ = np.linalg.lstsq(basis.T, img, rcond=None) # project onto span of rows
return np.linalg.norm(img - basis.T @ coef) / np.linalg.norm(img)
new_mix = (0.3 * ramp_x + 0.9 * disk).ravel()
checker = ((np.floor(xx * 4) + np.floor(yy * 4)) % 2).ravel()
print(f"relative residual outside span: mix {residual_outside(new_mix, B):.1e}, "
f"checkerboard {residual_outside(checker, B):.2f}")
# Sensor noise makes the matrix full rank in floating point, but not "really".
Xn = X + rng.normal(0, 0.01, size=X.shape)
print("noisy rank, default tolerance:", np.linalg.matrix_rank(Xn))
print("largest singular values:", np.round(np.linalg.svd(Xn, compute_uv=False)[:5], 2))
print("noisy rank, tolerance 1.0:", np.linalg.matrix_rank(Xn, tol=1.0))
Output:
data matrix: (200, 1024) rank: 3
null space dimension: 1021
relative residual outside span: mix 1.0e-15, checkerboard 0.75
noisy rank, default tolerance: 200
largest singular values: [329.43 53.05 38.36 0.46 0.45]
noisy rank, tolerance 1.0: 3
The rank is 3 because every row is a combination of three patterns, and rank–nullity leaves dimensions of images that no combination of the rows can produce. A new mixture lies in the three-dimensional row space to rounding error, while three quarters of the checkerboard’s norm lies outside it: projection onto a subspace separates what a model can express from what it cannot. Adding Gaussian noise with a standard deviation of 0.01 makes the matrix full rank, 200, under NumPy’s default tolerance, yet the singular values show three large values and then a floor near 0.46 set by the noise. A tolerance placed in that gap recovers the rank that the data really have, which is the practical meaning of numerical rank.
Geometric maps and a homogeneous system
The corners of a image are mapped by an affine matrix and by a homography in homogeneous coordinates. The homography is then recovered from the four correspondences as the null space of the DLT system.
import numpy as np
from scipy.linalg import null_space
np.set_printoptions(suppress=True, precision=4)
# Corners of a 640 x 480 image, one homogeneous point (x, y, 1) per column.
P = np.array([[0, 640, 640, 0],
[0, 0, 480, 480],
[1, 1, 1, 1]], dtype=float)
def apply(T, P):
Q = T @ P
return Q[:2] / Q[2] # back to Cartesian: divide by the last row
def side_directions(Q):
top, bottom = Q[:, 1] - Q[:, 0], Q[:, 2] - Q[:, 3]
return top / np.linalg.norm(top), bottom / np.linalg.norm(bottom)
th = np.deg2rad(10)
A = np.array([[1.2 * np.cos(th), -np.sin(th), 50], # affine: last row (0, 0, 1)
[0.9 * np.sin(th), np.cos(th), 20],
[0, 0, 1]])
H = np.array([[0.9, 0.05, 30], # homography: general last row
[0.02, 1.1, 10],
[0.0004, 0.0002, 1]])
for name, T in [("affine", A), ("homography", H)]:
t, b = side_directions(apply(T, P))
print(f"{name:10s} top/bottom edges parallel: {np.isclose(abs(t @ b), 1)}"
f" det = {np.linalg.det(T):.3f}")
# Composition and inversion are matrix products and inverses.
print("H^-1 H P == P:", np.allclose(apply(np.linalg.inv(H) @ H, P), P[:2]))
# Recover H from the four correspondences: each gives two rows of A h = 0 (the DLT).
Q = apply(H, P)
rows = []
for (x, y), (u, v) in zip(P[:2].T, Q.T):
rows.append([-x, -y, -1, 0, 0, 0, u * x, u * y, u])
rows.append([0, 0, 0, -x, -y, -1, v * x, v * y, v])
M = np.array(rows) # 8 x 9
N = null_space(M) # orthonormal basis of {h : M h = 0}
print("M:", M.shape, " rank:", np.linalg.matrix_rank(M), " null space dim:", N.shape[1])
H_est = N[:, 0].reshape(3, 3)
print("recovered H (scaled so H[2,2] = 1):\n", H_est / H_est[2, 2])
Output:
affine top/bottom edges parallel: True det = 1.191
homography top/bottom edges parallel: False det = 0.974
H^-1 H P == P: True
M: (8, 9) rank: 8 null space dim: 1
recovered H (scaled so H[2,2] = 1):
[[ 0.9 0.05 30. ]
[ 0.02 1.1 10. ]
[ 0.0004 0.0002 1. ]]
The affine map keeps the image’s top and bottom edges parallel, as every affine map does, while the homography’s non-zero bottom row makes them converge, which is how a photographed rectangle becomes a trapezoid. Both determinants are non-zero, so both maps can be undone by their inverse matrices. Each correspondence contributes two equations that are linear in the nine entries of , because the unknown scale of cancels when and are cross-multiplied. Four correspondences give eight equations in nine unknowns, a rank-8 matrix whose null space is one-dimensional, and that null space is up to scale. SciPy’s null_space computes it from the singular value decomposition. With more than four noisy correspondences the matrix has full column rank and null_space returns no vectors; the estimate is then the singular vector of the smallest singular value, as described for the homography.
Ill-conditioning: undoing a blur
A row of 64 pixels with two bright bars is blurred by a Gaussian of standard deviation 1.5 pixels, written as a matrix whose rows are normalized weights. Recovering the sharp row is solving . The smallest errors and residuals depend on the BLAS and LAPACK library that NumPy is linked to: the output below came from NumPy 2.3.5 with OpenBLAS 0.3.30 on an Apple arm64 machine, and other builds can print different values in those lines.
import numpy as np
n = 64
# One row of a synthetic image: a dark background with two bright bars.
x_true = np.full(n, 0.2)
x_true[12:20] = 0.9
x_true[36:52] = 0.6
# Gaussian blur (sigma = 1.5 pixels) of the row, written as an n x n matrix.
i = np.arange(n)
A = np.exp(-0.5 * ((i[:, None] - i[None, :]) / 1.5) ** 2)
A /= A.sum(axis=1, keepdims=True) # each output pixel is a weighted mean
b = A @ x_true # the blurred row
print(f"cond(A) = {np.linalg.cond(A):.1e}")
def report(name, x):
err = np.linalg.norm(x - x_true) / np.linalg.norm(x_true)
res = np.linalg.norm(A @ x - b) / np.linalg.norm(b)
print(f"{name:16s} relative error {err:.1e} relative residual {res:.1e}")
report("solve(A, b)", np.linalg.solve(A, b))
V = np.linalg.inv(A)
report("inv(A) @ b", V @ b)
I = np.eye(n)
print(f"||inv(A) A - I|| = {np.linalg.norm(V @ A - I, 2):.1e} ||A inv(A) - I|| = {np.linalg.norm(A @ V - I, 2):.1e}")
# A tiny perturbation of the data, like 8-bit quantization of the blurred row.
b_q = np.round(b * 255) / 255
print(f"relative change in b from quantization: {np.linalg.norm(b_q - b) / np.linalg.norm(b):.1e}")
x_q = np.linalg.solve(A, b_q)
print(f"relative error after quantization: {np.linalg.norm(x_q - x_true) / np.linalg.norm(x_true):.1e}")
Output:
cond(A) = 3.1e+04
solve(A, b) relative error 8.1e-13 relative residual 2.3e-16
inv(A) @ b relative error 8.9e-11 relative residual 4.8e-13
||inv(A) A - I|| = 1.7e-10 ||A inv(A) - I|| = 1.2e-12
relative change in b from quantization: 2.2e-03
relative error after quantization: 9.0e+00
A blur of only 1.5 pixels already gives a condition number of about . With exact data, solve returns a residual at machine precision and an error within times machine precision, about , as the conditioning bound allows. In this run, multiplying by the computed inverse leaves a residual three orders of magnitude larger and an error about a hundred times larger. The two residuals of the inverse itself suggest why: this computed inverse is a good right inverse, , but a much poorer left inverse, and the accuracy guarantee of Druinsky and Toledo needs a good left inverse. Rounding the blurred row to 8-bit grey levels, a relative change of 0.2%, turns the solution into noise nine times larger than the image itself, an amplification of several thousand that stays below the bound . No solver can do better on this data; deblurring needs a different problem, typically regularized least squares, which Boyd and Vandenberghe (2018, §15.3) apply to image deblurring.
Common Misconceptions
- “A matrix is either invertible or not.” Exactly, yes; numerically, a matrix with a condition number of is invertible in name only. Check the condition number or the singular values, not whether the solver raised an error.
- “A small determinant means nearly singular.” The determinant scales with the size of the entries and with the dimension: in 100 dimensions has determinant and condition number 1. Conditioning is measured by .
- “Compute the inverse, then multiply.” It costs more and, as the blur example shows, can lose accuracy and the small residual a direct solver guarantees. Use
solve,lstsqor a factorization; form only when its entries are themselves needed, as when composing a homography’s inverse with other transforms. - “Rank is a fixed number.” For measured data it depends on a tolerance. Choose it from the noise level, not from machine precision, when the noise is far above machine precision.
- “Affine and linear mean the same thing.” A linear map fixes the origin; an affine map adds a translation. Homogeneous coordinates turn affine and projective maps into linear ones, at the cost of one extra coordinate and, for projective maps, a final division.
- “Rows and columns are interchangeable conventions.” They are, but formulas change with them. With one sample per row, a per-sample linear map is and the covariance is ; with one sample per column they are and . Mixing sources without checking their layout silently transposes results.
Where It Is Used
- Camera models and geometry. The pinhole camera model, camera parameters, homographies, the fundamental matrix and essential matrix, triangulation and camera calibration are matrices acting on homogeneous coordinates and are estimated from homogeneous linear systems, then refined by nonlinear least squares and bundle adjustment.
- Image processing. Colour conversion (color spaces), linear filtering and convolution, image warping and resampling are linear maps on pixel vectors or pixel coordinates.
- Local image analysis. The structure tensor’s eigenvalues drive the Harris corner detector, feature tracking and the aperture problem; Lucas–Kanade solves a small least-squares system per window.
- Estimation. Least squares, the Kalman filter and RANSAC’s minimal solvers are linear algebra on noisy measurements.
- Learning. Data matrices, covariance and its eigenvectors for principal component analysis, inner products in similarity measures and embeddings, and the matrix products inside every neural network layer, whose gradients backpropagation computes with transposed matrices.
Related
- Least Squares
The method of fitting a model to more measurements than unknowns by minimizing the sum of squared residuals, the workhorse of estimation in computer vision.
- Homogeneous Coordinates
Representing a point by a vector with one extra coordinate, defined only up to scale, so that translations, perspective projections, and points at infinity can all be handled with matrix multiplication.
- Homography
An invertible projective transformation of the plane, a 3×3 matrix defined up to scale, that relates two images of a planar scene or two images taken by a camera rotating about its center.
- Camera Intrinsic and Extrinsic Parameters
The parameters of the pinhole camera matrix P = K[R | t], split into intrinsics K, which map camera coordinates to pixels, and extrinsics R and t, which place the camera in the world.
- Color Spaces
A color space assigns numbers to colors; choosing and converting between RGB, CIE XYZ, CIELAB, YCbCr, and HSV correctly matters for both image processing and learning.
- Convolution
The operation that replaces every pixel by a weighted sum of its neighbors, with the weights given by a kernel; the basis of linear filtering and of convolutional networks.
References
- Boyd, S. & Vandenberghe, L. (2018). Introduction to Applied Linear Algebra: Vectors, Matrices, and Least Squares. Cambridge University Press.
- Deisenroth, M. P., Faisal, A. A. & Ong, C. S. (2020). Mathematics for Machine Learning. Cambridge University Press. Section and theorem numbers cited from the authors' free PDF (draft of 15 January 2024), whose pagination differs from the print edition.
- Druinsky, A. & Toledo, S. (2012). How Accurate is inv(A)*b? arXiv:1201.6035.
- Hamilton, E. (1992). JPEG File Interchange Format, Version 1.02. C-Cube Microsystems.
- NumPy documentation (version 2.3): numpy.linalg.matrix_rank.