【问题标题】:Resizing png image with PIL loses transparency使用 PIL 调整 png 图像的大小会失去透明度
【发布时间】:2018-01-12 01:03:04
【问题描述】:

我使用这种方法来调整 png 图片的大小:

Method for converting PNGs to premultiplied alpha

但是这张图片还是会失去透明度:

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

来自

【问题讨论】:

  • @MarkRansom 但他的图像最初不是 RGBA,它是一个调色板图像,如果转换为 RGBA 会出错。
  • @mmgp,我明白了,感谢您澄清这一点。我没有通常用来评估该位置图像的工具,但我想在 PIL 中打开它会立即显示出来。
  • 我认为这个特殊的 PNG 与 PIL 完全不兼容。它有一个带透明度的调色板,当 PIL 读取文件时,透明度会被丢弃。
  • @MarkRansom 确实,我也无法使用 PIL 正确阅读它。这个:i.imgur.com/8eKkMdh.png,是我能得到的最好的。

标签: python python-imaging-library


【解决方案1】:

这是一个更复杂的调整大小(保持透明度)的函数:它允许您只使用新的宽度值、新的宽度和高度值或参考文件来获得新的大小。您也可以更改重采样方法:

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

【讨论】:

    【解决方案2】:

    该问题与链接的问题无关,而是您必须修补 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_tRNS,输入检查im_mode == "P"if 语句以及随后检查i >= 0,然后添加行self.im_info["actual_transparency"] = map(ord, s)

    【讨论】:

    • ImagePalette 类已经有一个与之关联的mode,如果只有 PIL 使用正确的'RGBA' 模式,一切都会好得多。您的解决方案适用于单色图像的这种特殊情况,但还有许多其他有效图像会失败。
    • @MarkRansom 但此透明度数据在 PLTE 块中不可用——PIL 为 png 图像构建调色板。因此,调色板是否简单地构建为RGBA 并不重要,它必须正确读取tRNS 块并可能将其组合到调色板中。所以包含的“补丁”只是为了正确读取tRNS块,任何依赖它的P模式下的png图像都可以与这个补丁一起使用。你想到什么样的 png 图像会失败?
    • 您的回答假设调色板是灰度调色板,但不一定如此。转换为 'RGBA' 而不是 'LA' 可以解决这个问题,但结果将不再是 8 位。
    • @MarkRansom 但那部分答案与问题无关,图像可能包含RGB 调色板,然后从LA 切换到RGBA 就可以正常工作。
    【解决方案3】:

    我已经解决了这个问题,不再需要来自 mmgp 的漂亮 hack。 PIL 现在将正确读取和应用透明度。

    https://github.com/d-schmidt/Pillow/commit/5baa1ac1b8d41fcedce7b12ed1c4a8e87b4851bc

    【讨论】:

    • 你的意思是最新的流血枕头会适用吗?我不确定您在这里所说的“hack”是什么意思,因为您也在该代码中执行self.im_info["transparency_palette"] = s。如何正确读取块?
    • 不,正如你已经注意到的,我也在使用这个想法。但在我的实现中,我没有复制图像并循环它。在加载透明度时,它会按照预期的方式应用。您的解决方案的底部是“黑客”。流血是什么意思?
    • 我的意思是最前沿的版本。您像我一样修补chunk_tRNS 只是巧合,但只是在 5 小时前?我并不是说这是革命性的,但我们可以看到这是多么可悲:A 人发布了一个答案,B 人应用了之前答案中的补丁加上一个 epsilon 修改,B 人不好谈论 A 人,因为+ 人 A 没有做的 epsilon 修改。最后,我没有费心“正确”修补 PIL,因为它涉及修改 _imaging.c(因此涉及重新编译),因为我所做的只是显示 PIL 错误地读取了该 png 块
    • 哦,称其为“黑客”,在我看来,我并没有说任何坏话。黑客很棒。
    • 我使用了你对块的知识,这不是巧合,我不会声称我发现了这个缺陷。 zuroc 将此链接的问题作为问题发布在枕头上。我刚刚看了一下,好好看看你的解决方案,并直接将它实现到 Pillow 中,在那里它将被重新编译。 MarkRansom 还提到了您的解决方案中的小缺陷,它强制图像变为灰度或 rgb 以应用透明度。内部错误修正没有这个问题。这只是上面提到的正确解决方案。
    猜你喜欢
    • 2010-10-19
    • 2012-09-15
    • 2011-03-13
    • 1970-01-01
    • 2013-01-02
    • 2014-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多