00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include <structureColoring/StructureColoring.h>
00037 #include <structureColoring/grids/CylinderGridMap.h>
00038 #include <structureColoring/RosVisualization.h>
00039 #include <structureColoring/NodePair.h>
00040 #include <structureColoring/histograms/SphereUniformSampling.h>
00041 #include <structureColoring/histograms/TriangleDistributionHistogram.h>
00042 #include <structureColoring/histograms/TriangleDistributionHistogramWithRemove.h>
00043 #include <structureColoring/histograms/TriangleDistributionHistogram2D.h>
00044 #include <structureColoring/histograms/WeightedIdxVector.h>
00045 #include <pcl/sample_consensus/method_types.h>
00046 #include <pcl/sample_consensus/model_types.h>
00047 #include <pcl/segmentation/sac_segmentation.h>
00048 #include <pcl/filters/extract_indices.h>
00049 #include <pcl/filters/voxel_grid.h>
00050 #include <pcl/filters/passthrough.h>
00051 #include <pcl/features/normal_3d.h>
00052 #include <pcl/io/pcd_io.h>
00053 #include <pcl/common/time.h>
00054 #include <algorithm>
00055 #include <cmath>
00056 #include <limits>
00057 #include <queue>
00058 #include <set>
00059 #include <Eigen/StdVector>
00060
00061
00062
00063
00064 StructureColoring::StructureColoring(ros::NodeHandle& n, int argc, char* argv[]) : mVis(NULL)
00065 {
00066 init(n, argc, argv);
00067 }
00068
00069
00070
00071 void StructureColoring::init(ros::NodeHandle& n, int argc, char* argv[])
00072 {
00073 init(argc, argv);
00074 initNode(n);
00075 mVis = new RosVisualization(n);
00076 if (mParams.mVerbose)
00077 {
00078 ROS_INFO("Verbose output enabled");
00079 mVis->printTopics();
00080 }
00081 }
00082
00083
00084
00085 void StructureColoring::init(int argc, char* argv[])
00086 {
00087 initParams();
00088 std::string paramFilename;
00089 parseInput(paramFilename, mParams.mInputFilename, mParams.mOutputFilename, argc, argv);
00090 if (mParams.mInputFilename == "")
00091 mParams.mInputFilename = "../ABW/abw.test.0";
00092 if (mParams.mABW)
00093 mParams.mInputFilename.append(".range");
00094 if (mParams.mOutputFilename == "")
00095 mParams.mOutputFilename = "output";
00096 mParams.mNormalOutputFilename = mParams.mOutputFilename;
00097 mParams.mOutputFilename.append(".ms-seg");
00098 mParams.mNormalOutputFilename.append(".ms-nor");
00099 mParams.parseParamFile(paramFilename);
00100 initOctree();
00101 }
00102
00103
00104
00105 void StructureColoring::init()
00106 {
00107 initParams();
00108 initOctree();
00109 }
00110
00111
00112
00113 StructureColoring::~StructureColoring()
00114 {
00115 if(mVis) delete mVis;
00116 }
00117
00118
00119
00120 void StructureColoring::parseInput(std::string& paramFilename, std::string& inputFilename, std::string& outputFilename,
00121 int argc, char* argv[])
00122 {
00123 for (int i = 1; i < argc; i += 2) {
00124 if (std::string(argv[i]) == "--kinect") {
00125 mParams.mKinect = true;
00126 mParams.mTextures = true;
00127 i--;
00128 continue;
00129 }
00130 else if (std::string(argv[i]) == "--textures") {
00131 mParams.mTextures = true;
00132 i--;
00133 continue;
00134 }
00135 else if (std::string(argv[i]) == "--cylinder") {
00136 mParams.mCylinder = true;
00137 i--;
00138 continue;
00139 }
00140 else if (std::string(argv[i]) == "--disableRansacStep") {
00141 mParams.mNoRansacStep = true;
00142 i--;
00143 continue;
00144 }
00145 else if (std::string(argv[i]) == "-v") {
00146 mParams.mVerbose = true;
00147 i--;
00148 continue;
00149 }
00150 if (i + 1 < argc) {
00151 if (std::string(argv[i]) == "--kinectt") {
00152 mKinectTopic = argv[i + 1];
00153 mParams.mKinect = true;
00154 mParams.mTextures = true;
00155 continue;
00156 } else if (std::string(argv[i]) == "--postprocessing") {
00157 mParams.mPostProcessing = atoi(argv[i + 1]);
00158 } else if (std::string(argv[i]) == "--debugsteps") {
00159 mParams.mDebugSteps = atoi(argv[i + 1]);
00160 } else if (std::string(argv[i]) == "-r") {
00161 inputFilename = argv[i + 1];
00162 mParams.mPGM = true;
00163 } else if (std::string(argv[i]) == "--laser") {
00164 mParams.mLaser = true;
00165 mParams.mLaserTopic = argv[i+1];
00166 } else if (std::string(argv[i]) == "-p") {
00167 paramFilename = argv[i + 1];
00168 } else if (std::string(argv[i]) == "-m") {
00169 outputFilename = argv[i + 1];
00170 } else if (std::string(argv[i]) == "--prefix") {
00171 mParams.mABW = true;
00172 inputFilename = outputFilename = argv[i + 1];
00173 } else if (std::string(argv[i]) == "--percPrefix") {
00174 mParams.mPerceptron = true;
00175 inputFilename = outputFilename = argv[i + 1];
00176 } else if (std::string(argv[i]) == "--rawpic") {
00177 mParams.mWriteRawPic = true;
00178 mParams.mRawPicFilename = argv[i + 1];
00179 } else if (std::string(argv[i]) == "--pcd") {
00180 mParams.mTextures = true;
00181 mParams.mLoadPCD = true;
00182 inputFilename = argv[i + 1];
00183 } else if (std::string(argv[i]) == "--pcdNoRgb") {
00184 mParams.mLoadPCD = true;
00185 mParams.mPCDnoRGB = true;
00186 inputFilename = argv[i + 1];
00187 } else if (std::string(argv[i]) == "--pclSAC") {
00188 mParams.mPclSAC = true;
00189 mParams.mPclSACmaxIter = atoi(argv[i+1]);
00190 } else if (std::string(argv[i]) == "--runtimeFile") {
00191 mParams.mRuntimeFilename = argv[i + 1];
00192 } else if (std::string(argv[i]) == "--depth") {
00193 mParams.mOnlyDepth = atoi(argv[i + 1]);
00194 }
00195 else {
00196 std::cout << "Not enough or invalid arguments, please try again.\n"
00197 << "arguments are:\n"
00198 << "--kinect Start segmentation on kinect topics\n"
00199 << "--kinectt [string] Start segmentation on kinect, specifying topic\n"
00200 << "--laser [string] Start segmentation on laser [string] topic\n"
00201 << "--textures Start segmentation and calculate textures for planes afterwards,\n"
00202 << " works on PointClouds with RGB Data ONLY!\n"
00203 << "--cylinder Start segmentation, do cylinder segmentation after plane segmentation\n"
00204 << "--disableRansacStep Start segmentation, but leave out ransac steps#\n"
00205 << "-v Start segmentation with verbose output (of ros frames)\n"
00206 << "--postprocessing [int] Start segmentation with postprocessing steps according to [int]\n"
00207 << "--debugsteps [int] Start segmentation with [int] steps for debugging\n"
00208 << "-r [string] set input PGM filename to [string]\n"
00209 << "-p [string] set param file to [string], 3 params can be configured there:\n"
00210 << " mMaxHTDistanceThreshold(float), mHTOctreeBinSizeFactor(float)\n"
00211 << " mHTDistanceDeviationFactor(float)\n"
00212 << "-m [string] set output PGM filename to [string]\n"
00213 << "--prefix [string] set ABW rasterfile input and output prefix (without .range)\n"
00214 << "--percPrefix [string] set PERCEPTRON rasterfile input file and output prefix\n"
00215 << "--rawpic [string] set output for pcd and picture of colored points,\n"
00216 << " works on PointClouds with RGB Data ONLY!\n"
00217 << "--pcd [string] set input pcd file to [string]\n"
00218 << "--pclSAC [int] start PCL RANSAC with [int] maxIterations\n"
00219 << "--runtimeFile [string] write time used for segmentation to file [string]\n"
00220 << "--depth [int] start segmentation with HT / CC / RANSAC on octree depth [int]\n";
00221 exit(0);
00222 }
00223 }
00224 }
00225 }
00226
00227
00228
00229 void StructureColoring::initNode(ros::NodeHandle& n)
00230 {
00231 mNodeHandle = &n;
00232
00233 if (mParams.mKinect)
00234 mPointCloud2Subscriber = mNodeHandle->subscribe<sensor_msgs::PointCloud2> (mKinectTopic, 1,
00235 &StructureColoring::pointCloud2Callback, this);
00236 if (mParams.mLaser)
00237 mPointCloudSubscriber = mNodeHandle->subscribe<sensor_msgs::PointCloud> (mParams.mLaserTopic, 1,
00238 &StructureColoring::pointCloudCallback, this);
00239
00240 }
00241
00242
00243
00244 void StructureColoring::initParams()
00245 {
00246 mLastCellSizeWithNormals = mParams.mRho_max;
00247 mKinectTopic = "/camera/depth_registered/points";
00248 }
00249
00250
00251
00252 void StructureColoring::initOctree()
00253 {
00254 unsigned int numPoints = 512 * 512;
00255 if (mParams.mKinect) numPoints = 640 * 480;
00256 if (mParams.mLaser) numPoints = 2000000;
00257 if (!mOcTree) mOcTree.reset(new OcTree(mParams.mMinOctreeResolution, mParams.mRho_max, mParams.mPrincipalVarFactor,
00258 mParams.mMinPointsForNormal, mParams.mCurvThreshold, mParams.mCurv2Threshold, mParams.mSqDistFactor, numPoints));
00259 }
00260
00261
00262
00263 void StructureColoring::pointCloud2Callback(const sensor_msgs::PointCloud2ConstPtr& msg)
00264 {
00265 PointCloudPtr pointCloud(new PointCloud());
00266 PlanePatchVector pointMappingPlanes;
00267 CylinderPatchVector pointMappingCylinders;
00268 PlanePatches extractedPlanes;
00269 CylinderPatches extractedCylinders;
00270
00271 filterMsgSetupCloud(*pointCloud, msg, -mParams.mRho_max, mParams.mRho_max, mParams.mKinect, mParams.mWriteRawPic, mParams.mRawPicFilename, mParams.mRawPicCounter++);
00272
00273 doSegmentation(pointCloud, pointMappingPlanes, extractedPlanes, pointMappingCylinders, extractedCylinders);
00274 }
00275
00276
00277
00278 void StructureColoring::doSegmentation(PointCloudPtr pointCloud, PlanePatchVector& pointMapping, PlanePatches& extractedPlanes,
00279 CylinderPatchVector& pointMappingCylinders, CylinderPatches& extractedCylinders){
00280 double elapsedTime = 0.0;
00281 if(mPointCloudMutex.tryLock(100)){
00282 mPointCloud = pointCloud;
00283 mPointCloudMutex.unlock();
00284 }
00285
00286 if(mParams.mCylinder)
00287 {
00288 initSegmentation(pointMappingCylinders, *pointCloud);
00289 if (mParams.mPclSAC)
00290 elapsedTime = segmentCylindersSAC(extractedCylinders, pointMappingCylinders, *mOcTree, pointCloud);
00291 else
00292 elapsedTime = segmentCylinders(extractedCylinders, pointMappingCylinders, *mOcTree, pointCloud);
00293 }
00294 else
00295 {
00296 initSegmentation(pointMapping, *pointCloud);
00297 if (mParams.mPclSAC)
00298 elapsedTime = segmentPlanesSAC(extractedPlanes, pointMapping, pointCloud);
00299 else
00300 elapsedTime = segmentPlanes(extractedPlanes, pointMapping, *mOcTree, pointCloud);
00301 }
00302 if(mPointCloudMutex.tryLock(100)){
00303 mPointMapping = pointMapping;
00304 mPointCloudMutex.unlock();
00305 }
00306 if(mPlanePatchesMutex.tryLock(100)){
00307 if (mParams.mTextures) generateTextures(extractedPlanes, *pointCloud);
00308 mPlanePatches = extractedPlanes;
00309 mPlanePatchesMutex.unlock();
00310 }
00311 if(mCylinderPatchesMutex.tryLock(100)){
00312 mCylinderPatches = extractedCylinders;
00313 mCylinderPatchesMutex.unlock();
00314 }
00315
00316 if (std::strcmp(mParams.mRuntimeFilename.c_str(), "") != 0) writeTimeToFile(mParams.mRuntimeFilename, elapsedTime);
00317
00318 callPublisher(*mOcTree, extractedPlanes, extractedCylinders, pointCloud, pointMapping, pointMappingCylinders);
00319 }
00320
00321
00322
00323 void StructureColoring::pointCloudCallback(const sensor_msgs::PointCloudConstPtr& msg) {
00324 sensor_msgs::PointCloud2Ptr pc2msg(new sensor_msgs::PointCloud2());
00325 if (sensor_msgs::convertPointCloudToPointCloud2(*msg.get(), *pc2msg)){
00326 pcl::PointCloud<pcl::PointXYZ>::Ptr noColorPC(new pcl::PointCloud<pcl::PointXYZ>);
00327 pcl::fromROSMsg(*pc2msg, *noColorPC);
00328 PointCloudPtr colorPC(new PointCloud);
00329 colorPC->points.reserve(noColorPC->points.size());
00330 for(std::vector<pcl::PointXYZ, Eigen::aligned_allocator<pcl::PointXYZ> >::const_iterator pit = noColorPC->points.begin();
00331 pit != noColorPC->points.end(); ++pit){
00332 PointT p;
00333 p.x = pit->x;
00334 p.y = pit->y;
00335 p.z = pit->z;
00336 p.rgb = 0;
00337 colorPC->points.push_back(p);
00338 }
00339 sensor_msgs::PointCloud2Ptr cpcMsg(new sensor_msgs::PointCloud2());
00340 pcl::toROSMsg<PointT>(*colorPC, *cpcMsg);
00341 pointCloud2Callback(cpcMsg);
00342 }
00343 }
00344
00345
00346
00347 void StructureColoring::readPointCloudFromPCD(PointCloud& pointCloud, const std::string& filename, unsigned int& width,
00348 unsigned int& height, std::vector<unsigned int>& undefPoints, const float& zMax, const float& zMin){
00349 sensor_msgs::PointCloud2Ptr msg(new sensor_msgs::PointCloud2);
00350 pcl::PCDReader reader;
00351 if (mParams.mPCDnoRGB)
00352 {
00353 reader.read(filename, *msg);
00354 pcl::PointCloud<pcl::PointXYZ> my_pointCloud;
00355 pcl::fromROSMsg(*msg, my_pointCloud);
00356 pointCloud.points.reserve(my_pointCloud.points.size());
00357 for (size_t i = 0; i < my_pointCloud.points.size(); ++i)
00358 {
00359 PointT p;
00360 p.rgb = 0x00000000;
00361 p.x = my_pointCloud.points[i].x;
00362 p.y = my_pointCloud.points[i].y;
00363 p.z = my_pointCloud.points[i].z;
00364 pointCloud.points.push_back(p);
00365 }
00366 }
00367 else
00368 reader.read(filename, pointCloud);
00369 filterCloud(pointCloud, undefPoints, zMax, zMin);
00370 if (mParams.mPCDnoRGB)
00371 {
00372 width = pointCloud.points.size();
00373 height = 1;
00374 }
00375 else
00376 {
00377 width = pointCloud.width;
00378 height = pointCloud.height;
00379 }
00380 pointCloud.width = pointCloud.points.size();
00381 pointCloud.height = 1;
00382 ROS_INFO("read PointCloud with %zu points, width = %d, height = %d", pointCloud.points.size(), pointCloud.width, pointCloud.height);
00383 }
00384
00385
00386
00387 void StructureColoring::filterCloud(PointCloud& pointCloud, std::vector<unsigned int>& undefPoints, const float& zMax, const float& zMin){
00388 PointCloud pc2;
00389 pc2.header = pointCloud.header;
00390 pc2.points.swap(pointCloud.points);
00391 pointCloud.points.reserve(pc2.points.size());
00392 undefPoints.reserve(pc2.points.size());
00393 for(unsigned int i=0; i<pc2.points.size(); ++i){
00394 const PointT& p = pc2.points[i];
00395 if(std::isfinite(p.x) && std::isfinite(p.y) && std::isfinite(p.z) && p.z < zMax && p.z > zMin)
00396 pointCloud.points.push_back(p);
00397 else undefPoints.push_back(i);
00398 }
00399 }
00400
00401
00402
00403 void StructureColoring::getPointsFromFile() {
00404 PointCloudPtr pointCloud(new PointCloud());
00405 PlanePatchVector pointMappingPlanes;
00406 PlanePatches extractedPlanes;
00407 CylinderPatches extractedCylinders;
00408 CylinderPatchVector pointMappingCylinders;
00409
00410 unsigned int width = 512, height = 512;
00411 std::vector<unsigned int> undefPoints;
00412 ROS_INFO("reading file: %s", mParams.mInputFilename.c_str());
00413 if (mParams.mPerceptron)
00414 readPointCloudFromPERCEPTRONFile(*pointCloud, mParams.mInputFilename, width, height, undefPoints);
00415 else if (mParams.mLoadPCD)
00416 readPointCloudFromPCD(*pointCloud, mParams.mInputFilename, width, height, undefPoints, mParams.mRho_max, -mParams.mRho_max);
00417 else if (mParams.mABW)
00418 readPointCloudFromABWFile(*pointCloud, mParams.mInputFilename, width, height, undefPoints, mParams.mPGM);
00419 else
00420 {
00421 ROS_ERROR("unknown input type");
00422 return;
00423 }
00424
00425 mOcTree.reset(new OcTree(mParams.mMinOctreeResolution, mParams.mRho_max, mParams.mPrincipalVarFactor, mParams.mMinPointsForNormal,
00426 mParams.mCurvThreshold, mParams.mCurv2Threshold, mParams.mSqDistFactor, width * height));
00427
00428 if (mVis) mVis->publishPointCloud(*pointCloud);
00429
00430 doSegmentation(pointCloud, pointMappingPlanes, extractedPlanes, pointMappingCylinders, extractedCylinders);
00431
00432 if(mParams.mCylinder)
00433 {
00434 writeSegments(width, height, undefPoints, pointMappingCylinders, mParams.mOutputFilename, extractedCylinders);
00435 }
00436 else
00437 {
00438 writeSegments(width, height, undefPoints, pointMappingPlanes, mParams.mOutputFilename, extractedPlanes);
00439 }
00440
00441 if(!mParams.mLoadPCD) writeMSNormals(mParams.mNormalOutputFilename, extractedPlanes);
00442 size_t unsegPoints = 0;
00443 for(PlanePatchVector::const_iterator ppvit = pointMappingPlanes.begin(); ppvit != pointMappingPlanes.end(); ++ppvit)
00444 {
00445 if (!*ppvit) ++unsegPoints;
00446 }
00447 ROS_INFO("number of undefined points = %zu, number of unsegmented points = %zu", undefPoints.size(), unsegPoints);
00448 }
00449
00450
00451
00452 double StructureColoring::segmentPlanes(PlanePatches& extractedPlanes, PlanePatchVector& pointMapping, OcTree& octree,
00453 const PointCloudPtr& pointCloud) {
00454 double start = pcl::getTime();
00455
00456 octree.buildOctree(*pointCloud);
00457 extractOctreePlanesFromCloud(extractedPlanes, pointMapping, octree, pointCloud);
00458
00459
00460 if (mParams.mPostProcessing & 1) {
00461 mergePlanes(extractedPlanes, pointMapping, *pointCloud);
00462 }
00463 if (mParams.mPostProcessing & 2) {
00464 spreadNodes(octree, extractedPlanes, pointMapping, *pointCloud);
00465 }
00466 if (mParams.mPostProcessing & 4)
00467 mergePlanes(extractedPlanes, pointMapping, *pointCloud);
00468
00469 double end = pcl::getTime();
00470 double elapsedTime = end - start;
00471 ROS_ERROR("response sent %f seconds after receiving pointcloud", elapsedTime);
00472 return elapsedTime;
00473 }
00474
00475
00476
00477 double StructureColoring::segmentCylinders(CylinderPatches& cylinderPatches, CylinderPatchVector& pointMapping, OcTree& octree, const PointCloudPtr& pointCloud){
00478 double start = pcl::getTime();
00479
00480 octree.buildOctree(*pointCloud);
00481
00482 extractOctreeCylindersFromCloud(cylinderPatches, pointMapping, octree, pointCloud);
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495 ROS_INFO("%zu cylinders left", cylinderPatches.size());
00496
00497 double end = pcl::getTime();
00498 double elapsedTime = end - start;
00499 ROS_ERROR("response sent %f seconds after receiving pointcloud", elapsedTime);
00500 return elapsedTime;
00501 }
00502
00503
00504
00505 void StructureColoring::generateTextures(const PlanePatches& extractedPlanes, const PointCloud& pointCloud){
00506 for (PlanePatches::const_iterator plane_it = extractedPlanes.begin(); plane_it != extractedPlanes.end(); ++plane_it){
00507 (*plane_it)->computeRGBTextureMap(pointCloud, mParams.mTexelSizeFactor, mParams.mDilateIterations);
00508 }
00509 }
00510
00511
00512
00513 void StructureColoring::callPublisher(const OcTree& octree, PlanePatches& extractedPlanes, const CylinderPatches& extractedCylinders , const PointCloudPtr pointCloud,
00514 const PlanePatchVector& pointMapping, const CylinderPatchVector& cylinderPointMapping) {
00515 CompArea compArea;
00516 extractedPlanes.sort(compArea);
00517 if (mVis) {
00518 mVis->publishOctreeNormalMarker(octree);
00519 mVis->publishPlaneMarker(extractedPlanes);
00520 mVis->publishSegmentedPointcloud(pointMapping, extractedPlanes, *pointCloud);
00521 mVis->publishSegmentedPointcloud(cylinderPointMapping, extractedCylinders, *pointCloud);
00522 mVis->publishPointCloud(*pointCloud);
00523 if(mParams.mCylinder){
00524 mVis->publishCylinderMarker(extractedCylinders);
00525 mVis->publishCylinderPoints(extractedCylinders, pointCloud);
00526 }
00527 }
00528 }
00529
00530
00531
00532 void writePicFromPointCloud(const PointCloud& cloudWithFarPoints, const std::string& rawPicFilename){
00533 cv::Mat pic(cloudWithFarPoints.height, cloudWithFarPoints.width, CV_8UC3);
00534 for(unsigned int r = 0; r < cloudWithFarPoints.height; ++r){
00535 for(unsigned int c = 0; c < cloudWithFarPoints.width; ++c){
00536 pcl::PointXYZRGB pointrgb = cloudWithFarPoints.points[r * cloudWithFarPoints.width + c];
00537 if(std::isfinite(pointrgb.x) && std::isfinite(pointrgb.y) && std::isfinite(pointrgb.z)){
00538 float rgbfloat = pointrgb.rgb;
00539 int rgbint = *reinterpret_cast<int*> (&rgbfloat);
00540 pic.at<cv::Vec3b>(r, c)[0] = rgbint & 0xff;
00541 pic.at<cv::Vec3b>(r, c)[1] = (rgbint >> 8) & 0xff;
00542 pic.at<cv::Vec3b>(r, c)[2] = (rgbint >> 16) & 0xff;
00543 } else {
00544 pic.at<cv::Vec3b>(r, c)[0] = 0.f;
00545 pic.at<cv::Vec3b>(r, c)[1] = 0.f;
00546 pic.at<cv::Vec3b>(r, c)[2] = 0.f;
00547 }
00548 }
00549 }
00550 std::string tmpfn(rawPicFilename);
00551 tmpfn.append(".ras");
00552 ROS_INFO("%s", tmpfn.c_str());
00553 cv::imwrite(tmpfn, pic);
00554 }
00555
00556
00557
00558 template<typename PointCloudType>
00559 void writePCDFromPointCloud(const PointCloudType& cloudWithFarPoints, const std::string& rawPicFilename){
00560 pcl::PCDWriter writer;
00561 std::string tmpfn(rawPicFilename);
00562 tmpfn.append(".pcd");
00563 writer.write(tmpfn, cloudWithFarPoints, false);
00564 }
00565
00566
00567
00568 void StructureColoring::filterMsgSetupCloud(PointCloud& pointCloud, const sensor_msgs::PointCloud2ConstPtr& pMsg, double zMin, double zMax, bool fromKinect, bool writePic, const std::string& picFilename, unsigned int picCounter) {
00569 PointCloud::Ptr cloudWithFarPoints(new PointCloud());
00570 pcl::fromROSMsg(*pMsg, *cloudWithFarPoints);
00571 if(writePic){
00572 std::string tmpPicFilename(picFilename);
00573 char counterChars[10];
00574 sprintf(counterChars, "%d", picCounter++);
00575 tmpPicFilename.append(counterChars);
00576 writePicFromPointCloud(*cloudWithFarPoints, tmpPicFilename);
00577 writePCDFromPointCloud(*cloudWithFarPoints, tmpPicFilename);
00578 }
00579
00580
00581 pcl::PassThrough<PointT> pass;
00582 pass.setInputCloud(cloudWithFarPoints);
00583 pass.setFilterFieldName("z");
00584 pass.setFilterLimits(zMin, zMax);
00585 pass.filter(pointCloud);
00586 pointCloud.width = pointCloud.points.size();
00587 pointCloud.height = 1;
00588
00589 if (mParams.mKinect) {
00590 #pragma omp parallel for schedule(dynamic,1)
00591 for (size_t i = 0; i < pointCloud.points.size(); ++i) {
00592 PointT& p = pointCloud.points[i];
00593 PointT tmp = p;
00594 p.x = tmp.z;
00595 p.y = -tmp.x;
00596 p.z = -tmp.y;
00597 }
00598 }
00599 }
00600
00601
00602
00603 void StructureColoring::filterConnectedNodes(std::vector<NodePointers>& CCInlierNodes, const Plane3D& pp,
00604 const NodePointers& octreeNodes, float cellSize, GridMap* oldGridMap, NodePointers* notConnectedNodesOutput) {
00605 float nodeXMin = std::numeric_limits<float>::max();
00606 float nodeXMax = -std::numeric_limits<float>::max();
00607 float nodeYMin = std::numeric_limits<float>::max();
00608 float nodeYMax = -std::numeric_limits<float>::max();
00609 for(NodePointers::const_iterator npit = octreeNodes.begin(); npit != octreeNodes.end(); ++npit){
00610 Vec3 tp(pp.transformToXYPlane((*npit)->value_.meanPos));
00611 if (tp.x() < nodeXMin) nodeXMin = tp.x();
00612 if (tp.x() > nodeXMax) nodeXMax = tp.x();
00613 if (tp.y() < nodeYMin) nodeYMin = tp.y();
00614 if (tp.y() > nodeYMax) nodeYMax = tp.y();
00615 }
00616 GridMap gridmap;
00617 if (oldGridMap == NULL){
00618 gridmap = GridMap(pp, nodeXMin, nodeXMax, nodeYMin, nodeYMax, cellSize);
00619 }
00620 else {
00621 float xMin = 0.f, xMax = 0.f, yMin = 0.f, yMax = 0.f;
00622 unsigned int xOff = 0, xAdd = 0, yOff = 0, yAdd = 0;
00623 oldGridMap->getNewExtremes(xMin, xMax, yMin, yMax, xOff, xAdd, yOff, yAdd, nodeXMin, nodeXMax, nodeYMin, nodeYMax, cellSize);
00624 gridmap = GridMap(*oldGridMap, xMin, xMax, yMin, yMax, xOff, xAdd, yOff, yAdd, cellSize);
00625 }
00626
00627 gridmap.populate(octreeNodes);
00628
00629 if (!oldGridMap)
00630 {
00631 gridmap.calculateStartPos();
00632 }
00633 gridmap.getConnectedComponentsAndNotConnectedNodes(CCInlierNodes, octreeNodes, cellSize, mParams.mMinOctreeNodes,
00634 mParams.mMinNodesInCC, notConnectedNodesOutput);
00635 }
00636
00637
00638
00639 void StructureColoring::estimatePlane(const pclPointIndices::Ptr& inliers, PlanePatchPtr& planePatch, const PointCloudPtr& pointCloud,
00640 const float& sacDist) {
00641 pcl::ModelCoefficients coefficients;
00642 pcl::SACSegmentation<PointT> seg;
00643 pclPointIndices newInliers;
00644
00645 seg.setOptimizeCoefficients(true);
00646 seg.setModelType(pcl::SACMODEL_PLANE);
00647 seg.setMethodType(pcl::SAC_MSAC);
00648 seg.setMaxIterations(100);
00649 seg.setDistanceThreshold(sacDist);
00650 seg.setInputCloud(pointCloud);
00651 seg.setIndices(inliers);
00652 assert(inliers->indices.size() > 3);
00653 seg.segment(newInliers, coefficients);
00654
00655 if (newInliers.indices.size() == 0) {
00656 planePatch.reset();
00657 } else {
00658 Vec3 norm(coefficients.values[0], coefficients.values[1], coefficients.values[2]);
00659
00660
00661
00662 float distance = -1.f * coefficients.values[3];
00663 if (distance < 0) {
00664 distance *= -1.f;
00665 norm *= -1.f;
00666 }
00667 planePatch.reset(new PlanePatch(norm, distance, newInliers.indices, *pointCloud, sacDist));
00668 }
00669 }
00670
00671 void StructureColoring::estimateCylinder(pclPointIndices& inliers, CylinderPatchPtr& cylinder, unsigned int& numPointsBefRansac, const NodePointers& octreeNodes,
00672 const OcTree& octree, const PointCloudPtr& pointCloud, const float& sacDist,
00673 const float& normalDistanceWeight, const float& minRadius, const float& maxRadius) {
00674 pcl::ModelCoefficients coefficients;
00675 pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg;
00676 typedef pcl::PointCloud<pcl::Normal> NormalCloud;
00677 NormalCloud::Ptr normalCloud(new NormalCloud());
00678 pclPointIndices partCloudIndices;
00679 PointCloudPtr partCloud(new PointCloud());
00680
00681 for(NodePointers::const_iterator np_it = octreeNodes.begin(); np_it != octreeNodes.end(); ++np_it){
00682 PointIndices inlierPoints;
00683 getAllPointsFromNodes(inlierPoints, NodePointers(1, *np_it), octree, *pointCloud);
00684 partCloudIndices.indices.reserve(partCloudIndices.indices.size() + inlierPoints.size());
00685 copy(inlierPoints.begin(), inlierPoints.end(), back_inserter( partCloudIndices.indices));
00686 }
00687 numPointsBefRansac = partCloudIndices.indices.size();
00688
00689 pcl::ExtractIndices<PointT> extract;
00690 extract.setInputCloud(pointCloud);
00691 extract.setIndices(boost::make_shared<pclPointIndices>(partCloudIndices));
00692 extract.setNegative(false);
00693 extract.filter(*partCloud);
00694
00695 normalCloud->points.reserve(partCloud->points.size());
00696 for(unsigned int p_count = 0; p_count < partCloud->points.size(); ++p_count){
00697 Vec3 point(partCloud->points[p_count].x, partCloud->points[p_count].y, partCloud->points[p_count].z);
00698 Vec3 eNormal;
00699 octree.findFinestNormal(eNormal, point);
00700 pcl::Normal normal;
00701 normal.normal_x = eNormal.x();
00702 normal.normal_y = eNormal.y();
00703 normal.normal_z = eNormal.z();
00704 normalCloud->points.push_back(normal);
00705 }
00706 assert(partCloud->points.size() == normalCloud->points.size());
00707
00708 seg.setOptimizeCoefficients(true);
00709 seg.setModelType(pcl::SACMODEL_CYLINDER);
00710 seg.setMethodType(pcl::SAC_RANSAC);
00711 seg.setMaxIterations(100);
00712 seg.setDistanceThreshold(sacDist);
00713 seg.setNormalDistanceWeight(normalDistanceWeight);
00714 seg.setRadiusLimits (minRadius, maxRadius);
00715 seg.setInputCloud(partCloud);
00716 seg.setInputNormals(normalCloud);
00717 seg.segment(inliers, coefficients);
00718
00719 if(inliers.indices.size() == 0){
00720 ROS_ERROR("RANSAC: no cylinder found, aborting");
00721 cylinder.reset();
00722 return;
00723 }
00724 Vec3 pointOnAxis(coefficients.values[0], coefficients.values[1], coefficients.values[2]);
00725 Vec3 axisDirection(coefficients.values[3], coefficients.values[4], coefficients.values[5]);
00726 float radius(coefficients.values[6]);
00727 const PointIndices& inlierIndices(inliers.indices);
00728 cylinder.reset(new CylinderPatch(inlierIndices, *partCloud, pointOnAxis, axisDirection, radius, sacDist));
00729 }
00730
00731 void StructureColoring::estimateCylinderNNNormals(CylinderPatchPtr& cylinder,
00732 const PointCloudPtr& pointCloud, const float& sacDist,
00733 const float& normalDistanceWeight, const float& minRadius,
00734 const float& maxRadius, float searchRadius, const Vec3& initialAxis) {
00735 pcl::ModelCoefficients coefficients;
00736 typedef pcl::PointCloud<pcl::Normal> NormalCloud;
00737 NormalCloud::Ptr normalCloud(new NormalCloud);
00738 pclPointIndices::Ptr partCloudIndices(new pclPointIndices());
00739 pclPointIndices::Ptr subsampledPartCloudIndices(new pclPointIndices());
00740 pclPointIndices subsampledInliers, inliers;
00741 PointCloudPtr partCloud(new PointCloud());
00742
00743 PointCloudPtr subsampledPartCloud(new PointCloud());
00744 PointCloudPtr subsampledCloud(new PointCloud());
00745
00746 copy(cylinder->getInliers().begin(), cylinder->getInliers().end(), back_inserter(partCloudIndices->indices));
00747
00748 const float subsamplingLeafSize = 0.5f*0.5f*searchRadius;
00749
00750 if( subsamplingLeafSize > 2.f*mParams.mMinOctreeResolution ) {
00751
00752 ROS_ERROR("downsampling");
00753
00754 pcl::VoxelGrid<PointT> subsampler1, subsampler2;
00755 subsampler1.setSaveLeafLayout(true);
00756 subsampler1.setInputCloud( pointCloud );
00757 subsampler1.setLeafSize( subsamplingLeafSize, subsamplingLeafSize, subsamplingLeafSize );
00758 subsampler1.setSaveLeafLayout(true);
00759 subsampler1.filter( *subsampledCloud );
00760 subsampler1.setSaveLeafLayout(true);
00761
00762
00763
00764 std::map< int, std::vector< int > > cylinderSubsamplingMap;
00765 std::set< int > subsampledPartCloudIndicesSet;
00766 for( unsigned int i = 0; i < partCloudIndices->indices.size(); i++ ) {
00767 const PointT& p = pointCloud->points[ partCloudIndices->indices[i] ];
00768 int subsampledIndex = subsampler1.getCentroidIndexAt( subsampler1.getGridCoordinates( p.x, p.y, p.z ) );
00769 if( subsampledIndex >= 0 ) {
00770 subsampledPartCloudIndicesSet.insert( subsampledIndex );
00771 cylinderSubsamplingMap[subsampledIndex].push_back( partCloudIndices->indices[i] );
00772 }
00773 }
00774
00775 for( std::set<int>::iterator it = subsampledPartCloudIndicesSet.begin(); it != subsampledPartCloudIndicesSet.end(); ++it ) {
00776 subsampledPartCloudIndices->indices.push_back( *it );
00777 }
00778
00779 pcl::ExtractIndices<PointT> extract;
00780 extract.setInputCloud(subsampledCloud);
00781 extract.setIndices(subsampledPartCloudIndices);
00782 extract.setNegative(false);
00783 extract.filter(*subsampledPartCloud);
00784
00785 pcl::NormalEstimation<PointT, pcl::Normal> ne;
00786 ne.setIndices(subsampledPartCloudIndices);
00787 ne.setInputCloud (subsampledCloud);
00788
00789
00790
00791 pcl::search::KdTree<pcl::PointXYZRGB>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZRGB>);
00792 tree->setInputCloud (subsampledCloud);
00793 ne.setSearchMethod (tree);
00794
00795
00796 ne.setRadiusSearch (searchRadius);
00797 ne.setViewPoint(0.f, 0.f, 0.f);
00798
00799
00800 ne.compute (*normalCloud);
00801
00802 assert(subsampledPartCloud->points.size() == normalCloud->points.size());
00803
00804 pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg;
00805 seg.setOptimizeCoefficients(false);
00806 seg.setModelType(pcl::SACMODEL_CYLINDER);
00807 seg.setMethodType(pcl::SAC_RANSAC);
00808 seg.setMaxIterations(10000);
00809 seg.setDistanceThreshold(sacDist);
00810 seg.setNormalDistanceWeight(normalDistanceWeight);
00811 seg.setRadiusLimits (minRadius, maxRadius);
00812 (void)initialAxis;
00813 seg.setInputCloud(subsampledPartCloud);
00814 seg.setInputNormals(normalCloud);
00815 seg.segment(subsampledInliers, coefficients);
00816
00817
00818 PointIndices inlierIndices;
00819
00820 for( unsigned int i = 0; i< subsampledInliers.indices.size(); i++ ) {
00821 int subsampledCloudIndex = subsampledPartCloudIndices->indices[ subsampledInliers.indices[i] ];
00822 if( cylinderSubsamplingMap.find( subsampledCloudIndex ) == cylinderSubsamplingMap.end() ) {
00823 continue;
00824 }
00825 copy(cylinderSubsamplingMap[subsampledCloudIndex].begin(), cylinderSubsamplingMap[subsampledCloudIndex].end(), back_inserter(inlierIndices));
00826 }
00827
00828 if(inlierIndices.size() < mParams.mMinPointsInCC){
00829 ROS_ERROR("RANSAC: no cylinder found, aborting");
00830 cylinder.reset();
00831 return;
00832 }
00833
00834 Vec3 pointOnAxis(coefficients.values[0], coefficients.values[1], coefficients.values[2]);
00835 Vec3 axisDirection(coefficients.values[3], coefficients.values[4], coefficients.values[5]);
00836 float radius(coefficients.values[6]);
00837 cylinder.reset(new CylinderPatch(inlierIndices, *pointCloud, pointOnAxis, axisDirection, radius, sacDist));
00838
00839 }
00840 else {
00841
00842 pcl::ExtractIndices<PointT> extract;
00843 extract.setInputCloud(pointCloud);
00844 extract.setIndices(partCloudIndices);
00845 extract.setNegative(false);
00846 extract.filter(*partCloud);
00847
00848 pcl::NormalEstimation<PointT, pcl::Normal> ne;
00849 ne.setIndices(partCloudIndices);
00850 ne.setInputCloud (pointCloud);
00851
00852
00853
00854 pcl::search::KdTree<pcl::PointXYZRGB>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZRGB>);
00855 tree->setInputCloud (pointCloud);
00856 ne.setSearchMethod (tree);
00857
00858
00859 ne.setRadiusSearch (searchRadius);
00860 ne.setViewPoint(0.f, 0.f, 0.f);
00861
00862 ros::Time startTime = ros::Time::now();
00863
00864 ne.compute (*normalCloud);
00865 ROS_ERROR("ne took %lf", (ros::Time::now()-startTime).toSec());
00866
00867 assert(partCloud->points.size() == normalCloud->points.size());
00868
00869 pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg;
00870 seg.setOptimizeCoefficients(false);
00871 seg.setModelType(pcl::SACMODEL_CYLINDER);
00872 seg.setMethodType(pcl::SAC_RANSAC);
00873 seg.setMaxIterations(10000);
00874
00875 seg.setDistanceThreshold(sacDist);
00876 seg.setNormalDistanceWeight(normalDistanceWeight);
00877 seg.setRadiusLimits (minRadius, maxRadius);
00878 (void)initialAxis;
00879
00880
00881 seg.setInputCloud(partCloud);
00882 seg.setInputNormals(normalCloud);
00883 seg.segment(inliers, coefficients);
00884
00885 if(inliers.indices.size() < mParams.mMinPointsInCC){
00886 ROS_ERROR("RANSAC: no cylinder found, aborting");
00887 cylinder.reset();
00888 return;
00889 }
00890 PointIndices inlierIndices;
00891 inlierIndices.reserve(inliers.indices.size());
00892 for(PointIndices::const_iterator pit = inliers.indices.begin(); pit != inliers.indices.end(); ++pit){
00893 inlierIndices.push_back(cylinder->getInliers()[*pit]);
00894 }
00895 Vec3 pointOnAxis(coefficients.values[0], coefficients.values[1], coefficients.values[2]);
00896 Vec3 axisDirection(coefficients.values[3], coefficients.values[4], coefficients.values[5]);
00897 float radius(coefficients.values[6]);
00898 cylinder.reset(new CylinderPatch(inlierIndices, *pointCloud, pointOnAxis, axisDirection, radius, sacDist));
00899
00900 }
00901 }
00902
00903
00904 void StructureColoring::estimateCylinderFromNodes(NodeIndices& inliers, CylinderPatchPtr& cylinder, const NodePointers& octreeNodes,
00905 const OcTree& octree, const PointCloudPtr& pointCloud, const float& sacDist, const float& normalDistanceWeight,
00906 const float& minRadius, const float& maxRadius) {
00907 pcl::ModelCoefficients coefficients;
00908 pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg;
00909 typedef pcl::PointCloud<pcl::Normal> NormalCloud;
00910 NormalCloud::Ptr normalCloud(new NormalCloud());
00911 pclPointIndices partCloudIndices;
00912 PointCloudPtr partCloud;
00913
00914 partCloud->points.reserve(octreeNodes.size());
00915 normalCloud->points.reserve(octreeNodes.size());
00916 PointT point;
00917 pcl::Normal normal;
00918 for(NodePointers::const_iterator np_it = octreeNodes.begin(); np_it != octreeNodes.end(); ++np_it){
00919 point.x = (*np_it)->value_.meanPos.x();
00920 point.y = (*np_it)->value_.meanPos.y();
00921 point.z = (*np_it)->value_.meanPos.z();
00922 partCloud->points.push_back(point);
00923 normal.normal_x = (*np_it)->value_.normal.x();
00924 normal.normal_y = (*np_it)->value_.normal.y();
00925 normal.normal_z = (*np_it)->value_.normal.z();
00926 normalCloud->points.push_back(normal);
00927 }
00928
00929 seg.setOptimizeCoefficients(true);
00930 seg.setModelType(pcl::SACMODEL_CYLINDER);
00931 seg.setMethodType(pcl::SAC_RANSAC);
00932 seg.setMaxIterations(100);
00933 seg.setDistanceThreshold(sacDist);
00934 seg.setNormalDistanceWeight(0.0f);
00935 seg.setRadiusLimits (minRadius, maxRadius);
00936 seg.setInputCloud(partCloud);
00937 seg.setInputNormals(normalCloud);
00938 seg.segment(partCloudIndices, coefficients);
00939
00940 if(partCloudIndices.indices.size() == 0){
00941 ROS_ERROR("RANSAC: no cylinder found, aborting");
00942 cylinder.reset();
00943 return;
00944 }
00945 Vec3 pointOnAxis(coefficients.values[0], coefficients.values[1], coefficients.values[2]);
00946 Vec3 axisDirection(coefficients.values[3], coefficients.values[4], coefficients.values[5]);
00947 float radius(coefficients.values[6]);
00948 PointIndices inlierIndices;
00949 getAllPointsFromNodes(inlierIndices, partCloudIndices.indices, octreeNodes, octree, *pointCloud);
00950 cylinder.reset(new CylinderPatch(inlierIndices, *partCloud, pointOnAxis, axisDirection, radius, sacDist));
00951 }
00952
00953
00954
00955 void StructureColoring::generateSingleSphereOctreeHTBins(SphereUniformSamplings& houghBins, SphereBinsVec& pointNeighborBins,
00956 const NodePointers& octreeNodes) {
00957 houghBins = SphereUniformSamplings(1, SphereUniformSampling(mParams.mPhi_resolution, true));
00958 pointNeighborBins.clear();
00959 pointNeighborBins.resize(octreeNodes.size());
00960 float angleEps = mParams.mAngleEps;
00961 for (unsigned int node = 0; node < octreeNodes.size(); node++) {
00962 SphereBins neighborBins;
00963 if (octreeNodes[node]->value_.stable) {
00964 float curv = octreeNodes[node]->value_.curv / mParams.mCurvThreshold;
00965
00966 float weight = octreeNodes[node]->value_.numPoints * (1.f - curv);
00967 const Vec3& n = octreeNodes[node]->value_.normal;
00968 std::vector<Pair2ui> neighbors;
00969 houghBins[0].getAllAngleNeighbors(neighbors, n, angleEps);
00970 for (unsigned int neighbor = 0; neighbor < neighbors.size(); neighbor++) {
00971 Vec3 norm = houghBins[0].getNormal(neighbors[neighbor].first, neighbors[neighbor].second);
00972 assert((norm.x()!=0)||(norm.y()!=0)||(norm.z()!=0));
00973 SphereBin pb;
00974 pb.rho = 0;
00975 pb.i = neighbors[neighbor].first;
00976 pb.j = neighbors[neighbor].second;
00977 float scale = std::min(1.f, std::max(0.001f, (fabsf(norm.dot(n)) - fabsf(cos(
00978 angleEps))) / fabsf(cos(angleEps))));
00979 houghBins[pb.rho].addPointNoNeighbors(pb.pit, pb.wit, pb.i, pb.j, node, weight * scale);
00980 neighborBins.push_back(pb);
00981 }
00982 }
00983 pointNeighborBins[node] = neighborBins;
00984 }
00985 }
00986
00987
00988
00989 void StructureColoring::estimatePlaneSingleSphereOctreeHT(pclPointIndices& inliers, Plane3DPtr& planePatch,
00990 const SphereUniformSamplings& houghBins, const SphereBinsVec& pointNeighborBins, const NodePointers& octreeNodes,
00991 NodeIndices& inlierNodes, const PointCloud& pointCloud, const OcTree& octree, const unsigned int& depth,
00992 const PlanePatchVector& pointMapping) {
00993 unsigned int imax = 0, jmax = 0;
00994 houghBins[0].getMaximum(imax, jmax);
00995 NodeIndices nodes;
00996 houghBins[0].getPoints(nodes, imax, jmax);
00997
00998
00999
01000 float HTDistThresh = getHTDistanceFromDepth(depth, octree);
01001 TriangleDistributionHistogram hist(-mParams.mRho_max, mParams.mRho_max, HTDistThresh, mParams.mHTDistanceDeviationFactor * HTDistThresh, pointMapping.size());
01002 Vec3 n(0.f, 0.f, 0.f);
01003 float sumWeight = 0.f;
01004 for (unsigned int p = 0; p < nodes.size(); p++) {
01005 float weight = 0.f;
01006 const SphereBins& bins = pointNeighborBins[nodes[p]];
01007 for (SphereBins::const_iterator sbit = bins.begin(); sbit != bins.end(); ++sbit)
01008 if (sbit->i == imax && sbit->j == jmax)
01009 weight = *(sbit->wit);
01010 sumWeight += weight;
01011 n += octreeNodes[nodes[p]]->value_.normal * weight;
01012 }
01013 n /= sumWeight;
01014
01015 for (unsigned int p = 0; p < nodes.size(); p++) {
01016 Vec3 pos = octreeNodes[nodes[p]]->value_.meanPos;
01017 float x = pos.x() * n.x();
01018 float y = pos.y() * n.y();
01019 float z = pos.z() * n.z();
01020 float rho = x + y + z;
01021 if (rho > mParams.mRho_max)
01022 ROS_ERROR("HT: Plane out of distance %f(%f)",rho, mParams.mRho_max);
01023 if (rho < -mParams.mRho_max)
01024 ROS_ERROR("HT: Plane out of distance %f(%f)", rho, -mParams.mRho_max);
01025 hist.addDistributedToBins(rho, WeightedIdx(1.f, nodes[p]));
01026 }
01027
01028 WeightedIdxVector bestBin;
01029 float bestRho;
01030 CompareWeightedIdxVector compareWeightedIdxVector;
01031 hist.getMaxBin(bestBin, bestRho, compareWeightedIdxVector);
01032
01033
01034 inliers.indices.clear();
01035 inliers.indices.reserve(bestBin.nodeIndices().size());
01036 inlierNodes.clear();
01037 inlierNodes.reserve(bestBin.nodeIndices().size());
01038 for (unsigned int p = 0; p < bestBin.nodeIndices().size(); p++) {
01039 inlierNodes.push_back(bestBin.nodeIndices()[p]);
01040 NodePointers all_nodes;
01041 octree.getAllLeafs(all_nodes, octreeNodes[bestBin.nodeIndices()[p]]);
01042 assert(octree.checkOctreeNodes(all_nodes));
01043 assert(octree.checkOctreeNodes(octreeNodes));
01044 for (unsigned int node = 0; node < all_nodes.size(); node++) {
01045 const PointIndices& nodepoints = octree.getPointsFromNodeId(all_nodes[node]->value_.id);
01046 for (PointIndices::const_iterator point_it = nodepoints.begin(); point_it != nodepoints.end(); ++point_it) {
01047 if ((!pointMapping[*point_it])) {
01048 inliers.indices.push_back(*point_it);
01049 }
01050 }
01051 }
01052 }
01053 if (inliers.indices.size() >= 3) {
01054 Vec3 normal = houghBins[0].getNormal(imax, jmax);
01055 float distance = bestRho;
01056 if (distance < 0) {
01057 distance *= -1.f;
01058 normal *= -1.f;
01059 }
01060 planePatch.reset(new Plane3D(normal, distance));
01061 } else {
01062 planePatch.reset();
01063 inliers.indices.clear();
01064 }
01065 }
01066
01067
01070 void StructureColoring::updateHTBins(const NodeIndices& inliers, SphereUniformSamplings& houghBins, SphereBinsVec& pointNeighborBins) {
01071 for (NodeIndices::const_iterator in_it = inliers.begin(); in_it != inliers.end(); ++in_it) {
01072 const SphereBins& neighborBins = pointNeighborBins[*in_it];
01073 for (SphereBins::const_iterator nb_it = neighborBins.begin(); nb_it != neighborBins.end(); ++nb_it) {
01074 assert(nb_it->rho < houghBins.size());
01075 houghBins[nb_it->rho].deletePointNWeightAt(nb_it->pit, nb_it->wit, nb_it->i, nb_it->j);
01076 }
01077 pointNeighborBins[*in_it].clear();
01078 }
01079 }
01080
01081
01082
01083 void StructureColoring::generateNeighboringNodePairs(NodePairs& nodePairs, NodePointers& octreeNodes,
01084 const unsigned int neighborhoodSize, const unsigned int& currentOctreeDepth, const OcTree& octree) {
01085 for (NodePointers::iterator node_it = octreeNodes.begin(); node_it
01086 != octreeNodes.end(); ++node_it) {
01087 if ((*node_it)->value_.stable) {
01088 NodePointers neighborNodes;
01089
01090 octree.getNeighboringNodes(neighborNodes, *node_it,
01091 currentOctreeDepth, (float)neighborhoodSize);
01092 for (NodePointers::const_iterator neighborNode_it =
01093 neighborNodes.begin(); neighborNode_it
01094 != neighborNodes.end(); ++neighborNode_it) {
01095 if ((*neighborNode_it)->value_.stable) {
01096 NodePair pair(*node_it, *neighborNode_it, mParams.mAngleEps);
01097 nodePairs.push_back(pair);
01098 }
01099 }
01100 }
01101 (*node_it)->value_.nodeQuerried = true;
01102 }
01103 }
01104
01105
01106
01107 void StructureColoring::generateCylinderOrientationHB(
01108 SphereUniformSampling& cylinderOrientationBins,
01109 SphereBinsVec& binNeighborhood, const NodePairs& nodePairs) {
01110 cylinderOrientationBins = SphereUniformSampling(mParams.mPhi_resolution);
01111 binNeighborhood.clear();
01112 binNeighborhood.resize(nodePairs.size());
01113 float angleEps = mParams.mAngleEps;
01114 #pragma omp parallel for schedule(dynamic,1)
01115 for (unsigned int i = 0; i < nodePairs.size(); ++i) {
01116 SphereBins neighborBins;
01117 const Vec3& n = nodePairs[i].getCross();
01118
01119 float curv = nodePairs[i].getCrossCurv() / mParams.mCurvThreshold;
01120 float weight = nodePairs[i].getNumPoints() * (1.f - curv);
01121 std::vector<Pair2ui> neighbors;
01122 cylinderOrientationBins.getAllAngleNeighbors(neighbors, n, angleEps);
01123 for (unsigned int neighbor = 0; neighbor < neighbors.size(); neighbor++) {
01124 Vec3 norm = cylinderOrientationBins.getNormal(
01125 neighbors[neighbor].first, neighbors[neighbor].second);
01126 assert((norm.x()!=0)||(norm.y()!=0)||(norm.z()!=0));
01127 SphereBin pb;
01128 pb.rho = 0;
01129 pb.i = neighbors[neighbor].first;
01130 pb.j = neighbors[neighbor].second;
01131 float scale = std::min(1.f, std::max(0.001f, (fabsf(norm.dot(n))
01132 - fabsf(cos(angleEps))) / fabsf(cos(angleEps))));
01133 #pragma omp critical
01134 cylinderOrientationBins.addPointNoNeighbors(pb.pit, pb.wit, pb.i,
01135 pb.j, i, weight * scale);
01136 neighborBins.push_back(pb);
01137 }
01138 binNeighborhood[i] = neighborBins;
01139 }
01140 }
01141
01142
01143
01144 void StructureColoring::estimateCylinderHT(const SphereUniformSampling& cylinderOrientationBins, const NodePairs& nodePairs,
01145 PairIndices& inlierIndices, CylinderPatchPtr& cylinder, const unsigned int& depth, const OcTree& octree,
01146 const CylinderPatchVector& pointMapping, const PointCloud& pointCloud, const float& minRadius, const float& maxRadius) {
01147 unsigned int imax = 0, jmax = 0;
01148 cylinderOrientationBins.getMaximum(imax, jmax);
01149 PairIndices pairIndices;
01150 std::vector<float> weights;
01151 cylinderOrientationBins.getPoints(pairIndices, imax, jmax, &weights);
01152
01153 Vec3 cylinderOrientation(0.f, 0.f, 0.f);
01154 float weightSum = 0.f;
01155 for(PairIndices::const_iterator pairInd_it = pairIndices.begin(); pairInd_it != pairIndices.end();++pairInd_it){
01156 cylinderOrientation += nodePairs[*pairInd_it].getCross() * weights[pairInd_it-pairIndices.begin()];
01157 weightSum += weights[pairInd_it-pairIndices.begin()];
01158 }
01159 cylinderOrientation /= weightSum;
01160
01161 Mat3 transformation;
01162 float cylinderRotationAngle;
01163 Vec3 cylinderRotationAxis;
01164 NodePair2D::get2DTransformation(transformation, cylinderRotationAngle, cylinderRotationAxis, cylinderOrientation);
01165
01166
01167
01168 unsigned int cylinderBins = mParams.mCylinderBins;
01169 float radBinSize = (maxRadius - minRadius) / (float)cylinderBins;
01170 float devFactor = mParams.mRadiusDevFactor;
01171 TriangleDistributionHistogramWithRemove radiusHistogram(minRadius, maxRadius, radBinSize, devFactor * radBinSize, nodePairs.size());
01172 NodePairs2D nodePairs2D;
01173
01174
01175 unsigned int radiusPairCounter = 0;
01176 std::vector<int> nodePairIndexFrom2DLookUpTable;
01177 float htDist = getHTDistanceFromDepth(depth, octree);
01178 float sacDist = getSACDistanceFromDepth(depth, octree);
01179 for(PairIndices::const_iterator pairInd_it = pairIndices.begin(); pairInd_it != pairIndices.end();++pairInd_it){
01180
01181 NodePair2D np2d(nodePairs[*pairInd_it], transformation, sacDist);
01182 if (np2d.getRadius() < minRadius || np2d.getRadius() > maxRadius || !np2d.stable()) continue;
01183 nodePairs2D.push_back(np2d);
01184 radiusHistogram.addDistributedToBins(np2d.getRadius(), WeightedIdx(1.f, radiusPairCounter++));
01185 nodePairIndexFrom2DLookUpTable.push_back(*pairInd_it);
01186 }
01187
01188
01189 WeightedIdxVector bestRadiusBin;
01190 float bestRadius;
01191
01192 WeightedIdxVector bestMidPointBin;
01193 float bestMidPointx = 0.f, bestMidPointy = 0.f;
01194
01195 unsigned int failcounter = 0;
01196 unsigned int triesForOneOrientation = mParams.mTriesOnEachDepth;
01197 do{
01198 CompareWeightedIdxVector compareWeightedIdxVector;
01199 radiusHistogram.getMaxBin(bestRadiusBin, bestRadius, compareWeightedIdxVector);
01200
01201 if (bestRadiusBin.nodeIndices().size() < mParams.mMinNodesInCylinder){
01202 for(PairIndices::const_iterator pairInd_it = pairIndices.begin(); pairInd_it != pairIndices.end();++pairInd_it){
01203 inlierIndices.push_back(*pairInd_it);
01204 }
01205 cylinder.reset();
01206 return;
01207 }
01208 float midPointBinSizeFactor = mParams.mMidPointBinSizeFactor;
01209 float midPointBinSize = htDist * midPointBinSizeFactor;
01210 float minMidX = std::numeric_limits<float>::max();
01211 float minMidY = std::numeric_limits<float>::max();
01212 float maxMidX = -std::numeric_limits<float>::max();
01213 float maxMidY = -std::numeric_limits<float>::max();
01214 for(unsigned int binIndex = 0; binIndex < bestRadiusBin.nodeIndices().size(); ++binIndex){
01215 assert(binIndex < bestRadiusBin.nodeIndices().size());
01216 assert((unsigned int)bestRadiusBin.nodeIndices()[binIndex] < nodePairs2D.size());
01217 const NodePair2D& np2d(nodePairs2D[bestRadiusBin.nodeIndices()[binIndex]]);
01218 const Eigen::Vector2f& mid = np2d.getCircleMidPoint();
01219 if(!std::isfinite(mid.x()) || !std::isfinite(mid.y())
01220 || mid.x() < -2.f * mParams.mRho_max || mid.x() > 2.f * mParams.mRho_max
01221 || mid.y() < -2.f * mParams.mRho_max || mid.y() > 2.f * mParams.mRho_max){
01222 continue;
01223 }
01224 if (minMidX > mid.x()) minMidX = mid.x();
01225 if (minMidY > mid.y()) minMidY = mid.y();
01226 if (maxMidX < mid.x()) maxMidX = mid.x();
01227 if (maxMidY < mid.y()) maxMidY = mid.y();
01228 }
01229 TriangleDistributionHistogram2D midPointHistogram(minMidX, maxMidX, minMidY, maxMidY,
01230 midPointBinSize, devFactor * midPointBinSize);
01231 unsigned int midpointPairCounter = 0;
01232 for(unsigned int binIndex = 0; binIndex < bestRadiusBin.nodeIndices().size(); ++binIndex){
01233 const NodePair2D& np2d(nodePairs2D[bestRadiusBin.nodeIndices()[binIndex]]);
01234
01235 const Eigen::Vector2f& mid = np2d.getCircleMidPoint();
01236 if(!std::isfinite(mid.x()) || !std::isfinite(mid.y())
01237 || mid.x() < -2.f * mParams.mRho_max || mid.x() > 2.f * mParams.mRho_max
01238 || mid.y() < -2.f * mParams.mRho_max || mid.y() > 2.f * mParams.mRho_max){
01239 continue;
01240 }
01241 midPointHistogram.addDistributedToBins(np2d.getCircleMidPoint().x(), np2d.getCircleMidPoint().y(), WeightedIdx(1.f, binIndex));
01242 ++midpointPairCounter;
01243 }
01244
01245
01246 midPointHistogram.getMaxBin(bestMidPointBin, bestMidPointx, bestMidPointy, compareWeightedIdxVector);
01247
01248 if (bestMidPointBin.nodeIndices().size() < mParams.mMinNodesInCylinder){
01249 if(failcounter < triesForOneOrientation){
01250
01251 radiusHistogram.removeAllFromBin(bestRadius);
01252
01253 } else{
01254 for(PairIndices::const_iterator pairInd_it = pairIndices.begin(); pairInd_it != pairIndices.end();++pairInd_it){
01255 inlierIndices.push_back(*pairInd_it);
01256 }
01257 cylinder.reset();
01258 return;
01259 }
01260 }
01261 }while(bestMidPointBin.nodeIndices().size() < mParams.mMinNodesInCylinder && failcounter++ < triesForOneOrientation);
01262
01263
01264 inlierIndices.clear();
01265 inlierIndices.reserve(2 * bestMidPointBin.nodeIndices().size());
01266 std::set<NodePtr> nodeSet;
01267 for(NodeIndices::const_iterator idx_it = bestMidPointBin.nodeIndices().begin(); idx_it != bestMidPointBin.nodeIndices().end(); ++idx_it){
01268 inlierIndices.push_back(nodePairIndexFrom2DLookUpTable[bestRadiusBin.nodeIndices()[*idx_it]]);
01269 const NodePair& np(nodePairs[nodePairIndexFrom2DLookUpTable[bestRadiusBin.nodeIndices()[*idx_it]]]);
01270 nodeSet.insert(np.getFirst());
01271 nodeSet.insert(np.getSecond());
01272 }
01273
01274 NodePointers inlierNodes;
01275 inlierNodes.reserve(nodeSet.size());
01276 copy(nodeSet.begin(), nodeSet.end(), back_inserter(inlierNodes));
01277 PointIndices pointIndices;
01278 getPointsFromNodes(pointIndices, inlierNodes, octree, pointCloud, pointMapping);
01279
01280 Mat3 invTrans = transformation.inverse();
01281 Vec3 midPoint2(bestMidPointx, bestMidPointy, 0.f);
01282 Vec3 midPoint3 = invTrans * midPoint2;
01283
01284 if (pointIndices.size() >= mParams.mMinPointsInCC) {
01285 cylinder.reset(new CylinderPatch(pointIndices, pointCloud, midPoint3, cylinderOrientation, bestRadius,
01286 2.f * getSACDistanceFromDepth(depth, octree)));
01287 } else {
01288 cylinder.reset();
01289 }
01290 }
01291
01292
01293
01294 void StructureColoring::updateCylinderHTBins(SphereUniformSampling& cylinderOrientationBins,
01295 const PairIndices& inlierIndices, SphereBinsVec& binNeighborhood) {
01296 for (PairIndices::const_iterator in_it = inlierIndices.begin(); in_it != inlierIndices.end(); ++in_it) {
01297 const SphereBins& neighborBins = binNeighborhood[*in_it];
01298 for (SphereBins::const_iterator nb_it = neighborBins.begin(); nb_it != neighborBins.end(); ++nb_it) {
01299 cylinderOrientationBins.deletePointNWeightAt(nb_it->pit, nb_it->wit, nb_it->i, nb_it->j);
01300 }
01301 binNeighborhood[*in_it].clear();
01302 }
01303 }
01304
01305
01306
01307 void StructureColoring::getAllPointsFromNodes(PointIndices& pointIndices, const NodePointers& octreeNodes,
01308 const OcTree& octree, const PointCloud& pointCloud) {
01309 pointIndices.clear();
01310 pointIndices.reserve(pointCloud.points.size());
01311 for (NodePointers::const_iterator node_it = octreeNodes.begin(); node_it != octreeNodes.end(); node_it++) {
01312 NodePointers leaf_nodes;
01313 octree.getAllLeafs(leaf_nodes, (*node_it));
01314 for (NodePointers::const_iterator leaf_it = leaf_nodes.begin(); leaf_it != leaf_nodes.end(); leaf_it++) {
01315 const PointIndices& points = octree.getPointsFromNodePtr(*leaf_it);
01316 for (std::vector<int>::const_iterator point_it = points.begin(); point_it != points.end(); point_it++) {
01317 pointIndices.push_back(*point_it);
01318 }
01319 }
01320 }
01321 }
01322
01323
01324
01325 void StructureColoring::getAllPointsFromNodes(PointIndices& pointIndices, const NodeIndices& nodeIndices,
01326 const NodePointers& octreeNodes, const OcTree& octree, const PointCloud& pointCloud) {
01327 pointIndices.clear();
01328 pointIndices.reserve(pointCloud.points.size());
01329 for (NodeIndices::const_iterator node_it = nodeIndices.begin(); node_it != nodeIndices.end(); node_it++) {
01330 NodePointers leaf_nodes;
01331 octree.getAllLeafs(leaf_nodes, octreeNodes[*node_it]);
01332 for (NodePointers::const_iterator leaf_it = leaf_nodes.begin(); leaf_it != leaf_nodes.end(); leaf_it++) {
01333 const PointIndices& points = octree.getPointsFromNodePtr(*leaf_it);
01334 for (std::vector<int>::const_iterator point_it = points.begin(); point_it != points.end(); point_it++) {
01335 pointIndices.push_back(*point_it);
01336 }
01337 }
01338 }
01339 }
01340
01341
01342
01343 void StructureColoring::addNodesToPlane(PlanePatchPtr& plane, const NodePointers& nodes, const OcTree& octree,
01344 const PointCloud& pointCloud, PlanePatchVector& pointMapping) {
01345 assert(!nodes.empty());
01346 PointIndices inlierCandidatePoints, inlierPoints;
01347 getPointsFromNodes(inlierCandidatePoints, nodes, octree, pointCloud, pointMapping);
01348 inlierPoints.reserve(inlierCandidatePoints.size());
01349 float xMin = 0.f, xMax = 0.f, yMin = 0.f, yMax= 0.f;
01350 unsigned int xOff = 0, xAdd = 0, yOff = 0, yAdd = 0;
01351 float pointXMin = std::numeric_limits<float>::max();
01352 float pointXMax = -std::numeric_limits<float>::max();
01353 float pointYMin = std::numeric_limits<float>::max();
01354 float pointYMax = -std::numeric_limits<float>::max();
01355 float cellSize = octree.getCellSizeFromDepth((*(nodes.rbegin()))->depth_);
01356 for (PointIndices::const_iterator poInIt = inlierCandidatePoints.begin(); poInIt != inlierCandidatePoints.end(); ++poInIt) {
01357 if (plane->getPlane3D().checkDistance(pointCloud.points[*poInIt].getVector3fMap(), plane->getDistanceThreshold())){
01358 inlierPoints.push_back(*poInIt);
01359 pointMapping[*poInIt] = plane;
01360 Vec3 tPoint(plane->getPlane3D().transformToXYPlane(pointCloud.points[*poInIt].getVector3fMap()));
01361 if(tPoint.x() < pointXMin) pointXMin = tPoint.x();
01362 if(tPoint.x() > pointXMax) pointXMax = tPoint.x();
01363 if(tPoint.y() < pointYMin) pointYMin = tPoint.y();
01364 if(tPoint.y() > pointYMax) pointYMax = tPoint.y();
01365 }
01366 }
01367
01368 plane->grid()->getNewExtremes(xMin, xMax, yMin, yMax, xOff, xAdd, yOff, yAdd, pointXMin, pointXMax, pointYMin, pointYMax, cellSize);
01369 plane->setGrid(new GridMap((*(plane->grid())), xMin, xMax, yMin, yMax, xOff, xAdd, yOff, yAdd, cellSize));
01370 plane->computeOrientedBoundingBox();
01371 plane->computeBoundingRectangle();
01372 plane->addPoints(inlierPoints, pointCloud);
01373 plane->grid()->blindPopulate(inlierPoints, pointCloud);
01374 }
01375
01376
01377
01378 void StructureColoring::markNodesAsSegmented(const PlanePatch& pp, const NodePointers& nodes, const OcTree& octree,
01379 const PointCloud& pointCloud, const float& maxDistance, const float& ratio) {
01380 for (NodePointers::const_iterator np_it = nodes.begin(); np_it != nodes.end(); ++np_it) {
01381 PointIndices allPointIndices;
01382 getAllPointsFromNodes(allPointIndices, NodePointers(1, *np_it), octree, pointCloud);
01383 unsigned int count = 0;
01384 for (PointIndices::const_iterator point_it = allPointIndices.begin(); point_it != allPointIndices.end(); ++point_it) {
01385 if (pp.getPlane3D().checkDistance(pointCloud.points[*point_it].getVector3fMap(), maxDistance))
01386 ++count;
01387 }
01388 if (ratio <= (float) count / (float) allPointIndices.size()) {
01389 (*np_it)->sweepDown(NULL, StructureColoring::markAsSegmented);
01390 }
01391 }
01392 }
01393
01394
01395
01396 void StructureColoring::assignNodesToPlanes(NodePointers& notAssignedOctreeNodes, const unsigned int& octreeDepth,
01397 const OcTree& octree, PlanePatches& extractedPlanes, const PointCloud& pointCloud, PlanePatchVector& pointMapping) {
01398 NodePtrList notAssignedNodePtrList;
01399 for (NodePointerList::const_iterator node_iter = octree.getNodeListOnDepth(octreeDepth).begin(); node_iter
01400 != octree.getNodeListOnDepth(octreeDepth).end(); ++node_iter) {
01401 if ((*node_iter)->value_.stable){
01402 if (!((*node_iter)->value_.segmented)){
01403 notAssignedNodePtrList.push_back(*node_iter);
01404 }
01405 }
01406 }
01407 if (!notAssignedNodePtrList.empty()) {
01408 float cellSize = octree.getCellSizeFromDepth(octreeDepth);
01409 if (cellSize < mLastCellSizeWithNormals)
01410 mLastCellSizeWithNormals = cellSize;
01411 }
01412
01413 for (PlanePatches::iterator planes_iter = extractedPlanes.begin(); planes_iter != extractedPlanes.end(); ++planes_iter) {
01414 NodePointers nodeCandidates;
01415 nodeCandidates.reserve(notAssignedNodePtrList.size());
01416 std::vector<NodePtrList::iterator> nodeCandidateIterators;
01417 nodeCandidateIterators.reserve(notAssignedNodePtrList.size());
01418 for (NodePtrList::iterator node_iter = notAssignedNodePtrList.begin(); node_iter != notAssignedNodePtrList.end(); ++node_iter) {
01419 if ((*planes_iter)->getPlane3D().checkDistance((*node_iter)->value_.meanPos, (*planes_iter)->getDistanceThreshold())
01420 && (*planes_iter)->getPlane3D().checkNormal((*node_iter)->value_.normal, getAngleEpsFromDepth(octreeDepth,
01421 octree))) {
01422 nodeCandidateIterators.push_back(node_iter);
01423 nodeCandidates.push_back(*node_iter);
01424 }
01425 }
01426 if (nodeCandidates.empty())
01427 continue;
01428 NodeIndices inlierIndices;
01429 (*planes_iter)->grid().get()->checkNodesAgainstGridConnection(inlierIndices, nodeCandidates, octree.getCellSizeFromDepth(octreeDepth),
01430 std::min(mParams.mMinOctreeNodes, mParams.mMinNodesInCC));
01431
01432 NodePointers inlierNodes;
01433 inlierNodes.reserve(inlierIndices.size());
01434 for (NodeIndices::const_iterator in_it = inlierIndices.begin(); in_it != inlierIndices.end(); ++in_it) {
01435 assert((unsigned int)*in_it < nodeCandidates.size());
01436 inlierNodes.push_back(nodeCandidates[*in_it]);
01437 notAssignedNodePtrList.erase(nodeCandidateIterators[*in_it]);
01438 }
01439 if (!inlierNodes.empty()) {
01440
01441 addNodesToPlane(*planes_iter, inlierNodes, octree, pointCloud, pointMapping);
01442 markNodesAsSegmented(**planes_iter, inlierNodes, octree, pointCloud, getSACDistanceFromDepth(octreeDepth, octree),
01443 mParams.mNodeSegmentedRatio);
01444 }
01445 }
01446 notAssignedOctreeNodes.resize(notAssignedNodePtrList.size());
01447 unsigned int i = 0;
01448 for (NodePtrList::const_iterator noas_iter = notAssignedNodePtrList.begin(); noas_iter != notAssignedNodePtrList.end(); ++noas_iter) {
01449 notAssignedOctreeNodes[i++] = *noas_iter;
01450 }
01451 }
01452
01453
01454
01455
01456
01457
01458
01459
01460
01461
01462
01463
01464
01465
01466
01467
01468
01469
01470
01471
01472 void StructureColoring::extractOctreePlanesFromCloud(PlanePatches& extractedPlanes, PlanePatchVector& pointMapping,
01473 const OcTree& octree, const PointCloudPtr& pointCloud){
01474
01475 NodePointers octreeNodes;
01476 assert(octree.checkSamplingMap());
01477 assert(octree.checkGetAllLeafs());
01478
01479 PointCloud histogramPC;
01480 unsigned int maxDepth = octree.getMaxDepth();
01481 unsigned int startDepth = 0;
01482 if (mParams.mDebugSteps >= 0 && (unsigned int) mParams.mDebugSteps <= maxDepth * 2)
01483 maxDepth = mParams.mDebugSteps / 2;
01484 if(mParams.mOnlyDepth){
01485 startDepth = mParams.mOnlyDepth;
01486 maxDepth = mParams.mOnlyDepth;
01487 }
01488 for (unsigned int depth = startDepth; depth <= maxDepth; depth++) {
01489
01490
01491 octreeNodes.clear();
01492 CompArea compArea;
01493 extractedPlanes.sort(compArea);
01494 assignNodesToPlanes(octreeNodes, depth, octree, extractedPlanes, *pointCloud, pointMapping);
01495
01496 if (mParams.mOnlyDepth) ROS_INFO("cellsize %f on depth %i", octree.getCellSizeFromDepth(depth), depth);
01497 if ((mParams.mDebugSteps < 0 || depth != maxDepth || mParams.mDebugSteps & 1) && octreeNodes.size() > mParams.mMinNodesInCC) {
01498 float sacDist = getSACDistanceFromDepth(depth, octree);
01499
01500 SphereUniformSamplings planeHTBins;
01501 SphereBinsVec pointNeighborhood;
01502 generateSingleSphereOctreeHTBins(planeHTBins, pointNeighborhood, octreeNodes);
01503
01504 if (mVis) {
01505
01506 PointCloud tmpPointCloud;
01507 planeHTBins[0].getHistogramAsPointCloud(tmpPointCloud, .1f, Vec3(.5f, 0.5f, 0.f));
01508 histogramPC += tmpPointCloud;
01509 }
01510 NodeIndices inlierNodes;
01511 unsigned int failcounter = 0;
01512 unsigned int numberPoints = 0;
01514 unsigned int HTCounter = 0;
01516 do {
01517 Plane3DPtr pp;
01518 inlierNodes.clear();
01519 pclPointIndices planeInliers;
01520 estimatePlaneSingleSphereOctreeHT(planeInliers, pp, planeHTBins, pointNeighborhood, octreeNodes, inlierNodes, *pointCloud, octree, depth, pointMapping);
01521
01523 Vec3 color(Vec3::Zero());
01524 if (mParams.mDebugSteps != -1 && depth != maxDepth && mParams.mDebugSteps & 1) {
01525 PointIndices HTPointIndices;
01526 getAllPointsFromNodes(HTPointIndices, getNodesFromIndices(inlierNodes, octreeNodes), octree, *pointCloud);
01527 char app[255];
01528 sprintf(app, "HTPlane HT(%d) DEBUG depth(%d)", HTCounter, depth);
01529 float rgb[3];
01530 RosVisualization::getColorByIndex(rgb, HTCounter, failcounter + 10);
01531 color = Vec3(rgb[0], rgb[1], rgb[2]);
01532 if (mVis) mVis->publishPoints(getPointsFromIndices(HTPointIndices, *pointCloud), std::string(app), color);
01533 }
01535 updateHTBins(inlierNodes, planeHTBins, pointNeighborhood);
01536 if (inlierNodes.size() < mParams.mMinOctreeNodes) continue;
01537
01538 if ((pp) && (planeInliers.indices.size() >= mParams.mMinPointsInCC)) {
01539 std::vector<NodePointers> CCInliers;
01540 NodePointers inlierOctreeNodes;
01541 for (NodeIndices::const_iterator in_it = inlierNodes.begin(); in_it != inlierNodes.end(); in_it++) {
01542 inlierOctreeNodes.push_back(octreeNodes[*in_it]);
01543 }
01544 filterConnectedNodes(CCInliers, *pp, inlierOctreeNodes, octree.getCellSizeFromDepth(depth));
01545
01546 for (unsigned int cc = 0; cc < CCInliers.size(); cc++) {
01547
01548 pclPointIndices::Ptr pinliers = boost::make_shared<pclPointIndices>();
01549 getPointsFromNodes(pinliers->indices, CCInliers[cc], octree, *pointCloud, pointMapping);
01551 if (mParams.mDebugSteps != -1 && depth != maxDepth && mParams.mDebugSteps & 1) {
01552 char nameApp[255];
01553 sprintf(nameApp, "CCPlane CC(%d) DEBUG HT(%d) depth(%d)", cc, HTCounter, depth);
01554
01555
01556
01557 if (mVis) mVis->publishPoints(getPointsFromIndices(pinliers->indices, *pointCloud), std::string(nameApp),
01558 color);
01559 }
01560 PointIndices CCIndices = pinliers->indices;
01562 size_t sizeOfInliers = pinliers->indices.size();
01563 if (sizeOfInliers >= mParams.mMinPointsInCC) {
01564 PlanePatchPtr pp2;
01565
01566
01567 if (mParams.mNoRansacStep){
01568 const PointIndices& pp1Inliers(pinliers->indices);
01569 PointIndices pp2Inliers;
01570 pp2Inliers.reserve(pp1Inliers.size());
01571 for(PointIndices::const_iterator pis_it = pp1Inliers.begin(); pis_it != pp1Inliers.end(); ++pis_it){
01572 Vec3 pis_it_point(pointCloud->points[*pis_it].x, pointCloud->points[*pis_it].y, pointCloud->points[*pis_it].z);
01573 if(pp->checkDistance(pis_it_point, getHTDistanceFromDepth(depth, octree)))
01574 pp2Inliers.push_back(*pis_it);
01575 }
01576
01577 if ((float)pp2Inliers.size() / (float)sizeOfInliers >= mParams.mSACOutlierRatioThreshold){
01578 pp2.reset(new PlanePatch(pp->getPlaneNormal(), pp->getPlaneDistanceToOrigin(), pp2Inliers, *pointCloud, getHTDistanceFromDepth(depth, octree)));
01579
01580 }
01581 } else
01582 estimatePlane(pinliers, pp2, pointCloud, sacDist);
01583 if (pp2 && (pp->checkNormal(pp2->getPlane3D().getPlaneNormal(), 2.f * getAngleEpsFromDepth(depth, octree)))
01584 && (fabsf(pp->getPlaneDistanceToOrigin() - pp2->getPlane3D().getPlaneDistanceToOrigin()) < 2.f
01585 * getHTDistanceFromDepth(depth, octree)) && (pp2->getInliers().size()
01586 >= mParams.mMinPointsInCC) && (((float) pp2->getInliers().size()) / ((float) sizeOfInliers)
01587 >= mParams.mSACOutlierRatioThreshold)) {
01588 markNodesAsSegmented(*pp2, CCInliers[cc], octree, *pointCloud, getSACDistanceFromDepth(depth, octree),
01589 mParams.mNodeSegmentedRatio);
01590 float cellSize = octree.getCellSizeFromDepth(depth);
01591
01592 pp2->newGrid(cellSize);
01593 pp2->grid()->blindPopulate(pp2->getInliers(), *pointCloud);
01594 pp2->grid()->calculateStartPos();
01595
01596
01597 extractedPlanes.push_front(pp2);
01598
01599 numberPoints += pp2->getInliers().size();
01601 if (mParams.mDebugSteps != -1 && depth != maxDepth && mParams.mDebugSteps & 1) {
01602 char nameApp[255];
01603 sprintf(nameApp, "Plane b.R. CC(%d) DEBUG HT(%d) depth(%d)", cc, HTCounter, depth);
01604 PointIndices pointIndices;
01605 sort(CCIndices.begin(), CCIndices.end());
01606 PointIndices planePointIndices = pp2->getInliers();
01607 sort(planePointIndices.begin(), planePointIndices.end());
01608 set_difference(CCIndices.begin(), CCIndices.end(), planePointIndices.begin(),
01609 planePointIndices.end(), std::back_inserter(pointIndices));
01610 Vec3 color2 = Vec3(1.f, 0.f, 0.f);
01611 if (mVis) mVis->publishPoints(getPointsFromIndices(pointIndices, *pointCloud),
01612 std::string(nameApp), color2);
01613
01614 char name[255];
01615 sprintf(name, "RANSACPlane CC(%d) DEBUG HT(%d) depth(%d)", cc, HTCounter, depth);
01616 color2 = Vec3(0.f, 1.f, 0.1f);
01617 if (mVis) mVis->publishPoints(getPointsFromIndices(pp2->getInliers(), *pointCloud), std::string(
01618 name), color2);
01619 }
01621
01622 for (PointIndices::const_iterator in_it = pp2->getInliers().begin(); in_it
01623 != pp2->getInliers().end(); in_it++) {
01624 pointMapping[*in_it] = pp2;
01625 }
01626
01627
01628 }
01629 }
01630 }
01631 }
01633 HTCounter++;
01635 } while (((inlierNodes.size() >= mParams.mMinOctreeNodes) && (numberPoints > mParams.mMinPointsInCC)) || (failcounter++
01636 < mParams.mTriesOnEachDepth));
01637
01638 }
01639 }
01640 if (mVis) {
01641 mVis->publishHTBinCloud(histogramPC);
01642 }
01643 }
01644
01645
01646
01647 void StructureColoring::assignNodesToCylinders(NodePointers& octreeNodes, CylinderPatches& extractedCylinders,
01648 CylinderPatchVector& pointMapping, const OcTree& octree, const PointCloud& pointCloud){
01649 typedef std::list<NodePtr> NodeList;
01650 typedef std::vector<NodeList::iterator> NodeListIterators;
01651 NodeList nodeList;
01652 std::copy(octreeNodes.begin(), octreeNodes.end(), std::back_inserter(nodeList));
01653 for(CylinderPatches::iterator cit = extractedCylinders.begin(); cit != extractedCylinders.end(); ++cit){
01654 NodePointers nodesToAdd;
01655 NodeListIterators nodesToBeErased;
01656 for(NodeList::iterator node_it = nodeList.begin(); node_it != nodeList.end(); ++node_it){
01657 if((*cit)->checkDistance((*node_it)->value_.meanPos)
01658 && (*cit)->checkNormal((*node_it)->value_.normal, (*node_it)->value_.meanPos, mParams.mAngleEps)
01659 && (*cit)->checkHeight((*node_it)->value_.meanPos, mParams.mCylinderHeightDev))
01660 {
01661 nodesToAdd.push_back(*node_it);
01662 nodesToBeErased.push_back(node_it);
01663 }
01664 }
01665 for(NodeListIterators::const_iterator nit_it = nodesToBeErased.begin(); nit_it != nodesToBeErased.end(); ++nit_it){
01666 nodeList.erase(*nit_it);
01667 }
01668 PointIndices pointIndicesToAdd;
01669 getPointsFromNodes(pointIndicesToAdd, nodesToAdd, octree, pointCloud, pointMapping);
01670 updatePointMapping(pointMapping, pointIndicesToAdd, *cit);
01671 (*cit)->addPoints(pointIndicesToAdd, pointCloud);
01672 }
01673 octreeNodes.clear();
01674 std::copy(nodeList.begin(), nodeList.end(), std::back_inserter(octreeNodes));
01675 }
01676
01677
01678
01679 void StructureColoring::extractOctreeCylindersFromCloud(CylinderPatches& extractedCylinders, CylinderPatchVector& pointMapping,
01680 const OcTree& octree, const PointCloudPtr& pointCloud){
01681 NodePointers octreeNodes;
01682 unsigned int startDepth = 0;
01683 unsigned int maxDepth = octree.getMaxDepth();
01684 unsigned int cylinderPairNeighborhoodSize = mParams.mCylinderPairNeighbors;
01685 if (mParams.mDebugSteps >= 0 && (unsigned int) mParams.mDebugSteps <= maxDepth * 2)
01686 maxDepth = mParams.mDebugSteps / 2;
01687 PointCloud histogramPC;
01688 if(mParams.mOnlyDepth){
01689 startDepth = mParams.mOnlyDepth;
01690 maxDepth = mParams.mOnlyDepth;
01691 }
01692 for (unsigned int depth = startDepth; depth <= maxDepth; depth++)
01693 {
01694 float sacDist = getSACDistanceFromDepth(depth, octree);
01695 float minRadius = 0.f, maxRadius = 0.f;
01696 getMinMaxRadiusFromDepth(minRadius, maxRadius, octree, depth);
01697 ROS_INFO("minRadius = %f, maxRadius = %f", minRadius, maxRadius);
01698 SphereUniformSampling cylinderOrientationBins(mParams.mPhi_resolution);
01699 SphereBinsVec binNeighborhood;
01700 NodePairs nodePairs;
01701 octreeNodes.clear();
01702 octreeNodes = octree.getNodesOnDepth(depth);
01703 size_t nrNodesOnDepth = octreeNodes.size();
01704 if(octreeNodes.size() < mParams.mMinNodesInCylinder) continue;
01705 assignNodesToCylinders(octreeNodes, extractedCylinders, pointMapping, octree, *pointCloud);
01706 if(octreeNodes.size() < mParams.mMinNodesInCylinder) continue;
01707 generateNeighboringNodePairs(nodePairs, octreeNodes, cylinderPairNeighborhoodSize, depth, octree);
01708 if(mVis){
01709 mVis->publishPairMarker(nodePairs);
01710 }
01711 ROS_INFO("depth %d: %zu cylinders, %zu octreeNodes, %zu not assigned, %zu node pairs", depth, extractedCylinders.size(), nrNodesOnDepth, octreeNodes.size(), nodePairs.size());
01712 if (nodePairs.size() < mParams.mMinNodesInCylinder) continue;
01713 generateCylinderOrientationHB(cylinderOrientationBins, binNeighborhood, nodePairs);
01714
01715
01716 if (mVis) {
01717 PointCloud tmpPointCloud;
01718 cylinderOrientationBins.getHistogramAsPointCloud(tmpPointCloud, .05f, Vec3(-.5f, 1.f - 0.125f * depth, 0.f));
01719 histogramPC += tmpPointCloud;
01720 }
01721
01722 unsigned int failcounter = 0;
01723 unsigned int numberPoints;
01724
01725
01726 PairIndices pairIndices;
01727 do{
01728
01729 numberPoints = 0;
01730 CylinderPatchPtr cylinder;
01731 NodePointers nodePointers;
01732 pairIndices.clear();
01733 estimateCylinderHT(cylinderOrientationBins, nodePairs, pairIndices, cylinder, depth, octree, pointMapping, *pointCloud, minRadius, maxRadius);
01734 updateCylinderHTBins(cylinderOrientationBins, pairIndices, binNeighborhood);
01735 if(!cylinder || pairIndices.size() < mParams.mMinOctreeNodes || (!checkCylinderDimensions(cylinder))){
01736 continue;
01737 }
01738 Vec3 htAxisDirection(cylinder->getAxisDirection());
01739 Vec3 htMidPoint(cylinder->getMidPoint());
01740 float htRadius(cylinder->getRadius());
01741 float cellSize(octree.getCellSizeFromDepth(depth));
01742
01743 unsigned int htNumPoints = cylinder->getInliers().size();
01744 if (!mParams.mNoRansacStep)
01745 {
01746 estimateCylinderNNNormals(cylinder, pointCloud, sacDist, mParams.mNormalDistanceWeight,
01747 0.001, 10.0,
01748 1.0*cellSize, htAxisDirection);
01749 if (!cylinder){
01750 ROS_WARN("estimateCylinderNNNormals() removed cylinder");
01751 continue;
01752 }
01753
01754 float cosAngle = htAxisDirection.dot(cylinder->getAxisDirection());
01755
01756
01757 if(fabsf(cosAngle) < cosf(2.f * mParams.mAngleEps)) {
01758 ROS_WARN("results of HT and RANSAC were too different (angle) %f %f", fabsf(cosAngle), cosf(2.f * mParams.mAngleEps) );
01759 continue;
01760 }
01761
01762 if(cylinder->getRadius() / htRadius > 2 || htRadius / cylinder->getRadius() > 2) {
01763 ROS_WARN("results of HT and RANSAC were too different (radius) %f", cylinder->getRadius() / htRadius );
01764 continue;
01765 }
01766
01767 if((cylinder->getInliers().size() < htNumPoints * mParams.mSACOutlierRatioThreshold)){
01768 ROS_WARN("results of HT and RANSAC were too different (num inliers)");
01769 continue;
01770 }
01771 }
01772 if (!checkCylinder(cylinder, pointCloud)){
01773 ROS_WARN("not enough area on cylinder");
01774 continue;
01775 }
01776 updatePointMapping(pointMapping, cylinder->getInliers(), cylinder);
01777 extractedCylinders.push_back(cylinder);
01778 numberPoints = cylinder->getInliers().size();
01779 } while (((pairIndices.size() >= mParams.mMinOctreeNodes) && (numberPoints > mParams.mMinPointsInCC)) || (failcounter++ < mParams.mTriesOnEachDepth));
01780 ROS_INFO("no more cylinders on depth %d", depth);
01781 }
01782 ROS_INFO("cylinder HT completed for all depths");
01783 if(mVis){
01784 mVis->publishHTBinCloud(histogramPC);
01785 }
01786 }
01787
01788
01789
01790 void StructureColoring::getNodesFromPairs(NodePointers& nodePointers, const PairIndices& pairIndices, const NodePairs& nodePairs){
01791 nodePointers.reserve(pairIndices.size() * 2);
01792 for(PairIndices::const_iterator pair_it = pairIndices.begin(); pair_it != pairIndices.end(); ++pair_it){
01793 nodePointers.push_back(nodePairs[*pair_it].getFirst());
01794 nodePointers.push_back(nodePairs[*pair_it].getSecond());
01795 }
01796 }
01797
01798
01799
01800 void StructureColoring::determineCandidatePlanes(NodeIndexToPlanePatchPointers& nodeToPlaneCandidates,
01801 const NodePointers& octreeNodes, PlanePatches& extractedPlanes) {
01802 #pragma omp parallel for schedule(dynamic,1)
01803 for (size_t nodeCount = 0; nodeCount < octreeNodes.size(); ++nodeCount) {
01804 for (PlanePatches::iterator planes_iter = extractedPlanes.begin(); planes_iter != extractedPlanes.end(); ++planes_iter) {
01805 if ((*planes_iter)->getPlane3D().checkDistance(octreeNodes[nodeCount]->value_.meanPos,
01806 (*planes_iter)->getDistanceThreshold()))
01807 {
01808 const Vec3& nodeCoG = octreeNodes[nodeCount]->value_.meanPos;
01809 if (((*planes_iter)->distanceToOBB(nodeCoG) < std::sqrt((*planes_iter)->getArea()) * mParams.mNodeToBBDistance))
01810 {
01811 if((*planes_iter)->checkPointConnection(nodeCoG, mParams.mConnectionNeighbors))
01812 {
01813 nodeToPlaneCandidates[nodeCount].push_back(*planes_iter);
01814 }
01815 }
01816 }
01817 }
01818 }
01819 }
01820
01821
01822
01823 void StructureColoring::determineCandidatePlanes(PatchToNodeIndicesMap& outIndicesMap,
01824 PatchToNodePointersMap& outPointersMap, const NodePointers& octreeNodes, PlanePatches& extractedPlanes) {
01825
01826 for (PlanePatches::iterator planes_iter = extractedPlanes.begin(); planes_iter != extractedPlanes.end(); ++planes_iter) {
01827 unsigned int possSize = std::min(octreeNodes.size(), (*planes_iter)->getInliers().size());
01828 outIndicesMap[*planes_iter].reserve(possSize);
01829 outPointersMap[*planes_iter].reserve(possSize);
01830 }
01831
01832 for (size_t nodeCount = 0; nodeCount < octreeNodes.size(); ++nodeCount) {
01833 for (PlanePatches::iterator planes_iter = extractedPlanes.begin(); planes_iter != extractedPlanes.end(); ++planes_iter) {
01834 if ((*planes_iter)->getPlane3D().checkDistance(octreeNodes[nodeCount]->value_.meanPos,
01835 (*planes_iter)->getDistanceThreshold())) {
01836 outIndicesMap[*planes_iter].push_back(nodeCount);
01837 outPointersMap[*planes_iter].push_back(octreeNodes[nodeCount]);
01838 }
01839 }
01840 }
01841 }
01842
01843
01844
01845 void StructureColoring::refineAssignmentWithCCs(NodeIndexToPlanePatchPointers& nodeToPlaneCandidates,
01846 const PatchToNodeIndicesMap& nodeIndicesMap, const PatchToNodePointersMap& nodePointersMap,
01847 const NodePointers& octreeNodes, PlanePatches& extractedPlanes) {
01848 for (PlanePatches::iterator planes_iter = extractedPlanes.begin(); planes_iter != extractedPlanes.end(); ++planes_iter) {
01849 const NodePointers& nodePointers = nodePointersMap.find(*planes_iter)->second;
01850 size_t nodeCount = 0;
01851 for (NodePointers::const_iterator nit = nodePointers.begin(); nit != nodePointers.end(); ++nit) {
01852 const Vec3& nodeCoG = (*nit)->value_.meanPos;
01853 if ((*planes_iter)->distanceToOBB(nodeCoG) < std::sqrt((*planes_iter)->getArea()) * mParams.mNodeToBBDistance
01854 && (*planes_iter)->checkPointConnection(nodeCoG, mParams.mConnectionNeighbors)) {
01855 const NodeIndices& nodeIndices = nodeIndicesMap.find(*planes_iter)->second;
01856 nodeToPlaneCandidates[nodeIndices[nodeCount]].push_back(*planes_iter);
01857 }
01858 ++nodeCount;
01859 }
01860 }
01861 }
01862
01863
01864
01865 void StructureColoring::calculateMiddlePlane(Plane3D& outParams, const PlanePatch& firstPPP,
01866 const PlanePatch& secondPPP) {
01867 Vec3 viewport(Vec3::Zero());
01868 Vec3 p1Norm = firstPPP.getPlane3D().getPlaneNormal();
01869 flipToViewport(p1Norm, firstPPP.getPlaneCoG() - viewport);
01870 Vec3 p2Norm = secondPPP.getPlane3D().getPlaneNormal();
01871 flipToViewport(p2Norm, secondPPP.getPlaneCoG() - viewport);
01872 Vec3 tang1(p2Norm.cross(p1Norm));
01873 Vec3 tang2((p1Norm + p2Norm));
01874 tang2.normalize();
01875 Vec3 normal = tang1.cross(tang2);
01876 normal.normalize();
01877 Vec3 v = tang1.cross(p1Norm);
01878 Vec3 pointOnPlane(firstPPP.getPlane3D().getOrthogonalProjectionOntoPlane(firstPPP.getPlaneCoG()));
01879 float lambda = (secondPPP.getPlane3D().getPlaneDistanceToOrigin() - pointOnPlane.dot(secondPPP.getPlane3D().getPlaneNormal()))
01880 / secondPPP.getPlane3D().getPlaneNormal().dot(v);
01881 Vec3 pointOnLineOfIntersection(pointOnPlane + lambda * v);
01882 if (pointOnLineOfIntersection.dot(normal) < 0) {
01883 normal *= -1.f;
01884 }
01885 outParams.setPlaneNormal(normal);
01886 outParams.setPlaneDistanceToOrigin(pointOnLineOfIntersection.dot(normal));
01887 }
01888
01889
01890
01891 void StructureColoring::refineEdges(PatchToNodePointersMap& finalPlaneNodeAssignmentMap,
01892 PatchToPointIndicesMap& finalPlanePointAssignmentMap, const NodeIndexToPlanePatchPointers& nodeToPlaneCandidates,
01893 const NodePointers& octreeNodes, const OcTree& octree, const PointCloud& pointCloud) {
01894 typedef std::pair<PlanePatchPtr, PlanePatchPtr> PlanePatchPointerPair;
01895 typedef std::map<PlanePatchPointerPair, Plane3D, CompPlanePtrPair> MiddlePlaneMap;
01896 typedef std::map<PlanePatchPointerPair, PointIndices, CompPlanePtrPair> MiddlePlaneIndicesMap;
01897 MiddlePlaneMap middlePlaneMap;
01898 MiddlePlaneIndicesMap middlePlaneIndicesMap;
01899 size_t nodeIndex = 0;
01900 for (NodeIndexToPlanePatchPointers::const_iterator node_idx_it = nodeToPlaneCandidates.begin(); node_idx_it
01901 != nodeToPlaneCandidates.end(); ++node_idx_it) {
01902 if (node_idx_it->size() > 1) {
01903 PointIndices pointIndices;
01904 getAllPointsFromNodes(pointIndices, NodePointers(1, octreeNodes[nodeIndex]), octree, pointCloud);
01905 for (PointIndices::const_iterator point_it = pointIndices.begin(); point_it != pointIndices.end(); ++point_it) {
01906 PlanePatchPtr firstPPP, secondPPP;
01907 float firstDist = std::numeric_limits<float>::max(), secondDist = std::numeric_limits<float>::max();
01908 for (PlanePatchVector::const_iterator pp_it = node_idx_it->begin(); pp_it != node_idx_it->end(); ++pp_it) {
01909 float currDist = (*pp_it)->getPlane3D().distance(octreeNodes[nodeIndex]->value_.meanPos);
01910 if (currDist <= firstDist) {
01911 secondDist = firstDist;
01912 secondPPP = firstPPP;
01913 firstPPP = *pp_it;
01914 firstDist = currDist;
01915 } else if (currDist <= secondDist) {
01916 secondPPP = *pp_it;
01917 secondDist = currDist;
01918 }
01919 }
01920
01921 bool notInList = true;
01922 PlanePatchPointerPair pppp(std::make_pair(firstPPP, secondPPP));
01923 PlanePatchPointerPair ppppSwap(std::make_pair(secondPPP, firstPPP));
01924 MiddlePlaneMap::iterator mpm_it = middlePlaneMap.find(pppp);
01925 MiddlePlaneMap::iterator mpmSwap_it = middlePlaneMap.find(ppppSwap);
01926 if (mpm_it != middlePlaneMap.end()) {
01927 notInList = false;
01928 } else if (mpmSwap_it != middlePlaneMap.end()) {
01929 notInList = false;
01930 mpm_it = mpmSwap_it;
01931 pppp = ppppSwap;
01932 }
01933 Plane3D planeParams;
01934 if (notInList) {
01935
01936 calculateMiddlePlane(middlePlaneMap[pppp], *firstPPP, *secondPPP);
01937 planeParams = middlePlaneMap.find(pppp)->second;
01938
01939
01940 if (middlePlaneIndicesMap.find(pppp) != middlePlaneIndicesMap.end()) {
01941 middlePlaneIndicesMap.find(pppp)->second.push_back(*point_it);
01942 } else {
01943 middlePlaneIndicesMap[pppp] = PointIndices(1, *point_it);
01944 }
01945 } else {
01946
01947 planeParams = mpm_it->second;
01948 PointIndices pointIndices;
01949 getAllPointsFromNodes(pointIndices, NodeIndices(1, nodeIndex), octreeNodes, octree, pointCloud);
01950 if (middlePlaneIndicesMap.find(pppp) != middlePlaneIndicesMap.end()) {
01951 middlePlaneIndicesMap.find(pppp)->second.push_back(*point_it);
01952 } else {
01953 middlePlaneIndicesMap[pppp] = PointIndices(1, *point_it);
01954 }
01955 }
01956
01957
01958 if (planeParams.signedDistance(firstPPP->getPlaneCoG()) * planeParams.signedDistance(secondPPP->getPlaneCoG()) > 0) {
01959
01960
01961
01962
01963
01964
01965
01966 finalPlanePointAssignmentMap[firstPPP].push_back(*point_it);
01967 } else {
01968
01969 if (planeParams.signedDistance(firstPPP->getPlaneCoG()) < 0.f) {
01970 if (planeParams.signedDistance(pointCloud.points[*point_it].getVector3fMap()) < 0.f) {
01971 finalPlanePointAssignmentMap[firstPPP].push_back(*point_it);
01972 } else
01973 finalPlanePointAssignmentMap[secondPPP].push_back(*point_it);
01974 } else {
01975 if (planeParams.signedDistance(pointCloud.points[*point_it].getVector3fMap()) > 0.f) {
01976 finalPlanePointAssignmentMap[firstPPP].push_back(*point_it);
01977 } else
01978 finalPlanePointAssignmentMap[secondPPP].push_back(*point_it);
01979 }
01980 }
01981 }
01982 } else {
01983 if (node_idx_it->size() == 1) {
01984
01985 finalPlaneNodeAssignmentMap[*(node_idx_it->begin())].push_back(octreeNodes[nodeIndex]);
01986 }
01987
01988 }
01989 nodeIndex++;
01990 }
01991 PlanePatches planes;
01992 for (MiddlePlaneMap::const_iterator mapit = middlePlaneMap.begin(); mapit != middlePlaneMap.end(); ++mapit) {
01993 const Plane3D& params = mapit->second;
01994 PointIndices pointIndices;
01995 MiddlePlaneIndicesMap::const_iterator mpimIt = middlePlaneIndicesMap.find(mapit->first);
01996 if (mpimIt != middlePlaneIndicesMap.end())
01997 pointIndices = mpimIt->second;
01998 if (pointIndices.size() > 3) {
01999 PlanePatchPtr plane(new PlanePatch(params.getPlaneNormal(), params.getPlaneDistanceToOrigin(), pointIndices, pointCloud,
02000 getSACDistanceFromDepth(octree.getMaxDepth(), octree)));
02001 planes.push_back(plane);
02002 }
02003 }
02004 if (mVis) mVis->publishPlaneMarker(planes, "middleplane");
02005 }
02006
02007
02008
02009 void StructureColoring::spreadNodes(const OcTree& octree, PlanePatches& extractedPlanes, PlanePatchVector& pointMapping,
02010 const PointCloud& pointCloud) {
02011 const NodePointers& octreeNodes = octree.getNodesOnDepth(octree.getMaxDepth());
02012 NodeIndexToPlanePatchPointers nodeToPlaneCandidates(octreeNodes.size());
02013
02014
02015
02016 PatchToNodePointersMap nodePointersMap;
02017 PatchToNodeIndicesMap nodeIndicesMap;
02018
02019
02020
02021
02022 determineCandidatePlanes(nodeToPlaneCandidates, octreeNodes, extractedPlanes);
02023
02024
02025
02026 PatchToNodePointersMap finalPlaneNodeAssignmentMap;
02027 PatchToPointIndicesMap finalPlanePointAssignmentMap;
02028 refineEdges(finalPlaneNodeAssignmentMap, finalPlanePointAssignmentMap, nodeToPlaneCandidates, octreeNodes, octree, pointCloud);
02029
02030
02031 for (PlanePatchVector::iterator seg_iter = pointMapping.begin(); seg_iter != pointMapping.end(); ++seg_iter)
02032 seg_iter->reset();
02033
02034 PlanePatches newPlanes;
02035
02036 for (PatchToNodePointersMap::iterator it = finalPlaneNodeAssignmentMap.begin(); it != finalPlaneNodeAssignmentMap.end(); ++it) {
02037 const PlanePatch& pRef = *(it->first);
02038 PointIndices inliers;
02039 getAllPointsFromNodes(inliers, it->second, octree, pointCloud);
02040 PatchToPointIndicesMap::iterator pointIndicesMap_it = finalPlanePointAssignmentMap.find(it->first);
02041 if (pointIndicesMap_it != finalPlanePointAssignmentMap.end()) {
02042 inliers.reserve(inliers.size() + pointIndicesMap_it->second.size());
02043 for (PointIndices::const_iterator point_indices_it = pointIndicesMap_it->second.begin(); point_indices_it
02044 != pointIndicesMap_it->second.end(); ++point_indices_it)
02045 inliers.push_back(*point_indices_it);
02046 }
02047 if (inliers.size() > mParams.mMinPointsInCC) {
02048 PlanePatchPtr ppp(new PlanePatch(inliers, pointCloud, pRef.getDistanceThreshold()));
02049 if (ppp) {
02050 ppp->newGrid(octree.getCellSizeFromDepth(octree.getMaxDepth()));
02051 ppp->grid()->blindPopulate(inliers, pointCloud);
02052 ppp->grid()->calculateStartPos();
02053 newPlanes.push_back(ppp);
02054 }
02055 }
02056 }
02057 extractedPlanes.clear();
02058 addPlanePatchesAndUpdateSegmented(extractedPlanes, pointMapping, newPlanes);
02059 }
02060
02061
02068 void StructureColoring::mergePlanes(PlanePatches& extractedPlanes, PlanePatchVector& pointMapping, const PointCloud& pointCloud) {
02069 PlanePatches merged;
02070 CompArea compArea;
02071 extractedPlanes.sort(compArea);
02072 PlanePatches::iterator plane_it_it = extractedPlanes.begin();
02073 while (plane_it_it != extractedPlanes.end()) {
02074 float sacDist = (*plane_it_it)->getDistanceThreshold();
02075 std::vector<PlanePatches::iterator> mergeWithPlane;
02076 for (PlanePatches::iterator filteredPlane_it = extractedPlanes.begin(); filteredPlane_it
02077 != extractedPlanes.end(); filteredPlane_it++) {
02078 if (filteredPlane_it != plane_it_it) {
02079 sacDist = std::max((*filteredPlane_it)->getDistanceThreshold(), sacDist);
02080 if (((*plane_it_it)->getPlane3D().checkNormal((*filteredPlane_it)->getPlane3D().getPlaneNormal(),
02081 mParams.mMergePlanesSimilarityFactor * mParams.mAngleEps))
02082 && (*plane_it_it)->getPlane3D().checkDistance((*filteredPlane_it)->getPlaneCoG(), mParams.mMergePlanesSimilarityFactor * sacDist)
02083 && (*filteredPlane_it)->getPlane3D().checkDistance((*plane_it_it)->getPlaneCoG(), mParams.mMergePlanesSimilarityFactor * sacDist)) {
02084 if (planesAreConnected(**plane_it_it, **filteredPlane_it, pointCloud)) {
02085 mergeWithPlane.push_back(filteredPlane_it);
02086 }
02087 }
02088 }
02089 }
02090 if (!mergeWithPlane.empty()) {
02091 pclPointIndices pinliers, outInliers;
02092 pinliers.indices = (*plane_it_it)->getInliers();
02093 std::sort(pinliers.indices.begin(), pinliers.indices.end());
02094 float cellSize = (*plane_it_it)->grid()->getCellSize();
02095 for (unsigned int i = 0; i < mergeWithPlane.size(); i++) {
02096 cellSize = std::min(cellSize, (*mergeWithPlane[i])->grid()->getCellSize());
02097 pclPointIndices pinliers2;
02098 pinliers2.indices = (*mergeWithPlane[i])->getInliers();
02099 std::sort(pinliers2.indices.begin(), pinliers2.indices.end());
02100
02101 std::merge(pinliers.indices.begin(), pinliers.indices.end(), pinliers2.indices.begin(),
02102 pinliers2.indices.end(), std::back_inserter(outInliers.indices));
02103 pinliers.indices.clear();
02104 pinliers.indices.swap(outInliers.indices);
02105 extractedPlanes.erase(mergeWithPlane[i]);
02106 }
02107 for (PointIndices::const_iterator point_it = pinliers.indices.begin(); point_it != pinliers.indices.end(); point_it++) {
02108 pointMapping[*point_it].reset();
02109 }
02110 PlanePatchPtr pp;
02111 if (pinliers.indices.size() >= 3)
02112 pp.reset(new PlanePatch(pinliers.indices, pointCloud, sacDist));
02113 if (pp) {
02114 pp->newGrid(cellSize);
02115 pp->grid()->blindPopulate(pp->getInliers(), pointCloud);
02116 pp->grid()->calculateStartPos();
02117 merged.push_front(pp);
02118 plane_it_it = extractedPlanes.erase(plane_it_it);
02119 }
02120 } else
02121 ++plane_it_it;
02122 }
02123 addPlanePatchesAndUpdateSegmented(extractedPlanes, pointMapping, merged);
02124 }
02125
02126
02127
02128 bool StructureColoring::planesAreConnected(const PlanePatch& pp1, const PlanePatch& pp2, const PointCloud& pointCloud) {
02129 const PointIndices& pp2Inliers = pp2.getInliers();
02130 for (PointIndices::const_iterator pp2Inliers_it = pp2Inliers.begin(); pp2Inliers_it != pp2Inliers.end(); ++pp2Inliers_it) {
02131 if (pp1.checkPointConnection(pointCloud.points[*pp2Inliers_it].getVector3fMap(),
02132 mParams.mConnectionNeighbors))
02133 return true;
02134 }
02135 return false;
02136 }
02137
02138
02139
02140 void StructureColoring::addPlanePatchesAndUpdateSegmented(PlanePatches& extractedPlanes, PlanePatchVector& pointMapping,
02141 const PlanePatches& planePatches) {
02142 for (PlanePatches::const_iterator plane_it = planePatches.begin(); plane_it != planePatches.end(); ++plane_it) {
02143 extractedPlanes.push_front(*plane_it);
02144 const PointIndices& indices = (*plane_it)->getInliers();
02145 for (PointIndices::const_iterator it = indices.begin(); it != indices.end(); ++it) {
02146 if (pointMapping[*it])
02147 ROS_WARN("plane inliers are not disjunct anymore!");
02148 pointMapping[*it] = *plane_it;
02149 }
02150 }
02151 }
02152
02153
02154
02155 float StructureColoring::getSACDistanceFromDepth(const unsigned int& depth, const OcTree& octree) {
02156 float currentOctreeBinSize = octree.getCellSizeFromDepth(depth);
02157 float dist = std::max(mParams.mMinSACDistanceThreshold, std::min(mParams.mMaxSACDistanceThreshold, mParams.mSACOctreeFactor
02158 * currentOctreeBinSize));
02159 return dist;
02160 }
02161
02162
02163
02164 float StructureColoring::getHTDistanceFromDepth(const unsigned int& depth, const OcTree& octree) {
02165 float currentOctreeBinSize = octree.getCellSizeFromDepth(depth);
02166 float dist = std::min(mParams.mMaxHTDistanceThreshold, mParams.mHTOctreeBinSizeFactor * currentOctreeBinSize);
02167 return dist;
02168 }
02169
02170
02171
02172 void StructureColoring::getMinMaxRadiusFromDepth(float& minRadius, float& maxRadius, const OcTree& octree, const float& depth) {
02173 minRadius = mParams.mMinRadiusFactor * octree.getCellSizeFromDepth(depth);
02174 maxRadius = mParams.mMaxRadiusFactor * octree.getCellSizeFromDepth(depth);
02175 }
02176
02177
02178
02179 float StructureColoring::getAngleEpsFromDepth(const unsigned int& depth, const OcTree& octree) {
02180 float angleEps = mParams.mAngleEps * (1.f - (float) depth / (float) octree.getMaxDepth()) + mParams.mAngleEpsOnMinOctreeRes
02181 * ((float) depth / (float) octree.getMaxDepth());
02182 return angleEps;
02183 }
02184
02185
02186
02187 void StructureColoring::flipToViewport(Vec3& vec, const Vec3& viewport) {
02188 if (viewport.dot(vec) > 0) {
02189 vec *= -1.f;
02190 }
02191 }
02192
02193
02194
02195 double StructureColoring::segmentPlanesSAC(PlanePatches& extractedPlanes, PlanePatchVector& pointMapping, const PointCloudPtr& pointCloud)
02196 {
02197 ROS_INFO("segmentPlanesSAC");
02198 ros::Time start = ros::Time::now();
02199
02200 mLastCellSizeWithNormals = mParams.mMinSACDistanceThreshold;
02201 pcl::ModelCoefficients coefficients;
02202 pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
02203 pcl::PointIndices::Ptr indices (new pcl::PointIndices);
02204 size_t nrPoints = pointCloud->points.size();
02205 indices->indices.reserve(nrPoints);
02206 for (size_t i = 0; i < nrPoints; ++i)
02207 {
02208 indices->indices.push_back(i);
02209 }
02210
02211 pcl::SACSegmentation<PointT> seg;
02212
02213 seg.setOptimizeCoefficients (true);
02214 seg.setMaxIterations(mParams.mPclSACmaxIter);
02215
02216 seg.setModelType (pcl::SACMODEL_PLANE);
02217 seg.setMethodType (pcl::SAC_RANSAC);
02218 seg.setDistanceThreshold (mParams.mMinSACDistanceThreshold);
02219 seg.setInputCloud(pointCloud);
02220
02221 ros::Time end = ros::Time::now();
02222 ros::Duration dt = end - start;
02223 ROS_INFO("preprocessing time: %f\n", dt.toSec());
02224
02225 unsigned int failcounter = 0;
02226 unsigned int maxIter = 100;
02227
02228 while (indices->indices.size () > mParams.mMinPointsInCC && failcounter < maxIter)
02229 {
02230 ros::Time start2 = ros::Time::now();
02231 seg.setIndices(indices);
02232 seg.segment (*inliers, coefficients);
02233 end = ros::Time::now();
02234 dt = end - start2;
02235 if (mParams.mVerbose)
02236 ROS_INFO("segmentation time: %f", dt.toSec());
02237
02238 if (inliers->indices.size() >= mParams.mMinPointsInCC)
02239 {
02240
02241 Vec3 norm(coefficients.values[0], coefficients.values[1], coefficients.values[2]);
02242
02243
02244
02245 float distance = -1.f * coefficients.values[3];
02246 if (distance < 0) {
02247 distance *= -1.f;
02248 norm *= -1.f;
02249 }
02250 PlanePatchPtr pp = boost::make_shared<PlanePatch>(norm, distance, inliers->indices, *pointCloud, mParams.mMinSACDistanceThreshold);
02251
02252
02253
02254 std::vector<NodePointers> CCInliers;
02255 OcTreePtr octreePtr(new OcTree(mParams.mMinOctreeResolution, mParams.mRho_max, mParams.mPrincipalVarFactor,
02256 mParams.mMinPointsForNormal, mParams.mCurvThreshold, mParams.mCurv2Threshold, mParams.mSqDistFactor, 5));
02257 NodePointers inlierOctreeNodes;
02258 for (PointIndices::const_iterator in_it = pp->getInliers().begin();
02259 in_it != pp->getInliers().end(); ++in_it) {
02260 NodePtr n = new spatialaggregate::OcTreeNode<float, ValueClass>();
02261
02262
02263
02264
02265
02266
02267
02268
02269
02270
02271
02272
02273
02274
02275
02276
02277 n->depth_ = 0;
02278 n->value_.meanPos(0) = pointCloud->points[*in_it].x;
02279 n->value_.meanPos(1) = pointCloud->points[*in_it].y;
02280 n->value_.meanPos(2) = pointCloud->points[*in_it].z;
02281
02282 n->value_.normal = norm;
02283 n->value_.stable = true;
02284
02285
02286 n->value_.numPoints = *in_it;
02287
02288 inlierOctreeNodes.push_back(n);
02289 }
02290
02291 filterConnectedNodes(CCInliers, pp->getPlane3D(), inlierOctreeNodes, mParams.mMinSACDistanceThreshold);
02292
02293 bool ccextracted = false;
02294 ROS_INFO("found %zu connected components", CCInliers.size());
02295 for (unsigned int cc = 0; cc < CCInliers.size(); cc++) {
02296
02297 PointIndices CCIndices;
02298
02299 for( unsigned int ccn = 0; ccn < CCInliers[cc].size(); ccn++ )
02300 CCIndices.push_back( CCInliers[cc][ccn]->value_.numPoints );
02301
02302 size_t sizeOfInliers = CCIndices.size();
02303 if (sizeOfInliers >= mParams.mMinPointsInCC) {
02304
02305 ccextracted = true;
02306 PlanePatchPtr ccpp;
02307 ccpp.reset(new PlanePatch(pp->getPlane3D().getPlaneNormal(), pp->getPlane3D().getPlaneDistanceToOrigin(), CCIndices, *pointCloud, pp->getDistanceThreshold()));
02308
02309 extractedPlanes.push_back(ccpp);
02310 for (PointIndices::const_iterator in_it = CCIndices.begin();
02311 in_it != CCIndices.end(); ++in_it)
02312 {
02313 pointMapping[*in_it] = ccpp;
02314 }
02315
02316
02317 start2 = ros::Time::now();
02318 PointIndices current = indices->indices;
02319 std::sort(CCIndices.begin(), CCIndices.end());
02320 indices->indices.clear();
02321 std::set_difference(current.begin(), current.end(), CCIndices.begin(), CCIndices.end(), std::back_inserter(indices->indices));
02322 end = ros::Time::now();
02323 dt = end - start2;
02324 if (mParams.mVerbose)
02325 ROS_INFO("extraction time: %f, %zu left", dt.toSec(), indices->indices.size());
02326
02327 } else {
02328
02329 }
02330
02331 }
02332 if (!ccextracted)
02333 failcounter++;
02334
02335 for( unsigned int i = 0; i < inlierOctreeNodes.size(); i++ )
02336 delete inlierOctreeNodes[i];
02337
02338 }
02339 else
02340 {
02341 ROS_ERROR("Could not estimate a planar model for the given dataset.\n");
02342 break;
02343 }
02344
02345 }
02346 dt = end - start;
02347 ROS_INFO("segmentPlanesSAC time: %f", dt.toSec());
02348 return dt.toSec();
02349 }
02350
02351 double StructureColoring::segmentCylindersSAC(CylinderPatches& cylinderPatches, CylinderPatchVector& pointMapping, OcTree& octree, const PointCloudPtr& pointCloud)
02352 {
02353 typedef pcl::PointCloud<pcl::Normal> NormalCloud;
02354
02355 ROS_INFO("segmentCylinderSAC");
02356 double start = pcl::getTime();
02357
02358 pcl::ModelCoefficients coefficients;
02359 pcl::PointIndices::Ptr inliers (new pcl::PointIndices);
02360 pcl::PointIndices::Ptr indices (new pcl::PointIndices);
02361 size_t nrPoints = pointCloud->points.size();
02362 indices->indices.reserve(nrPoints);
02363 for (size_t i = 0; i < nrPoints; ++i)
02364 {
02365 indices->indices.push_back(i);
02366 }
02367
02368 NormalCloud::Ptr normalCloud(new NormalCloud);
02369 pcl::NormalEstimation<PointT, pcl::Normal> ne;
02370
02371
02372 pcl::search::KdTree<pcl::PointXYZRGB>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZRGB>);
02373 tree->setInputCloud (pointCloud);
02374 ne.setSearchMethod (tree);
02375
02376 ne.setRadiusSearch (mParams.mMinOctreeResolution);
02377
02378 ne.setViewPoint(0.f, 0.f, 0.f);
02379
02380 ne.setInputCloud (pointCloud);
02381 ne.compute (*normalCloud);
02382 ROS_INFO("segmentCylinderSAC: normals computed.");
02383
02384 pcl::SACSegmentationFromNormals<PointT, pcl::Normal> seg;
02385 seg.setOptimizeCoefficients(true);
02386 seg.setModelType(pcl::SACMODEL_CYLINDER);
02387 seg.setMethodType(pcl::SAC_RANSAC);
02388 seg.setMaxIterations(mParams.mPclSACmaxIter);
02389
02390
02391 seg.setDistanceThreshold(mParams.mMinSACDistanceThreshold);
02392 seg.setNormalDistanceWeight(mParams.mNormalDistanceWeight);
02393 seg.setRadiusLimits (mParams.mMinCylinderRadius, mParams.mMaxCylinderRadius);
02394
02395 seg.setInputCloud(pointCloud);
02396 seg.setInputNormals(normalCloud);
02397
02398 unsigned int failcounter = 0;
02399 while ((indices->indices.size () > (nrPoints/2)) && (failcounter < mParams.mTriesOnEachDepth))
02400 {
02401 ROS_INFO("segmentCylinderSAC: segmenting, %zu points left ...", indices->indices.size());
02402 seg.setIndices(indices);
02403 seg.segment(*inliers, coefficients);
02404
02405 if (inliers->indices.size() >= mParams.mMinPointsInCC)
02406 {
02407 Vec3 pointOnAxis(coefficients.values[0], coefficients.values[1], coefficients.values[2]);
02408 Vec3 axisDirection(coefficients.values[3], coefficients.values[4], coefficients.values[5]);
02409 float radius(coefficients.values[6]);
02410 CylinderPatchPtr cylinder = boost::make_shared<CylinderPatch>(inliers->indices, *pointCloud, pointOnAxis, axisDirection, radius, mParams.mMinSACDistanceThreshold);
02411 if (checkCylinder(cylinder, pointCloud))
02412 {
02413 cylinderPatches.push_back(cylinder);
02414 for (PointIndices::const_iterator in_it = cylinder->getInliers().begin();
02415 in_it != cylinder->getInliers().end(); ++in_it)
02416 {
02417 pointMapping[*in_it] = cylinder;
02418 }
02419 }
02420 else
02421 failcounter++;
02422 }
02423 else {
02424 ROS_ERROR("RANSAC: no cylinder found, aborting");
02425 break;
02426 }
02427
02428 PointIndices current = indices->indices;
02429 indices->indices.clear();
02430 std::set_difference(current.begin(), current.end(), inliers->indices.begin(), inliers->indices.end(), std::back_inserter(indices->indices));
02431 }
02432 double end = pcl::getTime();
02433 double elapsedTime = end - start;
02434 ROS_INFO("segmentCylinderSAC time: %f", elapsedTime);
02435 return elapsedTime;
02436 }
02437
02438 bool StructureColoring::checkCylinderDimensions(const CylinderPatchPtr& cylinder) const
02439 {
02440 return ((cylinder->getRadius() > mParams.mMinCylinderRadius) && (cylinder->getRadius() < mParams.mMaxCylinderRadius));
02441 }
02442
02443 bool StructureColoring::checkCylinder(const CylinderPatchPtr& cylinder, const PointCloudPtr& pointCloud) const
02444 {
02445 float cellSize = mParams.mMinOctreeResolution;
02446 CylinderGridMap grid(cellSize, cylinder, *pointCloud);
02447 size_t nrCells = grid.getCells().size();
02448 assert(nrCells == (grid.getWidth()*grid.getHeight()));
02449
02450 int count = 0;
02451 for (size_t i = 0; i < nrCells; ++i)
02452 if (grid.getCells()[i]) ++count;
02453 float occupied = (float)count/(float)nrCells;
02454 return occupied > mParams.mOccupiedRatio;
02455 }
02456