Browse Source

Add FeatureSampleConfig class for command line configuration parsing

- Introduced `FeatureSampleConfig` class to encapsulate configuration parameters for feature sampling from command line arguments.
- Implemented methods for parsing and validating input parameters, ensuring required fields are provided.
- Updated `Source.cpp` to utilize the new configuration class, streamlining command line argument handling and improving code readability.
- Enhanced error handling for missing or invalid parameters, promoting robustness in the feature sampling process.
main
mckay 2 months ago
parent
commit
8940f83fcc
  1. 94
      code/pre_processing/FeatureSample/FeatureSampleConfig.cpp
  2. 48
      code/pre_processing/FeatureSample/FeatureSampleConfig.h
  3. 106
      code/pre_processing/FeatureSample/Source.cpp

94
code/pre_processing/FeatureSample/FeatureSampleConfig.cpp

@ -0,0 +1,94 @@
#include "FeatureSampleConfig.h"
#include <iostream>
namespace MeshLib {
FeatureSampleConfig FeatureSampleConfig::FromCommandLine(int argc, char** argv) {
cxxopts::Options options("FeatureSample", "Feature sampling for 3D meshes");
options.add_options()
("i,input", "input mesh (obj/off format)", cxxopts::value<std::string>())
("f,feature", "input feature file (fea format)", cxxopts::value<std::string>())
("p,pointcloud", "input pointcloud", cxxopts::value<std::string>())
("pf", "face of input pointcloud", cxxopts::value<std::string>())
("o,output", "output mesh/points", cxxopts::value<std::string>())
("k,mask", "output mask file", cxxopts::value<std::string>())
("fs", "feature samples", cxxopts::value<int>())
("ns", "non-feature samples", cxxopts::value<int>())
("m,mode", "processing mode", cxxopts::value<int>())
("c,color", "use coloring", cxxopts::value<int>())
("mp", "max patches per cluster", cxxopts::value<int>())
("cot", "use cotangent weight", cxxopts::value<int>())
("s,sigma", "position noise sigma", cxxopts::value<double>())
("sn", "normal noise sigma", cxxopts::value<double>())
("csg", "generate csg tree", cxxopts::value<int>())
("convex", "is first layer convex", cxxopts::value<int>())
("r,repairturn", "repair turn vertex", cxxopts::value<int>())
("verbose", "verbose level", cxxopts::value<int>())
("strict", "strict mode")
("repairtree", "repair tree feature")
("h,help", "print help");
auto result = options.parse(argc, argv);
if (result.count("help")) {
std::cout << options.help() << std::endl;
exit(0);
}
FeatureSampleConfig config;
// 检查必需参数
if (!result.count("m") || !result.count("i") || !result.count("o")) {
throw std::runtime_error("Missing required parameters");
}
// 设置文件路径
config.files.input_mesh = result["i"].as<std::string>();
config.files.output_file = result["o"].as<std::string>();
if (result.count("f")) config.files.input_feature = result["f"].as<std::string>();
if (result.count("p")) config.files.input_pointcloud = result["p"].as<std::string>();
if (result.count("pf")) config.files.input_pointcloud_face = result["pf"].as<std::string>();
if (result.count("k")) config.files.output_mask = result["k"].as<std::string>();
// 设置采样参数
if (result.count("fs")) config.sampling.feature_samples = result["fs"].as<int>();
if (result.count("ns")) config.sampling.nonfeature_samples = result["ns"].as<int>();
if (result.count("s")) config.sampling.sigma = result["s"].as<double>();
if (result.count("sn")) config.sampling.sigma_normal = result["sn"].as<double>();
if (result.count("cot")) config.sampling.use_cotweight = result["cot"].as<int>();
// 设置处理选项
config.processing.mode = result["m"].as<int>();
if (result.count("c")) config.processing.use_color = result["c"].as<int>();
if (result.count("mp")) config.processing.max_patches = result["mp"].as<int>();
if (result.count("csg")) config.processing.generate_csg = result["csg"].as<int>();
if (result.count("convex")) config.processing.is_convex = result["convex"].as<int>();
if (result.count("r")) config.processing.repair_turn = result["r"].as<int>();
if (result.count("verbose")) config.processing.verbose = result["verbose"].as<int>();
if (result.count("strict")) config.processing.strict_mode = true;
if (result.count("repairtree")) config.processing.repair_tree = true;
return config;
}
bool FeatureSampleConfig::Validate() const {
// 检查文件路径
if (files.input_mesh.empty() || files.output_file.empty()) {
return false;
}
// 检查采样参数
if (sampling.feature_samples < 0 || sampling.nonfeature_samples < 0) {
return false;
}
// 检查处理模式
if (processing.mode != 0 && processing.mode != 1) {
return false;
}
return true;
}
} // namespace MeshLib

48
code/pre_processing/FeatureSample/FeatureSampleConfig.h

@ -0,0 +1,48 @@
#pragma once
#include <string>
#include "cxxopts.hpp"
namespace MeshLib {
class FeatureSampleConfig {
public:
// 文件路径
struct FilePaths {
std::string input_mesh; // 输入网格文件
std::string input_feature; // 输入特征文件
std::string input_pointcloud; // 输入点云文件
std::string input_pointcloud_face; // 输入点云面片文件
std::string output_file; // 输出文件
std::string output_mask; // 输出掩码文件
} files;
// 采样参数
struct SamplingParams {
int feature_samples = 10000; // 特征边采样点数
int nonfeature_samples = 40000; // 非特征面采样点数
double sigma = 0.0; // 位置噪声标准差
double sigma_normal = 0.0; // 法向噪声标准差
bool use_cotweight = false; // 是否使用余切权重
} sampling;
// 处理选项
struct ProcessingOptions {
int mode = 1; // 处理模式 (0:标准化, 1:特征采样)
bool use_color = false; // 是否使用颜色
int max_patches = -1; // 每个颜色簇的最大面片数
bool generate_csg = false; // 是否生成CSG树
bool is_convex = false; // 第一层是否为凸的
bool repair_turn = true; // 是否修复转角顶点
bool repair_tree = false; // 是否修复树特征
bool strict_mode = false; // 是否使用严格模式
int verbose = 0; // 详细输出级别
} processing;
// 从命令行参数解析配置
static FeatureSampleConfig FromCommandLine(int argc, char** argv);
// 验证配置是否有效
bool Validate() const;
};
} // namespace MeshLib

106
code/pre_processing/FeatureSample/Source.cpp

@ -10,6 +10,7 @@
#include "helper.h"
#include "happly.h"
#include "Tree.h"
#include "FeatureSampleConfig.h"
using namespace MeshLib;
@ -120,57 +121,28 @@ int main(int argc, char** argv)
{
try
{
cxxopts::Options options("FeaturedModelPointSample", "Point Sampling program for featured CAD models (author: Haoxiang Guo, Email: guohaoxiangxiang@gmail.com)");
options
.positional_help("[optional args]")
.show_positional_help()
.allow_unrecognised_options()
.add_options()
("i,input", "input mesh (obj/off format)", cxxopts::value<std::string>())
("f,feature", "input feature file (fea format)", cxxopts::value<std::string>())
("p,pointcloud", "input pointcloud", cxxopts::value<std::string>())
("pf", "face of input pointcloud", cxxopts::value<std::string>())
("o,output", "output mesh/points (obj/off/points/xyz format)", cxxopts::value<std::string>())
("k,mask", "output mask file (txt format)", cxxopts::value<std::string>())
("fs", "number of samples on feature edges(default: 10000)", cxxopts::value<int>())
("ns", "number of samples on non-feature faces(default: 40000)", cxxopts::value<int>())
("m,mode", "processing mode: 0 for normalization and 1 for feature sample", cxxopts::value<int>())
("c,color", "whether coloring is used, 0: not used, 1: used, default: 0", cxxopts::value<int>())
("mp", "maximum number of patches in each colored cluster, only work for csg, default -1(no upper bound)", cxxopts::value<int>())
("cot", "whether cotangent weight is used for sampling, 0: not used, 1: used, default: 0", cxxopts::value<int>())
("s,sigma", "sigma for noisy points position, default 0.0", cxxopts::value<double>())
("sn", "sigma for noisy points normal in degrees, default 0.0", cxxopts::value<double>())
("csg", "whether generating csg tree for model, default: 0", cxxopts::value<int>())
("convex", "whether the first layer is convex, default: 0", cxxopts::value<int>())
("r,repairturn", "whether the turn vertex are repaired, default: 1", cxxopts::value<int>())
("verbose", "verbose setting, default: 0", cxxopts::value<int>())
("strict", "treat all edges as either strictly convex or concave")
("repairtree", "repair tree feature")
("h,help", "print help");
// 解析命令行参数到配置类
auto config = FeatureSampleConfig::FromCommandLine(argc, argv);
auto result = options.parse(argc, argv);
if (result.count("help"))
{
std::cout << options.help({ "", "Group" }) << std::endl;
exit(0);
// 验证配置
if (!config.Validate()) {
throw std::runtime_error("Invalid configuration");
}
int n_nonfeature_sample = 50000;
int n_feature_sample = 0;
int min_sample_perpatch = 50;
double sigma = -1.0;
double sigma_n = -1.0;
assert(result.count("m"));
int processing_mode = result["m"].as<int>();
assert(result.count("i") && result.count("o"));
auto& inputfile = result["i"].as<std::string>();
auto& outputfile = result["o"].as<std::string>();
int processing_mode = config.processing.mode;
auto& inputfile = config.files.input_mesh;
auto& outputfile = config.files.output_file;
//output pts by colors
int last_dot = (int)outputfile.find_last_of(".");
auto output_prefix = outputfile.substr(0, last_dot);
int flag_csg = 0;
bool flag_sample_pts = true; //simply generate mask and csg tree of a given point cloud
if (result.count("p"))
if (config.files.input_pointcloud.empty())
{
flag_sample_pts = false;
}
@ -183,13 +155,13 @@ int main(int argc, char** argv)
mesh.load_off(inputfile.c_str());
std::cout << "verts: " << mesh.get_vertices_list()->size() << " face: " << mesh.get_faces_list()->size() << std::endl;
bool flag_verbose = false;
if (result.count("verbose"))
if (config.processing.verbose)
{
flag_verbose = (bool)result["verbose"].as<int>();
flag_verbose = (bool)config.processing.verbose;
}
bool flag_strict = false;
if (result.count("strict"))
if (config.processing.strict_mode)
{
flag_strict = true;
}
@ -234,30 +206,30 @@ int main(int argc, char** argv)
//mask:
//feature: 0
//non feature: 1,2,3...indicating coloring
assert(result.count("f") && result.count("k"));
auto& inputfeaturefile = result["f"].as<std::string>();
auto& outputmaskfile = result["k"].as<std::string>();
if (result.count("fs"))
n_feature_sample = result["fs"].as<int>();
if (result.count("ns"))
n_nonfeature_sample = result["ns"].as<int>();
if (result.count("s"))
sigma = result["s"].as<double>();
if (result.count("sn"))
sigma_n = result["sn"].as<double>();
assert(!config.files.input_feature.empty() && !config.files.output_mask.empty());
auto& inputfeaturefile = config.files.input_feature;
auto& outputmaskfile = config.files.output_mask;
if (config.sampling.feature_samples)
n_feature_sample = config.sampling.feature_samples;
if (config.sampling.nonfeature_samples)
n_nonfeature_sample = config.sampling.nonfeature_samples;
if (config.sampling.sigma)
sigma = config.sampling.sigma;
if (config.sampling.sigma_normal)
sigma_n = config.sampling.sigma_normal;
bool flag_repair_turn_features = true;
if (result.count("r"))
flag_repair_turn_features = result["r"].as<int>();
if (config.processing.repair_turn)
flag_repair_turn_features = true;
bool flag_repair_tree_features = false;
if (result.count("repairtree"))
if (config.processing.repair_tree)
{
flag_repair_tree_features = true;
}
if (result.count("csg"))
flag_csg = result["csg"].as<int>();
if (config.processing.generate_csg)
flag_csg = true;
bool flag_skip_hanging_features = false; //not skipping hanging features
std::vector<int> sample_mask;
@ -267,13 +239,13 @@ int main(int argc, char** argv)
std::vector<size_t> sample_pts_tris; //used for assign labels of sample pts
if (!flag_sample_pts)
{
auto& inputpcfile = result["p"].as<std::string>();
auto& inputpcfile = config.files.input_pointcloud;
load_xyz_file(inputpcfile.c_str(), sample_pts, sample_pt_normals);
sample_pts_tris.resize(sample_pts.size(), 0);
if (result.count("pf"))
if (!config.files.input_pointcloud_face.empty())
{
auto& inputpffile = result["pf"].as<std::string>();
auto& inputpffile = config.files.input_pointcloud_face;
std::ifstream ifs(inputpffile);
for (size_t ii = 0; ii < sample_pts.size(); ii++)
@ -635,14 +607,14 @@ int main(int argc, char** argv)
int n_color = cluster_id;
//coloring
bool flag_coloring = false;
if (result.count("c"))
flag_coloring = result["c"].as<int>();
if (config.processing.use_color)
flag_coloring = true;
bool flag_first_convex = false;
if (result.count("convex"))
flag_first_convex = result["convex"].as<int>();
if (config.processing.is_convex)
flag_first_convex = true;
if (flag_coloring && !flag_csg)
{
@ -979,8 +951,8 @@ int main(int argc, char** argv)
if (flag_coloring)
{
int max_patch_per_cluster = -1;
if (result.count("mp"))
max_patch_per_cluster = result["mp"].as<int>();
if (config.processing.max_patches)
max_patch_per_cluster = config.processing.max_patches;
std::vector<size_t> cluster_color(connectivity_v.size(), -1);
n_color = tree_coloring<size_t>(tree, connectivity_v, cluster_color, 0, max_patch_per_cluster) + 1;
for (size_t i = 0; i < face_clusters.size(); i++)
@ -1002,7 +974,7 @@ int main(int argc, char** argv)
//sampling on triangles
std::vector<double> tri_mean_curvature_normalize(tri_verts.size(), 0.0);
if (result.count("cot"))
if (config.sampling.use_cotweight)
{
//compute tri_mean_curvature
std::vector<double> vert_curvature;

Loading…
Cancel
Save