The python code I was using was missing some logic required. This logic doesn't apply in the Python interface and there's no clue in Python how this works in the C library. Basically, IplImage (and I believe Mat, too; the C++ successor to the old IplImage struct) pads out rows of pixels in the imageData property to be divisible by 4 by adding that number of empty (0-value) bytes. So the code I had, which was this:
import cv2
img = cv2.cv.LoadImage('rgbw.png')
pixels = []
for ch in img.tostring():
pixels.append(ord(ch))
print pixels
[0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 255]
Was missing this logic. I solved this as the following:
import cv2
img = cv2.cv.LoadImage('rgbw.png')
height = img.height
width = img.width
raw_data = img.tostring()
# iplImage->imageData requires rows to be padded with zero bytes at the end
# so they be divisible by 4
pad_bytes_per_row = width % 4
# create the ctypes structure
ubyte_array_type = c_ubyte * (len(raw_data) + (height * pad_bytes_per_row))
ubyte_array = ubyte_array_type()
index = 0
for ch in raw_data:
ubyte_array[index] = ord(ch)
index += 1
if 0 == index % width: # end of row
pad_index = 0
while pad_index < pad_bytes_per_row:
ubyte_array[index] = 0
pad_index += 1
index += 1
Now ubyte_array is populated with the correct information from the python API of opencv. Note this would be the same if you were using a numpy_array.tostring() method for the data and wanted to use that to populate the Mat object. Hope this helps someone.