## 1. LMDB简介
LMDB的全称是Lighting Memory-Mapped Database(闪电般的内存映射数据库)。它的结构很简单,一个文件夹,里面一个数据文件,一个锁文件。数据随意复制,随意传输。它的访问简单,不需要单独的数据管理进程。只要在访问代码里引用LMDB库,访问时给文件路径即可。
Caffe中使用的数据结构很简单,就是大量的矩阵和向量。数据之间没有关联,数据内没有复杂的对象结构,就是矩阵和向量。既然数据不复杂,Caffe就选择了LMDB这个简单的数据库来存放数据。
Caffe使用LMDB数据库,有以下两点原因:
- 数据源的格式多样性,有文本文件、二进制文件、图像文件等等,不可能用一个代码完成上述所有的数据格式。因此,要通过LMDB数据库,转化成统一的数据格式可以简化数据读取层的实现。
- 使用LMDB数据库可以大大节约磁盘IO的时间开销。因此,我们都有体会,一个具有几万个、几十万个文件的数据集,不管是直接复制,还是打开再解包,过程都巨慢无比。LMDB只有一个文件,你的介质有多快,就能复制多快,不会因为文件多而慢的令人心碎。
## 2. 环境配置
首先我们需要python包lmdb和caffe的python接口pycaffe。LMDB提供键值对的存储方式,其中每一个键值对就是我们数据集的一个样本。一般而言,键代表ID字符串,值代表序列化后的Caffe框架的Datum类(使用protobuf编译)。
## 3. Caffe中的Datum数据结构
Caffe中并不是把向量和矩阵直接放进数据库,而是将数据通过`caffe.proto`里定义的一个`Datum`类来封装,数据库中存放的是一个个Datum对象序列化成的字符串。Datum的定义如下:
message Datum {
optional int32 channels = 1;
optional int32 height = 2;
optional int32 width = 3;
// the actual image data, in bytes
optional bytes data = 4;
optional int32 label = 5;
// Optionally, the datum could also hold float data.
repeated float float_data = 6;
// If true data contains an encoded image that need to be decoded
optional bool encoded = 7 [default = false];
}
## 4. 创建LMDB数据库
```python
import lmdb
import numpy as np
import cv2
import caffe
from caffe.proto import caffe_pb2
#basic setting
lmdb_file = 'lmdb_data'
batch_size = 256
# create the lmdb file
lmdb_env = lmdb.open(lmdb_file, map_size=int(1e12))
lmdb_txn = lmdb_env.begin(write=True)
datum = caffe_pb2.Datum()
item_id = -1
for x in range(1000):
item_id += 1
#prepare the data and label
data = np.ones((3,64,64), np.uint8) * (item_id%128 + 64) #CxHxW array, uint8 or float
label = item_id%128 + 64
# save in datum
datum = caffe.io.array_to_datum(data, label)
keystr = '{:0>8d}'.format(item_id)
lmdb_txn.put( keystr, datum.SerializeToString() )
# write batch
if(item_id + 1) % batch_size == 0:
mit()
lmdb_txn = lmdb_env.begin(write=True)
print (item_id + 1)
# write last batch
if (item_id+1) % batch_size != 0:
mit()
print 'last batch'
print (item_id + 1)
```
## 5. 读取LMDB
```python
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()
for key, value in lmdb_cursor:
datum.ParseFromString(value)
label = datum.label
data = caffe.io.datum_to_array(datum)
#CxHxW to HxWxC in cv2
image = np.transpose(data, (1,2,0))
cv2.imshow('cv2', image)
cv2.waitKey(1)
print('{},{}'.format(key, label))
```