Calibration Library 1.0.0
A C++ library for camera calibration and vision-related geometric transformations
Loading...
Searching...
No Matches
extrinsics.cpp
Go to the documentation of this file.
2
3// std
4#include <algorithm>
5#include <iterator>
6
9#include "detail/ceresutils.h"
12
13namespace calib {
14
15template <camera_model CameraT>
16struct ExtrinsicBlocks final : public ProblemParamBlocks {
18 std::vector<std::array<double, 4>> cam_quat_ref;
19 std::vector<std::array<double, 3>> cam_tran_ref;
20 std::vector<std::array<double, 4>> ref_quat_tgt;
21 std::vector<std::array<double, 3>> ref_tran_tgt;
22 std::vector<std::array<double, intr_size>> intrinsics;
23
24 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
25 ExtrinsicBlocks(size_t num_cams, size_t num_views)
26 : cam_quat_ref(num_cams),
27 cam_tran_ref(num_cams),
28 ref_quat_tgt(num_views),
29 ref_tran_tgt(num_views),
30 intrinsics(num_cams) {}
31
32 static auto create(const std::vector<CameraT>& cameras,
33 const std::vector<Eigen::Isometry3d>& init_cam_se3_ref,
34 const std::vector<Eigen::Isometry3d>& init_ref_se3_tgt) -> ExtrinsicBlocks {
35 const size_t num_cams = cameras.size();
36 const size_t num_views = init_ref_se3_tgt.size();
37 ExtrinsicBlocks blocks(num_cams, num_views);
38 for (size_t cam_idx = 0; cam_idx < num_cams; ++cam_idx) {
39 populate_quat_tran(init_cam_se3_ref[cam_idx], blocks.cam_quat_ref[cam_idx],
40 blocks.cam_tran_ref[cam_idx]);
41 CameraTraits<CameraT>::to_array(cameras[cam_idx], blocks.intrinsics[cam_idx]);
42 }
43 for (size_t view_idx = 0; view_idx < num_views; ++view_idx) {
44 populate_quat_tran(init_ref_se3_tgt[view_idx], blocks.ref_quat_tgt[view_idx],
45 blocks.ref_tran_tgt[view_idx]);
46 }
47 return blocks;
48 }
49
50 [[nodiscard]]
51 auto get_param_blocks() const -> std::vector<ParamBlock> override {
52 std::vector<ParamBlock> blocks;
53 blocks.reserve(intrinsics.size() + cam_quat_ref.size() + cam_tran_ref.size() +
54 ref_quat_tgt.size() + ref_tran_tgt.size());
55 std::transform(
56 intrinsics.begin(), intrinsics.end(), std::back_inserter(blocks),
57 [](const auto& intr) { return ParamBlock(intr.data(), intr.size(), intr_size); });
58 std::transform(cam_quat_ref.begin(), cam_quat_ref.end(), std::back_inserter(blocks),
59 [](const auto& quat) { return ParamBlock(quat.data(), quat.size(), 3); });
60 std::transform(cam_tran_ref.begin(), cam_tran_ref.end(), std::back_inserter(blocks),
61 [](const auto& tran) { return ParamBlock(tran.data(), tran.size(), 3); });
62 std::transform(ref_quat_tgt.begin(), ref_quat_tgt.end(), std::back_inserter(blocks),
63 [](const auto& quat) { return ParamBlock(quat.data(), quat.size(), 3); });
64 std::transform(ref_tran_tgt.begin(), ref_tran_tgt.end(), std::back_inserter(blocks),
65 [](const auto& tran) { return ParamBlock(tran.data(), tran.size(), 3); });
66 return blocks;
67 }
68
70 const size_t num_cams = cam_quat_ref.size();
71 const size_t num_views = ref_quat_tgt.size();
72 result.cameras.resize(num_cams);
73 result.c_se3_r.resize(num_cams);
74 result.r_se3_t.resize(num_views);
75 for (size_t cam_idx = 0; cam_idx < num_cams; ++cam_idx) {
76 result.cameras[cam_idx] =
77 CameraTraits<CameraT>::template from_array<double>(intrinsics[cam_idx].data());
78 result.c_se3_r[cam_idx] = restore_pose(cam_quat_ref[cam_idx], cam_tran_ref[cam_idx]);
79 }
80 for (size_t view_idx = 0; view_idx < num_views; ++view_idx) {
81 result.r_se3_t[view_idx] = restore_pose(ref_quat_tgt[view_idx], ref_tran_tgt[view_idx]);
82 }
83 }
84};
85
86template <camera_model CameraT>
87static void set_residual_blocks(ceres::Problem& problem,
88 const std::vector<MulticamPlanarView>& views,
89 const std::vector<CameraT>& cameras,
90 const ExtrinsicOptions& options, ExtrinsicBlocks<CameraT>& blocks) {
91 for (size_t view_index = 0; view_index < views.size(); ++view_index) {
92 const auto& multicam_view = views[view_index];
93 for (size_t cam_index = 0; cam_index < cameras.size(); ++cam_index) {
94 if (multicam_view[cam_index].empty()) {
95 continue;
96 }
97 auto* loss = options.core.huber_delta > 0
98 ? new ceres::HuberLoss(options.core.huber_delta)
99 : nullptr;
100 problem.AddResidualBlock(
101 ExtrinsicResidual<CameraT>::create(multicam_view[cam_index]), loss,
102 blocks.cam_quat_ref[cam_index].data(), blocks.cam_tran_ref[cam_index].data(),
103 blocks.ref_quat_tgt[view_index].data(), blocks.ref_tran_tgt[view_index].data(),
104 blocks.intrinsics[cam_index].data());
105 }
106 }
107}
108
109template <camera_model CameraT>
110static void set_param_constraints(ceres::Problem& problem, const ExtrinsicOptions& options,
111 ExtrinsicBlocks<CameraT>& blocks) {
112 for (auto& cam_quat : blocks.cam_quat_ref) {
113 problem.SetManifold(cam_quat.data(), new ceres::QuaternionManifold());
114 }
115 for (auto& ref_quat : blocks.ref_quat_tgt) {
116 problem.SetManifold(ref_quat.data(), new ceres::QuaternionManifold());
117 }
118 if (!options.optimize_intrinsics) {
119 for (auto& intr : blocks.intrinsics) {
120 problem.SetParameterBlockConstant(intr.data());
121 }
122 } else {
123 if (!blocks.ref_quat_tgt.empty()) {
124 problem.SetParameterBlockConstant(blocks.ref_quat_tgt[0].data());
125 problem.SetParameterBlockConstant(blocks.ref_tran_tgt[0].data());
126 }
127 }
128 if (!options.optimize_extrinsics) {
129 for (auto& cam_quat : blocks.cam_quat_ref) {
130 problem.SetParameterBlockConstant(cam_quat.data());
131 }
132 for (auto& cam_tran : blocks.cam_tran_ref) {
133 problem.SetParameterBlockConstant(cam_tran.data());
134 }
135 } else {
136 if (!blocks.cam_quat_ref.empty()) {
137 problem.SetParameterBlockConstant(blocks.cam_quat_ref[0].data());
138 problem.SetParameterBlockConstant(blocks.cam_tran_ref[0].data());
139 }
140 }
141 static constexpr size_t intr_size = CameraTraits<CameraT>::param_count;
142 for (auto& intr : blocks.intrinsics) {
143 problem.SetParameterLowerBound(intr.data(), CameraTraits<CameraT>::idx_fx, 0.0);
144 problem.SetParameterLowerBound(intr.data(), CameraTraits<CameraT>::idx_fy, 0.0);
145 if (!options.optimize_skew) {
146 problem.SetManifold(intr.data(), new ceres::SubsetManifold(
147 intr_size, {CameraTraits<CameraT>::idx_skew}));
148 }
149 }
150}
151
152template <camera_model CameraT>
153static auto build_problem(const std::vector<MulticamPlanarView>& views,
154 const std::vector<CameraT>& cameras, const ExtrinsicOptions& options,
155 ExtrinsicBlocks<CameraT>& blocks) -> ceres::Problem {
156 ceres::Problem problem;
157 set_residual_blocks(problem, views, cameras, options, blocks);
158 set_param_constraints(problem, options, blocks);
159 return problem;
160}
161
162template <camera_model CameraT>
163static void validate_input(const std::vector<CameraT>& init_cameras,
164 const std::vector<Eigen::Isometry3d>& init_c_se3_r,
165 const std::vector<Eigen::Isometry3d>& init_r_se3_t,
166 const std::vector<MulticamPlanarView>& views) {
167 const size_t num_cams = init_cameras.size();
168 const size_t num_views = views.size();
169 if (init_c_se3_r.size() != num_cams || init_r_se3_t.size() != num_views) {
170 throw std::invalid_argument("Incompatible pose vector sizes for joint optimization");
171 }
172}
173
174template <camera_model CameraT>
176 const std::vector<MulticamPlanarView>& views, const std::vector<CameraT>& init_cameras,
177 const std::vector<Eigen::Isometry3d>& init_c_se3_r,
178 const std::vector<Eigen::Isometry3d>& init_r_se3_t, const ExtrinsicOptions& opts) {
179 validate_input(init_cameras, init_c_se3_r, init_r_se3_t, views);
180
181 auto blocks = ExtrinsicBlocks<CameraT>::create(init_cameras, init_c_se3_r, init_r_se3_t);
182 ceres::Problem problem = build_problem(views, init_cameras, opts, blocks);
183
185 solve_problem(problem, opts.core, &result.core);
186
187 blocks.populate_result(result);
188 if (opts.core.compute_covariance) {
189 auto optcov = compute_covariance(blocks, problem);
190 if (optcov.has_value()) {
191 result.core.covariance = std::move(optcov.value());
192 }
193 }
194
195 return result;
196}
197
199 const std::vector<MulticamPlanarView>&, const std::vector<PinholeCamera<BrownConradyd>>&,
200 const std::vector<Eigen::Isometry3d>&, const std::vector<Eigen::Isometry3d>&,
201 const ExtrinsicOptions&);
202
204optimize_extrinsics(const std::vector<MulticamPlanarView>&,
206 const std::vector<Eigen::Isometry3d>&, const std::vector<Eigen::Isometry3d>&,
207 const ExtrinsicOptions&);
208
209} // namespace calib
Pinhole camera model with intrinsics and distortion correction.
Definition pinhole.h:39
Lens distortion models and correction algorithms.
Linear multi-camera extrinsics initialisation (DLT)
void populate_quat_tran(const Eigen::Isometry3d &pose, std::array< double, 4 > &quat, std::array< double, 3 > &translation)
static void set_residual_blocks(ceres::Problem &problem, const std::vector< MulticamPlanarView > &views, const std::vector< CameraT > &cameras, const ExtrinsicOptions &options, ExtrinsicBlocks< CameraT > &blocks)
static void set_param_constraints(ceres::Problem &problem, const ExtrinsicOptions &options, ExtrinsicBlocks< CameraT > &blocks)
auto optimize_extrinsics(const std::vector< MulticamPlanarView > &views, const std::vector< CameraT > &init_cameras, const std::vector< Eigen::Isometry3d > &init_c_se3_r, const std::vector< Eigen::Isometry3d > &init_r_se3_t, const ExtrinsicOptions &opts={}) -> ExtrinsicOptimizationResult< CameraT >
void validate_input(const std::vector< BundleObservation > &observations, const std::vector< CameraT > &initial_cameras)
Definition bundle.cpp:136
Eigen::Isometry3d restore_pose(const std::array< double, 4 > &quat, const std::array< double, 3 > &translation)
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 >
Definition ceresutils.h:69
static auto build_problem(const std::vector< BundleObservation > &observations, const BundleOptions &opts, BundleBlocks< CameraT > &blocks) -> ceres::Problem
Definition bundle.cpp:84
void solve_problem(ceres::Problem &problem, const OptimOptions &opts, OptimResult *result)
Definition ceresutils.h:27
Type traits template for camera model implementations.
Definition cameramodel.h:64
std::vector< std::array< double, 4 > > cam_quat_ref
void populate_result(ExtrinsicOptimizationResult< CameraT > &result) const
std::vector< std::array< double, 3 > > ref_tran_tgt
static auto create(const std::vector< CameraT > &cameras, const std::vector< Eigen::Isometry3d > &init_cam_se3_ref, const std::vector< Eigen::Isometry3d > &init_ref_se3_tgt) -> ExtrinsicBlocks
std::vector< std::array< double, intr_size > > intrinsics
std::vector< std::array< double, 4 > > ref_quat_tgt
std::vector< std::array< double, 3 > > cam_tran_ref
auto get_param_blocks() const -> std::vector< ParamBlock > override
ExtrinsicBlocks(size_t num_cams, size_t num_views)
static constexpr size_t intr_size
std::vector< Eigen::Isometry3d > c_se3_r
Definition extrinsics.h:18
std::vector< Eigen::Isometry3d > r_se3_t
Definition extrinsics.h:19
std::vector< CameraT > cameras
Definition extrinsics.h:17
bool optimize_extrinsics
Solve for camera and target extrinsics.
Definition extrinsics.h:26
bool optimize_skew
Solve for skew parameter.
Definition extrinsics.h:25
bool optimize_intrinsics
Solve for camera intrinsics.
Definition extrinsics.h:24
Eigen::MatrixXd covariance
Definition optimize.h:37
Camera model with a tilted sensor plane (Scheimpflug configuration).
Definition scheimpflug.h:35