Skip to content

▣ shipped · 603 GB · 1,620 clips

A wedding, 603 GB, and one laptop

1,620 clips multicam-synced by cross-correlation, 5,000+ photos graded to a learned house style, 14,077 duplicates proven — on a laptop and a subscription.

Period
Dec 2023 – July 2026
Role
Independent · with Claude
Status
case study
  • 603 GB inventoried
  • 1,620 clips synced
  • 14,077 duplicates proven
  • 5,000+ photos graded
  • #ffmpeg
  • #opencv
  • #signal-processing
  • #agent-orchestration
  • #python
  • #video

Vaidehi and I got married on 20–21 December 2023, over two days at Rockford Resort in Mahabaleshwar. Two photographers, a drone, five video cameras, and a room full of phones. By the time the celebration ended, our wedding existed as 603 GB of files: a Sony shooter's RAW frames, a Nikon shooter's JPGs, drone footage, and hours of video, copied and re-copied across six folders on different drives. Nobody had the full picture of what we actually had.

It sat like that for two and a half years. The reason was not technical. Shortlisting the photos, watching every video to find the moments worth keeping, then editing and enhancing all of it — that was simply too many man-hours for one couple. So it kept getting pushed. The honest counterfactual for this project is not "a studio would have charged us X." It is that this would never have got done at all.

What changed was specific and recent: Claude Code gained native image reading, and I finished setting up Immich with its full ML stack running locally. Those two together meant I could point a coding agent at a self-hosted photo library and have it drive the ML — find the bride and groom across tens of thousands of frames, build their timeline, and rebuild the chronology of the whole archive, which mattered because the timestamps were wrong. Adobe, CapCut and the DJI app all exist and all work, but they bring their own templates and the output looks like their output; more to the point, none of them are built to sit in front of half a terabyte of unsorted, mis-timestamped footage. The bottleneck was never "apply an effect." It was inventory, dedup, chronology, and finding the good moments.

A photograph from the sangeet: the bride and groom in evening formal wear, caught mid-frame under a hanging white floral canopy on the night's chevron walkway, one frame out of the terabyte-plus of raw footage the rest of this piece is about wrangling.

01 — one frame of 33,302

A photograph from the sangeet: the bride and groom in evening formal wear, caught mid-frame under a hanging white floral canopy on the night's chevron walkway, one frame out of the terabyte-plus of raw footage the rest of this piece is about wrangling.

sangeet · rockford resort, mahabaleshwar · 20–21 december 2023

The pile. The first job was knowing what existed: 33,302 files, ~603 GB, six source folders. Then dedup, with one rule — nothing gets deleted on a guess. A two-stage hashing pipeline grouped files by exact byte size, took a cheap head+tail partial hash to drop coincidental matches, then ran full SHA-256 on the ~26,760 survivors (~410 GB) to prove byte-identity. Verdict: 14,077 exact duplicates, ~219 GB of pure redundancy, one working folder that was 99.8% copies. Every claim was written to a CSV pointing at its canonical keeper before a byte was touched. We reclaimed ~217 GB. Zero exact-duplicate videos — the apparent video "duplicates" were multi-camera coverage of the same moment, which is a gift once you can line the angles up.

The photos. Rather than impose a generic look, we learned ours. Claude measured 41 original→edited pairs from our own photographers' finals — brightness, contrast curve, vibrance, a mild warm balance — and that became the starting grade for Sony RAW developed through rawpy. The rich version backfired: it pushed pink, then over-corrected to yellow. The fix was humility — drop the fixed channel pushes, use per-image auto white balance that neutralises casts off low-saturation surfaces while leaving haldi-yellow and mandap-pink alone. Plus an adaptive-gamma night profile for the sangeet, a brightness-robust blur filter so dark-but-sharp frames weren't punished, EXIF orientation fixes for the fifth of the archive shot vertical, and per-day clock corrections because the Nikon's clock read 2014 and had been reset between the two days. About 5,000 photos came out graded, oriented, and correctly timestamped, into 27 Immich albums following the real Marathi ceremony taxonomy rather than an algorithm's clusters.

The sync. 1,620 video clips, ~174 GB, several cameras, no shared clock — one camera ran about 9.5 hours fast, and video creation times are stored in UTC. Timecode was useless. Plain envelope cross-correlation scored around 0.2, which is noise. GCC-PHAT — generalised cross-correlation with phase transform — against a clean reference song track scored 25.8–352.6, a median around 105 against a lock threshold of 25, and locked frame-accurately. The insight was to sync each clip to the song rather than to the other cameras, so every angle gets a precise song-position and an assembly script places it on a shared timeline.

The flicker war. Two LED video walls flanked the stage and beat against the shutter. Three attempts failed: ffmpeg's built-in deflicker modes, a masked brightness detrend, and a whole-frame correction that left a yellow circle sitting on the dance floor. The turn came from treating it as a frequency problem rather than a levels problem — hand-tagging a hundred consecutive frames found a clean ~40 Hz beat, one bad frame every 2.5. The fix borrowed from astrophotography: stack each frame with its neighbours using gradient weights, heaviest on the centre, so the wall averages into itself while the anchor stays sharp. Flicker down ~82%, and it ran as a fast ffmpeg C-filter. Masked to the screens only, so the dancers kept every frame they were shot with.

The fleet. With four full multicam performances plus ceremony footage to finish, I stopped editing one video at a time and ran a fleet of Claude subagents, roughly one per performance, each handed the same playbook. The one thing that could not parallelize was the render: a 16 GB laptop that also hosts a Docker homelab OOM-crashes if two heavy ffmpeg passes run at once, and takes the house DNS with it. Every encode went through a machine-wide file lock. Editing parallel, rendering serial.

What it cost. One 16 GB Windows laptop with a GTX 1660 Ti, one Claude subscription, and the open-source stack: Python, FFmpeg, OpenCV, NumPy, SciPy, rawpy, YOLO11, Immich. The GPU turned out never to have been the bottleneck — NVENC was blocked by an old driver, and benchmarking showed these renders were filter-bound anyway. The real 4–6× came from rewriting the effects as ffmpeg C-filters instead of per-frame Python. And the best sync result in the project came after a confident, pose-verified answer turned out to be 141 seconds wrong, because the song loops and a single matching frame proves nothing. Two and a half years after the wedding, in July 2026, the whole celebration finally lives on our own server, organised the way the wedding actually unfolded.