00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 #include <pluginlib/class_list_macros.h>
00040 #include "openni_nodelet.h"
00041 #include "openni_camera/openni_device_kinect.h"
00042 #include "openni_camera/openni_image.h"
00043 #include "openni_camera/openni_depth_image.h"
00044 #include <sensor_msgs/image_encodings.h>
00045 #include <pcl_ros/point_cloud.h>
00046 #include <pcl/point_types.h>
00047 #include <stereo_msgs/DisparityImage.h>
00048
00049 #if ROS_VERSION_MINIMUM(1, 3, 0)
00050 #include <sensor_msgs/distortion_models.h>
00051 #endif
00052
00053 using namespace std;
00054 using namespace openni_wrapper;
00055 namespace openni_camera
00056 {
00057 inline bool operator == (const XnMapOutputMode& mode1, const XnMapOutputMode& mode2)
00058 {
00059 return (mode1.nXRes == mode2.nXRes && mode1.nYRes == mode2.nYRes && mode1.nFPS == mode2.nFPS);
00060 }
00061 inline bool operator != (const XnMapOutputMode& mode1, const XnMapOutputMode& mode2)
00062 {
00063 return !(mode1 == mode2);
00064 }
00065
00066
00067 PLUGINLIB_DECLARE_CLASS (openni_camera, OpenNINodelet, openni_camera::OpenNINodelet, nodelet::Nodelet);
00068
00069 typedef union
00070 {
00071 struct
00072 {
00073 unsigned char Blue;
00074 unsigned char Green;
00075 unsigned char Red;
00076 unsigned char Alpha;
00077 };
00078 float float_value;
00079 long long_value;
00080 } RGBValue;
00081
00082 OpenNINodelet::~OpenNINodelet ()
00083 {
00084 device_->stopDepthStream ();
00085 device_->stopImageStream ();
00086 }
00087
00088 void OpenNINodelet::onInit ()
00089 {
00090
00091
00092
00093 ros::NodeHandle comm_nh(getNodeHandle ().resolveName ("camera"));
00094 ros::NodeHandle param_nh = getPrivateNodeHandle ();
00095
00096 updateModeMaps ();
00097 device_ = boost::shared_ptr<openni_wrapper::OpenNIDevice > ((openni_wrapper::OpenNIDevice*)NULL);
00098 setupDevice (param_nh);
00099
00100 param_nh.param ("rgb_frame_id", rgb_frame_id_, string (""));
00101 if (rgb_frame_id_.empty ())
00102 {
00103 rgb_frame_id_ = "/openni_rgb_optical_frame";
00104 NODELET_INFO ("'rgb_frame_id' not set. using default: '%s'", rgb_frame_id_.c_str());
00105 }
00106 else
00107 NODELET_INFO ("rgb_frame_id = '%s' ", rgb_frame_id_.c_str());
00108
00109 param_nh.param ("depth_frame_id", depth_frame_id_, string (""));
00110 if (depth_frame_id_.empty ())
00111 {
00112 depth_frame_id_ = "/openni_depth_optical_frame";
00113 NODELET_INFO ("'depth_frame_id' not set. using default: '%s'", depth_frame_id_.c_str());
00114 }
00115 else
00116 NODELET_INFO ("depth_frame_id = '%s' ", depth_frame_id_.c_str());
00117
00118 param_nh.param ("use_indices", use_indices_, false);
00119
00120 image_transport::ImageTransport imageTransport (comm_nh);
00121 image_transport::SubscriberStatusCallback subscriberChanged = boost::bind(&OpenNINodelet::subscriberChangedEvent, this);
00122 pub_rgb_image_ = imageTransport.advertise ("rgb/image_color", 5, subscriberChanged, subscriberChanged );
00123 pub_image_raw_ = imageTransport.advertise ("rgb/image_raw", 5, subscriberChanged, subscriberChanged );
00124 pub_gray_image_ = imageTransport.advertise ("rgb/image_mono" , 5, subscriberChanged, subscriberChanged );
00125 pub_depth_image_ = imageTransport.advertise ("depth/image" , 5, subscriberChanged, subscriberChanged );
00126 pub_depth_raw_ = imageTransport.advertise ("depth/image_raw", 5, subscriberChanged, subscriberChanged );
00127
00128 ros::SubscriberStatusCallback subscriberChanged2 = boost::bind(&OpenNINodelet::subscriberChangedEvent, this);
00129 pub_disparity_ = comm_nh.advertise<stereo_msgs::DisparityImage > ("depth/disparity", 5, subscriberChanged2, subscriberChanged2);
00130 pub_rgb_info_ = comm_nh.advertise<sensor_msgs::CameraInfo > ("rgb/camera_info", 5, subscriberChanged2, subscriberChanged2);
00131 pub_depth_info_ = comm_nh.advertise<sensor_msgs::CameraInfo > ("depth/camera_info", 5, subscriberChanged2, subscriberChanged2);
00132 pub_point_cloud_ = comm_nh.advertise<pcl::PointCloud<pcl::PointXYZ> > ("depth/points", 5, subscriberChanged2, subscriberChanged2);
00133 pub_point_cloud_rgb_ = comm_nh.advertise<pcl::PointCloud<pcl::PointXYZRGB> > ("rgb/points", 5, subscriberChanged2, subscriberChanged2);
00134
00135 if (use_indices_)
00136 sub_mask_indices_ = comm_nh.subscribe("depth/indices", 5, &OpenNINodelet::maskIndicesCb, this);
00137
00138 SyncPolicy sync_policy (4);
00139 depth_rgb_sync_.reset (new Synchronizer (sync_policy));
00140 depth_rgb_sync_->registerCallback (boost::bind (&OpenNINodelet::publishXYZRGBPointCloud, this, _1, _2));
00141
00142
00143 reconfigure_server_.reset (new ReconfigureServer (reconfigure_mutex_, param_nh));
00144 reconfigure_mutex_.lock ();
00145 reconfigure_server_->updateConfig (config_);
00146 reconfigure_server_->setCallback (boost::bind (&OpenNINodelet::configCallback, this, _1, _2));
00147 reconfigure_mutex_.unlock ();
00148 }
00149
00150 void OpenNINodelet::setupDevice (ros::NodeHandle& param_nh)
00151 {
00152
00153 OpenNIDriver& driver = OpenNIDriver::getInstance ();
00154
00155 do {
00156
00157 if (!ros::ok())
00158 exit(-1);
00159
00160 driver.updateDeviceList ();
00161
00162 if (driver.getNumberDevices () == 0)
00163 {
00164 NODELET_INFO ("[%s] No devices connected.... waiting for devices to be connected", getName ().c_str ());
00165 sleep(1);
00166 continue;
00167 }
00168
00169 NODELET_INFO ("[%s] Number devices connected: %d", getName ().c_str (), driver.getNumberDevices ());
00170 for (unsigned deviceIdx = 0; deviceIdx < driver.getNumberDevices (); ++deviceIdx)
00171 {
00172 NODELET_INFO ("[%s] %u. device on bus %03u:%02u is a %s (%03x) from %s (%03x) with serial id \'%s\'"
00173 , getName ().c_str (), deviceIdx + 1, driver.getBus (deviceIdx), driver.getAddress (deviceIdx)
00174 , driver.getProductName (deviceIdx), driver.getProductID (deviceIdx), driver.getVendorName (deviceIdx)
00175 , driver.getVendorID (deviceIdx), driver.getSerialNumber (deviceIdx));
00176 }
00177
00178 string device_id;
00179 param_nh.param ("device_id", device_id, std::string ());
00180
00181 try {
00182 if (device_id.empty ())
00183 {
00184 NODELET_WARN ("[%s] device_id is not set! Using first device.", getName ().c_str ());
00185 device_ = driver.getDeviceByIndex (0);
00186 }
00187 else if (device_id.find ('@') != string::npos)
00188 {
00189 size_t pos = device_id.find ('@');
00190 unsigned bus = atoi (device_id.substr (0, pos).c_str ());
00191 unsigned address = atoi (device_id.substr (pos + 1, device_id.length () - pos - 1).c_str ());
00192 NODELET_INFO ("[%s] searching for device with bus@address = %d@%d", getName ().c_str (), bus, address);
00193 device_ = driver.getDeviceByAddress (bus, address);
00194 }
00195 else if (device_id[0] == '#')
00196 {
00197 unsigned index = atoi (device_id.c_str () + 1);
00198 NODELET_INFO ("[%s] searching for device with index = %d", getName ().c_str (), index);
00199 device_ = driver.getDeviceByIndex (index - 1);
00200 }
00201 else
00202 {
00203 NODELET_INFO ("[%s] searching for device with serial number = %s", getName ().c_str (), device_id.c_str ());
00204 device_ = driver.getDeviceBySerialNumber (device_id);
00205 }
00206 }
00207 catch (const OpenNIException& exception)
00208 {
00209 if (!device_)
00210 {
00211 NODELET_INFO ("[%s] No matching device found.... waiting for devices. Reason: %s", getName ().c_str (), exception.what ());
00212 sleep (1);
00213 continue;
00214 }
00215 else
00216 {
00217 NODELET_ERROR ("[%s] could not retrieve device. Reason %s", getName ().c_str (), exception.what ());
00218 exit (-1);
00219 }
00220 }
00221 } while (!device_);
00222
00223 NODELET_INFO ("[%s] Opened '%s' on bus %d:%d with serial number '%s'", getName ().c_str (),
00224 device_->getProductName (), device_->getBus (), device_->getAddress (), device_->getSerialNumber ());
00225
00226 device_->registerImageCallback (&OpenNINodelet::imageCallback, *this);
00227 device_->registerDepthCallback (&OpenNINodelet::depthCallback, *this);
00228
00229 bool registration = false;
00230 param_nh.param ("depth_registration", registration, false );
00231 config_.depth_registration = registration;
00232
00233 int debayering_method = 0;
00234 param_nh.param ("debayering", debayering_method, 0 );
00235 if(debayering_method > 2 || debayering_method < 0)
00236 {
00237 NODELET_ERROR ("Unknown debayering method %d. Only Folowing values are available: Bilinear (0), EdgeAware (1), EdgeAwareWeighted (2). Falling back to Bilinear (0).", debayering_method);
00238 debayering_method = 0;
00239 }
00240 config_.debayering = debayering_method;
00241
00242 param_nh.param ("depth_time_offset", config_.depth_time_offset, 0.0 );
00243 if(config_.depth_time_offset > 1.0 || config_.depth_time_offset < -1.0)
00244 {
00245 NODELET_ERROR ("depth time offset is % 2.5f seconds. Thats unlikely... setting back to 0.0 seconds", config_.depth_time_offset);
00246 config_.depth_time_offset = 0.0;
00247 }
00248
00249 param_nh.param ("image_time_offset", config_.image_time_offset, 0.0 );
00250 if(config_.image_time_offset > 1.0 || config_.image_time_offset < -1.0)
00251 {
00252 NODELET_ERROR ("image time offset is % 2.5f seconds. Thats unlikely... setting back to 0.0 seconds", config_.image_time_offset);
00253 config_.image_time_offset = 0.0;
00254 }
00255
00256 int image_mode = mapXnMode2ConfigMode (device_->getDefaultImageMode ());
00257 param_nh.param ("image_mode", image_mode, image_mode );
00258 if (image_mode < config_.__getMin__().image_mode || image_mode > config_.__getMax__ ().image_mode ||
00259 !isImageModeSupported (image_mode))
00260 {
00261 XnMapOutputMode image_md = device_->getDefaultImageMode ();
00262 NODELET_ERROR ("Unknown or unsopported image mode %d. Falling back to default mode %dx%d@%d.", image_mode, image_md.nXRes, image_md.nYRes, image_md.nFPS);
00263 image_mode = mapXnMode2ConfigMode (image_md);
00264 }
00265 config_.image_mode = image_mode;
00266
00267 int depth_mode = mapXnMode2ConfigMode (device_->getDefaultDepthMode ());
00268 param_nh.param ("depth_mode", depth_mode, depth_mode );
00269 if (depth_mode < OpenNI_VGA_30Hz || depth_mode > config_.__getMax__ ().image_mode ||
00270 !isDepthModeSupported (depth_mode))
00271 {
00272 XnMapOutputMode depth_md = device_->getDefaultImageMode ();
00273 NODELET_ERROR ("Unknown or unsopported depth mode %d. Falling back to default mode %dx%d@%d.", depth_mode, depth_md.nXRes, depth_md.nYRes, depth_md.nFPS);
00274 depth_mode = mapXnMode2ConfigMode (depth_md);
00275 }
00276 config_.depth_mode = depth_mode;
00277
00278 XnMapOutputMode image_md = mapConfigMode2XnMode ( config_.image_mode);
00279 image_width_ = image_md.nXRes;
00280 image_height_ = image_md.nYRes;
00281
00282 XnMapOutputMode depth_md = mapConfigMode2XnMode ( config_.depth_mode);
00283 depth_width_ = depth_md.nXRes;
00284 depth_height_ = depth_md.nYRes;
00285 }
00286
00287 void OpenNINodelet::imageCallback (boost::shared_ptr<openni_wrapper::Image> image, void* cookie)
00288 {
00289 ros::Time time = ros::Time::now () + ros::Duration(config_.image_time_offset);
00290
00291
00292
00293
00294
00295 if (pub_rgb_info_.getNumSubscribers () > 0)
00296 pub_rgb_info_.publish (fillCameraInfo (time, true));
00297
00298 if (pub_image_raw_.getNumSubscribers () > 0)
00299 publishRgbImageRaw (*image, time);
00300
00301 if (pub_rgb_image_.getNumSubscribers () > 0 || pub_point_cloud_rgb_.getNumSubscribers () > 0 )
00302 publishRgbImage (*image, time);
00303
00304 if (pub_gray_image_.getNumSubscribers () > 0)
00305 publishGrayImage (*image, time);
00306
00307
00308 }
00309
00310 void OpenNINodelet::depthCallback (boost::shared_ptr<openni_wrapper::DepthImage> depth_image, void* cookie)
00311 {
00312 ros::Time time = ros::Time::now () + ros::Duration(config_.depth_time_offset);
00313
00314
00315
00316
00317 if (pub_depth_info_.getNumSubscribers () > 0)
00318 pub_depth_info_.publish (fillCameraInfo (time, false));
00319
00320
00321 if (pub_depth_raw_.getNumSubscribers () > 0)
00322 publishDepthImageRaw (*depth_image, time);
00323
00324
00325 if (pub_depth_image_.getNumSubscribers () > 0 || (pub_point_cloud_rgb_.getNumSubscribers () > 0 ))
00326 publishDepthImage (*depth_image, time);
00327
00328
00329 if (pub_disparity_.getNumSubscribers () > 0)
00330 publishDisparity (*depth_image, time);
00331
00332
00333 if (pub_point_cloud_.getNumSubscribers () > 0 )
00334 publishXYZPointCloud (*depth_image, time);
00335
00336
00337 }
00338
00339 void OpenNINodelet::subscriberChangedEvent ()
00340 {
00341
00342 if (isImageStreamRequired () && !device_->isImageStreamRunning ())
00343 {
00344 device_->startImageStream ();
00345 startSynchronization ();
00346 }
00347 else if (!isImageStreamRequired () && device_->isImageStreamRunning ())
00348 {
00349 stopSynchronization ();
00350 device_->stopImageStream ();
00351 if (pub_rgb_info_.getNumSubscribers() > 0)
00352 NODELET_WARN("Camera Info for rgb stream has subscribers, but stream has stopped.");
00353 }
00354
00355 if (isDepthStreamRequired () && !device_->isDepthStreamRunning ())
00356 {
00357 device_->startDepthStream ();
00358 startSynchronization ();
00359 }
00360 else if ( !isDepthStreamRequired () && device_->isDepthStreamRunning ())
00361 {
00362 stopSynchronization ();
00363 device_->stopDepthStream ();
00364 if (pub_depth_info_.getNumSubscribers() > 0)
00365 NODELET_WARN("Camera Info for depth stream has subscribers, but stream has stopped.");
00366 }
00367
00368
00369
00370 if (pub_point_cloud_rgb_.getNumSubscribers() > 0)
00371 {
00372 Config config = config_;
00373
00374 reconfigure_mutex_.lock ();
00375 if (!device_->isDepthRegistered ())
00376 {
00377 NODELET_WARN ("turning on depth registration, since PointCloudXYZRGB has subscribers.");
00378 device_->setDepthRegistration (true);
00379 config.depth_registration = true;
00380 }
00381
00382 XnMapOutputMode depth_mode = mapConfigMode2XnMode (config_.depth_mode);
00383 XnMapOutputMode image_mode = mapConfigMode2XnMode (config_.image_mode);
00384 if (depth_mode.nXRes > image_mode.nXRes || depth_mode.nYRes > image_mode.nYRes)
00385 {
00386 NODELET_WARN ("PointCloudXYZRGB need at least the same image size for mapping rgb values to the points");
00387 config.image_mode = config_.depth_mode;
00388 }
00389
00390 reconfigure_server_->updateConfig (config);
00391 configCallback (config, 0);
00392 reconfigure_mutex_.unlock ();
00393 }
00394 }
00395
00396 void OpenNINodelet::publishRgbImage (const Image& image, ros::Time time) const
00397 {
00398 sensor_msgs::ImagePtr rgb_msg = boost::make_shared<sensor_msgs::Image > ();
00399 rgb_msg->header.stamp = time;
00400 rgb_msg->header.frame_id = rgb_frame_id_;
00401 rgb_msg->encoding = sensor_msgs::image_encodings::RGB8;
00402 rgb_msg->height = image_height_;
00403 rgb_msg->width = image_width_;
00404 rgb_msg->step = image_width_ * 3;
00405 rgb_msg->data.resize (rgb_msg->height * rgb_msg->step);
00406 image.fillRGB (rgb_msg->width, rgb_msg->height, &rgb_msg->data[0], rgb_msg->step);
00407
00408 if (pub_rgb_image_.getNumSubscribers () > 0)
00409 pub_rgb_image_.publish (rgb_msg);
00410
00411 if (pub_point_cloud_rgb_.getNumSubscribers () > 0)
00412 depth_rgb_sync_->add < 1 > (rgb_msg);
00413 }
00414
00415 void OpenNINodelet::publishRgbImageRaw (const Image& image, ros::Time time) const
00416 {
00417 sensor_msgs::ImagePtr rgb_msg = boost::make_shared<sensor_msgs::Image > ();
00418 rgb_msg->header.stamp = time;
00419 rgb_msg->header.frame_id = rgb_frame_id_;
00420 if (image.getEncoding () == openni_wrapper::Image::BAYER_GRBG)
00421 {
00422 rgb_msg->encoding = sensor_msgs::image_encodings::BAYER_GRBG8;
00423 rgb_msg->step = image_width_ * 1;
00424 }
00425 else if (image.getEncoding () == openni_wrapper::Image::YUV422)
00426 {
00427 rgb_msg->encoding = sensor_msgs::image_encodings::YUV422;
00428 rgb_msg->step = image_width_ * 2;
00429 }
00430 rgb_msg->height = image_height_;
00431 rgb_msg->width = image_width_;
00432 rgb_msg->data.resize (rgb_msg->height * rgb_msg->step);
00433 image.fillRaw (&rgb_msg->data[0]);
00434
00435 if (pub_image_raw_.getNumSubscribers () > 0)
00436 pub_image_raw_.publish (rgb_msg);
00437 }
00438
00439 void OpenNINodelet::publishGrayImage (const Image& image, ros::Time time) const
00440 {
00441 sensor_msgs::ImagePtr gray_msg = boost::make_shared<sensor_msgs::Image > ();
00442 gray_msg->header.stamp = time;
00443 gray_msg->header.frame_id = rgb_frame_id_;
00444 gray_msg->encoding = sensor_msgs::image_encodings::MONO8;
00445 gray_msg->height = image_height_;
00446 gray_msg->width = image_width_;
00447 gray_msg->step = image_width_;
00448 gray_msg->data.resize (gray_msg->height * gray_msg->step);
00449 image.fillGrayscale (gray_msg->width, gray_msg->height, &gray_msg->data[0], gray_msg->step);
00450
00451 pub_gray_image_.publish (gray_msg);
00452 }
00453
00454 void OpenNINodelet::publishDepthImage (const DepthImage& depth, ros::Time time) const
00455 {
00456 sensor_msgs::ImagePtr depth_msg = boost::make_shared<sensor_msgs::Image > ();
00457 depth_msg->header.stamp = time;
00458 depth_msg->header.frame_id = device_->isDepthRegistered () ? rgb_frame_id_ : depth_frame_id_;
00459 depth_msg->encoding = sensor_msgs::image_encodings::TYPE_32FC1;
00460 depth_msg->height = depth_height_;
00461 depth_msg->width = depth_width_;
00462 depth_msg->step = depth_msg->width * sizeof (float);
00463 depth_msg->data.resize (depth_msg->height * depth_msg->step);
00464
00465 depth.fillDepthImage (depth_width_, depth_height_, reinterpret_cast<float*>(&depth_msg->data[0]), depth_msg->step);
00466
00467 if (pub_depth_image_.getNumSubscribers () > 0)
00468 pub_depth_image_.publish (depth_msg);
00469
00470 if (pub_point_cloud_rgb_.getNumSubscribers () > 0)
00471 depth_rgb_sync_->add < 0 > (depth_msg);
00472 }
00473
00474 void OpenNINodelet::publishDepthImageRaw (const DepthImage& depth, ros::Time time) const
00475 {
00476 sensor_msgs::ImagePtr depth_msg = boost::make_shared<sensor_msgs::Image > ();
00477 depth_msg->header.stamp = time;
00478 depth_msg->header.frame_id = device_->isDepthRegistered () ? rgb_frame_id_ : depth_frame_id_;
00479 depth_msg->encoding = sensor_msgs::image_encodings::TYPE_16UC1;
00480 depth_msg->height = depth_height_;
00481 depth_msg->width = depth_width_;
00482 depth_msg->step = depth_msg->width * sizeof (short);
00483 depth_msg->data.resize (depth_msg->height * depth_msg->step);
00484
00485 depth.fillDepthImageRaw (depth_width_, depth_height_,
00486 reinterpret_cast<unsigned short*>(&depth_msg->data[0]), depth_msg->step);
00487
00488 if (pub_depth_raw_.getNumSubscribers () > 0)
00489 pub_depth_raw_.publish (depth_msg);
00490 }
00491
00492 void OpenNINodelet::publishDisparity (const DepthImage& depth, ros::Time time) const
00493 {
00494 stereo_msgs::DisparityImagePtr disp_msg = boost::make_shared<stereo_msgs::DisparityImage > ();
00495 disp_msg->header.stamp = time;
00496 disp_msg->header.frame_id = device_->isDepthRegistered () ? rgb_frame_id_ : depth_frame_id_;
00497 disp_msg->image.header = disp_msg->header;
00498 disp_msg->image.encoding = sensor_msgs::image_encodings::TYPE_32FC1;
00499 disp_msg->image.height = depth_height_;
00500 disp_msg->image.width = depth_width_;
00501 disp_msg->image.step = disp_msg->image.width * sizeof (float);
00502 disp_msg->image.data.resize (disp_msg->image.height * disp_msg->image.step);
00503 disp_msg->T = depth.getBaseline ();
00504 disp_msg->f = depth.getFocalLength () * depth_width_ / depth.getWidth ();
00505
00507 disp_msg->min_disparity = 0.0;
00508 disp_msg->max_disparity = disp_msg->T * disp_msg->f / 0.3;
00509 disp_msg->delta_d = 0.125;
00510
00511 depth.fillDisparityImage (depth_width_, depth_height_, reinterpret_cast<float*>(&disp_msg->image.data[0]), disp_msg->image.step);
00512
00513 pub_disparity_.publish (disp_msg);
00514 }
00515
00516 void OpenNINodelet::publishXYZPointCloud (const DepthImage& depth, ros::Time time) const
00517 {
00518 const xn::DepthMetaData& depth_md = depth.getDepthMetaData ();
00519
00520 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_msg (new pcl::PointCloud<pcl::PointXYZ>() );
00521 cloud_msg->header.stamp = time;
00522
00523 bool do_masking = (use_indices_ and (mask_indices_.size() != 0));
00524
00525 float centerX, centerY;
00526 unsigned depthStep = 1, depthSkip = 0;
00527 if (not do_masking)
00528 {
00529 cloud_msg->height = depth_height_;
00530 cloud_msg->width = depth_width_;
00531 centerX = (cloud_msg->width >> 1) - 0.5f;
00532 centerY = (cloud_msg->height >> 1) - 0.5f;
00533 depthStep = depth_md.XRes() / cloud_msg->width;
00534 depthSkip = (depth_md.YRes() / cloud_msg->height - 1) * depth_md.XRes();
00535 }
00536 else
00537 {
00538 cloud_msg->width = mask_indices_.size();
00539 cloud_msg->height = 1;
00540 centerX = (depth_width_ >> 1) - 0.5f;
00541 centerY = (depth_height_ >> 1) - 0.5f;
00542 }
00543 cloud_msg->is_dense = false;
00544
00545 cloud_msg->points.resize (cloud_msg->height * cloud_msg->width);
00546
00547 float constant = 0.001 / device_->getDepthFocalLength (depth_width_);
00548
00549 if (device_->isDepthRegistered ())
00550 cloud_msg->header.frame_id = rgb_frame_id_;
00551 else
00552 cloud_msg->header.frame_id = depth_frame_id_;
00553
00554 float bad_point = std::numeric_limits<float>::quiet_NaN ();
00555
00556 int depth_idx = 0;
00557 pcl::PointCloud<pcl::PointXYZ>::iterator pt_iter = cloud_msg->begin ();
00558 if (not do_masking)
00559 {
00560 for (int v = 0; v < (int)cloud_msg->height; ++v, depth_idx += depthSkip)
00561 {
00562 for (int u = 0; u < (int)cloud_msg->width; ++u, depth_idx += depthStep, ++pt_iter)
00563 {
00564 pcl::PointXYZ& pt = *pt_iter;
00565
00566
00567 if (depth_md[depth_idx] == 0 ||
00568 depth_md[depth_idx] == depth.getNoSampleValue () ||
00569 depth_md[depth_idx] == depth.getShadowValue ())
00570 {
00571
00572 pt.x = pt.y = pt.z = bad_point;
00573 continue;
00574 }
00575
00576
00577 pt.x = (u - centerX) * depth_md[depth_idx] * constant;
00578 pt.y = (v - centerY) * depth_md[depth_idx] * constant;
00579 pt.z = depth_md[depth_idx] * 0.001;
00580 }
00581 }
00582 }
00583 else
00584 {
00585 int u, v;
00586 unsigned int nMask = mask_indices_.size();
00587 int max_index = (depth_height_ * depth_width_) - 1;
00588 for (unsigned int i = 0; i < nMask; i++, ++pt_iter)
00589 {
00590 pcl::PointXYZ& pt = *pt_iter;
00591
00592 depth_idx = mask_indices_[i];
00593 if (depth_idx > max_index)
00594 {
00595 NODELET_ERROR("Mask index %d exceeds maximum index of %d", depth_idx, max_index);
00596 continue;
00597 }
00598 v = depth_idx / depth_width_;
00599 u = depth_idx % depth_width_;
00600
00601
00602 if (depth_md[depth_idx] == 0 ||
00603 depth_md[depth_idx] == depth.getNoSampleValue () ||
00604 depth_md[depth_idx] == depth.getShadowValue ())
00605 {
00606
00607 pt.x = pt.y = pt.z = bad_point;
00608 continue;
00609 }
00610
00611
00612 pt.x = (u - centerX) * depth_md[depth_idx] * constant;
00613 pt.y = (v - centerY) * depth_md[depth_idx] * constant;
00614 pt.z = depth_md[depth_idx] * 0.001;
00615 }
00616 }
00617
00618 pub_point_cloud_.publish (cloud_msg);
00619 }
00620
00621 void OpenNINodelet::publishXYZRGBPointCloud (const sensor_msgs::ImageConstPtr& depth_msg, const sensor_msgs::ImageConstPtr& rgb_msg) const
00622 {
00623 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_msg (new pcl::PointCloud<pcl::PointXYZRGB>() );
00624 cloud_msg->header.stamp = depth_msg->header.stamp;
00625 cloud_msg->header.frame_id = rgb_frame_id_;
00626 cloud_msg->is_dense = false;
00627
00628 bool do_masking = (use_indices_ and (mask_indices_.size() != 0));
00629
00630 float centerX, centerY;
00631 float constant;
00632 unsigned color_step, color_skip;
00633
00634 if (not do_masking)
00635 {
00636 cloud_msg->height = depth_msg->height;
00637 cloud_msg->width = depth_msg->width;
00638 centerX = (cloud_msg->width >> 1) - 0.5f;
00639 centerY = (cloud_msg->height >> 1) - 0.5f;
00640 constant = 1.0f / device_->getImageFocalLength(cloud_msg->width);
00641 color_step = 3 * rgb_msg->width / cloud_msg->width;
00642 color_skip = 3 * (rgb_msg->height / cloud_msg->height - 1) * rgb_msg->width;
00643
00644 }
00645 else
00646 {
00647 cloud_msg->width = mask_indices_.size();
00648 cloud_msg->height = 1;
00649 centerX = (depth_msg->width >> 1) - 0.5f;
00650 centerY = (depth_msg->height >> 1) - 0.5f;
00651 constant = 1.0f / device_->getImageFocalLength(depth_msg->width);
00652 color_step = 3 * rgb_msg->width / depth_msg->width;
00653 color_skip = 3 * (rgb_msg->height / depth_msg->height - 1) * rgb_msg->width;
00654 }
00655
00656 static bool info_sent = false;
00657 if (not info_sent)
00658 {
00659
00660 info_sent = true;
00661 }
00662
00663
00664 if (rgb_msg->height < depth_msg->height || rgb_msg->width < depth_msg->width)
00665 {
00666
00667 static unsigned warned = 0;
00668 if (warned % 100 == 0)
00669 NODELET_WARN("rgb image smaller than depth image... skipping point cloud for this frame rgb:%dx%d vs. depth:%3dx%d"
00670 , rgb_msg->width, rgb_msg->height, depth_msg->width, depth_msg->height );
00671 ++warned;
00672 return;
00673 }
00674 cloud_msg->points.resize (cloud_msg->height * cloud_msg->width);
00675
00676 const float* depth_buffer = reinterpret_cast<const float*>(&depth_msg->data[0]);
00677 const uint8_t* rgb_buffer = &rgb_msg->data[0];
00678
00679
00680 int color_idx = 0, depth_idx = 0;
00681 pcl::PointCloud<pcl::PointXYZRGB>::iterator pt_iter = cloud_msg->begin ();
00682 if (not do_masking)
00683 {
00684 for (int v = 0; v < (int)cloud_msg->height; ++v, color_idx += color_skip)
00685 {
00686 for (int u = 0; u < (int)cloud_msg->width; ++u, color_idx += color_step, ++depth_idx, ++pt_iter)
00687 {
00688 pcl::PointXYZRGB& pt = *pt_iter;
00689 float Z = depth_buffer[depth_idx];
00690
00691
00692 if (std::isnan (Z))
00693 {
00694 pt.x = pt.y = pt.z = Z;
00695 }
00696 else
00697 {
00698
00699 pt.x = (u - centerX) * Z * constant;
00700 pt.y = (v - centerY) * Z * constant;
00701 pt.z = Z;
00702 }
00703
00704
00705 RGBValue color;
00706 color.Red = rgb_buffer[color_idx];
00707 color.Green = rgb_buffer[color_idx + 1];
00708 color.Blue = rgb_buffer[color_idx + 2];
00709 color.Alpha = 0;
00710 pt.rgb = color.float_value;
00711 }
00712 }
00713 }
00714 else
00715 {
00716 int u, v;
00717 unsigned int nMask = mask_indices_.size();
00718 int max_index = (depth_msg->height * depth_msg->width) - 1;
00719 for (unsigned int i = 0; i < nMask; i++, ++pt_iter)
00720 {
00721 pcl::PointXYZRGB& pt = *pt_iter;
00722 depth_idx = mask_indices_[i];
00723 if (depth_idx > max_index)
00724 {
00725 NODELET_ERROR("Mask index %d exceeds maximum index of %d", depth_idx, max_index);
00726 continue;
00727 }
00728 float Z = depth_buffer[depth_idx];
00729
00730 v = depth_idx / depth_msg->width;
00731 u = depth_idx % depth_msg->width;
00732
00733
00734 if (std::isnan (Z))
00735 {
00736 pt.x = pt.y = pt.z = Z;
00737 }
00738 else
00739 {
00740
00741 pt.x = (u - centerX) * Z * constant;
00742 pt.y = (v - centerY) * Z * constant;
00743 pt.z = Z;
00744 }
00745
00746
00747 RGBValue color;
00748 color_idx = (v*depth_msg->width + u)*color_step;
00749 color.Red = rgb_buffer[color_idx];
00750 color.Green = rgb_buffer[color_idx + 1];
00751 color.Blue = rgb_buffer[color_idx + 2];
00752 color.Alpha = 0;
00753 pt.rgb = color.float_value;
00754 }
00755 }
00756
00757 pub_point_cloud_rgb_.publish (cloud_msg);
00758 }
00759
00760 sensor_msgs::CameraInfoPtr OpenNINodelet::fillCameraInfo (ros::Time time, bool is_rgb)
00761 {
00762 sensor_msgs::CameraInfoPtr info_msg = boost::make_shared<sensor_msgs::CameraInfo > ();
00763 info_msg->header.stamp = time;
00764 info_msg->header.frame_id = is_rgb ? rgb_frame_id_ : depth_frame_id_;
00765 info_msg->width = is_rgb ? image_width_ : depth_width_;
00766 info_msg->height = is_rgb ? image_height_ : depth_height_;
00767
00768 #if ROS_VERSION_MINIMUM(1, 3, 0)
00769 info_msg->D = std::vector<double>(5, 0.0);
00770 info_msg->distortion_model = sensor_msgs::distortion_models::PLUMB_BOB;
00771 #else
00772 info_msg->D.assign (0.0);
00773 #endif
00774 info_msg->K.assign (0.0);
00775 info_msg->R.assign (0.0);
00776 info_msg->P.assign (0.0);
00777
00778 double f = is_rgb ? device_->getImageFocalLength (image_width_) : device_->getDepthFocalLength (depth_width_);
00779 info_msg->K[0] = info_msg->K[4] = f;
00780 info_msg->K[2] = (info_msg->width / 2) - 0.5;
00781 info_msg->K[5] = (info_msg->width * 3./8.) - 0.5;
00782 info_msg->K[8] = 1.0;
00783
00784 info_msg->R[0] = info_msg->R[4] = info_msg->R[8] = 1.0;
00785
00786 info_msg->P[0] = info_msg->P[5] = info_msg->K[0];
00787 info_msg->P[2] = info_msg->K[2];
00788 info_msg->P[6] = info_msg->K[5];
00789 info_msg->P[10] = 1.0;
00790
00791 return info_msg;
00792 }
00793
00794 void OpenNINodelet::configCallback (Config &config, uint32_t level)
00795 {
00796 XnMapOutputMode old_image_mode = device_->getImageOutputMode ();
00797 XnMapOutputMode old_depth_mode = device_->getDepthOutputMode ();
00798
00799
00800 XnMapOutputMode image_mode, compatible_image_mode;
00801 image_mode = mapConfigMode2XnMode (config.image_mode);
00802
00803 if (!device_->findCompatibleImageMode (image_mode, compatible_image_mode))
00804 {
00805 NODELET_WARN ("Could not find any compatible image output mode for %d x %d @ %d.",
00806 image_mode.nXRes, image_mode.nYRes, image_mode.nFPS);
00807
00808
00809 config = config_;
00810 return;
00811 }
00812
00813 XnMapOutputMode depth_mode, compatible_depth_mode;
00814 depth_mode = mapConfigMode2XnMode (config.depth_mode);
00815 if (!device_->findCompatibleDepthMode (depth_mode, compatible_depth_mode))
00816 {
00817 NODELET_WARN ("Could not find any compatible depth output mode for %d x %d @ %d.",
00818 depth_mode.nXRes, depth_mode.nYRes, depth_mode.nFPS);
00819
00820
00821 config = config_;
00822 return;
00823 }
00824
00825 DeviceKinect* kinect = dynamic_cast<DeviceKinect*> (device_.get ());
00826 if (kinect)
00827 {
00828 switch (config.debayering)
00829 {
00830 case OpenNI_Bilinear:
00831 kinect->setDebayeringMethod (ImageBayerGRBG::Bilinear);
00832 break;
00833 case OpenNI_EdgeAware:
00834 kinect->setDebayeringMethod (ImageBayerGRBG::EdgeAware);
00835 break;
00836 case OpenNI_EdgeAwareWeighted:
00837 kinect->setDebayeringMethod (ImageBayerGRBG::EdgeAwareWeighted);
00838 break;
00839 default:
00840 NODELET_ERROR ("unknwon debayering method");
00841 config.debayering = config_.debayering;
00842 break;
00843 }
00844 }
00845 else if (config.debayering != config_.debayering)
00846 {
00847 NODELET_WARN ("%s does not output bayer images. Selection has no affect.", device_->getProductName () );
00848 }
00849
00850 if (pub_point_cloud_rgb_.getNumSubscribers () > 0)
00851 {
00852 if ( (depth_mode.nXRes > image_mode.nXRes) || (depth_mode.nYRes > image_mode.nYRes) ||
00853 (image_mode.nXRes % depth_mode.nXRes != 0) )
00854 {
00855
00856 NODELET_WARN ("depth mode not compatible to image mode, since PointCloudXYZRGB has subscribers.");
00857 config = config_;
00858 return;
00859 }
00860 if (!config.depth_registration && config_.depth_registration)
00861 {
00862 NODELET_WARN ("can not turn of registration, since PointCloudXYZRGB has subscribers.");
00863 config = config_;
00864 return;
00865 }
00866 }
00867
00868
00869 if (compatible_image_mode != old_image_mode || compatible_depth_mode != old_depth_mode)
00870 {
00871 stopSynchronization ();
00872
00873 if (compatible_image_mode != old_image_mode)
00874 {
00875
00876 device_->setImageOutputMode (compatible_image_mode);
00877 image_width_ = image_mode.nXRes;
00878 image_height_ = image_mode.nYRes;
00879
00880 }
00881
00882 if (compatible_depth_mode != old_depth_mode)
00883 {
00884
00885 device_->setDepthOutputMode (compatible_depth_mode);
00886 depth_width_ = depth_mode.nXRes;
00887 depth_height_ = depth_mode.nYRes;
00888
00889 }
00890 startSynchronization ();
00891 }
00892 else
00893 {
00894 if (config_.image_mode != config.image_mode)
00895 {
00896 image_width_ = image_mode.nXRes;
00897 image_height_ = image_mode.nYRes;
00898 }
00899
00900 if (config_.depth_mode != config.depth_mode)
00901 {
00902 depth_width_ = depth_mode.nXRes;
00903 depth_height_ = depth_mode.nYRes;
00904 }
00905 }
00906
00907 if (device_->isDepthRegistered () != config.depth_registration)
00908 {
00909 device_->setDepthRegistration (config.depth_registration);
00910 }
00911
00912
00913 config_ = config;
00914 }
00915
00916 bool OpenNINodelet::isImageModeSupported (int image_mode) const
00917 {
00918 XnMapOutputMode image_md = mapConfigMode2XnMode (image_mode);
00919 XnMapOutputMode compatible_mode;
00920 if (device_->findCompatibleImageMode (image_md, compatible_mode))
00921 return true;
00922 return false;
00923 }
00924
00925 bool OpenNINodelet::isDepthModeSupported (int depth_mode) const
00926 {
00927 XnMapOutputMode depth_md = mapConfigMode2XnMode (depth_mode);
00928 XnMapOutputMode compatible_mode;
00929 if (device_->findCompatibleDepthMode (depth_md, compatible_mode))
00930 return true;
00931 return false;
00932 }
00933
00934 void OpenNINodelet::startSynchronization ()
00935 {
00936 if (device_->isSynchronizationSupported () && !device_->isSynchronized () &&
00937 device_->getImageOutputMode ().nFPS == device_->getDepthOutputMode ().nFPS &&
00938 device_->isImageStreamRunning () && device_->isDepthStreamRunning () )
00939 device_->setSynchronization (true);
00940 }
00941
00942 void OpenNINodelet::stopSynchronization ()
00943 {
00944 if (device_->isSynchronizationSupported () && device_->isSynchronized ())
00945 device_->setSynchronization (false);
00946 }
00947
00948 void OpenNINodelet::updateModeMaps ()
00949 {
00950 XnMapOutputMode output_mode;
00951
00952 output_mode.nXRes = XN_SXGA_X_RES;
00953 output_mode.nYRes = XN_SXGA_Y_RES;
00954 output_mode.nFPS = 15;
00955 xn2config_map_[output_mode] = OpenNI_SXGA_15Hz;
00956 config2xn_map_[OpenNI_SXGA_15Hz] = output_mode;
00957
00958 output_mode.nXRes = XN_VGA_X_RES;
00959 output_mode.nYRes = XN_VGA_Y_RES;
00960 output_mode.nFPS = 25;
00961 xn2config_map_[output_mode] = OpenNI_VGA_25Hz;
00962 config2xn_map_[OpenNI_VGA_25Hz] = output_mode;
00963 output_mode.nFPS = 30;
00964 xn2config_map_[output_mode] = OpenNI_VGA_30Hz;
00965 config2xn_map_[OpenNI_VGA_30Hz] = output_mode;
00966
00967 output_mode.nXRes = XN_QVGA_X_RES;
00968 output_mode.nYRes = XN_QVGA_Y_RES;
00969 output_mode.nFPS = 25;
00970 xn2config_map_[output_mode] = OpenNI_QVGA_25Hz;
00971 config2xn_map_[OpenNI_QVGA_25Hz] = output_mode;
00972 output_mode.nFPS = 30;
00973 xn2config_map_[output_mode] = OpenNI_QVGA_30Hz;
00974 config2xn_map_[OpenNI_QVGA_30Hz] = output_mode;
00975 output_mode.nFPS = 60;
00976 xn2config_map_[output_mode] = OpenNI_QVGA_60Hz;
00977 config2xn_map_[OpenNI_QVGA_60Hz] = output_mode;
00978
00979 output_mode.nXRes = XN_QQVGA_X_RES;
00980 output_mode.nYRes = XN_QQVGA_Y_RES;
00981 output_mode.nFPS = 25;
00982 xn2config_map_[output_mode] = OpenNI_QQVGA_25Hz;
00983 config2xn_map_[OpenNI_QQVGA_25Hz] = output_mode;
00984 output_mode.nFPS = 30;
00985 xn2config_map_[output_mode] = OpenNI_QQVGA_30Hz;
00986 config2xn_map_[OpenNI_QQVGA_30Hz] = output_mode;
00987 output_mode.nFPS = 60;
00988 xn2config_map_[output_mode] = OpenNI_QQVGA_60Hz;
00989 config2xn_map_[OpenNI_QQVGA_60Hz] = output_mode;
00990 }
00991
00992 int OpenNINodelet::mapXnMode2ConfigMode (const XnMapOutputMode& output_mode) const
00993 {
00994 std::map<XnMapOutputMode, int, modeComp>::const_iterator it = xn2config_map_.find (output_mode);
00995
00996 if (it == xn2config_map_.end ())
00997 {
00998 NODELET_ERROR ("mode %dx%d@%d could not be found", output_mode.nXRes, output_mode.nYRes, output_mode.nFPS);
00999 exit (-1);
01000 }
01001 else
01002 return it->second;
01003 }
01004
01005 XnMapOutputMode OpenNINodelet::mapConfigMode2XnMode (int mode) const
01006 {
01007 std::map<int, XnMapOutputMode>::const_iterator it = config2xn_map_.find (mode);
01008 if (it == config2xn_map_.end ())
01009 {
01010 NODELET_ERROR ("mode %d could not be found", mode);
01011 exit (-1);
01012 }
01013 else
01014 return it->second;
01015 }
01016
01017 void OpenNINodelet::maskIndicesCb(const pcl::PointIndicesConstPtr& indices)
01018 {
01019 mask_indices_.resize(indices->indices.size());
01020
01021 memcpy(mask_indices_.data(), indices->indices.data(), indices->indices.size() * sizeof(int32_t));
01022 }
01023 }
01024
01025