基于C++的两向量距离优化求解方案咨询
嘿,你的问题完全是典型的非线性最小二乘拟合场景,dlib其实是可以搞定的——大概率是之前的用法没踩对点子上。下面给你几个靠谱的C++优化方案,包括修正后的dlib用法,还有工业级的Ceres Solver和轻量级的Eigen方案:
1. 修正dlib的solve_least_squares用法
dlib的这个函数天生适配你的场景,核心是要给它返回所有残差项(每个点的x坐标差、y坐标差),而不是直接返回平方和suma。它内部会自动处理平方和的最小化逻辑。
先定义计算残差的 functor:
#include <dlib/optimization.h> // 用结构体封装需要的固定数据(目标点、输入向量) struct ResidualCalculator { const std::vector<cv::Point2f>& target_points; const std::vector<float>& fixed_input; ResidualCalculator(const std::vector<cv::Point2f>& dst, const std::vector<float>& in) : target_points(dst), fixed_input(in) {} // 输入优化参数(dlib矩阵格式),输出残差向量 dlib::matrix<double> operator()(const dlib::matrix<double>& params_mat) const { // 把dlib矩阵转成std::vector,适配你的project_keypoints函数 std::vector<double> params(params_mat.begin(), params_mat.end()); std::vector<cv::Point2f> projected_points = project_keypoints(params, fixed_input); // 构造残差向量:每个点对应x、y两个残差项 dlib::matrix<double> residuals(target_points.size() * 2, 1); for (size_t i = 0; i < target_points.size(); ++i) { residuals(2*i) = target_points[i].x - projected_points[i].x; residuals(2*i + 1) = target_points[i].y - projected_points[i].y; } return residuals; } };
然后调用优化:
// 初始化参数:可以给个初始猜测(比如全0,或者根据业务逻辑给合理值) dlib::matrix<double> params(189, 1); dlib::set_all_elements(params, 0.0); // 设置优化选项:迭代次数、收敛阈值等 dlib::solve_least_squares_options opts; opts.max_iterations = 1000; opts.epsilon = 1e-7; // 残差变化小于这个值就停止 // 执行优化 ResidualCalculator calculator(dstpoints, input); solve_least_squares(dlib::objective_delta_stop_strategy(opts), calculator, params); // 把优化后的参数转成你需要的std::vector格式 std::vector<double> optimized_params(params.begin(), params.end());
2. 推荐:Google Ceres Solver(工业级非线性最小二乘库)
如果追求更好的性能和灵活性,Ceres绝对是首选——它专门为这类残差平方和最小化问题设计,支持自动求导、手动求导,还能处理稀疏性场景。
先定义代价函数(用自动求导的方式,最省心):
#include <ceres/ceres.h> // 每个点的代价函数:计算当前参数下的投影点与目标点的残差 struct ProjectionCost { ProjectionCost(float target_x, float target_y, const std::vector<float>& fixed_input) : target_x_(target_x), target_y_(target_y), fixed_input_(fixed_input) {} // 模板函数,支持自动求导的数值类型 template <typename T> bool operator()(const T* const params, T* residuals) const { // 注意:这里需要把你的project_keypoints改成模板函数,支持T类型 // 如果改不了,就用Ceres的数值求导替代(把AutoDiff换成NumericDiff) std::vector<cv::Point_<T>> projected_points = project_keypoints_template(params, fixed_input_); // 输出残差 residuals[0] = T(target_x_) - projected_points[i].x; residuals[1] = T(target_y_) - projected_points[i].y; return true; } private: float target_x_; float target_y_; const std::vector<float>& fixed_input_; };
然后构建问题并求解:
// 初始化参数数组(Ceres默认用double) double* params = new double[189]; std::fill(params, params + 189, 0.0); // 初始值 ceres::Problem problem; for (size_t i = 0; i < dstpoints.size(); ++i) { // 添加每个点的残差块:2个残差项,189个优化参数 ceres::CostFunction* cost_func = new ceres::AutoDiffCostFunction<ProjectionCost, 2, 189>( new ProjectionCost(dstpoints[i].x, dstpoints[i].y, input)); problem.AddResidualBlock(cost_func, nullptr, params); } // 设置求解器选项 ceres::Solver::Options opts; opts.max_num_iterations = 1000; opts.linear_solver_type = ceres::DENSE_QR; // 适合小规模参数(189属于小规模) opts.minimizer_progress_to_stdout = true; // 输出优化过程 ceres::Solver::Summary summary; ceres::Solve(opts, &problem, &summary); // 查看优化结果 std::cout << summary.BriefReport() << std::endl; // 转成vector格式 std::vector<double> optimized_params(params, params + 189); delete[] params;
3. 轻量级选择:Eigen的Levenberg-Marquardt模块
如果你已经在用Eigen库,不想额外安装其他依赖,Eigen的非线性优化模块完全够用,适合轻量级场景。
先定义残差函数:
#include <Eigen/Core> #include <Eigen/NonLinearOptimization> struct ProjectResidual { const std::vector<cv::Point2f>& target_points; const std::vector<float>& fixed_input; ProjectResidual(const std::vector<cv::Point2f>& dst, const std::vector<float>& in) : target_points(dst), fixed_input(in) {} // 计算残差:输入参数,输出残差向量 int operator()(const Eigen::VectorXd& params, Eigen::VectorXd& residuals) const { std::vector<double> params_vec(params.data(), params.data() + params.size()); std::vector<cv::Point2f> projected_points = project_keypoints(params_vec, fixed_input); residuals.resize(target_points.size() * 2); for (size_t i = 0; i < target_points.size(); ++i) { residuals(2*i) = target_points[i].x - projected_points[i].x; residuals(2*i + 1) = target_points[i].y - projected_points[i].y; } return 0; } }; // 用数值求导包装残差函数(如果不想手动写雅可比矩阵的话) typedef Eigen::NumericalDiff<ProjectResidual> NumericalDiffResidual;
然后执行优化:
// 初始化参数 Eigen::VectorXd params(189); params.setZero(); ProjectResidual residual_func(dstpoints, input); NumericalDiffResidual num_diff_func(residual_func); Eigen::LevenbergMarquardt<NumericalDiffResidual> lm_solver(num_diff_func); lm_solver.parameters.maxfev = 1000; // 最大迭代次数 lm_solver.parameters.xtol = 1e-7; // 参数变化阈值 // 执行优化 int status = lm_solver.minimize(params); // 转成vector格式 std::vector<double> optimized_params(params.data(), params.data() + params.size());
总结
- 如果你已经在用dlib,修正残差函数的返回值(返回每个残差项而非平方和)就能解决问题;
- 追求性能和灵活性的话,优先选Ceres Solver,它是这类问题的工业标准;
- 轻量级需求且已用Eigen,直接用Eigen的LM模块即可。
内容的提问来源于stack exchange,提问作者zacniewski
相关产品推荐
相关产品推荐

