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 #include <boost/shared_ptr.hpp>
00025
00026 #define NO_EXTTYPE
00027 #include <stdio.h>
00028 #include "dynamic/num_array.h"
00029
00030 #include <QtGui/QApplication>
00031 #include <QtGui/QLabel>
00032 #include <QtCore/QBasicTimer>
00033 #include <QtGui/QPainter>
00034 #include <QtGui/QDialog>
00035 #include <QtGui/QMainWindow>
00036
00037 #include <QtGui/QKeyEvent>
00038 #include <QtGui/QMouseEvent>
00039
00040
00041
00042
00043 #include "GL/glew.h"
00044 #include "GL/freeglut.h"
00045 #include <GL/glut.h>
00046
00047 #include "geom/nmmatrix.h"
00048
00049 using namespace exttype;
00050
00051 int getFieldIndex(const sensor_msgs::PointCloud2 &cloud, const std::string fieldName) {
00052 for (size_t i = 0; i < cloud.fields.size(); i++) {
00053 if (cloud.fields[i].name == fieldName) {
00054 return i;
00055 }
00056 }
00057 return -1;
00058 }
00059
00060 cv::Mat rotationFromTransform(const tf::Transform &t) {
00061 return (cv::Mat_<double>(3, 3) << t.getBasis()[0][0], t.getBasis()[0][1], t.getBasis()[0][2],
00062 t.getBasis()[1][0], t.getBasis()[1][1], t.getBasis()[1][2],
00063 t.getBasis()[2][0], t.getBasis()[2][1], t.getBasis()[2][2]);
00064 }
00065
00066 cv::Mat translationFromTransform(const tf::Transform &t) {
00067 return (cv::Mat_<double>(3, 1) << t.getOrigin()[0], t.getOrigin()[1], t.getOrigin()[2]);
00068 }
00069
00070 cv::Mat matFromCloud(sensor_msgs::PointCloud2ConstPtr cloud, const std::string fieldName, const int numElements) {
00071 const int numPoints = cloud->width * cloud->height;
00072 int fieldIndex = getFieldIndex(*cloud, fieldName);
00073 void *data = const_cast<void *>(static_cast<const void *>(&cloud->data[cloud->fields[fieldIndex].offset]));
00074 int matType;
00075 switch (cloud->fields[fieldIndex].datatype) {
00076 case sensor_msgs::PointField::FLOAT32:
00077 matType = CV_32FC1;
00078 break;
00079 case sensor_msgs::PointField::FLOAT64:
00080 matType = CV_64FC1;
00081 break;
00082 case sensor_msgs::PointField::UINT8:
00083 matType = CV_8UC1;
00084 break;
00085 default:
00086 assert (0);
00087 break;
00088 }
00089 return cv::Mat(numPoints, numElements, matType, data, cloud->point_step);
00090 };
00091
00092 void glcheck() {
00093 GLenum errCode;
00094 const GLubyte *errString;
00095 if ((errCode = glGetError()) != GL_NO_ERROR) {
00096 errString = gluErrorString(errCode);
00097 fprintf (stderr, "OpenGL Error: %s, code %x\n", errString,errCode);
00098 fflush(stdout);
00099 exit(1);
00100 };
00101 };
00102
00103
00104
00105 class PointCloudImage : public nodelet::Nodelet {
00106 public:
00107 PointCloudImage();
00108 virtual ~PointCloudImage();
00109 void clear();
00110 void onInit();
00111 public:
00112 QLabel * label;
00113 QImage img;
00114 public:
00115 int Width;
00116 int Height;
00117 int count;
00118 ros::NodeHandle nh;
00119 ros::NodeHandle pnh;
00120 image_transport::ImageTransport * it;
00121 private:
00122 GLuint fbo, d_render_buf;
00123 float farVal;
00124 private:
00125 tf::TransformListener transformListener;
00126 ros::Subscriber cameraSubscriber;
00127 image_transport::Publisher imagePublisher;
00128 ros::Subscriber pointCloudSub;
00129 dynamic::num_array<float,2> laser_image;
00130 std::string fixedFrame;
00131 std::string camera_topic;
00132
00133 std::deque<cv::Mat> w_clouds;
00134 typedef std::deque<cv::Mat>::iterator w_clouds_it;
00135
00136
00137 int pointCloudQueueSize;
00138 double waitForTransform;
00139 void init_image(int width, int height);
00140 void pointCloudCallback(const sensor_msgs::PointCloud2ConstPtr &cloudMsg);
00141 void cameraInfoCallback(const sensor_msgs::CameraInfoConstPtr &cameraInfoMsg);
00142 };
00143
00144 PointCloudImage::PointCloudImage() :
00145 nh(),
00146 pnh("~"),
00147
00148 transformListener(ros::Duration(200.0),true),
00149 fixedFrame("/map"),
00150
00151 pointCloudQueueSize(500),
00152 waitForTransform(2),
00153 label(0),
00154 Width(0),Height(0),count(0),it(0) {
00155 farVal = 100;
00156 }
00157
00158 PointCloudImage::~PointCloudImage() {
00159 clear();
00160 };
00161
00162 void PointCloudImage::clear() {
00163 if(label) {
00164 delete label;
00165 };
00166 glDeleteFramebuffers(1,&fbo);
00167 glDeleteRenderbuffers(1,&d_render_buf);
00168 };
00169
00170 void PointCloudImage::onInit() {
00171 NODELET_INFO("PointCloudImage::onInit: Initializing...");
00172
00173 pnh.param("fixed_frame", fixedFrame, fixedFrame);
00174
00175 pnh.param("point_cloud_queue_size", pointCloudQueueSize, pointCloudQueueSize);
00176 pointCloudQueueSize = pointCloudQueueSize >= 1 ? pointCloudQueueSize : 1;
00177
00178 pnh.param("wait_for_transform", waitForTransform, waitForTransform);
00179 waitForTransform = waitForTransform >= 0.0 ? waitForTransform : 0.0;
00180
00181 pnh.param("camera_topic", camera_topic, std::string("openni_camera"));
00182
00183
00184
00185 std::string pclInTopic;
00186 pnh.param("scan_point_cloud", pclInTopic, std::string("scan_point_cloud"));
00187
00188 cameraSubscriber = nh.subscribe(camera_topic+"/camera_info", 1, &PointCloudImage::cameraInfoCallback,this);
00189 std::cout<<"Subscribed "<<camera_topic+"/camera_info"<<"\n"; fflush(stdout);
00190 pointCloudSub = nh.subscribe<sensor_msgs::PointCloud2>(pclInTopic, 10, &PointCloudImage::pointCloudCallback, this);
00191 std::cout<<"Subscribed "<<pclInTopic<<"\n"; fflush(stdout);
00192 it = new image_transport::ImageTransport(nh);
00193 imagePublisher = it->advertise(camera_topic+"/laser_image", 5);
00194 std::cout<<"Advertised "<<camera_topic+"/laser_image"<<"\n"; fflush(stdout);
00195 label = new QLabel();
00196 #ifndef DISPLAY
00197 label->setVisible(false);
00198 #else
00199 label->show();
00200 #endif
00201 label->setScaledContents(true);
00202 }
00203
00204 void PointCloudImage::init_image(int width, int height) {
00205 Width = width;
00206 Height = height;
00207 img = QImage(Width, Height, QImage::Format_RGB32);
00208 label->resize(Width*2,Height*2);
00209
00210 glutInitWindowSize(Width,Height);
00211 int argc1=0;
00212 char *argv1[0];
00213 glutInit(&argc1, argv1);
00214 glutCreateWindow("GLEW window of the RIGHT size");
00215 glewExperimental=GL_TRUE;
00216 GLenum err = glewInit();
00217 if (GLEW_OK != err) {
00218
00219 fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
00220 exit(1);
00221 };
00222 printf("Vendor: %s\n", glGetString (GL_VENDOR));
00223 printf("Renderer: %s\n", glGetString (GL_RENDERER));
00224 printf("Version: %s\n", glGetString (GL_VERSION));
00225 fflush(stdout);
00226
00227 glGenFramebuffers(1,&fbo);
00228 glGenRenderbuffers(1,&d_render_buf);
00229 glBindFramebuffer(GL_FRAMEBUFFER,fbo);
00230
00231 glBindRenderbuffer(GL_RENDERBUFFER,d_render_buf);
00232 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, Width, Height);
00233 glcheck();
00234 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, d_render_buf);
00235
00236 GLenum e = glCheckFramebufferStatus(GL_FRAMEBUFFER);
00237 if(e!=GL_FRAMEBUFFER_COMPLETE) {
00238
00239 GLenum errCode;
00240 const GLubyte *errString;
00241
00242 if ((errCode = glGetError()) != GL_NO_ERROR) {
00243 errString = gluErrorString(errCode);
00244 fprintf (stderr, "OpenGL Error: %s, %x, %x\n", errString,e,errCode);
00245
00246 fflush(stdout);
00247 }
00248
00249 exit(1);
00250 };
00251
00252 glEnable(GL_DEPTH_TEST);
00253 glcheck();
00254 glClear(GL_DEPTH_BUFFER_BIT);
00255 glcheck();
00256 glDisable(GL_STENCIL_TEST);
00257 };
00258
00259 void PointCloudImage::cameraInfoCallback(const sensor_msgs::CameraInfoConstPtr &cameraInfoMsg) {
00260
00261 bool laser_empty = false;
00262 ++count;
00263 if(Width!=cameraInfoMsg->width) {
00264 laser_image.resize(exttype::mint2(cameraInfoMsg->width,cameraInfoMsg->height));
00265 init_image(cameraInfoMsg->width,cameraInfoMsg->height);
00266 };
00267
00268
00269 std::string err;
00271 transformListener.waitForTransform(cameraInfoMsg->header.frame_id, cameraInfoMsg->header.stamp,
00272 fixedFrame, cameraInfoMsg->header.stamp,
00273 fixedFrame, ros::Duration(0.01));
00274
00275 tf::StampedTransform tf1;
00276 try {
00277 transformListener.lookupTransform(cameraInfoMsg->header.frame_id, cameraInfoMsg->header.stamp,
00278 fixedFrame, cameraInfoMsg->header.stamp,
00279 fixedFrame, tf1);
00280 } catch (tf::TransformException ex) {
00281 std::cout<<"skipping image frame -- no tf "<<fixedFrame<<"->"<<cameraInfoMsg->header.frame_id<<"\n";
00282 laser_image << farVal;
00283 return;
00284 };
00285
00286 glBindFramebuffer(GL_FRAMEBUFFER,fbo);
00287 glClear(GL_DEPTH_BUFFER_BIT);
00288 {
00289 geom::matrixn<4> m;
00290 m << 0;
00291 m(0,0) = cameraInfoMsg->K[0]/Width*2;
00292 m(0,1) = cameraInfoMsg->K[1]/Width*2;
00293 m(1,0) = cameraInfoMsg->K[3]/Height*2;
00294 m(1,1) = cameraInfoMsg->K[4]/Height*2;
00295 m(3,3) = 1;
00296 m(2,2) = 1;
00297 glMatrixMode(GL_PROJECTION);
00298 glLoadMatrixd(&m(0,0));
00299 float v = 0.05;
00300 glFrustum(-v,v,-v,v,v,farVal);
00301 }
00302 {
00303 double m[16];
00304 tf1.getOpenGLMatrix(m);
00305 glMatrixMode(GL_MODELVIEW);
00306 for(int j=0; j<4; ++j)m[2+j*4] = -m[2+j*4];
00307 glLoadMatrixd(m);
00308 }
00309
00310 glPointSize(5.0);
00311 for(w_clouds_it pcloud=w_clouds.begin(); pcloud!=w_clouds.end(); ++pcloud) {
00312 glBegin(GL_POINTS);
00313 cv::Mat & X = *pcloud;
00314 for(int i=0;i<X.rows;++i){
00315 glVertex3fv(&X.at<float>(i,0));
00316 };
00317 glEnd();
00318 glcheck();
00319 };
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00522
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00572
00573
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604 glReadPixels(0,0,Width,Height,GL_DEPTH_COMPONENT,GL_FLOAT,&laser_image[0]);
00605 glcheck();
00606
00607
00608 laser_image*=farVal;
00609 float min_depth = std::max(float(0.01),laser_image.min().first);
00610 float max_depth = min_depth;
00611 for(int i = 0; i < (Width) *(Height); ++i) {
00612 if (laser_image[i] <farVal && max_depth < laser_image[i]) {
00613 max_depth = laser_image[i];
00614 };
00615 };
00616 max_depth = max_depth+0.1;
00617
00618 std::cout<<"["<<min_depth<<","<<max_depth<<"]\n";
00619
00620 {
00621 sensor_msgs::ImagePtr msg(new sensor_msgs::Image());
00622 msg->header = cameraInfoMsg->header;
00623 msg->encoding = sensor_msgs::image_encodings::TYPE_32FC1;
00624 if(laser_empty) {
00625 msg->data.resize(0);
00626 msg->step = 0;
00627 msg->height = 0;
00628 msg->width = 0;
00629 } else {
00630 int num_bytes = laser_image.byte_size();
00631 msg->data.resize(num_bytes);
00632 memcpy(&msg->data[0],laser_image.begin(),num_bytes);
00633 msg->step = laser_image.byte_stride(0,1);
00634 msg->height = laser_image.size()[1];
00635 msg->width = laser_image.size()[0];
00636
00637 {
00638 fflush(stdout);
00639 QRgb * pimg = (QRgb*)img.bits();
00640 for(int i = 0; i < (Width) *(Height); ++i) {
00641
00642
00643
00644 pimg[i] = 0;
00645 if(laser_image[i]<farVal) {
00646 double v = 1/(double(laser_image[i]));
00647 double V = (v-1/max_depth)/(1/min_depth-1/max_depth);
00648 pimg[i] = qRgb(floor((V)*255),floor((V)*255),0);
00649 };
00650 };
00651 img.setPixel(Width-2,Height-2,qRgb(255,0,255));
00652 img.setPixel(10,10,qRgb(255,0,255));
00653 label->setPixmap(QPixmap::fromImage(img));
00654 };
00655 };
00656
00657 imagePublisher.publish(msg);
00658 }
00659 }
00660
00661 void PointCloudImage::pointCloudCallback(const sensor_msgs::PointCloud2ConstPtr &cloudMsg) {
00662
00663 const int numPoints = cloudMsg->width * cloudMsg->height;
00664 const std::string cloudFrame = cloudMsg->header.frame_id;
00665 if (numPoints == 0 || cloudMsg->data.size() == 0) {
00666 return;
00667 };
00668
00670 transformListener.waitForTransform(fixedFrame, cloudMsg->header.stamp,
00671 cloudMsg->header.frame_id, cloudMsg->header.stamp,
00672 fixedFrame, ros::Duration(waitForTransform));
00673
00674 tf::StampedTransform tf1;
00675 try {
00676 transformListener.lookupTransform(fixedFrame, cloudMsg->header.stamp,
00677 cloudMsg->header.frame_id, cloudMsg->header.stamp,
00678 fixedFrame, tf1);
00679 } catch (tf::TransformException ex) {
00680 std::cout<<"skipping scan -- no tf "<<cloudMsg->header.frame_id<<"->"<<fixedFrame<<"\n";
00681 return;
00682 };
00683
00684 if(w_clouds.size() >= pointCloudQueueSize) {
00685 w_clouds.pop_back();
00686 };
00687
00688 cv::Mat camRotation;
00689 rotationFromTransform(tf1).convertTo(camRotation,CV_32FC1);
00690 cv::Mat camTranslation;
00691 translationFromTransform(tf1).convertTo(camTranslation,CV_32FC1);
00692 cv::Mat objectPoints;
00693 matFromCloud(cloudMsg, "x", 3).convertTo(objectPoints, CV_32FC1);
00694 objectPoints = objectPoints * camRotation.t() + cv::repeat(camTranslation.t(), objectPoints.rows, 1);
00695 objectPoints = objectPoints.reshape(3);
00696 w_clouds.push_front(objectPoints);
00697 }
00698
00699
00700
00701 class myApp : public QApplication {
00702 public:
00703 QLabel label;
00704 PointCloudImage node;
00705 int timer_count;
00706 public:
00707 myApp(int & argc, char ** argv):QApplication(argc,argv) {
00708 timer_count = 0;
00709 node.onInit();
00710 startTimer(1000/20);
00711 };
00712
00713 ~myApp() {
00714 };
00715
00716 void timerEvent(QTimerEvent * event) {
00717 ros::spinOnce();
00718 ++timer_count;
00719 if(node.count<1) {
00720 if(timer_count % 100 ==0) {
00721 std::cout<<"waiting for inputs\n";
00722 std::cout.flush();
00723 };
00724 };
00725
00726 if(!ros::ok()) {
00727
00728 printf("quitting");
00729 fflush(stdout);
00730 exit(0);
00731 };
00732 };
00733 bool notify(QObject * receiver, QEvent * ev) {
00734 if(ev->type() == QEvent::MouseButtonPress) {
00735 QMouseEvent * e = static_cast<QMouseEvent *>(ev);
00736 if(e->button()==Qt::LeftButton) {
00737 return true;
00738 };
00739 };
00740 if(ev->type() == QEvent::KeyPress) {
00741 QKeyEvent * e = static_cast<QKeyEvent *>(ev);
00742 if(e->key() == Qt::Key_Backspace) {
00743 return true;
00744 } else if(e->key() == Qt::Key_Return) {
00745 return true;
00746 } else if(e->key() == Qt::Key_Tab) {
00747 return true;
00748 } else if(e->key() == Qt::Key_Escape) {
00749 exit(0);
00750 return true;
00751 };
00752 };
00753 return QApplication::notify(receiver,ev);
00754 };
00755 };
00756
00757
00758 int main(int argc, char **argv) {
00759 ros::init(argc, argv, "point_cloud_to_image");
00760 int argc1=0;
00761 char *argv1[0];
00762 myApp * a = new myApp(argc1, argv1);
00763 a->exec();
00764 delete a;
00765 }
00766