depthcloud_encoder.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  *
00003  *  Copyright (c) 2014, Willow Garage, Inc.
00004  *  All rights reserved.
00005  *
00006  *  Redistribution and use in source and binary forms, with or without
00007  *  modification, are permitted provided that the following conditions
00008  *  are met:
00009  *
00010  *   * Redistributions of source code must retain the above copyright
00011  *     notice, this list of conditions and the following disclaimer.
00012  *   * Redistributions in binary form must reproduce the above
00013  *     copyright notice, this list of conditions and the following
00014  *     disclaimer in the documentation and/or other materials provided
00015  *     with the distribution.
00016  *   * Neither the name of the Willow Garage nor the names of its
00017  *     contributors may be used to endorse or promote products derived
00018  *     from this software without specific prior written permission.
00019  *
00020  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00028  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00030  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031  *  POSSIBILITY OF SUCH DAMAGE.
00032 
00033  *  Author: Julius Kammerl (jkammerl@willowgarage.com)
00034  *
00035  */
00036 
00037 #include "depthcloud_encoder/depthcloud_encoder.h"
00038 
00039 #include <boost/bind.hpp>
00040 #include <boost/cstdint.hpp>
00041 
00042 #include <cv_bridge/cv_bridge.h>
00043 
00044 namespace depthcloud
00045 {
00046 
00047 using namespace message_filters::sync_policies;
00048 namespace enc = sensor_msgs::image_encodings;
00049 
00050 static int queue_size_ = 10;
00051 static int target_resolution_ = 512;
00052 static int max_depth_per_tile = 2.0;
00053 
00054 DepthCloudEncoder::DepthCloudEncoder(ros::NodeHandle& nh, ros::NodeHandle& pnh) :
00055     nh_(nh),
00056     pnh_(pnh),
00057     depth_sub_(),
00058     color_sub_(),
00059     pub_it_(nh_),
00060     crop_size_(target_resolution_)
00061 {
00062   // ROS parameters
00063   ros::NodeHandle priv_nh_("~");
00064 
00065   // read depth map topic from param server
00066   priv_nh_.param<std::string>("depth", depthmap_topic_, "/camera/depth/image");
00067 
00068   // read rgb topic from param server
00069   priv_nh_.param<std::string>("rgb", rgb_image_topic_, "/camera/rgb/image_color");
00070 
00071   // Monitor whether anyone is subscribed to the output
00072   image_transport::SubscriberStatusCallback connect_cb = boost::bind(&DepthCloudEncoder::connectCb, this);
00073   // Make sure we don't enter connectCb() between advertising and assigning to pub_point_cloud_
00074   boost::lock_guard<boost::mutex> lock(connect_mutex_);
00075 
00076   pub_ = pub_it_.advertise("depthcloud_encoded", 1, connect_cb, connect_cb);
00077 }
00078 DepthCloudEncoder::~DepthCloudEncoder()
00079 {
00080 }
00081 
00082 void DepthCloudEncoder::connectCb()
00083 {
00084   boost::lock_guard<boost::mutex> lock(connect_mutex_);
00085 
00086   if (pub_.getNumSubscribers())
00087   {
00088     subscribe(depthmap_topic_, rgb_image_topic_);
00089   }
00090   else
00091   {
00092     unsubscribe();
00093   }
00094 }
00095 
00096 void DepthCloudEncoder::subscribe(std::string& depth_topic, std::string& color_topic)
00097 {
00098   unsubscribe();
00099 
00100   ROS_DEBUG_STREAM("Subscribing to "<< color_topic);
00101   ROS_DEBUG_STREAM("Subscribing to "<< depth_topic);
00102 
00103   if (!depth_topic.empty())
00104   {
00105     try
00106     {
00107       image_transport::ImageTransport depth_it(pnh_);
00108       image_transport::ImageTransport color_it(pnh_);
00109 
00110       // subscribe to depth map topic
00111       depth_sub_->subscribe(depth_it, depth_topic, 1, image_transport::TransportHints("raw"));
00112 
00113       if (!color_topic.empty())
00114       {
00115         // subscribe to color image topic
00116         color_sub_->subscribe(color_it, color_topic, 1, image_transport::TransportHints("raw"));
00117 
00118         // connect message filters to synchronizer
00119         sync_depth_color_->connectInput(*depth_sub_, *color_sub_);
00120         sync_depth_color_->setInterMessageLowerBound(0, ros::Duration(1.5));
00121         sync_depth_color_->setInterMessageLowerBound(1, ros::Duration(1.5));
00122         sync_depth_color_->registerCallback(boost::bind(&DepthCloudEncoder::depthColorCB, this, _1, _2));
00123       }
00124       else
00125       {
00126         depth_sub_->registerCallback(boost::bind(&DepthCloudEncoder::depthCB, this, _1));
00127       }
00128     }
00129     catch (ros::Exception& e)
00130     {
00131       ROS_ERROR("Error subscribing: %s", e.what());
00132     }
00133   }
00134 }
00135 
00136 
00137 void DepthCloudEncoder::unsubscribe()
00138 {
00139   try
00140   {
00141     // reset all message filters
00142     sync_depth_color_.reset(new SynchronizerDepthColor(SyncPolicyDepthColor(10)));
00143     depth_sub_.reset(new image_transport::SubscriberFilter());
00144     color_sub_.reset(new image_transport::SubscriberFilter());
00145   }
00146   catch (ros::Exception& e)
00147   {
00148     ROS_ERROR("Error unsubscribing: %s", e.what());
00149   }
00150 }
00151 
00152 void DepthCloudEncoder::depthCB(const sensor_msgs::ImageConstPtr& depth_msg)
00153 {
00154   process(depth_msg, sensor_msgs::ImageConstPtr(), crop_size_);
00155 }
00156 
00157 void DepthCloudEncoder::depthColorCB(const sensor_msgs::ImageConstPtr& depth_msg,
00158                                      const sensor_msgs::ImageConstPtr& color_msg)
00159 {
00160   ROS_DEBUG("Image depth/color pair received");
00161   process(depth_msg, color_msg, crop_size_);
00162 }
00163 
00164 void DepthCloudEncoder::process(const sensor_msgs::ImageConstPtr& depth_msg,
00165                                 const sensor_msgs::ImageConstPtr& color_msg,
00166                                 const std::size_t crop_size)
00167 {
00168   // Bit depth of image encoding
00169   int depth_bits = enc::bitDepth(depth_msg->encoding);
00170   int depth_channels = enc::numChannels(depth_msg->encoding);
00171 
00172   int color_bits = 0;
00173   int color_channels = 0;
00174 
00175   if ((depth_bits != 32) || (depth_channels != 1))
00176   {
00177     ROS_DEBUG_STREAM( "Invalid color depth image format ("<<depth_msg->encoding <<")");
00178     return;
00179   }
00180 
00181   // OpenCV-ros bridge
00182   cv_bridge::CvImagePtr color_cv_ptr;
00183 
00184   // check for color message
00185   if (color_msg)
00186   {
00187     try
00188     {
00189       color_cv_ptr = cv_bridge::toCvCopy(color_msg, "bgr8");
00190 
00191     }
00192     catch (cv_bridge::Exception& e)
00193     {
00194       ROS_ERROR_STREAM("OpenCV-ROS bridge error: " << e.what());
00195       return;
00196     }
00197 
00198     if (depth_msg->width != color_msg->width || depth_msg->height != color_msg->height)
00199     {
00200       ROS_DEBUG_STREAM( "Depth image resolution (" << (int)depth_msg->width << "x" << (int)depth_msg->height << ") "
00201       "does not match color image resolution (" << (int)color_msg->width << "x" << (int)color_msg->height << ")");
00202       return;
00203     }
00204   }
00205 
00206   // preprocessing => close NaN hole via interpolation and generate valid_point_mask
00207   sensor_msgs::ImagePtr depth_int_msg(new sensor_msgs::Image());
00208   sensor_msgs::ImagePtr valid_mask_msg(new sensor_msgs::Image());
00209 
00210   depthInterpolation(depth_msg, depth_int_msg, valid_mask_msg);
00211 
00212   unsigned int pix_size = enc::bitDepth(enc::BGR8) / 8 * 3;
00213 
00214   // generate output image message
00215   sensor_msgs::ImagePtr output_msg(new sensor_msgs::Image);
00216   output_msg->header = depth_int_msg->header;
00217   output_msg->encoding = enc::BGR8;
00218   output_msg->width = crop_size * 2;
00219   output_msg->height = crop_size * 2;
00220   output_msg->step = output_msg->width * pix_size;
00221   output_msg->is_bigendian = depth_int_msg->is_bigendian;
00222 
00223   // allocate memory
00224   output_msg->data.resize(output_msg->width * output_msg->height * pix_size, 0xFF);
00225 
00226   std::size_t input_width = depth_int_msg->width;
00227   std::size_t input_height = depth_int_msg->height;
00228 
00229   // copy depth & color data to output image
00230   {
00231     std::size_t y, x, left_x, top_y, width_x, width_y;
00232 
00233     // calculate borders to crop input image to crop_size X crop_size
00234     int top_bottom_corner = (input_height - crop_size) / 2;
00235     int left_right_corner = (input_width - crop_size) / 2;
00236 
00237     if (top_bottom_corner < 0)
00238     {
00239       top_y = 0;
00240       width_y = input_height;
00241     }
00242     else
00243     {
00244       top_y = top_bottom_corner;
00245       width_y = input_height - top_bottom_corner;
00246     }
00247 
00248     if (left_right_corner < 0)
00249     {
00250       left_x = 0;
00251       width_x = input_width;
00252     }
00253     else
00254     {
00255       left_x = left_right_corner;
00256       width_x = input_width - left_right_corner;
00257     }
00258 
00259     // pointer to output image
00260     uint8_t* dest_ptr = &output_msg->data[((top_y - top_bottom_corner) * output_msg->width + left_x - left_right_corner)
00261         * pix_size];
00262     const std::size_t dest_y_step = output_msg->step;
00263 
00264     // pointer to interpolated depth data
00265     const float* source_depth_ptr = (const float*)&depth_int_msg->data[(top_y * input_width + left_x) * sizeof(float)];
00266 
00267     // pointer to valid-pixel-mask
00268     const uint8_t* source_mask_ptr = &valid_mask_msg->data[(top_y * input_width + left_x) * sizeof(uint8_t)];
00269 
00270     // pointer to color data
00271     const uint8_t* source_color_ptr = 0;
00272     std::size_t source_color_y_step = 0;
00273     if (color_msg)
00274     {
00275       source_color_y_step = input_width * pix_size;
00276       source_color_ptr = static_cast<const uint8_t*>(&color_cv_ptr->image.data[(top_y * input_width + left_x) * pix_size]);
00277     }
00278 
00279     // helpers
00280     const std::size_t right_frame_shift = crop_size * pix_size;
00281     ;
00282     const std::size_t down_frame_shift = dest_y_step * crop_size;
00283 
00284     // generate output image
00285     for (y = top_y; y < width_y;
00286         ++y, source_color_ptr += source_color_y_step, source_depth_ptr += input_width, source_mask_ptr += input_width, dest_ptr +=
00287             dest_y_step)
00288     {
00289       const float *depth_ptr = source_depth_ptr;
00290 
00291       // reset iterator pointers for each column
00292       const uint8_t *color_ptr = source_color_ptr;
00293       const uint8_t *mask_ptr = source_mask_ptr;
00294 
00295       uint8_t *out_depth_low_ptr = dest_ptr;
00296       uint8_t *out_depth_high_ptr = dest_ptr + right_frame_shift;
00297       uint8_t *out_color_ptr = dest_ptr + right_frame_shift + down_frame_shift;
00298 
00299       for (x = left_x; x < width_x; ++x)
00300       {
00301         uint16_t depth_pix_low;
00302         uint16_t depth_pix_high;
00303 
00304         if (*depth_ptr == *depth_ptr) // valid point
00305         {
00306           depth_pix_low = std::min(std::max(0.0f, (*depth_ptr / 2.0f) * (float)(0xFF * 3)), (float)(0xFF * 3));
00307           depth_pix_high = std::min(std::max(0.0f, ((*depth_ptr - max_depth_per_tile) / 2.0f) * (float)(0xFF) * 3), (float)(0xFF * 3));
00308         }
00309         else
00310         {
00311           depth_pix_low = 0;
00312           depth_pix_high = 0;
00313 
00314         }
00315 
00316         uint8_t *mask_pix_ptr = out_depth_low_ptr + down_frame_shift;
00317         if (*mask_ptr == 0)
00318         {
00319           // black pixel for valid point
00320           memset(mask_pix_ptr, 0x00, pix_size);
00321         }
00322         else
00323         {
00324           // white pixel for invalid point
00325           memset(mask_pix_ptr, 0xFF, pix_size);
00326         }
00327 
00328         uint8_t depth_pix_low_r = depth_pix_low / 3;
00329         uint8_t depth_pix_low_g = depth_pix_low / 3;
00330         uint8_t depth_pix_low_b = depth_pix_low / 3;
00331 
00332         if (depth_pix_low % 3 == 1)  ++depth_pix_low_r;
00333         if (depth_pix_low % 3 == 2)  ++depth_pix_low_g;
00334 
00335         *out_depth_low_ptr = depth_pix_low_r;  ++out_depth_low_ptr;
00336         *out_depth_low_ptr = depth_pix_low_g;  ++out_depth_low_ptr;
00337         *out_depth_low_ptr = depth_pix_low_b;  ++out_depth_low_ptr;
00338 
00339         uint8_t depth_pix_high_r = depth_pix_high / 3;
00340         uint8_t depth_pix_high_g = depth_pix_high / 3;
00341         uint8_t depth_pix_high_b = depth_pix_high / 3;
00342 
00343         if ((depth_pix_high % 3) == 1)
00344           ++depth_pix_high_r;
00345         if ((depth_pix_high % 3) == 2)
00346           ++depth_pix_high_g;
00347 
00348         *out_depth_high_ptr = depth_pix_high_r; ++out_depth_high_ptr;
00349         *out_depth_high_ptr = depth_pix_high_g; ++out_depth_high_ptr;
00350         *out_depth_high_ptr = depth_pix_high_b; ++out_depth_high_ptr;
00351 
00352         if (color_ptr)
00353         {
00354           // copy three color channels
00355           *out_color_ptr = *color_ptr; ++out_color_ptr; ++color_ptr;
00356 
00357           *out_color_ptr = *color_ptr; ++out_color_ptr; ++color_ptr;
00358 
00359           *out_color_ptr = *color_ptr; ++out_color_ptr; ++color_ptr;
00360         }
00361 
00362         // increase input iterator pointers
00363         ++mask_ptr;
00364         ++depth_ptr;
00365 
00366       }
00367     }
00368   }
00369   pub_.publish(output_msg);
00370 }
00371 
00372 void DepthCloudEncoder::depthInterpolation(sensor_msgs::ImageConstPtr depth_msg, sensor_msgs::ImagePtr depth_int_msg,
00373                                            sensor_msgs::ImagePtr mask_msg)
00374 {
00375   const std::size_t input_width = depth_msg->width;
00376   const std::size_t input_height = depth_msg->height;
00377 
00378   // prepare output image - depth image with interpolated NaN holes
00379   depth_int_msg->header = depth_msg->header;
00380   depth_int_msg->encoding = depth_msg->encoding;
00381   depth_int_msg->width = input_width;
00382   depth_int_msg->height = input_height;
00383   depth_int_msg->step = depth_msg->step;
00384   depth_int_msg->is_bigendian = depth_msg->is_bigendian;
00385   depth_int_msg->data.resize(depth_int_msg->step * depth_int_msg->height, 0);
00386 
00387   // prepare output image - valid sample mask
00388   mask_msg->header = depth_msg->header;
00389   mask_msg->encoding = enc::TYPE_8UC1;
00390   mask_msg->width = input_width;
00391   mask_msg->height = input_height;
00392   mask_msg->step = depth_msg->step;
00393   mask_msg->is_bigendian = depth_msg->is_bigendian;
00394   mask_msg->data.resize(mask_msg->step * mask_msg->height, 0xFF);
00395 
00396   const float* depth_ptr = (const float*)&depth_msg->data[0];
00397   float* depth_int_ptr = (float*)&depth_int_msg->data[0];
00398   uint8_t* mask_ptr = (uint8_t*)&mask_msg->data[0];
00399 
00400   float leftVal = -1.0f;
00401 
00402   unsigned int y, len;
00403   for (y = 0; y < input_height; ++y, depth_ptr += input_width, depth_int_ptr += input_width, mask_ptr += input_width)
00404   {
00405     const float* in_depth_ptr = depth_ptr;
00406     float* out_depth_int_ptr = depth_int_ptr;
00407     uint8_t* out_mask_ptr = mask_ptr;
00408 
00409     const float* in_depth_end_ptr = depth_ptr + input_width;
00410 
00411     while (in_depth_ptr < in_depth_end_ptr)
00412     {
00413       len = 0;
00414       const float* last_valid_pix_ptr = in_depth_ptr;
00415       while ((isnan(*in_depth_ptr) || (*in_depth_ptr == 0)) && (in_depth_ptr < in_depth_end_ptr))
00416       {
00417         ++in_depth_ptr;
00418         ++len;
00419       }
00420       if (len > 0)
00421       {
00422         // we found a NaN hole
00423 
00424         // find valid pixel on right side of hole
00425         float rightVal;
00426         if (in_depth_ptr < in_depth_end_ptr)
00427         {
00428           rightVal = *in_depth_ptr;
00429         }
00430         else
00431         {
00432           rightVal = leftVal;
00433         }
00434 
00435         // find valid pixel on left side of hole
00436         if (isnan(leftVal) || (leftVal < 0.0f))
00437           leftVal = rightVal;
00438 
00439         float incVal = (rightVal - leftVal) / (float)len;
00440         float fillVal = leftVal;
00441         const float* fill_ptr;
00442 
00443         for (fill_ptr = last_valid_pix_ptr; fill_ptr < in_depth_ptr; ++fill_ptr)
00444         {
00445           // fill hole with interpolated pixel data
00446           *out_depth_int_ptr = fillVal;
00447           ++out_depth_int_ptr;
00448 
00449           // write unknown sample to valid-pixel-mask
00450           *out_mask_ptr = 0xFF; ++out_mask_ptr;
00451 
00452           fillVal += incVal;
00453         }
00454 
00455         leftVal = rightVal;
00456       }
00457       else
00458       {
00459         // valid sample found - no hole to patch
00460 
00461         leftVal = *in_depth_ptr;
00462 
00463         *out_depth_int_ptr = *in_depth_ptr;
00464 
00465         // write known sample to valid-pixel-mask
00466         *out_mask_ptr = 0; ++out_mask_ptr;
00467 
00468         ++in_depth_ptr;
00469         ++out_depth_int_ptr;
00470       }
00471 
00472     }
00473 
00474   }
00475 
00476 }
00477 
00478 
00479 }
00480 


depthcloud_encoder
Author(s): Julius Kammer
autogenerated on Sun Oct 5 2014 23:24:31