point_cloud_to_camera.cpp
Go to the documentation of this file.
00001 
00004 //#define USE_MT_NODE_HANDLE
00005 #include <boost/bind.hpp>
00006 #include <boost/format.hpp>
00007 #include <cv_bridge/cv_bridge.h>
00008 #include <image_transport/image_transport.h>
00009 #include <limits>
00010 #include <nodelet/nodelet.h>
00011 #include <opencv2/opencv.hpp>
00012 #include <pcl/io/io.h>
00013 #include <pluginlib/class_list_macros.h>
00014 #include <ros/ros.h>
00015 #include <sensor_msgs/CameraInfo.h>
00016 #include <sensor_msgs/image_encodings.h>
00017 #include <sensor_msgs/PointCloud2.h>
00018 #include <tf/transform_datatypes.h>
00019 #include <tf/transform_listener.h>
00020 
00021 #include <deque>          // std::deque
00022 #include <list>           // std::list
00023 
00024 #define NO_EXTTYPE
00025 #include <stdio.h>
00026 #include "dynamic/num_array.h"
00027 
00028 int getFieldIndex(const sensor_msgs::PointCloud2 &cloud, const std::string fieldName) {
00029     for (size_t i = 0; i < cloud.fields.size(); i++) {
00030         if (cloud.fields[i].name == fieldName) {
00031             return i;
00032         }
00033     }
00034     return -1;
00035 }
00036 
00037 cv::Mat rotationFromTransform(const tf::Transform &t) {
00038     return (cv::Mat_<double>(3, 3) << t.getBasis()[0][0], t.getBasis()[0][1], t.getBasis()[0][2],
00039             t.getBasis()[1][0], t.getBasis()[1][1], t.getBasis()[1][2],
00040             t.getBasis()[2][0], t.getBasis()[2][1], t.getBasis()[2][2]);
00041 }
00042 
00043 cv::Mat translationFromTransform(const tf::Transform &t) {
00044     return (cv::Mat_<double>(3, 1) << t.getOrigin()[0], t.getOrigin()[1], t.getOrigin()[2]);
00045 }
00046 
00047 cv::Mat matFromCloud(sensor_msgs::PointCloud2ConstPtr cloud, const std::string fieldName, const int numElements) {
00048     const int numPoints = cloud->width * cloud->height;
00049     int fieldIndex = getFieldIndex(*cloud, fieldName);
00050     void *data = const_cast<void *>(static_cast<const void *>(&cloud->data[cloud->fields[fieldIndex].offset]));
00051     int matType;
00052     switch (cloud->fields[fieldIndex].datatype) {
00053     case sensor_msgs::PointField::FLOAT32:
00054         matType = CV_32FC1;
00055         break;
00056     case sensor_msgs::PointField::FLOAT64:
00057         matType = CV_64FC1;
00058         break;
00059     case sensor_msgs::PointField::UINT8:
00060         matType = CV_8UC1;
00061         break;
00062     default:
00063         assert (0);
00064         break;
00065     }
00066     return cv::Mat(numPoints, numElements, matType, data, cloud->point_step);
00067 }
00068 /*
00069 sensor_msgs::PointCloud2Ptr createCloudWithColorFrom(const sensor_msgs::PointCloud2ConstPtr inCloud) {
00070   sensor_msgs::PointField rgbField;
00071 //  rgbField.name = "argb";
00072 //  rgbField.datatype = sensor_msgs::PointField::UINT32;
00073   rgbField.name = "rgb";
00074   rgbField.datatype = sensor_msgs::PointField::FLOAT32;
00075   rgbField.count = 1;
00076   rgbField.offset = 0;
00077   sensor_msgs::PointCloud2 rgbCloud;
00078   rgbCloud.header = inCloud->header;
00079   rgbCloud.width = inCloud->width;
00080   rgbCloud.height = inCloud->height;
00081   rgbCloud.fields.push_back(rgbField);
00082   rgbCloud.is_bigendian = inCloud->is_bigendian;
00083   rgbCloud.is_dense = inCloud->is_dense;
00084   rgbCloud.point_step = 4;
00085   rgbCloud.row_step = rgbCloud.width * rgbCloud.point_step;
00086   rgbCloud.data.resize(4 * rgbCloud.width * rgbCloud.height, 0);
00087   sensor_msgs::PointCloud2Ptr outCloud(new sensor_msgs::PointCloud2);
00088   pcl::concatenateFields(*inCloud, rgbCloud, *outCloud);
00089   return outCloud;
00090 }*/
00091 
00092 class PointCloudImage : public nodelet::Nodelet {
00093 public:
00094     PointCloudImage();
00095     virtual ~PointCloudImage();
00096     void onInit();
00097 private:
00098     tf::TransformListener transformListener;
00099     ros::Subscriber cameraSubscriber;
00100     image_transport::Publisher imagePublisher;
00101     ros::Subscriber pointCloudSub;
00102     dynamic::num_array<float,2> laser_image;
00103     std::string fixedFrame;
00104     std::string camera_topic;
00105     //std::queue<sensor_msgs::PointCloud2> clouds;
00106     typedef sensor_msgs::PointCloud2ConstPtr tcloudptr;
00107     std::deque<tcloudptr> clouds;
00108     typedef std::deque<tcloudptr>::iterator clouds_it;
00109     double maxCloudAge;
00110     int pointCloudQueueSize;
00111     double waitForTransform;
00112 
00113     void pointCloudCallback(const sensor_msgs::PointCloud2ConstPtr &cloudMsg);
00114     void cameraInfoCallback(const sensor_msgs::CameraInfoConstPtr &cameraInfoMsg);
00115 };
00116 
00117 PointCloudImage::PointCloudImage() :
00118     transformListener(ros::Duration(10.0)),
00119     fixedFrame("/odom"),
00120     maxCloudAge(10.0),
00121     pointCloudQueueSize(200),
00122     waitForTransform(1.0) {
00123 }
00124 
00125 PointCloudImage::~PointCloudImage() {
00126 }
00127 
00128 void PointCloudImage::onInit() {
00129     NODELET_INFO("PointCloudImage::onInit: Initializing...");
00130 
00131 #ifdef USE_MT_NODE_HANDLE
00132     ros::NodeHandle &nh = getMTNodeHandle();
00133     ros::NodeHandle &pnh = getMTPrivateNodeHandle();
00134 #else
00135     ros::NodeHandle &nh = getNodeHandle();
00136     ros::NodeHandle &pnh = getPrivateNodeHandle();
00137 #endif
00138 
00139     // Get and process parameters.
00140     pnh.param("fixed_frame", fixedFrame, fixedFrame);
00141     NODELET_INFO("PointCloudImage::onInit: Fixed frame: %s.", fixedFrame.c_str());
00142     pnh.param("point_cloud_queue_size", pointCloudQueueSize, pointCloudQueueSize);
00143     pointCloudQueueSize = pointCloudQueueSize >= 1 ? pointCloudQueueSize : 1;
00144     NODELET_INFO("PointCloudImage::onInit: Point cloud queue size: %i.", pointCloudQueueSize);
00145     pnh.param("wait_for_transform", waitForTransform, waitForTransform);
00146     waitForTransform = waitForTransform >= 0.0 ? waitForTransform : 0.0;
00147     NODELET_INFO("PointCloudImage::onInit: Wait for transform timeout: %.2f s.", waitForTransform);
00148     pnh.param("camera_topic", camera_topic, std::string("openni_camera"));
00149     NODELET_INFO("PointCloudImage::onInit: camera_topic: %s.", camera_topic.c_str());
00150 
00151     // Subscribe list of camera topics.
00152     image_transport::ImageTransport it(nh);
00153     //std::string cameraTopic = nh.resolveName((boost::format("camera_%i") % iCam).str(), true);
00154     //NODELET_INFO("PointCloudImage::onInit: Subscribing camera %s.", cameraTopic.c_str());
00155     //cameraSubscribers[iCam] = it.subscribeCamera(cameraTopic, imageQueueSize, (boost::bind(&PointCloudImage::cameraCallback, this, _1, _2, iCam)));
00156     cameraSubscriber = nh.subscribe(camera_topic+"/camera_info", 1, &PointCloudImage::cameraInfoCallback,this);
00157     imagePublisher = it.advertise(camera_topic+"/laser_image", 5);
00158 
00159     // Subscribe point cloud topic.
00160     std::string pclInTopic = nh.resolveName("pcl_in", true);
00161     NODELET_INFO("PointCloudImage::onInit: Subscribing point cloud %s.", pclInTopic.c_str());
00162     pointCloudSub = nh.subscribe<sensor_msgs::PointCloud2>(pclInTopic, pointCloudQueueSize+10, &PointCloudImage::pointCloudCallback, this);
00163 
00164 }
00165 
00166 void PointCloudImage::cameraInfoCallback(const sensor_msgs::CameraInfoConstPtr &cameraInfoMsg) {
00167     NODELET_DEBUG("PointCloudImage::cameraCallback: camera info frame %s.", cameraInfoMsg->header.frame_id.c_str());
00168 
00169     laser_image.resize(exttype::mint2(cameraInfoMsg->width,cameraInfoMsg->height));
00170 
00171 
00172     for(clouds_it pcloud=clouds.begin(); pcloud!=clouds.end(); ++pcloud) {
00173         const sensor_msgs::PointCloud2ConstPtr cloudMsg = *pcloud;
00174 
00175         const std::string cameraFrame = cameraInfoMsg->header.frame_id;
00176         const std::string cloudFrame = cloudMsg->header.frame_id;
00177 
00178         if (!transformListener.waitForTransform(cameraFrame, cameraInfoMsg->header.stamp, // target frame and time
00179                                                 cloudFrame, cloudMsg->header.stamp, // source frame and time
00180                                                 fixedFrame, ros::Duration(waitForTransform))) {
00181             NODELET_WARN("PointCloudImage::pointCloudCallback: Could not transform point cloud from frame %s to camera frame %s. Skipping the image...",
00182                          cloudFrame.c_str(), cameraFrame.c_str());
00183             continue;
00184         }
00185         tf::StampedTransform cloudToCamStamped;
00186         transformListener.lookupTransform(cameraFrame, cameraInfoMsg->header.stamp, // target frame and time
00187                                           cloudFrame, cloudMsg->header.stamp, // source frame and time
00188                                           fixedFrame, cloudToCamStamped);
00189         cv::Mat camRotation = rotationFromTransform(cloudToCamStamped);
00190         cv::Mat camTranslation = translationFromTransform(cloudToCamStamped);
00191         cv::Mat objectPoints;
00192         matFromCloud(cloudMsg, "x", 3).convertTo(objectPoints, CV_64FC1);
00193         // Rigid transform with points in rows: X' = X * R + t.
00194         objectPoints = objectPoints * camRotation.t() + cv::repeat(camTranslation.t(), objectPoints.rows, 1);
00195         cv::Mat cameraMatrix(3, 3, CV_64FC1, const_cast<void *>(reinterpret_cast<const void *>(&cameraInfoMsg->K[0])));
00196         cv::Mat distCoeffs(1, 5, CV_64FC1, const_cast<void *>(reinterpret_cast<const void *>(&cameraInfoMsg->D[0])));
00197 
00198         cv::Mat imagePoints;
00199         cv::projectPoints(objectPoints.reshape(3), cv::Mat::zeros(3, 1, CV_64FC1), cv::Mat::zeros(3, 1, CV_64FC1), cameraMatrix, distCoeffs, imagePoints);
00200 
00201         int numPoints = objectPoints.rows*objectPoints.cols;
00202         for (int iPoint = 0; iPoint < numPoints; iPoint++) {
00203             const double z = objectPoints.at<double>(iPoint, 2);
00204             // Skip points behind the camera.
00205             if (z <= 0.0) {
00206                 continue;
00207             }
00208             const cv::Vec2d pt = imagePoints.at<cv::Vec2d>(iPoint, 0);
00209             const double x = round(pt.val[0]);
00210             const double y = round(pt.val[1]);
00211             // Skip points outside the image.
00212             if (x < 0.0 || y < 0.0 || x >= static_cast<double>(cameraInfoMsg->width) || y >= static_cast<double>(cameraInfoMsg->height)) {
00213                 continue;
00214             }
00215             // Apply static mask with image ROI to be used for coloring.
00216             const int yi = static_cast<int>(y);
00217             const int xi = static_cast<int>(x);
00218             laser_image(xi,yi) = z;
00219         }
00220     }
00221     // publish image here
00222     {
00223         sensor_msgs::ImagePtr msg(new sensor_msgs::Image());
00224         msg->header = cameraInfoMsg->header;
00225         int num_bytes = laser_image.byte_size();
00226         msg->data.resize(num_bytes);
00227         memcpy(&msg->data[0],laser_image.begin(),num_bytes);
00228         msg->encoding = sensor_msgs::image_encodings::TYPE_32FC1;
00229         msg->step = laser_image.byte_stride(0,1);
00230         msg->height = laser_image.size()[1];
00231         msg->width = laser_image.size()[0];
00232         //msg->header.seq = count;
00233         imagePublisher.publish(msg);
00234     }
00235 }
00236 
00237 void PointCloudImage::pointCloudCallback(const sensor_msgs::PointCloud2ConstPtr &cloudMsg) {
00238     const int numPoints = cloudMsg->width * cloudMsg->height;
00239     const std::string cloudFrame = cloudMsg->header.frame_id;
00240     if (numPoints == 0 || cloudMsg->data.size() == 0) {
00241         NODELET_WARN("PointCloudImage::pointCloudCallback: Skipping empty point cloud in frame %s.", cloudFrame.c_str());
00242         return;
00243     }
00244     if(clouds.size() >=  pointCloudQueueSize) {
00245         clouds.pop_back();
00246     };
00247     //sensor_msgs::PointCloud2 cloud;
00248     clouds.push_front(cloudMsg);
00249     //sensor_msgs::PointCloud2Ptr outCloud = createCloudWithColorFrom(cloudMsg);
00250     //cv::Mat rgbMat = matFromCloud(outCloud, "rgb", 1);
00251     //rgbMat.setTo(defaultColor);
00252     // Initialize vector with projection distances from image center, used as a quality measure.
00253     //std::vector<float> dist(numPoints, std::numeric_limits<float>::infinity());
00254     //pointCloudPub.publish(outCloud);
00255 }
00256 
00257 /* namespace point_cloud_color */
00258 
00259 //PLUGINLIB_DECLARE_CLASS(point_cloud_color, point_cloud_color, point_cloud_color::PointCloudImage, nodelet::Nodelet)
00260 
00261 
00262 int main(int argc, char **argv) {
00263     ros::init(argc, argv, "bla");
00264     PointCloudImage node;
00265     ros::spin();
00266 }
 All Classes Namespaces Files Functions Variables Typedefs Defines


openni_cam
Author(s): Alexander Shekhovtsov
autogenerated on Sun Dec 1 2013 17:19:20