diff --git a/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp b/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp index 0f8c79474d..2eb291c43f 100644 --- a/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp +++ b/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp @@ -725,27 +725,92 @@ Mesh.show(m) } #endif #if defined(HAVE_PCL_SAMPLE_CONSENSUS) + /* +import ReverseEngineering as reen +import Points +import Part + +p = App.ActiveDocument.Points.Points +data = p.Points +n = reen.normalEstimation(p, 10) + +model = reen.sampleConsensus(SacModel="Plane", Points=p) +indices = model["Model"] +param = model["Parameters"] + +plane = Part.Plane() +plane.Axis = param[0:3] +plane.Position = -plane.Axis * param[3] + +np = Points.Points() +np.addPoints([data[i] for i in indices]) +Points.show(np) + +# sort in descending order +indices = list(indices) +indices.sort(reverse=True) + +# remove points of segment +for i in indices: + del data[i] + del n[i] + +p = Points.Points() +p.addPoints(data) +model = reen.sampleConsensus(SacModel="Cylinder", Points=p, Normals=n) +indices = model["Model"] + +np = Points.Points() +np.addPoints([data[i] for i in indices]) +Points.show(np) + */ Py::Object sampleConsensus(const Py::Tuple& args, const Py::Dict& kwds) { PyObject *pts; + PyObject *vec = nullptr; + const char* sacModelType = nullptr; - static char* kwds_sample[] = {"Points", NULL}; - if (!PyArg_ParseTupleAndKeywords(args.ptr(), kwds.ptr(), "O!", kwds_sample, - &(Points::PointsPy::Type), &pts)) + static char* kwds_sample[] = {"SacModel", "Points", "Normals", NULL}; + if (!PyArg_ParseTupleAndKeywords(args.ptr(), kwds.ptr(), "sO!|O", kwds_sample, + &sacModelType, &(Points::PointsPy::Type), &pts, &vec)) throw Py::Exception(); Points::PointKernel* points = static_cast(pts)->getPointKernelPtr(); + std::vector normals; + if (vec) { + Py::Sequence list(vec); + 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(v); + } + } + + SampleConsensus::SacModel sacModel = SampleConsensus::SACMODEL_PLANE; + if (sacModelType) { + if (strcmp(sacModelType, "Cylinder") == 0) + sacModel = SampleConsensus::SACMODEL_CYLINDER; + else if (strcmp(sacModelType, "Sphere") == 0) + sacModel = SampleConsensus::SACMODEL_SPHERE; + else if (strcmp(sacModelType, "Cone") == 0) + sacModel = SampleConsensus::SACMODEL_CONE; + } std::vector parameters; - SampleConsensus sample(*points); - double probability = sample.perform(parameters); + SampleConsensus sample(sacModel, *points, normals); + std::vector model; + double probability = sample.perform(parameters, model); 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])); + Py::Tuple data(model.size()); + for (std::size_t i = 0; i < model.size(); i++) + data.setItem(i, Py::Long(model[i])); dict.setItem(Py::String("Probability"), Py::Float(probability)); dict.setItem(Py::String("Parameters"), tuple); + dict.setItem(Py::String("Model"), data); return dict; } diff --git a/src/Mod/ReverseEngineering/App/SampleConsensus.cpp b/src/Mod/ReverseEngineering/App/SampleConsensus.cpp index cb0d654a66..9c53911e5a 100644 --- a/src/Mod/ReverseEngineering/App/SampleConsensus.cpp +++ b/src/Mod/ReverseEngineering/App/SampleConsensus.cpp @@ -30,8 +30,12 @@ #if defined(HAVE_PCL_SAMPLE_CONSENSUS) #include +#include #include #include +#include +#include +#include using namespace std; using namespace Reen; @@ -39,12 +43,14 @@ using pcl::PointXYZ; using pcl::PointNormal; using pcl::PointCloud; -SampleConsensus::SampleConsensus(const Points::PointKernel& pts) - : myPoints(pts) +SampleConsensus::SampleConsensus(SacModel sac, const Points::PointKernel& pts, const std::vector& nor) + : mySac(sac) + , myPoints(pts) + , myNormals(nor) { } -double SampleConsensus::perform(std::vector& parameters) +double SampleConsensus::perform(std::vector& parameters, std::vector& model) { pcl::PointCloud::Ptr cloud (new pcl::PointCloud); cloud->reserve(myPoints.size()); @@ -57,14 +63,67 @@ double SampleConsensus::perform(std::vector& parameters) cloud->height = 1; cloud->is_dense = true; + pcl::PointCloud::Ptr normals (new pcl::PointCloud ()); + if (mySac == SACMODEL_CONE || mySac == SACMODEL_CYLINDER) { +#if 0 + // Create search tree + pcl::search::KdTree::Ptr tree; + tree.reset (new pcl::search::KdTree (false)); + tree->setInputCloud (cloud); + + // Normal estimation + int ksearch = 10; + pcl::NormalEstimation n; + n.setInputCloud (cloud); + n.setSearchMethod (tree); + n.setKSearch (ksearch); + n.compute (*normals); +#else + normals->reserve(myNormals.size()); + for (std::vector::const_iterator it = myNormals.begin(); it != myNormals.end(); ++it) { + if (!boost::math::isnan(it->x) && !boost::math::isnan(it->y) && !boost::math::isnan(it->z)) + normals->push_back(pcl::Normal(it->x, it->y, it->z)); + } +#endif + } + // created RandomSampleConsensus object and compute the appropriated model - pcl::SampleConsensusModelPlane::Ptr - model_p (new pcl::SampleConsensusModelPlane (cloud)); + pcl::SampleConsensusModel::Ptr model_p; + switch (mySac) { + case SACMODEL_PLANE: + { + model_p.reset(new pcl::SampleConsensusModelPlane (cloud)); + break; + } + case SACMODEL_SPHERE: + { + model_p.reset(new pcl::SampleConsensusModelSphere (cloud)); + break; + } + case SACMODEL_CONE: + { + pcl::SampleConsensusModelCone::Ptr model_c + (new pcl::SampleConsensusModelCone (cloud)); + model_c->setInputNormals(normals); + model_p = model_c; + break; + } + case SACMODEL_CYLINDER: + { + pcl::SampleConsensusModelCylinder::Ptr model_c + (new pcl::SampleConsensusModelCylinder (cloud)); + model_c->setInputNormals(normals); + model_p = model_c; + break; + } + default: + throw Base::RuntimeError("Unsupported SAC model"); + } pcl::RandomSampleConsensus ransac (model_p); ransac.setDistanceThreshold (.01); ransac.computeModel(); - //ransac.getInliers(inliers); + ransac.getInliers(model); //ransac.getModel (model); Eigen::VectorXf model_p_coefficients; ransac.getModelCoefficients (model_p_coefficients); diff --git a/src/Mod/ReverseEngineering/App/SampleConsensus.h b/src/Mod/ReverseEngineering/App/SampleConsensus.h index e24780082b..0d8ea2cdf4 100644 --- a/src/Mod/ReverseEngineering/App/SampleConsensus.h +++ b/src/Mod/ReverseEngineering/App/SampleConsensus.h @@ -34,11 +34,24 @@ namespace Reen { class SampleConsensus { public: - SampleConsensus(const Points::PointKernel&); - double perform(std::vector& parameters); + enum SacModel + { + SACMODEL_PLANE, + SACMODEL_LINE, + SACMODEL_CIRCLE2D, + SACMODEL_CIRCLE3D, + SACMODEL_SPHERE, + SACMODEL_CYLINDER, + SACMODEL_CONE, + SACMODEL_TORUS, + }; + SampleConsensus(SacModel sac, const Points::PointKernel&, const std::vector&); + double perform(std::vector& parameters, std::vector& model); private: + SacModel mySac; const Points::PointKernel& myPoints; + const std::vector& myNormals; }; } // namespace Reen