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_