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 <Eigen/Geometry>
5#include <algorithm>
6#include <iterator>
7#include <stdexcept>
8#include <unordered_map>
9
12#include "calib/pipeline/detail/planar_utils.h" // for make_planar_view
13
14namespace calib::pipeline {
15
17
18namespace {
19
20[[nodiscard]] auto build_point_lookup(const PlanarDetections& detections)
21 -> std::unordered_map<std::string, const PlanarImageDetections*> {
22 std::unordered_map<std::string, const PlanarImageDetections*> lookup;
23 for (const auto& image : detections.images) {
24 lookup.emplace(image.file, &image);
25 }
26 return lookup;
27}
28
29[[nodiscard]] auto to_dual_camera(const PinholeCamera<BrownConradyd>& cam)
30 -> PinholeCamera<DualDistortion> {
31 DualDistortion dual;
32 dual.forward = cam.distortion.coeffs;
33 dual.inverse = invert_brown_conrady(cam.distortion.coeffs);
34 return PinholeCamera<DualDistortion>(cam.kmtx, dual);
35}
36
37} // namespace
38
39[[nodiscard]] auto compute_views(const StereoPairConfig& cfg,
40 const PlanarDetections& reference_detections,
41 const PlanarDetections& target_detections,
43 -> std::vector<MulticamPlanarView> {
44 const auto reference_lookup = build_point_lookup(reference_detections);
45 const auto target_lookup = build_point_lookup(target_detections);
46
47 std::vector<MulticamPlanarView> views;
48 views.reserve(cfg.views.size());
49
50 for (const auto& view_cfg : cfg.views) {
52 summary.reference_image = view_cfg.reference_image;
53 summary.target_image = view_cfg.target_image;
54
55 const auto ref_it = reference_lookup.find(view_cfg.reference_image);
56 const auto tgt_it = target_lookup.find(view_cfg.target_image);
57
58 if (ref_it == reference_lookup.end()) {
59 summary.status = "missing_reference_image";
60 result.view_summaries.push_back(std::move(summary));
61 continue;
62 }
63 if (tgt_it == target_lookup.end()) {
64 summary.status = "missing_target_image";
65 result.view_summaries.push_back(std::move(summary));
66 continue;
67 }
68
69 auto reference_view = make_planar_view(*ref_it->second);
70 auto target_view = make_planar_view(*tgt_it->second);
71 summary.reference_points = reference_view.size();
72 summary.target_points = target_view.size();
73
74 if (reference_view.size() < 4U || target_view.size() < 4U) {
75 summary.status = "insufficient_points";
76 result.view_summaries.push_back(std::move(summary));
77 continue;
78 }
79
80 MulticamPlanarView multi_view;
81 multi_view.push_back(std::move(reference_view));
82 multi_view.push_back(std::move(target_view));
83 views.push_back(std::move(multi_view));
84 summary.status = "ok";
85 result.view_summaries.push_back(std::move(summary));
86 }
87
88 return views;
89}
90
92 const PlanarDetections& reference_detections,
93 const PlanarDetections& target_detections,
94 const IntrinsicCalibrationOutputs& reference_intrinsics,
95 const IntrinsicCalibrationOutputs& target_intrinsics) const
98 result.requested_views = cfg.views.size();
99
100 if (reference_intrinsics.refine_result.camera.distortion.coeffs.size() == 0 ||
101 target_intrinsics.refine_result.camera.distortion.coeffs.size() == 0) {
102 throw std::runtime_error("StereoCalibrationFacade: camera intrinsics are not available.");
103 }
104
105 const auto views = compute_views(cfg, reference_detections, target_detections, result);
106
107 result.used_views = views.size();
108 if (views.empty()) {
109 result.success = false;
110 result.optimization.core.success = false;
111 return result;
112 }
113
114 std::vector<PinholeCamera<BrownConradyd>> init_cameras = {
115 reference_intrinsics.refine_result.camera, target_intrinsics.refine_result.camera};
116
117 std::vector<PinholeCamera<DualDistortion>> dlt_cameras;
118 dlt_cameras.reserve(init_cameras.size());
119 std::transform(init_cameras.begin(), init_cameras.end(), std::back_inserter(dlt_cameras),
120 [](const auto& cam) { return to_dual_camera(cam); });
121
122 result.initial_guess = estimate_extrinsic_dlt(views, dlt_cameras);
123
124 ExtrinsicOptions options = cfg.options;
125 options.optimize_intrinsics = cfg.options.optimize_intrinsics;
126
127 result.optimization = optimize_extrinsics(views, init_cameras, result.initial_guess.c_se3_r,
128 result.initial_guess.r_se3_t, options);
129 result.success = result.optimization.core.success;
130 return result;
131}
132
133// ---- Multicam generalization implementations ----
134static auto compute_views(const MultiCameraRigConfig& cfg,
135 const std::unordered_map<std::string, PlanarDetections>& dets)
136 -> std::vector<MulticamPlanarView> {
137 // Build lookup tables for each sensor
138 std::unordered_map<std::string, std::unordered_map<std::string, const PlanarImageDetections*>>
139 lookup;
140 for (const auto& [sid, d] : dets) {
141 std::unordered_map<std::string, const PlanarImageDetections*> map;
142 for (const auto& img : d.images) map.emplace(img.file, &img);
143 lookup.emplace(sid, std::move(map));
144 }
145
146 std::vector<MulticamPlanarView> views;
147 views.reserve(cfg.views.size());
148 for (const auto& view_sel : cfg.views) {
149 MulticamPlanarView multi;
150 multi.resize(cfg.sensors.size());
151 bool ok = true;
152 for (std::size_t i = 0; i < cfg.sensors.size(); ++i) {
153 const auto& sid = cfg.sensors[i];
154 auto img_it = view_sel.images.find(sid);
155 if (img_it == view_sel.images.end()) {
156 ok = false;
157 break;
158 }
159 auto det_cam_it = dets.find(sid);
160 if (det_cam_it == dets.end()) {
161 ok = false;
162 break;
163 }
164 const auto& img_lookup = lookup.at(sid);
165 auto img_det_it = img_lookup.find(img_it->second);
166 if (img_det_it == img_lookup.end()) {
167 ok = false;
168 break;
169 }
170 auto view = make_planar_view(*img_det_it->second);
171 if (view.size() < 4U) {
172 ok = false;
173 break;
174 }
175 multi[i] = std::move(view);
176 }
177 if (ok) {
178 views.push_back(std::move(multi));
179 }
180 }
181 return views;
182}
183
185 const MultiCameraRigConfig& cfg,
186 const std::unordered_map<std::string, PlanarDetections>& detections_by_sensor,
187 const std::unordered_map<std::string, IntrinsicCalibrationOutputs>& intrinsics_by_sensor) const
190 result.requested_views = cfg.views.size();
191 result.sensors = cfg.sensors;
192
193 // Validate intrinsics availability
194 for (const auto& sid : cfg.sensors) {
195 auto it = intrinsics_by_sensor.find(sid);
196 if (it == intrinsics_by_sensor.end() ||
197 it->second.refine_result.camera.distortion.coeffs.size() == 0) {
198 throw std::runtime_error(
199 "MultiCameraCalibrationFacade: intrinsics not available for sensor: " + sid);
200 }
201 }
202
203 const auto views = compute_views(cfg, detections_by_sensor);
204 result.used_views = views.size();
205 if (views.empty()) {
206 result.success = false;
207 result.optimization.core.success = false;
208 return result;
209 }
210
211 std::vector<PinholeCamera<BrownConradyd>> init_cameras(cfg.sensors.size());
212 std::transform(cfg.sensors.begin(), cfg.sensors.end(), init_cameras.begin(),
213 [&intrinsics_by_sensor](const auto& sid) {
214 return intrinsics_by_sensor.at(sid).refine_result.camera;
215 });
216
217 std::vector<PinholeCamera<DualDistortion>> dlt_cameras;
218 dlt_cameras.reserve(init_cameras.size());
219 std::transform(init_cameras.begin(), init_cameras.end(), std::back_inserter(dlt_cameras),
220 [](const auto& cam) { return to_dual_camera(cam); });
221
222 result.initial_guess = estimate_extrinsic_dlt(views, dlt_cameras);
223
224 ExtrinsicOptions options = cfg.options;
225 result.optimization = optimize_extrinsics(views, init_cameras, result.initial_guess.c_se3_r,
226 result.initial_guess.r_se3_t, options);
227 result.success = result.optimization.core.success;
228 return result;
229}
230
231} // namespace calib::pipeline
auto calibrate(const MultiCameraRigConfig &cfg, const std::unordered_map< std::string, PlanarDetections > &detections_by_sensor, const std::unordered_map< std::string, IntrinsicCalibrationOutputs > &intrinsics_by_sensor) const -> MultiCameraCalibrationRunResult
auto calibrate(const StereoPairConfig &cfg, const PlanarDetections &reference_detections, const PlanarDetections &target_detections, const IntrinsicCalibrationOutputs &reference_intrinsics, const IntrinsicCalibrationOutputs &target_intrinsics) const -> StereoCalibrationRunResult
Lens distortion models and correction algorithms.
std::vector< Eigen::Isometry3d > cam
nlohmann::json summary
PlanarDetections detections
Definition loaders.cpp:15
auto make_planar_view(const PlanarImageDetections &detections) -> PlanarView
auto compute_views(const StereoPairConfig &cfg, const PlanarDetections &reference_detections, const PlanarDetections &target_detections, StereoCalibrationRunResult &result) -> std::vector< MulticamPlanarView >
std::vector< PlanarView > MulticamPlanarView
Definition extrinsics.h:20
auto estimate_extrinsic_dlt(const std::vector< MulticamPlanarView > &views, const std::vector< CameraT > &cameras) -> ExtrinsicPoses
Definition extrinsics.h:28
DualBrownConrady< double > DualDistortion
Definition distortion.h:221
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 >
auto invert_brown_conrady(const Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > &forward) -> Eigen::Matrix< Scalar, Eigen::Dynamic, 1 >
Definition distortion.h:166
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > forward
Coefficients for distortion.
Definition distortion.h:200
bool optimize_intrinsics
Solve for camera intrinsics.
Definition extrinsics.h:24
std::vector< Eigen::Isometry3d > c_se3_r
Definition extrinsics.h:23
std::vector< Eigen::Isometry3d > r_se3_t
Definition extrinsics.h:24
ExtrinsicOptimizationResult< PinholeCamera< BrownConradyd > > optimization
Definition extrinsics.h:81
ExtrinsicOptimizationResult< PinholeCamera< BrownConradyd > > optimization
Definition extrinsics.h:48