目录
- 一、LMDB文件
- 二、准备训练集
- 三、安装basicsr包
- 四、LMDB文件制作
- 4.1 参数修改
- 4.2 其它格式图片修改
- 4.3 代码
- 4.4 转换结果
- 4.4.1 data.mdb文件
- 4.4.2 lock.mdb文件
- 4.4.3 meta_info.txt文件
- 五、总结
一、LMDB文件
在训练的时候使用LMDB 存储形式可以加快IO 和CPU 解压缩的速度(测试的时候数据较少, 一般就没有太必要使用LMDB)。其具体的加速要 根据机器的配置来,以下几个因素会影响:
-
有的机器设置了定时清理缓存,而LMDB 依赖于缓存。因此若一直缓存不进去,则需要检查一下。一般free -h 命令下, LMDB 占用的缓存会记录在buff/cache 条目下面。
-
机器的内存是否足够大,能够把整个LMDB 数据都放进去。如果不是,则它由于需要不断更换缓存,会影响速度。
-
若是第一次缓存LMDB 数据集,可能会影响训练速度。可以在训练前,进入LMDB 数据集
目录,把数据先缓存进去:cat data.mdb > /dev/null
二、准备训练集
训练集的种类及格式是多样的,这里主要以.png格式的图片进行讲解。如下:
三、安装basicsr包
进入终端后输入下面命令安装basicsr包:
pip install basicsr -i https://pypi.mirrors.ustc.edu.cn/simple/
四、LMDB文件制作
4.1 参数修改
4.2 其它格式图片修改
下面是数据集中,图片后缀为其它格式时,怎么修改代码,这里以.bmp格式图片为例:
4.3 代码
转换的详细代码见下:
import cv2
from basicsr.utils import scandir
import lmdb
import sys
from multiprocessing import Pool
from os import path as osp
from tqdm import tqdmdef create_lmdb_for_div2k():folder_path = 'Images/LMDB/Visible_Images'lmdb_path = 'Images/LMDB/Visible_Images.lmdb'img_path_list, keys = prepare_keys_div2k(folder_path)make_lmdb_from_imgs_lmdb(folder_path, lmdb_path, img_path_list, keys)def prepare_keys_div2k(folder_path):print('Reading image path list ...')img_path_list = sorted(list(scandir(folder_path, suffix='png', recursive=False))) # 获取文件夹下所有后缀为 png 的文件路径,但不进行递归搜索keys = [img_path.split('.png')[0] for img_path in sorted(img_path_list)] # 从图像路径中分离出键,去掉 '.png' 后缀并排序return img_path_list, keysdef prepare_keys_div2k_bmp(folder_path):print('Reading image path list ...')img_path_list = sorted(list(scandir(folder_path, suffix='bmp', recursive=False)))keys = [img_path.split('.bmp')[0] for img_path in sorted(img_path_list)]return img_path_list, keysdef read_img_worker(path, key, compress_level):img = cv2.imread(path, cv2.IMREAD_UNCHANGED)if img.ndim == 2:h, w = img.shapec = 1else:h, w, c = img.shape_, img_byte = cv2.imencode('.png', img, [cv2.IMWRITE_PNG_COMPRESSION, compress_level])return (key, img_byte, (h, w, c))def make_lmdb_from_imgs_lmdb(data_path,lmdb_path,img_path_list,keys,batch=5000,compress_level=1,multiprocessing_read=False,n_thread=40,map_size=None):assert len(img_path_list) == len(keys), ('img_path_list and keys should have the same length, 'f'but got {len(img_path_list)} and {len(keys)}')print(f'Create lmdb for {data_path}, save to {lmdb_path}...')print(f'Totoal images: {len(img_path_list)}')if not lmdb_path.endswith('.lmdb'):raise ValueError("lmdb_path must end with '.lmdb'.")if osp.exists(lmdb_path):print(f'Folder {lmdb_path} already exists. Exit.')sys.exit(1)if multiprocessing_read:# read all the images to memory (multiprocessing)dataset = {} # use dict to keep the order for multiprocessingshapes = {}print(f'Read images with multiprocessing, #thread: {n_thread} ...')pbar = tqdm(total=len(img_path_list), unit='image')def callback(arg):"""get the image data and update pbar."""key, dataset[key], shapes[key] = argpbar.update(1)pbar.set_description(f'Read {key}')pool = Pool(n_thread)for path, key in zip(img_path_list, keys):pool.apply_async(read_img_worker, args=(osp.join(data_path, path), key, compress_level), callback=callback)pool.close()pool.join()pbar.close()print(f'Finish reading {len(img_path_list)} images.')# create lmdb environmentif map_size is None:# obtain data size for one imageimg = cv2.imread(osp.join(data_path, img_path_list[0]), cv2.IMREAD_UNCHANGED)_, img_byte = cv2.imencode('.png', img, [cv2.IMWRITE_PNG_COMPRESSION, compress_level])data_size_per_img = img_byte.nbytesprint('Data size per image is: ', data_size_per_img)data_size = data_size_per_img * len(img_path_list)map_size = data_size * 10env = lmdb.open(lmdb_path, map_size=map_size)# write data to lmdbpbar = tqdm(total=len(img_path_list), unit='chunk')txn = env.begin(write=True)txt_file = open(osp.join(lmdb_path, 'meta_info.txt'), 'w')for idx, (path, key) in enumerate(zip(img_path_list, keys)):pbar.update(1)pbar.set_description(f'Write {key}')key_byte = key.encode('ascii')if multiprocessing_read:img_byte = dataset[key]h, w, c = shapes[key]else:_, img_byte, img_shape = read_img_worker(osp.join(data_path, path), key, compress_level)h, w, c = img_shapetxn.put(key_byte, img_byte)# write meta informationtxt_file.write(f'{key}.png ({h},{w},{c}) {compress_level}\n')if idx % batch == 0:txn.commit()txn = env.begin(write=True)pbar.close()txn.commit()env.close()txt_file.close()print('\nFinish writing lmdb.')if __name__ == '__main__':create_lmdb_for_div2k()
4.4 转换结果
运行上面脚本,输出如下:
最终的LMDB文件在代码中设置的路径下:
生成的各个文件解析加下。
4.4.1 data.mdb文件
存储数据库中的所有实际数据,包括键值对、元数据等。
是一个二进制文件,可以直接使用内存映射的方式进行读写,访问速度非常快。
文件大小取决于数据库中存储的数据量。
4.4.2 lock.mdb文件
一个用于控制数据库访问的锁文件。
确保同一时间只有一个进程可以对数据库进行读写操作,防止数据损坏。
文件大小很小,通常只有几百字节。
4.4.3 meta_info.txt文件
采用txt 来记录,是为了可读性,文件中内容如下:
上面每一行记录了一张图片,有三个字段,分别表示:
-
图像名称(带后缀): 0001.png
-
图像大小:(1404,2040,3) 表示是1404 × 2040 × 3的图像
-
其他参数(BasicSR 里面使用了cv2 压缩png 程度): 因为在复原任务中,通常使用png 来存储, 所以这个1 表示png 的压缩程度, 也就是CV_IMWRITE_PNG_COMPRESSION 为1。CV_IMWRITE_PNG_COMPRESSION可以取值为[0, 9] 的整数,更大的值表示更强的压缩,即更小的储存空间和更长的压缩时间。
五、总结
以上就是深度学习训练前标准的LMDB文件(data.mdb和lock.mdb)制作过程,希望能帮我你,有问题欢迎留言。
感谢您阅读到最后!关注公众号「视觉研坊」,获取干货教程、实战案例、技术解答、行业资讯!