Reputation: 37
I am trying to convert a binary string to an image in Python using numpy but i am having a hard time finding a good way of approaching it with a an unconventional bit distribution (as far as my knowledge goes).
these are the specifics of how and what to convert.
16-bit texture tile (256*256). Each bitu16 represents a pixel whose colour is of the form ARGB, MSB-to-LSB:
1-bit transparency
5-bit red channel
5-bit green channel
5-bit blue channel
Numpy does not really have support for anything 1 bit or 5 bit. I tried setting up a np.dtype with different argb channels but without any luck. unpackbits seems to not work on uint16 so in that case i might have to split it in 2 uint8's
dt = np.dtype([('a', np.bool_), ('r', np.half), ('g', np.half), ('b', np.half)])
data = data.read(131072)
dataFromBuffer = np.frombuffer(data, dtype=dt)
img = dataFromBuffer.reshape(256, 256)
Upvotes: 1
Views: 464
Reputation: 53079
Here is how to make your approach work:
# make small example
x = np.random.randint(0,1<<16,size=(5,5),dtype=np.uint16)
# set up dtype
dt = np.dtype([*zip('argb',(bool,*3*(np.uint8,)))])
# bit of bit twiddling
def unpack_argb(x):
out = np.empty(x.shape,dt)
for i,ch in enumerate(reversed('argb')):
out[ch] = (x>>(5*i))&31
return out
def pack_argb(x):
out = x['a'].astype(np.uint16)
for ch in 'rgb':
out <<= 5
out += x[ch]&31
return out
# check round trip
np.all(x == pack_argb(unpack_argb(x)))
# True
Update:
def argb16_to_rgba32(x):
out = np.empty(x.shape+(4,),np.uint8)
out[...,3] = (x>>8)&0x80
out[...,0] = (x>>7)&0xf8
out[...,1] = (x>>2)&0xf8
out[...,2] = (x<<3)&0xf8
return out
def rgba32_to_argb16(x):
x16 = x.astype(np.uint16)&0xf8
out = (x16[...,3]&0x80)<<8
out += x16[...,0]<<7
out += x16[...,1]<<2
out += x16[...,2]>>3
return out
Upvotes: 1
Reputation: 1606
You are right regarding the lack of bit numpy bit level support in this case. A high-level (yet functional) approach for handling the bits can be done as follows:
image_16_bit = 123 # A 16bit integer.
bits = '{:016b}'.format(image_16_bit)
transparency = int(bits[0], 2)
red_channel = int(bits[1:6], 2)
green_channel = int(bits[6:11], 2)
blue_channel = int(bits[11:], 2)
print(transparency, red_channel, green_channel, blue_channel) # 0 0 3 27
You may run this over all the ints, and then collect the individual channel values. Finally you may cast it to a numpy array to get your image as a numpy array.
Upvotes: 2