@@ -2,16 +2,15 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <glog/logging.h>
|
||||||
#include "Options.hpp"
|
#include "Options.hpp"
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
using namespace fmri;
|
using namespace fmri;
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static void show_help(const char *progname, int exitcode) {
|
[[noreturn]] static void show_help(const char *progname, int exitcode)
|
||||||
cerr << "Usage: " << progname << " -m MODEL -w WEIGHTS INPUTS..." << endl
|
{
|
||||||
<< endl
|
std::cerr << "Usage: " << progname << " -m MODEL -w WEIGHTS INPUTS...\n\n"
|
||||||
<< R"END(Simulate the specified network on the specified inputs.
|
<< R"END(Simulate the specified network on the specified inputs.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h show this message
|
-h show this message
|
||||||
@@ -19,129 +18,161 @@ Options:
|
|||||||
-w (required) the trained weights
|
-w (required) the trained weights
|
||||||
-m means file. Will be substracted from input if available.
|
-m means file. Will be substracted from input if available.
|
||||||
-l labels file. Will be used to print prediction labels if available.
|
-l labels file. Will be used to print prediction labels if available.
|
||||||
-d Image dump dir. Will be filled with PNG images of intermediate results.)END" << endl;
|
-d Image dump dir. Will be filled with PNG images of intermediate results.
|
||||||
|
-p Image path color in hex format (#RRGGBB or #RRGGBBAA))END" << std::endl;
|
||||||
|
|
||||||
exit(exitcode);
|
std::exit(exitcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void check_file(const char *filename) {
|
static void check_file(const char *filename)
|
||||||
|
{
|
||||||
if (access(filename, R_OK) != 0) {
|
if (access(filename, R_OK) != 0) {
|
||||||
perror(filename);
|
perror(filename);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Options Options::parse(const int argc, char *const argv[]) {
|
/**
|
||||||
string model;
|
* Parse a color string into a color array.
|
||||||
string weights;
|
*
|
||||||
string means;
|
* This function may terminate the program on a partial match.
|
||||||
string dump;
|
*
|
||||||
string labels;
|
* @param input
|
||||||
|
* @param targetColor
|
||||||
|
* @return true if the read was successful.
|
||||||
|
*/
|
||||||
|
static bool parse_color(const char *input, std::array<float, 4> &targetColor)
|
||||||
|
{
|
||||||
|
if (input[0] == '#') {
|
||||||
|
// Attempt to parse #RRGGBBAA
|
||||||
|
std::array<unsigned int, 4> colorBuf;
|
||||||
|
const int result = std::sscanf(input, "#%02x%02x%02x%02x", &colorBuf[0], &colorBuf[1], &colorBuf[2],
|
||||||
|
&colorBuf[3]);
|
||||||
|
CHECK_GE(result, 3) << "Invalid color HEX format, need at least 3 hex pairs, got " << result << "\n";
|
||||||
|
|
||||||
|
std::transform(colorBuf.begin(), colorBuf.end(), targetColor.begin(), [](auto x) { return x / 255.f; });
|
||||||
|
|
||||||
|
// Optionally, patch the alpha channel if not specified
|
||||||
|
if (result == 3) targetColor[3] = 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "Unknown color format used (" << input << ")\n";
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Options Options::parse(const int argc, char *const argv[])
|
||||||
|
{
|
||||||
|
Options options;
|
||||||
|
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "hm:w:n:l:d:")) != -1) {
|
while ((c = getopt(argc, argv, "hm:w:n:l:d:p:")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
show_help(argv[0], 0);
|
show_help(argv[0], 0);
|
||||||
break;
|
|
||||||
|
|
||||||
case 'w':
|
case 'w':
|
||||||
check_file(optarg);
|
check_file(optarg);
|
||||||
weights = optarg;
|
options.weightsPath = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'n':
|
case 'n':
|
||||||
check_file(optarg);
|
check_file(optarg);
|
||||||
model = optarg;
|
options.modelPath = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'm':
|
case 'm':
|
||||||
check_file(optarg);
|
check_file(optarg);
|
||||||
means = optarg;
|
options.meansPath = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
check_file(optarg);
|
check_file(optarg);
|
||||||
labels = optarg;
|
options.labelsPath = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'd':
|
case 'd':
|
||||||
dump = optarg;
|
options.dumpPath = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'p':
|
||||||
|
if (!parse_color(optarg, options.pathColor_)) {
|
||||||
|
show_help(argv[0], 1);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
show_help(argv[0], 1);
|
show_help(argv[0], 1);
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
cerr << "Unhandled option: " << c << endl;
|
std::cerr << "Unhandled option: " << c << std::endl;
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (weights.empty()) {
|
if (options.weightsPath.empty()) {
|
||||||
cerr << "Weights file is required!" << endl;
|
std::cerr << "Weights file is required!\n";
|
||||||
show_help(argv[0], 1);
|
show_help(argv[0], 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.empty()) {
|
if (options.modelPath.empty()) {
|
||||||
cerr << "Model file is required!" << endl;
|
std::cerr << "Model file is required!\n";
|
||||||
show_help(argv[0], 1);
|
show_help(argv[0], 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
for_each(argv + optind, argv + argc, check_file);
|
std::for_each(argv + optind, argv + argc, check_file);
|
||||||
|
options.inputPaths.insert(options.inputPaths.end(), argv + optind, argv + argc);
|
||||||
vector<string> inputs(argv + optind, argv + argc);
|
if (options.inputPaths.empty()) {
|
||||||
if (inputs.empty()) {
|
std::cerr << "No inputs specified\n";
|
||||||
cerr << "No inputs specified" << endl;
|
|
||||||
show_help(argv[0], 1);
|
show_help(argv[0], 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Options(move(model), move(weights), move(means), move(labels), move(dump), move(inputs));
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
Options::Options(string &&model, string &&weights, string&& means, string&& labels, string&& dumpPath, vector<string> &&inputs) noexcept:
|
const string &Options::model() const
|
||||||
modelPath(move(model)),
|
|
||||||
weightsPath(move(weights)),
|
|
||||||
meansPath(means),
|
|
||||||
labelsPath(labels),
|
|
||||||
dumpPath(dumpPath),
|
|
||||||
inputPaths(move(inputs))
|
|
||||||
{
|
{
|
||||||
}
|
|
||||||
|
|
||||||
const string& Options::model() const {
|
|
||||||
return modelPath;
|
return modelPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
const string& Options::weights() const {
|
const string &Options::weights() const
|
||||||
|
{
|
||||||
return weightsPath;
|
return weightsPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
const vector<string>& Options::inputs() const {
|
const vector<string> &Options::inputs() const
|
||||||
|
{
|
||||||
return inputPaths;
|
return inputPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
const string& Options::means() const
|
const string &Options::means() const
|
||||||
{
|
{
|
||||||
return meansPath;
|
return meansPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<vector<string>> Options::labels() const
|
std::optional<vector<string>> Options::labels() const
|
||||||
{
|
{
|
||||||
if (labelsPath.empty()) {
|
if (!labelsPath) {
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
} else {
|
} else {
|
||||||
return read_vector<string>(labelsPath);
|
return read_vector<std::string>(labelsPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<PNGDumper> Options::imageDumper() const
|
std::optional<PNGDumper> Options::imageDumper() const
|
||||||
{
|
{
|
||||||
if (dumpPath.empty()) {
|
if (!dumpPath) {
|
||||||
return nullopt;
|
return std::nullopt;
|
||||||
} else {
|
} else {
|
||||||
return move(PNGDumper(dumpPath));
|
return PNGDumper(dumpPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Options::Options() noexcept :
|
||||||
|
pathColor_({1, 1, 1, 0.1}),
|
||||||
|
labelsPath(nullptr),
|
||||||
|
dumpPath(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,13 +24,14 @@ namespace fmri {
|
|||||||
const vector<string>& inputs() const;
|
const vector<string>& inputs() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const string modelPath;
|
std::array<float, 4> pathColor_;
|
||||||
const string weightsPath;
|
string modelPath;
|
||||||
const string meansPath;
|
string weightsPath;
|
||||||
const string labelsPath;
|
string meansPath;
|
||||||
const string dumpPath;
|
char const * labelsPath;
|
||||||
const vector<string> inputPaths;
|
char const * dumpPath;
|
||||||
|
vector<string> inputPaths;
|
||||||
|
|
||||||
Options(string &&, string &&, string&&, string&&, string&&, vector<string> &&) noexcept;
|
Options() noexcept;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,19 @@ auto file_filter_for_extension(std::string_view extension)
|
|||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char * color_string(Gtk::ColorButton &button)
|
||||||
|
{
|
||||||
|
char* buffer = new char[2 * 4 + 2]; // 2 per channel, plus #, plus null byte
|
||||||
|
auto color = button.get_rgba();
|
||||||
|
|
||||||
|
// Note: Gdk stores its RGBA values in range 0..66535 instead of 0..255, so need to scale.
|
||||||
|
sprintf(buffer, "#%02x%02x%02x%02x", color.get_red_u() >> 8,
|
||||||
|
color.get_green_u() >> 8, color.get_blue_u() >> 8, color.get_alpha_u() >> 8);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap string into a dynamically allocated c-string.
|
* Wrap string into a dynamically allocated c-string.
|
||||||
*
|
*
|
||||||
@@ -55,6 +68,8 @@ public:
|
|||||||
~Launcher() override = default;
|
~Launcher() override = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
int rows;
|
||||||
|
|
||||||
Gtk::Grid grid;
|
Gtk::Grid grid;
|
||||||
Gtk::FileChooserButton fmriChooser;
|
Gtk::FileChooserButton fmriChooser;
|
||||||
Gtk::FileChooserButton modelChooser;
|
Gtk::FileChooserButton modelChooser;
|
||||||
@@ -62,6 +77,7 @@ private:
|
|||||||
Gtk::FileChooserButton labelChooser;
|
Gtk::FileChooserButton labelChooser;
|
||||||
Gtk::FileChooserButton meansChooser;
|
Gtk::FileChooserButton meansChooser;
|
||||||
Gtk::FileChooserButton inputChooser;
|
Gtk::FileChooserButton inputChooser;
|
||||||
|
Gtk::ColorButton pathColor;
|
||||||
Gtk::Button startButton;
|
Gtk::Button startButton;
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
@@ -69,42 +85,47 @@ private:
|
|||||||
std::vector<std::string> getInputFiles();
|
std::vector<std::string> getInputFiles();
|
||||||
Gtk::Label* getManagedLabel(const std::string& contents);
|
Gtk::Label* getManagedLabel(const std::string& contents);
|
||||||
void findExecutable();
|
void findExecutable();
|
||||||
|
void addRowWithLabel(const std::string& label, Gtk::Widget& widget);
|
||||||
};
|
};
|
||||||
|
|
||||||
Launcher::Launcher()
|
Launcher::Launcher()
|
||||||
:
|
:
|
||||||
Gtk::Window(),
|
Gtk::Window(),
|
||||||
|
rows(0),
|
||||||
fmriChooser("Select FMRI executable"),
|
fmriChooser("Select FMRI executable"),
|
||||||
modelChooser("Select caffe model prototxt"),
|
modelChooser("Select caffe model prototxt"),
|
||||||
weightsChooser("Select caffe model weights"),
|
weightsChooser("Select caffe model weights"),
|
||||||
labelChooser("Select label text file"),
|
labelChooser("Select label text file"),
|
||||||
meansChooser("Select means file"),
|
meansChooser("Select means file"),
|
||||||
inputChooser("Select input directory", Gtk::FileChooserAction::FILE_CHOOSER_ACTION_SELECT_FOLDER),
|
inputChooser("Select input directory", Gtk::FileChooserAction::FILE_CHOOSER_ACTION_SELECT_FOLDER),
|
||||||
|
pathColor(Gdk::RGBA("rgba(255, 255, 255, 0.1)")),
|
||||||
startButton("Start FMRI")
|
startButton("Start FMRI")
|
||||||
{
|
{
|
||||||
set_size_request(400, -1);
|
set_default_size(400, -1);
|
||||||
|
//set_size_request(400, -1);
|
||||||
add(grid);
|
add(grid);
|
||||||
|
|
||||||
|
// Configure all widgets
|
||||||
|
fmriChooser.set_hexpand(true);
|
||||||
|
findExecutable();
|
||||||
|
modelChooser.add_filter(file_filter_for_extension("prototxt"));
|
||||||
|
weightsChooser.add_filter(file_filter_for_extension("caffemodel"));
|
||||||
|
labelChooser.add_filter(file_filter_for_extension("txt"));
|
||||||
|
meansChooser.add_filter(file_filter_for_extension("binaryproto"));
|
||||||
|
pathColor.set_use_alpha(true);
|
||||||
|
|
||||||
|
// Configure grid display options
|
||||||
grid.set_row_spacing(2);
|
grid.set_row_spacing(2);
|
||||||
grid.set_column_spacing(2);
|
grid.set_column_spacing(2);
|
||||||
findExecutable();
|
|
||||||
fmriChooser.set_hexpand(true);
|
// Attach widgets to the grid
|
||||||
grid.attach(fmriChooser, 1, 0, 1, 1);
|
addRowWithLabel("FMRI executable", fmriChooser);
|
||||||
grid.attach_next_to(*getManagedLabel("FMRI executable"), fmriChooser, Gtk::PositionType::POS_LEFT, 1, 1);
|
addRowWithLabel("Model", modelChooser);
|
||||||
grid.attach(modelChooser, 1, 1, 1, 1);
|
addRowWithLabel("Weights", weightsChooser);
|
||||||
modelChooser.add_filter(file_filter_for_extension("prototxt"));
|
addRowWithLabel("Labels (optional)", labelChooser);
|
||||||
grid.attach_next_to(*getManagedLabel("Model"), modelChooser, Gtk::PositionType::POS_LEFT, 1, 1);
|
addRowWithLabel("Input directory", inputChooser);
|
||||||
grid.attach(weightsChooser, 1, 2, 1, 1);
|
addRowWithLabel("Means (optional)", meansChooser);
|
||||||
weightsChooser.add_filter(file_filter_for_extension("caffemodel"));
|
addRowWithLabel("Path color", pathColor);
|
||||||
grid.attach_next_to(*getManagedLabel("Weights"), weightsChooser, Gtk::PositionType::POS_LEFT, 1, 1);
|
|
||||||
grid.attach(labelChooser, 1, 3, 1, 1);
|
|
||||||
labelChooser.add_filter(file_filter_for_extension("txt"));
|
|
||||||
grid.attach_next_to(*getManagedLabel("Labels (optional)"), labelChooser, Gtk::PositionType::POS_LEFT, 1, 1);
|
|
||||||
grid.attach(inputChooser, 1, 4, 1, 1);
|
|
||||||
grid.attach_next_to(*getManagedLabel("Input directory"), inputChooser, Gtk::PositionType::POS_LEFT, 1, 1);
|
|
||||||
grid.attach(meansChooser, 1, 5, 1, 1);
|
|
||||||
meansChooser.add_filter(file_filter_for_extension("binaryproto"));
|
|
||||||
grid.attach_next_to(*getManagedLabel("Means (optional)"), meansChooser, Gtk::PositionType::POS_LEFT, 1, 1);
|
|
||||||
|
|
||||||
startButton.signal_clicked().connect(sigc::mem_fun(*this, &Launcher::start));
|
startButton.signal_clicked().connect(sigc::mem_fun(*this, &Launcher::start));
|
||||||
grid.attach_next_to(startButton, Gtk::PositionType::POS_BOTTOM, 2, 1);
|
grid.attach_next_to(startButton, Gtk::PositionType::POS_BOTTOM, 2, 1);
|
||||||
@@ -137,6 +158,8 @@ void Launcher::start()
|
|||||||
wrap_string(network),
|
wrap_string(network),
|
||||||
wrap_string("-w"),
|
wrap_string("-w"),
|
||||||
wrap_string(weights),
|
wrap_string(weights),
|
||||||
|
wrap_string("-p"),
|
||||||
|
color_string(pathColor),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (labelChooser.get_file()) {
|
if (labelChooser.get_file()) {
|
||||||
@@ -222,6 +245,13 @@ void Launcher::findExecutable()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Launcher::addRowWithLabel(const std::string &label, Gtk::Widget &widget)
|
||||||
|
{
|
||||||
|
int currentRow = rows++;
|
||||||
|
grid.attach(widget, 1, currentRow, 1, 1);
|
||||||
|
grid.attach_next_to(*getManagedLabel(label), widget, Gtk::PositionType::POS_LEFT, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
auto app = Gtk::Application::create(argc, argv);
|
auto app = Gtk::Application::create(argc, argv);
|
||||||
|
|||||||
Reference in New Issue
Block a user