Create an intermediate representation.
This way the visualiser does not need to know all about caffe, and can just work on the intermediate representation which is a lot easier on the compiler.
This commit is contained in:
65
src/LayerData.cpp
Normal file
65
src/LayerData.cpp
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#include <cstring>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
#include "LayerData.hpp"
|
||||||
|
|
||||||
|
using namespace fmri;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
LayerData::LayerData(const string& name, const vector<int>& shape, const DType* data, Type type) :
|
||||||
|
name_(name),
|
||||||
|
shape_(shape),
|
||||||
|
type_(type)
|
||||||
|
{
|
||||||
|
const auto dataSize = numEntries();
|
||||||
|
// Compute the dimension of the data area
|
||||||
|
data_.reset(new DType[dataSize]);
|
||||||
|
|
||||||
|
// Copy the data over with memcpy because it's just faster that way
|
||||||
|
memcpy(data_.get(), data, sizeof(DType) * dataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t LayerData::numEntries() const
|
||||||
|
{
|
||||||
|
return accumulate(shape_.begin(), shape_.end(), 1, multiplies<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
const vector<int>& LayerData::shape() const
|
||||||
|
{
|
||||||
|
return shape_;
|
||||||
|
}
|
||||||
|
|
||||||
|
typename LayerData::Type LayerData::type() const
|
||||||
|
{
|
||||||
|
return type_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const string& LayerData::name() const
|
||||||
|
{
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
DType const * LayerData::data() const
|
||||||
|
{
|
||||||
|
return data_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
LayerData::Type LayerData::typeFromString(string_view typeName)
|
||||||
|
{
|
||||||
|
if (typeName == "Input") {
|
||||||
|
return Type::Input;
|
||||||
|
} else if (typeName == "Convolution") {
|
||||||
|
return Type::Convolutional;
|
||||||
|
} else if (typeName == "ReLU") {
|
||||||
|
return Type::ReLU;
|
||||||
|
} else if (typeName == "Pooling") {
|
||||||
|
return Type::Pooling;
|
||||||
|
} else {
|
||||||
|
LOG(INFO) << "Received unknown layer type: " << typeName << endl;
|
||||||
|
return Type::Other;
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/LayerData.hpp
Normal file
50
src/LayerData.hpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
namespace fmri {
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::string_view;
|
||||||
|
using std::unique_ptr;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
|
class LayerData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Type {
|
||||||
|
Input,
|
||||||
|
Convolutional,
|
||||||
|
ReLU,
|
||||||
|
Pooling,
|
||||||
|
Output,
|
||||||
|
Other
|
||||||
|
};
|
||||||
|
|
||||||
|
LayerData(const string& name, const vector<int>& shape, const DType* data, Type type);
|
||||||
|
LayerData(const LayerData&) = delete;
|
||||||
|
LayerData(LayerData&&) = default;
|
||||||
|
|
||||||
|
LayerData& operator=(const LayerData&) = delete;
|
||||||
|
LayerData& operator=(LayerData&&) = default;
|
||||||
|
|
||||||
|
const string& name() const;
|
||||||
|
Type type() const;
|
||||||
|
const vector<int>& shape() const;
|
||||||
|
DType const * data() const;
|
||||||
|
size_t numEntries() const;
|
||||||
|
|
||||||
|
static Type typeFromString(string_view name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
string name_;
|
||||||
|
vector<int> shape_;
|
||||||
|
unique_ptr<DType[]> data_;
|
||||||
|
Type type_;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -28,8 +28,10 @@ Simulator::Simulator(const string& model_file, const string& weights_file, const
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<Simulator::DType> Simulator::simulate(const string& image_file)
|
vector<LayerData> Simulator::simulate(const string& image_file)
|
||||||
{
|
{
|
||||||
|
typedef LayerData::Type LType;
|
||||||
|
|
||||||
cv::Mat im = cv::imread(image_file, -1);
|
cv::Mat im = cv::imread(image_file, -1);
|
||||||
|
|
||||||
assert(!im.empty());
|
assert(!im.empty());
|
||||||
@@ -41,10 +43,20 @@ vector<Simulator::DType> Simulator::simulate(const string& image_file)
|
|||||||
|
|
||||||
net.Forward();
|
net.Forward();
|
||||||
|
|
||||||
Blob<DType> *output_layer = net.output_blobs()[0];
|
vector<LayerData> result;
|
||||||
const DType *begin = output_layer->cpu_data();
|
|
||||||
const DType *end = begin + output_layer->channels();
|
Blob<DType>* input_layer = net.input_blobs()[0];
|
||||||
vector<DType> result(begin, end);
|
|
||||||
|
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;
|
||||||
|
const auto blob = results[i][0];
|
||||||
|
|
||||||
|
result.emplace_back(names[i], blob->shape(), blob->cpu_data(), LayerData::typeFromString(layers[i]->type()));
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -67,10 +79,6 @@ vector<cv::Mat> Simulator::getWrappedInputLayer()
|
|||||||
}
|
}
|
||||||
|
|
||||||
static cv::Mat fix_channels(const int num_channels, cv::Mat original) {
|
static cv::Mat fix_channels(const int num_channels, cv::Mat original) {
|
||||||
if (num_channels == original.channels()) {
|
|
||||||
return original;
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::Mat converted;
|
cv::Mat converted;
|
||||||
|
|
||||||
if (num_channels == 1 && original.channels() == 3) {
|
if (num_channels == 1 && original.channels() == 3) {
|
||||||
@@ -82,8 +90,8 @@ static cv::Mat fix_channels(const int num_channels, cv::Mat original) {
|
|||||||
} else if (num_channels == 3 && original.channels() == 4) {
|
} else if (num_channels == 3 && original.channels() == 4) {
|
||||||
cv::cvtColor(original, converted, cv::COLOR_BGRA2BGR);
|
cv::cvtColor(original, converted, cv::COLOR_BGRA2BGR);
|
||||||
} else {
|
} else {
|
||||||
// Don't know how to convert.
|
CHECK(num_channels == original.channels()) << "Cannot convert between channel types. ";
|
||||||
abort();
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
return converted;
|
return converted;
|
||||||
|
|||||||
@@ -9,17 +9,18 @@
|
|||||||
#include <opencv2/highgui/highgui.hpp>
|
#include <opencv2/highgui/highgui.hpp>
|
||||||
#include <opencv2/imgproc/imgproc.hpp>
|
#include <opencv2/imgproc/imgproc.hpp>
|
||||||
|
|
||||||
|
#include "utils.hpp"
|
||||||
|
#include "LayerData.hpp"
|
||||||
|
|
||||||
namespace fmri {
|
namespace fmri {
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
class Simulator {
|
class Simulator {
|
||||||
public:
|
public:
|
||||||
typedef float DType;
|
|
||||||
|
|
||||||
Simulator(const string &model_file, const string &weights_file, const string &means_file = "");
|
Simulator(const string &model_file, const string &weights_file, const string &means_file = "");
|
||||||
|
|
||||||
vector<DType> simulate(const string &input_file);
|
vector<LayerData> simulate(const string &input_file);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
caffe::Net<DType> net;
|
caffe::Net<DType> net;
|
||||||
|
|||||||
33
src/main.cpp
33
src/main.cpp
@@ -8,7 +8,7 @@ using namespace std;
|
|||||||
using namespace fmri;
|
using namespace fmri;
|
||||||
|
|
||||||
int main(int argc, char *const argv[]) {
|
int main(int argc, char *const argv[]) {
|
||||||
::google::InitGoogleLogging(argv[0]);
|
google::InitGoogleLogging(argv[0]);
|
||||||
|
|
||||||
Options options = Options::parse(argc, argv);
|
Options options = Options::parse(argc, argv);
|
||||||
vector<string> labels;
|
vector<string> labels;
|
||||||
@@ -18,23 +18,24 @@ int main(int argc, char *const argv[]) {
|
|||||||
|
|
||||||
Simulator simulator(options.model(), options.weights(), options.means());
|
Simulator simulator(options.model(), options.weights(), options.means());
|
||||||
|
|
||||||
for (const auto &image : options.inputs()) {
|
for (const auto &image : options.inputs()) {
|
||||||
cout << "Result for " << image << ":" << endl;
|
const auto res = simulator.simulate(image);
|
||||||
auto res = simulator.simulate(image);
|
LOG(INFO) << "Result for " << image << ":" << endl;
|
||||||
if (!labels.empty()) {
|
|
||||||
auto scores = combine(res, labels);
|
|
||||||
sort(scores.begin(), scores.end(), greater<>());
|
|
||||||
for (unsigned int i = 0; i < scores.size() && i < 5; ++i) {
|
|
||||||
cout << scores[i].first << " " << scores[i].second << endl;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cout << "Best result: " << *(max_element(res.begin(), res.end())) << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
cout << endl;
|
const auto& resultRow = res[res.size() - 1];
|
||||||
}
|
if (!labels.empty()) {
|
||||||
|
vector<DType> weights(resultRow.data(), resultRow.data() + resultRow.numEntries());
|
||||||
|
auto scores = combine(weights, labels);
|
||||||
|
sort(scores.begin(), scores.end(), greater<>());
|
||||||
|
for (unsigned int i = 0; i < scores.size() && i < 5; ++i) {
|
||||||
|
LOG(INFO) << scores[i].first << " " << scores[i].second << endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG(INFO) << "Best result: " << *(resultRow.data(), resultRow.data() + resultRow.numEntries()) << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
::google::ShutdownGoogleLogging();
|
google::ShutdownGoogleLogging();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
namespace fmri
|
namespace fmri
|
||||||
{
|
{
|
||||||
|
typedef float DType;
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
inline T identity(T t) {
|
inline T identity(T t) {
|
||||||
@@ -57,4 +58,4 @@ namespace fmri
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user