本教程旨在督促自己从头到尾重新学习一遍Caffe,主要参考Caffe主页的教程和大牛们的博文,若有不妥之处,还望告知。
LMDB是Cafffe中应用的一种数据库,我们常常需要对LMDB进行读写操作,本文介绍如何采用Python代码进行LMDB的读写操作。
HDF5和LMDB相比,HDF5的读写格式简单;LMDB采用内存-映射文件(memory-mapped files),所以拥有非常好的I/O性能,而且对于大型数据库来说,HDF5的文件常常整个写入内存,所以HDF5的文件大小就受限于内存大小,当然也可以通过文件分割来解决问题,但其I/O性能就不如LMDB的页缓存(page cachiing)策略了。
Python读写LMDB
首先确认你安装了lmdb(pip安装:pip install lmdb)和Caffe的python包(Caffe中的pycaffe)。LMDB采用键值对<key-value>的存储格式,key就是字符形式的ID,value是Caffe中Datum类的序列化形式。
import numpy as np
import lmdb
import caffe
N = 1000
# Let's pretend this is interesting data
X = np.zeros((N, 3, 32, 32), dtype=np.uint8)
y = np.zeros(N, dtype=np.int64)
# We need to prepare the database for the size. We'll set it 10 times
# greater than what we theoretically need. There is little drawback to
# setting this too big. If you still run into problem after raising
# this, you might want to try saving fewer entries in a single
# transaction.
map_size = X.nbytes * 10
env = lmdb.open('mylmdb', map_size=map_size)
with env.begin(write=True) as txn:
# txn is a Transaction object
for i in range(N):
datum = caffe.proto.caffe_pb2.Datum()
datum.channels = X.shape[1]
datum.height = X.shape[2]
datum.width = X.shape[3]
datum.data = X[i].tobytes() # or .tostring() if numpy < 1.9
datum.label = int(y[i])
str_id = '{:08}'.format(i)
# The encode is only essential in Python 3
txn.put(str_id.encode('ascii'), datum.SerializeToString())
同样地,也可以采用Python读取LMDB数据库。
import numpy as np
import lmdb
import caffe
env = lmdb.open('mylmdb', readonly=True)
with env.begin() as txn:
raw_datum = txn.get(b'00000000')
datum = caffe.proto.caffe_pb2.Datum()
datum.ParseFromString(raw_datum)
flat_x = np.fromstring(datum.data, dtype=np.uint8)
x = flat_x.reshape(datum.channels, datum.height, datum.width)
y = datum.label
其中,<key, value>键值对的遍历:
with env.begin() as txn:
cursor = txn.cursor()
for key, value in cursor:
print(key, value)
Python读取图像数据
很多时候我们需要从硬盘读取图像数据并存入到LMDB数据库,或者读取LMDB数据Datum并还原图像。
读取LMDB数据库中的Datum数据,这里再稍微介绍一下Datum的格式:channels:图片的通道,彩色图有3个通道,灰度图只有1通道,当然也可以用通道数来表示其他意思,比如表示两张图片,每个通道一个单张的图;height:图片(即data)的高;width:图片(即data)的宽;data:图片的数据(像素值);label:图片的label。
import sys
import numpy as np
import lmdb
import caffe
import argparse
from matplotlib import pyplot
lmdbpath = 'you/lmdb/file/path'
env = lmdb.open(lmdbpath, readonly=True)
with env.begin() as txn:
cursor = txn.cursor()
for key, value in cursor:
print 'key: ',key
datum = caffe.proto.caffe_pb2.Datum() #datum类型
datum.ParseFromString(value) #转成datum
flat_x = np.fromstring(datum.data, dtype=np.uint8) #转成numpy类型
x = flat_x.reshape(datum.channels, datum.height, datum.width)
y = datum.label#图片的label
fig = pyplot.figure()#把两张图片显示出来
pyplot.imshow(x, cmap='gray')
Caffe一般将图片名字定入到txt文件中,也可以采用其他方式,这里直接采用image_list表示所有图像的路径及其对应的标签,采用二维字符数组形式保存。
import numpy as np
import lmdb
import Image as img
from skimage import io
import caffe
env = lmdb.Environment(args.lmdb, map_size=int(1e10)) # map_size指数据库大小,根据实际需要进行设置
with env.begin(write=True) as txn:
# txn is a Transaction object
for i in range(len(image_list)):
datum = caffe.proto.caffe_pb2.Datum()
img = np.array(io.imread(image_list[i][0]))
label = int(image_list[i][1])
img = img.transpose((2, 0, 1)) # caffe存储的原因,由RGB转为BGR
datum = caffe.io.array_to_datum(img)
datum.label = int(img_files[i]['label'])
str_id = '%08d' % i # 可以加上文件名或者其他字符
txn.put(str_id.encode('ascii'), datum.SerializeToString())
注意:数据库的读是按照key的排序读的,key的顺序并不是按照写的顺序,是字典序。所以写数据库时key必须重新写,如果把图片名字作为key读数据库出来的图片就是按照图片的字典序(不是写的顺序)。str_id是key。
4.1 准备
学习LMDB的时候不禁想到知乎上的提问“有哪些名人长期生活在其他名人的光环下”,说实话感觉查它的人基本都是为了用Caffe……
Anyway,LMDB和SQLite/MySQL等关系型数据库不同,属于key-value数据库(把LMDB想成dict会比较容易理解),键key与值value都是字符串。
安装:
pip install lmdb
使用时import lmdb。
4.2 操作流程
概况地讲,操作LMDB的流程是:
- 通过env = lmdb.open()打开环境
- 通过txn = env.begin()建立事务
- 通过txn.put(key, value)进行插入和修改
- 通过txn.delete(key)进行删除
- 通过txn.get(key)进行查询
- 通过txn.cursor()进行遍历
- 通过txn.commit()提交更改
4.3 操作实例
4.3.1 建立环境
-
#!/usr/bin/env python
-
import lmdb
-
env = lmdb.open("students");
运行一下,查看当前目录的变化:
set_env.py
可以看到当前目录下多了students目录,里面有data.mdb和lock.mdb两个文件。
4.3.2 插入、删除、修改
插入与修改都用put实现,删除用delete实现。
-
#!/usr/bin/env python
-
import lmdb
-
env = lmdb.open("students");
-
txn = env.begin(write = True);
-
txn.put(str(1), "Alice");
-
txn.put(str(2), "Bob");
-
txn.put(str(3), "Peter");
-
txn.delete(str(1));
-
txn.put(str(3), "Mark");
-
txn.commit();
注意用txn = env.begin()创建事务时,有write = True才能够写数据库。
4.3.3 查询
查单条记录用get(key),遍历数据库用cursor。
直接在上面的代码commit()之后加上:
-
txn = env.begin();
-
print txn.get(str(2));
-
for key, value in txn.cursor():
-
print (key, value);
运行一下,输出结果为:
test_query.py
注意上次commit()之后要用env.begin()更新txn。
4.3.4 完整的例子
-
#!/usr/bin/env python
-
import lmdb
-
import os, sys
-
def initialize():
-
env = lmdb.open("students");
-
return env;
-
def insert(env, sid, name):
-
txn = env.begin(write = True);
-
txn.put(str(sid), name);
-
txn.commit();
-
def delete(env, sid):
-
txn = env.begin(write = True);
-
txn.delete(str(sid));
-
txn.commit();
-
def update(env, sid, name):
-
txn = env.begin(write = True);
-
txn.put(str(sid), name);
-
txn.commit();
-
def search(env, sid):
-
txn = env.begin();
-
name = txn.get(str(sid));
-
return name;
-
def display(env):
-
txn = env.begin();
-
cur = txn.cursor();
-
for key, value in cur:
-
print (key, value);
-
env = initialize();
-
print "Insert 3 records."
-
insert(env, 1, "Alice");
-
insert(env, 2, "Bob");
-
insert(env, 3, "Peter");
-
display(env);
-
print "Delete the record where sid = 1."
-
delete(env, 1);
-
display(env);
-
print "Update the record where sid = 3."
-
update(env, 3, "Mark");
-
display(env);
-
print "Get the name of student whose sid = 3."
-
name = search(env, 3);
-
print name;
-
env.close();
-
os.system("rm -r students");
运行一下,输出结果为:
转载:https://zhuanlan.zhihu.com/p/23485774
转载:https://blog.csdn.net/haluoluo211/article/details/54428026