openmvg中cmd模块解析

---恢复内容开始---

在openmvg库中,定义了一个CmdLine类来定义例程的输入参数格式。源文件为.\openMVG\src\third_party\cmdLine\cmdLine.h。

先举个例子来说明一般输入参数格式,选用.\openMVG\src\software\SfM\main_SfMInit_ImageListing.cpp作为例程,对应的可执行文件为openMVG_main_SfMInit_ImageListing,在官网上(https://openmvg.readthedocs.io/en/latest/software/SfM/SfM/#)有该例程用法的介绍:

// Example(3种参数输入方式)
$openMVG_main_SfMInit_ImageListing --imageDirectory images --sensorWidthDatabase images/sensor_width_camera_database.txt --outputDirectory matches

$openMVG_main_SfMInit_ImageListing -i images -d images/sensor_width_camera_database.txt -o matches

$openMVG_main_SfMInit_ImageListing --imageDirectory=images --sensorWidthDatabase=images/sensor_width_camera_database.txt --outputDirectory=matches

其中第一种“--”后面接着的是长名标识符(longName如imageDirectory),images为对应参数

第二种“-”后面是短标识符(c如i,d,o等)

下面来分析与这3种输入参数格式相关的源代码。

我们先来看其中的一个类Option,它用于存储参数标识符(上面的-d,-i,-o等)与对应的长标识名(imageDirectory等),比如某个Option类种存储着-d和sensorWidthDatabase ,另一个Option类存储着-i和imageDirectory 。类的定义如下:

/// 类中包含3个数据成员,该类为父类,有两个子类(后面介绍)
class Option {
public:
    char c; ///短标识符:i,d,o等
    bool used; ///用于说明该参数是否已经被检测到,在解析输入参数时使用
    std::string longName; /// 长名标识符:imageDirectory等

    Option(char d, std::string name)    //构造函数
        : c(d), used(false), longName(name) {}
    virtual ~Option() = default;
    virtual bool check(int& argc, char* argv[]) = 0; ///检查是否存在有对应的符合格式的参数存在,并解析,具体解析见子类
    virtual Option* clone() const = 0; ///< Copy
};

OptionField类,是Option的一个子类

template <class T>
class OptionField : public Option {
public:
    //构造函数c,name分别对应父类Option种的c,longName;field对应参数比如例子中的输入参数images/sensor_width_camera_database.txt等
    //因为输入参数类型不固定,可能是字符串也可能是数字,所以采用模板类
    OptionField(char c, T& field, std::string name = "")    
        : Option(c, name), _field(field) {}
    /// 查找正确的输入参数
    bool check(int& argc, char* argv[]) override {    //这里的argc,argv形参,并不与主程序中的argc,argv等同
        std::string param; int arg = 0;
        if (std::string("-") + c == argv[0] ||
            (!longName.empty() && std::string("--") + longName == argv[0])) {    //对应输入参数格式为:-i ImageDataset_SceauxCastle/images
            if (argc <= 1)                                                        //                或:--imageDirectory ImageDataset_SceauxCastle/images
                throw std::string("Option ")
                + argv[0] + " requires argument";
            param = argv[1]; arg = 2;                                            //将ImageDataset_SceauxCastle/images赋值給param
        }
        else if (std::string(argv[0]).find(std::string("-") + c) == 0) {        //对应输入参数格式为:-iImageDataset_SceauxCastle/images
            param = argv[0] + 2; arg = 1;                                        //将ImageDataset_SceauxCastle/images赋值給param
        }
        else if (!longName.empty() &&                                            //对应输入参数格式为:--imageDirectory=ImageDataset_SceauxCastle/images
            std::string(argv[0]).find(std::string("--") + longName + '=') == 0) {
            size_t size = (std::string("--") + longName + '=').size();
            param = std::string(argv[0]).substr(size); arg = 1;                    //将ImageDataset_SceauxCastle/images赋值給param
        }
        if (arg>0) {    //arg>0代表检查到了符合格式的输入参数
            if (!read_param(param))    //读入检测到的参数,如果不成功,抛出异常
                throw std::string("Unable to interpret ")
                + param + " as argument of " + argv[0];
            used = true;            //该option对象对应的参数已经检测到
            std::rotate(argv, argv + arg, argv + argc); //把从参数argv到argv + argc之间的参数序列看成一个圆,对他们进行旋转,旋转后的圆的第一个元素为argv + arg
                                                        //作用就是将检测到的参数放在输入参数序列后面
                                                        //rotate函数的用法详细介绍见:http://c.biancheng.net/view/609.html
            /*
            比如原始序列为:openMVG_main_SfMInit_ImageListing -i images -d images/sensor_width_camera_database.txt -o matches
            假如argv对应-i  argv + argc对应images/sensor_width_camera_database.txt   arg=2
            -i images -d images/sensor_width_camera_database.txt构成待旋转的圆,argv + arg对应的-d为旋转后圆的第一个元素
            那么rotate后的新序列为openMVG_main_SfMInit_ImageListing -d images/sensor_width_camera_database.txt -i images -o matches
            */
            argc -= arg;
            return true;
        }
        return false;
    }
    /// 将读到的参数param赋值给_field;param为string型,_field类型在OptionField对象中预先定义了,见于make_option函数,也在cmdLine.h文件中
    bool read_param(const std::string& param) {
        std::stringstream str(param); char unused;
        return !((str >> _field).fail() || !(str >> unused).fail());
    }
    /// Copy
    Option* clone() const override {
        return new OptionField<T>(c, _field, longName);
    }
private:
    T& _field; 
};
View Code

另一个类OptionSwitch用的少,用于是否选用某种方法或者模式,比如:输入参数中如果有-u或者--use_sift参数,就使用sift方法,如果没有就不适用,只有是否两种情况;具体类定义就不做介绍了

Option类check用于匹配单个输入参数,CmdLine类实现所有的输入参数与所有的Option对象进行匹配

/// Command line parsing
class CmdLine {
    std::vector<Option*> opts;    //opts中包含所有的必须的或者可选的参数对应的标识符;即该程序需要输入哪些参数
public:
    /// Destructor
    ~CmdLine() {
        std::vector<Option*>::iterator it = opts.begin();
        for (; it != opts.end(); ++it)
            delete *it;
    }
    /// Add an option
    void add(const Option& opt) {    //在Option对象向量中添加Option对象
        opts.push_back(opt.clone());
    }
    /// ************主要的成员函数************
    /// 实现输入参数与程序需求的参数进行匹配
    void process(int& argc, char* argv[]) {
        std::vector<Option*>::iterator it = opts.begin();
        for (; it != opts.end(); ++it)
            (*it)->used = false;
        for (int i = 1; i<argc;) {
            if (std::string("--") == argv[i]) { // "--" 为参数结尾标志,不知道这个有什么用
                std::rotate(argv + i, argv + i + 1, argv + argc);    //旋转,使"--"为序列结尾
                --argc;
                break;
            }
            bool found = false; // Find option
            for (it = opts.begin(); it != opts.end(); ++it) {    //将预先定义的Option类的各个对象分别与输入参数进行匹配
                int n = argc - i;
                found = (*it)->check(n, argv + i);    //argv + i(argv[1])对应的参数与当前Option对象匹配
                if (found) {    
                    argc = n + i;    //这行也不知道有什么用
                    break;
                }
            }
            if (!found) { //如果没找到合适的匹配,但是输入的参数开头为"_",则抛出异常未识别标识符
                if (std::string(argv[i]).size()>1 && argv[i][0] == '-') {
                    std::istringstream str(argv[i]);
                    float v;
                    if (!(str >> v).eof())
                        throw std::string("Unrecognized option ") + argv[i];
                }
                ++i;
            }
        }
    }
    /// Was the option used in last parsing?
    bool used(char c) const {
        std::vector<Option*>::const_iterator it = opts.begin();
        for (; it != opts.end(); ++it)
            if ((*it)->c == c)
                return (*it)->used;
        assert(false); // Called with non-existent option, probably a bug
        return false;
    }
};
View Code

在openmvg库中例程读入参数的主要流程如下:

#include"cmdLine.h"
#include <Eigen/Core>
//#include <Eigen/Dense>
//#include <Eigen/SparseCore>
//#include <Eigen/StdVector>
using namespace std;
using namespace Eigen;

enum EINTRINSIC
{
    PINHOLE_CAMERA_START = 0,
    PINHOLE_CAMERA,         // No distortion
    PINHOLE_CAMERA_RADIAL1, // radial distortion K1
    PINHOLE_CAMERA_RADIAL3, // radial distortion K1,K2,K3
    PINHOLE_CAMERA_BROWN, // radial distortion K1,K2,K3, tangential distortion T1,T2
    PINHOLE_CAMERA_FISHEYE, // a simple Fish-eye distortion model with 4 distortion coefficients
    PINHOLE_CAMERA_END,
    CAMERA_SPHERICAL = PINHOLE_CAMERA_END + 1
};

using Vec3 = Eigen::Vector3d;

inline bool split
(
    const std::string & rhs,
    const char delim,
    std::vector<std::string> & items
)
{
    items.clear();
    std::stringstream ss(rhs);
    std::string item;
    while (std::getline(ss, item, delim))
    {
        items.emplace_back(item);
    }

    // return true if the delimiter is present in the input string
    return rhs.find(delim) != std::string::npos;
}

/// Check that Kmatrix is a string like "f;0;ppx;0;f;ppy;0;0;1"
/// With f,ppx,ppy as valid numerical value
bool checkIntrinsicStringValidity(const std::string & Kmatrix, double & focal, double & ppx, double & ppy)
{
    std::vector<std::string> vec_str;
    split(Kmatrix, ';', vec_str);
    if (vec_str.size() != 9) {
        std::cerr << "\n Missing ';' character" << std::endl;
        return false;
    }
    // Check that all K matrix value are valid numbers
    for (size_t i = 0; i < vec_str.size(); ++i) {
        double readvalue = 0.0;
        std::stringstream ss;
        ss.str(vec_str[i]);
        if (!(ss >> readvalue)) {
            std::cerr << "\n Used an invalid not a number character" << std::endl;
            return false;
        }
        if (i == 0) focal = readvalue;
        if (i == 2) ppx = readvalue;
        if (i == 5) ppy = readvalue;
    }
    return true;
}

int main()
{
    int argc = 5;
    char* argv[] = { "cmdline_study","-iimages", "-dsensor_width_camera_database.txt", "-ocmdline_study_outputdir", "-k2905.88; 0; 1416; \
        0; 2905.88; 1064; \
        0; 0; 1" };
    //1.定义一个CmdLine类
    CmdLine cmd;

    std::string sImageDir,    //2.定义输入参数对应数据类型,有的为string,有的为double等
        sfileDatabase,
        sOutputDir,
        sKmatrix;
    std::string sPriorWeights;
    std::pair<bool, Vec3> prior_w_info(false, Vec3(1.0, 1.0, 1.0));
    int i_User_camera_model = PINHOLE_CAMERA_RADIAL3;
    bool b_Group_camera_model = true;
    int i_GPS_XYZ_method = 0;
    double focal_pixels = -1.0;
    //3.在CmdLine类中添加option对象(即需要输入的参数标识符)
    cmd.add(make_option('i', sImageDir, "imageDirectory"));
    cmd.add(make_option('d', sfileDatabase, "sensorWidthDatabase"));
    cmd.add(make_option('o', sOutputDir, "outputDirectory"));
    cmd.add(make_option('f', focal_pixels, "focal"));
    cmd.add(make_option('k', sKmatrix, "intrinsics"));
    cmd.add(make_option('c', i_User_camera_model, "camera_model"));
    cmd.add(make_option('g', b_Group_camera_model, "group_camera_model"));
    cmd.add(make_switch('P', "use_pose_prior"));
    cmd.add(make_option('W', sPriorWeights, "prior_weights"));
    cmd.add(make_option('m', i_GPS_XYZ_method, "gps_to_xyz_method"));
    //4.进行输入参数与标识符匹配(CmdLine::process())
    try {
        if (argc == 1) throw std::string("Invalid command line parameter.");
        cmd.process(argc, argv);
    }
    catch (const std::string& s) {
        std::cerr << "Usage: " << argv[0] << '\n'
            << "[-i|--imageDirectory]\n"
            << "[-d|--sensorWidthDatabase]\n"
            << "[-o|--outputDirectory]\n"
            << "[-f|--focal] (pixels)\n"
            << "[-k|--intrinsics] Kmatrix: \"f;0;ppx;0;f;ppy;0;0;1\"\n"
            << "[-c|--camera_model] Camera model type:\n"
            << "\t" << static_cast<int>(PINHOLE_CAMERA) << ": Pinhole\n"
            << "\t" << static_cast<int>(PINHOLE_CAMERA_RADIAL1) << ": Pinhole radial 1\n"
            << "\t" << static_cast<int>(PINHOLE_CAMERA_RADIAL3) << ": Pinhole radial 3 (default)\n"
            << "\t" << static_cast<int>(PINHOLE_CAMERA_BROWN) << ": Pinhole brown 2\n"
            << "\t" << static_cast<int>(PINHOLE_CAMERA_FISHEYE) << ": Pinhole with a simple Fish-eye distortion\n"
            << "\t" << static_cast<int>(CAMERA_SPHERICAL) << ": Spherical camera\n"
            << "[-g|--group_camera_model]\n"
            << "\t 0-> each view have it's own camera intrinsic parameters,\n"
            << "\t 1-> (default) view can share some camera intrinsic parameters\n"
            << "\n"
            << "[-P|--use_pose_prior] Use pose prior if GPS EXIF pose is available"
            << "[-W|--prior_weights] \"x;y;z;\" of weights for each dimension of the prior (default: 1.0)\n"
            << "[-m|--gps_to_xyz_method] XZY Coordinate system:\n"
            << "\t 0: ECEF (default)\n"
            << "\t 1: UTM\n"
            << std::endl;

        std::cerr << s << std::endl;
        return EXIT_FAILURE;
    }

    system("pause");
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/dengmingtian/p/10967762.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值