Frage

Ich habe ein Gerät, das 4-Farben-Grafik (ähnlich wie CGA in den alten Tagen) unterstützt.

Ich wollte PIL das Bild zu lesen und wandeln es mein unter Verwendung von 4- Farbpalette (rot, grün, gelb, schwarz), aber ich kann nicht herausfinden, ob es überhaupt noch möglich ist. Ich fand Beiträge einige Mailing-Liste Archiv, das andere Leute versucht haben, deuten darauf hin, scheinen dies zu tun und sind gescheitert.

Ein einfaches Python-Beispiel würde sehr geschätzt werden!

Bonuspunkte, wenn man etwas hinzufügen, die dann das Bild auf eine Bytefolge wandelt wobei jedes Byte 4 Pixel von Daten (mit jeweils zwei Bits von 0 bis 3 darstellt, eine Farbe)

steht
War es hilfreich?

Lösung

Erstens: Ihre vier Farbpalette (schwarz, grün, rot, gelb) hat nicht blaue Komponente. Also müssen Sie akzeptieren, dass Ihr Ausgangsbild kaum das Eingangsbild annähern wird, es sei denn, es keine blaue Komponente ist mit zu beginnen.

Versuchen Sie diesen Code ein:

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)

Dies erzeugt ein Bild PNG-Palette mit nur den ersten vier Paletteneinträgen auf Ihre Farben. Dieses Beispielbild:

wird

Es ist trivial, die Ausgabe von image2cga zu nehmen (ergibt eine Folge von 0-3-Werte) und alle vier Werte auf ein Byte packen.

Wenn Sie Hilfe benötigen, zu wissen, was der Code ist, fragen Sie bitte und ich werde erklären.

EDIT1: Sie neu erfinden das Rad nicht

Natürlich stellt sich heraus, auch ich war begeistert und -wie Thomas die Image.quantize Methode discovered- kann eine Palette Bild als Argument nehmen und zu tun, um die Quantisierung mit weitaus bessere Ergebnisse als meine Ad-hoc-Verfahren oben:

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: Packen Sie die Pixel in Bytes

Für "Mehrwert", hier Code folgt dem gepackter String (4 Pixel pro Byte) zu erzeugen:

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

Andere Tipps

John, fand ich, dass die erste Verbindung als gut, aber es hat direkt mir nicht mit dem Problem helfen. Es hat aussehen mich tiefer in Quantisierungsraster though.

Ich kam mit dieser gestern, bevor Sie zu Bett gehen:

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, Ihre Lösung scheint nach den gleichen Prinzipien zu arbeiten. Ein großes Lob, ich sollte daran zu arbeiten aufgehört haben, und wartete auf Ihre Antwort. Ich ist ein bisschen einfacher, obwohl definitiv nicht logischer als deine. PIL ist umständlich zu bedienen. Sie erklärt, was los ist, es zu tun.

import sys
import PIL
from PIL import Image

def quantizetopalette(silf, palette, dither=False):
    """Convert an RGB or L mode image to use a given P image's palette."""

    silf.load()

    # use palette from reference image
    palette.load()
    if palette.mode != "P":
        raise ValueError("bad mode for palette image")
    if silf.mode != "RGB" and silf.mode != "L":
        raise ValueError(
            "only RGB or L mode images can be quantized to a palette"
            )
    im = silf.im.convert("P", 1 if dither else 0, palette.im)
    # the 0 above means turn OFF dithering
    return silf._makeself(im)

if __name__ == "__main__":
    import sys, os

for imgfn in sys.argv[1:]:
    palettedata = [ 0, 0, 0, 0, 255, 0, 255, 0, 0, 255, 255, 0,] 
    palimage = Image.new('P', (16, 16))
    palimage.putpalette(palettedata + [0, ] * 252 * 3)
    oldimage = Image.open(sys.argv[1])
    newimage = quantizetopalette(oldimage, palimage, dither=False)
    dirname, filename= os.path.split(imgfn)
    name, ext= os.path.splitext(filename)
    newpathname= os.path.join(dirname, "cga-%s.png" % name)
    newimage.save(newpathname)

Für diejenigen, die NO-Dithering wollte Unifarben zu bekommen. i modded: Konvertierung von Abbildern auf bestimmte Palette PIL verwenden, ohne Dithering mit den beiden Lösungen in diesem Thread. Auch wenn dieser Thread alt ist, wollen einige von uns diese Informationen. Kudios

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top