Implement a color passing function for various options.

Refs #1.
This commit is contained in:
2018-04-03 16:15:08 +02:00
parent d76c067489
commit 62153d9c6d
3 changed files with 142 additions and 80 deletions

View File

@@ -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)
{
}

View File

@@ -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;
}; };
} }

View File

@@ -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);