Python 使用PIL调整png图像的大小会丢失透明度
我使用此方法调整png图像的大小: 但该图像仍会失去透明度:Python 使用PIL调整png图像的大小会丢失透明度,python,python-imaging-library,Python,Python Imaging Library,我使用此方法调整png图像的大小: 但该图像仍会失去透明度: import Image, numpy def resize(filename, img,height, width): if filename.endswith(".png"): img = img.convert('RGBA') premult = numpy.fromstring(img.tostring(), dtype=numpy.uint8) alphaLayer
import Image, numpy
def resize(filename, img,height, width):
if filename.endswith(".png"):
img = img.convert('RGBA')
premult = numpy.fromstring(img.tostring(), dtype=numpy.uint8)
alphaLayer = premult[3::4] / 255.0
premult[::4] *= alphaLayer
premult[1::4] *= alphaLayer
premult[2::4] *= alphaLayer
img = Image.fromstring("RGBA", img.size, premult.tostring())
img = img.resize((height,width), Image.ANTIALIAS)
return img
从
到
问题与链接的问题无关,相反,您必须修补PIL,使其正确读取
tRNS
PNG块。PIL对此区块采用单个值,但显示的此图像对调色板中的每个值都有透明度描述。处理完后,就可以简单地解决问题:将图像转换为'LA'
模式并调整大小:
import sys
from PIL import Image
img = Image.open(sys.argv[1])
pal = img.getpalette()
width, height = img.size
actual_transp = img.info['actual_transparency'] # XXX This will fail.
result = Image.new('LA', img.size)
im = img.load()
res = result.load()
for x in range(width):
for y in range(height):
t = actual_transp[im[x, y]]
color = pal[im[x, y]]
res[x, y] = (color, t)
result.resize((64, 64), Image.ANTIALIAS).save(sys.argv[2])
所以我们从这个,到这个:
针对这种特定情况的PIL补丁实际上非常简单。打开你的
PIL/PngImagePlugin.py
,进入函数chunk\u tRNS
,输入检查im\u模式==“P”
的if
语句,然后检查i>=0
,然后添加行self.im.info[“实际透明度”]=map(ord,s)
我已经解决了这个问题,不再需要来自mmgp的漂亮破解。 PIL现在将正确读取并应用透明度
这是一个更复杂的调整大小功能(保持透明度):它只允许使用新的宽度值、新的宽度和高度值或参考文件来获得新的大小。您也可以更改重采样方法:
## PngResizeTransparency.py
#
## Resize PNG image by keeping the transparency
## thanks to https://stackoverflow.com/users/1453719/nicolas-barbey
#
## Use:
## - using a reference file to get new sizes:
# PNG_ResizeKeepTransparency(SourceFile, ResizedFile, RefFile ='YourRefFile.png')
## - using only the resized width:
# PNG_ResizeKeepTransparency(SourceFile, ResizedFile, new_width)
## - using resized width and hight:
# PNG_ResizeKeepTransparency(SourceFile, ResizedFile, new_width, new_height)
## - using resample mode: add param resample="NEAREST"/"BILINEAR"/"BICUBIC"/"ANTIALIAS"
from PIL import Image
def PNG_ResizeKeepTransparency(SourceFile, ResizedFile, new_width=0, new_height=0, resample="ANTIALIAS", RefFile =''):
# needs PIL
# Inputs:
# - SourceFile = initial PNG file (including the path)
# - ResizedFile = resized PNG file (including the path)
# - new_width = resized width in pixels; if you need % plz include it here: [your%] *initial width
# - new_height = resized hight in pixels ; default = 0 = it will be calculated using new_width
# - resample = "NEAREST", "BILINEAR", "BICUBIC" and "ANTIALIAS"; default = "ANTIALIAS"
# - RefFile = reference file to get the size for resize; default = ''
img = Image.open(SourceFile) # open PNG image path and name
img = img.convert("RGBA") # convert to RGBA channels
width, height = img.size # get initial size
# if there is a reference file to get the new size
if RefFile != '':
imgRef = Image.open(RefFile)
new_width, new_height = imgRef.size
else:
# if we use only the new_width to resize in proportion the new_height
# if you want % of resize please use it into new_width (?% * initial width)
if new_height == 0:
new_height = new_width*width/height
# split image by channels (bands) and resize by channels
img.load()
bands = img.split()
# resample mode
if resample=="NEAREST":
resample = Image.NEAREST
else:
if resample=="BILINEAR":
resample = Image.BILINEAR
else:
if resample=="BICUBIC":
resample = Image.BICUBIC
else:
if resample=="ANTIALIAS":
resample = Image.ANTIALIAS
bands = [b.resize((new_width, new_height), resample) for b in bands]
# merge the channels after individual resize
img = Image.merge('RGBA', bands)
# save the image
img.save(ResizedFile)
return
#######################################################
if __name__ == "__main__":
sFile = './autumn-png-leaf.png'
# resize using new width value (new height is calculated by keeping image aspect)
PNG_ResizeKeepTransparency(sFile, sFile[:-4]+'_resized.png', 400)
# resize using a reference file to get the new image dimension
PNG_ResizeKeepTransparency(sFile, sFile[:-4]+'_resized.png', RefFile = 'autumn-png-leaf_starry-night-van-gogh_fchollet_10.png')
可能是@MarkRansom的副本,但他的图像最初不是在RGBA中,它是一个苍白的图像,如果转换为RGBA,会使一切都出错。@mmgp,我明白了,谢谢你清理了它。我没有我通常用来评估这个位置的图像的工具,但是我想在PIL中打开它会立即显示出来。我认为这个特定的PNG与PIL完全不兼容。它有一个带有透明度的调色板,当PIL读取文件时,透明度会被丢弃。@MarkRansom确实,我也无法用PIL正确读取它。这:,是我能得到的最好的了。
ImagePalette
类已经有了一个与之相关联的模式,只要PIL使用一个合适的'RGBA'
模式,一切都会好得多。您的解决方案适用于单色图像的这种特殊情况,但有许多其他有效图像可能会失败。@MarkRansom但此透明度数据在PLTE
区块中不可用,PIL在该区块中为png图像构建调色板。因此,如果调色板仅构建为RGBA
,则它必须正确读取tRNS
块,并可能将其组合到调色板中。因此,包含的“补丁”仅用于正确读取tRNS
块,任何模式P
下的png图像都可以使用此补丁。你认为哪种png图像会失败?你的回答假设调色板是灰度调色板,但事实并非如此。转换为'RGBA'
而不是'LA'
可以解决这个问题,但结果将不再是8位。@MarkRansom但答案的这一部分与问题无关,图像可以包含一个RGB
调色板,然后从LA
切换到RGBA
就可以正常工作。你是说最新的流血枕头会使用它吗?我不知道你这里所说的“黑客”是什么意思,因为你在代码中也在做self.im\u info[“transparency\u palete”]=s
。如何正确地阅读区块是一种黑客行为?不,正如你已经注意到的,我也在使用这个想法。但是在我的实现中,我没有复制图像并在其上循环。加载时,透明度将按预期方式应用。解决方案的最底层是“黑客”。你说流血是什么意思?我指的是流血版本。你修补了chunk\tRNS
和我一样,但仅仅是5小时前,这只是巧合吗?我并不是说这在任何方面都是革命性的,但我们可以看到这是多么可悲:人A发布了一个答案,人B应用了先前答案中的补丁加上ε修改,人B不好地谈论人A,因为人A没有做ε修改。最后,我没有“正确地”修补PIL,因为它涉及修改\u imaging.c
(因此涉及重新编译),因为我所做的只是显示PIL读取png块不正确,称之为“hack”,我并没有说任何我认为不好的话。黑客真是太棒了。我用了你对块的知识,这不是巧合,我不会说我发现了这个缺陷。zuroc在枕头上发布了这个相关问题。我刚刚看了一下,很好地看了一下您的解决方案,并将其直接实现到枕头中,在那里它将被重新编译。MarkRansom还提到了解决方案中的一个小缺陷,即强制将图像转换为灰度或rgb以应用透明度。内部错误修复没有这个问题。这正是上面提到的正确的解决方案标记。