Project 3: (Auto)stitching and Photo Mosaics

Course: CS180 • Samhita Ghosh • Fall 2025

Part A: IMAGE WARPING and MOSAICING

Part A.1: Shoot the Pictures

Image set 1: bookcase in Cory Hall

I picked this set of images since there were a lot of clear corners (from the bookcase) to work with, making it easy to identify the overlap between different images. I tried to keep the center of projection fixed and rotated the camera, shooting with a good amount of overlap so that features repeat across views.

Cory bookcase, left angle
Cory bookcase, left angle
Cory bookcase, center angle
Cory bookcase, center angle
Cory bookcase, right angle
Cory bookcase, right angle

Image set 2: lab in Cory Hall

The ping pong table and the shelves in this lab provided a lot of corners for me to work with.

Cory lab, left angle
Cory lab, left angle
Cory lab, center angle
Cory lab, center angle
Cory lab, right angle
Cory lab, right angle

Image set 3: Jacobs entrance

The windows and the lettering at the entrance gave me easy corner points to work with.

Jacobs, left angle
Jacobs, left angle
Jacobs, center angle
Jacobs, center angle
Jacobs, right angle
Jacobs, right angle

Part A.2: Recover Homographies

My computeH routine implements the Direct Linear Transform with least squares. Given point correspondences \(p_i=(x_i,y_i,1)^\top\) in the source image and \(p'_i=(x'_i,y'_i,1)^\top\) in the destination image, I solved for a homography \(H\) (with 8 variables by fixing \(h_{33}=1\)) that satisfies \(p'_i\sim H p_i\).

For each correspondence, I add two rows to \(A\) and solve the overdetermined system \(A\,h=b\) (or equivalently the homogeneous system \(A\,h=0\) with \(\|h\|=1\)): \[ \begin{bmatrix} x_i & y_i & 1 & 0 & 0 & 0 & -x'_i x_i & -x'_i y_i \\ 0 & 0 & 0 & x_i & y_i & 1 & -y'_i x_i & -y'_i y_i \end{bmatrix} \begin{bmatrix} h_{11} \\ h_{12} \\ h_{13} \\ h_{21} \\ h_{22} \\ h_{23} \\ h_{31} \\ h_{32} \end{bmatrix} = \begin{bmatrix} x'_i \\ y'_i \end{bmatrix} \] I use more than four correspondences and solve via least squares (numerically via SVD / np.linalg.lstsq). Before solving, I normalize points (translate to zero mean and scale so average distance is \(\sqrt{2}\)) to improve accuracy; after solving, I denormalize and enforce \(h_{33}=1\).

To verify the estimate, I project the annotated points from one view onto the other and overlay them. The recovered-*.JPG overlays below show that the cyan crosses (warped points) align with the red dots (manual picks), indicating a good fit.

Image set 1: bookcase in Cory Hall

For the bookcase, I had the following homography: \[ \begin{bmatrix} 0.724138 & 0.007652 & 1456.190610 \\ -0.174361 & 0.959848 & 46.128964 \\ -0.000072 & 0.000011 & 1.000000 \end{bmatrix} \]

Cory bookcase, right angle
Cory bookcase, right angle
Cory bookcase with applied homography
Cory bookcase with applied homography
Cory bookcase, center angle
Cory bookcase, center angle

Image set 2: lab in Cory Hall

For the lab, I had the following homography: \[ \begin{bmatrix} 0.324593 & -0.063095 & 1133.726300 \\ -0.409549 & 0.706606 & 542.347616 \\ -0.000218 & -0.000014 & 1.000000 \end{bmatrix} \]

Cory lab, right angle
Cory lab, right angle
Cory lab with applied homography
Cory lab with applied homography
Cory lab, center angle
Cory lab, center angle

Image set 3: Jacobs entrance

For the third homography, I had the following homography: \[ \begin{bmatrix} 1.758435 & 0.039888 & -1284.801950 \\ 0.473575 & 1.442918 & -806.244441 \\ 0.000253 & 0.000002 & 1.000000 \end{bmatrix} \]

Jacobs, left angle
Jacobs, left angle
Jacobs with applied homography
Jacobs with applied homography
Jacobs, center angle
Jacobs, center angle

Part A.3: Warp the Images

Using \(H\), I inverse-warped each output pixel back into the source image. I first predicted the destination canvas bounds by projecting the four source corners with \(H\) and taking integer min/max extents. Then I created a regular grid over that canvas, mapped each grid location through \(H^{-1}\), and sampled the source either with nearest neighbor or bilinear interpolation:

I also used an alpha mask that marks valid samples (inside the source bounds) so later stages could overlap cleanly.

image 1 for rectification, original
image 1 for rectification, original
image 1 rectified using nearest neighbor interpolation
image 1 rectified using nearest neighbor interpolation
image 1 rectified using bilinear interpolation
image 1 rectified using bilinear interpolation
image 2 for rectification, original
image 2 for rectification, original
image 2 rectified using nearest neighbor interpolation
image 2 rectified using nearest neighbor interpolation
image 2 rectified using bilinear interpolation
image 2 rectified using bilinear interpolation

Trade-off: bilinear is slightly slower but reduces aliasing and “stair-step” artifacts compared to nearest neighbor, matching the expected quality/speed trade-off from the spec.

Part A.4: Blend the Images into a Mosaic

To build mosaics, I chose the middle view as reference, estimated \(H\) for the side views into that reference, warped them, and assembled all images on a canvas large enough to contain all warped corners. For blending, I started with feathering and where seams are noticeable, I use a small Laplacian pyramid to blend the overlap while keeping edges crisp.

Image set 1: bookcase in Cory Hall

Cory bookcase, left angle
Cory bookcase, left angle
Cory bookcase, center angle
Cory bookcase, center angle
Cory bookcase, right angle
Cory bookcase, right angle
Cory bookcase mosaic
Cory bookcase mosaic

Image set 2: lab in Cory Hall

Cory lab, left angle
Cory lab, left angle
Cory lab, center angle
Cory lab, center angle
Cory lab, right angle
Cory lab, right angle
Cory lab mosaic
Cory lab mosaic

Image set 3: Jacobs entrance

Jacobs, left angle
Jacobs, left angle
Jacobs, center angle
Jacobs, center angle
Jacobs, right angle
Jacobs, right angle
Jacobs mosaic
Jacobs mosaic

Part B: FEATURE MATCHING for AUTOSTITCHING

Part B.1: Harris Corner Detection

Following the spec, I detect corners using the Harris response (second-moment matrix and its eigenvalues).

Harris Corners for Cory bookcase, center angle
Harris Corners for Cory bookcase, center angle
Harris Corners for Cory lab, center angle
Harris Corners for Cory lab, center angle
Harris Corners for Jacobs, center angle
Harris Corners for Jacobs, center angle

Part B.2: Feature Descriptor Extraction

I extract square patches centered at each selected corner, normalize them (subtract mean, divide by std), and downsample to a compact feature vector. These descriptors are grayscale and contrast-normalized so that lighting changes don’t dominate matching.

Top 12 features in Jacobs photo (center angle)

feature 1 for Jacobs
feature 1 for Jacobs
feature 2 for Jacobs
feature 2 for Jacobs
feature 3 for Jacobs
feature 3 for Jacobs
feature 4 for Jacobs
feature 4 for Jacobs
feature 5 for Jacobs
feature 5 for Jacobs
feature 6 for Jacobs
feature 6 for Jacobs
feature 7 for Jacobs
feature 7 for Jacobs
feature 8 for Jacobs
feature 8 for Jacobs
feature 9 for Jacobs
feature 9 for Jacobs
feature 10 for Jacobs
feature 10 for Jacobs
feature 11 for Jacobs
feature 11 for Jacobs
feature 12 for Jacobs
feature 12 for Jacobs

Part B.3: Feature Matching

I match descriptors using SSD (sum of squared differences) and apply Lowe’s ratio test to prune ambiguous matches. The ratio between the best and second-best SSD must fall below a threshold; surviving matches are much more reliable and reduce false correspondences in repetitive textures.

Top 12 features in Jacobs photos (center angle vs. right angle)

feature 1 for Jacobs
feature 1 for Jacobs
feature 2 for Jacobs
feature 2 for Jacobs
feature 3 for Jacobs
feature 3 for Jacobs
feature 4 for Jacobs
feature 4 for Jacobs
feature 5 for Jacobs
feature 5 for Jacobs
feature 6 for Jacobs
feature 6 for Jacobs
feature 7 for Jacobs
feature 7 for Jacobs
feature 8 for Jacobs
feature 8 for Jacobs
feature 9 for Jacobs
feature 9 for Jacobs
feature 10 for Jacobs
feature 10 for Jacobs
feature 11 for Jacobs
feature 11 for Jacobs
feature 12 for Jacobs
feature 12 for Jacobs

Part B.4: RANSAC for Robust Homography

Given tentative matches, I run RANSAC: repeatedly sample 4 correspondences, compute a candidate \(H\), and count inliers whose transfer error is below a pixel threshold. After a fixed number of iterations (or early stopping when inliers stabilize), I refit \(H\) to all inliers and use it for warping and mosaicing. The side-by-side results below compare the manual mosaics from Part A against the automatic pipeline.

Cory bookcase mosaic (manual)
Cory bookcase mosaic (manual)
Cory bookcase mosaic (auto-stitched)
Cory bookcase mosaic (auto-stitched)
Cory lab mosaic (manual)
Cory lab mosaic (manual)
Cory lab mosaic (auto-stitched)
Cory lab mosaic (auto-stitched)
Jacobs mosaic (manual)
Jacobs mosaic (manual)
Jacobs mosaic (auto-stitched)
Jacobs mosaic (auto-stitched)