我有一个支持4色图形的设备(很像以前的CGA).
我想使用PIL读取图像并使用我的4色调色板(红色,绿色,黄色,黑色)进行转换,但我无法弄清楚它是否可能.我发现一些邮件列表存档帖子似乎暗示其他人已经尝试这样做并且失败了.
一个简单的python示例将非常感谢!
如果添加一些内容然后将图像转换为字节字符串,其中每个字节代表4个像素的数据(每两个位代表一个0到3的颜色),则可获得奖励积分
第一:你的四个调色板(黑色,绿色,红色,黄色)没有蓝色成分.因此,您必须接受输出图像几乎不会接近输入图像,除非没有蓝色组件开始.
试试这段代码:
import Image def estimate_color(c, bit, c_error): c_new= c - c_error if c_new > 127: c_bit= bit c_error= 255 - c_new else: c_bit= 0 c_error= -c_new return c_bit, c_error def image2cga(im): "Produce a sequence of CGA pixels from image im" im_width= im.size[0] for index, (r, g, b) in enumerate(im.getdata()): if index % im_width == 0: # start of a line r_error= g_error= 0 r_bit, r_error= estimate_color(r, 1, r_error) g_bit, g_error= estimate_color(g, 2, g_error) yield r_bit|g_bit def cvt2cga(imgfn): "Convert an RGB image to (K, R, G, Y) CGA image" inp_im= Image.open(imgfn) # assume it's RGB out_im= Image.new("P", inp_im.size, None) out_im.putpalette( ( 0, 0, 0, 255, 0, 0, 0, 255, 0, 255, 255, 0, ) ) out_im.putdata(list(image2cga(inp_im))) return out_im if __name__ == "__main__": import sys, os for imgfn in sys.argv[1:]: im= cvt2cga(imgfn) dirname, filename= os.path.split(imgfn) name, ext= os.path.splitext(filename) newpathname= os.path.join(dirname, "cga-%s.png" % name) im.save(newpathname)
这将创建一个PNG调色板图像,只有前四个调色板条目设置为您的颜色.此示例图片:
变
获取输出image2cga
(产生0-3值的序列)并将每四个值打包到一个字节是微不足道的.
如果您需要有关代码的帮助,请询问,我会解释.
当然,事实证明我太热情了 - 而且Thomas发现 - Image.quantize方法可以将调色板图像作为参数进行量化,并且比上面的ad-hoc方法得到更好的结果:
def cga_quantize(image): pal_image= Image.new("P", (1,1)) pal_image.putpalette( (0,0,0, 0,255,0, 255,0,0, 255,255,0) + (0,0,0)*252) return image.convert("RGB").quantize(palette=pal_image)
对于"附加值",下面是生成打包字符串的代码(每个字节4个像素):
import itertools as it # setup: create a map with tuples [(0,0,0,0)?(3,3,3,3)] as keys # and values [chr(0)?chr(255)], because PIL does not yet support # 4 colour palette images TUPLE2CHAR= {} # Assume (b7, b6) are pixel0, (b5, b4) are pixel1… # Call it "big endian" KEY_BUILDER= [ (0, 64, 128, 192), # pixel0 value used as index (0, 16, 32, 48), # pixel1 (0, 4, 8, 12), # pixel2 (0, 1, 2, 3), # pixel3 ] # For "little endian", uncomment the following line ## KEY_BUILDER.reverse() # python2.6 has itertools.product, but for compatibility purposes # let's do it verbosely: for ix0, px0 in enumerate(KEY_BUILDER[0]): for ix1, px1 in enumerate(KEY_BUILDER[1]): for ix2, px2 in enumerate(KEY_BUILDER[2]): for ix3, px3 in enumerate(KEY_BUILDER[3]): TUPLE2CHAR[ix0,ix1,ix2,ix3]= chr(px0+px1+px2+px3) # Another helper function, copied almost verbatim from itertools docs def grouper(n, iterable, padvalue=None): "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')" return it.izip(*[it.chain(iterable, it.repeat(padvalue, n-1))]*n) # now the functions def seq2str(seq): """Takes a sequence of [0..3] values and packs them into bytes using two bits per value""" return ''.join( TUPLE2CHAR[four_pixel] for four_pixel in grouper(4, seq, 0)) # and the image related function # Note that the following function is correct, # but is not useful for Windows 16 colour bitmaps, # which start at the *bottom* row… def image2str(img): return seq2str(img.getdata())
约翰,我发现第一个链接,但它没有直接帮助我解决这个问题.它确实让我更深入地了解量化.
我昨天睡觉前想出了这个:
import sys import PIL import Image PALETTE = [ 0, 0, 0, # black, 00 0, 255, 0, # green, 01 255, 0, 0, # red, 10 255, 255, 0, # yellow, 11 ] + [0, ] * 252 * 3 # a palette image to use for quant pimage = Image.new("P", (1, 1), 0) pimage.putpalette(PALETTE) # open the source image image = Image.open(sys.argv[1]) image = image.convert("RGB") # quantize it using our palette image imagep = image.quantize(palette=pimage) # save imagep.save('/tmp/cga.png')
TZ.TZIOY,您的解决方案似乎遵循相同的原则.荣幸,我应该停止工作并等待你的回复.我觉得有点简单,虽然肯定不比你的更合乎逻辑.PIL使用起来很麻烦.你的解释是怎么回事.