I built a 3D scanner using shadow planes: sweep a stick’s shadow across an object on two calibrated planes, film it, and recover the object’s shape. I re-implemented the project in C++, with openFrameworks and OpenCV, and OpenGL for the 3D view.

Calibration. The camera’s intrinsics come from automatic checkerboard detection in OpenCV, with sub-corner refinement, and its distortion is corrected with remap images. From the intrinsics and extrinsics, every pixel becomes a ray in world coordinates.

Checkerboard calibration, detected automatically

Shadow edges. A first pass over the video builds minimum, maximum and threshold shadow images, plus a mask of pixels in shadow the whole time. Each frame’s shadow crossings are fit with least-squares lines on the vertical and horizontal planes; projected into 3D, the two lines define that frame’s shadow plane. A temporal image records, per pixel, the frame the shadow crossed it, and planes can be interpolated between frames.

The temporal image: each pixel colored by when the shadow crossed it

Reconstruction. Each pixel’s ray is intersected with the shadow plane of its crossing time, skipping rays nearly parallel to their plane, where the tiniest error throws the point wildly off. The result is a colored point cloud, viewable in the app and exported as .ply.

A scanned ship in the point-cloud viewer

View project ↗