From 816b54bd258528e24271e9884155735cc9ba55b6 Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 3 Mar 2016 18:07:17 +0100 Subject: [PATCH] + add segmentation algorithm --- .../App/AppReverseEngineering.cpp | 35 ++++- src/Mod/ReverseEngineering/App/CMakeLists.txt | 2 + .../ReverseEngineering/App/Segmentation.cpp | 148 ++++++++++++++++++ src/Mod/ReverseEngineering/App/Segmentation.h | 52 ++++++ 4 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 src/Mod/ReverseEngineering/App/Segmentation.cpp create mode 100644 src/Mod/ReverseEngineering/App/Segmentation.h diff --git a/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp b/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp index 144d14c3b..47d48f77f 100644 --- a/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp +++ b/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp @@ -45,6 +45,7 @@ #include "BSplineFitting.h" #include "SurfaceTriangulation.h" #include "RegionGrowing.h" +#include "Segmentation.h" #include "SampleConsensus.h" #if defined(HAVE_PCL_FILTERS) #include @@ -99,8 +100,11 @@ public: add_keyword_method("regionGrowingSegmentation",&Module::regionGrowingSegmentation, "regionGrowingSegmentation()." ); + add_keyword_method("featureSegmentation",&Module::featureSegmentation, + "featureSegmentation()." + ); #endif -#if defined(HAVE_PCL_SEGMENTATION) +#if defined(HAVE_PCL_SAMPLE_CONSENSUS) add_keyword_method("sampleConsensus",&Module::sampleConsensus, "sampleConsensus()." ); @@ -625,10 +629,37 @@ Mesh.show(m) lists.append(tuple); } + return lists; + } + Py::Object featureSegmentation(const Py::Tuple& args, const Py::Dict& kwds) + { + PyObject *pts; + int ksearch=5; + + static char* kwds_segment[] = {"Points", "KSearch", NULL}; + if (!PyArg_ParseTupleAndKeywords(args.ptr(), kwds.ptr(), "O!|i", kwds_segment, + &(Points::PointsPy::Type), &pts, &ksearch)) + throw Py::Exception(); + + Points::PointKernel* points = static_cast(pts)->getPointKernelPtr(); + + std::list > clusters; + Segmentation segm(*points, clusters); + segm.perform(ksearch); + + Py::List lists; + for (std::list >::iterator it = clusters.begin(); it != clusters.end(); ++it) { + Py::Tuple tuple(it->size()); + for (std::size_t i = 0; i < it->size(); i++) { + tuple.setItem(i, Py::Long((*it)[i])); + } + lists.append(tuple); + } + return lists; } #endif -#if defined(HAVE_PCL_SEGMENTATION) +#if defined(HAVE_PCL_SAMPLE_CONSENSUS) Py::Object sampleConsensus(const Py::Tuple& args, const Py::Dict& kwds) { PyObject *pts; diff --git a/src/Mod/ReverseEngineering/App/CMakeLists.txt b/src/Mod/ReverseEngineering/App/CMakeLists.txt index d81f28338..b2e1f8008 100644 --- a/src/Mod/ReverseEngineering/App/CMakeLists.txt +++ b/src/Mod/ReverseEngineering/App/CMakeLists.txt @@ -65,6 +65,8 @@ SET(Reen_SRCS RegionGrowing.h SampleConsensus.cpp SampleConsensus.h + Segmentation.cpp + Segmentation.h SurfaceTriangulation.cpp SurfaceTriangulation.h PreCompiled.cpp diff --git a/src/Mod/ReverseEngineering/App/Segmentation.cpp b/src/Mod/ReverseEngineering/App/Segmentation.cpp new file mode 100644 index 000000000..0dd2b84c8 --- /dev/null +++ b/src/Mod/ReverseEngineering/App/Segmentation.cpp @@ -0,0 +1,148 @@ +/*************************************************************************** + * Copyright (c) 2016 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" + +#include "Segmentation.h" +#include +#include + +#if defined(HAVE_PCL_SEGMENTATION) +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace Reen; +using pcl::PointXYZ; +using pcl::PointNormal; +using pcl::PointCloud; + +Segmentation::Segmentation(const Points::PointKernel& pts, std::list >& clusters) + : myPoints(pts) + , myClusters(clusters) +{ +} + +void Segmentation::perform(int ksearch) +{ + // All the objects needed + pcl::PassThrough pass; + pcl::NormalEstimation ne; + pcl::SACSegmentationFromNormals seg; + pcl::ExtractIndices extract; + pcl::ExtractIndices extract_normals; + pcl::search::KdTree::Ptr tree (new pcl::search::KdTree ()); + + // Datasets + pcl::PointCloud::Ptr cloud (new pcl::PointCloud); + pcl::PointCloud::Ptr cloud_filtered (new pcl::PointCloud); + pcl::PointCloud::Ptr cloud_normals (new pcl::PointCloud); + pcl::PointCloud::Ptr cloud_filtered2 (new pcl::PointCloud); + pcl::PointCloud::Ptr cloud_normals2 (new pcl::PointCloud); + pcl::ModelCoefficients::Ptr coefficients_plane (new pcl::ModelCoefficients), coefficients_cylinder (new pcl::ModelCoefficients); + pcl::PointIndices::Ptr inliers_plane (new pcl::PointIndices), inliers_cylinder (new pcl::PointIndices); + + // Copy the points + cloud->reserve(myPoints.size()); + for (Points::PointKernel::const_iterator it = myPoints.begin(); it != myPoints.end(); ++it) { + cloud->push_back(pcl::PointXYZ(it->x, it->y, it->z)); + } + + cloud->width = int (cloud->points.size ()); + cloud->height = 1; + + // Build a passthrough filter to remove spurious NaNs + pass.setInputCloud (cloud); + pass.setFilterFieldName ("z"); + pass.setFilterLimits (0, 1.5); + pass.filter (*cloud_filtered); + + // Estimate point normals + ne.setSearchMethod (tree); + ne.setInputCloud (cloud_filtered); + ne.setKSearch (50); + ne.compute (*cloud_normals); + + // Create the segmentation object for the planar model and set all the parameters + seg.setOptimizeCoefficients (true); + seg.setModelType (pcl::SACMODEL_NORMAL_PLANE); + seg.setNormalDistanceWeight (0.1); + seg.setMethodType (pcl::SAC_RANSAC); + seg.setMaxIterations (100); + seg.setDistanceThreshold (0.03); + seg.setInputCloud (cloud_filtered); + seg.setInputNormals (cloud_normals); + + // Obtain the plane inliers and coefficients + seg.segment (*inliers_plane, *coefficients_plane); + myClusters.push_back(inliers_plane->indices); + + // Extract the planar inliers from the input cloud + extract.setInputCloud (cloud_filtered); + extract.setIndices (inliers_plane); + extract.setNegative (false); + + // Write the planar inliers to disk + pcl::PointCloud::Ptr cloud_plane (new pcl::PointCloud ()); + extract.filter (*cloud_plane); + + // Remove the planar inliers, extract the rest + extract.setNegative (true); + extract.filter (*cloud_filtered2); + extract_normals.setNegative (true); + extract_normals.setInputCloud (cloud_normals); + extract_normals.setIndices (inliers_plane); + extract_normals.filter (*cloud_normals2); + + // Create the segmentation object for cylinder segmentation and set all the parameters + seg.setOptimizeCoefficients (true); + seg.setModelType (pcl::SACMODEL_CYLINDER); + seg.setNormalDistanceWeight (0.1); + seg.setMethodType (pcl::SAC_RANSAC); + seg.setMaxIterations (10000); + seg.setDistanceThreshold (0.05); + seg.setRadiusLimits (0, 0.1); + seg.setInputCloud (cloud_filtered2); + seg.setInputNormals (cloud_normals2); + + // Obtain the cylinder inliers and coefficients + seg.segment (*inliers_cylinder, *coefficients_cylinder); + myClusters.push_back(inliers_cylinder->indices); + + // Write the cylinder inliers to disk + extract.setInputCloud (cloud_filtered2); + extract.setIndices (inliers_cylinder); + extract.setNegative (false); + pcl::PointCloud::Ptr cloud_cylinder (new pcl::PointCloud ()); + extract.filter (*cloud_cylinder); +} + +#endif // HAVE_PCL_SEGMENTATION + diff --git a/src/Mod/ReverseEngineering/App/Segmentation.h b/src/Mod/ReverseEngineering/App/Segmentation.h new file mode 100644 index 000000000..4e55362c4 --- /dev/null +++ b/src/Mod/ReverseEngineering/App/Segmentation.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (c) 2016 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#ifndef REEN_SEGMENTATION_H +#define REEN_SEGMENTATION_H + +#include +#include +#include + +namespace Points {class PointKernel;} + +namespace Reen { + +class Segmentation +{ +public: + Segmentation(const Points::PointKernel&, std::list >& clusters); + /** \brief Set the number of k nearest neighbors to use for the normal estimation. + * \param[in] k the number of k-nearest neighbors + */ + void perform(int ksearch); + +private: + const Points::PointKernel& myPoints; + std::list >& myClusters; +}; + +} // namespace Reen + +#endif // REEN_SEGMENTATION_H +