我在Python中有一个二进制图像,我想将它保存在我的电脑中。 一旦存储在我的计算机中,我需要它是一个1位深的png图像。 我怎样才能做到这一点?我尝试使用PIL和cv2,但我无法以1位深度保存它。
答案 0 :(得分:3)
我发现自己处于需要创建大量二进制映像的情况,并且对在线可用信息感到沮丧。由于这里和其他地方的答案和评论,我找到了可以接受的解决方案。 @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())
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
。尽管进行了png压缩,但以下命令占用0.4ms的时间,导致文件大小为2566字节。
_ = cv2.imwrite('bw_cv2.png', check.astype(int), [cv2.IMWRITE_PNG_BILEVEL, 1])
PIL
显然是速度和文件大小最好的。
我当然错过了一些优化,欢迎发表评论!
答案 1 :(得分:1)
使用:
cv2.imwrite(<image_name>, img, [cv2.IMWRITE_PNG_BILEVEL, 1])
(这仍将使用压缩,因此在实践中它很可能每像素少于1位)
答案 2 :(得分:0)
如果您不加载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/(麻省理工学院)
通过阅读png规范: https://www.w3.org/TR/PNG-Chunks.html
请记住,buf的1位数据应像png规范在正常非隔行扫描模式(我们声明的那样)中那样从左向右写入。多余的数据填充最后一位(如果存在),并且步幅是编码扫描线所需的字节数。此外,如果您希望这些1位具有调色板颜色,则必须编写一个PLTE块并将其类型切换为3而不是0。