pcdfilter_pa_ros.cpp
Go to the documentation of this file.
00001 /******************************************************************************
00002 *                                                                             *
00003 * pcdfilter_pa_ros.cpp                                                        *
00004 * ====================                                                        *
00005 *                                                                             *
00006 *******************************************************************************
00007 *                                                                             *
00008 * github repository                                                           *
00009 *   https://github.com/TUC-ProAut/ros_pcdfilter                               *
00010 *                                                                             *
00011 * Chair of Automation Technology, Technische Universität Chemnitz             *
00012 *   https://www.tu-chemnitz.de/etit/proaut                                    *
00013 *                                                                             *
00014 *******************************************************************************
00015 *                                                                             *
00016 * New BSD License                                                             *
00017 *                                                                             *
00018 * Copyright (c) 2015-2018, Peter Weissig, Technische Universität Chemnitz     *
00019 * All rights reserved.                                                        *
00020 *                                                                             *
00021 * Redistribution and use in source and binary forms, with or without          *
00022 * modification, are permitted provided that the following conditions are met: *
00023 *     * Redistributions of source code must retain the above copyright        *
00024 *       notice, this list of conditions and the following disclaimer.         *
00025 *     * Redistributions in binary form must reproduce the above copyright     *
00026 *       notice, this list of conditions and the following disclaimer in the   *
00027 *       documentation and/or other materials provided with the distribution.  *
00028 *     * Neither the name of the Technische Universität Chemnitz nor the       *
00029 *       names of its contributors may be used to endorse or promote products  *
00030 *       derived from this software without specific prior written permission. *
00031 *                                                                             *
00032 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" *
00033 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE   *
00034 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  *
00035 * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY      *
00036 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  *
00037 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR          *
00038 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER  *
00039 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          *
00040 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY   *
00041 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
00042 * DAMAGE.                                                                     *
00043 *                                                                             *
00044 ******************************************************************************/
00045 
00046 // local headers
00047 #include "pcdfilter_pa/pcdfilter_pa_ros.h"
00048 
00049 // ros headers
00050 #include <sensor_msgs/point_cloud_conversion.h>
00051 #include <laser_geometry/laser_geometry.h>
00052 
00053 //**************************[cPcdFilterPaRos]**********************************
00054 cPcdFilterPaRos::cPcdFilterPaRos() {
00055 }
00056 
00057 //**************************[~cPcdFilterPaRos]*********************************
00058 cPcdFilterPaRos::~cPcdFilterPaRos() {
00059 }
00060 
00061 //**************************[convertCloud]*************************************
00062 sensor_msgs::PointCloud2Ptr cPcdFilterPaRos::convertCloud (
00063     const sensor_msgs::PointCloudConstPtr &msg) const {
00064 
00065     sensor_msgs::PointCloud2Ptr result(new sensor_msgs::PointCloud2);
00066     sensor_msgs::convertPointCloudToPointCloud2(*msg, *result);
00067 
00068     return result;
00069 }
00070 
00071 //**************************[convertCloud]*************************************
00072 sensor_msgs::PointCloud2Ptr cPcdFilterPaRos::convertCloud(
00073   const sensor_msgs::LaserScanConstPtr &msg) const {
00074 
00075     laser_geometry::LaserProjection projector;
00076 
00077     sensor_msgs::PointCloud2Ptr result (new sensor_msgs::PointCloud2);
00078 
00079     if (rosparams_.laser_nan_replacement_value_ < 0) {
00080         projector.projectLaser(*msg, *result, -1,
00081           laser_geometry::channel_option::Default);
00082     } else {
00083         sensor_msgs::LaserScan msg_changed(*msg);
00084 
00085         for (int i = 0; i < msg_changed.ranges.size(); i++) {
00086             if (std::isnan(msg_changed.ranges[i])) {
00087                 msg_changed.ranges[i] =
00088                   rosparams_.laser_nan_replacement_value_;
00089             }
00090         }
00091 
00092         projector.projectLaser(msg_changed, *result, -1,
00093           laser_geometry::channel_option::Default);
00094     }
00095 
00096     return result;
00097 }
00098 
00099 //**************************[updateTf]*****************************************
00100 bool cPcdFilterPaRos::updateTf( const tf::TransformListener &tf_listener,
00101   const std::string base_frame, const ros::Time time) {
00102 
00103     bool result = filters_.update(tf_listener, base_frame, time,
00104       params_.filters_);
00105 
00106     if (rosparams_.debugging_) {
00107         for (int i = 0; i < params_.filters_.size(); i++) {
00108             ROS_INFO("%s:   updated filter[%d] (%s)",
00109               ros::this_node::getName().data(), i,
00110               params_.filters_[i].toString().data());
00111         }
00112     }
00113 
00114     return result;
00115 }
00116 
00117 //**************************[updateTf]*****************************************
00118 bool cPcdFilterPaRos::updateTf(
00119   const tf::TransformListener &tf_listener) {
00120 
00121     if (pcd_buffered_msg_.use_count() < 1) {
00122         params_.filters_.clear();
00123         return false;
00124     }
00125     return updateTf(tf_listener, pcd_buffered_msg_->header.frame_id,
00126       pcd_buffered_msg_->header.stamp);
00127 }
00128 
00129 //**************************[filterCloud]**************************************
00130 bool cPcdFilterPaRos::filterCloud(const sensor_msgs::PointCloud2ConstPtr &msg,
00131   sensor_msgs::PointCloud2Ptr &result) {
00132 
00133     pcd_buffered_msg_ = msg;
00134 
00135     // convert pointcloud to opencv
00136     pcd_buffered_points_= convertCloudToOpencv(msg);
00137 
00138     if (pcd_buffered_points_.rows == 0) {
00139         ROS_WARN("%s: size of pointcloud after conversion to opencv is 0",
00140           ros::this_node::getName().data());
00141 
00142         pcd_buffered_points_.release();
00143         pcd_buffered_msg_.reset();
00144         return false;
00145     }
00146 
00147     // filter pointcloud
00148     bool result_bool = filterCloud(result);
00149 
00150     if (!rosparams_.buffer_pointcloud_) {
00151         pcd_buffered_points_.release();
00152         pcd_buffered_msg_.reset();
00153     }
00154     return result_bool;
00155 }
00156 
00157 //**************************[filterCloud]**************************************
00158 bool cPcdFilterPaRos::filterCloud(sensor_msgs::PointCloud2Ptr &result) {
00159 
00160     if ((pcd_buffered_msg_.use_count() < 1) ||
00161       (pcd_buffered_points_.empty())) {
00162         return false;
00163     }
00164 
00165     std::vector<bool> mask;
00166     std::vector<int> count;
00167 
00168 
00169     // filter pointcloud
00170     count = pointcloudFilter(pcd_buffered_points_, mask);
00171 
00172     // apply mask
00173     result = applyMasktoCloud(pcd_buffered_msg_, mask);
00174 
00175     if (rosparams_.debugging_) {
00176         std::stringstream ss;
00177         ss << "remaining points " << result->height << " (" <<
00178           pcd_buffered_points_.rows;
00179         for (int i = 0; i < count.size(); i++) {
00180             ss << " - " << count[i];
00181         }
00182         ss << ")";
00183         ROS_INFO("%s: %s", ros::this_node::getName().data(), ss.str().data());
00184     }
00185 
00186     return true;
00187 }
00188 
00189 //**************************[convertCloudToOpencv]*****************************
00190 const cv::Mat cPcdFilterPaRos::convertCloudToOpencv(
00191     const sensor_msgs::PointCloud2ConstPtr &msg,
00192     const bool force_copy) const {
00193 
00194     // number of points
00195     int count = msg->height * msg->width;
00196     int point_step = msg->point_step;
00197 
00198     // copy info about the resulting columns (x,y,z, dummy)
00199     int types[4] = {-1, -1, -1, -1};
00200     int offsets[4] = {0, 0, 0, 0};
00201     for (int i = 0; i < msg->fields.size(); i++) {
00202         int position = 3;
00203         if ((msg->fields[i].name == "x") || (msg->fields[i].name == "X")) {
00204             position = 0;
00205         }
00206         if ((msg->fields[i].name == "y") || (msg->fields[i].name == "Y")) {
00207             position = 1;
00208         }
00209         if ((msg->fields[i].name == "z") || (msg->fields[i].name == "Z")) {
00210             position = 2;
00211         }
00212 
00213         types[position]   = msg->fields[i].datatype;
00214         offsets[position] = msg->fields[i].offset  ;
00215     }
00216     // check if all resulting columns (x,y,z) exist
00217     for (int i = 0; i < 3; i++) {
00218         if (types[i] < 0) {
00219             return cv::Mat();
00220         }
00221     }
00222 
00223     // check if simple reassignment of data is possible (instead of copying)
00224     bool simple_assign = true;
00225     for (int i = 0; i < 3; i++) {
00226         if ((types[0] != types[i]) || (offsets[i] != i * offsets[1])) {
00227             simple_assign = false;
00228             break;
00229         }
00230     }
00231     if (simple_assign) {
00232         if ((types[0] == sensor_msgs::PointField::FLOAT32) &&
00233           (offsets[1] == 4)) {
00234             cv::Mat result(count, 3, CV_32FC1,
00235               const_cast<uint8_t*> (&(msg->data.front())), point_step);
00236             if (force_copy) {
00237                 if (rosparams_.debugging_) {
00238                     ROS_INFO("%s: opencv - simple copy of pointcloud)",
00239                       ros::this_node::getName().data());
00240                 }
00241                 cv::Mat result_copy;
00242                 result.copyTo(result_copy);
00243                 return result_copy;
00244             } else {
00245                 if (rosparams_.debugging_) {
00246                     ROS_INFO("%s: opencv - reassignment of pointcloud)",
00247                       ros::this_node::getName().data());
00248                 }
00249                 return result;
00250             }
00251         }
00252         if ((types[0] == sensor_msgs::PointField::FLOAT64) &&
00253           (offsets[1] == 8)) {
00254             if (rosparams_.debugging_) {
00255                 ROS_INFO("%s: opencv - reassignment of pointcloud"
00256                   " + conversion", ros::this_node::getName().data());
00257             }
00258             cv::Mat result;
00259             cv::Mat (count, 3, CV_64FC1,
00260               const_cast<uint8_t*> (&(msg->data.front())),
00261               point_step).convertTo(result, CV_32FC1);
00262             return result;
00263         }
00264     }
00265 
00266     // copy data (and maybe change data_type)
00267     if (rosparams_.debugging_) {
00268         ROS_INFO("%s: opencv - complex copy of pointcloud",
00269           ros::this_node::getName().data());
00270     }
00271     cv::Mat result(count, 3, CV_32FC1);
00272     int current_pos = 0;
00273     for (int i = 0; i < count; i++) {
00274 
00275         for (int j = 0; j < 3; j++) {
00276             const void *ptr = &(msg->data[current_pos + offsets[j]]);
00277             double value = -1;
00278             switch (types[j]) {
00279                 case sensor_msgs::PointField::FLOAT64:
00280                     value = float(*((const double*  ) ptr));
00281                     break;
00282                 case sensor_msgs::PointField::FLOAT32:
00283                     value =       *((const float*   ) ptr) ;
00284                     break;
00285                 case sensor_msgs::PointField::INT32:
00286                     value = float(*((const int32_t* ) ptr));
00287                     break;
00288                 case sensor_msgs::PointField::UINT32:
00289                     value = float(*((const uint32_t*) ptr));
00290                     break;
00291                 case sensor_msgs::PointField::INT16:
00292                     value = float(*((const int16_t* ) ptr));
00293                     break;
00294                 case sensor_msgs::PointField::UINT16:
00295                     value = float(*((const uint16_t*) ptr));
00296                     break;
00297                 case sensor_msgs::PointField::INT8:
00298                     value = float(*((const int8_t*  ) ptr));
00299                     break;
00300                 case sensor_msgs::PointField::UINT8:
00301                     value = float(*((const uint8_t* ) ptr));
00302                     break;
00303             }
00304             result.at<double>(i,j) = value;
00305         }
00306         current_pos+= point_step;
00307     }
00308 
00309     return result;
00310 }
00311 
00312 //**************************[applyMasktoCloud]*********************************
00313 sensor_msgs::PointCloud2Ptr cPcdFilterPaRos::applyMasktoCloud(
00314   const sensor_msgs::PointCloud2ConstPtr cloud,
00315   const std::vector<bool> mask) const {
00316 
00317     int point_step = cloud->point_step;
00318 
00319     int size_old = cloud->height * cloud->width;
00320     if ((size_old == 0) && (point_step > 0)) {
00321         size_old = cloud->data.size() / point_step;
00322     }
00323 
00324     int size_new = mask.size() <= size_old ? mask.size() : size_old;
00325     int count = 0;
00326     for (int i = 0; i < size_new; i++) {
00327         if (mask[i]) {count++;}
00328     }
00329     size_new = count;
00330 
00331 
00332     sensor_msgs::PointCloud2Ptr result (new sensor_msgs::PointCloud2);
00333     result->header.frame_id = cloud->header.frame_id;
00334     result->header.stamp    = cloud->header.stamp;
00335     result->is_bigendian    = cloud->is_bigendian;
00336     result->is_dense        = cloud->is_dense;
00337     result->height          = size_new;
00338     result->width           = 1;
00339     result->point_step      = cloud->point_step;
00340     result->row_step        = cloud->point_step;
00341 
00342     result->fields          = cloud->fields;
00343     result->data.resize(size_new * point_step);
00344 
00345     uint8_t *pos_new = &result->data[0];
00346     const uint8_t *pos_old_base = &cloud->data[0];
00347     for (int i = 0; i < size_old; i++) {
00348         if (mask[i]) {
00349             memcpy(pos_new, pos_old_base + i*point_step, point_step);
00350             pos_new+= point_step;
00351         }
00352     }
00353 
00354     return result;
00355 }


pcdfilter_pa
Author(s):
autogenerated on Thu Jun 6 2019 21:01:00