在Python中保存1位深度的二进制图像

11

我在Python中有一张二进制图像,我想将其保存在我的电脑上。 存储在计算机中后,我需要它成为1位深的PNG图像。 我该怎么做? 我尝试过PIL和cv2,但是我无法使用1位深度保存它。

3个回答

8

我发现自己处于需要创建许多二进制图像的情况中,并对网上提供的信息感到沮丧。幸运的是,得益于在这里和其他SO网站上的答案和评论,我能够找到一个可接受的解决方案。@Jimbo的评论到目前为止是最好的。下面是一些代码,用于重新生成我在python中保存二进制图像的一些方式的探索:

加载库和数据:

from skimage import data, io, util #'0.16.2'
import matplotlib.pyplot as plt #'3.0.3'
import PIL #'6.2.1'
import cv2 #'4.1.1'
check = util.img_as_bool(data.checkerboard())
< p >来自skimage的棋盘图像尺寸为200x200。没有压缩,作为1比特图像,它应该由(200*200/8)5000字节表示。

要使用skimage保存,请注意包会抱怨数据不是uint,因此需要进行转换。保存图像平均需要2.8毫秒,文件大小为408字节。

io.imsave('bw_skimage.png',util.img_as_uint(check),plugin='pil',optimize=True,bits=1)

使用matplotlib,文件大小为4.2毫秒和693字节。

plt.imsave('bw_mpl.png',check,cmap='gray')

使用PIL,0.5毫秒和164字节的文件大小。
img = PIL.Image.fromarray(check)
img.save('bw_pil.png',bits=1,optimize=True)

使用cv2时,还会对bool类型的输入发出警告。下面的命令需要0.4毫秒,并且尽管使用了PNG压缩,但仍然产生了2566字节的文件大小...
_ = cv2.imwrite('bw_cv2.png', check.astype(int), [cv2.IMWRITE_PNG_BILEVEL, 1])

PIL在处理速度和文件大小方面显然是最好的。

我肯定错过了一些优化,欢迎评论!


这正是我想要的。谢谢 :) - Masoud Maleki
检查对象是什么?它是一个类似于 [True, False, True, False] 的布尔值的 numpy 数组吗?看起来 PIL 期望 uint8。 - Leandro David

4

使用:

cv2.imwrite(<image_name>, img, [cv2.IMWRITE_PNG_BILEVEL, 1])

这仍然会使用压缩,因此实际上每个像素的位数很可能少于1位。


3
我使用PIL.Image中的语句 "img.save(filename, bit=1)" 解决了它。 - Jimbo
我发现为了使这个工作起来,与之前使用PIL的情况不同,我们需要使用PNG扩展名进行保存。 - Daniel Möller

2
如果你不加载png或其他格式,那么它的行为就很合理,可以直接编写代码。这样你的代码就不需要PIL或各种导入和导入等头疼的问题。
import struct
import zlib
from math import ceil


def write_png_1bit(buf, width, height, stride=None):
    if stride is None:
        stride = int(ceil(width / 8))
    raw_data = b"".join(
        b'\x00' + buf[span:span + stride] for span in range(0, (height - 1) * stride, stride))

    def png_pack(png_tag, data):
        chunk_head = png_tag + data
        return struct.pack("!I", len(data)) + chunk_head + struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head))

    return b"".join([
        b'\x89PNG\r\n\x1a\n',
        png_pack(b'IHDR', struct.pack("!2I5B", width, height, 1, 0, 0, 0, 0)),
        png_pack(b'IDAT', zlib.compress(raw_data, 9)),
        png_pack(b'IEND', b'')])

参考来源: http://code.activestate.com/recipes/577443-write-a-png-image-in-native-python/ (MIT)

通过阅读png规范: https://www.w3.org/TR/PNG-Chunks.html

需要注意的是来自buf的1位数据,应该像png规范中正常的非交错模式(我们声明为这种模式)所需的左到右写入。如果存在多余的数据,则用其填充最后一位。stride是编码扫描线所需的字节数量。此外,如果您想让这些1位具有调色板颜色,您将需要编写一个PLTE块并将类型从0切换为3。等等。


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