Optimizing Bayer16 -> RGB in Python
<p>I am reading a camera that gives me Bayer16 format (GRGB) and I wrote the following code in python to modify it from bayer16 to bayer8, and then use OpenCV to convert it to RGB:</p>

<pre><code>def _convert_GRGB_to_RGB(self, bayer16_image):
  bayer8_image = bytearray()
  # Convert bayer16 to bayer 8
  for i in range(0, len(bayer16_image), 2):
    data_byte = (bayer16_image[i] &amp; 0xF0) &gt;&gt; 4
    data_byte |= (bayer16_image[i+1] &amp; 0x0F) &lt;&lt; 4
    bayer8_image.append(data_byte)
  bayer8_image = numpy.frombuffer(bayer8_image, dtype=numpy.uint8).reshape((720, 1280))
  # Use OpenCV to convert Bayer GRGB to RGB
  return cv2.cvtColor(bayer8_image, cv2.COLOR_BayerGR2RGB)
</code></pre>

<p>After doing some timing, the for loop takes most of the running time and is extremely inefficient (although I think it does not allocate any space, unless numpy makes a copy for very edit). I am wondering how to improve this function as a whole, or the for loop in particular (as it is the slowest part of this function, by an order of magnitude).</p>

<p>Does anyone have tips and advice about how to improve this Bayer16 -> RGB conversion if I am to use Python please?</p>

<p><strong>EDIT:</strong></p>

<p>I found a solution using numpy array that makes my code pretty fast:</p>

<pre><code>def _convert_GRGB_to_RGB(self, data_bytes):
  even = numpy.frombuffer(data_bytes[0::2], dtype=numpy.uint8)
  odd = numpy.frombuffer(data_bytes[1::2], dtype=numpy.uint8)
  # Convert bayer16 to bayer8
  even = numpy.right_shift(even, 4)
  odd = numpy.left_shift(odd, 4)
  bayer8_image = numpy.bitwise_or(even, odd).reshape((720, 1280))
  # Use OpenCV to convert Bayer GRGB to RGB
  return cv2.cvtColor(bayer8_image, cv2.COLOR_BayerGR2RGB)
</code></pre>

<p>This solution satisfies my need but if anyone has any suggestion, I'm curious to hear them!</p>

<p>As a guess, your color problem is as follows - your <code>GRBG</code> data comes in like this:</p>

<pre><code>G0 B1 G2 ...
R0 G1 R2
</code></pre>

<p>Where the numbers represent the uint16 index. OpenCV needs them to be numbered</p>

<pre><code>G0 B0 G1 R1 ...
R6 G6 R7 G7
</code></pre>

<p>You can fix this with some careful reshape and transposing:</p>

<pre><code>data_bytes = np.frombuffer(data_bytes, dtype=np.uint8)
data = data.reshape(height / 2, width, 2) # a pair for each uint16
data = data.transpose((0, 2, 1))  #move the G/RB axis to be adjacent to the height axis
data = data.reshape(height, width)  # collapse it
</code></pre>

<p>Example</p>

<pre><code># manually constructed by hand
sample = ''.join([
    'grbGgRbGgRbg'
    'grBGGRBGGRbg'
    'grBgGrBgGrbg'
])
width = height = 6
data = np.array(list(sample))

data = (data
    .reshape(height / 2, width, 2)
    .transpose((0, 2, 1))
    .reshape(height, width)
)

# easy way to view the output
&gt;&gt;&gt; data.view((np.str_,6))
array([['gbgbgb'],
       ['rGRGRg'],
       ['gBGBGb'],
       ['rGRGRg'],
       ['gBGBGb'],
       ['rgrgrg']], 
      dtype='&lt;U6')
</code></pre>

python image numpy