struct.unpack和struct.pack打包、解包图像和npy类型文件

一、简介

        网上未见关于struct.unpack和struct.pack对图片或者npy类型文件打包解包的教程,故搜集资料钻研一下关于这方面的用法。

        有些情况下,直接对文件夹中的大量图像等文件进行处理,可能会影响程序执行的速度。这时,如果能够先将图像等文件打包合并为一个(二进制bytes流)文件,从内存中读取bytes,一定程度上能够提高读取的速度。

        本文中对各种图片类型文件和numpy生成的存储矩阵的npy类型文件进行打包。

        我的文件目录如下安排:

test
   |--image
         |--1.jpg
         |--2.jpg
         |--...
   |--npy
         |--2887.npy
         |--2888.npy
         |--...
   |--bigdata   #存放打包后的大文件。如image.bigfile
   |--out1      #将打包后的大文件还原,此处还原结果和image文件夹内容一致
   |--out2      #将打包后的大文件还原,此处还原结果和npy文件夹内容一致
   pack.py
   unpack.py

二、打包struct.pack

打包文件夹中的文件:(如图片类型、npy类型)

如下为pack.py文件内容:

import os
import struct


# 判断文件夹中是否有目标类型图片,没有则返回0
def is_image_file(filename):
    # 如果不都为空、0、false,则any()返回true
    return any(filename.endswith(extension) for extension in IMG_EXTENSIONS)


# 创建图片数据集,存在列表中并返回
def make_dataset(dir):
    images = []
    assert os.path.isdir(dir), '%s is not a valid directory' % dir

    # os.walk(top[, topdown=True[, onerror=None[, followlinks=False]]]) 通过在目录树中游走输出在目录中的文件名,top返回三项(root,dirs,files),分别代表:
    # 当前正在遍历的这个文件夹的本身的地址;  list类型,内容是该文件夹中所有的目录的名字(不包括子目录);  list类型,内容是该文件夹中所有的文件(不包括子目录)
    for root, _, fnames in sorted(os.walk(dir)):
        for fname in fnames:
            if is_image_file(fname):
                # print(fname)
                # 拼接出图片的地址,并加入到images列表
                path = os.path.join(root, fname)
                images.append(path)

    return images


def pack(out_dir, indir, target_folders):
    # 遍历存放数据集的文件夹
    for target_folder in target_folders:
        # 拼接生成存放数据集文件夹的路径
        curr_indir = os.path.join(indir, target_folder)
        # 生成的大文件路径(含问文件名)
        curr_out_file = os.path.join(os.path.join(out_dir, '%s.bigfile' % (target_folder)))
        image_lists = make_dataset(curr_indir)
        image_lists.sort()
        with open(curr_out_file, 'wb') as wfid:
            # 写入文件数量
            wfid.write(struct.pack('i', len(image_lists)))
            for i, img_path in enumerate(image_lists):
                # 写入文件名称
                img_name = os.path.basename(img_path)
                img_name_bytes = img_name.encode('utf-8')
                wfid.write(struct.pack('i', len(img_name_bytes)))
                wfid.write(img_name_bytes)

                # 写入图片数据
                with open(img_path, 'rb') as img_fid:
                    img_bytes = img_fid.read()
                wfid.write(struct.pack('i', len(img_bytes)))
                wfid.write(img_bytes)

                if i % 1 == 0:
                    print('write %d files done' % i)


if __name__ == '__main__':
    IMG_EXTENSIONS = [
        '.jpg', '.JPG', '.jpeg', '.JPEG',
        '.png', '.PNG', '.ppm', '.PPM', '.bmp', '.BMP', '.npy'
    ]
    #打包结果存储位置
    out_dir = 'C:/Users/Administrator/Desktop/test/bigdata'
    #待打包文件的文件夹路径
    indir = 'C:/Users/Administrator/Desktop/test'
    #存储待打包文件的文件夹名字
    target_folders = ['data', 'npy']

    pack(out_dir, indir, target_folders)

打包完成之后会在bigdata文件夹下生成两个文件:image.bigfile 和 npy.bigfile

struct.unpack和struct.pack打包、解包图像和npy类型文件_第1张图片

二、解包struct.unpack

         现在我们只需要对data.bigfile 和 npy.bigfile两个文件进行操作即可。使用struct.unpack能够将这个大文件还原为原始的照片和npy类型文件。但是需要注意的是:还原不同文件需要使用不同的函数载入io.BytesIO读取的二进制流:

类型为图片时我们使用 Image.open(io.BytesIO(img_bytes[]))

类型为npy时我们使用np.load(io.BytesIO(img_bytes[]))

如下为unpack.py文件内容:

# -*- coding:utf-8 -*-
import io
import struct
import os
from PIL import Image
import numpy as np

def unpack(file_path,save_path,flag=1):
    print('start load bigfile (%0.02f GB) into memory' % (os.path.getsize(file_path) / 1024 / 1024 / 1024))
    with open(file_path, 'rb') as fid:
        img_num = struct.unpack('i', fid.read(4))[0]
        img_names = []
        img_bytes = []
        print('find total %d images' % img_num)
        for i in range(img_num):
            img_name_len = struct.unpack('i', fid.read(4))[0]
            img_name = fid.read(img_name_len).decode('utf-8')
            img_names.append(img_name)
            img_bytes_len = struct.unpack('i', fid.read(4))[0]
            img_bytes.append(fid.read(img_bytes_len))
            if i % 5000 == 0:
                print('load %d images done' % i)
        print('load all %d images done' % img_num)

    # 返回图片名字和图片
    for index in range(0, len(img_names)):
        try:
            if flag == 1: #解包图片类型
                img = Image.open(io.BytesIO(img_bytes[index])).convert('RGB')
                path_img = os.path.join(save_path,img_names[index])
                img.save(path_img)
            elif flag == 0: #解包npy类型
                npy= np.load(io.BytesIO(img_bytes[index]))
                path_npy = os.path.join(save_path,img_names[index])
                np.save(path_npy,npy)
        except Exception:
            print('file read error for index %d: %s' % (index, img_names[index]))


if __name__ == '__main__':
    #打包图片类型后的文件路径
    filepath1 = 'C:/Users/Administrator/Desktop/test/bigdata/image.bigfile'
    #解包后图片存储地址
    save_path1 = 'C:/Users/Administrator/Desktop/test/out1'

    #打包npy类型后的文件路径
    filepath2 = 'C:/Users/Administrator/Desktop/test/bigdata/npy.bigfile'
    #解包后npy存储地址
    save_path2 = 'C:/Users/Administrator/Desktop/test/out2'

    ##解包图片文件
    #unpack(filepath1,save_path1,flag=1)

    #解包npy矩阵文件
    unpack(filepath2,save_path2,flag=0)

解包后的结果会存储在save_path1 或者save_path2目录之下,也就是之前打包的文件被还原了。

如下

struct.unpack和struct.pack打包、解包图像和npy类型文件_第2张图片

原创文章,请勿转载https://blog.csdn.net/Crystal_remember/article/details/119742405

你可能感兴趣的:(python,python)