CS180 · Project 1

Colorizing the Prokudin‑Gorskii Photo Collection

Aligning three monochrome glass‑plate channels (B, G, R) into vivid color images using single‑scale search and coarse‑to‑fine image pyramids. Used NCC in alignment algorithm.

Course: CS180 — Intro to Computer Vision & Computational Photography Project #1 Author: Kourosh Salahi Due: Sep 12, 2025 • 11:59 PM

Overview & Key Ideas

Given three images taken in black and white which have R, G, and B filters put on the lenses of the camera, we can use the intensities of the pixel brightness and these three images to reconstruct a color image. Utilizing an alignment algorithm, this was accomplished.

Scoring metrics. I implemented NCC as the scoring metric. This worked well throughout.

Method

Below is the end‑to‑end pipeline I used.

1) Preprocessing

  • Split B/G/R: Each input plate is divided into three equal vertical slices in B→G→R order.
  • Type/scale: Convert to float to make metrics comparable across images.
  • Interior crop for scoring: Ignore ~10% borders on each side when computing alignment scores to avoid frame artifacts and scan edges.

2) Single‑Scale Exhaustive Search

  • For each channel C ∈ {G,R}, search integer displacements with np.roll (dx,dy) ∈ [−w,w], where w is the displacement window that is being searched.
  • NCC: ⟨B̂, Ĉ⟩ = (B)·(C) / (‖B‖‖C‖). Calculated by flattening channels into vectors. Higher is better.
  • Pick the displacement with the best score and record (dx,dy) for R→B and G→B.

3) Coarse‑to‑Fine Image Pyramid

  • Build pyramid: Repeatedly downsample by 2 (with anti‑aliasing) until 1/16.
  • Coarsest match: Run the single‑scale matcher to get an initial (dx,dy).
  • Upscale & refine: Multiply displacements by 2 when moving up a level and refine in a small window (e.g., ±4) around the upscaled estimate.
  • Rationale: The pyramid reduces the number of candidate shifts dramatically while avoiding local minima from large motions.

4) Composition

  • Shift G and R by the chosen displacements using np.roll (or equivalent) and stack channels into [R,G,B].
  • For visualization, optionally crop outer margins to remove wraparound introduced by shifting.

Scoring Notes & Design Choices

  • Metric choice: NCC is generally more robust when channels have different brightness/contrast; L2 serves as a simple baseline.
  • Interior crop: Ignoring borders prevents frame edges from dominating the score and helps with plates that include thick margins.
  • Vectorization: All per‑candidate scores are computed with NumPy operations; loops only iterate over candidate shifts.

Results — Single‑Scale (Low‑Res JPEGs)

These are the single scale images, which were constructed very quickly.

Results — Multi‑Scale Pyramid (Mandatory Set)

Some very nice recolored images!

Image Metric R→B (dx, dy) G→B (dx, dy) Status
church.tifNCC(-4, 58)(4, 25)OK
emir.tifNCC(55, 103)(24, 49)Off
harvesters.tifNCC(13, 123)(16, 59)OK
icon.tifNCC(23, 89)(17, 41)OK
italil.tifNCC(35, 76)(21, 38)OK
lastochikino.tifNCC(-8, 75)(-2, -2)OK
lugano.tifNCC(-29, 92)(-16, 41)OK
melons.tifNCC(13, 178)(10, 81)OK
self_portrait.tifNCC(37, 176)(29, 78)OK
siren.tifNCC(-25, 95)(-6, 49)OK
three_generations.tifNCC(11, 112)(14, 53)OK

Additional Examples — My Own Selections

These four were chosen from the online collection.

Failure Cases & Analysis

Some images had slight alignment effects that can be attributed to people, animals, or scenes such as water moving inbetween shots. The only image that seemed to fail completely in aligning was the image of the Emir. I believe this to be due to the following:

  • Image artifacts: Some channels had scratches while others didn't potentially causing the optimization function to try to match patterns that didnt actually correspond to real things in the image
  • Cross channel brightness: The Emir, in his eternal swagger, has a very bright garment, which appears very differently across the different color channels (very bright in Blue and very dark in Red).

Reproducibility & Notes

  • While the code for this is not publicly available, I encourage those who stumble upon this and are not part of CS180 to attempt reimplementing this. It is a very fun exercise!

Thanks for reading! 📷🧮

I hope you enjoyed reading about my project and viewing these beautiful images :)