00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include <ros/ros.h>
00016 #include <visiontransfer/asynctransfer.h>
00017 #include <visiontransfer/reconstruct3d.h>
00018 #include <sensor_msgs/PointCloud2.h>
00019 #include <sensor_msgs/Image.h>
00020 #include <opencv2/opencv.hpp>
00021 #include <cv_bridge/cv_bridge.h>
00022 #include <iostream>
00023 #include <iomanip>
00024 #include <nerian_sp1/StereoCameraInfo.h>
00025 #include <boost/smart_ptr.hpp>
00026 #include <colorcoder.h>
00027 #include <sdlwindow.h>
00028
00029 using namespace std;
00030
00050 class Sp1Node {
00051 public:
00052 Sp1Node() {
00053 }
00054
00055 ~Sp1Node() {
00056 }
00057
00061 void init() {
00062 ros::NodeHandle privateNh("~");
00063
00064
00065 if (!privateNh.getParam("point_cloud_intensity_channel", intensityChannel)) {
00066 intensityChannel = true;
00067 }
00068
00069 if (!privateNh.getParam("color_code_disparity_map", colorCodeDispMap)) {
00070 colorCodeDispMap = true;
00071 }
00072
00073 if (!privateNh.getParam("color_code_legend", colorCodeLegend)) {
00074 colorCodeLegend = false;
00075 }
00076
00077 if (!privateNh.getParam("frame", frame)) {
00078 frame = "world";
00079 }
00080
00081 if (!privateNh.getParam("port", port)) {
00082 port = "7681";
00083 }
00084
00085 if (!privateNh.getParam("use_tcp", useTcp)) {
00086 useTcp = false;
00087 }
00088
00089 if (!privateNh.getParam("host", host)) {
00090 host = "";
00091 }
00092
00093 if (!privateNh.getParam("ros_coordinate_system", rosCoordinateSystem)) {
00094 rosCoordinateSystem = true;
00095 }
00096
00097 if (!privateNh.getParam("calibration_file", calibFile)) {
00098 calibFile = "";
00099 }
00100
00101 if (!privateNh.getParam("disparity_window", dispWindow)) {
00102 dispWindow = false;
00103 }
00104
00105
00106 if(dispWindow) {
00107 window.reset(new SDLWindow(640, 480, "Disparity Map"));
00108 }
00109
00110
00111 disparityPublisher.reset(new ros::Publisher(nh.advertise<sensor_msgs::Image>(
00112 "/nerian_sp1/disparity_map", 5)));
00113 imagePublisher.reset(new ros::Publisher(nh.advertise<sensor_msgs::Image>(
00114 "/nerian_sp1/left_image", 5)));
00115
00116 if(calibFile == "" ) {
00117 ROS_WARN("No camera calibration file configured. Point cloud and camera information publishing is disabled!");
00118 } else {
00119 if (!calibStorage.open(calibFile, cv::FileStorage::READ)) {
00120 throw std::runtime_error("Error reading calibration file: " + calibFile);
00121 }
00122
00123 cameraInfoPublisher.reset(new ros::Publisher(nh.advertise<nerian_sp1::StereoCameraInfo>(
00124 "/nerian_sp1/stereo_camera_info", 1)));
00125 cloudPublisher.reset(new ros::Publisher(nh.advertise<sensor_msgs::PointCloud2>(
00126 "/nerian_sp1/point_cloud", 5)));
00127
00128
00129 initPointCloud(privateNh);
00130 }
00131 }
00132
00136 int run() {
00137 ros::Time stamp = ros::Time::now();
00138 ros::Time lastLogTime;
00139 int lastLogFrames = 0;
00140
00141 int leftWidth, leftHeight, leftStride;
00142 unsigned char* leftData = NULL;
00143
00144 int dispWidth, dispHeight, dispStride;
00145 unsigned char* dispData = NULL;
00146
00147
00148 AsyncTransfer asyncTransfer(useTcp ? ImageTransfer::TCP_CLIENT : ImageTransfer::UDP_CLIENT,
00149 host.c_str(), port.c_str());
00150
00151 while(ros::ok()) {
00152 int imageNum, width, height, stride;
00153 ImageProtocol::ImageFormat format;
00154 unsigned char* data = NULL;
00155
00156
00157 data = asyncTransfer.collectReceivedImage(imageNum, width, height, stride, format, 0.5);
00158
00159
00160 if(data != NULL && imageNum == 0) {
00161 stamp = ros::Time::now();
00162 leftWidth = width;
00163 leftHeight = height;
00164 leftStride = stride;
00165 leftData = data;
00166 } else if(data != NULL && imageNum == 1) {
00167 dispWidth = width;
00168 dispHeight = height;
00169 dispStride = stride;
00170 dispData = data;
00171 }
00172
00173 if(leftData != NULL && dispData != NULL) {
00174
00175
00176 if(leftWidth != dispWidth || leftHeight != dispHeight) {
00177 throw std::runtime_error("Invalid image sizes!");
00178 }
00179
00180
00181 if(imagePublisher->getNumSubscribers() > 0) {
00182 publishImageMsg(leftWidth, leftHeight, leftStride, leftData, stamp);
00183 }
00184
00185 if(disparityPublisher->getNumSubscribers() > 0 || window != NULL) {
00186 publishDispMapMsg(dispWidth, dispHeight, dispStride, dispData, stamp);
00187 }
00188
00189 if(cloudPublisher != NULL && cloudPublisher->getNumSubscribers() > 0) {
00190 publishPointCloudMsg(dispWidth, dispHeight, leftStride, dispStride,
00191 leftData, dispData, stamp);
00192 }
00193
00194 if(cameraInfoPublisher != NULL && cameraInfoPublisher->getNumSubscribers() > 0) {
00195 publishCameraInfo(stamp);
00196 }
00197
00198
00199 frameNum++;
00200 if(stamp.sec != lastLogTime.sec) {
00201 if(lastLogTime != ros::Time()) {
00202 double dt = (stamp - lastLogTime).toSec();
00203 double fps = (frameNum - lastLogFrames) / dt;
00204 ROS_INFO("%.1f fps", fps);
00205 }
00206 lastLogFrames = frameNum;
00207 lastLogTime = stamp;
00208 }
00209
00210 leftData = NULL;
00211 dispData = NULL;
00212 }
00213 }
00214 }
00215
00216 private:
00217
00218 ros::NodeHandle nh;
00219 boost::scoped_ptr<ros::Publisher> cloudPublisher;
00220 boost::scoped_ptr<ros::Publisher> disparityPublisher;
00221 boost::scoped_ptr<ros::Publisher> imagePublisher;
00222 boost::scoped_ptr<ros::Publisher> cameraInfoPublisher;
00223
00224
00225 bool intensityChannel;
00226 bool useTcp;
00227 bool colorCodeDispMap;
00228 bool colorCodeLegend;
00229 bool rosCoordinateSystem;
00230 std::string port;
00231 std::string frame;
00232 std::string host;
00233 std::string calibFile;
00234 bool dispWindow;
00235
00236
00237 int frameNum;
00238 boost::scoped_ptr<Reconstruct3D> recon3d;
00239 boost::scoped_ptr<ColorCoder> colCoder;
00240 cv::Mat_<cv::Vec3b> colDispMap;
00241 sensor_msgs::PointCloud2Ptr pointCloudMsg;
00242 cv::FileStorage calibStorage;
00243 nerian_sp1::StereoCameraInfoPtr camInfoMsg;
00244 ros::Time lastCamInfoPublish;
00245 boost::scoped_ptr<SDLWindow> window;
00246
00250 void publishImageMsg(int width, int height, int stride, unsigned char* data,
00251 ros::Time stamp) {
00252 cv_bridge::CvImage cvImg;
00253 cvImg.header.frame_id = frame;
00254 cvImg.header.stamp = stamp;
00255 cvImg.header.seq = frameNum;
00256
00257 cvImg.image = cv::Mat_<unsigned char>(height, width, data, stride);
00258 sensor_msgs::ImagePtr msg = cvImg.toImageMsg();
00259 msg->encoding = "mono8";
00260 imagePublisher->publish(msg);
00261 }
00262
00267 void publishDispMapMsg(int width, int height, int stride, unsigned char* data,
00268 ros::Time stamp) {
00269 cv_bridge::CvImage cvImg;
00270 cvImg.header.frame_id = frame;
00271 cvImg.header.stamp = stamp;
00272 cvImg.header.seq = frameNum;
00273
00274 cv::Mat_<unsigned short> monoImg(height, width,
00275 reinterpret_cast<unsigned short*>(data), stride);
00276 string encoding = "";
00277
00278 if(!colorCodeDispMap) {
00279 cvImg.image = monoImg;
00280 encoding = "mono16";
00281 } else {
00282 if(colCoder == NULL) {
00283 colCoder.reset(new ColorCoder(0, 16*111, true, true));
00284 if(colorCodeLegend) {
00285
00286 colDispMap = colCoder->createLegendBorder(width, height, 1.0/16.0);
00287 } else {
00288 colDispMap = cv::Mat_<cv::Vec3b>(height, width);
00289 }
00290 }
00291
00292 cv::Mat_<cv::Vec3b> dispSection = colDispMap(cv::Rect(0, 0, width, height));
00293 colCoder->codeImage(monoImg, dispSection);
00294 cvImg.image = colDispMap;
00295 encoding = "bgr8";
00296 }
00297
00298 if(disparityPublisher->getNumSubscribers() > 0) {
00299 sensor_msgs::ImagePtr msg = cvImg.toImageMsg();
00300 msg->encoding = encoding;
00301 disparityPublisher->publish(msg);
00302 }
00303
00304 if(window != NULL) {
00305 if(window->getSize() != cvImg.image.size()) {
00306 window->resize(cvImg.image.size());
00307 }
00308 window->displayImage(cvImg.image);
00309 window->processEvents(false);
00310 }
00311 }
00312
00317 void publishPointCloudMsg(int width, int height, int leftStride, int dispStride,
00318 unsigned char* leftData, unsigned char* dispData, ros::Time stamp) {
00319
00320 float* pointMap = recon3d->createPointMap(reinterpret_cast<unsigned short*>(dispData),
00321 width, height, dispStride, 0);
00322
00323
00324 pointCloudMsg->header.stamp = stamp;
00325 pointCloudMsg->header.frame_id = frame;
00326 pointCloudMsg->header.seq = frameNum;
00327
00328
00329 if(pointCloudMsg->data.size() != width*height*4*sizeof(float)) {
00330
00331 pointCloudMsg->data.resize(width*height*4*sizeof(float));
00332
00333
00334 pointCloudMsg->width = width;
00335 pointCloudMsg->height = height;
00336 pointCloudMsg->is_bigendian = false;
00337 pointCloudMsg->point_step = 4*sizeof(float);
00338 pointCloudMsg->row_step = width * pointCloudMsg->point_step;
00339 pointCloudMsg->is_dense = false;
00340
00341 pointCloudMsg->fields[0].count = width * height;
00342 pointCloudMsg->fields[1].count = width * height;
00343 pointCloudMsg->fields[2].count = width * height;
00344
00345 if(intensityChannel) {
00346 pointCloudMsg->fields[3].count = width * height;
00347 }
00348 }
00349
00350 memcpy(&pointCloudMsg->data[0], pointMap, width*height*4*sizeof(float));
00351
00352
00353 if(intensityChannel) {
00354
00355 unsigned char* cloudStart = &pointCloudMsg->data[0];
00356 unsigned char* cloudEnd = &pointCloudMsg->data[0] + width*height*4*sizeof(float);
00357
00358
00359 unsigned char* imagePtr = &leftData[0];
00360 unsigned char* rowEndPtr = imagePtr + width;
00361
00362 for(unsigned char* cloudPtr = cloudStart + 3*sizeof(float);
00363 cloudPtr < cloudEnd; cloudPtr+= 4*sizeof(float)) {
00364 *cloudPtr = *imagePtr;
00365
00366 imagePtr++;
00367 if(imagePtr == rowEndPtr) {
00368
00369 imagePtr += (leftStride - width);
00370 rowEndPtr = imagePtr + width;
00371 }
00372 }
00373 }
00374
00375 cloudPublisher->publish(pointCloudMsg);
00376 }
00377
00382 void initPointCloud(ros::NodeHandle& privateNh) {
00383
00384 std::vector<float> qMatrix;
00385 calibStorage["Q"] >> qMatrix;
00386 if(qMatrix.size() != 16) {
00387 throw std::runtime_error("Q matrix has invalid size!");
00388 }
00389
00390 if(rosCoordinateSystem) {
00391
00392
00393 std::vector<float> qNew(16);
00394 qNew[0] = qMatrix[8]; qNew[1] = qMatrix[9];
00395 qNew[2] = qMatrix[10]; qNew[3] = qMatrix[11];
00396
00397 qNew[4] = -qMatrix[0]; qNew[5] = -qMatrix[1];
00398 qNew[6] = -qMatrix[2]; qNew[7] = -qMatrix[3];
00399
00400 qNew[8] = -qMatrix[4]; qNew[9] = -qMatrix[5];
00401 qNew[10] = -qMatrix[6]; qNew[11] = -qMatrix[7];
00402
00403 qNew[12] = qMatrix[12]; qNew[13] = qMatrix[13];
00404 qNew[14] = qMatrix[14]; qNew[15] = qMatrix[15];
00405
00406 qMatrix = qNew;
00407 }
00408
00409
00410 recon3d.reset(new Reconstruct3D(&qMatrix[0]));
00411
00412
00413 pointCloudMsg.reset(new sensor_msgs::PointCloud2);
00414
00415
00416 sensor_msgs::PointField fieldX;
00417 fieldX.name ="x";
00418 fieldX.offset = 0;
00419 fieldX.datatype = sensor_msgs::PointField::FLOAT32;
00420 pointCloudMsg->fields.push_back(fieldX);
00421
00422 sensor_msgs::PointField fieldY;
00423 fieldY.name ="y";
00424 fieldY.offset = sizeof(float);
00425 fieldY.datatype = sensor_msgs::PointField::FLOAT32;
00426 pointCloudMsg->fields.push_back(fieldY);
00427
00428 sensor_msgs::PointField fieldZ;
00429 fieldZ.name ="z";
00430 fieldZ.offset = 2*sizeof(float);
00431 fieldZ.datatype = sensor_msgs::PointField::FLOAT32;
00432 pointCloudMsg->fields.push_back(fieldZ);
00433
00434 if(intensityChannel) {
00435 sensor_msgs::PointField fieldI;
00436 fieldI.name ="intensity";
00437 fieldI.offset = 3*sizeof(float);
00438 fieldI.datatype = sensor_msgs::PointField::UINT8;
00439 pointCloudMsg->fields.push_back(fieldI);
00440 }
00441 }
00442
00446 void publishCameraInfo(ros::Time stamp) {
00447 if(camInfoMsg == NULL) {
00448
00449 camInfoMsg.reset(new nerian_sp1::StereoCameraInfo);
00450
00451 camInfoMsg->header.frame_id = frame;
00452 camInfoMsg->header.seq = 0;
00453
00454 std::vector<int> sizeVec;
00455 calibStorage["size"] >> sizeVec;
00456 if(sizeVec.size() != 2) {
00457 std::runtime_error("Calibration file format error!");
00458 }
00459
00460 camInfoMsg->left_info.header = camInfoMsg->header;
00461 camInfoMsg->left_info.width = sizeVec[0];
00462 camInfoMsg->left_info.height = sizeVec[1];
00463 camInfoMsg->left_info.distortion_model = "plumb_bob";
00464 calibStorage["D1"] >> camInfoMsg->left_info.D;
00465 readCalibrationArray("M1", camInfoMsg->left_info.K);
00466 readCalibrationArray("R1", camInfoMsg->left_info.R);
00467 readCalibrationArray("P1", camInfoMsg->left_info.P);
00468 camInfoMsg->left_info.binning_x = 1;
00469 camInfoMsg->left_info.binning_y = 1;
00470 camInfoMsg->left_info.roi.do_rectify = false;
00471 camInfoMsg->left_info.roi.height = 0;
00472 camInfoMsg->left_info.roi.width = 0;
00473 camInfoMsg->left_info.roi.x_offset = 0;
00474 camInfoMsg->left_info.roi.y_offset = 0;
00475
00476 camInfoMsg->right_info.header = camInfoMsg->header;
00477 camInfoMsg->right_info.width = sizeVec[0];
00478 camInfoMsg->right_info.height = sizeVec[1];
00479 camInfoMsg->right_info.distortion_model = "plumb_bob";
00480 calibStorage["D2"] >> camInfoMsg->right_info.D;
00481 readCalibrationArray("M2", camInfoMsg->right_info.K);
00482 readCalibrationArray("R2", camInfoMsg->right_info.R);
00483 readCalibrationArray("P2", camInfoMsg->right_info.P);
00484 camInfoMsg->right_info.binning_x = 1;
00485 camInfoMsg->right_info.binning_y = 1;
00486 camInfoMsg->right_info.roi.do_rectify = false;
00487 camInfoMsg->right_info.roi.height = 0;
00488 camInfoMsg->right_info.roi.width = 0;
00489 camInfoMsg->right_info.roi.x_offset = 0;
00490 camInfoMsg->right_info.roi.y_offset = 0;
00491
00492 readCalibrationArray("Q", camInfoMsg->Q);
00493 readCalibrationArray("T", camInfoMsg->T_left_right);
00494 readCalibrationArray("R", camInfoMsg->R_left_right);
00495 }
00496
00497 double dt = (stamp - lastCamInfoPublish).toSec();
00498 if(dt > 1.0) {
00499
00500 camInfoMsg->header.stamp = stamp;
00501 camInfoMsg->left_info.header.stamp = stamp;
00502 camInfoMsg->right_info.header.stamp = stamp;
00503 cameraInfoPublisher->publish(camInfoMsg);
00504
00505 lastCamInfoPublish = stamp;
00506 }
00507 }
00508
00512 template<class T>
00513 void readCalibrationArray(const char* key, T& dest) {
00514 std::vector<double> doubleVec;
00515 calibStorage[key] >> doubleVec;
00516
00517 if(doubleVec.size() != dest.size()) {
00518 std::runtime_error("Calibration file format error!");
00519 }
00520
00521 std::copy(doubleVec.begin(), doubleVec.end(), dest.begin());
00522 }
00523 };
00524
00525 int main(int argc, char** argv) {
00526 try {
00527 ros::init(argc, argv, "nerian_sp1");
00528 Sp1Node node;
00529 node.init();
00530 return node.run();
00531 } catch(const std::exception& ex) {
00532 ROS_FATAL("Exception occured: %s", ex.what());
00533 return 1;
00534 }
00535 }