为什么枕头转换指定调色板之外的返回颜色?

gla*_*snt 5 python palette python-imaging-library

使用Pillow 5.4.1,Python 3.6.8

给定一个image.png具有9种不同颜色的图像,并给一个具有5种不同颜色的数据调色板,人们可能会希望pillow将图像缩小为所描述的调色板,从而使生成的图像仅包含该调色板中的颜色。

但是,使用该im.im.convert方法返回的图像具有超出指定调色板的颜色。具体来说,它们始终是灰度图像(R==B==G值)

示例代码,为原始图像,调色板和转换后的图像输出唯一的颜色集。

from PIL import Image
im = Image.open("image.png")

# create palette from raw data
# colours: Red, Green, Blue, Black, and White (5 total)
RGBBW = [(255,0,0), (0,255,0), (0,0,255), (0,0,0), (255,255,255)]
data = sum([list(x) for x in RGBBW], [])[:256]
pimg = Image.new("P",(16,16))
pimg.putpalette(data)

# Hack
im.convert("RGB")
cim_ = im.im.convert("P", 0, pimg.im)
cim = im._new(cim_).convert("RGB")

def colors(im):
    cs = []
    for x in range(im.width):
        for y in range(im.height):
            cs.append(im.getpixel((x,y)))
    return list(set(cs))

print("Original: %s" % colors(im))
print("Palette: %s" % RGBBW)
print("Convert: %s" % colors(cim))
Run Code Online (Sandbox Code Playgroud)

输入图像:-> 输入图像 <-(3x3像素的图像,所有像素都是唯一的颜色)

(较大的版本,仅用于可视化: 在此处输入图片说明

输出:

Original: [(85, 85, 85, 255), (0, 0, 255, 255), (0, 0, 0, 255), (255, 0, 0, 255), (0, 255, 255, 255), (255, 255, 255, 255), (255, 255, 0, 255), (255, 0, 255, 255), (0, 255, 0, 255)]
Palette: [(255, 0, 0), (0, 255, 0), (0, 0, 255), (0, 0, 0), (255, 255, 255)]
Convert: [(252, 252, 252), (0, 0, 255), (255, 0, 0), (0, 0, 0), (170, 170, 170), (0, 255, 0), (84, 84, 84)]
Run Code Online (Sandbox Code Playgroud)

(请注意,防止抖动的黑客是一种解决方法,有待我提供给母版的修复程序(尚未切成新版本))

这些值[(170, 170, 170), (84, 84, 84), (252, 252, 252)]显示在转换后的图像中,但未在原始调色板中指定。它们都是灰度的。

我认为src / libImaging / Palette.c中有一些东西会影响这一点,但是我不确定这是代码的错误还是libjpeg的“功能”

gla*_*snt 2

事实证明,这个问题既是用户错误也是意外的初始化问题。

初始化问题:正如评论中所指出的,新图像的调色板专门初始化为 greyscale

如果我们用我们自己的调色板替换整个调色板,那就没问题了。但我不是。

data = sum([list(x) for x in RGBBW], [])[:256]

这条线在逻辑上是不正确的。

调色板需要一个最多包含 256 个 RGB 三元组的扁平列表,即最大长度为 768 的数组。如果数组小于此值,则其余的灰度仍将发挥作用。

重新初始化调色板的更好方法是确保我们重复一个值来覆盖灰度。

在这种情况下:

data = (sum([list(x) for x in RGBBW], []) + (RGBBW[-1] * (256 - len(RGBBW))))[:256*3]

Run Code Online (Sandbox Code Playgroud)

那是:

data = (
    sum([list(x) for x in RGBBW], []) # flatten the nested array
    + (RGBBW[-1] * (256 - len(RGBBW))) # extend with the last value, to our required length, if needed
    )[:256*3] # and trim back, if needed.

Run Code Online (Sandbox Code Playgroud)

这将导致调色板的长度始终为 768。

使用我们提供的数组中的最后一个值是任意选择,因为它仅用作有效的填充值。