Skip to content

Commit

Permalink
Merge from upstream dmlc/mxnet
Browse files Browse the repository at this point in the history
  • Loading branch information
yzhliu committed Dec 19, 2015
2 parents aae94f0 + e316055 commit ea1414f
Show file tree
Hide file tree
Showing 25 changed files with 1,319 additions and 230 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,4 @@ List of Contributors
* [Zhang Chen](https://github.com/zhangchen-qinyinghua)
* [Xianliang Wang](https://github.com/wangxianliang)
* [Junru Shao](https://github.com/yzgysjr)
* [Xiao Liu](https://github.com/skylook)
30 changes: 27 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -98,23 +98,47 @@ OBJ = $(patsubst src/%.cc, build/%.o, $(SRC))
CUSRC = $(wildcard src/*/*.cu)
CUOBJ = $(patsubst src/%.cu, build/%_gpu.o, $(CUSRC))

ifneq ($(EXTRA_OPERATORS), NONE)
EXTRA_SRC = $(wildcard $(EXTRA_OPERATORS)/*.cc $(EXTRA_OPERATORS)/*/*.cc)
EXTRA_OBJ = $(patsubst $(EXTRA_OPERATORS)/%.cc, $(EXTRA_OPERATORS)/build/%.o, $(EXTRA_SRC))
EXTRA_CUSRC = $(wildcard $(EXTRA_OPERATORS)/*.cu $(EXTRA_OPERATORS)/*/*.cu)
EXTRA_CUOBJ = $(patsubst $(EXTRA_OPERATORS)/%.cu, $(EXTRA_OPERATORS)/build/%_gpu.o, $(EXTRA_CUSRC))
else
EXTRA_SRC =
EXTRA_OBJ =
EXTRA_CUSRC =
EXTRA_CUOBJ =
endif

LIB_DEP += $(DMLC_CORE)/libdmlc.a
ALL_DEP = $(OBJ) $(LIB_DEP)
ALL_DEP = $(OBJ) $(EXTRA_OBJ) $(LIB_DEP)
ifeq ($(USE_CUDA), 1)
ALL_DEP += $(CUOBJ)
ALL_DEP += $(CUOBJ) $(EXTRA_CUOBJ)
LDFLAGS += -lnvrtc -lcuda
endif



build/%.o: src/%.cc
@mkdir -p $(@D)
$(CXX) -std=c++0x $(CFLAGS) -MM -MT build/$*.o $< >build/$*.d
$(CXX) -std=c++0x -c $(CFLAGS) -c $< -o $@

build/%_gpu.o: src/%.cu
@mkdir -p $(@D)
$(NVCC) $(NVCCFLAGS) -Xcompiler "$(CFLAGS)" -M build/$*_gpu.o $< >build/$*_gpu.d
$(NVCC) $(NVCCFLAGS) -Xcompiler "$(CFLAGS)" -M -MT build/$*_gpu.o $< >build/$*_gpu.d
$(NVCC) -c -o $@ $(NVCCFLAGS) -Xcompiler "$(CFLAGS)" $<

$(EXTRA_OPERATORS)/build/%.o: $(EXTRA_OPERATORS)/%.cc
@mkdir -p $(@D)
$(CXX) -std=c++0x $(CFLAGS) -Isrc/operator -MM -MT $(EXTRA_OPERATORS)/build/$*.o $< >$(EXTRA_OPERATORS)/build/$*.d
$(CXX) -std=c++0x -c $(CFLAGS) -Isrc/operator -c $< -o $@

$(EXTRA_OPERATORS)/build/%_gpu.o: $(EXTRA_OPERATORS)/%.cu
@mkdir -p $(@D)
$(NVCC) $(NVCCFLAGS) -Xcompiler "$(CFLAGS) -Isrc/operator" -M -MT $(EXTRA_OPERATORS)/build/$*_gpu.o $< >$(EXTRA_OPERATORS)/build/$*_gpu.d
$(NVCC) -c -o $@ $(NVCCFLAGS) -Xcompiler "$(CFLAGS) -Isrc/operator" $<

lib/libmxnet.a: $(ALL_DEP)
@mkdir -p $(@D)
ar crv $@ $(filter %.o, $?)
Expand Down
2 changes: 1 addition & 1 deletion doc/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Then build mxnet

```bash
git clone --recursive https://github.com/dmlc/mxnet
cd mxnet; cp make/osx.mk .;make -j4
cd mxnet; cp make/osx.mk ./config.mk; make -j4
```

Troubleshooting:
Expand Down
31 changes: 31 additions & 0 deletions example/cpp/image-classification/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Special thanks to https://github.com/pertusa for the Makefile
CFLAGS=-std=c++11 -Wno-unknown-pragmas -Wall

# Added for openblas
# export OPENBLAS_ROOT=/usr/local/opt/openblas

# CFLAGS+= -I${OPENBLAS_ROOT}/include
# LDFLAGS=-L${OPENBLAS_ROOT}/lib -lopenblas

# Added for opencv
CFLAGS+= `pkg-config --cflags opencv`
LDFLAGS+=`pkg-config --libs opencv`

# Added for mxnet
export MXNET_ROOT=`pwd`/../../../../mxnet

CFLAGS+= -I$(MXNET_ROOT)/include
LDFLAGS+=$(MXNET_ROOT)/lib/libmxnet.so

image-classification-predict: image-classification-predict.o
g++ -O3 -o image-classification-predict image-classification-predict.o $(LDFLAGS)

image-classification-predict.o: image-classification-predict.cc
g++ -O3 -c image-classification-predict.cc ${CFLAGS}

clean:
rm image-classification-predict
rm -f *.d *.o

lint:
python ../../../dmlc-core/scripts/lint.py mxnet "cpp" ./
64 changes: 64 additions & 0 deletions example/cpp/image-classification/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Image Classification Example of C++
This is a simple predictor which shows how to use c api for image classfication.

It uses opencv for image reading

# How to Use

## Build
1. Edit image-classification-predict.cc file, change the following lines to your model paths:
```bash
// Models path for your model, you have to modify it
BufferFile json_data("model/Inception/Inception_BN-symbol.json");
BufferFile param_data("model/Inception/Inception_BN-0039.params");
```

2. Edit synset file path if you have it:
```bash
// Synset path for your model, you have to modify it
std::vector<std::string> synset = LoadSynset("model/Inception/synset.txt");
```

3. You may also want to change the image size and channels:
```bash
// Image size and channels
int width = 224;
int height = 224;
int channels = 3;
```

4. Simply just use our Makefile to build:
```bash
make
```

## Usage
* Run:
```bash
./image-classification-predict apple.jpg
```
The only parameter is the path of the test image.

## Tips
* The model used in the sample can be downloaded here:
http://pan.baidu.com/s/1sjXKrqX

* If you donot run it in the mxnet root path, maybe you will need to copy lib folder here.

# Author
* **Xiao Liu**

* E-mail: [email protected]

* Homepage: [www.liuxiao.org](http://www.liuxiao.org/)

# Thanks
* pertusa (for Makefile and image reading check)

* caprice-j (for reading function)

* sofiawu (for sample model)

* piiswrong and tqchen (for useful coding suggestions)


237 changes: 237 additions & 0 deletions example/cpp/image-classification/image-classification-predict.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
/*!
* Copyright (c) 2015 by Xiao Liu, pertusa, caprice-j
* \file image_classification-predict.cpp
* \brief C++ predict example of mxnet
*/

//
// File: image-classification-predict.cpp
// This is a simple predictor which shows
// how to use c api for image classfication
// It uses opencv for image reading
// Created by liuxiao on 12/9/15.
// Thanks to : pertusa, caprice-j, sofiawu, tqchen, piiswrong
// Home Page: www.liuxiao.org
// E-mail: [email protected]
//

#include <stdio.h>

// Path for c_predict_api
#include <mxnet/c_predict_api.h>

#include <opencv2/core/core.hpp>
#include <opencv2/contrib/contrib.hpp>
#include <opencv2/highgui/highgui.hpp>

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

// Read file to buffer
class BufferFile {
public :
std::string file_path_;
int length_;
char* buffer_;

explicit BufferFile(std::string file_path)
:file_path_(file_path) {

std::ifstream ifs(file_path.c_str(), std::ios::in | std::ios::binary);
if (!ifs) {
std::cerr << "Can't open the file. Please check " << file_path << ". \n";
assert(false);
}

ifs.seekg(0, std::ios::end);
length_ = ifs.tellg();
ifs.seekg(0, std::ios::beg);
std::cout << file_path.c_str() << " ... "<< length_ << " bytes\n";

buffer_ = new char[sizeof(char) * length_];
ifs.read(buffer_, length_);
ifs.close();
}

int GetLength() {
return length_;
}
char* GetBuffer() {
return buffer_;
}

~BufferFile() {
delete[] buffer_;
buffer_ = NULL;
}
};

void GetMeanFile(const std::string image_file, mx_float* image_data,
const int channels, const cv::Size resize_size) {
// Read all kinds of file into a BGR color 3 channels image
cv::Mat im_ori = cv::imread(image_file, 1);

if (im_ori.empty()) {
std::cerr << "Can't open the image. Please check " << image_file << ". \n";
assert(false);
}

cv::Mat im;

resize(im_ori, im, resize_size);

// Better to be read from a mean.nb file
float mean = 117.0;

int size = im.rows * im.cols * 3;

mx_float* ptr_image_r = image_data;
mx_float* ptr_image_g = image_data + size / 3;
mx_float* ptr_image_b = image_data + size / 3 * 2;

for (int i = 0; i < im.rows; i++) {
uchar* data = im.ptr<uchar>(i);

for (int j = 0; j < im.cols; j++) {
mx_float b = static_cast<mx_float>(*data++) - mean;
mx_float g = static_cast<mx_float>(*data++) - mean;
mx_float r = static_cast<mx_float>(*data++) - mean;

*ptr_image_r++ = r;
*ptr_image_g++ = g;
*ptr_image_b++ = b;
}
}
}

// LoadSynsets
// Code from : https://github.com/pertusa/mxnet_predict_cc/blob/master/mxnet_predict.cc
std::vector<std::string> LoadSynset(const char *filename) {
std::ifstream fi(filename);

if ( !fi.is_open() ) {
std::cerr << "Error opening file " << filename << std::endl;
assert(false);
}

std::vector<std::string> output;

std::string synset, lemma;
while ( fi >> synset ) {
getline(fi, lemma);
output.push_back(lemma);
}

fi.close();

return output;
}

void PrintOutputResult(const std::vector<float>& data, const std::vector<std::string>& synset) {
if (data.size() != synset.size()) {
std::cerr << "Result data and synset size does not match!" << std::endl;
}

float best_accuracy = 0.0;
int best_idx = 0;

for ( int i = 0; i < static_cast<int>(data.size()); i++ ) {
printf("Accuracy[%d] = %.8f\n", i, data[i]);

if ( data[i] > best_accuracy ) {
best_accuracy = data[i];
best_idx = i;
}
}

printf("Best Result: [%s] id = %d, accuracy = %.8f\n",
synset[best_idx].c_str(), best_idx, best_accuracy);
}

int main(int argc, char* argv[]) {
if (argc < 2) {
std::cout << "No test image here." << std::endl
<< "Usage: ./image-classification-predict apple.jpg" << std::endl;
return 0;
}

std::string test_file;
test_file = std::string(argv[1]);

// Models path for your model, you have to modify it
BufferFile json_data("model/Inception/Inception_BN-symbol.json");
BufferFile param_data("model/Inception/Inception_BN-0039.params");

// Parameters
int dev_type = 1; // 1: cpu, 2: gpu
int dev_id = 0; // arbitrary.
mx_uint num_input_nodes = 1; // 1 for feedforward
const char* input_key[1] = {"data"};
const char** input_keys = input_key;

// Image size and channels
int width = 224;
int height = 224;
int channels = 3;

const mx_uint input_shape_indptr[2] = { 0, 4 };
// ( trained_width, trained_height, channel, num)
const mx_uint input_shape_data[4] = { 1,
static_cast<mx_uint>(channels),
static_cast<mx_uint>(width),
static_cast<mx_uint>(height) };
PredictorHandle out = 0; // alias for void *

//-- Create Predictor
MXPredCreate((const char*)json_data.GetBuffer(),
(const char*)param_data.GetBuffer(),
static_cast<size_t>(param_data.GetLength()),
dev_type,
dev_id,
num_input_nodes,
input_keys,
input_shape_indptr,
input_shape_data,
&out);

// Just a big enough memory 1000x1000x3
int image_size = width * height * channels;
std::vector<mx_float> image_data = std::vector<mx_float>(image_size);

//-- Read Mean Data
GetMeanFile(test_file, image_data.data(), channels, cv::Size(width, height));

//-- Set Input Image
MXPredSetInput(out, "data", image_data.data(), image_size);

//-- Do Predict Forward
MXPredForward(out);

mx_uint output_index = 0;

mx_uint *shape = 0;
mx_uint shape_len;

//-- Get Output Result
MXPredGetOutputShape(out, output_index, &shape, &shape_len);

size_t size = 1;
for (mx_uint i = 0; i < shape_len; ++i) size *= shape[i];

std::vector<float> data(size);

MXPredGetOutput(out, output_index, &(data[0]), size);

// Release Predictor
MXPredFree(out);

// Synset path for your model, you have to modify it
std::vector<std::string> synset = LoadSynset("model/Inception/synset.txt");

//-- Print Output Data
PrintOutputResult(data, synset);

return 0;
}
Loading

0 comments on commit ea1414f

Please sign in to comment.