00001
00008
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
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
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
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
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
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
00149 pcl::PointCloud<PointOut> pclOut;
00150 pcl::copyPointCloud<PointIn, PointOut>(*pclPtr, pclOut);
00151
00152
00153 pcl::PointCloud<pcl::PointXYZ> pclXyz;
00154 pcl::copyPointCloud<PointIn, pcl::PointXYZ>(*pclPtr, pclXyz);
00155
00156
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
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
00180 pcl::PointCloud<pcl::PointXYZ> pclCam;
00181 if (!transformListener.waitForTransform(cameraFrame, cameraInfoPtr->header.stamp,
00182 pclPtr->header.frame_id, pclPtr->header.stamp,
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
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
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
00203 continue;
00204 }
00205
00206 int x = round(p2.x);
00207 int y = round(p2.y);
00208
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
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 }
00225
00226 PLUGINLIB_DECLARE_CLASS(point_cloud_color, point_cloud_color, point_cloud_color::PointCloudColor, nodelet::Nodelet)