使用 PIL 将图像转换为特定调色板而不抖动
Convert image to specific palette using PIL without dithering
我正在尝试使用 Pillow 库(Python 图像库,PIL)将 PNG 格式的 RGB 图像转换为使用特定的索引调色板。但我想使用 "round to closest 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's answer to a similar question 中提到的,但它也产生了抖动:
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's answer 及其评论中所建议的那样,因为我以前的解决方案涉及在 Python 中编写的内部循环已被证明是大图像明显慢。
Pillow 6 incorporates pull request 3699,合并于 2019-03-11,
它将 dither
参数添加到普通的 quantize()
方法。
在 Pillow 6 之前,需要以下内容:
用 C 实现的 PIL 部分在 PIL._imaging
模块中,在您 from PIL import Image
之后也可以作为 Image.core
使用。
Pillow 的当前版本为每个 PIL.Image.Image
实例提供了一个名为 im
的成员,它是 ImagingCore
的一个实例,class 在 PIL._imaging
中定义。
您可以使用 help(oldimage.im)
列出其方法,但方法本身在 Python.
中未记录
ImagingCore
个对象的 convert
方法在 _imaging.c
中实现。
它需要一到三个参数并创建一个新的 ImagingCore
对象(在 _imaging.c
中称为 Imaging_Type
)。
mode
(必填):模式字符串(例如"P"
)
dither
(可选,默认0):PIL传递0或1
paletteimage
(可选):带有调色板的ImagingCore
我遇到的问题是 dist-packages/PIL/Image.py
中的 quantize()
强制 dither
参数为 1。
所以我提取了 quantize()
方法的副本并对其进行了更改。
因为它依赖于表面上私有的方法,所以它可能无法在 Pillow 的未来版本中使用。
然而,到那时,我们可以预期 Pillow pre-6 将不再使用,因为 Debian“bullseye”(2021 年年中稳定)和 Ubuntu“focal”(2020 年年中 LTS)包 Pillow 7 或更高版本。
#!/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
# Really old versions of Pillow (before 4.x) have _new
# under a different name
try:
return silf._new(im)
except AttributeError:
return silf._makeself(im)
# putpalette() input is a sequence of [r, g, b, r, g, b, ...]
# The data chosen for this particular answer represent
# the four gray values in a game console's palette
palettedata = [0, 0, 0, 102, 102, 102, 176, 176, 176, 255, 255, 255]
# Fill the entire palette so that no entries in Pillow's
# default palette for P images can interfere with conversion
NUM_ENTRIES_IN_PILLOW_PALETTE = 256
num_bands = len("RGB")
num_entries_in_palettedata = len(palettedata) // num_bands
palettedata.extend(palettedata[:num_bands]
* (NUM_ENTRIES_IN_PILLOW_PALETTE
- num_entries_in_palettedata))
# Create a palette image whose size does not matter
arbitrary_size = 16, 16
palimage = Image.new('P', arbitrary_size)
palimage.putpalette(palettedata)
# Perform the conversion
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.
我正在尝试使用 Pillow 库(Python 图像库,PIL)将 PNG 格式的 RGB 图像转换为使用特定的索引调色板。但我想使用 "round to closest 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's answer to a similar question 中提到的,但它也产生了抖动:
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's answer 及其评论中所建议的那样,因为我以前的解决方案涉及在 Python 中编写的内部循环已被证明是大图像明显慢。
Pillow 6 incorporates pull request 3699,合并于 2019-03-11,
它将 dither
参数添加到普通的 quantize()
方法。
在 Pillow 6 之前,需要以下内容:
用 C 实现的 PIL 部分在 PIL._imaging
模块中,在您 from PIL import Image
之后也可以作为 Image.core
使用。
Pillow 的当前版本为每个 PIL.Image.Image
实例提供了一个名为 im
的成员,它是 ImagingCore
的一个实例,class 在 PIL._imaging
中定义。
您可以使用 help(oldimage.im)
列出其方法,但方法本身在 Python.
ImagingCore
个对象的 convert
方法在 _imaging.c
中实现。
它需要一到三个参数并创建一个新的 ImagingCore
对象(在 _imaging.c
中称为 Imaging_Type
)。
mode
(必填):模式字符串(例如"P"
)dither
(可选,默认0):PIL传递0或1paletteimage
(可选):带有调色板的ImagingCore
我遇到的问题是 dist-packages/PIL/Image.py
中的 quantize()
强制 dither
参数为 1。
所以我提取了 quantize()
方法的副本并对其进行了更改。
因为它依赖于表面上私有的方法,所以它可能无法在 Pillow 的未来版本中使用。
然而,到那时,我们可以预期 Pillow pre-6 将不再使用,因为 Debian“bullseye”(2021 年年中稳定)和 Ubuntu“focal”(2020 年年中 LTS)包 Pillow 7 或更高版本。
#!/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
# Really old versions of Pillow (before 4.x) have _new
# under a different name
try:
return silf._new(im)
except AttributeError:
return silf._makeself(im)
# putpalette() input is a sequence of [r, g, b, r, g, b, ...]
# The data chosen for this particular answer represent
# the four gray values in a game console's palette
palettedata = [0, 0, 0, 102, 102, 102, 176, 176, 176, 255, 255, 255]
# Fill the entire palette so that no entries in Pillow's
# default palette for P images can interfere with conversion
NUM_ENTRIES_IN_PILLOW_PALETTE = 256
num_bands = len("RGB")
num_entries_in_palettedata = len(palettedata) // num_bands
palettedata.extend(palettedata[:num_bands]
* (NUM_ENTRIES_IN_PILLOW_PALETTE
- num_entries_in_palettedata))
# Create a palette image whose size does not matter
arbitrary_size = 16, 16
palimage = Image.new('P', arbitrary_size)
palimage.putpalette(palettedata)
# Perform the conversion
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.