使用PIL将图像转换为特定调色板而不抖动

2024-09-27 23:25:12 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试使用枕头库(Python图像库,PIL)将PNG格式的RGB图像转换为使用特定的索引调色板。但我想用“舍入到最接近的颜色”的方法来转换,而不是抖动,因为图像是像素艺术,抖动会扭曲区域的轮廓,并给打算平坦的区域添加噪声。

我试过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()    

我试过pictu's answer to a similar question中提到的Image.Image.quantize(),但也产生了抖动:

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(),它没有抖动就转换了图像,但它包含了指定颜色以外的其他颜色,可能是因为它使用了web调色板或自适应调色板

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's answer及其注释中所建议的,因为我以前的解决方案涉及Python编写的内部循环,对于大型图像来说,速度明显较慢。


Tags: from图像imageimportpilpng颜色show
2条回答

在C中实现的PIL部分位于PIL._imaging模块中,在您from PIL import Image之后也可以作为Image.core使用。 当前版本的枕头给每个PIL.Image.Image实例一个名为im的成员,它是ImagingCore的实例,一个在PIL._imaging中定义的类。 您可以用help(oldimage.im)列出它的方法,但是这些方法本身在Python中没有文档。

ImagingCore对象的convert方法在^{}中实现。 它接受一到三个参数并创建一个新的ImagingCore对象(在_imaging.c内称为Imaging_Type)。

  • mode(必需):模式字符串(例如"P"
  • dither(可选,默认为0):PIL传递0或1
  • paletteimage(可选):带有调色板的ImagingCore

我面临的问题是dist-packages/PIL/Image.py中的quantize()dither参数强制为1。 所以我取出了quantize()方法的一个副本并更改了它。 这在枕头的未来版本中可能不起作用,但如果不起作用,它们很可能实现quantize()中的注释承诺的“以后版本中的扩展量化器接口”。

#!/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'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

    # 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()

我把所有这些都记下来,使它更快,并添加了一些注释,以便您理解,并将其转换为枕头而不是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 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's 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.

相关问题 更多 >

    热门问题