diff --git a/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp b/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp index 0fc1761fb..dacfd7992 100644 --- a/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp +++ b/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp @@ -44,7 +44,10 @@ #include "ApproxSurface.h" #include "BSplineFitting.h" #include "SurfaceTriangulation.h" +#include "RegionGrowing.h" +#include "SampleConsensus.h" #if defined(HAVE_PCL_FILTERS) +#include #include #include #endif @@ -92,6 +95,16 @@ public: "filterVoxelGrid(dim)." ); #endif +#if defined(HAVE_PCL_SEGMENTATION) + add_keyword_method("regionGrowingSegmentation",&Module::regionGrowingSegmentation, + "regionGrowingSegmentation()." + ); +#endif +#if defined(HAVE_PCL_SEGMENTATION) + add_keyword_method("sampleConsensus",&Module::sampleConsensus, + "sampleConsensus()." + ); +#endif initialize("This module is the ReverseEngineering module."); // register with Python } @@ -572,6 +585,74 @@ Mesh.show(m) return Py::asObject(new Points::PointsPy(points_sample)); } #endif +#if defined(HAVE_PCL_SEGMENTATION) + Py::Object regionGrowingSegmentation(const Py::Tuple& args, const Py::Dict& kwds) + { + PyObject *pts; + PyObject *vec = 0; + int ksearch=5; + + static char* kwds_segment[] = {"Points", "KSearch", "Normals", NULL}; + if (!PyArg_ParseTupleAndKeywords(args.ptr(), kwds.ptr(), "O!|iO", kwds_segment, + &(Points::PointsPy::Type), &pts, + &ksearch, &vec)) + throw Py::Exception(); + + Points::PointKernel* points = static_cast(pts)->getPointKernelPtr(); + + std::list > clusters; + RegionGrowing segm(*points, clusters); + if (vec) { + Py::Sequence list(vec); + std::vector normals; + normals.reserve(list.size()); + for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) { + Base::Vector3d v = Py::Vector(*it).toVector(); + normals.push_back(Base::convertTo(v)); + } + segm.perform(normals); + } + else { + 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) + Py::Object sampleConsensus(const Py::Tuple& args, const Py::Dict& kwds) + { + PyObject *pts; + + static char* kwds_sample[] = {"Points", NULL}; + if (!PyArg_ParseTupleAndKeywords(args.ptr(), kwds.ptr(), "O!", kwds_sample, + &(Points::PointsPy::Type), &pts)) + throw Py::Exception(); + + Points::PointKernel* points = static_cast(pts)->getPointKernelPtr(); + + std::vector parameters; + SampleConsensus sample(*points); + double probability = sample.perform(parameters); + + Py::Dict dict; + Py::Tuple tuple(parameters.size()); + for (std::size_t i = 0; i < parameters.size(); i++) + tuple.setItem(i, Py::Float(parameters[i])); + dict.setItem(Py::String("Probability"), Py::Float(probability)); + + return dict; + } +#endif }; } // namespace Reen diff --git a/src/Mod/ReverseEngineering/App/CMakeLists.txt b/src/Mod/ReverseEngineering/App/CMakeLists.txt index 8f5cdc584..d81f28338 100644 --- a/src/Mod/ReverseEngineering/App/CMakeLists.txt +++ b/src/Mod/ReverseEngineering/App/CMakeLists.txt @@ -16,6 +16,14 @@ if (PCL_FILTERS_FOUND AND PCL_FEATURES_FOUND) add_definitions(-DHAVE_PCL_FILTERS) endif () +if (PCL_SEGMENTATION_FOUND AND PCL_FEATURES_FOUND) + add_definitions(-DHAVE_PCL_SEGMENTATION) +endif () + +if (PCL_SAMPLE_CONSENSUS_FOUND) + add_definitions(-DHAVE_PCL_SAMPLE_CONSENSUS) +endif () + include_directories( ${CMAKE_SOURCE_DIR}/src ${Boost_INCLUDE_DIRS} @@ -42,6 +50,8 @@ set(Reen_LIBS ${PCL_FILTERS_LIBRARIES} ${PCL_SEARCH_LIBRARIES} ${PCL_SURFACE_LIBRARIES} + ${PCL_SEGMENTATION_LIBRARIES} + ${PCL_SAMPLE_CONSENSUS_LIBRARIES} ${QT_QTCORE_LIBRARY} ) @@ -51,6 +61,10 @@ SET(Reen_SRCS ApproxSurface.h BSplineFitting.cpp BSplineFitting.h + RegionGrowing.cpp + RegionGrowing.h + SampleConsensus.cpp + SampleConsensus.h SurfaceTriangulation.cpp SurfaceTriangulation.h PreCompiled.cpp diff --git a/src/Mod/ReverseEngineering/App/RegionGrowing.cpp b/src/Mod/ReverseEngineering/App/RegionGrowing.cpp new file mode 100644 index 000000000..a81e5c48d --- /dev/null +++ b/src/Mod/ReverseEngineering/App/RegionGrowing.cpp @@ -0,0 +1,147 @@ +/*************************************************************************** + * 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 "RegionGrowing.h" +#include +#include + +#if defined(HAVE_PCL_FILTERS) +#include +#include +#endif +#if defined(HAVE_PCL_SEGMENTATION) +#include +#include +#include +#include +#include + +using namespace std; +using namespace Reen; +using pcl::PointXYZ; +using pcl::PointNormal; +using pcl::PointCloud; + +RegionGrowing::RegionGrowing(const Points::PointKernel& pts, std::list >& clusters) + : myPoints(pts) + , myClusters(clusters) +{ +} + +void RegionGrowing::perform(int ksearch) +{ + pcl::PointCloud::Ptr cloud (new pcl::PointCloud); + 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)); + } + + //normal estimation + pcl::search::Search::Ptr tree = boost::shared_ptr > (new pcl::search::KdTree); + pcl::PointCloud ::Ptr normals (new pcl::PointCloud ); + pcl::NormalEstimation normal_estimator; + normal_estimator.setSearchMethod (tree); + normal_estimator.setInputCloud (cloud); + normal_estimator.setKSearch (ksearch); + normal_estimator.compute (*normals); + + // pass through + pcl::IndicesPtr indices (new std::vector ); + pcl::PassThrough pass; + pass.setInputCloud (cloud); + pass.setFilterFieldName ("z"); + pass.setFilterLimits (0.0, 1.0); + pass.filter (*indices); + + pcl::RegionGrowing reg; + reg.setMinClusterSize (50); + reg.setMaxClusterSize (1000000); + reg.setSearchMethod (tree); + reg.setNumberOfNeighbours (30); + reg.setInputCloud (cloud); + //reg.setIndices (indices); + reg.setInputNormals (normals); + reg.setSmoothnessThreshold (3.0 / 180.0 * M_PI); + reg.setCurvatureThreshold (1.0); + + std::vector clusters; + reg.extract (clusters); + + for (std::vector::iterator it = clusters.begin (); it != clusters.end (); ++it) { + myClusters.push_back(std::vector()); + myClusters.back().swap(it->indices); + } +} + +void RegionGrowing::perform(const std::vector& myNormals) +{ + if (myPoints.size() != myNormals.size()) + throw Base::RuntimeError("Number of points doesn't match with number of normals"); + + pcl::PointCloud::Ptr cloud (new pcl::PointCloud); + 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)); + } + + pcl::search::Search::Ptr tree = boost::shared_ptr > (new pcl::search::KdTree); + tree->setInputCloud (cloud); + + pcl::PointCloud ::Ptr normals (new pcl::PointCloud ); + normals->reserve(myNormals.size()); + for (std::vector::const_iterator it = myNormals.begin(); it != myNormals.end(); ++it) { + normals->push_back(pcl::Normal(it->x, it->y, it->z)); + } + + // pass through + pcl::IndicesPtr indices (new std::vector ); + pcl::PassThrough pass; + pass.setInputCloud (cloud); + pass.setFilterFieldName ("z"); + pass.setFilterLimits (0.0, 1.0); + pass.filter (*indices); + + pcl::RegionGrowing reg; + reg.setMinClusterSize (50); + reg.setMaxClusterSize (1000000); + reg.setSearchMethod (tree); + reg.setNumberOfNeighbours (30); + reg.setInputCloud (cloud); + //reg.setIndices (indices); + reg.setInputNormals (normals); + reg.setSmoothnessThreshold (3.0 / 180.0 * M_PI); + reg.setCurvatureThreshold (1.0); + + std::vector clusters; + reg.extract (clusters); + + for (std::vector::iterator it = clusters.begin (); it != clusters.end (); ++it) { + myClusters.push_back(std::vector()); + myClusters.back().swap(it->indices); + } +} + +#endif // HAVE_PCL_SEGMENTATION + diff --git a/src/Mod/ReverseEngineering/App/RegionGrowing.h b/src/Mod/ReverseEngineering/App/RegionGrowing.h new file mode 100644 index 000000000..c241f1de8 --- /dev/null +++ b/src/Mod/ReverseEngineering/App/RegionGrowing.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * 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_REGIONGROWING_H +#define REEN_REGIONGROWING_H + +#include +#include +#include + +namespace Points {class PointKernel;} + +namespace Reen { + +class RegionGrowing +{ +public: + RegionGrowing(const Points::PointKernel&, std::list >&); + /** \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); + /** \brief Pass the normals to the points given in the constructor. + * \param[in] normals the normals to the given points. + */ + void perform(const std::vector& normals); + +private: + const Points::PointKernel& myPoints; + std::list >& myClusters; +}; + +} // namespace Reen + +#endif // REEN_REGIONGROWING_H + diff --git a/src/Mod/ReverseEngineering/App/SampleConsensus.cpp b/src/Mod/ReverseEngineering/App/SampleConsensus.cpp new file mode 100644 index 000000000..2e398bf90 --- /dev/null +++ b/src/Mod/ReverseEngineering/App/SampleConsensus.cpp @@ -0,0 +1,76 @@ +/*************************************************************************** + * 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 "SampleConsensus.h" +#include +#include + +#if defined(HAVE_PCL_SAMPLE_CONSENSUS) +#include +#include +#include + +using namespace std; +using namespace Reen; +using pcl::PointXYZ; +using pcl::PointNormal; +using pcl::PointCloud; + +SampleConsensus::SampleConsensus(const Points::PointKernel& pts) + : myPoints(pts) +{ +} + +double SampleConsensus::perform(std::vector& parameters) +{ + pcl::PointCloud::Ptr cloud (new pcl::PointCloud); + 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; + cloud->is_dense = true; + + // created RandomSampleConsensus object and compute the appropriated model + pcl::SampleConsensusModelPlane::Ptr + model_p (new pcl::SampleConsensusModelPlane (cloud)); + + pcl::RandomSampleConsensus ransac (model_p); + ransac.setDistanceThreshold (.01); + ransac.computeModel(); + //ransac.getInliers(inliers); + //ransac.getModel (model); + Eigen::VectorXf model_p_coefficients; + ransac.getModelCoefficients (model_p_coefficients); + for (int i=0; i * + * * + * 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_SAMPLECONSENSUS_H +#define REEN_SAMPLECONSENSUS_H + +#include +#include + +namespace Points {class PointKernel;} + +namespace Reen { + +class SampleConsensus +{ +public: + SampleConsensus(const Points::PointKernel&); + double perform(std::vector& parameters); + +private: + const Points::PointKernel& myPoints; +}; + +} // namespace Reen + +#endif // REEN_SAMPLECONSENSUS_H +