You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:15:55