From a7397da70ba75d0ba4cef5c680058e2b0d5a4812 Mon Sep 17 00:00:00 2001 From: jiayuzhou Date: Wed, 1 Jul 2015 18:38:49 -0700 Subject: [PATCH] Integrate protobuf in matlab. The separate protobuf (e.g., https://gist.github.com/jiayuzhou/b5029bb1ba7bd7f1d911) is likely to crash Matlab due to the conflict below: [libprotobuf ERROR google/protobuf/descriptor_database.cc:57] File already exists in database: caffe.proto [libprotobuf FATAL google/protobuf/descriptor.cc:1018] CHECK failed: generated_database_->Add(encoded_file_descriptor, size) --- Makefile | 3 +- Makefile.config.example | 1 + matlab/+caffe/fromDatum.m | 11 +++++ matlab/+caffe/private/caffe_.cpp | 67 ++++++++++++++++++++++++++++- matlab/demo/lmdb_datum_demo.m | 72 ++++++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 matlab/+caffe/fromDatum.m create mode 100644 matlab/demo/lmdb_datum_demo.m diff --git a/Makefile b/Makefile index e4e66dfd138..425042473d8 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ PROJECT := caffe + CONFIG_FILE := Makefile.config # Explicitly check for the config file, otherwise make -k will proceed anyway. ifeq ($(wildcard $(CONFIG_FILE)),) @@ -456,7 +457,7 @@ $(MAT$(PROJECT)_SO): $(MAT$(PROJECT)_SRC) $(STATIC_NAME) exit 1; \ fi @ echo MEX $< - $(Q)$(MATLAB_DIR)/bin/mex $(MAT$(PROJECT)_SRC) \ + $(Q)$(MATLAB_DIR)/bin/mex -I$(MEXPLUS_DIR) $(MAT$(PROJECT)_SRC) \ CXX="$(CXX)" \ CXXFLAGS="\$$CXXFLAGS $(MATLAB_CXXFLAGS)" \ CXXLIBS="\$$CXXLIBS $(STATIC_LINK_COMMAND) $(LDFLAGS)" -output $@ diff --git a/Makefile.config.example b/Makefile.config.example index a873502559f..61254c30ec7 100644 --- a/Makefile.config.example +++ b/Makefile.config.example @@ -45,6 +45,7 @@ BLAS := atlas # MATLAB directory should contain the mex binary in /bin. # MATLAB_DIR := /usr/local # MATLAB_DIR := /Applications/MATLAB_R2012b.app +# MEXPLUS_DIR := /path/to/your/mexplus/include # NOTE: this is required only if you will compile the python interface. # We need to be able to find Python.h and numpy/arrayobject.h. diff --git a/matlab/+caffe/fromDatum.m b/matlab/+caffe/fromDatum.m new file mode 100644 index 00000000000..fafda8c35ae --- /dev/null +++ b/matlab/+caffe/fromDatum.m @@ -0,0 +1,11 @@ +function [ label, image ] = fromDatum( varargin ) +%FROMDATUM decode image and label from caffe protobuf. + +CHECK(nargin > 0, ['usage: '... + '[ label, image ] = fromDatum( datum )']); +datum = varargin{1}; + +[label, image] = caffe_('from_datum', datum); + +end + diff --git a/matlab/+caffe/private/caffe_.cpp b/matlab/+caffe/private/caffe_.cpp index 4e0ebc1c00a..399fcf8a748 100644 --- a/matlab/+caffe/private/caffe_.cpp +++ b/matlab/+caffe/private/caffe_.cpp @@ -14,12 +14,14 @@ #include #include "mex.h" - +#include "mexplus.h" #include "caffe/caffe.hpp" #define MEX_ARGS int nlhs, mxArray **plhs, int nrhs, const mxArray **prhs using namespace caffe; // NOLINT(build/namespaces) +//using namespace std; +//using namespace mexplus; // Do CHECK and throw a Mex error if check fails inline void mxCHECK(bool expr, const char* msg) { @@ -478,6 +480,68 @@ static void read_mean(MEX_ARGS) { mxFree(mean_proto_file); } +// Usage: caffe_('from_datum', datum) +static void from_datum(MEX_ARGS) { + mexplus::OutputArguments output(nlhs, plhs, 2); + mxCHECK(nrhs == 1 && mxIsChar(prhs[0]), + "Usage: caffe_('from_datum', datum)"); + + caffe::Datum datum; + std::basic_string datum_content = mexplus::MxArray::to(prhs[0]); //datum received from matlab + + mxCHECK(datum.ParseFromString(datum_content), + "Failed to parse datum."); + output.set(0, datum.data()); + + if (datum.has_encoded() && datum.encoded()) { + output.set(0, datum.data()); + } + else { + vector dimensions(3); + dimensions[0] = (datum.has_height()) ? datum.height() : 0; + dimensions[1] = (datum.has_width()) ? datum.width() : 0; + dimensions[2] = (datum.has_channels()) ? datum.channels() : 0; + mexplus::MxArray array; + vector subscripts(3); + int index = 0; + if (datum.has_data()) { + array.reset(mxCreateNumericArray(dimensions.size(), + &dimensions[0], + mxUINT8_CLASS, + mxREAL)); + const string& data = datum.data(); + for (int k = dimensions[2] - 1; k >= 0; --k) { // BGR to RGB order. + subscripts[2] = k; + for (int i = 0; i < dimensions[0]; ++i) { + subscripts[0] = i; + for (int j = 0; j < dimensions[1]; ++j) { + subscripts[1] = j; + array.set(subscripts, data[index++]); + } + } + } + } + else if (datum.float_data_size() > 0) { + array.reset(mxCreateNumericArray(dimensions.size(), + &dimensions[0], + mxSINGLE_CLASS, + mxREAL)); + for (int k = dimensions[2] - 1; k >= 0; --k) { // BGR to RGB order. + subscripts[2] = k; + for (int i = 0; i < dimensions[0]; ++i) { + subscripts[0] = i; + for (int j = 0; j < dimensions[1]; ++j) { + subscripts[1] = j; + array.set(subscripts, datum.float_data(index++)); + } + } + } + } + output.set(0, array.release()); + } + output.set(1, (datum.has_label()) ? datum.label() : 0); +} + /** ----------------------------------------------------------------- ** Available commands. **/ @@ -515,6 +579,7 @@ static handler_registry handlers[] = { { "get_init_key", get_init_key }, { "reset", reset }, { "read_mean", read_mean }, + { "from_datum", from_datum }, // The end. { "END", NULL }, }; diff --git a/matlab/demo/lmdb_datum_demo.m b/matlab/demo/lmdb_datum_demo.m new file mode 100644 index 00000000000..9a5bb260f8f --- /dev/null +++ b/matlab/demo/lmdb_datum_demo.m @@ -0,0 +1,72 @@ +% Example of using lmdb and caffe protobuf (datum) in matlab. +% +% by Jiayu, July 1, 2015. +% +% NOTE 1. start matlab with a specified libtiff.5.dylib. +% DYLD_INSERT_LIBRARIES=/usr/local/lib/libtiff.5.dylib /Applications/MATLAB_R2012b.app/bin/matlab & +% +% 2. install matlab-lmdb +% https://github.com/illidanlab/matlab-lmdb +% +% 3. the image num (the first input_num) in the model file should set to 1. +% will fix later. + +if exist('../+caffe', 'dir') + addpath('..'); +else + error('Please run this demo from caffe/matlab/demo'); +end + +addpath ../../../matlab-lmdb/ % change to your matlab-lmdb path + +cur_director = pwd; +net_model = strcat(cur_director, '/../../examples/mnist/lenet.prototxt'); +net_weights = strcat(cur_director, '/../../examples/mnist/lenet_iter_10000.caffemodel'); +db_path = strcat(cur_director, '/../../examples/mnist/mnist_test_lmdb'); +use_gpu = 0; +phase = 'test'; + + +% create caffe net instance +caffe.set_mode_cpu(); +net = caffe.Net(net_model, net_weights, phase); + +% load an existing lmdb database (crated using the shell in example). +database = lmdb.DB(db_path, 'RDONLY', true, 'NOLOCK', true); +cursor = database.cursor('RDONLY', true); + +max_count = 10; % maximum test cases + +count = 0; +correctNum = 0; +while cursor.next() + key = cursor.key; + value = cursor.value; + + % transform datum. + [image, label] = caffe.fromDatum(value); + + % prepare image + data = single(image); + data = permute(data, [2,1,3]); + + % generate prediction + scores = net.forward({data}); + predict_class = find(scores{1}==1) - 1; % shift 1 + + + fprintf('[%u] Class %u predicted as %u \n', count+1, label, predict_class) + + if(predict_class == label) + correctNum = correctNum + 1; + end + + count = count + 1; + if (count >= max_count) + break; + end +end + +fprintf('Correctly classified %d images out of %d ( %d percent)\n', correctNum, count, correctNum/count * 100) + +clear cursor;