Replace getopt with Boost::program_options.

This commit is contained in:
2018-04-05 14:22:23 +02:00
parent ffea333609
commit 68be06f378
3 changed files with 69 additions and 92 deletions

View File

@@ -23,12 +23,14 @@ find_package(Caffe REQUIRED)
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
find_package(GLUT REQUIRED) find_package(GLUT REQUIRED)
find_package(png++ REQUIRED) find_package(png++ REQUIRED)
find_package(Boost REQUIRED COMPONENTS filesystem program_options)
target_link_libraries(fmri PUBLIC target_link_libraries(fmri PUBLIC
Caffe::Caffe Caffe::Caffe
GLUT::GLUT GLUT::GLUT
OpenGL::GLU OpenGL::GLU
png++::png++ png++::png++
Boost::program_options
) )
target_include_directories(fmri PUBLIC target_include_directories(fmri PUBLIC
@@ -46,7 +48,6 @@ target_link_libraries(deinplace Caffe::Caffe protobuf::libprotobuf)
# Build isntructions for the launcher tool # Build isntructions for the launcher tool
find_package(GTK3 REQUIRED COMPONENTS gtk gtkmm) find_package(GTK3 REQUIRED COMPONENTS gtk gtkmm)
find_package(Boost REQUIRED COMPONENTS filesystem)
add_executable(fmri-launcher src/tools/launcher.cpp) add_executable(fmri-launcher src/tools/launcher.cpp)
target_compile_options(fmri-launcher PRIVATE "-Wall" "-Wextra" "-pedantic") target_compile_options(fmri-launcher PRIVATE "-Wall" "-Wextra" "-pedantic")
target_link_libraries(fmri-launcher GTK3::gtkmm Boost::filesystem GTK3::gtk) target_link_libraries(fmri-launcher GTK3::gtkmm Boost::filesystem GTK3::gtk)

View File

@@ -1,34 +1,18 @@
#include <algorithm> #include <algorithm>
#include <cstdio>
#include <iostream> #include <iostream>
#include <unistd.h> #include <boost/program_options.hpp>
#include <glog/logging.h> #include <glog/logging.h>
#include "Options.hpp" #include "Options.hpp"
using namespace fmri; using namespace fmri;
[[noreturn]] static void show_help(const char *progname, int exitcode) static void check_file(const std::string& filename)
{ {
std::cerr << "Usage: " << progname << " -m MODEL -w WEIGHTS INPUTS...\n\n" if (access(filename.c_str(), R_OK) != 0) {
<< R"END(Simulate the specified network on the specified inputs. char errorBuf[1024];
std::snprintf(errorBuf, sizeof(errorBuf) - 1, "%s: %s", filename.c_str(), std::strerror(errno));
Options: throw std::invalid_argument(errorBuf);
-h show this message
-n (required) the model file to simulate
-w (required) the trained weights
-m means file. Will be substracted from input 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.
-p Image path color in hex format (#RRGGBB or #RRGGBBAA))END" << std::endl;
std::exit(exitcode);
}
static void check_file(const char *filename)
{
if (access(filename, R_OK) != 0) {
perror(filename);
exit(1);
} }
} }
@@ -41,95 +25,89 @@ static void check_file(const char *filename)
* @param targetColor * @param targetColor
* @return true if the read was successful. * @return true if the read was successful.
*/ */
static bool parse_color(const char *input, Color &targetColor) static void parse_color(const char *input, Color &targetColor)
{ {
if (input[0] == '#') { if (input[0] == '#') {
// Attempt to parse #RRGGBBAA // Attempt to parse #RRGGBBAA
std::array<unsigned int, 4> colorBuf; std::array<unsigned int, 4> colorBuf;
const int result = std::sscanf(input, "#%02x%02x%02x%02x", &colorBuf[0], &colorBuf[1], &colorBuf[2], const int result = std::sscanf(input, "#%02x%02x%02x%02x", &colorBuf[0], &colorBuf[1], &colorBuf[2],
&colorBuf[3]); &colorBuf[3]);
CHECK_GE(result, 3) << "Invalid color HEX format, need at least 3 hex pairs, got " << result << "\n"; if (result < 3) {
throw std::invalid_argument("Invalid color HEX format, need at least 3 hex pairs");
}
std::transform(colorBuf.begin(), colorBuf.end(), targetColor.begin(), [](auto x) { return x / 255.f; }); std::transform(colorBuf.begin(), colorBuf.end(), targetColor.begin(), [](auto x) { return x / 255.f; });
// Optionally, patch the alpha channel if not specified // Optionally, patch the alpha channel if not specified
if (result == 3) targetColor[3] = 1; if (result == 3) targetColor[3] = 1;
return true; return;
} }
std::cerr << "Unknown color format used (" << input << ")\n"; char errorBuf[1024];
std::snprintf(errorBuf, sizeof(errorBuf) - 1, "Unknown color value: %s", input);
return false; throw std::invalid_argument(errorBuf);
} }
Options Options::parse(const int argc, char *const argv[]) Options Options::parse(const int argc, char *const argv[])
{ {
using namespace boost::program_options;
try {
Options options; Options options;
char c; options_description desc("Options");
positional_options_description positionals;
positionals.add("input", -1);
while ((c = getopt(argc, argv, "hm:w:n:l:d:p:")) != -1) { options_description hidden;
switch (c) { hidden.add_options()
case 'h': ("input", value<std::vector<std::string>>(&options.inputPaths)->required()->composing());
show_help(argv[0], 0);
case 'w': desc.add_options()
check_file(optarg); ("help,h", "Show this help message")
options.weightsPath = optarg; ("weights,w", value<std::string>(&options.weightsPath)->required(), "weights file for the network")
break; ("network,n", value<std::string>(&options.modelPath)->required(), "caffe model file for the network")
("labels,l", value<std::string>(&options.labelsPath), "labels file")
("means,m", value<std::string>(&options.meansPath), "means file")
("path-color,p", value<std::string>(), "color for paths")
("dump,d", value<std::string>(&options.dumpPath), "dump convolutional images in this directory");
case 'n': options_description composed = desc;
check_file(optarg); composed.add(hidden);
options.modelPath = optarg;
break;
case 'm': variables_map vm;
check_file(optarg); store(command_line_parser(argc, argv).options(composed).positional(positionals).run(), vm);
options.meansPath = optarg;
break;
case 'l': if (vm.count("help")) {
check_file(optarg); std::cout << "Usage: " << argv[0] << " [OPTIONS] [INPUTS]\n\n" << desc << '\n';
options.labelsPath = optarg; std::exit(0);
break;
case 'd':
options.dumpPath = optarg;
break;
case 'p':
if (!parse_color(optarg, options.pathColor_)) {
show_help(argv[0], 1);
}
break;
case '?':
show_help(argv[0], 1);
default:
std::cerr << "Unhandled option: " << c << std::endl;
abort();
}
} }
if (options.weightsPath.empty()) { notify(vm);
std::cerr << "Weights file is required!\n";
show_help(argv[0], 1); if (vm.count("path-color")) {
parse_color(vm["path-color"].as<std::string>().c_str(), options.pathColor_);
} }
if (options.modelPath.empty()) { // Sanity checks
std::cerr << "Model file is required!\n"; check_file(options.modelPath);
show_help(argv[0], 1); check_file(options.weightsPath);
} check_file(options.meansPath);
std::for_each(options.inputPaths.begin(), options.inputPaths.end(), check_file);
std::for_each(argv + optind, argv + argc, check_file);
options.inputPaths.insert(options.inputPaths.end(), argv + optind, argv + argc);
if (options.inputPaths.empty()) {
std::cerr << "No inputs specified\n";
show_help(argv[0], 1);
}
return options; return options;
} catch (required_option& e) {
std::cerr << e.get_option_name() << std::endl;
if (e.get_option_name() == "--input") {
std::cerr << "No input files specified" << std::endl;
} else {
std::cerr << e.what() << std::endl;
}
} catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
std::exit(1);
} }
const string &Options::model() const const string &Options::model() const
@@ -154,7 +132,7 @@ const string &Options::means() const
std::optional<vector<string>> Options::labels() const std::optional<vector<string>> Options::labels() const
{ {
if (!labelsPath) { if (labelsPath.empty()) {
return std::nullopt; return std::nullopt;
} else { } else {
return read_vector<std::string>(labelsPath); return read_vector<std::string>(labelsPath);
@@ -163,7 +141,7 @@ std::optional<vector<string>> Options::labels() const
std::optional<PNGDumper> Options::imageDumper() const std::optional<PNGDumper> Options::imageDumper() const
{ {
if (!dumpPath) { if (dumpPath.empty()) {
return std::nullopt; return std::nullopt;
} else { } else {
return PNGDumper(dumpPath); return PNGDumper(dumpPath);
@@ -171,9 +149,7 @@ std::optional<PNGDumper> Options::imageDumper() const
} }
Options::Options() noexcept : Options::Options() noexcept :
pathColor_({1, 1, 1, 0.1}), pathColor_({1, 1, 1, 0.1})
labelsPath(nullptr),
dumpPath(nullptr)
{ {
} }

View File

@@ -30,8 +30,8 @@ namespace fmri {
string modelPath; string modelPath;
string weightsPath; string weightsPath;
string meansPath; string meansPath;
char const * labelsPath; string labelsPath;
char const * dumpPath; string dumpPath;
vector<string> inputPaths; vector<string> inputPaths;
Options() noexcept; Options() noexcept;