Преобразование изображения в конкретную палитру с использованием PIL без сглаживания

1

Я пытаюсь преобразовать изображение RGB в формате PNG, чтобы использовать определенную индексированную палитру, используя библиотеку Pillow (Python Image Library, PIL). Но я хочу преобразовать с использованием метода "round to closeest color", а не сглаживания, потому что изображение - это искусство пикселя, а сглаживание искажает контуры областей и добавляет шум в области, которые должны быть плоскими.

Я попробовал Image.Image.paste(), и он использовал четыре указанных цвета, но он произвел изображение с затуханием:

from PIL import Image
oldimage = Image.open("oldimage.png")
palettedata = [0, 0, 0, 102, 102, 102, 176, 176, 176, 255, 255, 255]
newimage = Image.new('P', oldimage.size)
newimage.putpalette(palettedata * 64)
newimage.paste(oldimage, (0, 0) + oldimage.size)
newimage.show()    

Я попробовал Image.Image.quantize(), как упоминалось в pictu ответить на аналогичный вопрос, но также произвел сглаживание:

from PIL import Image
palettedata = [0, 0, 0, 102, 102, 102, 176, 176, 176, 255, 255, 255]
palimage = Image.new('P', (16, 16))
palimage.putpalette(palettedata * 64)
oldimage = Image.open("School_scrollable1.png")
newimage = oldimage.quantize(palette=palimage)
newimage.show()

Я пробовал Image.Image.convert(), и он преобразовал изображение без сглаживания, но он включал цвета, отличные от указанных, по-видимому, потому, что он использовал либо палитру или адаптивную палитру

from PIL import Image
oldimage = Image.open("oldimage.png")
palettedata = [0, 0, 0, 102, 102, 102, 176, 176, 176, 255, 255, 255]
expanded_palettedata = palettedata * 64
newimage = oldimage.convert('P', dither=Image.NONE, palette=palettedata)
newimage.show()

Как автоматически преобразовать изображение в определенную палитру без сглаживания? Я хотел бы избежать решения, которое обрабатывает каждый отдельный пиксель в Python, как указано в John La Rooy answer и комментарии к нему, потому что мое предыдущее решение, включающее внутренний цикл, написанный на Для больших изображений Python оказался заметно медленным.

  • 1
    «Округление до ближайшего цвета» с заданной произвольной цветовой палитрой в значительной степени требует пиксель-пиксельной техники, поэтому, вероятно, лучше всего писать на неинтерпретированном языке, таком как C. Части PIL написаны на C - его открытый исходный код - так Вы можете расширить его, а не писать новый модуль расширения с нуля.
  • 0
    @martineau Дизеринг в пользовательскую палитру также требует попиксельной техники, но PIL может это сделать. Для округления до ближайшего цвета в сети или адаптивной палитре также требуется попиксельная техника, но PIL может это сделать. Он просто не может округлить до ближайшего цвета в пользовательской палитре. Если бы мне пришлось раскошелиться на PIL, мне пришлось бы покупать по одной машине для каждой платформы, чтобы поддерживать двоичные файлы для каждой платформы.
Показать ещё 2 комментария
Теги:
image
python-imaging-library

2 ответа

2
Лучший ответ

Части PIL, реализованные в C, находятся в модуле PIL._imaging, также доступны как Image.core после from PIL import Image. Текущие версии Pillow предоставляют каждому экземпляру PIL.Image.Image член с именем im, который является экземпляром ImagingCore, класса, определенного в PIL._imaging. Вы можете перечислить его методы с помощью help(oldimage.im), но сами методы недокументированы из Python.

Метод convert объектов ImagingCore реализован в _imaging.c. Он принимает от одного до трех аргументов и создает новый объект ImagingCore (называемый Imaging_Type внутри _imaging.c).

  • mode (обязательно): строка режима (например, "P")
  • dither (необязательно, по умолчанию 0): PIL передает 0 или 1
  • paletteimage (необязательно): ImagingCore с палитрой

Проблема, с которой я столкнулся, заключается в том, что quantize() в dist-packages/PIL/Image.py заставляет аргумент dither равным 1. Поэтому я вытащил копию метода quantize() и изменил это. Возможно, это не сработает в будущих версиях Pillow, но в противном случае они могут реализовать "расширенный интерфейс квантизатора в более поздней версии", который содержит комментарий в quantize() promises.

#!/usr/bin/env python3
from PIL import Image

def quantizetopalette(silf, palette, dither=False):
    """Convert an RGB or L mode image to use a given P image 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

    # Later versions of Pillow (4.x) rename _makeself to _new
    try:
        return silf._new(im)
    except AttributeError:
        return silf._makeself(im)

palettedata = [0, 0, 0, 102, 102, 102, 176, 176, 176, 255, 255, 255]
palimage = Image.new('P', (16, 16))
palimage.putpalette(palettedata * 64)
oldimage = Image.open("School_scrollable1.png")
newimage = quantizetopalette(oldimage, palimage, dither=False)
newimage.show()
0

Я взял все это и сделал это быстрее, добавил заметки, чтобы вы поняли и превратились в подушку вместо пила. В основном.

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 palette."""

    silf.load()

    # use palette from reference image made below
    palette.load()
    im = silf.im.convert("P", 0, palette.im)
    # the 0 above means turn OFF dithering making solid colors
    return silf._new(im)

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

for imgfn in sys.argv[1:]:
    palettedata = [ 0, 0, 0, 255, 0, 0, 255, 255, 0, 0, 255, 0, 255, 255, 255,85,255,85, 255,85,85, 255,255,85] 

#   palettedata = [ 0, 0, 0, 0,170,0, 170,0,0, 170,85,0,] # pallet 0 dark
#   palettedata = [ 0, 0, 0, 85,255,85, 255,85,85, 255,255,85]  # pallet 0 light

#   palettedata = [ 0, 0, 0, 85,255,255, 255,85,255, 255,255,255,]  #pallete 1 light
#   palettedata = [ 0, 0, 0, 0,170,170, 170,0,170, 170,170,170,] #pallete 1 dark
#   palettedata = [ 0,0,170, 0,170,170, 170,0,170, 170,170,170,] #pallete 1 dark sp

#   palettedata = [ 0, 0, 0, 0,170,170, 170,0,0, 170,170,170,] # pallet 3 dark
#   palettedata = [ 0, 0, 0, 85,255,255, 255,85,85, 255,255,255,] # pallet 3 light

#  grey  85,85,85) blue (85,85,255) green (85,255,85) cyan (85,255,255) lightred 255,85,85 magenta (255,85,255)  yellow (255,255,85) 
# black 0, 0, 0,  blue (0,0,170) darkred 170,0,0 green (0,170,0)  cyan (0,170,170)magenta (170,0,170) brown(170,85,0) light grey (170,170,170) 
#  
# below is the meat we make an image and assign it a palette
# after which it used to quantize the input image, then that is saved 
    palimage = Image.new('P', (16, 16))
    palimage.putpalette(palettedata *32)
    oldimage = Image.open(sys.argv[1])
    oldimage = oldimage.convert("RGB")
    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)

#   palimage.putpalette(palettedata *64)  64 times 4 colors on the 256 index 4 times, == 256 colors, we made a 256 color pallet.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню