使用PIL加载RGBA位图

8
我尝试使用PIL将32位位图转换为32位PNG。
from PIL import Image
im = Image.open('example.bmp')
print im.mode
# it prints 'RGB', but expected was 'RGBA'
im.save('output.png', format='PNG')

期望的图像模式是'RGBA',但实际上我得到了'RGB'。

我也尝试了下面的代码,但它并没有起作用。

from PIL import Image
im = Image.open('example.bmp')
im = im.convert('RGBA')
im.save('output.png', format='PNG')

1
我认为32位图像文件是一种非标准格式,PIL不支持。尝试将RGBA图像写入.BMP会产生错误“无法将RGBA模式写入BMP”。 - Mark Ransom
3个回答

5

好的,让我们开始吧。由于我不知道你的BMP文件的具体格式是什么,所以我只处理了一个我恰巧拥有的带有完整Alpha通道的BMP特定情况。我在这里处理的BMP类型可以通过使用ImageMagick将具有Alpha通道的PNG转换为BMP而获得。这将创建所谓的“BITMAPV5”。根据您的描述,您没有BitmapV5(因为PIL甚至无法打开它),因此我们需要进行讨论的迭代来解决您的特定情况。

因此,您需要一个新的文件解码器或已修补的BmpImagePlugin.py。如何执行前者在PIL的手册中有描述。对于后者,您显然需要发送一个补丁,并希望将其纳入下一个PIL版本。我专注于创建一个新的解码器:

from PIL import ImageFile, BmpImagePlugin

_i16, _i32 = BmpImagePlugin.i16, BmpImagePlugin.i32

class BmpAlphaImageFile(ImageFile.ImageFile):
    format = "BMP+Alpha"
    format_description = "BMP with full alpha channel"

    def _open(self):
        s = self.fp.read(14)
        if s[:2] != 'BM':
            raise SyntaxError("Not a BMP file")
        offset = _i32(s[10:])

        self._read_bitmap(offset)

    def _read_bitmap(self, offset):

        s = self.fp.read(4)
        s += ImageFile._safe_read(self.fp, _i32(s) - 4)

        if len(s) not in (40, 108, 124):
            # Only accept BMP v3, v4, and v5.
            raise IOError("Unsupported BMP header type (%d)" % len(s))

        bpp = _i16(s[14:])
        if bpp != 32:
            # Only accept BMP with alpha.
            raise IOError("Unsupported BMP pixel depth (%d)" % bpp)

        compression = _i32(s[16:])
        if compression == 3:
            # BI_BITFIELDS compression
            mask = (_i32(self.fp.read(4)), _i32(self.fp.read(4)),
                    _i32(self.fp.read(4)), _i32(self.fp.read(4)))
            # XXX Handle mask.
        elif compression != 0:
            # Only accept uncompressed BMP.
            raise IOError("Unsupported BMP compression (%d)" % compression)

        self.mode, rawmode = 'RGBA', 'BGRA'

        self.size = (_i32(s[4:]), _i32(s[8:]))
        direction = -1
        if s[11] == '\xff':
            # upside-down storage
            self.size = self.size[0], 2**32 - self.size[1]
            direction = 0

        self.info["compression"] = compression

        # data descriptor
        self.tile = [("raw", (0, 0) + self.size, offset,
            (rawmode, 0, direction))]

要正确使用它,据说规范的方法是执行以下操作:

from PIL import Image
Image.register_open(BmpAlphaImageFile.format, BmpAlphaImageFile)
# XXX register_save

Image.register_extension(BmpAlphaImageFile.format, ".bmp")

问题在于已经有一个处理".bmp"的插件了,而我没有找到如何在BmpImagePlugin被使用之前将这个新扩展名添加到插件中(我也不知道在PIL中是否可能这样做)。话虽如此,我实际上直接使用了以下代码:

from BmpAlphaImagePlugin import BmpAlphaImageFile

x = BmpAlphaImageFile('gearscolor.bmp')
print x.mode
x.save('abc1.png')

其中 gearscolor.bmp 是一个具有完整 alpha 通道的示例位图,如前所述。生成的 png 文件将保存 alpha 数据。如果您检查 BmpImagePlugin.py 的代码,您会注意到我重用了它的大部分代码。


我的位图文件出现了错误“不支持的BMP头类型(40)”。 - stanleyxu2005
我本来就期待着这个。你能把你的确切bmp文件上传到某个地方吗? - mmgp
看看更新后的代码,我几乎不需要改动任何东西就能处理你的文件格式。 - mmgp

1
@mmgp的代码非常适合加载具有Alpha通道的BMP文件,但在Python 3上需要进行两个小更改:
if s[:2] != 'BM':

成为:

if s[:2] != b'BM':

并且需要更改使用self.size的地方,所以代码的结尾变成:

self._size = (_i32(s[4:]), _i32(s[8:]))
direction = -1
if s[11] == '\xff':
    # upside-down storage
    self._size = self._size[0], 2**32 - self._size[1]
    direction = 0

self.info["compression"] = compression

# data descriptor
self.tile = [("raw", (0, 0) + self._size, offset,
    (rawmode, 0, direction))]

此外,有时会失败-因为一些BMP格式不正确。您可以预先选择:
if typename != "alpha":
    img = Image.open(filename)
else:
    img = BmpAlphaImageFile(filename)

或者使用try/catch块作为备用方案:
try:
    img = BmpAlphaImageFile(filename)
except IOError:
    img = Image.open(filename)

0

PIL存在缺陷,无法正确处理透明的BMP文件。

如果我没记错,wxPython似乎可以正确处理它们。大约一年前,我写了一个小的包装器在两者之间,只要我能找到代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接