How to convert this indexed PNG to grayscale and keep transparency, using Python and Pillow?

风格不统一 提交于 2019-12-21 22:47:21

问题


I am trying to convert images to grayscale using Python/Pillow. I had no difficulty in most images, but then, while testing with different images, I found this logo from the BeeWare project, that I know that has been further edited with some image editor and recompressed using ImageOptim.

The image has some kind of transparency (in the whole white area around the bee), but black color gets messed up. Here is the code:

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

I also tried this:

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)

Using either approach, all the black in the image becomes transparent.

I am trying to understand what I am missing here, or if this is some bug or limitation in Pillow. Digging a little through the image palette, I would say that transparency is in fact assigned to the black color in the palette. For instance, if I convert it to RGBA mode, the outside becomes black. So there must be something else that makes the outside area transparent.

Any tips?


回答1:


Digging a little through the image palette, I would say that transparency is in fact assigned to the black color in the palette.

pngcheck tells me that is not the case:

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

Each actual color has an index in PLTE, including black, and there is an additional entry that is designated "transparent". The black surroundings are probably an artefact of one of the previous conversions, where alpha=0 got translated to RGBA (0,0,0,0).

It seems Pillow's immediate conversion to Lab ("L" and "LA") cannot handle indexed color conversions.
You can solve this by converting the image to RGBA first, then converting each pixel quadruplet of RGBA to gray using the Lab conversion formula from the documentation, and then converting it back to palettized:

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])

but then I realized that since you start out with a palettized image and want to end up with one again, converting only the palette is much easier ...

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

(Hardcoded to assume your input image is indeed an indexed file. Add checks if you want to make sure.)

Result, wrapped inside a comment block so you can see the transparency:



来源:https://stackoverflow.com/questions/51445513/how-to-convert-this-indexed-png-to-grayscale-and-keep-transparency-using-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!