pcl_projection.cpp
Go to the documentation of this file.
1 /*
2  velo2cam_calibration - Automatic calibration algorithm for extrinsic parameters of a stereo camera and a velodyne
3  Copyright (C) 2017-2018 Jorge Beltran, Carlos Guindel
4 
5  This file is part of velo2cam_calibration.
6 
7  velo2cam_calibration is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 2 of the License, or
10  (at your option) any later version.
11 
12  velo2cam_calibration is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with velo2cam_calibration. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 /*
22  pcl_projection: Project the cloud points to the image
23 */
24 
25 #define DEBUG 1
26 
27 #include "velo2cam_utils.h"
28 #include <ros/ros.h>
29 #include "ros/package.h"
30 #include <tf/tf.h>
31 #include <tf/transform_listener.h>
32 #include <pcl/point_cloud.h>
34 #include <pcl/common/transforms.h>
35 #include <pcl/filters/passthrough.h>
36 #include <pcl_ros/transforms.h>
37 #include <opencv2/opencv.hpp>
38 #include <cv_bridge/cv_bridge.h>
39 #include <sensor_msgs/CameraInfo.h>
44 
45 using namespace std;
46 using namespace sensor_msgs;
47 
49 
50 typedef struct {
51  double r,g,b;
52 } COLOUR;
53 
54 COLOUR // Color map
55 GetColour(double v,double vmin,double vmax)
56 {
57  COLOUR c = {1.0,1.0,1.0}; // white
58  double dv;
59 
60  if (v < vmin) v = vmin;
61  if (v > vmax) v = vmax;
62  dv = vmax - vmin;
63 
64  if (v < (vmin + 0.25 * dv)) {
65  c.r = 0;
66  c.g = 4 * (v - vmin) / dv;
67  } else if (v < (vmin + 0.5 * dv)) {
68  c.r = 0;
69  c.b = 1 + 4 * (vmin + 0.25 * dv - v) / dv;
70  } else if (v < (vmin + 0.75 * dv)) {
71  c.r = 4 * (v - vmin - 0.5 * dv) / dv;
72  c.b = 0;
73  } else {
74  c.g = 1 + 4 * (vmin + 0.75 * dv - v) / dv;
75  c.b = 0;
76  }
77 
78  return(c);
79 }
80 
81 void // Get the TF as an Eigen matrix
82 transformAsMatrix (const tf::Transform& bt, Eigen::Matrix4f &out_mat)
83 {
84  double mv[12];
85  bt.getBasis ().getOpenGLSubMatrix (mv);
86 
87  tf::Vector3 origin = bt.getOrigin ();
88 
89  out_mat (0, 0) = mv[0]; out_mat (0, 1) = mv[4]; out_mat (0, 2) = mv[8];
90  out_mat (1, 0) = mv[1]; out_mat (1, 1) = mv[5]; out_mat (1, 2) = mv[9];
91  out_mat (2, 0) = mv[2]; out_mat (2, 1) = mv[6]; out_mat (2, 2) = mv[10];
92 
93  out_mat (3, 0) = out_mat (3, 1) = out_mat (3, 2) = 0; out_mat (3, 3) = 1;
94  out_mat (0, 3) = origin.x ();
95  out_mat (1, 3) = origin.y ();
96  out_mat (2, 3) = origin.z ();
97 }
98 
99 void // Get the camera_info P as an Eigen matrix
100 projectionAsMatrix (const CameraInfoConstPtr& cinfo_msg, Eigen::Matrix4f &out_mat)
101 {
102  for (int i = 0, k = 0; i < 3; i++) {
103  for (int j = 0; j < 4; j++, k++){
104  out_mat(i, j) = cinfo_msg->P[k];
105  }
106  }
107  out_mat(3,0) = out_mat(3,1) = out_mat(3,2) = 0;
108  out_mat(3,3) = 1;
109 }
110 
111 void callback(const PointCloud2::ConstPtr& pcl_msg, const CameraInfoConstPtr& cinfo_msg, const ImageConstPtr& image_msg){
112 
113 
114  pcl::PointCloud<Velodyne::Point>::Ptr pcl_cloud(new pcl::PointCloud<Velodyne::Point>); // From ROS Msg
115  pcl::PointCloud<Velodyne::Point>::Ptr filtered_pcl_cloud(new pcl::PointCloud<Velodyne::Point>); // Only forward points
116  pcl::PointCloud<Velodyne::Point>::Ptr trans_cloud(new pcl::PointCloud<Velodyne::Point>); // After transformation
117 
118  tf::TransformListener listener;
119  tf::StampedTransform transform;
120 
121  // Get the image
122  cv::Mat image;
123  try{
124  image = cv_bridge::toCvShare(image_msg)->image;
125  }catch (cv_bridge::Exception& e){
126  ROS_ERROR("cv_bridge exception: %s", e.what());
127  return;
128  }
129 
130  // Get the cloud
131  fromROSMsg(*pcl_msg, *pcl_cloud);
132  Velodyne::addRange(*pcl_cloud);
133 
134  // Get the TF
135  if(DEBUG) ROS_INFO("Waiting for TF");
136  try{
137  listener.waitForTransform(image_msg->header.frame_id.c_str(), pcl_msg->header.frame_id.c_str(), ros::Time(0), ros::Duration(20.0));
138  listener.lookupTransform (image_msg->header.frame_id.c_str(), pcl_msg->header.frame_id.c_str(), ros::Time(0), transform);
139  }catch (tf::TransformException& ex) {
140  ROS_WARN("[pcl_projection] TF exception:\n%s", ex.what());
141  return;
142  }
143  if(DEBUG) ROS_INFO("TF available");
144 
145  // Filter points behind image plane (approximation)
146  pcl::PassThrough<Velodyne::Point> pass;
147  pass.setInputCloud (pcl_cloud);
148  pass.setFilterFieldName ("x");
149  pass.setFilterLimits (0.0, 100.0);
150  pass.filter (*filtered_pcl_cloud);
151 
152  // Move the lidar cloud to the camera frame
153  Eigen::Matrix4f transf_mat;
154  transformAsMatrix(transform, transf_mat);
155 
156  // Thanks @irecorte for this procedure:
157  // Get the 4x4 projection matrix
158  Eigen::Matrix4f projection_matrix;
159  projectionAsMatrix(cinfo_msg, projection_matrix);
160 
161  // Get the array of lidar points
162  Eigen::MatrixXf points_all = filtered_pcl_cloud->getMatrixXfMap();
163 
164  // Only x,y,z,1 (homogeneous coordinates)
165  Eigen::MatrixXf points = points_all.topRows(4);
166  points.row(3) = Eigen::MatrixXf::Constant(1, points_all.cols(), 1);
167 
168  // Projection
169  Eigen::MatrixXf points_img = projection_matrix * transf_mat * points;
170  points_img.row(0) = points_img.row(0).cwiseQuotient(points_img.row(2));
171  points_img.row(1) = points_img.row(1).cwiseQuotient(points_img.row(2));
172 
173  // Draw the points
174  for (int i = 0; i < points_img.cols(); i++){
175  cv::Point2i p(points_img(0,i), points_img(1,i));
176  // Only points within the image
177  if (p.x >= 0 && p.x < cinfo_msg->width && p.y >= 0 && p.y < cinfo_msg->height){
178  // Apply colormap to depth
179  float depth = filtered_pcl_cloud->points[i].range;
180  // Want it faster? Try depth = transf_points(2, i)
181  COLOUR c = GetColour(int(depth/20.0*255.0), 0, 255);
182  cv::circle(image, p, 3, cv::Scalar(int(255*c.b),int(255*c.g),int(255*c.r)), -1);
183  }
184  }
185 
186  // Publish
187  pub.publish(cv_bridge::CvImage(image_msg->header, image_msg->encoding, image).toImageMsg());
188  // if(DEBUG) ROS_INFO("Done");
189 }
190 
191 int main(int argc, char **argv){
192  ros::init(argc, argv, "pcl_projection");
193  ros::NodeHandle nh_("~"); // LOCAL
195 
196  // Subscribers
197  message_filters::Subscriber<PointCloud2> pc_sub(nh_, "pointcloud", 1);
198  message_filters::Subscriber<CameraInfo> cinfo_sub(nh_, "camera_info", 1);
199  message_filters::Subscriber<Image> image_sub(nh_, "image", 1);
200 
201  pub = it.advertise("image_with_points", 1);
202 
204  message_filters::Synchronizer<SyncPolicy> sync(SyncPolicy(10), pc_sub, cinfo_sub, image_sub);
205  sync.registerCallback(boost::bind(&callback, _1, _2, _3));
206 
207  ros::spin();
208  return 0;
209 }
CvImageConstPtr toCvShare(const sensor_msgs::ImageConstPtr &source, const std::string &encoding=std::string())
COLOUR GetColour(double v, double vmin, double vmax)
image_transport::Publisher pub
TFSIMD_FORCE_INLINE Matrix3x3 & getBasis()
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
Publisher advertise(const std::string &base_topic, uint32_t queue_size, bool latch=false)
void fromROSMsg(const sensor_msgs::PointCloud2 &cloud, pcl::PointCloud< T > &pcl_cloud)
#define ROS_WARN(...)
ROSCPP_DECL void spin(Spinner &spinner)
bool waitForTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration &timeout, const ros::Duration &polling_sleep_duration=ros::Duration(0.01), std::string *error_msg=NULL) const
TFSIMD_FORCE_INLINE const tfScalar & x() const
Connection registerCallback(C &callback)
TFSIMD_FORCE_INLINE const tfScalar & z() const
void publish(const sensor_msgs::Image &message) const
#define ROS_INFO(...)
void projectionAsMatrix(const CameraInfoConstPtr &cinfo_msg, Eigen::Matrix4f &out_mat)
void transformAsMatrix(const tf::Transform &bt, Eigen::Matrix4f &out_mat)
TFSIMD_FORCE_INLINE const tfScalar & y() const
void addRange(pcl::PointCloud< Velodyne::Point > &pc)
double b
Definition: levinson.cpp:82
double g
Definition: levinson.cpp:82
void callback(const PointCloud2::ConstPtr &pcl_msg, const CameraInfoConstPtr &cinfo_msg, const ImageConstPtr &image_msg)
TFSIMD_FORCE_INLINE Vector3 & getOrigin()
void lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, StampedTransform &transform) const
double r
Definition: levinson.cpp:82
#define DEBUG
#define ROS_ERROR(...)
void getOpenGLSubMatrix(tfScalar *m) const
int main(int argc, char **argv)
sensor_msgs::ImagePtr toImageMsg() const


velo2cam_calibration
Author(s): Jorge Beltran , Carlos Guindel
autogenerated on Thu Feb 28 2019 03:24:25