point_cloud_color.cpp
Go to the documentation of this file.
00001 
00008 //#define USE_MT_NODE_HANDLE
00009 
00010 #include <limits>
00011 #include <boost/format.hpp>
00012 
00013 #include <cv_bridge/cv_bridge.h>
00014 #include <image_geometry/pinhole_camera_model.h>
00015 #include <image_transport/image_transport.h>
00016 #include <nifti_pcl_common/point_types.h>
00017 #include <nodelet/nodelet.h>
00018 #include <pcl/io/io.h>
00019 #include <pcl/point_cloud.h>
00020 #include <pcl/point_types.h>
00021 #include <pcl_ros/point_cloud.h>
00022 #include <pcl_ros/transforms.h>
00023 #include <pluginlib/class_list_macros.h>
00024 #include <ros/ros.h>
00025 #include <sensor_msgs/image_encodings.h>
00026 #include <sensor_msgs/CameraInfo.h>
00027 #include <tf/transform_listener.h>
00028 
00029 typedef nifti_pcl_common::LaserPoint PointIn;
00030 typedef nifti_pcl_common::AssemblerPoint PointOut;
00031 
00032 using namespace sensor_msgs;
00033 using namespace std;
00034 
00035 namespace point_cloud_color {
00036 
00037 class PointCloudColor: public nodelet::Nodelet {
00038 public:
00039   PointCloudColor();
00040   virtual ~PointCloudColor();
00041   void onInit();
00042   void cameraCallback(const sensor_msgs::ImageConstPtr &imageMsg, const sensor_msgs::CameraInfoConstPtr &cameraInfoMsg);
00043   void pointCloudCallback(const pcl::PointCloud<PointIn>::ConstPtr &pointCloudMsg);
00044   EIGEN_MAKE_ALIGNED_OPERATOR_NEW
00045 private:
00046   tf::TransformListener transformListener;
00047   vector<image_transport::CameraSubscriber> cameraSubscribers;
00048   ros::Subscriber pointCloudSub;
00049   ros::Publisher pointCloudPub;
00050   ros::Subscriber pointCloud2Sub;
00051   ros::Publisher pointCloud2Pub;
00052   map<string, cv_bridge::CvImageConstPtr> images;
00053   map<string, sensor_msgs::CameraInfoConstPtr> cameraInfos;
00054   string targetFrame;
00055   string fixedFrame;
00056   int numCameras;
00057   double maxImageAge;
00058   bool useFirstValid;
00059   bool unrectify;
00060   int imageQueueSize;
00061   int pointCloudQueueSize;
00062   double waitForTransform;
00063 };
00064 
00065 PointCloudColor::PointCloudColor() :
00066     transformListener(ros::Duration(15.0)),
00067     targetFrame("/laser"),
00068     fixedFrame("/odom"),
00069     numCameras(1),
00070     maxImageAge(5.0),
00071     useFirstValid(true),
00072     unrectify(false),
00073     imageQueueSize(1),
00074     pointCloudQueueSize(1),
00075     waitForTransform(1.0) {
00076 }
00077 
00078 PointCloudColor::~PointCloudColor() {
00079 }
00080 
00081 void PointCloudColor::onInit() {
00082   NODELET_INFO("PointCloudColor::onInit: Initializing...");
00083 
00084 #ifdef USE_MT_NODE_HANDLE
00085   ros::NodeHandle &nh = getMTNodeHandle();
00086   ros::NodeHandle &pnh = getMTPrivateNodeHandle();
00087 #else
00088   ros::NodeHandle &nh = getNodeHandle();
00089   ros::NodeHandle &pnh = getPrivateNodeHandle();
00090 #endif
00091 
00092   // Get and process parameters.
00093   pnh.param("target_frame", targetFrame, targetFrame);
00094   NODELET_INFO("PointCloudColor::onInit: Target frame: %s.", targetFrame.c_str());
00095   pnh.param("fixed_frame", fixedFrame, fixedFrame);
00096   NODELET_INFO("PointCloudColor::onInit: Fixed frame: %s.", fixedFrame.c_str());
00097   pnh.param("num_cameras", numCameras, numCameras);
00098   numCameras = numCameras >= 0 ? numCameras : 0;
00099   NODELET_INFO("PointCloudColor::onInit: Number of cameras: %i.", numCameras);
00100   pnh.param("max_image_age", maxImageAge, maxImageAge);
00101   NODELET_INFO("PointCloudColor::onInit: Maximum image age: %.1f s.", maxImageAge);
00102   pnh.param("use_first_valid", useFirstValid, useFirstValid);
00103   NODELET_INFO("PointCloudColor::onInit: Use first valid projection: %s.", useFirstValid ? "yes" : "no");
00104   pnh.param("unrectify", unrectify, unrectify);
00105   NODELET_INFO("PointCloudColor::onInit: Unrectify points: %s.", unrectify ? "yes" : "no");
00106   pnh.param("image_queue_size", imageQueueSize, imageQueueSize);
00107   imageQueueSize = imageQueueSize >= 1 ? imageQueueSize : 1;
00108   NODELET_INFO("PointCloudColor::onInit: Image queue size: %i.", imageQueueSize);
00109   pnh.param("point_cloud_queue_size", pointCloudQueueSize, pointCloudQueueSize);
00110   pointCloudQueueSize = pointCloudQueueSize >= 1 ? pointCloudQueueSize : 1;
00111   NODELET_INFO("PointCloudColor::onInit: Point cloud queue size: %i.", pointCloudQueueSize);
00112   pnh.param("wait_for_transform", waitForTransform, waitForTransform);
00113   waitForTransform = waitForTransform >= 0.0 ? waitForTransform : 0.0;
00114   NODELET_INFO("PointCloudColor::onInit: Wait for transform timeout: %.2f s.", waitForTransform);
00115 
00116   // Subscribe list of camera topics.
00117   image_transport::ImageTransport it(nh);
00118   cameraSubscribers.resize(numCameras);
00119   for (int iCam = 0; iCam < numCameras; iCam++) {
00120     string cameraTopic = nh.resolveName((boost::format("camera_%i/image") % iCam).str(), true);
00121     NODELET_INFO("PointCloudColor::onInit: Subscribing camera %s.", cameraTopic.c_str());
00122     cameraSubscribers[iCam] = it.subscribeCamera(cameraTopic, imageQueueSize, &PointCloudColor::cameraCallback, this);
00123   }
00124 
00125   // Subscribe point cloud topic.
00126   string pclInTopic = nh.resolveName("pcl_in", true);
00127   NODELET_INFO("PointCloudColor::onInit: Subscribing point cloud %s.", pclInTopic.c_str());
00128   pointCloudSub = nh.subscribe<pcl::PointCloud<PointIn> >(pclInTopic, pointCloudQueueSize, &PointCloudColor::pointCloudCallback, this);
00129 
00130   // Advertise colored point cloud topic.
00131   string pclOutTopic = nh.resolveName("pcl_out", true);
00132   NODELET_INFO("PointCloudColor::onInit: Advertising colored point cloud %s.", pclOutTopic.c_str());
00133   pointCloudPub = nh.advertise<pcl::PointCloud<PointOut> >(pclOutTopic, pointCloudQueueSize, false);
00134 }
00135 
00136 void PointCloudColor::cameraCallback(const sensor_msgs::ImageConstPtr &imageMsg, const sensor_msgs::CameraInfoConstPtr &cameraInfoMsg) {
00137   NODELET_DEBUG("PointCloudColor::cameraCallback: Image frame %s, camera info frame %s.", imageMsg->header.frame_id.c_str(), cameraInfoMsg->header.frame_id.c_str());
00138   // Use frame id from image message for both image and camera info to ensure consistency.
00139   images[imageMsg->header.frame_id] = cv_bridge::toCvShare(imageMsg, sensor_msgs::image_encodings::BGR8);
00140   cameraInfos[imageMsg->header.frame_id] = cameraInfoMsg;
00141 }
00142 
00143 void PointCloudColor::pointCloudCallback(const pcl::PointCloud<PointIn>::ConstPtr &pclPtr) {
00144   ros::Time t1 = ros::Time::now();
00145   size_t numPoints = pclPtr->points.size();
00146   NODELET_DEBUG("PointCloudColor::pointCloudCallback: Point cloud received (%lu points).", numPoints);
00147 
00148   // Prepare output point cloud.
00149   pcl::PointCloud<PointOut> pclOut;
00150   pcl::copyPointCloud<PointIn, PointOut>(*pclPtr, pclOut);
00151 
00152   // For point projections get rid off the fields we don't need.
00153   pcl::PointCloud<pcl::PointXYZ> pclXyz;
00154   pcl::copyPointCloud<PointIn, pcl::PointXYZ>(*pclPtr, pclXyz);
00155 
00156   // Initialize vector with projection distances from image center, used as a quality measure.
00157   vector<double> dist(numPoints, DBL_MAX);
00158   map<string, cv_bridge::CvImageConstPtr>::iterator itImages;
00159   for (itImages = images.begin(); itImages != images.end(); itImages++) {
00160     string cameraFrame = (*itImages).first;
00161     cv_bridge::CvImageConstPtr imagePtr  = images[cameraFrame];
00162     sensor_msgs::CameraInfoConstPtr cameraInfoPtr = cameraInfos[cameraFrame];
00163     NODELET_DEBUG("Map key %s, image frame %s, camera info frame %s.", cameraFrame.c_str(), imagePtr->header.frame_id.c_str(), cameraInfoPtr->header.frame_id.c_str());
00164 
00165     image_geometry::PinholeCameraModel cameraModel;
00166     if (!cameraModel.fromCameraInfo(cameraInfoPtr)) {
00167       NODELET_WARN("Camera model could not be constructed from camera info. Skipping the image...");
00168       continue;
00169     }
00170 
00171     // Check relative age of the point cloud and the image. Skip the image if the time span is to large.
00172     double imageAge = (pclPtr->header.stamp - imagePtr->header.stamp).toSec();
00173     if (imageAge > maxImageAge) {
00174       NODELET_WARN("Image %.1f s > %.1f s older than point cloud. Skipping the image...", imageAge, maxImageAge);
00175       continue;
00176     }
00177     NODELET_DEBUG("PointCloudColor::pointCloudCallback: Using camera %s, image age %.1f s.", cameraFrame.c_str(), imageAge);
00178 
00179     // Transform point cloud into camera frame, skip the image if the transform is not available.
00180     pcl::PointCloud<pcl::PointXYZ> pclCam;
00181     if (!transformListener.waitForTransform(cameraFrame, cameraInfoPtr->header.stamp, // target frame and time
00182                            pclPtr->header.frame_id, pclPtr->header.stamp, // source frame and time
00183                            fixedFrame, ros::Duration(waitForTransform))
00184         || !pcl_ros::transformPointCloud(cameraFrame, imagePtr->header.stamp, pclXyz, fixedFrame, pclCam, transformListener)) {
00185       NODELET_WARN("PointCloudColor::pointCloudCallback: Cannot transform point cloud to camera frame %s. Skipping the image...", cameraFrame.c_str());
00186       continue;
00187     }
00188 
00189     for (size_t iPoint = 0; iPoint < numPoints; iPoint++) {
00190       // Skip points behind the camera.
00191       if (pclCam.points[iPoint].z < 0 || (useFirstValid && dist[iPoint] < DBL_MAX)) {
00192         continue;
00193       }
00194       cv::Point3d p3 = cv::Point3d(pclCam.points[iPoint].x, pclCam.points[iPoint].y, pclCam.points[iPoint].z);
00195       // Get rectified image coordinates.
00196       cv::Point2d p2 = cameraModel.project3dToPixel(p3);
00197       if (unrectify) {
00198         p2 = cameraModel.unrectifyPoint(p2);
00199       }
00200       double d = hypot(cameraInfoPtr->width / 2.0 - p2.x, cameraInfoPtr->height / 2.0 - p2.y);
00201       if (d >= dist[iPoint]) {
00202         // Keep color from the previous projection which we assume is better.
00203         continue;
00204       }
00205       // TODO: Linear interpolation.
00206       int x = round(p2.x);
00207       int y = round(p2.y);
00208       // Skip points outside the image.
00209       if (x < 0 || y < 0 || x >= cameraInfoPtr->width || y >= cameraInfoPtr->height) {
00210         continue;
00211       }
00212       dist[iPoint] = d;
00213       cv::Vec3b px = imagePtr->image.at<cv::Vec3b>(y, x);
00214       // Assuming RGB.
00215       pclOut.points[iPoint].r = px.val[0];
00216       pclOut.points[iPoint].g = px.val[1];
00217       pclOut.points[iPoint].b = px.val[2];
00218     }
00219   }
00220   NODELET_DEBUG("PointCloudColor::pointCloudCallback: Coloring point cloud: %.3f s.", (ros::Time::now() - t1).toSec());
00221   pointCloudPub.publish(pclOut);
00222 }
00223 
00224 } /* namespace point_cloud_color */
00225 
00226 PLUGINLIB_DECLARE_CLASS(point_cloud_color, point_cloud_color, point_cloud_color::PointCloudColor, nodelet::Nodelet)
 All Classes Namespaces Files Functions Variables Typedefs


point_cloud_color
Author(s): Tomas Petricek / petrito1@cmp.felk.cvut.cz
autogenerated on Tue Dec 10 2013 14:28:16