00001
00004
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>
00022 #include <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
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
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
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
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
00152 image_transport::ImageTransport it(nh);
00153
00154
00155
00156 cameraSubscriber = nh.subscribe(camera_topic+"/camera_info", 1, &PointCloudImage::cameraInfoCallback,this);
00157 imagePublisher = it.advertise(camera_topic+"/laser_image", 5);
00158
00159
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,
00179 cloudFrame, cloudMsg->header.stamp,
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,
00187 cloudFrame, cloudMsg->header.stamp,
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
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
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
00212 if (x < 0.0 || y < 0.0 || x >= static_cast<double>(cameraInfoMsg->width) || y >= static_cast<double>(cameraInfoMsg->height)) {
00213 continue;
00214 }
00215
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
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
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
00248 clouds.push_front(cloudMsg);
00249
00250
00251
00252
00253
00254
00255 }
00256
00257
00258
00259
00260
00261
00262 int main(int argc, char **argv) {
00263 ros::init(argc, argv, "bla");
00264 PointCloudImage node;
00265 ros::spin();
00266 }