16 #include <visiontransfer/asynctransfer.h> 17 #include <visiontransfer/reconstruct3d.h> 18 #include <sensor_msgs/PointCloud2.h> 19 #include <sensor_msgs/Image.h> 20 #include <opencv2/opencv.hpp> 24 #include <nerian_sp1/StereoCameraInfo.h> 25 #include <boost/smart_ptr.hpp> 26 #include <colorcoder.h> 64 if (!privateNh.
getParam(
"point_cloud_intensity_channel", intensityChannel)) {
65 intensityChannel =
true;
68 if (!privateNh.
getParam(
"color_code_disparity_map", colorCodeDispMap)) {
69 colorCodeDispMap =
true;
72 if (!privateNh.
getParam(
"color_code_legend", colorCodeLegend)) {
73 colorCodeLegend =
false;
76 if (!privateNh.
getParam(
"frame", frame)) {
80 if (!privateNh.
getParam(
"remote_port", remotePort)) {
84 if (!privateNh.
getParam(
"remote_host", remoteHost)) {
85 remoteHost =
"0.0.0.0";
88 if (!privateNh.
getParam(
"local_port", localPort)) {
92 if (!privateNh.
getParam(
"local_host", localHost)) {
93 localHost =
"0.0.0.0";
96 if (!privateNh.
getParam(
"use_tcp", useTcp)) {
100 if (!privateNh.
getParam(
"ros_coordinate_system", rosCoordinateSystem)) {
101 rosCoordinateSystem =
true;
104 if (!privateNh.
getParam(
"calibration_file", calibFile)) {
108 if (!privateNh.
getParam(
"delay_execution", execDelay)) {
112 if (!privateNh.
getParam(
"max_depth", maxDepth)) {
120 disparityPublisher.reset(
new ros::Publisher(nh.advertise<sensor_msgs::Image>(
121 "/nerian_sp1/disparity_map", 5)));
122 leftImagePublisher.reset(
new ros::Publisher(nh.advertise<sensor_msgs::Image>(
123 "/nerian_sp1/left_image", 5)));
124 rightImagePublisher.reset(
new ros::Publisher(nh.advertise<sensor_msgs::Image>(
125 "/nerian_sp1/right_image", 5)));
127 loadCameraCalibration();
129 cameraInfoPublisher.reset(
new ros::Publisher(nh.advertise<nerian_sp1::StereoCameraInfo>(
130 "/nerian_sp1/stereo_camera_info", 1)));
131 cloudPublisher.reset(
new ros::Publisher(nh.advertise<sensor_msgs::PointCloud2>(
132 "/nerian_sp1/point_cloud", 5)));
141 int lastLogFrames = 0;
143 AsyncTransfer asyncTransfer(useTcp ? ImageTransfer::TCP_CLIENT : ImageTransfer::UDP,
144 remoteHost.c_str(), remotePort.c_str(), localHost.c_str(), localPort.c_str());
149 if(!asyncTransfer.collectReceivedImagePair(imagePair, 0.5)) {
156 if(leftImagePublisher->getNumSubscribers() > 0) {
157 publishImageMsg(imagePair, stamp);
160 if(disparityPublisher->getNumSubscribers() > 0 || rightImagePublisher->getNumSubscribers() > 0) {
161 publishDispMapMsg(imagePair, stamp);
164 if(cloudPublisher->getNumSubscribers() > 0) {
165 if(recon3d ==
nullptr) {
170 publishPointCloudMsg(imagePair, stamp);
173 if(cameraInfoPublisher != NULL && cameraInfoPublisher->getNumSubscribers() > 0) {
174 publishCameraInfo(stamp, imagePair);
179 if(stamp.
sec != lastLogTime.
sec) {
181 double dt = (stamp - lastLogTime).toSec();
182 double fps = (frameNum - lastLogFrames) / dt;
185 lastLogFrames = frameNum;
189 }
catch(
const std::exception& ex) {
190 ROS_FATAL(
"Exception occured: %s", ex.what());
232 if(calibFile ==
"" ) {
233 ROS_WARN(
"No camera calibration file configured. Cannot publish detailed camera information!");
235 bool success =
false;
237 if (calibStorage.open(calibFile, cv::FileStorage::READ)) {
244 ROS_WARN(
"Error reading calibration file: %s\n" 245 "Cannot publish detailed camera information!", calibFile.c_str());
255 cvImg.
header.frame_id = frame;
256 cvImg.
header.stamp = stamp;
257 cvImg.
header.seq = imagePair.getSequenceNumber();
259 cvImg.
image = cv::Mat_<unsigned char>(imagePair.getHeight(),
260 imagePair.getWidth(), imagePair.getPixelData(0), imagePair.getRowStride(0));
261 sensor_msgs::ImagePtr msg = cvImg.
toImageMsg();
264 msg->encoding =
"mono8";
265 leftImagePublisher->publish(msg);
274 cvImg.
header.frame_id = frame;
275 cvImg.
header.stamp = stamp;
276 cvImg.
header.seq = imagePair.getSequenceNumber();
278 bool format12Bit = (imagePair.getPixelFormat(1) == ImagePair::FORMAT_12_BIT);
279 cv::Mat monoImg(imagePair.getHeight(), imagePair.getWidth(),
280 format12Bit ? CV_16UC1 : CV_8UC1,
281 imagePair.getPixelData(1), imagePair.getRowStride(1));
282 string encoding =
"";
284 if(!colorCodeDispMap || !format12Bit) {
285 cvImg.
image = monoImg;
286 encoding = (format12Bit ?
"mono16":
"mono8");
288 if(colCoder == NULL) {
289 colCoder.reset(
new ColorCoder(ColorCoder::COLOR_RED_BLUE_BGR, 0, 16*111,
true,
true));
290 if(colorCodeLegend) {
292 colDispMap = colCoder->createLegendBorder(monoImg.cols, monoImg.rows, 1.0/16.0);
294 colDispMap = cv::Mat_<cv::Vec3b>(monoImg.rows, monoImg.cols);
298 cv::Mat_<cv::Vec3b> dispSection = colDispMap(cv::Rect(0, 0, monoImg.cols, monoImg.rows));
300 colCoder->codeImage(cv::Mat_<unsigned short>(monoImg), dispSection);
301 cvImg.
image = colDispMap;
305 sensor_msgs::ImagePtr msg = cvImg.
toImageMsg();
306 msg->encoding = encoding;
308 if(disparityPublisher->getNumSubscribers() > 0 && format12Bit) {
309 disparityPublisher->publish(msg);
311 if(rightImagePublisher->getNumSubscribers() > 0 && !format12Bit) {
312 rightImagePublisher->publish(msg);
321 dst[0] = src[8]; dst[1] = src[9];
322 dst[2] = src[10]; dst[3] = src[11];
324 dst[4] = -src[0]; dst[5] = -src[1];
325 dst[6] = -src[2]; dst[7] = -src[3];
327 dst[8] = -src[4]; dst[9] = -src[5];
328 dst[10] = -src[6]; dst[11] = -src[7];
330 dst[12] = src[12]; dst[13] = src[13];
331 dst[14] = src[14]; dst[15] = src[15];
339 if(imagePair.getPixelFormat(1) != ImagePair::FORMAT_12_BIT) {
345 if(rosCoordinateSystem) {
346 qMatrixToRosCoords(imagePair.getQMatrix(), qRos);
347 imagePair.setQMatrix(qRos);
351 float* pointMap =
nullptr;
353 pointMap = recon3d->createPointMap(imagePair, 0);
354 }
catch(std::exception& ex) {
355 cerr <<
"Error creating point cloud: " << ex.what() << endl;
360 pointCloudMsg->header.stamp = stamp;
361 pointCloudMsg->header.frame_id = frame;
362 pointCloudMsg->header.seq = imagePair.getSequenceNumber();
365 if(pointCloudMsg->data.size() != imagePair.getWidth()*imagePair.getHeight()*4*
sizeof(float)) {
367 pointCloudMsg->data.resize(imagePair.getWidth()*imagePair.getHeight()*4*
sizeof(float));
370 pointCloudMsg->width = imagePair.getWidth();
371 pointCloudMsg->height = imagePair.getHeight();
372 pointCloudMsg->is_bigendian =
false;
373 pointCloudMsg->point_step = 4*
sizeof(float);
374 pointCloudMsg->row_step = imagePair.getWidth() * pointCloudMsg->point_step;
375 pointCloudMsg->is_dense =
false;
380 memcpy(&pointCloudMsg->data[0], pointMap,
381 imagePair.getWidth()*imagePair.getHeight()*4*
sizeof(float));
384 if(rosCoordinateSystem) {
385 copyPointCloudClamped<0>(pointMap,
reinterpret_cast<float*
>(&pointCloudMsg->data[0]),
386 imagePair.getWidth()*imagePair.getHeight());
388 copyPointCloudClamped<2>(pointMap,
reinterpret_cast<float*
>(&pointCloudMsg->data[0]),
389 imagePair.getWidth()*imagePair.getHeight());
394 if(intensityChannel) {
396 unsigned char* cloudStart = &pointCloudMsg->data[0];
397 unsigned char* cloudEnd = &pointCloudMsg->data[0]
398 + imagePair.getWidth()*imagePair.getHeight()*4*
sizeof(float);
401 unsigned char* imagePtr = imagePair.getPixelData(0);
402 unsigned char* rowEndPtr = imagePtr + imagePair.getWidth();
403 int rowIncrement = imagePair.getRowStride(0) - imagePair.getWidth();
405 for(
unsigned char* cloudPtr = cloudStart + 3*
sizeof(
float);
406 cloudPtr < cloudEnd; cloudPtr+= 4*
sizeof(float)) {
407 *cloudPtr = *imagePtr;
410 if(imagePtr == rowEndPtr) {
412 imagePtr += rowIncrement;
413 rowEndPtr = imagePtr + imagePair.getWidth();
418 cloudPublisher->publish(pointCloudMsg);
427 float* endPtr = src + 4*size;
428 for(
float *srcPtr = src, *dstPtr = dst; srcPtr < endPtr; srcPtr+=4, dstPtr+=4) {
429 if(srcPtr[coord] > maxDepth) {
430 dstPtr[0] = std::numeric_limits<float>::quiet_NaN();
431 dstPtr[1] = std::numeric_limits<float>::quiet_NaN();
432 dstPtr[2] = std::numeric_limits<float>::quiet_NaN();
434 dstPtr[0] = srcPtr[0];
435 dstPtr[1] = srcPtr[1];
436 dstPtr[2] = srcPtr[2];
449 recon3d.reset(
new Reconstruct3D);
452 pointCloudMsg.reset(
new sensor_msgs::PointCloud2);
455 sensor_msgs::PointField fieldX;
458 fieldX.datatype = sensor_msgs::PointField::FLOAT32;
460 pointCloudMsg->fields.push_back(fieldX);
462 sensor_msgs::PointField fieldY;
464 fieldY.offset =
sizeof(float);
465 fieldY.datatype = sensor_msgs::PointField::FLOAT32;
467 pointCloudMsg->fields.push_back(fieldY);
469 sensor_msgs::PointField fieldZ;
471 fieldZ.offset = 2*
sizeof(float);
472 fieldZ.datatype = sensor_msgs::PointField::FLOAT32;
474 pointCloudMsg->fields.push_back(fieldZ);
476 if(intensityChannel) {
477 sensor_msgs::PointField fieldI;
478 fieldI.name =
"intensity";
479 fieldI.offset = 3*
sizeof(float);
480 fieldI.datatype = sensor_msgs::PointField::UINT8;
482 pointCloudMsg->fields.push_back(fieldI);
490 if(camInfoMsg == NULL) {
492 camInfoMsg.reset(
new nerian_sp1::StereoCameraInfo);
494 camInfoMsg->header.frame_id = frame;
495 camInfoMsg->header.seq = imagePair.getSequenceNumber();
497 if(calibFile !=
"") {
498 std::vector<int> sizeVec;
499 calibStorage[
"size"] >> sizeVec;
500 if(sizeVec.size() != 2) {
501 std::runtime_error(
"Calibration file format error!");
504 camInfoMsg->left_info.header = camInfoMsg->header;
505 camInfoMsg->left_info.width = sizeVec[0];
506 camInfoMsg->left_info.height = sizeVec[1];
507 camInfoMsg->left_info.distortion_model =
"plumb_bob";
508 calibStorage[
"D1"] >> camInfoMsg->left_info.D;
509 readCalibrationArray(
"M1", camInfoMsg->left_info.K);
510 readCalibrationArray(
"R1", camInfoMsg->left_info.R);
511 readCalibrationArray(
"P1", camInfoMsg->left_info.P);
512 camInfoMsg->left_info.binning_x = 1;
513 camInfoMsg->left_info.binning_y = 1;
514 camInfoMsg->left_info.roi.do_rectify =
false;
515 camInfoMsg->left_info.roi.height = 0;
516 camInfoMsg->left_info.roi.width = 0;
517 camInfoMsg->left_info.roi.x_offset = 0;
518 camInfoMsg->left_info.roi.y_offset = 0;
520 camInfoMsg->right_info.header = camInfoMsg->header;
521 camInfoMsg->right_info.width = sizeVec[0];
522 camInfoMsg->right_info.height = sizeVec[1];
523 camInfoMsg->right_info.distortion_model =
"plumb_bob";
524 calibStorage[
"D2"] >> camInfoMsg->right_info.D;
525 readCalibrationArray(
"M2", camInfoMsg->right_info.K);
526 readCalibrationArray(
"R2", camInfoMsg->right_info.R);
527 readCalibrationArray(
"P2", camInfoMsg->right_info.P);
528 camInfoMsg->right_info.binning_x = 1;
529 camInfoMsg->right_info.binning_y = 1;
530 camInfoMsg->right_info.roi.do_rectify =
false;
531 camInfoMsg->right_info.roi.height = 0;
532 camInfoMsg->right_info.roi.width = 0;
533 camInfoMsg->right_info.roi.x_offset = 0;
534 camInfoMsg->right_info.roi.y_offset = 0;
536 readCalibrationArray(
"Q", camInfoMsg->Q);
537 readCalibrationArray(
"T", camInfoMsg->T_left_right);
538 readCalibrationArray(
"R", camInfoMsg->R_left_right);
542 double dt = (stamp - lastCamInfoPublish).toSec();
545 const float* qMatrix = imagePair.getQMatrix();
546 if(qMatrix[0] != 0.0) {
547 for(
int i=0; i<16; i++) {
548 camInfoMsg->Q[i] =
static_cast<double>(qMatrix[i]);
553 camInfoMsg->header.stamp = stamp;
554 camInfoMsg->left_info.header.stamp = stamp;
555 camInfoMsg->right_info.header.stamp = stamp;
556 cameraInfoPublisher->publish(camInfoMsg);
558 lastCamInfoPublish = stamp;
567 std::vector<double> doubleVec;
568 calibStorage[key] >> doubleVec;
570 if(doubleVec.size() != dest.size()) {
571 std::runtime_error(
"Calibration file format error!");
574 std::copy(doubleVec.begin(), doubleVec.end(), dest.begin());
578 int main(
int argc,
char** argv) {
584 }
catch(
const std::exception& ex) {
585 ROS_FATAL(
"Exception occured: %s", ex.what());
ros::Time lastCamInfoPublish
cv::Mat_< cv::Vec3b > colDispMap
void publishImageMsg(const ImagePair &imagePair, ros::Time stamp)
Publishes a rectified left camera image.
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
A simple node that receives data from the SP1 stereo vision system and forwards it to ROS...
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...
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...
boost::scoped_ptr< ros::Publisher > rightImagePublisher
cv::FileStorage calibStorage
void loadCameraCalibration()
Loads a camera calibration file if configured.
void initPointCloud()
Performs all neccessary initializations for point cloud+ publishing.
int main(int argc, char **argv)
boost::scoped_ptr< ros::Publisher > cloudPublisher
int run()
The main loop of this node.
nerian_sp1::StereoCameraInfoPtr camInfoMsg
boost::scoped_ptr< ColorCoder > colCoder
void readCalibrationArray(const char *key, T &dest)
Reads a vector from the calibration file to a boost:array.
void publishPointCloudMsg(ImagePair &imagePair, ros::Time stamp)
Reconstructs the 3D locations form the disparity map and publishes them as point cloud.
boost::scoped_ptr< Reconstruct3D > recon3d
bool getParam(const std::string &key, std::string &s) const
boost::scoped_ptr< ros::Publisher > cameraInfoPublisher
boost::scoped_ptr< ros::Publisher > leftImagePublisher
void init()
Performs general initializations.
sensor_msgs::PointCloud2Ptr pointCloudMsg
boost::scoped_ptr< ros::Publisher > disparityPublisher
sensor_msgs::ImagePtr toImageMsg() const
void publishCameraInfo(ros::Time stamp, const ImagePair &imagePair)
Publishes the camera info once per second.
void publishDispMapMsg(const ImagePair &imagePair, ros::Time stamp)
Publishes the disparity map as 16-bit grayscale image or color coded RGB image.