【问题标题】:Load RGBA bitmap with PIL使用 PIL 加载 RGBA 位图
【发布时间】:2012-05-14 06:43:08
【问题描述】:

我尝试使用 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')

【问题讨论】:

  • 我认为 32 位位图文件是 PIL 不支持的非标准格式。尝试将 RGBA 图像写入 .bmp 会产生错误 cannot write mode RGBA as BMP

标签: python png python-imaging-library bmp rgba


【解决方案1】:

好的,现在开始吧。由于我不知道具体哪种格式是您的 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)”
  • 我早就料到了。你能把你的确切 bmp 上传到某个地方吗?
  • 查看更新后的代码,我几乎不需要更改任何内容来处理您的文件格式。
【解决方案2】:

PIL 有问题,无法正常处理透明 BMP 文件。

如果我没记错的话,wxPython 似乎可以正常使用它们。大约一年前,我在两者之间写了一个小包装,前提是我能找到代码。

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 2012-09-09
      • 1970-01-01
      • 2020-05-07
      • 1970-01-01
      • 2012-02-28
      • 1970-01-01
      • 2021-04-28
      • 2014-10-07
      • 2016-08-26
      相关资源
      最近更新 更多