point_cloud.cpp
Go to the documentation of this file.
00001 #include "pointmatcher_ros/point_cloud.h"
00002 #include "ros/ros.h"
00003 #include "boost/detail/endian.hpp"
00004 #include "tf/transform_listener.h"
00005 #include <vector>
00006 #include <memory>
00007 
00008 namespace PointMatcher_ros
00009 {
00010         using namespace std;
00011         
00013         template<typename T>
00014         typename PointMatcher<T>::DataPoints rosMsgToPointMatcherCloud(const sensor_msgs::PointCloud2& rosMsg)
00015         {
00016                 typedef PointMatcher<T> PM;
00017                 typedef typename PM::DataPoints DataPoints;
00018                 typedef typename DataPoints::Label Label;
00019                 typedef typename DataPoints::Labels Labels;
00020                 typedef typename DataPoints::View View;
00021                 
00022                 if (rosMsg.fields.empty())
00023                         return DataPoints();
00024                 
00025                 // fill labels
00026                 // conversions of descriptor fields from pcl
00027                 // see http://www.ros.org/wiki/pcl/Overview
00028                 Labels featLabels;
00029                 Labels descLabels;
00030                 vector<bool> isFeature;
00031                 for(auto it(rosMsg.fields.begin()); it != rosMsg.fields.end(); ++it)
00032                 {
00033                         const string name(it->name);
00034                         const size_t count(std::max<size_t>(it->count, 1));
00035                         if (name == "x" || name == "y" || name == "z")
00036                         {
00037                                 featLabels.push_back(Label(name, count));
00038                                 isFeature.push_back(true);
00039                         }
00040                         else if (name == "rgb" || name == "rgba")
00041                         {
00042                                 descLabels.push_back(Label("color", (name == "rgba") ? 4 : 3));
00043                                 isFeature.push_back(false);
00044                         }
00045                         else if ((it+1) != rosMsg.fields.end() && it->name == "normal_x" && (it+1)->name == "normal_y")
00046                         {
00047                                 if ((it+2) != rosMsg.fields.end() && (it+2)->name == "normal_z")
00048                                 {
00049                                         descLabels.push_back(Label("normals", 3));
00050                                         it += 2;
00051                                         isFeature.push_back(false);
00052                                         isFeature.push_back(false);
00053                                 }
00054                                 else
00055                                 {
00056                                         descLabels.push_back(Label("normals", 2));
00057                                         it += 1;
00058                                         isFeature.push_back(false);
00059                                 }
00060                                 isFeature.push_back(false);
00061                         }
00062                         else 
00063                         {
00064                                 descLabels.push_back(Label(name, count));
00065                                 isFeature.push_back(false);
00066                         }
00067                 }
00068                 featLabels.push_back(Label("pad", 1));
00069                 assert(isFeature.size() == rosMsg.fields.size());
00070                 
00071                 // create cloud
00072                 const unsigned pointCount(rosMsg.width * rosMsg.height);
00073                 DataPoints cloud(featLabels, descLabels, pointCount);
00074                 cloud.getFeatureViewByName("pad").setConstant(1);
00075                 
00076                 // fill cloud
00077                 // TODO: support big endian, pass through endian-swapping method just after the *reinterpret_cast
00078                 typedef sensor_msgs::PointField PF;
00079                 size_t fieldId = 0;
00080                 for(auto it(rosMsg.fields.begin()); it != rosMsg.fields.end(); ++it, ++fieldId)
00081                 {
00082                         if (it->name == "rgb" || it->name == "rgba")
00083                         {
00084                                 // special case for colors
00085                                 if (((it->datatype != PF::UINT32) && (it->datatype != PF::INT32) && (it->datatype != PF::FLOAT32)) || (it->count != 1))
00086                                         throw runtime_error(
00087                                                 (boost::format("Colors in a point cloud must be a single element of size 32 bits, found %1% elements of type %2%") % it->count % unsigned(it->datatype)).str()
00088                                         );
00089                                 View view(cloud.getDescriptorViewByName("color"));
00090                                 int ptId(0);
00091                                 for (size_t y(0); y < rosMsg.height; ++y)
00092                                 {
00093                                         const uint8_t* dataPtr(&rosMsg.data[0] + rosMsg.row_step*y);
00094                                         for (size_t x(0); x < rosMsg.width; ++x)
00095                                         {
00096                                                 const uint32_t rgba(*reinterpret_cast<const uint32_t*>(dataPtr + it->offset));
00097                                                 const T colorA(T((rgba >> 24) & 0xff) / 255.);
00098                                                 const T colorR(T((rgba >> 16) & 0xff) / 255.);
00099                                                 const T colorG(T((rgba >> 8) & 0xff) / 255.);
00100                                                 const T colorB(T((rgba >> 0) & 0xff) / 255.);
00101                                                 view(0, ptId) = colorR;
00102                                                 view(1, ptId) = colorG;
00103                                                 view(2, ptId) = colorB;
00104                                                 if (view.rows() > 3)
00105                                                         view(3, ptId) = colorA;
00106                                                 dataPtr += rosMsg.point_step;
00107                                                 ptId += 1;
00108                                         }
00109                                 }
00110                         }
00111                         else
00112                         {
00113                                 // get view for editing data
00114                                 View view(
00115                                          (it->name == "normal_x") ? cloud.getDescriptorRowViewByName("normals", 0) :
00116                                         ((it->name == "normal_y") ? cloud.getDescriptorRowViewByName("normals", 1) :
00117                                         ((it->name == "normal_z") ? cloud.getDescriptorRowViewByName("normals", 2) :
00118                                         ((isFeature[fieldId]) ? cloud.getFeatureViewByName(it->name) :
00119                                         cloud.getDescriptorViewByName(it->name))))
00120                                 );
00121                                 // use view to read data
00122                                 int ptId(0);
00123                                 const size_t count(std::max<size_t>(it->count, 1));
00124                                 for (size_t y(0); y < rosMsg.height; ++y)
00125                                 {
00126                                         const uint8_t* dataPtr(&rosMsg.data[0] + rosMsg.row_step*y);
00127                                         for (size_t x(0); x < rosMsg.width; ++x)
00128                                         {
00129                                                 const uint8_t* fPtr(dataPtr + it->offset);
00130                                                 for (unsigned dim(0); dim < count; ++dim)
00131                                                 {
00132                                                         switch (it->datatype)
00133                                                         {
00134                                                                 case PF::INT8: view(dim, ptId) = T(*reinterpret_cast<const int8_t*>(fPtr)); fPtr += 1; break;
00135                                                                 case PF::UINT8: view(dim, ptId) = T(*reinterpret_cast<const uint8_t*>(fPtr)); fPtr += 1; break;
00136                                                                 case PF::INT16: view(dim, ptId) = T(*reinterpret_cast<const int16_t*>(fPtr)); fPtr += 2; break;
00137                                                                 case PF::UINT16: view(dim, ptId) = T(*reinterpret_cast<const uint16_t*>(fPtr)); fPtr += 2; break;
00138                                                                 case PF::INT32: view(dim, ptId) = T(*reinterpret_cast<const int32_t*>(fPtr)); fPtr += 4; break;
00139                                                                 case PF::UINT32: view(dim, ptId) = T(*reinterpret_cast<const uint32_t*>(fPtr)); fPtr += 4; break;
00140                                                                 case PF::FLOAT32: view(dim, ptId) = T(*reinterpret_cast<const float*>(fPtr)); fPtr += 4; break;
00141                                                                 case PF::FLOAT64: view(dim, ptId) = T(*reinterpret_cast<const double*>(fPtr)); fPtr += 8; break;
00142                                                                 default: abort();
00143                                                         }
00144                                                 }
00145                                                 dataPtr += rosMsg.point_step;
00146                                                 ptId += 1;
00147                                         }
00148                                 }
00149                         }
00150                 }
00151 
00152                 
00153                 shared_ptr<typename PM::DataPointsFilter> filter(PM::get().DataPointsFilterRegistrar.create("RemoveNaNDataPointsFilter"));
00154                 return filter->filter(cloud);
00155         }
00156         
00157         template
00158         PointMatcher<float>::DataPoints rosMsgToPointMatcherCloud<float>(const sensor_msgs::PointCloud2& rosMsg);
00159         template
00160         PointMatcher<double>::DataPoints rosMsgToPointMatcherCloud<double>(const sensor_msgs::PointCloud2& rosMsg);
00161         
00162         
00163         template<typename T>
00164         typename PointMatcher<T>::DataPoints rosMsgToPointMatcherCloud(const sensor_msgs::LaserScan& rosMsg, const tf::TransformListener* listener, const std::string& fixedFrame)
00165         {
00166                 typedef PointMatcher<T> PM;
00167                 typedef typename PM::DataPoints DataPoints;
00168                 typedef typename DataPoints::Label Label;
00169                 typedef typename DataPoints::Labels Labels;
00170                 typedef typename DataPoints::View View;
00171                 
00172                 Labels featLabels;
00173                 featLabels.push_back(Label("x", 1));
00174                 featLabels.push_back(Label("y", 1));
00175                 featLabels.push_back(Label("pad", 1));
00176                 
00177                 Labels descLabels;
00178                 if (!rosMsg.intensities.empty())
00179                 {
00180                         descLabels.push_back(Label("intensity", 1));
00181                         assert(rosMsg.intensities.size() == rosMsg.ranges.size());
00182                 }
00183                 
00184                 // create cloud
00185                 size_t goodCount(0);
00186                 for (size_t i = 0; i < rosMsg.ranges.size(); ++i)
00187                 {
00188                         const float range(rosMsg.ranges[i]);
00189                         if (range >= rosMsg.range_min && range <= rosMsg.range_max)
00190                                 ++goodCount;
00191                 }
00192                 if (goodCount == 0)
00193                         return DataPoints();
00194                 DataPoints cloud(featLabels, descLabels, goodCount);
00195                 cloud.getFeatureViewByName("pad").setConstant(1);
00196                 
00197                 // fill features
00198                 const ros::Time& startTime(rosMsg.header.stamp);
00199                 const ros::Time endTime(startTime + ros::Duration(rosMsg.time_increment * (rosMsg.ranges.size() - 1)));
00200                 auto xs(cloud.getFeatureViewByName("x"));
00201                 auto ys(cloud.getFeatureViewByName("y"));
00202                 float angle(rosMsg.angle_min);
00203                 int j(0);
00204                 for (size_t i = 0; i < rosMsg.ranges.size(); ++i)
00205                 {
00206                         const float range(rosMsg.ranges[i]);
00207                         if (range >= rosMsg.range_min && range <= rosMsg.range_max)
00208                         {
00209                                 xs(0,j) = cos(angle) * range;
00210                                 ys(0,j) = sin(angle) * range;
00211                                 
00212                                 if (listener)
00213                                 {
00214                                         /* Note:
00215                                                 We do an approximation, as some filters like
00216                                                 ObservationDirectionDataPointsFilter should be applied per 
00217                                                 point *before* correcting them using the tf transform, but
00218                                                 as we expect the scan to be fast with respect to the speed 
00219                                                 of the robot, we consider this approximation as being ok.
00220                                         */
00221                                         const ros::Time curTime(rosMsg.header.stamp + ros::Duration(i * rosMsg.time_increment));
00222                                         // wait for transform
00223                                         listener->waitForTransform(
00224                                                 rosMsg.header.frame_id,
00225                                                 endTime,
00226                                                 rosMsg.header.frame_id,
00227                                                 curTime,
00228                                                 fixedFrame,
00229                                                 ros::Duration(1.0)
00230                                         );
00231                                         // transform data
00232                                         geometry_msgs::Vector3Stamped pin, pout;
00233                                         pin.header.stamp = curTime;
00234                                         pin.header.frame_id = rosMsg.header.frame_id;
00235                                         pin.vector.x = xs(0,j);
00236                                         pin.vector.y = ys(0,j);
00237                                         pin.vector.z = 0;
00238                                         try
00239                                         {
00240                                                 listener->transformVector(
00241                                                         rosMsg.header.frame_id,
00242                                                         endTime,
00243                                                         pin,
00244                                                         fixedFrame,
00245                                                         pout
00246                                                 );
00247                                         }
00248                                         catch (const tf::ExtrapolationException& e)
00249                                         {
00250                                                 return DataPoints();
00251                                         }
00252                                         // write back
00253                                         xs(0,j) = pout.vector.x;
00254                                         ys(0,j) = pout.vector.y;
00255                                 }
00256                                 
00257                                 ++j;
00258                         }
00259                         angle += rosMsg.angle_increment;
00260                 }
00261                 
00262                 // fill descriptors
00263                 if (!rosMsg.intensities.empty())
00264                 {
00265                         assert(rosMsg.intensities.size() == rosMsg.ranges.size());
00266                         auto is(cloud.getDescriptorViewByName("intensity"));
00267                         j = 0;
00268                         for (size_t i = 0; i < rosMsg.intensities.size(); ++i)
00269                         {
00270                                 const float range(rosMsg.ranges[i]);
00271                                 if (range >= rosMsg.range_min && range <= rosMsg.range_max)
00272                                 {
00273                                         is(0,j) = rosMsg.intensities[i];
00274                                         ++j;
00275                                 }
00276                         }
00277                 }
00278                 
00279                 //cerr << "point cloud:\n" << cloud.features.leftCols(10) << endl;
00280                 return cloud;
00281         }
00282         
00283         template
00284         PointMatcher<float>::DataPoints rosMsgToPointMatcherCloud<float>(const sensor_msgs::LaserScan& rosMsg, const tf::TransformListener* listener, const std::string& fixedFrame);
00285         template
00286         PointMatcher<double>::DataPoints rosMsgToPointMatcherCloud<double>(const sensor_msgs::LaserScan& rosMsg, const tf::TransformListener* listener, const std::string& fixedFrame);
00287 
00288 
00289         template<typename T>
00290         sensor_msgs::PointCloud2 pointMatcherCloudToRosMsg(const typename PointMatcher<T>::DataPoints& pmCloud, const std::string& frame_id, const ros::Time& stamp)
00291         {
00292                 sensor_msgs::PointCloud2 rosCloud;
00293                 typedef sensor_msgs::PointField PF;
00294                 
00295                 // check type and get sizes
00296                 BOOST_STATIC_ASSERT(is_floating_point<T>::value);
00297                 BOOST_STATIC_ASSERT((is_same<T, long double>::value == false));
00298                 uint8_t dataType;
00299                 size_t scalarSize;
00300                 if (typeid(T) == typeid(float))
00301                 {
00302                         dataType = PF::FLOAT32;
00303                         scalarSize = 4;
00304                 }
00305                 else
00306                 {
00307                         dataType = PF::FLOAT64;
00308                         scalarSize = 8;
00309                 }
00310                 
00311                 // build labels
00312                 unsigned offset(0);
00313                 assert(!pmCloud.featureLabels.empty());
00314                 assert(pmCloud.featureLabels[pmCloud.featureLabels.size()-1].text == "pad");
00315                 for(auto it(pmCloud.featureLabels.begin()); it != pmCloud.featureLabels.end(); ++it)
00316                 {
00317                         // last label is padding
00318                         if ((it + 1) == pmCloud.featureLabels.end())
00319                                 break;
00320                         PF pointField;
00321                         pointField.name = it->text;
00322                         pointField.offset = offset;
00323                         pointField.datatype= dataType;
00324                         pointField.count = it->span;
00325                         rosCloud.fields.push_back(pointField);
00326                         offset += it->span * scalarSize;
00327                 }
00328                 bool addZ(false);
00329                 if (!pmCloud.featureLabels.contains("z"))
00330                 {
00331                         PF pointField;
00332                         pointField.name = "z";
00333                         pointField.offset = offset;
00334                         pointField.datatype= dataType;
00335                         pointField.count = 1;
00336                         rosCloud.fields.push_back(pointField);
00337                         offset += scalarSize;
00338                         addZ = true;
00339                 }
00340                 const bool isDescriptor(!pmCloud.descriptorLabels.empty());
00341                 bool hasColor(false);
00342                 unsigned colorPos(0);
00343                 unsigned colorCount(0);
00344                 unsigned inDescriptorPos(0);
00345                 for(auto it(pmCloud.descriptorLabels.begin()); it != pmCloud.descriptorLabels.end(); ++it)
00346                 {
00347                         PF pointField;
00348                         if (it->text == "normals")
00349                         {
00350                                 assert((it->span == 2) || (it->span == 3));
00351                                 pointField.datatype = dataType;
00352                                 pointField.name = "normal_x";
00353                                 pointField.offset = offset;
00354                                 pointField.count = 1;
00355                                 rosCloud.fields.push_back(pointField);
00356                                 offset += scalarSize;
00357                                 pointField.name = "normal_y";
00358                                 pointField.offset = offset;
00359                                 pointField.count = 1;
00360                                 rosCloud.fields.push_back(pointField);
00361                                 offset += scalarSize;
00362                                 if (it->span == 3)
00363                                 {
00364                                         pointField.name = "normal_z";
00365                                         pointField.offset = offset;
00366                                         pointField.count = 1;
00367                                         rosCloud.fields.push_back(pointField);
00368                                         offset += scalarSize;
00369                                 }
00370                         }
00371                         else if (it->text == "color")
00372                         {
00373                                 colorPos = inDescriptorPos;
00374                                 colorCount = it->span;
00375                                 hasColor = true;
00376                                 pointField.datatype = (colorCount == 4) ? uint8_t(PF::UINT32) : uint8_t(PF::FLOAT32);
00377                                 pointField.name = (colorCount == 4) ? "rgba" : "rgb";
00378                                 pointField.offset = offset;
00379                                 pointField.count = 1;
00380                                 rosCloud.fields.push_back(pointField);
00381                                 offset += 4;
00382                         }
00383                         else
00384                         {
00385                                 pointField.datatype = dataType;
00386                                 pointField.name = it->text;
00387                                 pointField.offset = offset;
00388                                 pointField.count = it->span;
00389                                 rosCloud.fields.push_back(pointField);
00390                                 offset += it->span * scalarSize;
00391                         }
00392                         inDescriptorPos += it->span;
00393                 }
00394                 
00395                 // fill cloud with data
00396                 rosCloud.header.frame_id = frame_id;
00397                 rosCloud.header.stamp = stamp;
00398                 rosCloud.height = 1;
00399                 rosCloud.width = pmCloud.features.cols();
00400                 #ifdef BOOST_BIG_ENDIAN
00401                 rosCloud.is_bigendian = true;
00402                 #else // BOOST_BIG_ENDIAN
00403                 rosCloud.is_bigendian = false;
00404                 #endif // BOOST_BIG_ENDIAN
00405                 rosCloud.point_step = offset;
00406                 rosCloud.row_step = rosCloud.point_step * rosCloud.width;
00407                 rosCloud.is_dense = true;
00408                 rosCloud.data.resize(rosCloud.row_step * rosCloud.height);
00409                 const unsigned featureDim(pmCloud.features.rows()-1);
00410                 const unsigned descriptorDim(pmCloud.descriptors.rows());
00411                 assert(descriptorDim == inDescriptorPos);
00412                 const unsigned postColorPos(colorPos + colorCount);
00413                 assert(postColorPos <= inDescriptorPos);
00414                 const unsigned postColorCount(descriptorDim - postColorPos);
00415                 for (unsigned pt(0); pt < rosCloud.width; ++pt)
00416                 {
00417                         uint8_t *fPtr(&rosCloud.data[pt * offset]);
00418                         memcpy(fPtr, reinterpret_cast<const uint8_t*>(&pmCloud.features(0, pt)), scalarSize * featureDim);
00419                         fPtr += scalarSize * featureDim;
00420                         if (addZ)
00421                         {
00422                                 memset(fPtr, 0, scalarSize);
00423                                 fPtr += scalarSize;
00424                         }
00425                         if (isDescriptor)
00426                         {
00427                                 if (hasColor)
00428                                 {
00429                                         // before color
00430                                         memcpy(fPtr, reinterpret_cast<const uint8_t*>(&pmCloud.descriptors(0, pt)), scalarSize * colorPos);
00431                                         fPtr += scalarSize * colorPos;
00432                                         // compact color
00433                                         uint32_t rgba;
00434                                         unsigned colorR(unsigned(pmCloud.descriptors(colorPos+0, pt) * 255.) & 0xFF);
00435                                         unsigned colorG(unsigned(pmCloud.descriptors(colorPos+1, pt) * 255.) & 0xFF);
00436                                         unsigned colorB(unsigned(pmCloud.descriptors(colorPos+2, pt) * 255.) & 0xFF);
00437                                         unsigned colorA(0);
00438                                         if (colorCount == 4)
00439                                                 colorA = unsigned(pmCloud.descriptors(colorPos+3, pt) * 255.) & 0xFF;
00440                                         rgba = colorA << 24 | colorR << 16 | colorG << 8 | colorB;
00441                                         memcpy(fPtr, reinterpret_cast<const uint8_t*>(&rgba), 4);
00442                                         fPtr += 4;
00443                                         // after color
00444                                         memcpy(fPtr, reinterpret_cast<const uint8_t*>(&pmCloud.descriptors(postColorPos, pt)), scalarSize * postColorCount);
00445                                 }
00446                                 else
00447                                 {
00448                                         memcpy(fPtr, reinterpret_cast<const uint8_t*>(&pmCloud.descriptors(0, pt)), scalarSize * descriptorDim);
00449                                 }
00450                         }
00451                 }
00452 
00453                 // fill remaining information
00454                 rosCloud.header.frame_id = frame_id;
00455                 rosCloud.header.stamp = stamp;
00456                 
00457                 return rosCloud;
00458         }
00459         
00460         template
00461         sensor_msgs::PointCloud2 pointMatcherCloudToRosMsg<float>(const PointMatcher<float>::DataPoints& pmCloud, const std::string& frame_id, const ros::Time& stamp);
00462         template
00463         sensor_msgs::PointCloud2 pointMatcherCloudToRosMsg<double>(const PointMatcher<double>::DataPoints& pmCloud, const std::string& frame_id, const ros::Time& stamp);
00464 
00465 } // PointMatcher_ros


libpointmatcher_ros
Author(s): Stéphane Magnenat
autogenerated on Thu Jan 2 2014 11:16:13