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
00026
00027
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
00072 const unsigned pointCount(rosMsg.width * rosMsg.height);
00073 DataPoints cloud(featLabels, descLabels, pointCount);
00074 cloud.getFeatureViewByName("pad").setConstant(1);
00075
00076
00077
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
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
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
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
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
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
00215
00216
00217
00218
00219
00220
00221 const ros::Time curTime(rosMsg.header.stamp + ros::Duration(i * rosMsg.time_increment));
00222
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
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
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
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
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
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
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
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
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
00430 memcpy(fPtr, reinterpret_cast<const uint8_t*>(&pmCloud.descriptors(0, pt)), scalarSize * colorPos);
00431 fPtr += scalarSize * colorPos;
00432
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
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
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 }