问题:如图,已知相机的外参,并已知正方体的底部正方形ABCD在图像上的坐标,如何反投影求得正方形ABCD在世界坐标系上的坐标点?
相机投影和反投影
(粗的代表摄像头坐标系,细的代表世界坐标系)
如图红色为x轴,绿色为y轴,蓝色为z。

主要解决思路是:反投影将图像坐标化为归一化平面。由于摄像头在世界坐标系上的高度已知,将归一化平面的坐标乘以尺度并转换就能反投影得到ABCD在世界坐标系上的点。

1.针孔相机模型的投影与反投影

代码如下

#include <ros/ros.h>
#include <visualization_msgs/Marker.h>
#include<time.h>
 #include <unistd.h>
#include<iostream>
#include<eigen3/Eigen/Core>
#include<eigen3/Eigen/Geometry>
#include <opencv2/opencv.hpp>
#include <tf/transform_broadcaster.h>
using namespace cv;
using namespace std;
using namespace Eigen;


void project_point(Point3f pt3d,Mat R,Mat t,Mat K,Point2f& pt2d)
{
    cv::Mat p3Dw (pt3d);
    // Transform into Camera Coords.
    cv::Mat p3Dc = R*p3Dw+t;
    cout<<"p3Dc"<<p3Dc<<endl;
    // Project into Image
    const float invz = 1/p3Dc.at<float>(2);
    const float x = p3Dc.at<float>(0)*invz;
    const float y = p3Dc.at<float>(1)*invz;

    cout<<"project norm plan x="<<x<<"y="<<y<<endl;
    const float fx=K.at<float>(0,0);
    const float fy=K.at<float>(1,1);
    const float cx=K.at<float>(0,2);
    const float cy=K.at<float>(1,2);
    const float u = fx*x+cx;
    const float v = fy*y+cy;
    pt2d.x=u;
    pt2d.y=v;
}

void unproject_point(Point2f pt2d,Mat R,Mat t,Mat K,Mat& p3Dw)
{
    const float fx=K.at<float>(0,0);
    const float fy=K.at<float>(1,1);
    const float cx=K.at<float>(0,2);
    const float cy=K.at<float>(1,2);

    float u=pt2d.x;
    float v=pt2d.y;
    float x=(u-cx)/fx;
    float y=(v-cy)/fy;
    Point3f p3DNormPlan;
    p3DNormPlan.x=x;
    p3DNormPlan.y=y;
    p3DNormPlan.z=1;
    Mat mp3Dc(p3DNormPlan);

    cout<<"unproject normplan"<<p3DNormPlan<<endl;

    float static_y=t.at<float>(0,1);
    cout<<"static_y"<<static_y<<endl;

    Mat mp3DNormPlanWorld=R.inv()*(mp3Dc-t);
    cout<<"mp3DNormPlanWorld"<<mp3DNormPlanWorld<<endl;

    double scale=(double)fabs(static_y/mp3DNormPlanWorld.at<float>(0,1));

    cout<<"scale"<<scale<<endl;

    p3Dw=scale*mp3DNormPlanWorld;
    cout<<"p3Dw"<<p3Dw<<endl;

}

int main( int argc, char** argv )
{

  ros::init(argc, argv, "projecttest");
   float len=4;
  //1.1 get para intrinsic
  float f = 200, w = 1000, h = 1000;
  cv::Mat K = (Mat_<float>(3, 3) <<
               f, 0, w/2,
               0, f, h/2,
               0, 0, 1);
  //1.2 get para extrinsic
  Mat cv_R12=(cv::Mat_<float>(3, 3) <<
                   0,0,1,
                   -1,0,0,
                   0,-1,0);
  cv::Mat cv_t12 = (cv::Mat_<float>(3, 1) << 0,0,len/2);
  Eigen::Matrix3d e_R12;
  e_R12<<0,0,1,
          -1,0,0,
          0,-1,0;

  //t
  Eigen::Vector3d e_t12={0,0,len/2};


  Mat cv_R21=cv_R12.inv();
  Mat cv_t21=-cv_R12.inv()*cv_t12;
  Eigen::Matrix3d e_R21=e_R12.inverse();
  Eigen::Vector3d e_t21=-e_R12.inverse()*e_t12;
  cout<<"e_t21"<<e_t21<<endl;

  tf::TransformBroadcaster br;
  tf::Transform transform;
  transform.setOrigin( tf::Vector3(e_t12(0), e_t12(1), e_t12(2)));
  Eigen::Quaterniond eq(e_R12);
  tf::Quaternion tq(eq.x(),eq.y(),eq.z(),eq.w());

  transform.setRotation(tq);
  ros::NodeHandle n;
  ros::Publisher marker_pub = n.advertise<visualization_msgs::Marker>("visualization_marker", 1);
  // Set our initial shape type to be a cube
  //uint32_t shape = visualization_msgs::Marker::CUBE;
  int k=0;
  cout<<"start program"<<endl;
   //2.get cube position and publish
  while(k<2)
  {
      visualization_msgs::Marker marker;
      marker.header.frame_id = "/world";
      marker.header.stamp = ros::Time::now();
      // Set the namespace and id for this marker.  This serves to create a unique ID
      // Any marker sent with the same namespace and id will overwrite the old one
      marker.ns = "basic_shapes";
      marker.id = 0;
      // Set the marker type.  Initially this is CUBE, and cycles between that and SPHERE, ARROW, and CYLINDER
      marker.type = visualization_msgs::Marker::CUBE;
      // Set the marker action.  Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL)
      marker.action = visualization_msgs::Marker::ADD;
      // Set the pose of the marker.  This is a full 6DOF pose relative to the frame/time specified in the header
      marker.pose.position.x = 1.5*len;
      marker.pose.position.y = 0;
      marker.pose.position.z = len/2;
      marker.pose.orientation.x = 0.0;
      marker.pose.orientation.y = 0.0;
      marker.pose.orientation.z = 0.0;
      marker.pose.orientation.w = 1.0;

      // Set the scale of the marker -- 1x1x1 here means 1m on a side
      marker.scale.x = len;
      marker.scale.y = len;
      marker.scale.z = len;

      // Set the color -- be sure to set alpha to something non-zero!
      marker.color.r = 0.0f;
      marker.color.g = 1.0f;
      marker.color.b = 0.0f;
      marker.color.a = 1.0;
      //marker.lifetime = ros::Duration();
      marker_pub.publish(marker);
      sleep(1);
      k++;
      br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world", "camera"));
  }

//  //x>0 ABCD EFGH
  Point3f cube_center={len,0,len/2};
  Point3f A={cube_center.x-len/2,cube_center.y+len/2,cube_center.z-len/2};

  Point3f B={cube_center.x-len/2,cube_center.y-len/2,cube_center.z-len/2};


  Point3f C={cube_center.x+len/2,cube_center.y-len/2,cube_center.z-len/2};
  Point3f D={cube_center.x+len/2,cube_center.y+len/2,cube_center.z-len/2};


  cout<<"ABCD"<<A<< B<<C<<D<<endl;
  vector<Point3f> vp3d;
  vp3d.push_back(A);
  vp3d.push_back(B);
  vp3d.push_back(C);
  vp3d.push_back(D);

  //3.get binImage
  vector<Point2f> vpt2d;
  vector<Point> contour;
  for(size_t i=0;i<vp3d.size();i++)
  {
      Point2f prj_p;
      project_point(vp3d[i],cv_R21,cv_t21,K,prj_p);
      cout<<"prj_p i="<<i<<" "<<prj_p<<endl;
      Point p2d={(int)prj_p.x,(int)prj_p.y};
      vpt2d.push_back(prj_p);
      contour.push_back(p2d);
  }
  Mat binImage=Mat::zeros(cvSize(w,h),CV_8UC1);
 // Mat mask = Mat::zeros(binImage.size(), CV_8UC1);
  vector<vector<Point> > contours;
  contours.push_back(contour);
  cv::drawContours(binImage, contours, -1, cv::Scalar::all(255),CV_FILLED);



  for(size_t i=0;i<vpt2d.size();i++)
  {
      Mat mp3d;
      unproject_point(vpt2d[i],cv_R21,cv_t21,K,mp3d);
//      cout<<"x="<<mp3d.at<float>(0,0)<<
//            "y="<<mp3d.at<float>(1,0)<<
//            "z="<<mp3d.at<float>(2,0)<<endl;
  }

  imshow("binImage",binImage);
  waitKey();
  //4.unproject to plan
 // cout<<"prj2dp"<<prj_A<<endl;


}

2.鱼眼相机的投影与反投影

鱼眼相机代码如下
主要的是调用opencv自带的投影和反投影函数。
其中反投影函数undistortPoints 如果不输入外参,输出的是归一化的3d点

#include <ros/ros.h>
#include <visualization_msgs/Marker.h>
#include<time.h>
 #include <unistd.h>
#include<iostream>
#include<eigen3/Eigen/Core>
#include<eigen3/Eigen/Geometry>
#include <opencv2/opencv.hpp>
#include <tf/transform_broadcaster.h>
using namespace cv;
using namespace std;
using namespace Eigen;



int main( int argc, char** argv )
{
  ros::init(argc, argv, "projecttest");
   float len=4;
  //1.1 get para intrinsic
  float f = 200, w = 1000, h = 1000;
  cv::Mat K = (Mat_<float>(3, 3) <<
               f, 0, w/2,
               0, f, h/2,
               0, 0, 1);
  cv::Mat coff=(Mat_<float>(1,4) <<0,0.001,0,0.0001);
  //1.2 get para extrinsic
  Mat cv_R12=(cv::Mat_<float>(3, 3) <<
                   0,0,1,
                   -1,0,0,
                   0,-1,0);
  cv::Mat cv_t12 = (cv::Mat_<float>(3, 1) << 0,0,len/2);
  Eigen::Matrix3d e_R12;
  e_R12<<0,0,1,
          -1,0,0,
          0,-1,0;

  //t
  Eigen::Vector3d e_t12={0,0,len/2};

  Mat cv_R21=cv_R12.inv();
  Mat cv_t21=-cv_R12.inv()*cv_t12;
  Eigen::Matrix3d e_R21=e_R12.inverse();
  Eigen::Vector3d e_t21=-e_R12.inverse()*e_t12;
  cout<<"e_t21"<<e_t21<<endl;

  tf::TransformBroadcaster br;
  tf::Transform transform;
  transform.setOrigin( tf::Vector3(e_t12(0), e_t12(1), e_t12(2)));
  Eigen::Quaterniond eq(e_R12);
  tf::Quaternion tq(eq.x(),eq.y(),eq.z(),eq.w());

  transform.setRotation(tq);
  ros::NodeHandle n;
  ros::Publisher marker_pub = n.advertise<visualization_msgs::Marker>("visualization_marker", 1);
  // Set our initial shape type to be a cube
  //uint32_t shape = visualization_msgs::Marker::CUBE;
  int k=0;
  cout<<"start program"<<endl;
   //2.get cube position and publish
  while(k<2)
  {
      visualization_msgs::Marker marker;
      marker.header.frame_id = "/world";
      marker.header.stamp = ros::Time::now();
      // Set the namespace and id for this marker.  This serves to create a unique ID
      // Any marker sent with the same namespace and id will overwrite the old one
      marker.ns = "basic_shapes";
      marker.id = 0;
      // Set the marker type.  Initially this is CUBE, and cycles between that and SPHERE, ARROW, and CYLINDER
      marker.type = visualization_msgs::Marker::CUBE;
      // Set the marker action.  Options are ADD, DELETE, and new in ROS Indigo: 3 (DELETEALL)
      marker.action = visualization_msgs::Marker::ADD;
      // Set the pose of the marker.  This is a full 6DOF pose relative to the frame/time specified in the header
      marker.pose.position.x = 1.5*len;
      marker.pose.position.y = 0;
      marker.pose.position.z = len/2;
      marker.pose.orientation.x = 0.0;
      marker.pose.orientation.y = 0.0;
      marker.pose.orientation.z = 0.0;
      marker.pose.orientation.w = 1.0;

      // Set the scale of the marker -- 1x1x1 here means 1m on a side
      marker.scale.x = len;
      marker.scale.y = len;
      marker.scale.z = len;

      // Set the color -- be sure to set alpha to something non-zero!
      marker.color.r = 0.0f;
      marker.color.g = 1.0f;
      marker.color.b = 0.0f;
      marker.color.a = 1.0;
      //marker.lifetime = ros::Duration();
      marker_pub.publish(marker);
      sleep(1);
      k++;
      br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "world", "camera"));
  }

//  //x>0 ABCD EFGH
  Point3f cube_center={len,0,len/2};
  Point3f A={cube_center.x-len/2,cube_center.y+len/2,cube_center.z-len/2};

  Point3f B={cube_center.x-len/2,cube_center.y-len/2,cube_center.z-len/2};


  Point3f C={cube_center.x+len/2,cube_center.y-len/2,cube_center.z-len/2};
  Point3f D={cube_center.x+len/2,cube_center.y+len/2,cube_center.z-len/2};


  cout<<"ABCD"<<A<< B<<C<<D<<endl;
  vector<Point3f> vp3d;
  vp3d.push_back(A);
  vp3d.push_back(B);
  vp3d.push_back(C);
  vp3d.push_back(D);

  //3.get binImage
  vector<Point2f> vpt2d;
  vector<Point> contour;
  cv::Mat rvec21;
  Rodrigues(cv_R21,rvec21);
  projectPoints(vp3d,rvec21,cv_t21,K,coff,vpt2d);
  for(size_t i=0;i<vpt2d.size();i++)
  {
      cout<<"prj x="<<vpt2d[i].x<<"  y="<<vpt2d[i].y<<endl;
      Point pt;
      pt.x=vpt2d[i].x;
      pt.y=vpt2d[i].y;
      contour.push_back(pt);

  }

  Mat binImage=Mat::zeros(cvSize(w,h),CV_8UC1);
 // Mat mask = Mat::zeros(binImage.size(), CV_8UC1);
  vector<vector<Point> > contours;
  contours.push_back(contour);
  cv::drawContours(binImage, contours, -1, cv::Scalar::all(255),CV_FILLED);

  std::vector<cv::Point2f> upj2d_norm_plan_cam;
  undistortPoints(vpt2d,upj2d_norm_plan_cam,K,coff);

   float static_world_z=cv_t21.at<float>(0,1);
  for(size_t i=0;i<upj2d_norm_plan_cam.size();i++ )
  {
      cout<<"norm x="<<upj2d_norm_plan_cam[i].x<<"  y="<<upj2d_norm_plan_cam[i].y<<endl;

      Point3f tp3d;
      tp3d.x=upj2d_norm_plan_cam[i].x;
      tp3d.y=upj2d_norm_plan_cam[i].y;
      tp3d.z=1;
      Mat mPt(tp3d);
      Mat mp3DNormPlanWorld=cv_R21.inv()*(mPt-cv_t21);
      cout<<"mp3DNormPlanWorld"<<mp3DNormPlanWorld<<endl;
      double scale=(double)fabs(static_world_z/mp3DNormPlanWorld.at<float>(0,1));

      Mat mp3DPlanWorld=scale*mp3DNormPlanWorld;

      cout<<"mp3DPlanWorld"<<mp3DPlanWorld<<endl;
  }

  imshow("binImage",binImage);
  waitKey();
  return 0;
}

相关文章: