【问题标题】:How to convert this indexed PNG to grayscale and keep transparency, using Python and Pillow?如何使用 Python 和 Pillow 将此索引 PNG 转换为灰度并保持透明度?
【发布时间】:2018-12-28 22:35:24
【问题描述】:

我正在尝试使用 Python/Pillow 将图像转换为灰度。我在大多数图像上都没有遇到任何困难,但是在测试不同的图像时,我从 BeeWare 项目中找到了这个标志,我知道它已经用一些图像编辑器进一步编辑并使用 ImageOptim 重新压缩。

图像具有某种透明度(在蜜蜂周围的整个白色区域中),但黑色会变得混乱。代码如下:

#/usr/bin/env python3

import os
from PIL import Image, ImageFile

src_path = os.path.expanduser("~/Desktop/prob.png")
img = Image.open(src_path)
folder, filename = os.path.split(src_path)
temp_file_path = os.path.join(folder + "/~temp~" + filename)


if 'transparency' in img.info:
    transparency = img.info['transparency']
else:
    transparency = None

if img.mode == "P":
    img = img.convert("LA").convert("P")

try:
    img.save(temp_file_path, optimize=True, format="PNG", transparency=transparency)
except IOError:
    ImageFile.MAXBLOCK = img.size[0] * img.size[1]
    img.save(temp_file_path, optimize=True, format="PNG", transparency=transparency)

我也试过这个:

png_info = img.info

if img.mode == "P":
    img = img.convert("LA").convert("P")

try:
    img.save(temp_file_path, optimize=True, format="PNG", **png_info)
except IOError:
    ImageFile.MAXBLOCK = img.size[0] * img.size[1]
    img.save(temp_file_path, optimize=True, format="PNG", **png_info)

使用任何一种方法,图像中的所有黑色都变得透明。

我试图了解我在这里缺少什么,或者这是否是 Pillow 中的一些错误或限制。通过图像调色板挖掘一点,我会说透明度实际上分配给调色板中的黑色。例如,如果我将其转换为 RGBA 模式,则外部变为黑色。所以一定有别的东西可以让外面的区域透明。

有什么建议吗?

【问题讨论】:

    标签: python-3.x png transparency pillow alpha-transparency


    【解决方案1】:

    稍微研究一下图像调色板,我会说透明度实际上是分配给调色板中的黑色的。

    pngcheck 告诉我情况并非如此:

    ...
    chunk PLTE at offset 0x00025, length 48: 16 palette entries
    chunk tRNS at offset 0x00061, length 1: 1 transparency entry
    

    每种实际颜色在PLTE中都有一个索引,包括黑色,还有一个额外的条目被指定为“透明”。黑色环境可能是先前转换之一的产物,其中 alpha=0 被转换为 RGBA (0,0,0,0)。

    Pillow 立即转换为 Lab(“L”和“LA”)似乎无法处理索引颜色转换。
    您可以通过先将图像转换为 RGBA,然后使用来自the documentation 的 Lab 转换公式将 RGBA 的每个像素四元组转换为灰色,然后将其转换回调色板来解决此问题:

    for i in range(img.size[0]): # for every pixel:
        for j in range(img.size[1]):
            g = (pixels[i,j][0]*299 + pixels[i,j][1]*587 + pixels[i,j][2]*114)//1000
            pixels[i,j] = (g,g,g,pixels[i,j][3])
    

    但后来我意识到,由于您从一个调色板开始并希望再次以一个结束,只转换调色板要容易得多...

    #/usr/bin/env python3
    
    from PIL import Image, ImageFile
    
    img = Image.open('bee.png')
    
    palette = img.getpalette()
    for i in range(len(palette)//3):
        gray = (palette[3*i]*299 + palette[3*i+1]*587 + palette[3*i+2]*114)//1000
        palette[3*i:3*i+3] = [gray,gray,gray]
    
    img.putpalette(palette)
    img.save('bee2a.png', optimize=True, format="PNG")
    
    print ('done')
    

    (硬编码假设您的输入图像确实是一个索引文件。如果您想确定,请添加检查。)

    结果,包裹在注释块中,以便您可以看到透明度:

    【讨论】:

    • 是的,它按预期工作。不过,我有一个问题。难道我们不能最终得到一个比必要的更大的调色板,并带有重复的颜色吗?
    • @VictorDomingos:当使用调色板改变时?它是高效的,因为它不需要接触原始像素数据;如果你改变调色板,你也必须改变像素。我的第一次尝试确实 改变了 RGB 数据并将转换为索引颜色再次转换为 Pillow。这也可以优化调色板,因此您可以使用效率较低的方法。
    【解决方案2】:

    找不到让我的 .png “索引”或使用调色板的方法。最终的结果是:

    • 保存 Alpha 通道
    • 转换为“L”,然后转换为“RGB”
    • 重新应用 Alpha 通道(又称透明度)
        img_colored = Image.open("transparency.png")
        img_colored.load()
        alpha = img_colored.split()[-1]
        img_grey = img_colored.convert("L").convert("RGB")
        img_grey.putalpha(alpha)
    

    可能效率不高,但可以完成工作

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-24
      • 2011-01-25
      • 2021-12-06
      • 2018-12-01
      • 2011-05-06
      • 2012-03-29
      • 1970-01-01
      • 2017-12-27
      相关资源
      最近更新 更多