00001
00045 #include <ros/ros.h>
00046
00047 #include <ros/package.h>
00048 #include <sensor_msgs/PointCloud2.h>
00049 #include <pcl_conversions/pcl_conversions.h>
00050 #include <pcl/point_cloud.h>
00051 #include <pcl/point_types.h>
00052 #include <math.h>
00053
00054 using namespace ros;
00055
00056 #include "pcl_converter/gnuplotPaletteReader.h"
00057
00058 ros::Publisher pub;
00059
00060 GnuPlotPalette pal;
00061 bool usingPal = false;
00062
00063 bool ignoreIntensityMissing = true;
00064
00065 double minRange = 255.99 * 0.25;
00066 double maxRange = 0.00;
00067 float minRangeGreyVal = 0.0;
00068 float maxRangeGreyVal = 255.99;
00069
00070 int numEchoOutput = 1;
00071
00072 class ParamSettings
00073 {
00074 private:
00075 bool dump2csv;
00076 public:
00077 bool isDump2csv() const
00078 {
00079 return dump2csv;
00080 }
00081
00082 void setDump2csv(bool dump2csv)
00083 {
00084 ParamSettings::dump2csv = dump2csv;
00085 }
00086
00087 };
00088
00089 ParamSettings paramSettings;
00090
00091
00092 void dump2csv(const sensor_msgs::PointCloud2ConstPtr &cloud, int x_offset, int y_offset, int z_offset)
00093 {
00094 static int cnt = 0;
00095 char szFileName[255] = {0};
00096 sprintf(szFileName,"/tmp/cloud_dump_%04d.csv", cnt);
00097 FILE *fout;
00098 fout = fopen(szFileName,"w");
00099 if (fout != NULL)
00100 {
00101 int i,j;
00102 fprintf(fout,"%-8s;%-8s;%-8s;%-8s;%-8s;%-8s\n","X(col)","Y(Col)","X","Y","Z","Range");
00103 for (i = 0; i < cloud->height; i++)
00104 {
00105 for (j = 0; j < cloud->width; j++)
00106 {
00107 float xRange,yRange,zRange;
00108 float range;
00109 memcpy(&xRange, &(cloud->data[x_offset]), sizeof(float));
00110 memcpy(&yRange, &(cloud->data[y_offset]), sizeof(float));
00111 memcpy(&zRange, &(cloud->data[z_offset]), sizeof(float));
00112 range = sqrt(xRange*xRange + yRange*yRange + zRange*zRange);
00113 fprintf(fout,"%5d;%5d;", i, j);
00114 fprintf(fout,"%8.3f;%8.3f;%8.3f;%8.3f", xRange, yRange, zRange, range);
00115 fprintf(fout,"\n");
00116
00117 x_offset += cloud->point_step;
00118 y_offset += cloud->point_step;
00119 z_offset += cloud->point_step;
00120 }
00121 }
00122 }
00123 fclose(fout);
00124 cnt++;
00125 }
00126
00127
00128
00129
00130
00138 void
00139 cloud2image_cb(const sensor_msgs::PointCloud2ConstPtr &cloud)
00140 {
00141 sensor_msgs::Image msg;
00142 sensor_msgs::Image msgVGA;
00143 int rgb_index = -1;
00144 int x_index = -1;
00145 int y_index = -1;
00146 int z_index = -1;
00147
00148 int conversionTask = 3;
00149 float range_eps = 0.1;
00150
00151
00152 int heightUsed = cloud->height;
00153 if (numEchoOutput == 1)
00154 {
00155 if (heightUsed > 24)
00156 {
00157 heightUsed = 24;
00158 }
00159 }
00160
00161 for (size_t d = 0; d < cloud->fields.size(); ++d)
00162 {
00163 if (cloud->fields[d].name == "intensity")
00164 {
00165 rgb_index = static_cast<int>(d);
00166
00167 }
00168 if (cloud->fields[d].name == "x")
00169 {
00170 x_index = static_cast<int>(d);
00171
00172 }
00173 if (cloud->fields[d].name == "y")
00174 {
00175 y_index = static_cast<int>(d);
00176
00177 }
00178 if (cloud->fields[d].name == "z")
00179 {
00180 z_index = static_cast<int>(d);
00181
00182 }
00183 }
00184 if (rgb_index == -1)
00185 {
00186 if (ignoreIntensityMissing)
00187 {
00188 return;
00189
00190 }
00191 else
00192 {
00193 throw std::runtime_error("No intensity entry!!");
00194 }
00195 }
00196
00197
00198
00199 int heightStretch = 5;
00200
00201 int imgChannelCnt = 0;
00202
00203 const int maxAllowedEncodings = 2;
00204 bool encodeFlagArray[maxAllowedEncodings] = {0};
00205 bool useHDFormat = false;
00206 if (conversionTask & 1)
00207 {
00208 encodeFlagArray[0] = true;
00209 imgChannelCnt++;
00210 }
00211 if (conversionTask & 2)
00212 {
00213 encodeFlagArray[1] = true;
00214 imgChannelCnt++;
00215 }
00216
00217 if (cloud->width == 0 && heightUsed == 0)
00218 {
00219 throw std::runtime_error("Needs to be a dense like cloud!!");
00220 }
00221 else
00222 {
00223 msg.height = heightStretch * heightUsed * imgChannelCnt;
00224 msg.width = cloud->width;
00225 if (useHDFormat)
00226 {
00227 if (1080 > msg.height)
00228 {
00229 msg.height = 1080;
00230 }
00231 if (1920 > msg.width)
00232 {
00233 msg.width = 1920;
00234 }
00235 }
00236 }
00237 int rgb_offset = cloud->fields[rgb_index].offset;
00238 int x_offset = cloud->fields[x_index].offset;
00239 int y_offset = cloud->fields[y_index].offset;
00240 int z_offset = cloud->fields[z_index].offset;
00241
00242
00243
00244 int point_step = cloud->point_step;
00245
00246
00247
00248
00249
00250
00251 if (paramSettings.isDump2csv())
00252 {
00253 dump2csv(cloud, x_offset, y_offset, z_offset);
00254 }
00255
00256 msg.encoding = "bgr8";
00257 msg.step = static_cast<uint32_t>(msg.width * sizeof(uint8_t) * 3);
00258 msg.data.resize(msg.step * msg.height);
00259
00260
00261 int maxY = imgChannelCnt * heightStretch * heightUsed;
00262
00263 for (size_t y = 0; y < heightUsed; y++)
00264 {
00265 for (size_t x = 0; x < cloud->width; x++)
00266 {
00267 for (int chIdx = 0; chIdx < imgChannelCnt; chIdx++)
00268 {
00269 int encodingCnt = 0;
00270 int encodeIdx = -1;
00271 for (int j = 0; j < maxAllowedEncodings; j++)
00272 {
00273 if (encodeFlagArray[j])
00274 {
00275 if (chIdx == encodingCnt)
00276 {
00277 encodeIdx = j;
00278 break;
00279 }
00280 encodingCnt++;
00281 }
00282 }
00283
00284 for (int rowInner = 0; rowInner < heightStretch; rowInner++)
00285 {
00286 int rowTmp = (y * heightStretch + rowInner);
00287 rowTmp += encodingCnt * heightStretch * heightUsed;
00288
00289 int rowInnerIdx = maxY - (rowTmp) - 1;
00290 int xTmp = x;
00291 xTmp = cloud->width - 1 - x;
00292 uint8_t *pixel = &(msg.data[rowInnerIdx * msg.step + xTmp * 3]);
00293 uint8_t *pixelVGA = NULL;
00294
00295 float intensity;
00296 float xRange;
00297 float yRange;
00298 float zRange;
00299 float range = 0.0;
00300 memcpy(&xRange, &(cloud->data[x_offset]), sizeof(float));
00301 memcpy(&yRange, &(cloud->data[y_offset]), sizeof(float));
00302 memcpy(&zRange, &(cloud->data[z_offset]), sizeof(float));
00303 range = sqrt(xRange * xRange + yRange * yRange + zRange * zRange);
00304 memcpy(&intensity, &(cloud->data[rgb_offset]), sizeof(float));
00305 if (intensity > 255.0)
00306 {
00307 intensity = 255.0;
00308 }
00309 if (intensity < 0.0)
00310 {
00311 intensity = 0.0;
00312 }
00313 unsigned char r, g, b;
00314 unsigned char grey = 0;
00315 if (encodeIdx == 0)
00316 {
00317 grey = (unsigned char) intensity;
00318 }
00319
00320 float minRealRange = (minRange < maxRange) ? minRange : maxRange;
00321 float maxRealRange = (minRange < maxRange) ? maxRange : minRange;
00322
00323 if (encodeIdx == 1)
00324 {
00325 bool setBlack = false;
00326 if (range < range_eps)
00327 {
00328 setBlack = true;
00329 }
00330 if (range < minRealRange)
00331 {
00332 range = minRealRange;
00333
00334 }
00335 if (range > maxRealRange)
00336 {
00337 range = maxRealRange;
00338 }
00339
00340 range = (range - minRange) / (maxRange - minRange) * (maxRangeGreyVal - minRangeGreyVal);
00341 range += minRangeGreyVal;
00342
00343 grey = (unsigned char) range;
00344 if (setBlack)
00345 {
00346 grey = 0;
00347 }
00348 }
00349
00350
00351 r = grey;
00352 g = grey;
00353 b = grey;
00354
00355
00356 if (usingPal)
00357 {
00358 r = pal.getRbgValue(grey, 0);
00359 g = pal.getRbgValue(grey, 1);
00360 b = pal.getRbgValue(grey, 2);
00361 }
00362 pixel[2] = r;
00363 pixel[1] = g;
00364 pixel[0] = b;
00365
00366
00367 }
00368 }
00369 rgb_offset += point_step;
00370 x_offset += point_step;
00371 y_offset += point_step;
00372 z_offset += point_step;
00373
00374 }
00375
00376 }
00377
00378
00379 pub.publish(msg);
00380 }
00381
00382 void argUsage()
00383 {
00384 printf("sick_pcl_converter <input_pointcloud2-topic> <output_image-topic>");
00385 }
00386
00387 int
00388 main(int argc, char **argv)
00389 {
00390 bool dumpCsvFlag = false;
00391
00392 if (argc < 3)
00393 {
00394 argUsage();
00395 exit(-1);
00396 }
00397
00398
00399
00400 std::string inputTopic = argv[1];
00401 std::string outputTopic = argv[2];
00402 ros::init(argc, argv, "sick_pcl_converter");
00403
00404
00405 ros::NodeHandle nh("sick_pcl_converter");
00406 std::string heatMap;
00407 nh.getParam("heat_map", heatMap);
00408
00409 nh.getParam("dump_csv", dumpCsvFlag);
00410
00411 paramSettings.setDump2csv(dumpCsvFlag);
00412
00413 if (paramSettings.isDump2csv())
00414 {
00415 ROS_INFO("Dump to csv file for debugging enabled.\n Files will be dumped at /tmp ");
00416 }
00417
00418 std::string path = ros::package::getPath("sick_scan");
00419
00420 if (heatMap.length() > 0)
00421 {
00422 std::string heatMapFileName = path + "/config/" + heatMap;
00423 pal.load(heatMapFileName);
00424 usingPal = true;
00425 }
00426 ROS_INFO("Subscribing to %s and publishing images to %s\n", inputTopic.c_str(), outputTopic.c_str());
00427
00428
00429 ros::Subscriber sub = nh.subscribe(inputTopic, 1, cloud2image_cb);
00430
00431
00432
00433
00434 pub = nh.advertise<sensor_msgs::Image>(outputTopic, 1);
00435
00436
00437 ros::spin();
00438 }