From a151f0f5e3bc5d2974bbdc918dda65611ea7b392 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Wed, 18 Oct 2017 12:12:01 +0200 Subject: [PATCH] Use PIMPL idiom to reduce compile times. --- CMakeLists.txt | 16 ++++--- src/Simulator.cpp | 110 +++++++++++++++++++++++++++------------------- src/Simulator.hpp | 22 +--------- 3 files changed, 79 insertions(+), 69 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f42649a..3f0f921 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,18 +6,19 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules") set(CMAKE_CXX_STANDARD 17) +# Define executable and dependencies file(GLOB fmri_SRC "src/*.cpp" + "src/*.hpp" ) +add_executable(fmri ${fmri_SRC}) + # Build without GPU support for quicker development add_definitions(-DCPU_ONLY) -# Locate libraries - -add_executable(fmri ${fmri_SRC}) - +# Library dependencies # Require OpenCV find_package(OpenCV REQUIRED) include_directories(${OpenCV_INCLUDE_DIRS}) @@ -38,7 +39,12 @@ find_package(Glog REQUIRED) include_directories(${GLOG_INCLUDE_DIRS}) target_link_libraries(fmri ${GLOG_LIBRARIES}) -# Require png++ +# Require png++ (dumping to file) find_package(png++ REQUIRED) include_directories(${png++_INCLUDE_DIRS}) target_link_libraries(fmri ${png++_LIBRARIES}) + +# Require GLUT (visualisation) +find_package(GLUT REQUIRED) +include_directories(${GLUT_INCLUDE_DIRS}) +target_link_libraries(fmri ${GLUT_LIBRARIES}) diff --git a/src/Simulator.cpp b/src/Simulator.cpp index e112f17..3e7f305 100644 --- a/src/Simulator.cpp +++ b/src/Simulator.cpp @@ -1,39 +1,84 @@ #include #include +#include #include -#include "Simulator.hpp" #include - #include #include #include +#include "Simulator.hpp" +#include "utils.hpp" + using namespace caffe; using namespace std; using namespace fmri; -Simulator::Simulator(const string& model_file, const string& weights_file, const string& means_file) : - net(new Net(model_file, TEST)) +struct Simulator::Impl { - net->CopyTrainedLayersFrom(weights_file); + caffe::Net net; + cv::Size input_geometry; + optional means; + unsigned int num_channels; - Blob* input_layer = net->input_blobs()[0]; + Impl(const string& model_file, const string& weights_file, const string& means_file); + + vector getWrappedInputLayer(); + cv::Mat preprocess(cv::Mat original) const; + vector simulate(const string &input_file); +}; + +// Create simple forwarding functions. +Simulator::Simulator(const string& model_file, const string& weights_file, const string& means_file) : + pImpl(new Impl(model_file, weights_file, means_file)) +{ +} + +vector Simulator::simulate(const string& image_file) +{ + return pImpl->simulate(image_file); +} + +Simulator::Impl::Impl(const string& model_file, const string& weights_file, const string& means_file) : + net(model_file, TEST) +{ + net.CopyTrainedLayersFrom(weights_file); + + auto input_layer = net.input_blobs()[0]; input_geometry = cv::Size(input_layer->width(), input_layer->height()); num_channels = input_layer->channels(); input_layer->Reshape(1, num_channels, input_geometry.height, input_geometry.width); /* Forward dimension change to all layers. */ - net->Reshape(); + net.Reshape(); - if (means_file != "") { - means = processMeans(means_file); + if (!means_file.empty()) { + // Read in the means file + BlobProto proto; + ReadProtoFromBinaryFileOrDie(means_file, &proto); + + Blob mean_blob; + mean_blob.FromProto(proto); + + CHECK_EQ(mean_blob.channels(), num_channels) << "Number of channels should match!" << endl; + + vector channels; + float* data = mean_blob.mutable_cpu_data(); + for (unsigned int i = 0; i < num_channels; ++i) { + channels.emplace_back(mean_blob.height(), mean_blob.width(), CV_32FC1, data); + data += mean_blob.height() * mean_blob.width(); + } + + cv::Mat mean; + cv::merge(channels, mean); + + means = cv::Mat(input_geometry, mean.type(), cv::mean(mean)); } - } -vector Simulator::simulate(const string& image_file) +vector Simulator::Impl::simulate(const string& image_file) { typedef LayerData::Type LType; @@ -46,15 +91,15 @@ vector Simulator::simulate(const string& image_file) cv::split(input, channels); - net->Forward(); + net.Forward(); vector result; - Blob* input_layer = net->input_blobs()[0]; + auto input_layer = net.input_blobs()[0]; - const auto& names = net->layer_names(); - const auto& results = net->top_vecs(); - const auto& layers = net->layers(); + const auto& names = net.layer_names(); + const auto& results = net.top_vecs(); + const auto& layers = net.layers(); for (unsigned int i = 0; i < names.size(); ++i) { CHECK_EQ(results[i].size(), 1) << "Multiple outputs per layer are not supported!" << endl; @@ -66,10 +111,10 @@ vector Simulator::simulate(const string& image_file) return result; } -vector Simulator::getWrappedInputLayer() +vector Simulator::Impl::getWrappedInputLayer() { vector channels; - Blob* input_layer = net->input_blobs()[0]; + auto input_layer = net.input_blobs()[0]; const int width = input_geometry.width; const int height = input_geometry.height; @@ -114,7 +159,7 @@ static cv::Mat resize(const cv::Size& targetSize, cv::Mat original) return original; } -cv::Mat Simulator::preprocess(cv::Mat original) const +cv::Mat Simulator::Impl::preprocess(cv::Mat original) const { auto converted = fix_channels(num_channels, original); @@ -123,40 +168,17 @@ cv::Mat Simulator::preprocess(cv::Mat original) const cv::Mat sample_float; resized.convertTo(sample_float, num_channels == 3 ? CV_32FC3 : CV_32FC1); - if (means.empty()) { + if (!means) { return sample_float; } cv::Mat normalized; - cv::subtract(sample_float, means, normalized); + cv::subtract(sample_float, *means, normalized); return normalized; } -cv::Mat Simulator::processMeans(const string &means_file) const -{ - BlobProto proto; - ReadProtoFromBinaryFileOrDie(means_file, &proto); - - Blob mean_blob; - mean_blob.FromProto(proto); - - assert(mean_blob.channels() == num_channels); - - vector channels; - float* data = mean_blob.mutable_cpu_data(); - for (unsigned int i = 0; i < num_channels; ++i) { - channels.emplace_back(mean_blob.height(), mean_blob.width(), CV_32FC1, data); - data += mean_blob.height() * mean_blob.width(); - } - - cv::Mat mean; - cv::merge(channels, mean); - - return cv::Mat(input_geometry, mean.type(), cv::mean(mean)); -} - Simulator::~Simulator() { // Empty but defined constructor. diff --git a/src/Simulator.hpp b/src/Simulator.hpp index a19ce28..4353b8c 100644 --- a/src/Simulator.hpp +++ b/src/Simulator.hpp @@ -3,20 +3,8 @@ #include #include #include - -#include -#include - -#include "utils.hpp" #include "LayerData.hpp" -// Forward declare caffe::net so we don't have to include it here. -namespace caffe -{ - template - class Net; -} - namespace fmri { using std::string; using std::vector; @@ -29,13 +17,7 @@ namespace fmri { vector simulate(const string &input_file); private: - std::unique_ptr> net; - cv::Size input_geometry; - cv::Mat means; - unsigned int num_channels; - - vector getWrappedInputLayer(); - cv::Mat preprocess(cv::Mat original) const; - cv::Mat processMeans(const string &means_file) const; + struct Impl; + std::unique_ptr pImpl; }; }