Binary & Gray code patterns

A single light plane needs one frame per profile. Coded patterns do better: each of nn stripe patterns halves the regions of the one before, and the sequence of bright and dark a pixel sees over the nn frames is the number of the projector stripe that lit it. Step through the patterns, watch the regions split, and compare how the binary and the Gray code cope with pixels on a stripe border.

Camera image, pattern click to select a pixel
Bits read from this pattern white = 1, black = 0 no projector light (shadow)
Decoded with the binary code gray level = stripe number off by more than one stripe
Decoded with the Gray code gray level = stripe number off by more than one stripe
The patterns, one row per frame and one column per projector stripe outlined: true stripe of the selected pixel ▲ decoded stripe
Decoded minus true stripe along row binary Gray

From stripes to codes

With one light plane per frame, there is never a doubt which plane lit a pixel, but NN planes need NN frames. Projecting all of them at once is fast, but then the stripes can no longer be told apart. Coded patterns sit in between: number the projector stripes 0…2n−10 \dots 2^n - 1 and project one bit of that number per frame. Pattern ii is bright on all stripes whose code word has bit ii set. A pixel that sees bright, dark, bright, … over the nn frames has read the code word of its stripe:

n patterns  ⇒  2n stripes, 2n−1 stripe bordersn \text{ patterns} \;\Rightarrow\; 2^n \text{ stripes},\ 2^n - 1 \text{ stripe borders}

Eight patterns tell 256 stripes apart. Once the stripe is known, its light plane is known, and the depth follows by intersecting the camera ray with it, as in the light-plane demo.

Reading a bit

Whether a pixel is "bright" depends on the surface: a dark object under the projector can be darker than a white one without it, and surfaces at a grazing angle get little light. A fixed threshold fails there. Two extra frames fix it: one with the projector fully on, IwhiteI_{\text{white}}, and one with it off, IblackI_{\text{black}}. A pixel reads 1 if

Ii>12(Iwhite+Iblack)I_i > \tfrac{1}{2} \left(I_{\text{white}} + I_{\text{black}}\right)

and pixels where Iwhite−IblackI_{\text{white}} - I_{\text{black}} is tiny are in a shadow of the projector and get no stripe at all. Another common choice is to project every pattern together with its inverse and compare the two.

Errors at stripe borders

A pixel that covers a stripe border sees a mix of both stripes, and with blur and noise each bit can come out either way. In the binary code several bits change at some borders: between stripes 31 and 32 of a 6-bit code, 011111 turns into 100000, and all six bits flip. A pixel there can read any mix, such as 111111 = 63 or 000000 = 0, far from both true stripes.

The Gray code g=s⊕(s≫1)g = s \oplus (s \gg 1) orders the code words so that neighboring stripes differ in exactly one bit. A pixel on a border can then only be uncertain about that one bit, and reads either of the two neighboring stripes: the error is at most one stripe. Decoding converts the Gray code back with b0=g0b_0 = g_0 and bi=bi−1⊕gib_i = b_{i-1} \oplus g_i. The finest pattern has the most borders; when its stripes become narrower than a camera pixel, it can no longer be read, which limits the useful nn.

Try this