waveform80 / picamera

A pure Python interface to the Raspberry Pi camera module
https://picamera.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
1.57k stars 357 forks source link

Is the `PiBayerArray.flush` method unpacking data correctly? #730

Open JesseBuesking opened 1 year ago

JesseBuesking commented 1 year ago

On line 477 the original data is unpacked and shifted to the left 2 bits

data = data.astype(np.uint16) << 2

That's fine, as you're shifting everything to facilitate easier unpacking in the following lines

for byte in range(4):
    data[:, byte::5] |= ((data[:, 4::5] >> (byte * 2)) & 3)

Which essentially says "every byte is made by the combination of the original byte shifted to the left 2 (from before), with the 5th byte's lowest 2 bits added on".

However, all the bytes were shifted by two, from above, so the 5th byte also contains shifted byte data. Taking for example byte = 0, so we're looking at the first byte, this essentially says "the 0th byte is the combination of itself shifted to the left, and the lowest two bits of the 4th (zero-indexed) byte, which is currently 0 because of the previous shift operation."

Should the code not instead be

for byte in range(4):
    data[:, byte::5] |= ((data[:, 4::5] >> ((byte + 1) * 2)) & 3)

To make it even clearer, if we had this data representing the first byte and the 5th byte of some set

0000 0001 <- 1st byte
0000 0011 <- 5th byte

The shift by two results in 16 bit data looking like

0000 0000 0000 0100 <- 1st byte
0000 0000 0000 1100 <- 5th byte

On the first iteration of the loop, byte is 0, so the line reads

data[:, 0::5] |= ((data[:, 4::5] >> (0 * 2)) & 3)

Or simplified

data[:, 0::5] |= ((data[:, 4::5) & 3) <- no shift of the 5th byte.

The bitwise AND with 3 on the 5th byte looks like

0000 0000 0000 1100 <- 5th byte
0000 0000 0000 0011 <- 3
======
0000 0000 0000 0000 <- result

When it should look lke

0000 0000 0000 0011 <- 5th byte
0000 0000 0000 0011 <- 3
======
0000 0000 0000 0011 <- result

Hopefully that makes sense. Let me know if I missed something.

Followup:

Tested against the output looking at the lowest bits to confirm:

for byte in range(4):
    print(byte, np.unique(data[byte::4, byte::4] & 3))

> 0 [0]
  1 [0 1 2 3]
  2 [0 1 2 3]
  3 [0 1 2 3]

Every fourth byte ends in 00.