Python 이미징 라이브러리를 사용하여 이미지를 4 색 팰릿 이미지로 변환하려면 어떻게해야합니까?
-
04-07-2019 - |
문제
4 색 그래픽을 지원하는 장치가 있습니다 (예전의 CGA와 마찬가지로).
사용하고 싶었습니다 필 이미지를 읽고 4 색 팔레트 (빨간색, 녹색, 노란색, 검은 색)를 사용하여 변환하려면 전혀 가능한지 알 수 없습니다. 다른 사람들이 그렇게 시도하고 실패했다고 제안하는 메일 링리스트 아카이브 게시물을 발견했습니다.
간단한 파이썬 예제는 대단히 감사하겠습니다!
보너스 포인트를 추가하면 이미지를 바이트 문자열로 변환하면 각 바이트가 4 픽셀의 데이터를 나타냅니다 (각각 0 ~ 3의 색상을 나타내는 두 비트)
해결책
첫째 : 4 개의 색상 팔레트 (검은 색, 녹색, 빨간색, 노란색)가 있습니다. 아니요 파란색 구성 요소. 따라서 파란색 구성 요소가 없다면 출력 이미지가 입력 이미지에 거의 근사하지 않음을 수락해야합니다.
이 코드를 시도하십시오 :
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)
이렇게하면 처음 4 개의 팔레트 항목 만 색상으로 설정된 PNG 팔레트 이미지를 만듭니다. 이 샘플 이미지 :
becomes
출력을 취하는 것은 사소한 일입니다 image2cga
(0-3 값의 시퀀스를 생성하고 4 값을 바이트에 포장합니다.
코드가하는 일에 대한 도움이 필요하면 물어 보시면 설명하겠습니다.
edit1 : 바퀴를 재창조하지 마십시오
물론, 나는 너무 열성적이었고 토마스가 발견 한대로 이미지가 발견 된 바와 같이, 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())
다른 팁
John, 나는 첫 번째 링크도 발견했지만 문제에 직접 도움이되지는 않았습니다. 그래도 나는 더 깊이 양자화되는 것처럼 보이게했다.
나는 어제 잠자리에 들기 전에 이것을 생각해 냈습니다.
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, 귀하의 솔루션은 동일한 원칙에 따라 작동하는 것 같습니다. Kudos, 나는 그것에 대한 일을 중단하고 당신의 답변을 기다렸을 것입니다. 내 것은 당신보다 더 논리적이지는 않지만 조금 더 간단합니다. 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)
단색을 얻기 위해 디더링을 원하지 않는 사람들을 위해. 나는 modded : 디더링없이 PIL을 사용하여 이미지를 특정 팔레트로 변환하십시오이 스레드의 두 솔루션으로. 이 실이 오래되었지만 우리 중 일부는 그 정보를 원합니다. Kudios