Bach Pham
Back

From 2D Images to 3D Worlds: The Linear Algebra of Structure from Motion

Bach Pham

Abstract

This paper explores the essence of Structure from Motion (SfM), a technique in computer vision that can reconstruct 3D scenes from some 2D images with good overlap. The paper aims to apply linear algebra, especially Singular Value Decomposition (SVD), to deeply explore the mathematics behind SfM. I focus mostly on the Fundamental Matrix and the Eight-Point Algorithm. Specifically, I demonstrate how geometric constraints are transformed into a homogeneous system of linear equations (Ax=0Ax = 0) and prove why the Singular Value Decomposition provides the optimal solution to this problem.

Introduction: The Inverse Problem

Sometimes we only have a handful of images of a scene, but we really want to know what the whole scene looks like. People have thought of combining images to create panoramas using feature matching, but this is still just a larger image. We want to move from a flat 2D image to a 3D structure.

In the 1970s, structure from motion was developed. It has been the ground to many modern techniques to reconstruct 3d scene, especially all kinds of Neural Radiance Field methods (NeRFs) and Gaussian Splatting.

In rendering (forward problem), we project 3D coordinates onto a 2D plane, which causes the loss of depth information. On the other hand, Structure from Motion (SfM) tries to reverse this by using the parallax effect which is the relative motion of points between different viewpoints.

A prominent implementation of this is Google Map. It has been using SfM all the time until 2023 when Google released the "immersive view" option on Google Maps which uses another technology built on top of SfM: NeRF. The NeRF paper was released in 2020 by Ben Mildenhall, Pratul P. Srinivasan, Matthew Tancik, Jonathan T. Barron, Ravi Ramamoorthi, and Ren Ng. Later on in 2024, Google released a research paper about a hybrid technology (combining the high quality of NeRF and the ultra fast rendering of Gaussian Splatting): RadSplat

The SfM pipeline generally consists of:

  1. Feature Matching (SIFT) and Outlier Rejection (RANSAC).
  2. Estimating the Fundamental Matrix (FF) (The focus of this paper).
  3. Estimating the Essential Matrix (EE).
  4. Triangulation and Bundle Adjustment.

Epipolar Geometry

Before we get to the Fundamental Matrix, we have to know what Epipolar geometry is.

The Coplanarity Constraint

Consider a point X\bX in 3D space viewed by two cameras centered at C\bC and C′\bC'.

  • Let x\bx be the projection of X\bX onto the first image.
  • Let x′\bx' be the projection of X\bX onto the second image.

As illustrated in Figure 1, the points C\bC, C′\bC', and X\bX form a triangle. Consequently, the vectors Cx→\overrightarrow{\bC\bx}, CC′→\overrightarrow{\bC\bC'}, and C′x′→\overrightarrow{\bC'\bx'} must lie on the same plane, called the epipolar plane. And since they are on the same plane, their scalar triple product is zero:

Cx→⋅(CC′→×C′x′→)=0\overrightarrow{\bC\bx} \cdot \left(\overrightarrow{\bC\bC'} \times \overrightarrow{\bC'\bx'}\right) = 0
Epipolar planeEpipolar line
Figure 1: Epipolar Geometry

Epipoles and Epipolar Lines

If we know a point x\bx in the above image, we cannot determine its exact depth. However, the ray from C\bC through x\bx looks like a line when viewed from the second camera. This is the Epipolar Line (l′\mathbf{l}'). The matching point x′\bx' must lie somewhere on this line. This constraint significantly reduces the search for matches because we just need to search on a line and do not need to search for the entire image anymore.

The Fundamental Matrix FF

The Fundamental Matrix FF is the algebraic representation of epipolar geometry. It is a 3×33 \times 3 matrix. The relationship between a point in image 1, xi=[xi,yi,1]T\mathbf{x}_i = [x_i, y_i, 1]^T, and a point in image 2, xi′=[xi′,yi′,1]T\mathbf{x}'_i = [x'_i, y'_i, 1]^T, is given by the Longuet-Higgins equation:

xi′TFxi=0\mathbf{x}'^T_i F \mathbf{x}_i = 0

Deriving the System of Equations (Ax=0Ax=0)

To find FF, we need to solve for its 9 entries. We can write the matrix product explicitly for a single pair of points:

[xi′yi′1][f11f12f13f21f22f23f31f32f33][xiyi1]=0\begin{bmatrix} x'_i & y'_i & 1 \end{bmatrix} \begin{bmatrix} f_{11} & f_{12} & f_{13} \\ f_{21} & f_{22} & f_{23} \\ f_{31} & f_{32} & f_{33} \end{bmatrix} \begin{bmatrix} x_i \\ y_i \\ 1 \end{bmatrix} = 0

Expanding this multiplication results in a scalar equation. Grouping the terms to isolate the coefficients of FF, we obtain:

xixi′f11+xiyi′f21+xif31+yixi′f12+yiyi′f22+yif32+xi′f13+yi′f23+f33=0x_i x'_i f_{11} + x_i y'_i f_{21} + x_i f_{31} + y_i x'_i f_{12} + y_i y'_i f_{22} + y_i f_{32} + x'_i f_{13} + y'_i f_{23} + f_{33} = 0

If we have mm correspondences (where m≥8m \geq 8), we can stack these linear equations into a large matrix system. By defining a vector f\mathbf{f} that contains the entries of the matrix FF, we can rewrite the system as:

[x1x1′x1y1′x1y1x1′y1y1′y1x1′y1′1⋮⋮⋮⋮⋮⋮⋮⋮⋮xmxm′xmym′xmymxm′ymym′ymxm′ym′1][f11f21f31f12f22f32f13f23f33]=0\begin{bmatrix} x_1 x'_1 & x_1 y'_1 & x_1 & y_1 x'_1 & y_1 y'_1 & y_1 & x'_1 & y'_1 & 1 \\ \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots \\ x_m x'_m & x_m y'_m & x_m & y_m x'_m & y_m y'_m & y_m & x'_m & y'_m & 1 \end{bmatrix} \begin{bmatrix} f_{11} \\ f_{21} \\ f_{31} \\ f_{12} \\ f_{22} \\ f_{32} \\ f_{13} \\ f_{23} \\ f_{33} \end{bmatrix} = \mathbf{0}

This can be written compactly as Af=0A\mathbf{f} = 0.

Since real-world data has noise, AfA\mathbf{f} will never be exactly zero. We instead try to find the f\mathbf{f} that makes the result as close to zero as possible. We also fix the length of f\mathbf{f} to 1 so the solution isn't just a vector of all zeros.

min⁡∥Af∥subject to∥f∥=1\min \| A\mathbf{f} \| \quad \text{subject to} \quad \| \mathbf{f} \| = 1

Mathematical Proof: Solving via SVD

Understand SVD geometrically

We have already known that A=UΣVTA = U \Sigma V^T. Geometrically, multiplying a vector x\mathbf{x} by AA happens in three steps:

  1. Rotate (VTV^T): The matrix VTV^T is orthogonal. It takes the input vector x\mathbf{x} and rotates it to a new coordinate system.
  2. Stretch (Σ\Sigma): The matrix Σ\Sigma is diagonal. It stretches the vector along the coordinate axes. The amount of stretching is determined by the singular values σ1≥σ2≥⋯≥σn≥0\sigma_1 \geq \sigma_2 \geq \dots \geq \sigma_n \geq 0.
  3. Rotate (UU): The matrix UU is orthogonal. It rotates the stretched vector one final time.

We want the final result AxA\mathbf{x} to be as small as possible.

Proof

Step 1: Ignore the Final Rotation (UU) We have:

∥Ax∥=∥UΣVTx∥\| A\mathbf{x} \| = \| U \Sigma V^T \mathbf{x} \|

The matrix UU is orthogonal. A property of orthogonal matrices is that they act like rotation or reflection matrix. That means they change the direction of a vector but not the length. Therefore, for any vector z\mathbf{z}, ∥Uz∥=∥z∥\| U\mathbf{z} \| = \| \mathbf{z} \|.

∥U(ΣVTx)∥=∥ΣVTx∥\| U (\Sigma V^T \mathbf{x}) \| = \| \Sigma V^T \mathbf{x} \|

Step 2: Change Variables. Let's define a temporary vector y=VTx\mathbf{y} = V^T \mathbf{x}. Since VV is orthogonal, VTV^T is also orthogonal. This means VTV^T just rotates x\mathbf{x}. Since we are constrained to unit vectors (∥x∥=1\| \mathbf{x} \| = 1), and rotation preserves length, our new vector y\mathbf{y} is also a unit vector (∥y∥=1\| \mathbf{y} \| = 1).

Our problem is now much simpler: Minimize ∥Σy∥\| \Sigma \mathbf{y} \|.

Step 3: Analyze the Stretching (Σ\Sigma)

Σy=[σ10…00σ2…0⋮⋮⋱⋮00…σn][y1y2⋮yn]=[σ1y1σ2y2⋮σnyn]\Sigma \mathbf{y} = \begin{bmatrix} \sigma_1 & 0 & \dots & 0 \\ 0 & \sigma_2 & \dots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \dots & \sigma_n \end{bmatrix} \begin{bmatrix} y_1 \\ y_2 \\ \vdots \\ y_n \end{bmatrix} = \begin{bmatrix} \sigma_1 y_1 \\ \sigma_2 y_2 \\ \vdots \\ \sigma_n y_n \end{bmatrix}

The squared length of this resulting vector is the sum of its squared components:

∥Σy∥2=(σ1y1)2+(σ2y2)2+⋯+(σnyn)2\| \Sigma \mathbf{y} \|^2 = (\sigma_1 y_1)^2 + (\sigma_2 y_2)^2 + \dots + (\sigma_n y_n)^2

We want to make this sum as small as possible. We know the singular values are sorted in descending order: σ1\sigma_1 is the largest (maximum stretch) and σn\sigma_n is the smallest (minimum stretch). We also know that y12+y22+⋯+yn2=1y_1^2 + y_2^2 + \dots + y_n^2 = 1 (because y\mathbf{y} is a unit vector).

To get the smallest total sum, we should put all of our weight into the term multiplied by the smallest number.

  • If we choose y1=1y_1=1, the result is σ12\sigma_1^2 (The Maximum).
  • If we choose yn=1y_n=1, the result is σn2\sigma_n^2 (The Minimum).

Therefore, the optimal vector y\mathbf{y} is all zeros, except for a 1 in the last position:

yoptimal=[00…1]T\mathbf{y}_{optimal} = \begin{bmatrix} 0 & 0 & \dots & 1 \end{bmatrix}^T

Step 4: Convert back to x\mathbf{x}. We found the best y\mathbf{y}, but we need to find the original x\mathbf{x}. We defined y=VTx\mathbf{y} = V^T \mathbf{x}. Therefore, x is

x=Vy\mathbf{x} = V \mathbf{y}
x=[∣∣∣v1v2…vn∣∣∣][00⋮1]\mathbf{x} = \begin{bmatrix} | & | & & | \\ \mathbf{v}_1 & \mathbf{v}_2 & \dots & \mathbf{v}_n \\ | & | & & | \end{bmatrix} \begin{bmatrix} 0 \\ 0 \\ \vdots \\ 1 \end{bmatrix}

By the rules of matrix-vector multiplication, this operation selects the last column of the matrix VV.

Conclusion: The vector x\mathbf{x} that minimizes the error is the last column of VV.

Rank Enforcement

After using the theorem above, we have a solution for f\mathbf{f} and reshape it into a 3×33 \times 3 matrix FF. However, we have one problem.

The Rank 2 Constraint

In our geometric setup, the Fundamental Matrix maps a point to a line. For this to work mathematically, the determinant of FF must be zero. In Linear Algebra terms, FF must be Rank 2.

However, because our input images have noise (pixels aren't perfect), the FF we calculated in Section 4 will likely be Rank 3 (Full Rank). It will satisfy the algebraic least squares, but it won't satisfy the strict geometry (the epipolar lines won't intersect perfectly at one point).

The Eckart-Young-Mirsky Theorem

We need to find a new matrix F′F' that is Rank 2 but is as close as possible to our calculated FF. The Eckart-Young-Mirsky theorem tells us exactly how to do this:

  1. Decompose: Take the SVD of our noisy matrix FF: F=UΣVTF = U \Sigma V^T.
  2. Inspect Σ\Sigma: The diagonal matrix will look like this:
    Σ=diag⁡(σ1,σ2,σ3)\Sigma = \operatorname{diag}(\sigma_1, \sigma_2, \sigma_3)
    Because of noise, σ3\sigma_3 will be a small non-zero number (e.g., 0.005). This tiny number is what makes the matrix Rank 3.
  3. Truncate: We manually set the smallest singular value to zero.
    Σ′=diag⁡(σ1,σ2,0)\Sigma' = \operatorname{diag}(\sigma_1, \sigma_2, 0)
  4. Reconstruct: We multiply the matrices back together: F′=UΣ′VTF' = U \Sigma' V^T.

This result, F′F', is the Fundamental Matrix used in Structure from Motion. Of course this is just the very basic idea. We then need to use RANSAC again for outlier rejection to find best F, then estimate the essential matrix, then the triangulation, then PnP, and a lot of optimizations. However, I will not include those in this paper because we only focus on the core math, not the programming aspect.

Conclusion

SfM is a very interesting application of Linear Algebra. The math behind is so beautiful, especially for the minimization problem. It is also very convenient when we can just decompose, force an entry in the diagonal matrix to be zero, and multiply everything back. After taking linear algebra course, I finally understand deeply what is happening behind the libraries I imported in my code.

References

[1] University of Maryland. (2022). CMSC733 Project 3: Build your own SfM! Retrieved from https://cmsc733.github.io/2022/proj/p3/