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.