Calibration Library 1.0.0
A C++ library for camera calibration and vision-related geometric transformations
Loading...
Searching...
No Matches
stereo_stage.cpp
Go to the documentation of this file.
4
5namespace calib::pipeline {
6
7namespace {
8
10
11auto build_detection_lookup(const std::vector<PlanarDetections>& detections)
12 -> std::unordered_map<std::string, const PlanarDetections*> {
13 std::unordered_map<std::string, const PlanarDetections*> lookup;
14 for (const auto& det : detections) {
15 if (!det.sensor_id.empty()) {
16 lookup.emplace(det.sensor_id, &det);
17 }
18 }
19 return lookup;
20}
21
22} // namespace
23
24// TODO: 1. refactor, 2. support multi-camera rigs
27 result.name = name();
28 const std::size_t calibrated_cameras = context.intrinsic_results.size();
29 result.summary["input_cameras"] = calibrated_cameras;
30
31 if (!context.has_stereo_config()) {
32 result.summary["status"] = "missing_config";
33 result.success = false;
34 return result;
35 }
36 if (calibrated_cameras < 2) {
37 result.summary["status"] = "waiting_for_multiple_intrinsic_results";
38 result.success = false;
39 return result;
40 }
41
42 const auto& stereo_cfg = context.stereo_config();
43 result.summary["requested_pairs"] = stereo_cfg.pairs.size();
44 if (stereo_cfg.pairs.empty()) {
45 result.summary["status"] = "no_pairs_configured";
46 result.success = false;
47 return result;
48 }
49
50 const auto detections_by_sensor = build_detection_lookup(context.dataset.planar_cameras);
51
52 if (!context.artifacts.is_object()) {
53 context.artifacts = nlohmann::json::object();
54 }
55 auto& stereo_artifacts = context.artifacts["stereo"];
56 if (!stereo_artifacts.is_object()) {
57 stereo_artifacts = nlohmann::json::object();
58 }
59 stereo_artifacts["pairs"] = nlohmann::json::object();
60 context.stereo_results.clear();
61
63 nlohmann::json pairs_summary = nlohmann::json::array();
64
65 bool all_success = true;
66 bool any_success = false;
67
68 for (const auto& pair_cfg : stereo_cfg.pairs) {
69 nlohmann::json pair_json;
70 pair_json["pair_id"] = pair_cfg.pair_id;
71 pair_json["reference_sensor"] = pair_cfg.reference_sensor;
72 pair_json["target_sensor"] = pair_cfg.target_sensor;
73 pair_json["requested_views"] = pair_cfg.views.size();
74
75 const auto ref_intr_it = context.intrinsic_results.find(pair_cfg.reference_sensor);
76 const auto tgt_intr_it = context.intrinsic_results.find(pair_cfg.target_sensor);
77
78 if (ref_intr_it == context.intrinsic_results.end() ||
79 tgt_intr_it == context.intrinsic_results.end()) {
80 std::vector<std::string> missing;
81 if (ref_intr_it == context.intrinsic_results.end())
82 missing.push_back(pair_cfg.reference_sensor);
83 if (tgt_intr_it == context.intrinsic_results.end())
84 missing.push_back(pair_cfg.target_sensor);
85 pair_json["status"] = "missing_intrinsics";
86 pair_json["missing"] = missing;
87 pair_json["success"] = false;
88 all_success = false;
89 pairs_summary.push_back(std::move(pair_json));
90 continue;
91 }
92
93 const auto ref_det_it = detections_by_sensor.find(pair_cfg.reference_sensor);
94 const auto tgt_det_it = detections_by_sensor.find(pair_cfg.target_sensor);
95 if (ref_det_it == detections_by_sensor.end() || tgt_det_it == detections_by_sensor.end()) {
96 std::vector<std::string> missing;
97 if (ref_det_it == detections_by_sensor.end())
98 missing.push_back(pair_cfg.reference_sensor);
99 if (tgt_det_it == detections_by_sensor.end()) missing.push_back(pair_cfg.target_sensor);
100 pair_json["status"] = "missing_detections";
101 pair_json["missing"] = missing;
102 pair_json["success"] = false;
103 all_success = false;
104 pairs_summary.push_back(std::move(pair_json));
105 continue;
106 }
107
108 try {
109 auto pair_result = facade.calibrate(pair_cfg, *ref_det_it->second, *tgt_det_it->second,
110 ref_intr_it->second, tgt_intr_it->second);
111
112 pair_json["views"] = pair_result.view_summaries;
113 pair_json["used_views"] = pair_result.used_views;
114 pair_json["success"] = pair_result.success;
115 pair_json["status"] = pair_result.success ? "ok" : "failed";
116 pair_json["final_cost"] = pair_result.optimization.core.final_cost;
117
118 if (pair_result.success) {
119 any_success = true;
120 context.stereo_results[pair_cfg.pair_id] = pair_result.optimization;
121 } else {
122 all_success = false;
123 }
124
125 nlohmann::json initial_guess_json;
126 nlohmann::json cams_json = nlohmann::json::array();
127 std::copy(pair_result.initial_guess.c_se3_r.begin(),
128 pair_result.initial_guess.c_se3_r.end(), std::back_inserter(cams_json));
129 nlohmann::json targets_json = nlohmann::json::array();
130 std::copy(pair_result.initial_guess.r_se3_t.begin(),
131 pair_result.initial_guess.r_se3_t.end(), std::back_inserter(targets_json));
132 initial_guess_json["c_se3_r"] = std::move(cams_json);
133 initial_guess_json["r_se3_t"] = std::move(targets_json);
134
135 nlohmann::json artifact;
136 artifact["initial_guess"] = std::move(initial_guess_json);
137 artifact["views"] = pair_json["views"];
138 artifact["optimization"] = pair_result.optimization;
139 artifact["final_cost"] = pair_result.optimization.core.final_cost;
140 stereo_artifacts["pairs"][pair_cfg.pair_id] = std::move(artifact);
141 } catch (const std::exception& ex) {
142 pair_json["status"] = "calibration_error";
143 pair_json["error"] = ex.what();
144 pair_json["success"] = false;
145 all_success = false;
146 }
147
148 pairs_summary.push_back(std::move(pair_json));
149 }
150
151 result.summary["pairs"] = std::move(pairs_summary);
152 if (any_success && all_success) {
153 result.summary["status"] = "ok";
154 result.success = true;
155 } else if (any_success) {
156 result.summary["status"] = "partial_success";
157 result.success = false;
158 } else {
159 result.summary["status"] = "failed";
160 result.success = false;
161 }
162
163 return result;
164}
165
166} // namespace calib::pipeline
auto calibrate(const StereoPairConfig &cfg, const PlanarDetections &reference_detections, const PlanarDetections &target_detections, const IntrinsicCalibrationOutputs &reference_intrinsics, const IntrinsicCalibrationOutputs &target_intrinsics) const -> StereoCalibrationRunResult
auto run(PipelineContext &context) -> PipelineStageResult override
PlanarDetections detections
Definition loaders.cpp:15
auto build_sensor_index(const std::vector< PlanarDetections > &detections) -> std::unordered_map< std::string, SensorDetectionsIndex >