如何使用Python Imaging Library将任何图像转换为4色调色板图像? [英] How do I convert any image to a 4-color paletted image using the Python Imaging Library?

查看:657
本文介绍了如何使用Python Imaging Library将任何图像转换为4色调色板图像?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个支持4色图形的设备(很像以前的CGA)。

I have a device that supports 4-color graphics (much like CGA in the old days).

我想用 PIL 读取图像并使用我的4色调色板(红色,绿色,黄色,黑色)转换它,但我无法弄清楚它是否甚至可能。我发现了一些邮件列表存档帖子似乎暗示其他人试图这样做并失败。

I wanted to use PIL to read the image and convert it using my 4-color palette (of red, green, yellow, black), but I can't figure out if it's even possible at all. I found some mailing list archive posts that seem to suggest other people have tried to do so and failed.

一个简单的python示例将非常感谢!

A simple python example would be much appreciated!

如果添加一些内容然后将图像转换为字节字符串,其中每个字节代表4个像素的数据(每两个位代表一个0到3的颜色),则为奖励积分

Bonus points if you add something that then converts the image to a byte string where each byte represents 4 pixels of data (with each two bits representing a color from 0 to 3)

推荐答案

首先:你的四个调色板(黑色,绿色,红色,黄色)有 no 蓝色成分。所以,你必须接受你的输出图像几乎不会接近输入图像,除非没有蓝色组件开始。

First: your four colour palette (black, green, red, yellow) has no blue component. So, you have to accept that your output image will hardly approximate the input image, unless there is no blue component to start with.

试试这段代码:

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调色板图像四个调色板条目设置为您的颜色。此示例图片:

This creates a PNG palette image with only the first four palette entries set to your colours. This sample image:

http:// tzotzioy。 googlepages.com/new_pic_baby2.jpg

变为

http://tzotzioy.googlepages.com/cga-new_pic_baby2.png

获取 image2cga 的输出(产生0-3值的序列)并将每四个值打包到一个字节是微不足道的。

It's trivial to take the output of image2cga (yields a sequence of 0-3 values) and pack every four values to a byte.

如果您需要有关代码的帮助,请询问并解释。

If you need help about what the code does, please ask and I will explain.

当然,事实证明我太热情了 - 如托马斯发现 - Image.quantize方法可以将调色板图像作为参数并进行量化,结果远比我的广告好上面的-hoc方法:

Of course, turns out I was too enthusiastic and —as Thomas discovered— the Image.quantize method can take a palette image as argument and do the quantization with far better results than my ad-hoc method above:

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)



EDIT1,cont:将像素打包成字节



对于附加值,下面是生成打包字符串的代码(每个字节4个像素):

EDIT1, cont: Pack the pixels into bytes

For "added value", here follows code to produce the packed string (4 pixels per byte):

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())

这篇关于如何使用Python Imaging Library将任何图像转换为4色调色板图像?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆