Replace getopt with Boost::program_options.
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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[])
|
||||||
{
|
{
|
||||||
Options options;
|
using namespace boost::program_options;
|
||||||
|
|
||||||
char c;
|
try {
|
||||||
|
Options options;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "hm:w:n:l:d:p:")) != -1) {
|
options_description desc("Options");
|
||||||
switch (c) {
|
positional_options_description positionals;
|
||||||
case 'h':
|
positionals.add("input", -1);
|
||||||
show_help(argv[0], 0);
|
|
||||||
|
|
||||||
case 'w':
|
options_description hidden;
|
||||||
check_file(optarg);
|
hidden.add_options()
|
||||||
options.weightsPath = optarg;
|
("input", value<std::vector<std::string>>(&options.inputPaths)->required()->composing());
|
||||||
break;
|
|
||||||
|
|
||||||
case 'n':
|
desc.add_options()
|
||||||
check_file(optarg);
|
("help,h", "Show this help message")
|
||||||
options.modelPath = 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 'm':
|
options_description composed = desc;
|
||||||
check_file(optarg);
|
composed.add(hidden);
|
||||||
options.meansPath = optarg;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'l':
|
variables_map vm;
|
||||||
check_file(optarg);
|
store(command_line_parser(argc, argv).options(composed).positional(positionals).run(), vm);
|
||||||
options.labelsPath = optarg;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'd':
|
if (vm.count("help")) {
|
||||||
options.dumpPath = optarg;
|
std::cout << "Usage: " << argv[0] << " [OPTIONS] [INPUTS]\n\n" << desc << '\n';
|
||||||
break;
|
std::exit(0);
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notify(vm);
|
||||||
|
|
||||||
|
if (vm.count("path-color")) {
|
||||||
|
parse_color(vm["path-color"].as<std::string>().c_str(), options.pathColor_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity checks
|
||||||
|
check_file(options.modelPath);
|
||||||
|
check_file(options.weightsPath);
|
||||||
|
check_file(options.meansPath);
|
||||||
|
std::for_each(options.inputPaths.begin(), options.inputPaths.end(), check_file);
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.weightsPath.empty()) {
|
std::exit(1);
|
||||||
std::cerr << "Weights file is required!\n";
|
|
||||||
show_help(argv[0], 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.modelPath.empty()) {
|
|
||||||
std::cerr << "Model file is required!\n";
|
|
||||||
show_help(argv[0], 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user