我在Python中有一张二进制图像,我想将其保存在我的电脑上。 存储在计算机中后,我需要它成为1位深的PNG图像。 我该怎么做? 我尝试过PIL和cv2,但是我无法使用1位深度保存它。
我在Python中有一张二进制图像,我想将其保存在我的电脑上。 存储在计算机中后,我需要它成为1位深的PNG图像。 我该怎么做? 我尝试过PIL和cv2,但是我无法使用1位深度保存它。
我发现自己处于需要创建许多二进制图像的情况中,并对网上提供的信息感到沮丧。幸运的是,得益于在这里和其他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')
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
在处理速度和文件大小方面显然是最好的。
我肯定错过了一些优化,欢迎评论!
使用:
cv2.imwrite(<image_name>, img, [cv2.IMWRITE_PNG_BILEVEL, 1])
这仍然会使用压缩,因此实际上每个像素的位数很可能少于1位。
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。等等。