10#include <ceres/ceres.h>
11#include <ceres/rotation.h>
19 const std::vector<PlanarObservation>& data,
20 std::span<const int> inliers) ->
double {
21 if (inliers.empty()) {
22 return std::numeric_limits<double>::infinity();
24 const double sum_sq_err = std::accumulate(
25 inliers.begin(), inliers.end(), 0.0,
26 [&](
double acc,
int idx) { return acc + HomographyEstimator::residual(hmtx, data[idx]); });
27 return std::sqrt(sum_sq_err / (2.0 *
static_cast<double>(inliers.size())));
31 std::vector<int> inliers(data.size());
32 std::iota(inliers.begin(), inliers.end(), 0);
34 if (!hmtx_opt.has_value()) {
35 std::cout <<
"Homography estimation failed.\n";
39 result.
hmtx = hmtx_opt.value();
47 auto ransac_result = ransac<HomographyEstimator>(data, ransac_opts);
48 if (!ransac_result.success) {
49 std::cout <<
"Homography RANSAC failed.\n";
53 result.
hmtx = ransac_result.model;
54 result.
inliers = std::move(ransac_result.inliers);
58 std::cout <<
"Homography inliers: " << result.
inliers.size() <<
" / " << data.size()
66 if (!ransac_opts.has_value()) {
75using Vec2 = Eigen::Vector2d;
76using Mat3 = Eigen::Matrix3d;
84 blocks.
params = {init_h(0, 0), init_h(0, 1), init_h(0, 2), init_h(1, 0),
85 init_h(1, 1), init_h(1, 2), init_h(2, 0), init_h(2, 1)};
97 mat_h << params[0], params[1], params[2], params[3], params[4], params[5], params[6], params[7],
108 template <
typename T>
114 Eigen::Matrix<T, 3, 3> hmtx;
115 hmtx << h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], T(1);
116 Eigen::Vector3<T> xyvec(T(
x_), T(
y_), T(1));
118 Eigen::Vector3<T> uvw = hmtx * xyvec;
119 Eigen::Vector2<T> uv_hat = uvw.hnormalized();
120 residuals[0] = uv_hat.x() - T(
u_);
121 residuals[1] = uv_hat.y() - T(
v_);
125 static ceres::CostFunction*
create(
double x,
double y,
double u,
double v) {
126 return new ceres::AutoDiffCostFunction<HomographyResidual, 2, 8>(
133 ceres::Problem problem;
134 std::for_each(data.begin(), data.end(), [&](
const auto& obs) {
135 problem.AddResidualBlock(
136 HomographyResidual::create(obs.object_xy.x(), obs.object_xy.y(), obs.image_uv.x(),
138 options.huber_delta > 0 ? new ceres::HuberLoss(options.huber_delta) : nullptr,
139 blocks.params.data());
145 const OptimOptions& options) {
146 if (data.size() < 4) {
147 throw std::invalid_argument(
"At least 4 correspondences are required.");
151 ceres::Problem problem =
build_problem(data, options, blocks);
157 if (std::abs(hmtx(2, 2)) > 1e-15) {
162 if (options.compute_covariance) {
163 std::vector<double> residuals(data.size() * 2);
164 ceres::Problem::EvaluateOptions evopts;
165 problem.Evaluate(evopts,
nullptr, &residuals,
nullptr,
nullptr);
166 const double ssr = std::accumulate(residuals.begin(), residuals.end(), 0.0,
167 [](
double sum,
double r) { return sum + r * r; });
169 if (optcov.has_value()) {
Linear multi-camera extrinsics initialisation (DLT)
static auto symmetric_rms_px(const Eigen::Matrix3d &hmtx, const PlanarView &view, std::span< const int > inliers) -> double
auto optimize_homography(const PlanarView &data, const Eigen::Matrix3d &init_h, const OptimOptions &options={}) -> OptimizeHomographyResult
static void estimate_homography_ransac(const PlanarView &data, HomographyResult &result, const RansacOptions &ransac_opts)
auto estimate_homography(const PlanarView &data, std::optional< RansacOptions > ransac_opts=std::nullopt) -> HomographyResult
static auto params_to_h(const HomographyParams ¶ms) -> Mat3
std::vector< PlanarObservation > PlanarView
auto compute_covariance(const ProblemParamBlocks &problem_param_blocks, ceres::Problem &problem, double sum_squared_residuals=0, size_t total_residuals=0) -> std::optional< Eigen::MatrixXd >
std::array< double, 8 > HomographyParams
static auto build_problem(const std::vector< BundleObservation > &observations, const BundleOptions &opts, BundleBlocks< CameraT > &blocks) -> ceres::Problem
void solve_problem(ceres::Problem &problem, const OptimOptions &opts, OptimResult *result)
static void estimate_homography_dlt(const PlanarView &data, HomographyResult &result)
auto get_param_blocks() const -> std::vector< ParamBlock > override
static auto create(const Eigen::Matrix3d &init_h) -> HomographyBlocks
static auto fit(const std::vector< Datum > &data, std::span< const int > sample) -> std::optional< Model >
bool operator()(const T *const h, T *residuals) const
static ceres::CostFunction * create(double x, double y, double u, double v)
HomographyResidual(double x, double y, double u, double v)
std::vector< int > inliers
Eigen::MatrixXd covariance
OptimResult core
Core optimization result.
Eigen::Matrix3d homography