كيف يمكنني تحويل أي صورة إلى 4 لون صورة المرصفة باستخدام مكتبة التصوير بيثون؟

StackOverflow https://stackoverflow.com/questions/236692

سؤال

ولدي جهاز يدعم رسومات 4-لون (مثل الكثير من CGA في الأيام الخوالي).

وكنت أرغب في استخدام PIL لقراءة الصورة وتحويله باستخدام بلدي 4- لوحة الألوان (الأحمر والأخضر والأصفر والأسود)، ولكن أنا لا يمكن معرفة ما اذا كان من الممكن حتى على الإطلاق. لقد وجدت بعض أرشيف القائمة البريدية المشاركات التي يبدو أنها تشير إلى أشخاص آخرين حاولوا القيام بذلك وفشلت.

وهناك مثال بسيط الثعبان سيكون محل تقدير كبير!

ونقاط مكافأة إذا قمت بإضافة شيء ثم تحويل الصورة إلى سلسلة بايت حيث يمثل كل بايت 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 القيم) وتغليف كل أربعة القيم إلى بايت.

إذا كنت بحاجة إلى مساعدة حول ما رمز لا، من فضلك اطلب وسأشرح.

EDIT1: لا إعادة اختراع العجلة

وبطبيعة الحال، تبين تحمست جدا و-كما توماس discovered- طريقة Image.quantize يمكن التقاط صورة لوحة كوسيطة والقيام تكميم مع نتائج أفضل بكثير من بلدي طريقة مخصصة أعلاه:

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، تابع: حزمة بكسل إلى بايت

ل"القيمة المضافة"، وهنا يلي كود لإنتاج سلسلة معبأة (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 هو مرهق للاستخدام. وتفضلوا بقبول فائق يفسر ما يحدث للقيام بذلك.

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)

وبالنسبة لأولئك الذين يريدون NO التردد للحصول على الألوان الصلبة. انا كتكوت: تحويل الصورة إلى لوحة محددة باستخدام PIL دون التردد مع حلول اثنين في هذا الموضوع. على الرغم من أن هذا الموضوع هو قديم، والبعض منا يريد تلك المعلومات. Kudios

scroll top