Calibration Library 1.0.0
A C++ library for camera calibration and vision-related geometric transformations
Loading...
Searching...
No Matches
bundle_stage.cpp
Go to the documentation of this file.
5
6namespace calib::pipeline {
7
10 result.name = name();
11
12 if (context.intrinsic_results.empty()) {
13 result.summary["status"] = "waiting_for_intrinsic_stage";
14 result.success = false;
15 return result;
16 }
17 if (!context.has_bundle_config()) {
18 result.summary["status"] = "missing_config";
19 result.success = false;
20 return result;
21 }
22
23 const auto& cfg = context.bundle_config();
24 if (cfg.rigs.empty()) {
25 result.summary["status"] = "no_rigs_configured";
26 result.success = false;
27 return result;
28 }
29
30 const auto sensor_index = detail::build_sensor_index(context.dataset.planar_cameras);
31
32 context.bundle_results.clear();
33 if (!context.artifacts.is_object()) {
34 context.artifacts = nlohmann::json::object();
35 }
36 auto& bundle_artifacts = context.artifacts["bundle"];
37 if (!bundle_artifacts.is_object()) {
38 bundle_artifacts = nlohmann::json::object();
39 }
40
41 const HandEyePipelineConfig* handeye_cfg_ptr =
42 context.has_handeye_config() ? &context.handeye_config() : nullptr;
43
44 bool overall_success = true;
45 bool any_success = false;
46 nlohmann::json rigs_json = nlohmann::json::array();
47
48 for (const auto& rig : cfg.rigs) {
49 nlohmann::json rig_json;
50 rig_json["rig_id"] = rig.rig_id;
51 rig_json["sensor_count"] = rig.sensors.size();
52 rig_json["min_angle_deg"] = rig.min_angle_deg;
53
54 const auto* observations = detail::select_bundle_observations(rig, handeye_cfg_ptr);
55 const std::size_t requested_views = observations != nullptr ? observations->size() : 0U;
56
57 if (observations == nullptr || observations->empty()) {
58 rig_json["status"] = "no_observations";
59 rig_json["observations"] =
60 nlohmann::json::object({{"requested", requested_views}, {"used", 0}});
61 rigs_json.push_back(std::move(rig_json));
62 overall_success = false;
63 continue;
64 }
65
66 auto& rig_artifact = bundle_artifacts[rig.rig_id];
67 if (!rig_artifact.is_object()) {
68 rig_artifact = nlohmann::json::object();
69 }
70 rig_artifact["options"] = rig.options;
71 rig_artifact["min_angle_deg"] = rig.min_angle_deg;
72
73 const auto sensor_setup =
74 detail::collect_bundle_sensor_setup(rig, context.intrinsic_results);
75 if (!sensor_setup.missing_sensors.empty() ||
76 sensor_setup.cameras.size() != rig.sensors.size()) {
77 rig_json["status"] = "missing_intrinsics";
78 rig_json["observations"] =
79 nlohmann::json::object({{"requested", requested_views}, {"used", 0}});
80 rigs_json.push_back(std::move(rig_json));
81 overall_success = false;
82 continue;
83 }
84
85 auto view_result = detail::collect_bundle_observations(
86 *observations, rig.sensors, sensor_setup.sensor_to_index, sensor_index,
87 context.intrinsic_results);
88
89 rig_json["observations"] = nlohmann::json::object(
90 {{"requested", requested_views}, {"used", view_result.observations.size()}});
91 rig_json["views"] = view_result.views;
92
93 if (view_result.observations.empty()) {
94 rig_json["status"] = "no_valid_observations";
95 rigs_json.push_back(std::move(rig_json));
96 overall_success = false;
97 continue;
98 }
99
100 auto handeye_init = detail::compute_handeye_initialization(rig, context.handeye_results,
101 view_result.accumulators);
102 rig_json["handeye_initialization"] = handeye_init.report;
103
104 auto target_init =
105 detail::choose_initial_target(rig, view_result.accumulators, handeye_init.transforms);
106 rig_json["initial_target_source"] = target_init.source;
107
108 rig_artifact["initial_hand_eye"] = handeye_init.report;
109 rig_artifact["initial_target"] = target_init.pose;
110
111 if (handeye_init.failed && !rig.initial_target.has_value()) {
112 overall_success = false;
113 }
114
115 BundleOptions options = rig.options;
116 try {
117 auto bundle_result =
118 optimize_bundle(view_result.observations, sensor_setup.cameras,
119 handeye_init.transforms, target_init.pose, options);
120
121 nlohmann::json result_json;
122 result_json["success"] = bundle_result.core.success;
123 result_json["final_cost"] = bundle_result.core.final_cost;
124 result_json["report"] = bundle_result.core.report;
125 result_json["b_se3_t"] = bundle_result.b_se3_t;
126 result_json["g_se3_c"] = bundle_result.g_se3_c;
127 result_json["cameras"] = bundle_result.cameras;
128 if (bundle_result.core.covariance.size() > 0) {
129 result_json["covariance"] = bundle_result.core.covariance;
130 }
131
132 rig_artifact["result"] = result_json;
133 rig_artifact["views"] = rig_json["views"];
134
135 rig_json["success"] = bundle_result.core.success;
136 rig_json["final_cost"] = bundle_result.core.final_cost;
137
138 if (bundle_result.core.success) {
139 rig_json["status"] = "ok";
140 any_success = true;
141 context.bundle_results[rig.rig_id] = bundle_result;
142 } else {
143 rig_json["status"] = "optimization_failed";
144 overall_success = false;
145 }
146 } catch (const std::exception& ex) {
147 rig_json["status"] = "optimization_error";
148 rig_json["error"] = ex.what();
149 rig_artifact["error"] = ex.what();
150 overall_success = false;
151 }
152
153 rigs_json.push_back(std::move(rig_json));
154 }
155
156 result.summary["rigs"] = std::move(rigs_json);
157 if (any_success && overall_success) {
158 result.summary["status"] = "ok";
159 result.success = true;
160 } else if (any_success) {
161 result.summary["status"] = "partial_success";
162 result.success = false;
163 } else {
164 result.summary["status"] = "failed";
165 result.success = false;
166 }
167
168 return result;
169}
170
171} // namespace calib::pipeline
auto run(PipelineContext &context) -> PipelineStageResult override
const std::vector< HandEyeObservationConfig > * select_bundle_observations(const BundleRigConfig &rig, const HandEyePipelineConfig *handeye_cfg)
auto collect_bundle_sensor_setup(const BundleRigConfig &rig, const std::unordered_map< std::string, IntrinsicCalibrationOutputs > &intrinsics) -> BundleSensorSetup
auto compute_handeye_initialization(const BundleRigConfig &rig, const std::unordered_map< std::string, std::unordered_map< std::string, HandeyeResult > > &handeye_results, const std::vector< SensorAccumulator > &accumulators) -> HandeyeInitializationResult
auto choose_initial_target(const BundleRigConfig &rig, const std::vector< SensorAccumulator > &accumulators, const std::vector< Eigen::Isometry3d > &init_g_se3_c) -> TargetInitializationResult
auto collect_bundle_observations(const std::vector< HandEyeObservationConfig > &observation_config, const std::vector< std::string > &sensors, const std::unordered_map< std::string, std::size_t > &sensor_to_index, const std::unordered_map< std::string, SensorDetectionsIndex > &sensor_index, const std::unordered_map< std::string, IntrinsicCalibrationOutputs > &intrinsics) -> BundleViewProcessingResult
auto build_sensor_index(const std::vector< PlanarDetections > &detections) -> std::unordered_map< std::string, SensorDetectionsIndex >
auto optimize_bundle(const std::vector< BundleObservation > &observations, const std::vector< CameraT > &initial_cameras, const std::vector< Eigen::Isometry3d > &init_g_se3_c, const Eigen::Isometry3d &init_b_se3_t, const BundleOptions &opts={}) -> BundleResult< CameraT >
Perform bundle-adjustment style optimisation of the hand-eye calibration problem.
Definition bundle.cpp:148
Pipeline configuration helpers for hand-eye and bundle adjustment stages.
Options controlling the hand-eye calibration optimisation.
Definition bundle.h:30