Mythsman


乐极生悲,苦尽甘来。


利用Caffe与lmdb读写图像数据

由于有关caffe的开发资料实在太少,单单是这个问题就困扰了我半天。最后终于找到了一个大腿----beenfrog,也是一个正在学习caffe框架的研究人员。博客mark下,以后有问题可以去这里找。

本文代码部分主要参考于此文

简述

lmdb是一种轻量级的数据库,caffe中主要就是使用lmdb模块来进行图像数据集的保存。据说是因为lmdb有读取速度快,支持多线程、多进程并发,等这样那样的优点(具体见官网,虽然我暂时没有看出来,据我所知网上查找lmdb文档的人大都仅仅是为了使用caffe的),注意到这个数据库其实并没有任何压缩处理的作用,他的目的只是为了快速的索引和存取。他的数据都会带着一定的数据结构从而使的体积略微增大。

事实上如果仅仅看lmdb的用法是无法直接应用于图像文件的处理的。由于caffe是将图像以他自带的数据类型的形式传入lmdb中的,因此我们必须结合caffe的数据类型才能完成读取和使用。

lmdb的安装

参考官方文档,安装如下依赖即可:

udo apt-get install cython
sudo apt-get install libffi-dev python-dev build-essential
sudo apt-get install python-cffi
sudo easy_install lmdb

实践再次映证了用easy_install 安装要比pip好的多(在版本跟的上的情况下)。

生成数据文件

这里以zhengfang/文件夹下的1.png至1000.png为例

#coding:utf-8
import lmdb
import numpy as np
import cv2
import caffe
from caffe.proto import caffe_pb2

#basic setting
lmdb_file = 'lmdb_data'#期望生成的数据文件
batch_size = 200       #lmdb对于数据进行的是先缓存后一次性写入从而提高效率,因此定义一个batch_size控制每次写入的量。

# create the leveldb file
lmdb_env = lmdb.open(lmdb_file, map_size=int(1e12))#生成一个数据文件,定义最大空间
lmdb_txn = lmdb_env.begin(write=True)              #打开数据库的句柄
datum = caffe_pb2.Datum()                          #这是caffe中定义数据的重要类型

for x in range(1000):
    x+=1
    img=cv2.imread('zhengfang/'+str(x)+'.png').convert('RGB')     #从zhengfang/文件夹中依次读取图像

    # save in datum
    data = img.astype('int').transpose(2,0,1)      #图像矩阵,注意需要调节维度
    #data = np.array([img.convert('L').astype('int')]) #或者这样增加维度
    label = x                                      #图像的标签,为了方便存储,这个必须是整数。
    datum = caffe.io.array_to_datum(data, label)   #将数据以及标签整合为一个数据项

    keystr = '{:0>8d}'.format(x-1)                 #lmdb的每一个数据都是由键值对构成的,因此生成一个用递增顺序排列的定长唯一的key
    lmdb_txn.put( keystr, datum.SerializeToString())#调用句柄,写入内存。

    # write batch
    if x % batch_size == 0:                        #每当累计到一定的数据量,便用commit方法写入硬盘。
        lmdb_txn.commit()
        lmdb_txn = lmdb_env.begin(write=True)      #commit之后,之前的txn就不能用了,必须重新开一个。
        print 'batch {} writen'.format(x)

lmdb_env.close()                                   #结束后记住释放资源,否则下次用的时候打不开。。。

输出:

batch 200 writen
batch 400 writen
batch 600 writen
batch 800 writen
batch 1000 writen

照着注释,很好理解了。值得一提的是,生成的文件是以文件夹的形式保存的,内部由data.mdb lock.mdb组成,不用管他。

这里需要强烈注意的一点是,datum里的图像是需要有长宽高三个维度的,而且第一个维度默认是通道数,而我们平常的图像格式是第三位是通道。因此我们需要在读入普通图像的时候将他转置一下存入datum中,或者在外面增加一层。

读取数据文件

读取上面生成的数据文件。

#coding:utf-8

import caffe
import lmdb
import numpy as np
import cv2
from caffe.proto import caffe_pb2

lmdb_env = lmdb.open('lmdb_data')#打开数据文件
lmdb_txn = lmdb_env.begin()      #生成句柄
lmdb_cursor = lmdb_txn.cursor()  #生成迭代器指针
datum = caffe_pb2.Datum()        #caffe定义的数据类型

for key, value in lmdb_cursor:   #循环获取数据
    datum.ParseFromString(value) #从value中读取datum数据

    label = datum.label          #获取标签以及图像数据
    data = caffe.io.datum_to_array(datum)
    print data.shape
    print datum.channels
    image =data.transpose(1,2,0)
    cv2.imshow('cv2.png', image) #显示
    cv2.waitKey(0)

cv2.destroyAllWindows()
lmdb_env.close()

输出:(图像略)

(3, 27, 72)
3

基本是和写入程序一一相对,很好理解。同样需要注意的是如需要显示图像,则需要将数据转置回来。