BEVERLY QIN

Back to Projects

Simple Structure from Motion

my role

Developer

year

2026

contribution

Computer Vision, Geometry, 3D Reconstruction

tools

Python, NumPy, OpenCV, SciPy, Viser

team

Individual project

Overview

Built a sparse Structure from Motion pipeline that estimates image correspondences, camera motion, and 3D scene points from two staff-provided iPhone 15 Pro living-room images.

Settings: random seed 42; 8,000 maximum SIFT features; NNDR threshold 0.8; 2,000 RANSAC iterations; and a Sampson-distance inlier threshold of 0.001.

Camera Intrinsics & Features

Computed the intrinsic matrix from a 6.765 mm focal length, 9.757 mm sensor width, and 1280×960 image dimensions: f = 6.765 × (1280 / 9.757) ≈ 887.49 px, with principal point (640, 480). The height calculation produced 887.46 px, supporting the square-pixel assumption.

Used OpenCV SIFT because it is robust to the pair's rotation and translation. The detector produced 7,807 features in image one and 7,372 in image two, using default SIFT parameters except an 8,000 feature cap.

Input living-room image pair
Input living-room image pair
SIFT keypoints
SIFT keypoints

Matching & Robust Pose Recovery

OpenCV's BFMatcher compares SIFT descriptors with L2 distance. An NNDR threshold of 0.8 retained 617 candidate correspondence pairs; it is intentionally permissive because geometry-based RANSAC removes outliers downstream.

Each RANSAC iteration samples eight correspondences, estimates an Essential matrix with the 8-point algorithm, decomposes it into four candidate poses, and uses a cheirality check to select the pose with the most positive-depth triangulations. All correspondences are scored with Sampson distance, then the best model is refined using every inlier.

The final model retained 469/617 inliers (76.01%). Tightening epsilon from 0.01 to 0.001 corrected an initially incorrect pose by excluding geometrically inconsistent matches.

Correspondences before RANSAC
Correspondences before RANSAC
NNDR distribution
NNDR distribution
Top NNDR matches
Top NNDR matches
Feature matches
Feature matches
RANSAC inlier correspondences
RANSAC inlier correspondences
Epipolar lines
Epipolar lines
Matches after RANSAC
Matches after RANSAC
RANSAC convergence
RANSAC convergence

Triangulation & Point Cloud

Inlier pairs are triangulated with Direct Linear Transform, then filtered by positive depth and reprojection error. The final sparse reconstruction contains 469 points, with mean depth 1.6068 and depth range [0.5960, 20.5436].

The recovered rotation has Euler angles roll 21.36°, pitch 22.08°, yaw 12.40°; the normalized translation is [-0.73835, 0.55234, 0.38698]. The final point cloud can be explored through the generated Viser scene data.

Triangulated 3D point cloud
Triangulated 3D point cloud
Recovered camera poses
Recovered camera poses
Pipeline summary
Pipeline summary
Pose summary
Pose summary
Viser view 1
Viser view 1
Viser view 2
Viser view 2
Viser view 3
Viser view 3