caffe---数组,非图像数据,制作lmdb

本教程旨在督促自己从头到尾重新学习一遍Caffe,主要参考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 建立环境

 
  1. #!/usr/bin/env python

  2.  
  3. import lmdb

  4.  
  5. env = lmdb.open("students");

运行一下,查看当前目录的变化:

set_env.py

可以看到当前目录下多了students目录,里面有data.mdb和lock.mdb两个文件。

4.3.2 插入、删除、修改

插入与修改都用put实现,删除用delete实现。

 
  1. #!/usr/bin/env python

  2.  
  3. import lmdb

  4.  
  5. env = lmdb.open("students");

  6. txn = env.begin(write = True);

  7.  
  8. txn.put(str(1), "Alice");

  9. txn.put(str(2), "Bob");

  10. txn.put(str(3), "Peter");

  11.  
  12. txn.delete(str(1));

  13.  
  14. txn.put(str(3), "Mark");

  15.  
  16. txn.commit();

注意用txn = env.begin()创建事务时,有write = True才能够写数据库。

4.3.3 查询

查单条记录用get(key),遍历数据库用cursor。

直接在上面的代码commit()之后加上:

 
  1. txn = env.begin();

  2. print txn.get(str(2));

  3.  
  4. for key, value in txn.cursor():

  5. print (key, value);

运行一下,输出结果为:

test_query.py

注意上次commit()之后要用env.begin()更新txn。

4.3.4 完整的例子

 
  1. #!/usr/bin/env python

  2.  
  3. import lmdb

  4. import os, sys

  5.  
  6. def initialize():

  7. env = lmdb.open("students");

  8. return env;

  9.  
  10. def insert(env, sid, name):

  11. txn = env.begin(write = True);

  12. txn.put(str(sid), name);

  13. txn.commit();

  14.  
  15. def delete(env, sid):

  16. txn = env.begin(write = True);

  17. txn.delete(str(sid));

  18. txn.commit();

  19.  
  20. def update(env, sid, name):

  21. txn = env.begin(write = True);

  22. txn.put(str(sid), name);

  23. txn.commit();

  24.  
  25. def search(env, sid):

  26. txn = env.begin();

  27. name = txn.get(str(sid));

  28. return name;

  29.  
  30. def display(env):

  31. txn = env.begin();

  32. cur = txn.cursor();

  33. for key, value in cur:

  34. print (key, value);

  35.  
  36. env = initialize();

  37.  
  38. print "Insert 3 records."

  39. insert(env, 1, "Alice");

  40. insert(env, 2, "Bob");

  41. insert(env, 3, "Peter");

  42. display(env);

  43.  
  44. print "Delete the record where sid = 1."

  45. delete(env, 1);

  46. display(env);

  47.  
  48. print "Update the record where sid = 3."

  49. update(env, 3, "Mark");

  50. display(env);

  51.  
  52. print "Get the name of student whose sid = 3."

  53. name = search(env, 3);

  54. print name;

  55.  
  56. env.close();

  57.  
  58. os.system("rm -r students");

运行一下,输出结果为:

 

转载:https://zhuanlan.zhihu.com/p/23485774

 

转载:https://blog.csdn.net/haluoluo211/article/details/54428026

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值