Почему Pillow преобразует возвращаемые цвета за пределы указанной палитры? - PullRequest
5 голосов
/ 01 апреля 2019

Использование подушки 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))

Входное изображение: -> input image <- (изображение 3x3 пикселя, все цвета уникальные) </p>

(увеличенная версия, только для визуализации: enter image description here)

Вывод:

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

(Обратите внимание, что взлом для предотвращения дизеринга - это обходной путь, ожидающий исправления, которое я внес в мастер (еще не добавлено в новую версию))

Значения [(170, 170, 170), (84, 84, 84), (252, 252, 252)] появляются в преобразованном изображении, но не были указаны в исходной палитре. Все они оказываются серыми.

Я думаю, что-то в src / libImaging / Palette.c это влияет на это, но я не уверен, является ли это ошибкой кода или «особенностью» libjpeg

1 Ответ

1 голос
/ 19 апреля 2019

Оказывается, эта проблема является и ошибкой пользователя, и неожиданной проблемой инициализации.

Проблема инициализации: Как указано в комментариях *1003*, палитра для нового изображения специально инициализирована как оттенки серого .

Если мы заменим всю палитру своей собственной, то все в порядке.За исключением, я не был.

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]

То есть:

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.

В результате палитра всегда будет иметь длину 768.

Использование последнего значения из предоставленного нами массива является произвольным выбором, так как используется только в качестве допустимого значения заполнения.

...