简体   繁体   中英

How to convert a uint32 Numpy array into 4 uint8 Numpy arrays taking care of endianness

For context, I'm writing a Python program that writes out images. But these images are a bit special in the sense that they are used as intermediate data containers that are further digested by other programs written in C and use the libgd library. I have no clue about C.

My precise problem is that I have a Numpy array of dtype='uint32' . I want to decode this array to get 4 arrays of dtype='uint8' , and then use them to write out an image. This can be done with numpy.view :

img_decoded = img_coded[:, :, np.newaxis].view('uint8')

Now, img_decoded is of shape (dimY, dimX, 4) . My doubt is what index of the third dimension should I make correspond to what channel . The C programs I'm interacting with expect that the most significative byte is written to the Alpha channel, then Red, then Green and finally Blue. How can I make sure that this correspondence is fulfilled? I'm aware this has something to do with endianness, but this concept is still fuzzy to me.


Related to all this, I have been playing with this to try to gain insight in these concepts, but yet commands like this blow my mind:

In []: np.array([256 * 4 + 1], dtype='uint16').view(dtype='uint8')
Out[]: array([1, 4], dtype=uint8)

What does this tell me about the order of the most significant bit? Why is the output [1,4] and not the other way around? What has this to do with endianness?

The C programs I'm interacting with expect that the most significative byte is written to the Alpha channel, then Red, then Green and finally Blue. How can I make sure that this correspondence is fulfilled?

This is highly dependent of both the pixel encoding method and the target platform .

Regarding the encoding, some libraries use the BGRA format while some use the RGBA format for example. Many support multiple format but one need to be selected at a time.

On conventional/mainstream platforms, an uint32 type is composed of 4 x 8 bits and is stored in 4 consecutive 8-bit bytes of memory. The 8 most significant bits can be stored in the byte with the lowest memory address or the highest memory address regarding the platform. This is indeed what is called endianness . Some platform can have weird endianness (like middle endian) or can support multiple endianness resulting in some case to runtime-defined endianness (AFAIK, ARM and POWER for example support that although the "default" endianness should be the little-endian nowadays). Endianness issues happens only on native types (or low-level unions) with a size of multiple bytes.

You can check the endianness at runtime with the example code you provided (although using a uint32 -typed variable is safer). Regarding the result (ie. [1, 4] or [4, 1] ) you can guess the endianness. Based on the endianness, you can use a if-else statement to encode, decode or even directly compute the pixels (you can put that in a generic encoding/decoding function).

An alternative solution is not to use views at all and use portable bit-wise operations (independent of the endianness of the target platform).

Here is an example:

alpha = img_coded >> 24
red = (img_coded >> 16) & 0xFF
green = (img_coded >> 8) & 0xFF
blue = img_coded & 0xFF

What does this tell me about the order of the most significant bit? Why is the output [1,4] and not the other way around? What has this to do with endianness?

This means your platform use the little-endian format. This is what mainstream x86-64 platforms use. The little-endian format store the less-significant bytes first ( 1 here). The same code on a big-endian platform should result in [4,1] .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM