18 #ifndef VISION_COMMON_POSE_ESTIMATION_H_
19 #define VISION_COMMON_POSE_ESTIMATION_H_
32 #include <Eigen/Geometry>
34 #include <ceres/ceres.h>
35 #include <ceres/solver.h>
37 #include <opencv2/calib3d/calib3d.hpp>
38 #include <opencv2/core/eigen.hpp>
39 #include <opencv2/imgcodecs.hpp>
40 #include <opencv2/imgproc.hpp>
41 #include <opencv2/opencv.hpp>
43 #include <boost/optional.hpp>
47 #include <unordered_set>
52 void UndistortedPnP(
const std::vector<cv::Point2d>& undistorted_image_points,
const std::vector<cv::Point3d>& points_3d,
53 const cv::Mat& intrinsics,
const int pnp_method, cv::Mat& rotation, cv::Mat& translation);
55 std::vector<int>
RandomNIndices(
const int num_possible_indices,
const int num_sampled_indices);
57 template <
typename DISTORTER>
58 int Inliers(
const std::vector<Eigen::Vector2d>& image_points,
const std::vector<Eigen::Vector3d>& points_3d,
59 const Eigen::Matrix3d& intrinsics,
const Eigen::VectorXd& distortion,
61 boost::optional<std::vector<int>&> inliers = boost::none);
64 std::vector<T>
SampledValues(
const std::vector<T>& values,
const std::vector<int>& indices);
66 template <
typename DISTORTER>
67 boost::optional<PoseWithCovarianceAndInliers>
RansacPnP(
const std::vector<Eigen::Vector2d>& image_points,
68 const std::vector<Eigen::Vector3d>& points_3d,
69 const Eigen::Matrix3d& intrinsics,
70 const Eigen::VectorXd& distortion,
71 const RansacPnPParams& params);
73 template <
typename DISTORTER>
74 boost::optional<PoseWithCovarianceAndInliers>
ReprojectionPoseEstimate(
const std::vector<Eigen::Vector2d>& image_points,
75 const std::vector<Eigen::Vector3d>& points_3d,
76 const Eigen::Vector2d& focal_lengths,
77 const Eigen::Vector2d& principal_points,
78 const Eigen::VectorXd& distortion,
79 const ReprojectionPoseEstimateParams& params);
81 template <
typename DISTORTER>
82 boost::optional<PoseWithCovarianceAndInliers>
ReprojectionPoseEstimate(
const std::vector<Eigen::Vector2d>& image_points,
83 const std::vector<Eigen::Vector3d>& points_3d,
84 const Eigen::Matrix3d& intrinsics,
85 const Eigen::VectorXd& distortion,
86 const ReprojectionPoseEstimateParams& params);
88 template <
typename DISTORTER>
90 const std::vector<Eigen::Vector2d>& image_points,
const std::vector<Eigen::Vector3d>& points_3d,
91 const Eigen::Vector2d& focal_lengths,
const Eigen::Vector2d& principal_points,
const Eigen::VectorXd& distortion,
92 const ReprojectionPoseEstimateParams& params,
const Eigen::Isometry3d& initial_estimate,
93 const std::vector<int>& initial_inliers);
95 template <
typename DISTORTER>
97 const std::vector<Eigen::Vector2d>& image_points,
const std::vector<Eigen::Vector3d>& points_3d,
98 const Eigen::Matrix3d& intrinsics,
const Eigen::VectorXd& distortion,
const ReprojectionPoseEstimateParams& params,
99 const Eigen::Isometry3d& initial_estimate,
const std::vector<int>& initial_inliers);
101 template <
typename DISTORTER>
102 int Inliers(
const std::vector<Eigen::Vector2d>& image_points,
const std::vector<Eigen::Vector3d>& points_3d,
103 const Eigen::Matrix3d& intrinsics,
const Eigen::VectorXd& distortion,
105 boost::optional<std::vector<int>&> inliers) {
107 for (
int i = 0; i < static_cast<int>(image_points.size()); ++i) {
108 const Eigen::Vector2d& image_point = image_points[i];
111 const Eigen::Vector2d projected_image_point =
112 ProjectWithDistortion<DISTORTER>(transformed_point_3d, intrinsics, distortion);
113 const Eigen::Vector2d
error = (image_point - projected_image_point);
114 const double error_norm =
error.norm();
115 if (error_norm <= max_inlier_threshold) {
117 if (inliers) inliers->emplace_back(i);
123 template <
typename T>
124 std::vector<T>
SampledValues(
const std::vector<T>& values,
const std::vector<int>& indices) {
125 std::vector<T> sampled_values;
126 for (
const int i : indices) {
127 sampled_values.emplace_back(values[i]);
129 return sampled_values;
132 template <
typename DISTORTER>
133 boost::optional<PoseWithCovarianceAndInliers>
RansacPnP(
const std::vector<Eigen::Vector2d>& image_points,
134 const std::vector<Eigen::Vector3d>& points_3d,
135 const Eigen::Matrix3d& intrinsics,
136 const Eigen::VectorXd& distortion,
138 if (image_points.size() < 4) {
139 LogError(
"RansacPnP: Too few matched points given.");
143 const DISTORTER distorter;
144 const std::vector<Eigen::Vector2d> undistorted_image_points =
145 distorter.Undistort(image_points, intrinsics, distortion);
147 std::vector<cv::Point2d> undistorted_image_points_cv;
148 for (
const auto& undistorted_image_point : undistorted_image_points) {
149 undistorted_image_points_cv.emplace_back(
cv::Point2d(undistorted_image_point.x(), undistorted_image_point.y()));
151 std::vector<cv::Point3d> points_3d_cv;
152 for (
const auto& point_3d : points_3d) {
153 points_3d_cv.emplace_back(point_3d.x(), point_3d.y(), point_3d.z());
156 cv::Mat intrinsics_cv;
157 cv::eigen2cv(intrinsics, intrinsics_cv);
158 cv::Mat rodrigues_rotation_cv(3, 1, cv::DataType<double>::type, cv::Scalar(0));
159 cv::Mat translation_cv(3, 1, cv::DataType<double>::type, cv::Scalar(0));
160 int max_num_inliers = 0;
164 constexpr
int kNumSampledValues = 4;
166 const auto sampled_indices =
RandomNIndices(image_points.size(), kNumSampledValues);
167 const auto sampled_undistorted_image_points =
SampledValues(undistorted_image_points_cv, sampled_indices);
168 const auto sampled_points_3d =
SampledValues(points_3d_cv, sampled_indices);
170 rodrigues_rotation_cv, translation_cv);
172 const int num_inliers =
173 Inliers<DISTORTER>(image_points, points_3d, intrinsics, distortion, pose_estimate, params.
max_inlier_threshold);
174 if (num_inliers > max_num_inliers) {
175 best_pose_estimate = pose_estimate;
176 max_num_inliers = num_inliers;
181 LogError(
"RansacPnP: Failed to find pose with enough inliers.");
185 std::vector<int> inliers;
186 Inliers<DISTORTER>(image_points, points_3d, intrinsics, distortion, best_pose_estimate, params.
max_inlier_threshold,
192 template <
typename DISTORTER>
194 const std::vector<Eigen::Vector3d>& points_3d,
195 const Eigen::Vector2d& focal_lengths,
196 const Eigen::Vector2d& principal_points,
197 const Eigen::VectorXd& distortion,
199 if (image_points.size() < 4) {
200 LogError(
"ReprojectionPoseEstimate: Too few matched points given.");
207 const auto ransac_pnp_estimate =
208 RansacPnP<DISTORTER>(image_points, points_3d, intrinsics, distortion, params.
ransac_pnp);
209 if (!ransac_pnp_estimate) {
210 LogError(
"ReprojectionPoseEstimate: Failed to get ransac pnp initial estimate.");
214 const int num_inliers = ransac_pnp_estimate->inliers.size();
217 << num_inliers <<
".");
221 return ReprojectionPoseEstimateWithInitialEstimate<DISTORTER>(
222 image_points, points_3d, focal_lengths, principal_points, distortion, params, ransac_pnp_estimate->pose,
223 ransac_pnp_estimate->inliers);
226 template <
typename DISTORTER>
228 const std::vector<Eigen::Vector2d>& image_points,
const std::vector<Eigen::Vector3d>& points_3d,
229 const Eigen::Vector2d& focal_lengths,
const Eigen::Vector2d& principal_points,
const Eigen::VectorXd& distortion,
231 const std::vector<int>& initial_inliers) {
232 if (image_points.size() < 4) {
233 LogError(
"ReprojectionPoseEstimateWithInitialEstimate: Too few matched points given.");
237 const int num_initial_inliers = initial_inliers.size();
239 LogError(
"ReprojectionPoseEstimateWithInitialEstimate: Too few inliers provided. Need "
248 ceres::Problem problem;
254 problem.AddParameterBlock(pose_estimate_vector.data(), 6);
256 for (
int i = 0; i < num_initial_inliers; ++i) {
257 const int inlier_index = initial_inliers[i];
259 image_points[inlier_index], points_3d[inlier_index], pose_estimate_vector,
260 const_cast<Eigen::Vector2d&
>(focal_lengths),
const_cast<Eigen::Vector2d&
>(principal_points),
264 ceres::Solver::Summary summary;
268 LogError(
"ReprojectionPoseEstimateWithInitialEstimate: Initial estimate: " << std::endl
269 << initial_estimate.matrix() << std::endl
270 <<
"Optimized estimate: " << std::endl
271 << optimized_estimate.matrix()
273 <<
"Summary: " << std::endl
274 << summary.FullReport());
276 if (!summary.IsSolutionUsable()) {
277 LogError(
"ReprojectionPoseEstimateWithInitialEstimate: Failed to find solution.");
281 ceres::Covariance::Options options;
283 std::vector<std::pair<const double*, const double*>> covariance_blocks;
284 covariance_blocks.push_back(std::make_pair(pose_estimate_vector.data(), pose_estimate_vector.data()));
285 if (!covariance.Compute(covariance_blocks, &problem)) {
286 LogError(
"ReprojectionPoseEstimateWithInitialEstimate: Failed to compute covariances.");
291 covariance.GetCovarianceBlock(pose_estimate_vector.data(), pose_estimate_vector.data(), pose_covariance.data());
293 std::vector<int> inliers;
295 Inliers<DISTORTER>(image_points, points_3d, intrinsics, distortion, optimized_estimate, params.
max_inlier_threshold,
300 template <
typename DISTORTER>
302 const std::vector<Eigen::Vector3d>& points_3d,
303 const Eigen::Matrix3d& intrinsics,
304 const Eigen::VectorXd& distortion,
308 return ReprojectionPoseEstimate<DISTORTER>(image_points, points_3d, focal_lengths, principal_points, distortion,
312 template <
typename DISTORTER>
314 const std::vector<Eigen::Vector2d>& image_points,
const std::vector<Eigen::Vector3d>& points_3d,
316 const Eigen::Isometry3d& initial_estimate,
const std::vector<int>& initial_inliers) {
319 return ReprojectionPoseEstimateWithInitialEstimate<DISTORTER>(
320 image_points, points_3d, focal_lengths, principal_points, distortion, params, initial_estimate, initial_inliers);
323 #endif // VISION_COMMON_POSE_ESTIMATION_H_