使用numpy和pil将565(16位色)转换为888(24位色)
我得先说明一下,我现在有一个用位移和putpixel的方法可以工作,但速度非常慢,所以我想用numpy来加快这个过程。我觉得我快成功了,但还差一点。我测了一下我认为可以工作的代码,发现时间提高了0.3秒,这让我更有动力去改进。
我现在能用的代码是:
buff # a binary set of data
im = Image.new("RGBA",(xdim,ydim))
for y in xrange(ydim):
for x in xrange(xdim):
px = buff[x*y]
# the 255 is for the alpha channel which I plan to use later
im.putpixel((x,y),(px&0xF800) >> 8, (px&0x07E0) >> 3, (px&0x001F) <<3, 255))
return im
我想让它工作的代码看起来是这样的:
im16 = numpy.fromstring(buff,dtype=numpy.uint16) #read data as shorts
im16 = numpy.array(im16,dtype=numpy.uint32) #now that it's in the correct order, convert to 32 bit so there is room to do shifting
r = numpy.right_shift(8, im16.copy() & 0xF800)
g = numpy.right_shift(3, im16.copy() & 0x07E0)
b = numpy.left_shift( 3, im16 & 0x001F)
pA = numpy.append(r,g)
pB = numpy.append(b,numpy.ones((xdim,ydim),dtype=numpy.uint32) * 0xFF) #this is a black alpha channel
img = numpy.left_shift(img,8) #gives me green channel
im24 = Image.fromstring("RGBA",(xdim,ydim),img)
return im24
所以最后的问题是,颜色通道没有正确结合,我觉得我不应该再做最后的位移(注意,如果我不位移8位,我能得到红色通道)。如果能帮我正确地把所有东西结合起来,我会非常感激。
解决方案
import numpy as np
arr = np.fromstring(buff,dtype=np.uint16).astype(np.uint32)
arr = 0xFF000000 + ((arr & 0xF800) >> 8) + ((arr & 0x07E0) << 5) + ((arr & 0x001F) << 19)
return Image.frombuffer('RGBA', (xdim,ydim), arr, 'raw', 'RGBA', 0, 1)
关键在于你需要把它打包成MSB(透明度,蓝色,绿色,红色)LSB,这和putpixel的方式有点反直觉,但这样做是有效的,而且效果很好。
2 个回答
1
如果你想要正确地进行缩放,这里有一个更符合PIL(Python Imaging Library)风格的方法来解决你的问题。
FROM_5 = ((np.arange(32, dtype=numpy.uint16) * 255 + 15) // 31).astype(numpy.ubyte)
FROM_6 = ((np.arange(64, dtype=numpy.uint16) * 255 + 31) // 63).astype(numpy.ubyte)
data = numpy.fromstring(buff, dtype=numpy.uint16)
r = Image.frombuffer('L', shape, FROM_5[data >> 11], 'raw', 'L', 0, 1)
g = Image.frombuffer('L', shape, FROM_6[(data >> 5) & 0x3F], 'raw', 'L', 0, 1)
b = Image.frombuffer('L', shape, FROM_5[data & 0x1F], 'raw', 'L', 0, 1)
return Image.merge('RGB', (r, g, b))
3
警告:下面的代码还没有经过检查,但我觉得这应该能满足你的需求(前提是我理解得没错):
import numpy as np
arr = np.fromstring(buff,dtype=np.uint16).astype(np.uint32)
arr = ((arr & 0xF800) << 16) + ((arr & 0x07E0) << 13) + ((arr & 0x001F) << 11) + 0xFF
return Image.frombuffer('RGBA', (xdim,ydim), arr, 'raw', 'RGBA', 0, 1)
我把所有的颜色通道合并成了32位。在进行位移操作的那一行,最左边的8位是红色,接下来的8位是绿色,再接下来的8位是蓝色,最后的8位是透明度(alpha)。位移的数字可能看起来有点奇怪,因为我参考了16位格式的位移方式。此外,我使用了 frombuffer
,这样我们就可以利用Numpy正在使用的缓冲区,而不是先转换成字符串。
你可以看看这个页面。我觉得它不是特别好,但根据我的经验,这就是使用PIL时的情况。文档其实并不太友好,老实说我常常觉得很困惑,不过我也不打算自告奋勇去重写它,因为我不常用PIL。