You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mxnet.apache.org by ma...@apache.org on 2018/03/02 09:44:50 UTC
[incubator-mxnet] branch master updated: Cleaned up image
classification cpp example (#9799)
This is an automated email from the ASF dual-hosted git repository.
marcoabreu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-mxnet.git
The following commit(s) were added to refs/heads/master by this push:
new 9757091 Cleaned up image classification cpp example (#9799)
9757091 is described below
commit 97570916f844bcb4515d972c75fb0a75da345d97
Author: Anton Chernov <me...@gmail.com>
AuthorDate: Fri Mar 2 10:44:40 2018 +0100
Cleaned up image classification cpp example (#9799)
* Cleaned up cpp image classification example
* Added gpg key
* Revert "Added gpg key"
This reverts commit d5c29d7853d1c8040d3940947e86036978df5d79.
* Adressed review comments, made other small improvements
* Reverted default device type to cpu
* Minor type change
* Applied review comments
* Brought back the static linking option
---
CMakeLists.txt | 4 +-
.../predict-cpp/CMakeLists.txt | 60 +--
.../predict-cpp/image-classification-predict.cc | 441 +++++++++++----------
3 files changed, 261 insertions(+), 244 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7f68ed2..a076524 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,6 +32,7 @@ mxnet_option(USE_GPROF "Compile with gprof (profiling) flag" OFF)
mxnet_option(USE_CXX14_IF_AVAILABLE "Build with C++14 if the compiler supports it" OFF)
mxnet_option(USE_VTUNE "Enable use of Intel Amplifier XE (VTune)" OFF) # one could set VTUNE_ROOT for search path
mxnet_option(ENABLE_CUDA_RTC "Build with CUDA runtime compilation support" ON)
+mxnet_option(BUILD_CPP_EXAMPLES "Build cpp examples" ON)
mxnet_option(INSTALL_EXAMPLES "Install the example source files." OFF)
mxnet_option(USE_SIGNAL_HANDLER "Print stack traces on segfaults." OFF)
@@ -690,8 +691,7 @@ if(USE_CPP_PACKAGE)
add_subdirectory(cpp-package)
endif()
-# Problems on Mac OS X: 1. librt not available 2. mxnet built as MODULE library, which can't be linked.
-if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin"))
+if(BUILD_CPP_EXAMPLES)
add_subdirectory(example/image-classification/predict-cpp)
endif()
diff --git a/example/image-classification/predict-cpp/CMakeLists.txt b/example/image-classification/predict-cpp/CMakeLists.txt
index 59c98d8..a2f52b9 100644
--- a/example/image-classification/predict-cpp/CMakeLists.txt
+++ b/example/image-classification/predict-cpp/CMakeLists.txt
@@ -1,34 +1,36 @@
-if(USE_OPENCV)
- find_package(OpenCV QUIET COMPONENTS core highgui imgproc imgcodecs)
- if(NOT OpenCV_FOUND) # if not OpenCV 3.x, then imgcodecs are not found
- find_package(OpenCV REQUIRED COMPONENTS core highgui imgproc)
- endif()
+# Check OpenCV
+if(NOT USE_OPENCV OR NOT OpenCV_FOUND)
+ message(WARNING "\
+OpenCV should be enabled and found to build image classification example, skipping...")
+ return()
+endif()
- if(NOT MSVC)
- set(UNITTEST_STATIC_LINK ON)
- endif()
+if(NOT MSVC)
+ set(IMG_CLASSIFICATION_EXAMPLE_STATIC_LINK ON CACHE BOOL "\
+Link mxnet library statically in the c++ image classification example")
+else()
+ # disable static linking on Windows
+ set(IMG_CLASSIFICATION_EXAMPLE_STATIC_LINK OFF)
+endif()
- add_executable(image-classification-predict image-classification-predict.cc)
- include_directories(SYSTEM ${OpenCV_INCLUDE_DIRS})
+add_executable(image-classification-predict image-classification-predict.cc)
+include_directories(SYSTEM ${OpenCV_INCLUDE_DIRS})
- if(UNITTEST_STATIC_LINK)
- target_link_libraries(image-classification-predict
- ${BEGIN_WHOLE_ARCHIVE} mxnet_static ${END_WHOLE_ARCHIVE}
- dmlc
- ${mxnet_LINKER_LIBS}
- )
- else()
- target_link_libraries(image-classification-predict
- dmlc
- ${nnvm_LINKER_LIBS}
- ${mxnet_LINKER_LIBS}
- mxnet
- )
- endif()
- target_link_libraries(image-classification-predict ${OpenCV_LIBS})
- if(UNIX)
- target_link_libraries(image-classification-predict rt)
- endif()
- list(APPEND mxnet_LINKER_LIBS ${OpenCV_LIBS})
+if(IMG_CLASSIFICATION_EXAMPLE_STATIC_LINK)
+ target_link_libraries(image-classification-predict
+ ${BEGIN_WHOLE_ARCHIVE} mxnet_static ${END_WHOLE_ARCHIVE}
+ dmlc
+ ${mxnet_LINKER_LIBS}
+ )
+ add_dependencies(image-classification-predict mxnet_static)
+else()
+ target_link_libraries(image-classification-predict
+ dmlc
+ ${nnvm_LINKER_LIBS}
+ ${mxnet_LINKER_LIBS}
+ mxnet
+ )
+ add_dependencies(image-classification-predict mxnet)
endif()
+
diff --git a/example/image-classification/predict-cpp/image-classification-predict.cc b/example/image-classification/predict-cpp/image-classification-predict.cc
index a4a968e..186107b 100644
--- a/example/image-classification/predict-cpp/image-classification-predict.cc
+++ b/example/image-classification/predict-cpp/image-classification-predict.cc
@@ -1,283 +1,298 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
/*!
- * Copyright (c) 2015 by Xiao Liu, pertusa, caprice-j
+ * 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: liuxiao@foxmail.com
-//
-
-#include <stdio.h>
-
-// Path for c_predict_api
-#include <mxnet/c_predict_api.h>
-
+ *
+ * This is a simple predictor which shows how to use c api for image classification. 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: liuxiao@foxmail.com
+*/
+
+#include <cstdio>
+#include <cstdlib>
#include <iostream>
#include <fstream>
-#include <string>
#include <vector>
-
+#include <memory>
+#include <iomanip>
#include <opencv2/opencv.hpp>
+// Path for c_predict_api
+#include <mxnet/c_predict_api.h>
const mx_float DEFAULT_MEAN = 117.0;
+static std::string trim(const std::string& input) {
+ auto not_space = [](int ch) {
+ return !std::isspace(ch);
+ };
+ auto output = input;
+ output.erase(output.begin(), std::find_if(output.begin(), output.end(), not_space));
+ output.erase(std::find_if(output.rbegin(), output.rend(), not_space).base(), output.end());
+ return output;
+}
+
// 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";
- length_ = 0;
- buffer_ = NULL;
- return;
- }
+ std::string file_path_;
+ std::size_t length_ = 0;
+ std::unique_ptr<char[]> buffer_;
- ifs.seekg(0, std::ios::end);
- length_ = ifs.tellg();
- ifs.seekg(0, std::ios::beg);
- std::cout << file_path.c_str() << " ... "<< length_ << " bytes\n";
+ explicit BufferFile(const std::string& file_path)
+ : file_path_(file_path) {
- buffer_ = new char[sizeof(char) * length_];
- ifs.read(buffer_, length_);
- ifs.close();
+ 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";
+ return;
}
- int GetLength() {
- return length_;
- }
- char* GetBuffer() {
- return buffer_;
- }
+ ifs.seekg(0, std::ios::end);
+ length_ = static_cast<std::size_t>(ifs.tellg());
+ ifs.seekg(0, std::ios::beg);
+ std::cout << file_path.c_str() << " ... " << length_ << " bytes\n";
- ~BufferFile() {
- if (buffer_) {
- delete[] buffer_;
- buffer_ = NULL;
- }
- }
-};
+ buffer_.reset(new char[length_]);
+ ifs.read(buffer_.get(), length_);
+ ifs.close();
+ }
-void GetImageFile(const std::string image_file,
- mx_float* image_data, const int channels,
- const cv::Size resize_size, const mx_float* mean_data = nullptr) {
- // Read all kinds of file into a BGR color 3 channels image
- cv::Mat im_ori = cv::imread(image_file, cv::IMREAD_COLOR);
+ std::size_t GetLength() {
+ return length_;
+ }
- if (im_ori.empty()) {
- std::cerr << "Can't open the image. Please check " << image_file << ". \n";
- assert(false);
- }
+ char* GetBuffer() {
+ return buffer_.get();
+ }
+};
- cv::Mat im;
+void GetImageFile(const std::string& image_file,
+ mx_float* image_data, int channels,
+ cv::Size resize_size, const mx_float* mean_data = nullptr) {
+ // Read all kinds of file into a BGR color 3 channels image
+ cv::Mat im_ori = cv::imread(image_file, cv::IMREAD_COLOR);
- resize(im_ori, im, resize_size);
+ if (im_ori.empty()) {
+ std::cerr << "Can't open the image. Please check " << image_file << ". \n";
+ assert(false);
+ }
- int size = im.rows * im.cols * channels;
+ cv::Mat im;
- 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;
+ resize(im_ori, im, resize_size);
- float mean_b, mean_g, mean_r;
- mean_b = mean_g = mean_r = DEFAULT_MEAN;
+ int size = im.rows * im.cols * channels;
- for (int i = 0; i < im.rows; i++) {
- uchar* data = im.ptr<uchar>(i);
+ 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 j = 0; j < im.cols; j++) {
- if (mean_data) {
- mean_r = *mean_data;
- if (channels > 1) {
- mean_g = *(mean_data + size / 3);
- mean_b = *(mean_data + size / 3 * 2);
- }
- mean_data++;
- }
- if (channels > 1) {
- *ptr_image_b++ = static_cast<mx_float>(*data++) - mean_b;
- *ptr_image_g++ = static_cast<mx_float>(*data++) - mean_g;
- }
+ float mean_b, mean_g, mean_r;
+ mean_b = mean_g = mean_r = DEFAULT_MEAN;
- *ptr_image_r++ = static_cast<mx_float>(*data++) - mean_r;;
+ for (int i = 0; i < im.rows; i++) {
+ auto data = im.ptr<uchar>(i);
+
+ for (int j = 0; j < im.cols; j++) {
+ if (mean_data) {
+ mean_r = *mean_data;
+ if (channels > 1) {
+ mean_g = *(mean_data + size / 3);
+ mean_b = *(mean_data + size / 3 * 2);
}
+ mean_data++;
+ }
+ if (channels > 1) {
+ *ptr_image_b++ = static_cast<mx_float>(*data++) - mean_b;
+ *ptr_image_g++ = static_cast<mx_float>(*data++) - mean_g;
+ }
+
+ *ptr_image_r++ = static_cast<mx_float>(*data++) - mean_r;;
}
+ }
}
// LoadSynsets
// Code from : https://github.com/pertusa/mxnet_predict_cc/blob/master/mxnet_predict.cc
-std::vector<std::string> LoadSynset(std::string synset_file) {
- std::ifstream fi(synset_file.c_str());
+std::vector<std::string> LoadSynset(const std::string& synset_file) {
+ std::ifstream fi(synset_file.c_str());
- if ( !fi.is_open() ) {
- std::cerr << "Error opening synset file " << synset_file << std::endl;
- assert(false);
- }
+ if (!fi.is_open()) {
+ std::cerr << "Error opening synset file " << synset_file << std::endl;
+ assert(false);
+ }
- std::vector<std::string> output;
+ std::vector<std::string> output;
- std::string synset, lemma;
- while ( fi >> synset ) {
- getline(fi, lemma);
- output.push_back(lemma);
- }
+ std::string synset, lemma;
+ while (fi >> synset) {
+ getline(fi, lemma);
+ output.push_back(lemma);
+ }
- fi.close();
+ fi.close();
- return output;
+ 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;
- }
+ if (data.size() != synset.size()) {
+ std::cerr << "Result data and synset size do not match!" << std::endl;
+ }
- float best_accuracy = 0.0;
- int best_idx = 0;
+ float best_accuracy = 0.0;
+ std::size_t best_idx = 0;
- for ( int i = 0; i < static_cast<int>(data.size()); i++ ) {
- printf("Accuracy[%d] = %.8f\n", i, data[i]);
+ for (std::size_t i = 0; i < data.size(); ++i) {
+ std::cout << "Accuracy[" << i << "] = " << std::setprecision(8) << data[i] << std::endl;
- if ( data[i] > best_accuracy ) {
- best_accuracy = data[i];
- best_idx = 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);
+ std::cout << "Best Result: " << trim(synset[best_idx]) << " (id=" << best_idx << ", " <<
+ "accuracy=" << std::setprecision(8) << best_accuracy << ")" << std::endl;
}
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
- std::string json_file = "model/Inception/Inception-BN-symbol.json";
- std::string param_file = "model/Inception/Inception-BN-0126.params";
- std::string synset_file = "model/Inception/synset.txt";
- std::string nd_file = "model/Inception/mean_224.nd";
-
- BufferFile json_data(json_file);
- BufferFile param_data(param_file);
-
- // 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 };
- const mx_uint input_shape_data[4] = { 1,
+ if (argc < 2) {
+ std::cout << "No test image here." << std::endl
+ << "Usage: ./image-classification-predict apple.jpg" << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ std::string test_file(argv[1]);
+
+ // Models path for your model, you have to modify it
+ std::string json_file = "model/Inception/Inception-BN-symbol.json";
+ std::string param_file = "model/Inception/Inception-BN-0126.params";
+ std::string synset_file = "model/Inception/synset.txt";
+ std::string nd_file = "model/Inception/mean_224.nd";
+
+ BufferFile json_data(json_file);
+ BufferFile param_data(param_file);
+
+ // 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 };
+ const mx_uint input_shape_data[4] = { 1,
static_cast<mx_uint>(channels),
static_cast<mx_uint>(height),
- static_cast<mx_uint>(width)};
- PredictorHandle pred_hnd = 0;
-
- if (json_data.GetLength() == 0 ||
- param_data.GetLength() == 0) {
- return -1;
- }
-
- // 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,
- &pred_hnd);
- assert(pred_hnd);
-
- int image_size = width * height * channels;
-
- // Read Mean Data
- const mx_float* nd_data = NULL;
- NDListHandle nd_hnd = 0;
- BufferFile nd_buf(nd_file);
-
- if (nd_buf.GetLength() > 0) {
- mx_uint nd_index = 0;
- mx_uint nd_len;
- const mx_uint* nd_shape = 0;
- const char* nd_key = 0;
- mx_uint nd_ndim = 0;
-
- MXNDListCreate((const char*)nd_buf.GetBuffer(),
- nd_buf.GetLength(),
+ static_cast<mx_uint>(width) };
+ PredictorHandle pred_hnd = nullptr;
+
+ if (json_data.GetLength() == 0 || param_data.GetLength() == 0) {
+ return EXIT_FAILURE;
+ }
+
+ // Create Predictor
+ MXPredCreate(static_cast<const char*>(json_data.GetBuffer()),
+ static_cast<const char*>(param_data.GetBuffer()),
+ static_cast<int>(param_data.GetLength()),
+ dev_type,
+ dev_id,
+ num_input_nodes,
+ input_keys,
+ input_shape_indptr,
+ input_shape_data,
+ &pred_hnd);
+ assert(pred_hnd);
+
+ auto image_size = static_cast<std::size_t>(width * height * channels);
+
+ // Read Mean Data
+ const mx_float* nd_data = nullptr;
+ NDListHandle nd_hnd = nullptr;
+ BufferFile nd_buf(nd_file);
+
+ if (nd_buf.GetLength() > 0) {
+ mx_uint nd_index = 0;
+ mx_uint nd_len;
+ const mx_uint* nd_shape = nullptr;
+ const char* nd_key = nullptr;
+ mx_uint nd_ndim = 0;
+
+ MXNDListCreate(static_cast<const char*>(nd_buf.GetBuffer()),
+ static_cast<int>(nd_buf.GetLength()),
&nd_hnd, &nd_len);
- MXNDListGet(nd_hnd, nd_index, &nd_key, &nd_data, &nd_shape, &nd_ndim);
- }
+ MXNDListGet(nd_hnd, nd_index, &nd_key, &nd_data, &nd_shape, &nd_ndim);
+ }
- // Read Image Data
- std::vector<mx_float> image_data = std::vector<mx_float>(image_size);
+ // Read Image Data
+ std::vector<mx_float> image_data(image_size);
- GetImageFile(test_file, image_data.data(),
- channels, cv::Size(width, height), nd_data);
+ GetImageFile(test_file, image_data.data(), channels, cv::Size(width, height), nd_data);
- // Set Input Image
- MXPredSetInput(pred_hnd, "data", image_data.data(), image_size);
+ // Set Input Image
+ MXPredSetInput(pred_hnd, "data", image_data.data(), static_cast<mx_uint>(image_size));
- // Do Predict Forward
- MXPredForward(pred_hnd);
+ // Do Predict Forward
+ MXPredForward(pred_hnd);
- mx_uint output_index = 0;
+ mx_uint output_index = 0;
- mx_uint *shape = 0;
- mx_uint shape_len;
+ mx_uint* shape = nullptr;
+ mx_uint shape_len;
- // Get Output Result
- MXPredGetOutputShape(pred_hnd, output_index, &shape, &shape_len);
+ // Get Output Result
+ MXPredGetOutputShape(pred_hnd, output_index, &shape, &shape_len);
- size_t size = 1;
- for (mx_uint i = 0; i < shape_len; ++i) size *= shape[i];
+ std::size_t size = 1;
+ for (mx_uint i = 0; i < shape_len; ++i) { size *= shape[i]; }
- std::vector<float> data(size);
+ std::vector<float> data(size);
- MXPredGetOutput(pred_hnd, output_index, &(data[0]), size);
+ MXPredGetOutput(pred_hnd, output_index, &(data[0]), static_cast<mx_uint>(size));
- // Release NDList
- if (nd_hnd)
- MXNDListFree(nd_hnd);
+ // Release NDList
+ if (nd_hnd) {
+ MXNDListFree(nd_hnd);
+ }
- // Release Predictor
- MXPredFree(pred_hnd);
+ // Release Predictor
+ MXPredFree(pred_hnd);
- // Synset path for your model, you have to modify it
- std::vector<std::string> synset = LoadSynset(synset_file);
+ // Synset path for your model, you have to modify it
+ auto synset = LoadSynset(synset_file);
- // Print Output Data
- PrintOutputResult(data, synset);
+ // Print Output Data
+ PrintOutputResult(data, synset);
- return 0;
+ return EXIT_SUCCESS;
}
--
To stop receiving notification emails like this one, please contact
marcoabreu@apache.org.