nerian_stereo_node_base.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  * Copyright (c) 2019 Nerian Vision GmbH
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *******************************************************************************/
14 
16 
17 namespace nerian_stereo {
18 
19 void StereoNodeBase::dynamicReconfigureCallback(nerian_stereo::NerianStereoConfig &config, uint32_t level) {
21  ROS_INFO("Received a new configuration via dynamic_reconfigure");
22  // Unfortunately, we have to check for each potential change (no configuration deltas provided).
23  // This is done in the autogenerated external code.
25  } else {
26  initialConfigReceived = true;
27  }
28  lastKnownConfig = config;
29 }
30 
31 void StereoNodeBase::updateParameterServerFromDevice(std::map<std::string, ParameterInfo>& cfg) {
32  // Publish the current config to the parameter server
34  // Publish reboot flag to definitely be set to false in the parameter server
35  getNH().setParam("/nerian_stereo/reboot", false);
36 }
37 
38 void StereoNodeBase::updateDynamicReconfigureFromDevice(std::map<std::string, ParameterInfo>& cfg) {
40 }
41 /*
42  * \brief Initialize and publish configuration with a dynamic_reconfigure server
43  */
45  std::map<std::string, ParameterInfo> ssParams;
46  // Connect to parameter server on device
47  ROS_INFO("Connecting to %s for parameter service", remoteHost.c_str());
48  try {
49  sceneScanParameters.reset(new SceneScanParameters(remoteHost.c_str()));
50  } catch(visiontransfer::ParameterException& e) {
51  ROS_ERROR("ParameterException while connecting to parameter service: %s", e.what());
52  throw;
53  }
54  try {
55  ssParams = sceneScanParameters->getAllParameters();
56  } catch(visiontransfer::TransferException& e) {
57  ROS_ERROR("TransferException while obtaining parameter enumeration: %s", e.what());
58  throw;
59  } catch(visiontransfer::ParameterException& e) {
60  ROS_ERROR("ParameterException while obtaining parameter enumeration: %s", e.what());
61  throw;
62  }
63  // First make sure that the parameter server gets all *current* values
65  // Initialize (and publish) initial configuration from compile-time generated header
66  dynReconfServer.reset(new dynamic_reconfigure::Server<nerian_stereo::NerianStereoConfig>());
67  // Obtain and publish the default, min, and max values from the device to dyn_reconf
69  // Callback for future changes requested from the ROS side
70  dynReconfServer->setCallback(boost::bind(&StereoNodeBase::dynamicReconfigureCallback, this, _1, _2));
71 }
72 
77  ros::NodeHandle& privateNh = getPrivateNH();
78 
79  // Read all ROS parameters
80  std::string intensityChannel = "mono8";
81  privateNh.getParam("point_cloud_intensity_channel", intensityChannel);
82  if(intensityChannel == "none") {
84  } else if(intensityChannel == "rgb8") {
86  } else if(intensityChannel == "rgb32f") {
88  } else {
90  }
91 
92  if (!privateNh.getParam("color_code_disparity_map", colorCodeDispMap)) {
93  colorCodeDispMap = "";
94  }
95 
96  if (!privateNh.getParam("color_code_legend", colorCodeLegend)) {
97  colorCodeLegend = false;
98  }
99 
100  if (!privateNh.getParam("frame", frame)) {
101  frame = "world";
102  }
103 
104  if (!privateNh.getParam("remote_port", remotePort)) {
105  remotePort = "7681";
106  }
107 
108  if (!privateNh.getParam("remote_host", remoteHost)) {
109  remoteHost = "0.0.0.0";
110  }
111 
112  if (!privateNh.getParam("use_tcp", useTcp)) {
113  useTcp = false;
114  }
115 
116  if (!privateNh.getParam("ros_coordinate_system", rosCoordinateSystem)) {
117  rosCoordinateSystem = true;
118  }
119 
120  if (!privateNh.getParam("ros_timestamps", rosTimestamps)) {
121  rosTimestamps = true;
122  }
123 
124  if (!privateNh.getParam("calibration_file", calibFile)) {
125  calibFile = "";
126  }
127 
128  if (!privateNh.getParam("delay_execution", execDelay)) {
129  execDelay = 0;
130  }
131 
132  if (!privateNh.getParam("max_depth", maxDepth)) {
133  maxDepth = -1;
134  }
135 
136  if (!privateNh.getParam("q_from_calib_file", useQFromCalibFile)) {
137  useQFromCalibFile = false;
138  }
139 
140  // Apply an initial delay if configured
142 
143  // Create publishers
144  disparityPublisher.reset(new ros::Publisher(getNH().advertise<sensor_msgs::Image>(
145  "/nerian_stereo/disparity_map", 5)));
146  leftImagePublisher.reset(new ros::Publisher(getNH().advertise<sensor_msgs::Image>(
147  "/nerian_stereo/left_image", 5)));
148  rightImagePublisher.reset(new ros::Publisher(getNH().advertise<sensor_msgs::Image>(
149  "/nerian_stereo/right_image", 5)));
150 
152 
153  cameraInfoPublisher.reset(new ros::Publisher(getNH().advertise<nerian_stereo::StereoCameraInfo>(
154  "/nerian_stereo/stereo_camera_info", 1)));
155  cloudPublisher.reset(new ros::Publisher(getNH().advertise<sensor_msgs::PointCloud2>(
156  "/nerian_stereo/point_cloud", 5)));
157 }
158 
160  ROS_INFO("Connecting to %s:%s for data transfer", remoteHost.c_str(), remotePort.c_str());
161  asyncTransfer.reset(new AsyncTransfer(remoteHost.c_str(), remotePort.c_str(),
162  useTcp ? ImageProtocol::PROTOCOL_TCP : ImageProtocol::PROTOCOL_UDP));
163 }
164 
166  // Receive image data
167  ImagePair imagePair;
168  if(!asyncTransfer->collectReceivedImagePair(imagePair, 0.5)) {
169  return;
170  }
171 
172  // Get time stamp
173  ros::Time stamp;
174  if(rosTimestamps) {
175  stamp = ros::Time::now();
176  } else {
177  int secs = 0, microsecs = 0;
178  imagePair.getTimestamp(secs, microsecs);
179  stamp = ros::Time(secs, microsecs*1000);
180  }
181 
182  // Publish image data messages
183  publishImageMsg(imagePair, 0, stamp, false, leftImagePublisher.get());
184  if(imagePair.isImageDisparityPair()) {
185  publishImageMsg(imagePair, 1, stamp, true, disparityPublisher.get());
186  } else {
187  publishImageMsg(imagePair, 1, stamp, false, rightImagePublisher.get());
188  }
189 
190  if(cloudPublisher->getNumSubscribers() > 0) {
191  if(recon3d == nullptr) {
192  // First initialize
193  initPointCloud();
194  }
195 
196  publishPointCloudMsg(imagePair, stamp);
197  }
198 
199  if(cameraInfoPublisher != NULL && cameraInfoPublisher->getNumSubscribers() > 0) {
200  publishCameraInfo(stamp, imagePair);
201  }
202 
203  // Display some simple statistics
204  frameNum++;
205  if(stamp.sec != lastLogTime.sec) {
206  if(lastLogTime != ros::Time()) {
207  double dt = (stamp - lastLogTime).toSec();
208  double fps = (frameNum - lastLogFrames) / dt;
209  ROS_INFO("%.1f fps", fps);
210  }
212  lastLogTime = stamp;
213  }
214 }
215 
217  if(calibFile == "" ) {
218  ROS_WARN("No camera calibration file configured. Cannot publish detailed camera information!");
219  } else {
220  bool success = false;
221  try {
222  if (calibStorage.open(calibFile, cv::FileStorage::READ)) {
223  success = true;
224  }
225  } catch(...) {
226  }
227 
228  if(!success) {
229  ROS_WARN("Error reading calibration file: %s\n"
230  "Cannot publish detailed camera information!", calibFile.c_str());
231  }
232  }
233 }
234 
235 void StereoNodeBase::publishImageMsg(const ImagePair& imagePair, int imageIndex, ros::Time stamp, bool allowColorCode,
236  ros::Publisher* publisher) {
237 
238  if(publisher->getNumSubscribers() <= 0) {
239  return; //No subscribers
240  }
241 
242  cv_bridge::CvImage cvImg;
243  cvImg.header.frame_id = frame;
244  cvImg.header.stamp = stamp;
245  cvImg.header.seq = imagePair.getSequenceNumber(); // Actually ROS will overwrite this
246 
247  bool format12Bit = (imagePair.getPixelFormat(imageIndex) == ImagePair::FORMAT_12_BIT_MONO);
248  cv::Mat monoImg(imagePair.getHeight(), imagePair.getWidth(),
249  format12Bit ? CV_16UC1 : CV_8UC1,
250  imagePair.getPixelData(imageIndex), imagePair.getRowStride(imageIndex));
251  string encoding = "";
252 
253  if(colorCodeDispMap == "" || colorCodeDispMap == "none" || !allowColorCode || !format12Bit) {
254  cvImg.image = monoImg;
255  encoding = (format12Bit ? "mono16": "mono8");
256  } else {
257  if(colCoder == NULL) {
258  int dispMin = 0, dispMax = 0;
259  imagePair.getDisparityRange(dispMin, dispMax);
260 
261  colCoder.reset(new ColorCoder(
262  colorCodeDispMap == "rainbow" ? ColorCoder::COLOR_RAINBOW_BGR : ColorCoder::COLOR_RED_BLUE_BGR,
263  dispMin*16, dispMax*16, true, true));
264  if(colorCodeLegend) {
265  // Create the legend
266  colDispMap = colCoder->createLegendBorder(monoImg.cols, monoImg.rows, 1.0/16.0);
267  } else {
268  colDispMap = cv::Mat_<cv::Vec3b>(monoImg.rows, monoImg.cols);
269  }
270  }
271 
272  cv::Mat_<cv::Vec3b> dispSection = colDispMap(cv::Rect(0, 0, monoImg.cols, monoImg.rows));
273 
274  colCoder->codeImage(cv::Mat_<unsigned short>(monoImg), dispSection);
275  cvImg.image = colDispMap;
276  encoding = "bgr8";
277  }
278 
279  sensor_msgs::ImagePtr msg = cvImg.toImageMsg();
280  msg->encoding = encoding;
281  publisher->publish(msg);
282 }
283 
284 void StereoNodeBase::qMatrixToRosCoords(const float* src, float* dst) {
285  dst[0] = src[8]; dst[1] = src[9];
286  dst[2] = src[10]; dst[3] = src[11];
287 
288  dst[4] = -src[0]; dst[5] = -src[1];
289  dst[6] = -src[2]; dst[7] = -src[3];
290 
291  dst[8] = -src[4]; dst[9] = -src[5];
292  dst[10] = -src[6]; dst[11] = -src[7];
293 
294  dst[12] = src[12]; dst[13] = src[13];
295  dst[14] = src[14]; dst[15] = src[15];
296 }
297 
298 void StereoNodeBase::publishPointCloudMsg(ImagePair& imagePair, ros::Time stamp) {
299  if(imagePair.getPixelFormat(1) != ImagePair::FORMAT_12_BIT_MONO) {
300  return; // This is not a disparity map
301  }
302 
303  // Set static q matrix if desired
304  if(useQFromCalibFile) {
305  static std::vector<float> q;
306  calibStorage["Q"] >> q;
307  imagePair.setQMatrix(&q[0]);
308  }
309 
310  // Transform Q-matrix if desired
311  float qRos[16];
312  if(rosCoordinateSystem) {
313  qMatrixToRosCoords(imagePair.getQMatrix(), qRos);
314  imagePair.setQMatrix(qRos);
315  }
316 
317  // Get 3D points
318  float* pointMap = nullptr;
319  try {
320  pointMap = recon3d->createPointMap(imagePair, 0);
321  } catch(std::exception& ex) {
322  cerr << "Error creating point cloud: " << ex.what() << endl;
323  return;
324  }
325 
326  // Create message object and set header
327  pointCloudMsg->header.stamp = stamp;
328  pointCloudMsg->header.frame_id = frame;
329  pointCloudMsg->header.seq = imagePair.getSequenceNumber(); // Actually ROS will overwrite this
330 
331  // Copy 3D points
332  if(pointCloudMsg->data.size() != imagePair.getWidth()*imagePair.getHeight()*4*sizeof(float)) {
333  // Allocate buffer
334  pointCloudMsg->data.resize(imagePair.getWidth()*imagePair.getHeight()*4*sizeof(float));
335 
336  // Set basic data
337  pointCloudMsg->width = imagePair.getWidth();
338  pointCloudMsg->height = imagePair.getHeight();
339  pointCloudMsg->is_bigendian = false;
340  pointCloudMsg->point_step = 4*sizeof(float);
341  pointCloudMsg->row_step = imagePair.getWidth() * pointCloudMsg->point_step;
342  pointCloudMsg->is_dense = false;
343  }
344 
345  if(maxDepth < 0) {
346  // Just copy everything
347  memcpy(&pointCloudMsg->data[0], pointMap,
348  imagePair.getWidth()*imagePair.getHeight()*4*sizeof(float));
349  } else {
350  // Only copy points up to maximum depth
351  if(rosCoordinateSystem) {
352  copyPointCloudClamped<0>(pointMap, reinterpret_cast<float*>(&pointCloudMsg->data[0]),
353  imagePair.getWidth()*imagePair.getHeight());
354  } else {
355  copyPointCloudClamped<2>(pointMap, reinterpret_cast<float*>(&pointCloudMsg->data[0]),
356  imagePair.getWidth()*imagePair.getHeight());
357  }
358  }
359 
360  // Copy intensity values
361  switch(pointCloudColorMode) {
362  case INTENSITY:
363  copyPointCloudIntensity<INTENSITY>(imagePair);
364  break;
365  case RGB_COMBINED:
366  copyPointCloudIntensity<RGB_COMBINED>(imagePair);
367  break;
368  case RGB_SEPARATE:
369  copyPointCloudIntensity<RGB_SEPARATE>(imagePair);
370  break;
371  case NONE:
372  break;
373  }
374 
375  cloudPublisher->publish(pointCloudMsg);
376 }
377 
378 template <StereoNodeBase::PointCloudColorMode colorMode> void StereoNodeBase::copyPointCloudIntensity(ImagePair& imagePair) {
379  // Get pointers to the beginnig and end of the point cloud
380  unsigned char* cloudStart = &pointCloudMsg->data[0];
381  unsigned char* cloudEnd = &pointCloudMsg->data[0]
382  + imagePair.getWidth()*imagePair.getHeight()*4*sizeof(float);
383 
384  if(imagePair.getPixelFormat(0) == ImagePair::FORMAT_8_BIT_MONO) {
385  // Get pointer to the current pixel and end of current row
386  unsigned char* imagePtr = imagePair.getPixelData(0);
387  unsigned char* rowEndPtr = imagePtr + imagePair.getWidth();
388  int rowIncrement = imagePair.getRowStride(0) - imagePair.getWidth();
389 
390  for(unsigned char* cloudPtr = cloudStart + 3*sizeof(float);
391  cloudPtr < cloudEnd; cloudPtr+= 4*sizeof(float)) {
392  if(colorMode == RGB_SEPARATE) {// RGB as float
393  *reinterpret_cast<float*>(cloudPtr) = static_cast<float>(*imagePtr) / 255.0F;
394  } else if(colorMode == RGB_COMBINED) {// RGB as integer
395  const unsigned char intensity = *imagePtr;
396  *reinterpret_cast<unsigned int*>(cloudPtr) = (intensity << 16) | (intensity << 8) | intensity;
397  } else {
398  *cloudPtr = *imagePtr;
399  }
400 
401  imagePtr++;
402  if(imagePtr == rowEndPtr) {
403  // Progress to next row
404  imagePtr += rowIncrement;
405  rowEndPtr = imagePtr + imagePair.getWidth();
406  }
407  }
408  } else if(imagePair.getPixelFormat(0) == ImagePair::FORMAT_12_BIT_MONO) {
409  // Get pointer to the current pixel and end of current row
410  unsigned short* imagePtr = reinterpret_cast<unsigned short*>(imagePair.getPixelData(0));
411  unsigned short* rowEndPtr = imagePtr + imagePair.getWidth();
412  int rowIncrement = imagePair.getRowStride(0) - 2*imagePair.getWidth();
413 
414  for(unsigned char* cloudPtr = cloudStart + 3*sizeof(float);
415  cloudPtr < cloudEnd; cloudPtr+= 4*sizeof(float)) {
416 
417  if(colorMode == RGB_SEPARATE) {// RGB as float
418  *reinterpret_cast<float*>(cloudPtr) = static_cast<float>(*imagePtr) / 4095.0F;
419  } else if(colorMode == RGB_COMBINED) {// RGB as integer
420  const unsigned char intensity = *imagePtr/16;
421  *reinterpret_cast<unsigned int*>(cloudPtr) = (intensity << 16) | (intensity << 8) | intensity;
422  } else {
423  *cloudPtr = *imagePtr/16;
424  }
425 
426  imagePtr++;
427  if(imagePtr == rowEndPtr) {
428  // Progress to next row
429  imagePtr += rowIncrement;
430  rowEndPtr = imagePtr + imagePair.getWidth();
431  }
432  }
433  } else if(imagePair.getPixelFormat(0) == ImagePair::FORMAT_8_BIT_RGB) {
434  // Get pointer to the current pixel and end of current row
435  unsigned char* imagePtr = imagePair.getPixelData(0);
436  unsigned char* rowEndPtr = imagePtr + imagePair.getWidth();
437  int rowIncrement = imagePair.getRowStride(0) - imagePair.getWidth();
438 
439  static bool warned = false;
440  if(colorMode == RGB_SEPARATE && !warned) {
441  warned = true;
442  ROS_WARN("RGBF32 is not supported for color images. Please use RGB8!");
443  }
444 
445  for(unsigned char* cloudPtr = cloudStart + 3*sizeof(float);
446  cloudPtr < cloudEnd; cloudPtr+= 4*sizeof(float)) {
447  if(colorMode == RGB_SEPARATE) {// RGB as float
448  *reinterpret_cast<float*>(cloudPtr) = static_cast<float>(imagePtr[2]) / 255.0F;
449  } else if(colorMode == RGB_COMBINED) {// RGB as integer
450  *reinterpret_cast<unsigned int*>(cloudPtr) = (imagePtr[0] << 16) | (imagePtr[1] << 8) | imagePtr[2];
451  } else {
452  *cloudPtr = (imagePtr[0] + imagePtr[1]*2 + imagePtr[2])/4;
453  }
454 
455  imagePtr+=3;
456  if(imagePtr == rowEndPtr) {
457  // Progress to next row
458  imagePtr += rowIncrement;
459  rowEndPtr = imagePtr + imagePair.getWidth();
460  }
461  }
462  } else {
463  throw std::runtime_error("Invalid pixel format!");
464  }
465 }
466 
467 template <int coord> void StereoNodeBase::copyPointCloudClamped(float* src, float* dst, int size) {
468  // Only copy points that are below the minimum depth
469  float* endPtr = src + 4*size;
470  for(float *srcPtr = src, *dstPtr = dst; srcPtr < endPtr; srcPtr+=4, dstPtr+=4) {
471  if(srcPtr[coord] > maxDepth) {
472  dstPtr[0] = std::numeric_limits<float>::quiet_NaN();
473  dstPtr[1] = std::numeric_limits<float>::quiet_NaN();
474  dstPtr[2] = std::numeric_limits<float>::quiet_NaN();
475  } else {
476  dstPtr[0] = srcPtr[0];
477  dstPtr[1] = srcPtr[1];
478  dstPtr[2] = srcPtr[2];
479  }
480  }
481 }
482 
484  //ros::NodeHandle privateNh("~"); // RYT TODO check
485 
486  // Initialize 3D reconstruction class
487  recon3d.reset(new Reconstruct3D);
488 
489  // Initialize message
490  pointCloudMsg.reset(new sensor_msgs::PointCloud2);
491 
492  // Set channel information.
493  sensor_msgs::PointField fieldX;
494  fieldX.name ="x";
495  fieldX.offset = 0;
496  fieldX.datatype = sensor_msgs::PointField::FLOAT32;
497  fieldX.count = 1;
498  pointCloudMsg->fields.push_back(fieldX);
499 
500  sensor_msgs::PointField fieldY;
501  fieldY.name ="y";
502  fieldY.offset = sizeof(float);
503  fieldY.datatype = sensor_msgs::PointField::FLOAT32;
504  fieldY.count = 1;
505  pointCloudMsg->fields.push_back(fieldY);
506 
507  sensor_msgs::PointField fieldZ;
508  fieldZ.name ="z";
509  fieldZ.offset = 2*sizeof(float);
510  fieldZ.datatype = sensor_msgs::PointField::FLOAT32;
511  fieldZ.count = 1;
512  pointCloudMsg->fields.push_back(fieldZ);
513 
515  sensor_msgs::PointField fieldI;
516  fieldI.name ="intensity";
517  fieldI.offset = 3*sizeof(float);
518  fieldI.datatype = sensor_msgs::PointField::UINT8;
519  fieldI.count = 1;
520  pointCloudMsg->fields.push_back(fieldI);
521  }
522  else if(pointCloudColorMode == RGB_SEPARATE) {
523  sensor_msgs::PointField fieldRed;
524  fieldRed.name ="r";
525  fieldRed.offset = 3*sizeof(float);
526  fieldRed.datatype = sensor_msgs::PointField::FLOAT32;
527  fieldRed.count = 1;
528  pointCloudMsg->fields.push_back(fieldRed);
529 
530  sensor_msgs::PointField fieldGreen;
531  fieldGreen.name ="g";
532  fieldGreen.offset = 3*sizeof(float);
533  fieldGreen.datatype = sensor_msgs::PointField::FLOAT32;
534  fieldGreen.count = 1;
535  pointCloudMsg->fields.push_back(fieldGreen);
536 
537  sensor_msgs::PointField fieldBlue;
538  fieldBlue.name ="b";
539  fieldBlue.offset = 3*sizeof(float);
540  fieldBlue.datatype = sensor_msgs::PointField::FLOAT32;
541  fieldBlue.count = 1;
542  pointCloudMsg->fields.push_back(fieldBlue);
543  } else if(pointCloudColorMode == RGB_COMBINED) {
544  sensor_msgs::PointField fieldRGB;
545  fieldRGB.name ="rgb";
546  fieldRGB.offset = 3*sizeof(float);
547  fieldRGB.datatype = sensor_msgs::PointField::UINT32;
548  fieldRGB.count = 1;
549  pointCloudMsg->fields.push_back(fieldRGB);
550  }
551 }
552 
553 void StereoNodeBase::publishCameraInfo(ros::Time stamp, const ImagePair& imagePair) {
554  if(camInfoMsg == NULL) {
555  // Initialize the camera info structure
556  camInfoMsg.reset(new nerian_stereo::StereoCameraInfo);
557 
558  camInfoMsg->header.frame_id = frame;
559  camInfoMsg->header.seq = imagePair.getSequenceNumber(); // Actually ROS will overwrite this
560 
561  if(calibFile != "") {
562  std::vector<int> sizeVec;
563  calibStorage["size"] >> sizeVec;
564  if(sizeVec.size() != 2) {
565  std::runtime_error("Calibration file format error!");
566  }
567 
568  camInfoMsg->left_info.header = camInfoMsg->header;
569  camInfoMsg->left_info.width = sizeVec[0];
570  camInfoMsg->left_info.height = sizeVec[1];
571  camInfoMsg->left_info.distortion_model = "plumb_bob";
572  calibStorage["D1"] >> camInfoMsg->left_info.D;
573  readCalibrationArray("M1", camInfoMsg->left_info.K);
574  readCalibrationArray("R1", camInfoMsg->left_info.R);
575  readCalibrationArray("P1", camInfoMsg->left_info.P);
576  camInfoMsg->left_info.binning_x = 1;
577  camInfoMsg->left_info.binning_y = 1;
578  camInfoMsg->left_info.roi.do_rectify = false;
579  camInfoMsg->left_info.roi.height = 0;
580  camInfoMsg->left_info.roi.width = 0;
581  camInfoMsg->left_info.roi.x_offset = 0;
582  camInfoMsg->left_info.roi.y_offset = 0;
583 
584  camInfoMsg->right_info.header = camInfoMsg->header;
585  camInfoMsg->right_info.width = sizeVec[0];
586  camInfoMsg->right_info.height = sizeVec[1];
587  camInfoMsg->right_info.distortion_model = "plumb_bob";
588  calibStorage["D2"] >> camInfoMsg->right_info.D;
589  readCalibrationArray("M2", camInfoMsg->right_info.K);
590  readCalibrationArray("R2", camInfoMsg->right_info.R);
591  readCalibrationArray("P2", camInfoMsg->right_info.P);
592  camInfoMsg->right_info.binning_x = 1;
593  camInfoMsg->right_info.binning_y = 1;
594  camInfoMsg->right_info.roi.do_rectify = false;
595  camInfoMsg->right_info.roi.height = 0;
596  camInfoMsg->right_info.roi.width = 0;
597  camInfoMsg->right_info.roi.x_offset = 0;
598  camInfoMsg->right_info.roi.y_offset = 0;
599 
601  readCalibrationArray("T", camInfoMsg->T_left_right);
602  readCalibrationArray("R", camInfoMsg->R_left_right);
603  }
604  }
605 
606  double dt = (stamp - lastCamInfoPublish).toSec();
607  if(dt > 1.0) {
608  // Rather use the Q-matrix that we received over the network if it is valid
609  const float* qMatrix = imagePair.getQMatrix();
610  if(qMatrix[0] != 0.0) {
611  for(int i=0; i<16; i++) {
612  camInfoMsg->Q[i] = static_cast<double>(qMatrix[i]);
613  }
614  }
615 
616  // Publish once per second
617  camInfoMsg->header.stamp = stamp;
618  camInfoMsg->left_info.header.stamp = stamp;
619  camInfoMsg->right_info.header.stamp = stamp;
621 
622  lastCamInfoPublish = stamp;
623  }
624 }
625 
626 template<class T> void StereoNodeBase::readCalibrationArray(const char* key, T& dest) {
627  std::vector<double> doubleVec;
628  calibStorage[key] >> doubleVec;
629 
630  if(doubleVec.size() != dest.size()) {
631  std::runtime_error("Calibration file format error!");
632  }
633 
634  std::copy(doubleVec.begin(), doubleVec.end(), dest.begin());
635 }
636 
637 } // namespace
virtual ros::NodeHandle & getPrivateNH()=0
void prepareAsyncTransfer()
Connects to the image service to request the stream of image pairs.
sensor_msgs::PointCloud2Ptr pointCloudMsg
boost::scoped_ptr< dynamic_reconfigure::Server< nerian_stereo::NerianStereoConfig > > dynReconfServer
boost::scoped_ptr< ColorCoder > colCoder
void publish(const boost::shared_ptr< M > &message) const
virtual ros::NodeHandle & getNH()=0
void loadCameraCalibration()
Loads a camera calibration file if configured.
nerian_stereo::StereoCameraInfoPtr camInfoMsg
boost::scoped_ptr< ros::Publisher > rightImagePublisher
void publishCameraInfo(ros::Time stamp, const ImagePair &imagePair)
Publishes the camera info once per second.
bool sleep() const
void copyPointCloudIntensity(ImagePair &imagePair)
Copies the intensity or RGB data to the point cloud.
void publishPointCloudMsg(ImagePair &imagePair, ros::Time stamp)
Reconstructs the 3D locations form the disparity map and publishes them as point cloud.
void autogen_updateParameterServerFromDevice(std::map< std::string, ParameterInfo > &cfg)
Auto-generated code to set initial parameters according to those obtained from the device...
void autogen_updateDynamicReconfigureFromDevice(std::map< std::string, ParameterInfo > &cfg)
Auto-generated code to override the dynamic_reconfigure limits and defaults for all parameters...
void dynamicReconfigureCallback(nerian_stereo::NerianStereoConfig &config, uint32_t level)
#define ROS_WARN(...)
#define ROS_INFO(...)
boost::scoped_ptr< SceneScanParameters > sceneScanParameters
void initPointCloud()
Performs all neccessary initializations for point cloud+ publishing.
void readCalibrationArray(const char *key, T &dest)
Reads a vector from the calibration file to a boost:array.
boost::scoped_ptr< Reconstruct3D > recon3d
boost::scoped_ptr< ros::Publisher > cloudPublisher
boost::scoped_ptr< ros::Publisher > disparityPublisher
void publishImageMsg(const ImagePair &imagePair, int imageIndex, ros::Time stamp, bool allowColorCode, ros::Publisher *publisher)
Publishes the disparity map as 16-bit grayscale image or color coded RGB image.
uint32_t getNumSubscribers() const
bool getParam(const std::string &key, std::string &s) const
A driver node that receives data from SceneScan/SP1 and forwards it to ROS.
boost::scoped_ptr< ros::Publisher > leftImagePublisher
void updateParameterServerFromDevice(std::map< std::string, ParameterInfo > &cfg)
void updateDynamicReconfigureFromDevice(std::map< std::string, ParameterInfo > &cfg)
static Time now()
void copyPointCloudClamped(float *src, float *dst, int size)
Copies all points in a point cloud that have a depth smaller than maxDepth. Other points are set to N...
void qMatrixToRosCoords(const float *src, float *dst)
Transform Q matrix to match the ROS coordinate system: Swap y/z axis, then swap x/y axis...
#define ROS_ERROR(...)
void setParam(const std::string &key, const XmlRpc::XmlRpcValue &v) const
sensor_msgs::ImagePtr toImageMsg() const
void init()
Performs general initializations.
void autogen_dynamicReconfigureCallback(nerian_stereo::NerianStereoConfig &config, uint32_t level)
Auto-generated code to check for parameter changes and forward them to the device.
std_msgs::Header header
boost::scoped_ptr< ros::Publisher > cameraInfoPublisher
nerian_stereo::NerianStereoConfig lastKnownConfig
boost::scoped_ptr< AsyncTransfer > asyncTransfer


nerian_stereo
Author(s): Nerian Vision Technologies
autogenerated on Thu Jun 20 2019 19:12:46