From 398d2e45b0e45b4ec9e91960000df1f287ac0771 Mon Sep 17 00:00:00 2001 From: Bertrand Kerautret Date: Tue, 5 Mar 2024 15:48:20 +0100 Subject: [PATCH 01/14] base meshTrunk2Ptd --- geometry3d/CMakeLists.txt | 1 + geometry3d/meshTrunk2Pts.cpp | 123 +++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 geometry3d/meshTrunk2Pts.cpp diff --git a/geometry3d/CMakeLists.txt b/geometry3d/CMakeLists.txt index cd759e0..77e2e83 100644 --- a/geometry3d/CMakeLists.txt +++ b/geometry3d/CMakeLists.txt @@ -11,6 +11,7 @@ SET(DGTAL_TOOLS_DEVEL_SRC vol2meshAndNormals meshAxisCutter graph2vol + meshTrunk2Pts ) diff --git a/geometry3d/meshTrunk2Pts.cpp b/geometry3d/meshTrunk2Pts.cpp new file mode 100644 index 0000000..b5aefa4 --- /dev/null +++ b/geometry3d/meshTrunk2Pts.cpp @@ -0,0 +1,123 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +/** + * @file + * @ingroup geometry3d + * @author Bertrand Kerautret (\c bertrand.kerautret@univ-lyon2.fr ) + * Laboratoire d'InfoRmatique en Image et Systemes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * + * @date 2024/03/05 + * + * Source file of the tool meshTrunk2Pts + * + * This file is part of the DGtal library/DGtalTools-contrib Project. + */ + +/////////////////////////////////////////////////////////////////////////////// +#include "DGtal/base/Common.h" +#include "DGtal/helpers/StdDefs.h" +#include "DGtal/io/readers/MeshReader.h" +#include "DGtal/io/readers/TableReader.h" + +#include "CLI11.hpp" + +/////////////////////////////////////////////////////////////////////////////// +using namespace std; +using namespace DGtal; +/////////////////////////////////////////////////////////////////////////////// + + +/** + @page meshTrunk2Pts meshTrunk2Pts + + @brief Description of the tool... + + @b Usage: meshTrunk2Pts [input] + + @b Allowed @b options @b are : + + @code + -h [ --help ] display this message + -i [ --input ] arg an input file... + -p [ --parameter] arg a double parameter... + @endcode + + @b Example: + + @code + meshTrunk2Pts -i $DGtal/examples/samples/.... + @endcode + + @image html resmeshTrunk2Pts.png "Example of result. " + + @see + @ref meshTrunk2Pts.cpp + + */ + + +int main( int argc, char** argv ) +{ + typedef std::vector CylCoordsCont; + double parameter {1.0}; + std::string inputMeshFileName; + std::string inputCLineFileName; + + std::string outputBaseName; + std::stringstream usage; + usage << "Usage: " << argv[0] << " [input]\n" + << "Typical use example:\n \t meshTrunk2Pts -i ... \n"; + // parse command line using CLI------------------------------------------------------- + CLI::App app; + app.description("Transform an input mesh into points cloud simulating acquisition process like lidar Scan .\n" + usage.str() ); + app.add_option("--inputMesh,-i,1", inputMeshFileName, "Input file") + ->required()->check(CLI::ExistingFile); + app.add_option("--InputCCoords,-c,2", inputCLineFileName, "Input file containing cylinder coordinates") + ->required()->check(CLI::ExistingFile); + + app.add_option("--outputBaseName,-o,3", outputBaseName, "Output SDP filename")->required(); + app.add_option("--parameter,-p", parameter, "a double parameter", true); + + app.get_formatter()->column_width(40); + CLI11_PARSE(app, argc, argv); + // END parse command line using CLI ---------------------------------------------- + + + // Reading input mesh -------------------------------------------------- + DGtal::Mesh aMesh; + DGtal::Mesh aCenterLine; + std::vector cylCoordinates; + + trace.info() << "Starting " << argv[0] << "with input: " + << inputMeshFileName << " and output :" << outputBaseName + << " param: " << parameter <::getLinesElementsFromFile(inputCLineFileName); + trace.info() << " [done]" << std::endl; + trace.info() << "Read tab with " << cylCoordinates.size() << std::endl; + + + return 0; +} + + From e0dd52d501b5acfee5fe59acfdb49c013d709f83 Mon Sep 17 00:00:00 2001 From: Bertrand Kerautret Date: Wed, 6 Mar 2024 11:40:19 +0100 Subject: [PATCH 02/14] filter base --- geometry3d/meshTrunk2Pts.cpp | 61 +++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/geometry3d/meshTrunk2Pts.cpp b/geometry3d/meshTrunk2Pts.cpp index b5aefa4..09cfed4 100644 --- a/geometry3d/meshTrunk2Pts.cpp +++ b/geometry3d/meshTrunk2Pts.cpp @@ -32,6 +32,7 @@ #include "DGtal/helpers/StdDefs.h" #include "DGtal/io/readers/MeshReader.h" #include "DGtal/io/readers/TableReader.h" +#include "DGtal/io/writers/MeshWriter.h" #include "CLI11.hpp" @@ -76,9 +77,11 @@ int main( int argc, char** argv ) double parameter {1.0}; std::string inputMeshFileName; std::string inputCLineFileName; - + std::vector basePoint; std::string outputBaseName; std::stringstream usage; + double angleRange = 1.2; + usage << "Usage: " << argv[0] << " [input]\n" << "Typical use example:\n \t meshTrunk2Pts -i ... \n"; // parse command line using CLI------------------------------------------------------- @@ -88,17 +91,21 @@ int main( int argc, char** argv ) ->required()->check(CLI::ExistingFile); app.add_option("--InputCCoords,-c,2", inputCLineFileName, "Input file containing cylinder coordinates") ->required()->check(CLI::ExistingFile); - + app.add_option("--basePoint,-b",basePoint , "trunk base coordinate point", true) + ->expected(3); + app.add_option("--angleRange,-a",angleRange , "angle range of accepted face orientations."); app.add_option("--outputBaseName,-o,3", outputBaseName, "Output SDP filename")->required(); - app.add_option("--parameter,-p", parameter, "a double parameter", true); + app.get_formatter()->column_width(40); CLI11_PARSE(app, argc, argv); // END parse command line using CLI ---------------------------------------------- - + DGtal::Mesh resultingMesh; + // Reading input mesh -------------------------------------------------- DGtal::Mesh aMesh; + DGtal::Mesh aCenterLine; std::vector cylCoordinates; @@ -115,8 +122,54 @@ int main( int argc, char** argv ) cylCoordinates = TableReader::getLinesElementsFromFile(inputCLineFileName); trace.info() << " [done]" << std::endl; trace.info() << "Read tab with " << cylCoordinates.size() << std::endl; + Z3i::RealPoint ptBase; + if (basePoint.size() >2){ + ptBase[0] = basePoint[0]; + ptBase[1] = basePoint[1]; + ptBase[2] = basePoint[2]; + trace.info() << "Input base point: " << ptBase << std::endl; + } + + double baseRad = cylCoordinates[0][0]; + // prepare resulting mesh + for (auto it = aMesh.vertexBegin(); it != aMesh.vertexEnd(); it++){ + resultingMesh.addVertex(*it); + } + + // First sector extraction + std::string extr1NamePts = outputBaseName+"_Extr1.pts"; + std::string extr1NameMesh = outputBaseName+"_Extr1.off"; + + Z3i::RealPoint originExtr1 = ptBase; + originExtr1[0] += 2.0*baseRad; + Z3i::RealPoint aNormal = originExtr1 - ptBase; + aNormal = aNormal.getNormalized(); + trace.info() << "Origin point from extraction simulation:" + << originExtr1 << std::endl; + //a) filter faces from face normal vector + for (auto it = aMesh.faceBegin(); + it!= aMesh.faceEnd(); it++){ + DGtal::Mesh::MeshFace aFace = *it; + bool okOrientation = true; + Z3i::RealPoint p0 = aMesh.getVertex(aFace.at(1)); + Z3i::RealPoint p1 = aMesh.getVertex(aFace.at(0)); + Z3i::RealPoint p2 = aMesh.getVertex(aFace.at(2)); + Z3i::RealPoint vectNormal = ((p1-p0).crossProduct(p2 - p0)).getNormalized(); + vectNormal /= vectNormal.norm(); + okOrientation = vectNormal.dot(aNormal) > cos(angleRange); + if( okOrientation ){ + resultingMesh.addFace(aFace); + } + } + trace.info() << "Cleaning isolated vertices from " << resultingMesh.nbVertex(); + resultingMesh.removeIsolatedVertices(); + trace.info() << "to " << resultingMesh.nbVertex() << " [done]"; + + trace.info() << "Writing output mesh..."; + resultingMesh >> extr1NameMesh; + trace.info() << "[done]." << std::endl; return 0; } From 415c20912ee65c7148d07473dbbd9a23176963f5 Mon Sep 17 00:00:00 2001 From: Bertrand Kerautret Date: Wed, 6 Mar 2024 15:53:15 +0100 Subject: [PATCH 03/14] filter from face position and angular sector --- geometry3d/meshTrunk2Pts.cpp | 87 +++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 11 deletions(-) diff --git a/geometry3d/meshTrunk2Pts.cpp b/geometry3d/meshTrunk2Pts.cpp index 09cfed4..86bfb7f 100644 --- a/geometry3d/meshTrunk2Pts.cpp +++ b/geometry3d/meshTrunk2Pts.cpp @@ -31,6 +31,7 @@ #include "DGtal/base/Common.h" #include "DGtal/helpers/StdDefs.h" #include "DGtal/io/readers/MeshReader.h" +#include "DGtal/io/readers/PointListReader.h" #include "DGtal/io/readers/TableReader.h" #include "DGtal/io/writers/MeshWriter.h" @@ -70,17 +71,57 @@ using namespace DGtal; */ +struct PithSectionCenter { + std::vector myPith; + std::vector mySampledPith; + double myMinZ, myMaxZ; + const double sampleSize = 20.0; + int myNbIntervals; + PithSectionCenter(const std::vector &aPith): myPith(aPith){ + myMaxZ = (*std::max_element(aPith.begin(), aPith.end(), [](auto a, auto b){return a[2] < b[2];}))[2]; + myMinZ = (*std::min_element(aPith.begin(), aPith.end(), [](auto a, auto b){return a[2] < b[2];}))[2]; + myNbIntervals = (myMaxZ - myMinZ)/sampleSize; + for (unsigned int i = 0; i < myNbIntervals; i++){ + mySampledPith.push_back(Z3i::RealPoint(0,0,0)); + } + for (auto const &p: myPith){ + int i = (int) floor((p[2]-myMinZ)/sampleSize); + mySampledPith[i] = p; + } + //check if all sample are presents + unsigned int n = 0; + for (unsigned int i = 0; i < myNbIntervals; i++){ + if (mySampledPith[i] != Z3i::RealPoint(0,0,0)){ + n++; + } + } + if (n != mySampledPith.size()){ + trace.warning() << "all samples are not represented: " << n << "over " << myNbIntervals << std::endl; + } + } + Z3i::RealPoint pithRepresentant(const Z3i::RealPoint &p) const { + int i = (int) floor((p[2]-myMinZ)/sampleSize); + assert(i > 0 && i < mySampledPith.size()); + return mySampledPith[i]; + } +}; + + + int main( int argc, char** argv ) { - typedef std::vector CylCoordsCont; double parameter {1.0}; std::string inputMeshFileName; std::string inputCLineFileName; + std::string inputPithFileName; + std::vector basePoint; std::string outputBaseName; std::stringstream usage; - double angleRange = 1.2; + double normalAngleRange = 0.6; + double posAngleRange = 3.0; + usage << "Usage: " << argv[0] << " [input]\n" << "Typical use example:\n \t meshTrunk2Pts -i ... \n"; @@ -91,9 +132,14 @@ int main( int argc, char** argv ) ->required()->check(CLI::ExistingFile); app.add_option("--InputCCoords,-c,2", inputCLineFileName, "Input file containing cylinder coordinates") ->required()->check(CLI::ExistingFile); + app.add_option("--InputPithCoords,-p,3", inputPithFileName, "Input file containing pith coordinates") + ->required()->check(CLI::ExistingFile); + app.add_option("--basePoint,-b",basePoint , "trunk base coordinate point", true) ->expected(3); - app.add_option("--angleRange,-a",angleRange , "angle range of accepted face orientations."); + app.add_option("--normalAngleRange,-a",normalAngleRange , "angle range of accepted face orientations."); + app.add_option("--posAngleRange,-a",posAngleRange , "position angle range of accepted mesh points."); + app.add_option("--outputBaseName,-o,3", outputBaseName, "Output SDP filename")->required(); @@ -107,7 +153,8 @@ int main( int argc, char** argv ) DGtal::Mesh aMesh; DGtal::Mesh aCenterLine; - std::vector cylCoordinates; + std::vector cylCoordinates; + std::vector pith; trace.info() << "Starting " << argv[0] << "with input: " << inputMeshFileName << " and output :" << outputBaseName @@ -115,12 +162,22 @@ int main( int argc, char** argv ) trace.info() << "Reading input mesh..."; aMesh << inputMeshFileName; - trace.info() << " [done]" << std::endl; - trace.info() << "Read mesh with " << aMesh.nbVertex() << std::endl; - + trace.info() << " [done] (" << aMesh.nbVertex() << ")" << std::endl; + + trace.info() << "Reading input pith coordinates..."; + pith = PointListReader::getPointsFromFile(inputPithFileName); + trace.info() << " [done] (" << pith.size() << ")" << std::endl; + PithSectionCenter pSct (pith); + trace.info() << "Reading input cylinder coordinates... (R,theta,Z)"; - cylCoordinates = TableReader::getLinesElementsFromFile(inputCLineFileName); - trace.info() << " [done]" << std::endl; + cylCoordinates = PointListReader::getPointsFromFile(inputCLineFileName); + trace.info() << " [done] (" << cylCoordinates.size() << ")" << std::endl; + + + + + + trace.info() << "Read tab with " << cylCoordinates.size() << std::endl; Z3i::RealPoint ptBase; if (basePoint.size() >2){ @@ -158,8 +215,16 @@ int main( int argc, char** argv ) Z3i::RealPoint p2 = aMesh.getVertex(aFace.at(2)); Z3i::RealPoint vectNormal = ((p1-p0).crossProduct(p2 - p0)).getNormalized(); vectNormal /= vectNormal.norm(); - okOrientation = vectNormal.dot(aNormal) > cos(angleRange); - if( okOrientation ){ + okOrientation = vectNormal.dot(aNormal) > cos(normalAngleRange); + + bool sectorCompatible = true; + auto pB = (p0+p1+p2)/3.0; + Z3i::RealPoint pC = pSct.pithRepresentant(pB); + Z3i::RealPoint vectDir = pB - pC; + vectDir /= vectDir.norm(); + sectorCompatible = vectDir.dot(aNormal) > cos(posAngleRange/2.0); + + if( okOrientation && sectorCompatible ){ resultingMesh.addFace(aFace); } } From e46417de64b29a9ac3a1283b171d128c65b33e4f Mon Sep 17 00:00:00 2001 From: Bertrand Kerautret Date: Wed, 6 Mar 2024 18:25:12 +0100 Subject: [PATCH 04/14] deform by sector --- geometry3d/meshTrunk2Pts.cpp | 56 ++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/geometry3d/meshTrunk2Pts.cpp b/geometry3d/meshTrunk2Pts.cpp index 86bfb7f..372b259 100644 --- a/geometry3d/meshTrunk2Pts.cpp +++ b/geometry3d/meshTrunk2Pts.cpp @@ -34,9 +34,10 @@ #include "DGtal/io/readers/PointListReader.h" #include "DGtal/io/readers/TableReader.h" #include "DGtal/io/writers/MeshWriter.h" - +#include +#include +#include #include "CLI11.hpp" - /////////////////////////////////////////////////////////////////////////////// using namespace std; using namespace DGtal; @@ -71,6 +72,8 @@ using namespace DGtal; */ + + struct PithSectionCenter { std::vector myPith; std::vector mySampledPith; @@ -100,13 +103,41 @@ struct PithSectionCenter { } } Z3i::RealPoint pithRepresentant(const Z3i::RealPoint &p) const { - int i = (int) floor((p[2]-myMinZ)/sampleSize); - assert(i > 0 && i < mySampledPith.size()); + unsigned int i = (unsigned int) ceil((p[2]-myMinZ)/sampleSize); + assert(i >= 0); + i = std::min((unsigned int)(mySampledPith.size()-1), i); return mySampledPith[i]; } }; +struct TrunkDeformator { + double mySectorSize; + double myMinZ, myMaxZ; + int myNbSectors; + std::vector mySectorShift; + const PithSectionCenter& mySectionCenter; + + TrunkDeformator(const PithSectionCenter &pSectCenter, double maxShift, double sectSize): + mySectionCenter(pSectCenter), + mySectorSize(sectSize){ + myNbSectors = (int)floor((2.0*M_PI) / mySectorSize); + std::srand((unsigned int) std::time(NULL)); + for (unsigned int i = 0; i < myNbSectors; i++){ + double shift = rand()%((int)floor(2000.0*maxShift)); + shift /= 2000.0; + mySectorShift.push_back(shift); + } + } + Z3i::RealPoint deform(const Z3i::RealPoint &pt, const Z3i::RealPoint &ptCyl) const { + Z3i::RealPoint res = pt; + unsigned int sectInd = (unsigned int) floor(ptCyl[1]/mySectorSize); + double ratioZ = (pt[2]-mySectionCenter.myMinZ)/(mySectionCenter.myMaxZ-mySectionCenter.myMinZ); + double hShift = mySectorShift[sectInd]*ratioZ; + res = res + (pt-mySectionCenter.pithRepresentant(pt)).getNormalized()*hShift; + return res; + } +}; int main( int argc, char** argv ) @@ -121,7 +152,8 @@ int main( int argc, char** argv ) std::stringstream usage; double normalAngleRange = 0.6; double posAngleRange = 3.0; - + double ampliMaxShift = 100.0; + double sectSize = 0.3; usage << "Usage: " << argv[0] << " [input]\n" << "Typical use example:\n \t meshTrunk2Pts -i ... \n"; @@ -138,7 +170,9 @@ int main( int argc, char** argv ) app.add_option("--basePoint,-b",basePoint , "trunk base coordinate point", true) ->expected(3); app.add_option("--normalAngleRange,-a",normalAngleRange , "angle range of accepted face orientations."); - app.add_option("--posAngleRange,-a",posAngleRange , "position angle range of accepted mesh points."); + app.add_option("--posAngleRange,-r",posAngleRange , "position angle range of accepted mesh points."); + app.add_option("--ampliMaxShift,-s",ampliMaxShift , "maximal amplitude of sector shift."); + app.add_option("--sectSize,-S",sectSize , "sector size of the deformation."); app.add_option("--outputBaseName,-o,3", outputBaseName, "Output SDP filename")->required(); @@ -228,6 +262,16 @@ int main( int argc, char** argv ) resultingMesh.addFace(aFace); } } + //b) applying shift on sector + TrunkDeformator tDef (pSct, ampliMaxShift, sectSize); + for (unsigned int i = 0; i < resultingMesh.nbVertex(); i++){ + Z3i::RealPoint &pt = resultingMesh.getVertex(i); + Z3i::RealPoint ptCyl = cylCoordinates[i]; + Z3i::RealPoint newP = tDef.deform(pt, ptCyl); + pt[0] = newP[0]; pt[1] = newP[1]; pt[2] = newP[2]; + + } + trace.info() << "Cleaning isolated vertices from " << resultingMesh.nbVertex(); resultingMesh.removeIsolatedVertices(); trace.info() << "to " << resultingMesh.nbVertex() << " [done]"; From 76c0a1965ecf05910f508a40b849fd62722c3ba9 Mon Sep 17 00:00:00 2001 From: Bertrand Kerautret Date: Thu, 7 Mar 2024 10:40:40 +0100 Subject: [PATCH 05/14] add gaussian smoth --- geometry3d/meshTrunk2Pts.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/geometry3d/meshTrunk2Pts.cpp b/geometry3d/meshTrunk2Pts.cpp index 372b259..1ee6ccf 100644 --- a/geometry3d/meshTrunk2Pts.cpp +++ b/geometry3d/meshTrunk2Pts.cpp @@ -71,7 +71,11 @@ using namespace DGtal; @ref meshTrunk2Pts.cpp */ - +double +gaussF(double x, double mu, double sigma){ + double max = exp((-(mu)*(mu))/(2.0*sigma*sigma))*(1.0/(sigma*sqrt(2*M_PI))); + return (exp((-(x-mu)*(x-mu))/(2.0*sigma*sigma))*(1.0/(sigma*sqrt(2*M_PI))))/max; +} struct PithSectionCenter { @@ -132,8 +136,10 @@ struct TrunkDeformator { Z3i::RealPoint deform(const Z3i::RealPoint &pt, const Z3i::RealPoint &ptCyl) const { Z3i::RealPoint res = pt; unsigned int sectInd = (unsigned int) floor(ptCyl[1]/mySectorSize); + double posA = (((double) sectInd)*mySectorSize+mySectorSize/2.0)-ptCyl[1]; + double gCoef = gaussF(posA, 0, mySectorSize/4.0 ); double ratioZ = (pt[2]-mySectionCenter.myMinZ)/(mySectionCenter.myMaxZ-mySectionCenter.myMinZ); - double hShift = mySectorShift[sectInd]*ratioZ; + double hShift = mySectorShift[sectInd]*ratioZ*gCoef*0.5; res = res + (pt-mySectionCenter.pithRepresentant(pt)).getNormalized()*hShift; return res; } From 0ffed17656708671cdfd140a7330d8b2179bc990 Mon Sep 17 00:00:00 2001 From: Bertrand Kerautret Date: Mon, 11 Mar 2024 12:41:17 +0100 Subject: [PATCH 06/14] rename and clena --- geometry3d/meshTrunkTransform.cpp | 276 ++++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 geometry3d/meshTrunkTransform.cpp diff --git a/geometry3d/meshTrunkTransform.cpp b/geometry3d/meshTrunkTransform.cpp new file mode 100644 index 0000000..48ad49d --- /dev/null +++ b/geometry3d/meshTrunkTransform.cpp @@ -0,0 +1,276 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + **/ + +/** + * @file + * @ingroup geometry3d + * @author Bertrand Kerautret (\c bertrand.kerautret@univ-lyon2.fr ) + * Laboratoire d'InfoRmatique en Image et Systemes d'information - LIRIS (CNRS, UMR 5205), CNRS, France + * + * @date 2024/03/05 + * + * Source file of the tool meshTrunkTransform + * + * This file is part of the DGtal library/DGtalTools-contrib Project. + */ + +/////////////////////////////////////////////////////////////////////////////// +#include "DGtal/base/Common.h" +#include "DGtal/helpers/StdDefs.h" +#include "DGtal/io/readers/MeshReader.h" +#include "DGtal/io/readers/PointListReader.h" +#include "DGtal/io/readers/TableReader.h" +#include "DGtal/io/writers/MeshWriter.h" +#include +#include +#include +#include "CLI11.hpp" +/////////////////////////////////////////////////////////////////////////////// +using namespace std; +using namespace DGtal; +/////////////////////////////////////////////////////////////////////////////// + + +/** + @page meshTrunkTransformmeshTrunkTransform + + @brief Description of the tool... + + @b Usage: meshTrunkTransform [input] + + @b Allowed @b options @b are : + + @code + -h [ --help ] display this message + -i [ --input ] arg an input file... + -p [ --parameter] arg a double parameter... + @endcode + + @b Example: + + @code + meshTrunkTransform -i $DGtal/examples/samples/.... + @endcode + + @image html resmeshTrunkTransform.png "Example of result. " + + @see + @ref meshTrunkTransform.cpp + + */ +double +gaussF(double x, double mu, double sigma){ + double max = exp((-(mu)*(mu))/(2.0*sigma*sigma))*(1.0/(sigma*sqrt(2*M_PI))); + return (exp((-(x-mu)*(x-mu))/(2.0*sigma*sigma))*(1.0/(sigma*sqrt(2*M_PI))))/max; +} + + +struct PithSectionCenter { + std::vector myPith; + std::vector mySampledPith; + double myMinZ, myMaxZ; + const double sampleSize = 20.0; + int myNbIntervals; + PithSectionCenter(const std::vector &aPith): myPith(aPith){ + myMaxZ = (*std::max_element(aPith.begin(), aPith.end(), + [](Z3i::RealPoint a, Z3i::RealPoint b){return a[2] < b[2];}))[2]; + myMinZ = (*std::min_element(aPith.begin(), aPith.end(), + [](Z3i::RealPoint a, Z3i::RealPoint b){return a[2] < b[2];}))[2]; + myNbIntervals = (myMaxZ - myMinZ)/sampleSize; + for (unsigned int i = 0; i < myNbIntervals; i++){ + mySampledPith.push_back(Z3i::RealPoint(0,0,0)); + } + for (auto const &p: myPith){ + int i = (int) floor((p[2]-myMinZ)/sampleSize); + mySampledPith[i] = p; + } + //check if all sample are presents + unsigned int n = 0; + for (unsigned int i = 0; i < myNbIntervals; i++){ + if (mySampledPith[i] != Z3i::RealPoint(0,0,0)){ + n++; + } + } + if (n != mySampledPith.size()){ + trace.warning() << "all samples are not represented: " << n << "over " << myNbIntervals << std::endl; + } + } + Z3i::RealPoint pithRepresentant(const Z3i::RealPoint &p) const { + unsigned int i = (unsigned int) ceil((p[2]-myMinZ)/sampleSize); + assert(i >= 0); + i = std::min((unsigned int)(mySampledPith.size()-1), i); + return mySampledPith[i]; + } +}; + + +struct TrunkDeformator { + double mySectorSize; + double myMinZ, myMaxZ; + int myNbSectors; + std::vector mySectorShift; + const PithSectionCenter& mySectionCenter; + + TrunkDeformator(const PithSectionCenter &pSectCenter, double maxShift, double sectSize): + mySectionCenter(pSectCenter), + mySectorSize(sectSize){ + myNbSectors = (int)floor((2.0*M_PI) / mySectorSize); + std::srand((unsigned int) std::time(NULL)); + for (unsigned int i = 0; i < myNbSectors; i++){ + double shift = rand()%((int)floor(2000.0*maxShift)); + shift /= 2000.0; + mySectorShift.push_back(shift); + } + } + Z3i::RealPoint deform(const Z3i::RealPoint &pt, const Z3i::RealPoint &ptCyl) const { + Z3i::RealPoint res = pt; + unsigned int sectInd = (unsigned int) floor(ptCyl[1]/mySectorSize); + double posA = (((double) sectInd)*mySectorSize+mySectorSize/2.0)-ptCyl[1]; + double gCoef = gaussF(posA, 0, mySectorSize/4.0 ); + double ratioZ = (pt[2]-mySectionCenter.myMinZ)/(mySectionCenter.myMaxZ-mySectionCenter.myMinZ); + double hShift = mySectorShift[sectInd]*ratioZ*gCoef*0.5; + res = res + (pt-mySectionCenter.pithRepresentant(pt)).getNormalized()*hShift; + return res; + } +}; + + +int main( int argc, char** argv ) +{ + double parameter {1.0}; + std::string inputMeshFileName; + std::string inputCLineFileName; + std::string inputPithFileName; + + std::string outputBaseName; + std::stringstream usage; + double normalAngleRange = 0.6; + double posAngleRange = 3.0; + double ampliMaxShift = 100.0; + double sectSize = 0.3; + + usage << "Usage: " << argv[0] << " [input]\n" + << "Typical use example:\n \t meshTrunkTransform -i ... \n"; + // parse command line using CLI------------------------------------------------------- + CLI::App app; + app.description("Transform an input mesh into points cloud simulating acquisition process like lidar Scan .\n" + usage.str() ); + app.add_option("--inputMesh,-i,1", inputMeshFileName, "Input file") + ->required()->check(CLI::ExistingFile); + app.add_option("--InputCCoords,-c,2", inputCLineFileName, "Input file containing cylinder coordinates") + ->required()->check(CLI::ExistingFile); + app.add_option("--InputPithCoords,-p,3", inputPithFileName, "Input file containing pith coordinates") + ->required()->check(CLI::ExistingFile); + + app.add_option("--normalAngleRange,-a",normalAngleRange , "angle range of accepted face orientations."); + app.add_option("--posAngleRange,-r",posAngleRange , "position angle range of accepted mesh points."); + app.add_option("--ampliMaxShift,-s",ampliMaxShift , "maximal amplitude of sector shift."); + app.add_option("--sectSize,-S",sectSize , "sector size of the deformation."); + + app.add_option("--outputBaseName,-o,3", outputBaseName, "Output SDP filename")->required(); + + + app.get_formatter()->column_width(40); + CLI11_PARSE(app, argc, argv); + // END parse command line using CLI ---------------------------------------------- + + DGtal::Mesh resultingMesh; + + // Reading input mesh -------------------------------------------------- + DGtal::Mesh aMesh; + + DGtal::Mesh aCenterLine; + std::vector cylCoordinates; + std::vector pith; + + trace.info() << "Starting " << argv[0] << "with input: " + << inputMeshFileName << " and output :" << outputBaseName + << " param: " << parameter <::getPointsFromFile(inputPithFileName); + trace.info() << " [done] (" << pith.size() << ")" << std::endl; + PithSectionCenter pSct (pith); + + trace.info() << "Reading input cylinder coordinates... (R,theta,Z)"; + cylCoordinates = PointListReader::getPointsFromFile(inputCLineFileName); + trace.info() << " [done] (" << cylCoordinates.size() << ")" << std::endl; + + + trace.info() << "Read tab with " << cylCoordinates.size() << std::endl; + + double baseRad = cylCoordinates[0][0]; + + // prepare resulting mesh + for (auto it = aMesh.vertexBegin(); it != aMesh.vertexEnd(); it++){ + resultingMesh.addVertex(*it); + } + + + // First sector extraction + std::string extr1NamePts = outputBaseName+"_Extr1.pts"; + std::string extr1NameMesh = outputBaseName+"_Extr1.off"; + + Z3i::RealPoint aNormal (1,0,0); + aNormal = aNormal.getNormalized(); + + //a) filter faces from face normal vector + for (auto it = aMesh.faceBegin(); + it!= aMesh.faceEnd(); it++){ + DGtal::Mesh::MeshFace aFace = *it; + bool okOrientation = true; + Z3i::RealPoint p0 = aMesh.getVertex(aFace.at(1)); + Z3i::RealPoint p1 = aMesh.getVertex(aFace.at(0)); + Z3i::RealPoint p2 = aMesh.getVertex(aFace.at(2)); + Z3i::RealPoint vectNormal = ((p1-p0).crossProduct(p2 - p0)).getNormalized(); + vectNormal /= vectNormal.norm(); + okOrientation = vectNormal.dot(aNormal) > cos(normalAngleRange); + + bool sectorCompatible = true; + auto pB = (p0+p1+p2)/3.0; + Z3i::RealPoint pC = pSct.pithRepresentant(pB); + Z3i::RealPoint vectDir = pB - pC; + vectDir /= vectDir.norm(); + sectorCompatible = vectDir.dot(aNormal) > cos(posAngleRange/2.0); + + if( okOrientation && sectorCompatible ){ + resultingMesh.addFace(aFace); + } + } + //b) applying shift on sector + TrunkDeformator tDef (pSct, ampliMaxShift, sectSize); + for (unsigned int i = 0; i < resultingMesh.nbVertex(); i++){ + Z3i::RealPoint &pt = resultingMesh.getVertex(i); + Z3i::RealPoint ptCyl = cylCoordinates[i]; + Z3i::RealPoint newP = tDef.deform(pt, ptCyl); + pt[0] = newP[0]; pt[1] = newP[1]; pt[2] = newP[2]; + + } + + trace.info() << "Cleaning isolated vertices from " << resultingMesh.nbVertex(); + resultingMesh.removeIsolatedVertices(); + trace.info() << "to " << resultingMesh.nbVertex() << " [done]"; + + trace.info() << "Writing output mesh..."; + resultingMesh >> extr1NameMesh; + trace.info() << "[done]." << std::endl; + return 0; +} + + From 5318afc2c0588c8b64c85c541bc2d8417f0f84f4 Mon Sep 17 00:00:00 2001 From: Bertrand Kerautret Date: Mon, 11 Mar 2024 12:41:18 +0100 Subject: [PATCH 07/14] rename and clena --- geometry3d/CMakeLists.txt | 2 +- geometry3d/meshTrunk2Pts.cpp | 291 ------------------------------ geometry3d/meshTrunkTransform.cpp | 91 ++++++---- 3 files changed, 59 insertions(+), 325 deletions(-) delete mode 100644 geometry3d/meshTrunk2Pts.cpp diff --git a/geometry3d/CMakeLists.txt b/geometry3d/CMakeLists.txt index 77e2e83..8b2599b 100644 --- a/geometry3d/CMakeLists.txt +++ b/geometry3d/CMakeLists.txt @@ -11,7 +11,7 @@ SET(DGTAL_TOOLS_DEVEL_SRC vol2meshAndNormals meshAxisCutter graph2vol - meshTrunk2Pts + meshTrunkTransform ) diff --git a/geometry3d/meshTrunk2Pts.cpp b/geometry3d/meshTrunk2Pts.cpp deleted file mode 100644 index 1ee6ccf..0000000 --- a/geometry3d/meshTrunk2Pts.cpp +++ /dev/null @@ -1,291 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - **/ - -/** - * @file - * @ingroup geometry3d - * @author Bertrand Kerautret (\c bertrand.kerautret@univ-lyon2.fr ) - * Laboratoire d'InfoRmatique en Image et Systemes d'information - LIRIS (CNRS, UMR 5205), CNRS, France - * - * @date 2024/03/05 - * - * Source file of the tool meshTrunk2Pts - * - * This file is part of the DGtal library/DGtalTools-contrib Project. - */ - -/////////////////////////////////////////////////////////////////////////////// -#include "DGtal/base/Common.h" -#include "DGtal/helpers/StdDefs.h" -#include "DGtal/io/readers/MeshReader.h" -#include "DGtal/io/readers/PointListReader.h" -#include "DGtal/io/readers/TableReader.h" -#include "DGtal/io/writers/MeshWriter.h" -#include -#include -#include -#include "CLI11.hpp" -/////////////////////////////////////////////////////////////////////////////// -using namespace std; -using namespace DGtal; -/////////////////////////////////////////////////////////////////////////////// - - -/** - @page meshTrunk2Pts meshTrunk2Pts - - @brief Description of the tool... - - @b Usage: meshTrunk2Pts [input] - - @b Allowed @b options @b are : - - @code - -h [ --help ] display this message - -i [ --input ] arg an input file... - -p [ --parameter] arg a double parameter... - @endcode - - @b Example: - - @code - meshTrunk2Pts -i $DGtal/examples/samples/.... - @endcode - - @image html resmeshTrunk2Pts.png "Example of result. " - - @see - @ref meshTrunk2Pts.cpp - - */ -double -gaussF(double x, double mu, double sigma){ - double max = exp((-(mu)*(mu))/(2.0*sigma*sigma))*(1.0/(sigma*sqrt(2*M_PI))); - return (exp((-(x-mu)*(x-mu))/(2.0*sigma*sigma))*(1.0/(sigma*sqrt(2*M_PI))))/max; -} - - -struct PithSectionCenter { - std::vector myPith; - std::vector mySampledPith; - double myMinZ, myMaxZ; - const double sampleSize = 20.0; - int myNbIntervals; - PithSectionCenter(const std::vector &aPith): myPith(aPith){ - myMaxZ = (*std::max_element(aPith.begin(), aPith.end(), [](auto a, auto b){return a[2] < b[2];}))[2]; - myMinZ = (*std::min_element(aPith.begin(), aPith.end(), [](auto a, auto b){return a[2] < b[2];}))[2]; - myNbIntervals = (myMaxZ - myMinZ)/sampleSize; - for (unsigned int i = 0; i < myNbIntervals; i++){ - mySampledPith.push_back(Z3i::RealPoint(0,0,0)); - } - for (auto const &p: myPith){ - int i = (int) floor((p[2]-myMinZ)/sampleSize); - mySampledPith[i] = p; - } - //check if all sample are presents - unsigned int n = 0; - for (unsigned int i = 0; i < myNbIntervals; i++){ - if (mySampledPith[i] != Z3i::RealPoint(0,0,0)){ - n++; - } - } - if (n != mySampledPith.size()){ - trace.warning() << "all samples are not represented: " << n << "over " << myNbIntervals << std::endl; - } - } - Z3i::RealPoint pithRepresentant(const Z3i::RealPoint &p) const { - unsigned int i = (unsigned int) ceil((p[2]-myMinZ)/sampleSize); - assert(i >= 0); - i = std::min((unsigned int)(mySampledPith.size()-1), i); - return mySampledPith[i]; - } -}; - - -struct TrunkDeformator { - double mySectorSize; - double myMinZ, myMaxZ; - int myNbSectors; - std::vector mySectorShift; - const PithSectionCenter& mySectionCenter; - - TrunkDeformator(const PithSectionCenter &pSectCenter, double maxShift, double sectSize): - mySectionCenter(pSectCenter), - mySectorSize(sectSize){ - myNbSectors = (int)floor((2.0*M_PI) / mySectorSize); - std::srand((unsigned int) std::time(NULL)); - for (unsigned int i = 0; i < myNbSectors; i++){ - double shift = rand()%((int)floor(2000.0*maxShift)); - shift /= 2000.0; - mySectorShift.push_back(shift); - } - } - Z3i::RealPoint deform(const Z3i::RealPoint &pt, const Z3i::RealPoint &ptCyl) const { - Z3i::RealPoint res = pt; - unsigned int sectInd = (unsigned int) floor(ptCyl[1]/mySectorSize); - double posA = (((double) sectInd)*mySectorSize+mySectorSize/2.0)-ptCyl[1]; - double gCoef = gaussF(posA, 0, mySectorSize/4.0 ); - double ratioZ = (pt[2]-mySectionCenter.myMinZ)/(mySectionCenter.myMaxZ-mySectionCenter.myMinZ); - double hShift = mySectorShift[sectInd]*ratioZ*gCoef*0.5; - res = res + (pt-mySectionCenter.pithRepresentant(pt)).getNormalized()*hShift; - return res; - } -}; - - -int main( int argc, char** argv ) -{ - double parameter {1.0}; - std::string inputMeshFileName; - std::string inputCLineFileName; - std::string inputPithFileName; - - std::vector basePoint; - std::string outputBaseName; - std::stringstream usage; - double normalAngleRange = 0.6; - double posAngleRange = 3.0; - double ampliMaxShift = 100.0; - double sectSize = 0.3; - - usage << "Usage: " << argv[0] << " [input]\n" - << "Typical use example:\n \t meshTrunk2Pts -i ... \n"; - // parse command line using CLI------------------------------------------------------- - CLI::App app; - app.description("Transform an input mesh into points cloud simulating acquisition process like lidar Scan .\n" + usage.str() ); - app.add_option("--inputMesh,-i,1", inputMeshFileName, "Input file") - ->required()->check(CLI::ExistingFile); - app.add_option("--InputCCoords,-c,2", inputCLineFileName, "Input file containing cylinder coordinates") - ->required()->check(CLI::ExistingFile); - app.add_option("--InputPithCoords,-p,3", inputPithFileName, "Input file containing pith coordinates") - ->required()->check(CLI::ExistingFile); - - app.add_option("--basePoint,-b",basePoint , "trunk base coordinate point", true) - ->expected(3); - app.add_option("--normalAngleRange,-a",normalAngleRange , "angle range of accepted face orientations."); - app.add_option("--posAngleRange,-r",posAngleRange , "position angle range of accepted mesh points."); - app.add_option("--ampliMaxShift,-s",ampliMaxShift , "maximal amplitude of sector shift."); - app.add_option("--sectSize,-S",sectSize , "sector size of the deformation."); - - app.add_option("--outputBaseName,-o,3", outputBaseName, "Output SDP filename")->required(); - - - app.get_formatter()->column_width(40); - CLI11_PARSE(app, argc, argv); - // END parse command line using CLI ---------------------------------------------- - - DGtal::Mesh resultingMesh; - - // Reading input mesh -------------------------------------------------- - DGtal::Mesh aMesh; - - DGtal::Mesh aCenterLine; - std::vector cylCoordinates; - std::vector pith; - - trace.info() << "Starting " << argv[0] << "with input: " - << inputMeshFileName << " and output :" << outputBaseName - << " param: " << parameter <::getPointsFromFile(inputPithFileName); - trace.info() << " [done] (" << pith.size() << ")" << std::endl; - PithSectionCenter pSct (pith); - - trace.info() << "Reading input cylinder coordinates... (R,theta,Z)"; - cylCoordinates = PointListReader::getPointsFromFile(inputCLineFileName); - trace.info() << " [done] (" << cylCoordinates.size() << ")" << std::endl; - - - - - - - trace.info() << "Read tab with " << cylCoordinates.size() << std::endl; - Z3i::RealPoint ptBase; - if (basePoint.size() >2){ - ptBase[0] = basePoint[0]; - ptBase[1] = basePoint[1]; - ptBase[2] = basePoint[2]; - trace.info() << "Input base point: " << ptBase << std::endl; - } - - double baseRad = cylCoordinates[0][0]; - - // prepare resulting mesh - for (auto it = aMesh.vertexBegin(); it != aMesh.vertexEnd(); it++){ - resultingMesh.addVertex(*it); - } - - - // First sector extraction - std::string extr1NamePts = outputBaseName+"_Extr1.pts"; - std::string extr1NameMesh = outputBaseName+"_Extr1.off"; - - Z3i::RealPoint originExtr1 = ptBase; - originExtr1[0] += 2.0*baseRad; - Z3i::RealPoint aNormal = originExtr1 - ptBase; - aNormal = aNormal.getNormalized(); - trace.info() << "Origin point from extraction simulation:" - << originExtr1 << std::endl; - //a) filter faces from face normal vector - for (auto it = aMesh.faceBegin(); - it!= aMesh.faceEnd(); it++){ - DGtal::Mesh::MeshFace aFace = *it; - bool okOrientation = true; - Z3i::RealPoint p0 = aMesh.getVertex(aFace.at(1)); - Z3i::RealPoint p1 = aMesh.getVertex(aFace.at(0)); - Z3i::RealPoint p2 = aMesh.getVertex(aFace.at(2)); - Z3i::RealPoint vectNormal = ((p1-p0).crossProduct(p2 - p0)).getNormalized(); - vectNormal /= vectNormal.norm(); - okOrientation = vectNormal.dot(aNormal) > cos(normalAngleRange); - - bool sectorCompatible = true; - auto pB = (p0+p1+p2)/3.0; - Z3i::RealPoint pC = pSct.pithRepresentant(pB); - Z3i::RealPoint vectDir = pB - pC; - vectDir /= vectDir.norm(); - sectorCompatible = vectDir.dot(aNormal) > cos(posAngleRange/2.0); - - if( okOrientation && sectorCompatible ){ - resultingMesh.addFace(aFace); - } - } - //b) applying shift on sector - TrunkDeformator tDef (pSct, ampliMaxShift, sectSize); - for (unsigned int i = 0; i < resultingMesh.nbVertex(); i++){ - Z3i::RealPoint &pt = resultingMesh.getVertex(i); - Z3i::RealPoint ptCyl = cylCoordinates[i]; - Z3i::RealPoint newP = tDef.deform(pt, ptCyl); - pt[0] = newP[0]; pt[1] = newP[1]; pt[2] = newP[2]; - - } - - trace.info() << "Cleaning isolated vertices from " << resultingMesh.nbVertex(); - resultingMesh.removeIsolatedVertices(); - trace.info() << "to " << resultingMesh.nbVertex() << " [done]"; - - trace.info() << "Writing output mesh..."; - resultingMesh >> extr1NameMesh; - trace.info() << "[done]." << std::endl; - return 0; -} - - diff --git a/geometry3d/meshTrunkTransform.cpp b/geometry3d/meshTrunkTransform.cpp index 48ad49d..0d1c14e 100644 --- a/geometry3d/meshTrunkTransform.cpp +++ b/geometry3d/meshTrunkTransform.cpp @@ -34,9 +34,13 @@ #include "DGtal/io/readers/PointListReader.h" #include "DGtal/io/readers/TableReader.h" #include "DGtal/io/writers/MeshWriter.h" + #include #include #include +#include +#include + #include "CLI11.hpp" /////////////////////////////////////////////////////////////////////////////// using namespace std; @@ -154,13 +158,16 @@ int main( int argc, char** argv ) std::string inputMeshFileName; std::string inputCLineFileName; std::string inputPithFileName; - - std::string outputBaseName; + std::string outputMesh = "result.off"; + std::string outputPts = "result.pts"; + + std::string outputBaseName {"resDeform"}; std::stringstream usage; double normalAngleRange = 0.6; double posAngleRange = 3.0; double ampliMaxShift = 100.0; double sectSize = 0.3; + std::pair shiftFacePosParams {1.0, 1.0}; usage << "Usage: " << argv[0] << " [input]\n" << "Typical use example:\n \t meshTrunkTransform -i ... \n"; @@ -174,12 +181,12 @@ int main( int argc, char** argv ) app.add_option("--InputPithCoords,-p,3", inputPithFileName, "Input file containing pith coordinates") ->required()->check(CLI::ExistingFile); - app.add_option("--normalAngleRange,-a",normalAngleRange , "angle range of accepted face orientations."); - app.add_option("--posAngleRange,-r",posAngleRange , "position angle range of accepted mesh points."); - app.add_option("--ampliMaxShift,-s",ampliMaxShift , "maximal amplitude of sector shift."); - app.add_option("--sectSize,-S",sectSize , "sector size of the deformation."); - - app.add_option("--outputBaseName,-o,3", outputBaseName, "Output SDP filename")->required(); + auto filterFaceNormal = app.add_option("--filterFaceNormal,-F",normalAngleRange , "filter mesh faces using their normal vector: the accepted face orientations are defined the face normal and filtering direction (see option --filterDir). "); + auto filterFacePosition = app.add_option("--filterFacePosition,-P", posAngleRange , "filter mesh faces using angle defined from the face barycenter position with its associated pith center (angle in radians) and the filtering direction (see option --filterDir)."); + auto shiftFacePos = app.add_option("--shiftFacePos,-s", shiftFacePosParams, "shift face position using maximal amplitude (first parameter value) of sector shift (sector size defined with the second parameter value).") + ->expected(1); + auto outMesh = app.add_option("--outputMesh,-o,3", outputMesh, "Output mesh file name."); + auto outPts = app.add_option("--outputPoints", outputPts, "Output pts file name"); app.get_formatter()->column_width(40); @@ -224,13 +231,24 @@ int main( int argc, char** argv ) // First sector extraction - std::string extr1NamePts = outputBaseName+"_Extr1.pts"; - std::string extr1NameMesh = outputBaseName+"_Extr1.off"; Z3i::RealPoint aNormal (1,0,0); aNormal = aNormal.getNormalized(); - //a) filter faces from face normal vector + + //a) applying shift on sector + if (shiftFacePos->count()>0){ + ampliMaxShift = shiftFacePosParams.first; + sectSize = shiftFacePosParams.second; + TrunkDeformator tDef (pSct, ampliMaxShift, sectSize); + for (unsigned int i = 0; i < resultingMesh.nbVertex(); i++){ + Z3i::RealPoint &pt = resultingMesh.getVertex(i); + Z3i::RealPoint ptCyl = cylCoordinates[i]; + Z3i::RealPoint newP = tDef.deform(pt, ptCyl); + pt[0] = newP[0]; pt[1] = newP[1]; pt[2] = newP[2]; + } + } + //b) filter faces from face normal vector for (auto it = aMesh.faceBegin(); it!= aMesh.faceEnd(); it++){ DGtal::Mesh::MeshFace aFace = *it; @@ -238,38 +256,45 @@ int main( int argc, char** argv ) Z3i::RealPoint p0 = aMesh.getVertex(aFace.at(1)); Z3i::RealPoint p1 = aMesh.getVertex(aFace.at(0)); Z3i::RealPoint p2 = aMesh.getVertex(aFace.at(2)); - Z3i::RealPoint vectNormal = ((p1-p0).crossProduct(p2 - p0)).getNormalized(); - vectNormal /= vectNormal.norm(); - okOrientation = vectNormal.dot(aNormal) > cos(normalAngleRange); - + if (filterFaceNormal -> count() > 0 ){ + Z3i::RealPoint vectNormal = ((p1-p0).crossProduct(p2 - p0)).getNormalized(); + vectNormal /= vectNormal.norm(); + okOrientation = vectNormal.dot(aNormal) > cos(normalAngleRange/2.0); + } bool sectorCompatible = true; - auto pB = (p0+p1+p2)/3.0; - Z3i::RealPoint pC = pSct.pithRepresentant(pB); - Z3i::RealPoint vectDir = pB - pC; - vectDir /= vectDir.norm(); - sectorCompatible = vectDir.dot(aNormal) > cos(posAngleRange/2.0); - + if (filterFacePosition -> count() > 0 ){ + auto pB = (p0+p1+p2)/3.0; + Z3i::RealPoint pC = pSct.pithRepresentant(pB); + Z3i::RealPoint vectDir = pB - pC; + vectDir /= vectDir.norm(); + sectorCompatible = vectDir.dot(aNormal) > cos(posAngleRange/2.0); + } if( okOrientation && sectorCompatible ){ resultingMesh.addFace(aFace); } } - //b) applying shift on sector - TrunkDeformator tDef (pSct, ampliMaxShift, sectSize); - for (unsigned int i = 0; i < resultingMesh.nbVertex(); i++){ - Z3i::RealPoint &pt = resultingMesh.getVertex(i); - Z3i::RealPoint ptCyl = cylCoordinates[i]; - Z3i::RealPoint newP = tDef.deform(pt, ptCyl); - pt[0] = newP[0]; pt[1] = newP[1]; pt[2] = newP[2]; - - } trace.info() << "Cleaning isolated vertices from " << resultingMesh.nbVertex(); resultingMesh.removeIsolatedVertices(); trace.info() << "to " << resultingMesh.nbVertex() << " [done]"; - trace.info() << "Writing output mesh..."; - resultingMesh >> extr1NameMesh; - trace.info() << "[done]." << std::endl; + if (outMesh->count() > 0 ){ + trace.info() << "Writing output mesh..."; + resultingMesh >> outputMesh; + trace.info() << "[done]." << std::endl; + } + if (outPts->count() > 0 ){ + trace.info() << "Writing output points..."; + + ofstream fout; + fout.open(outputPts); + for (auto it = resultingMesh.vertexBegin(); it != resultingMesh.vertexEnd(); it++){ + fout << (*it)[0] << " " << (*it)[1] << " " << (*it)[2] << std::endl; + } + fout.close(); + trace.info() << "[done]." << std::endl; + } + return 0; } From 5f20978e2952bce09d1b4e9a19c1f9ff46685d9c Mon Sep 17 00:00:00 2001 From: Bertrand Kerautret Date: Mon, 11 Mar 2024 15:42:12 +0100 Subject: [PATCH 08/14] add option to filter according the main direction --- geometry3d/meshTrunkTransform.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/geometry3d/meshTrunkTransform.cpp b/geometry3d/meshTrunkTransform.cpp index 0d1c14e..b1cde32 100644 --- a/geometry3d/meshTrunkTransform.cpp +++ b/geometry3d/meshTrunkTransform.cpp @@ -52,7 +52,7 @@ using namespace DGtal; @page meshTrunkTransformmeshTrunkTransform @brief Description of the tool... - + à)d @b Usage: meshTrunkTransform [input] @b Allowed @b options @b are : @@ -168,7 +168,8 @@ int main( int argc, char** argv ) double ampliMaxShift = 100.0; double sectSize = 0.3; std::pair shiftFacePosParams {1.0, 1.0}; - + Z3i::RealPoint mainDir {1.0,0.0,0.0}; + std::vector mainDirV {1.0,0.0,0.0}; usage << "Usage: " << argv[0] << " [input]\n" << "Typical use example:\n \t meshTrunkTransform -i ... \n"; // parse command line using CLI------------------------------------------------------- @@ -181,14 +182,14 @@ int main( int argc, char** argv ) app.add_option("--InputPithCoords,-p,3", inputPithFileName, "Input file containing pith coordinates") ->required()->check(CLI::ExistingFile); - auto filterFaceNormal = app.add_option("--filterFaceNormal,-F",normalAngleRange , "filter mesh faces using their normal vector: the accepted face orientations are defined the face normal and filtering direction (see option --filterDir). "); - auto filterFacePosition = app.add_option("--filterFacePosition,-P", posAngleRange , "filter mesh faces using angle defined from the face barycenter position with its associated pith center (angle in radians) and the filtering direction (see option --filterDir)."); - auto shiftFacePos = app.add_option("--shiftFacePos,-s", shiftFacePosParams, "shift face position using maximal amplitude (first parameter value) of sector shift (sector size defined with the second parameter value).") + auto filterFaceNormal = app.add_option("--filterFaceNormal,-F",normalAngleRange , "Filter mesh faces using their normal vector: the accepted face orientations are defined the face normal and filtering direction (see option --filterDir). "); + auto filterFacePosition = app.add_option("--filterFacePosition,-P", posAngleRange , "Filter mesh faces using angle defined from the face barycenter position with its associated pith center (angle in radians) and the filtering direction (see option --filterDir)."); + auto shiftFacePos = app.add_option("--shiftFacePos,-s", shiftFacePosParams, "Shift face position using maximal amplitude (first parameter value) of sector shift (sector size defined with the second parameter value).") ->expected(1); + auto mainDirOpt = app.add_option("--mainDir,-m", mainDirV, "Define the main direction to define the filtering angle based (see --filterFacePosition and --filterFaceNormal ") + ->expected(3); auto outMesh = app.add_option("--outputMesh,-o,3", outputMesh, "Output mesh file name."); auto outPts = app.add_option("--outputPoints", outputPts, "Output pts file name"); - - app.get_formatter()->column_width(40); CLI11_PARSE(app, argc, argv); // END parse command line using CLI ---------------------------------------------- @@ -223,7 +224,10 @@ int main( int argc, char** argv ) trace.info() << "Read tab with " << cylCoordinates.size() << std::endl; double baseRad = cylCoordinates[0][0]; - + mainDir[0] = mainDirV[0]; + mainDir[1] = mainDirV[1]; + mainDir[2] = mainDirV[2]; + // prepare resulting mesh for (auto it = aMesh.vertexBegin(); it != aMesh.vertexEnd(); it++){ resultingMesh.addVertex(*it); @@ -232,8 +236,7 @@ int main( int argc, char** argv ) // First sector extraction - Z3i::RealPoint aNormal (1,0,0); - aNormal = aNormal.getNormalized(); + mainDir = mainDir.getNormalized(); //a) applying shift on sector @@ -259,7 +262,7 @@ int main( int argc, char** argv ) if (filterFaceNormal -> count() > 0 ){ Z3i::RealPoint vectNormal = ((p1-p0).crossProduct(p2 - p0)).getNormalized(); vectNormal /= vectNormal.norm(); - okOrientation = vectNormal.dot(aNormal) > cos(normalAngleRange/2.0); + okOrientation = vectNormal.dot(mainDir) > cos(normalAngleRange/2.0); } bool sectorCompatible = true; if (filterFacePosition -> count() > 0 ){ @@ -267,7 +270,7 @@ int main( int argc, char** argv ) Z3i::RealPoint pC = pSct.pithRepresentant(pB); Z3i::RealPoint vectDir = pB - pC; vectDir /= vectDir.norm(); - sectorCompatible = vectDir.dot(aNormal) > cos(posAngleRange/2.0); + sectorCompatible = vectDir.dot(mainDir) > cos(posAngleRange/2.0); } if( okOrientation && sectorCompatible ){ resultingMesh.addFace(aFace); From 31a4849297523fd89e96424c625e00629e52ff01 Mon Sep 17 00:00:00 2001 From: Bertrand Kerautret Date: Mon, 11 Mar 2024 15:45:19 +0100 Subject: [PATCH 09/14] cleaning output tools --- geometry3d/meshTrunkTransform.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/geometry3d/meshTrunkTransform.cpp b/geometry3d/meshTrunkTransform.cpp index b1cde32..7ace941 100644 --- a/geometry3d/meshTrunkTransform.cpp +++ b/geometry3d/meshTrunkTransform.cpp @@ -209,20 +209,18 @@ int main( int argc, char** argv ) trace.info() << "Reading input mesh..."; aMesh << inputMeshFileName; - trace.info() << " [done] (" << aMesh.nbVertex() << ")" << std::endl; + trace.info() << " [done] (#vertices: " << aMesh.nbVertex() << ")" << std::endl; trace.info() << "Reading input pith coordinates..."; pith = PointListReader::getPointsFromFile(inputPithFileName); - trace.info() << " [done] (" << pith.size() << ")" << std::endl; + trace.info() << " [done] (#pith size:" << pith.size() << ")" << std::endl; PithSectionCenter pSct (pith); trace.info() << "Reading input cylinder coordinates... (R,theta,Z)"; cylCoordinates = PointListReader::getPointsFromFile(inputCLineFileName); - trace.info() << " [done] (" << cylCoordinates.size() << ")" << std::endl; + trace.info() << " [done] (#vertices: " << cylCoordinates.size() << ")" << std::endl; - trace.info() << "Read tab with " << cylCoordinates.size() << std::endl; - double baseRad = cylCoordinates[0][0]; mainDir[0] = mainDirV[0]; mainDir[1] = mainDirV[1]; @@ -279,7 +277,7 @@ int main( int argc, char** argv ) trace.info() << "Cleaning isolated vertices from " << resultingMesh.nbVertex(); resultingMesh.removeIsolatedVertices(); - trace.info() << "to " << resultingMesh.nbVertex() << " [done]"; + trace.info() << " to " << resultingMesh.nbVertex() << " [done]" << std::endl; if (outMesh->count() > 0 ){ trace.info() << "Writing output mesh..."; @@ -297,7 +295,6 @@ int main( int argc, char** argv ) fout.close(); trace.info() << "[done]." << std::endl; } - return 0; } From e0b6d9e0f83732b8dec2c396f3d1f66ffcf55876 Mon Sep 17 00:00:00 2001 From: Bertrand Kerautret Date: Mon, 11 Mar 2024 16:24:22 +0100 Subject: [PATCH 10/14] rename --- geometry3d/CMakeLists.txt | 2 +- ...shTrunkTransform.cpp => trunkMeshTransform.cpp} | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) rename geometry3d/{meshTrunkTransform.cpp => trunkMeshTransform.cpp} (97%) diff --git a/geometry3d/CMakeLists.txt b/geometry3d/CMakeLists.txt index 8b2599b..310905d 100644 --- a/geometry3d/CMakeLists.txt +++ b/geometry3d/CMakeLists.txt @@ -11,7 +11,7 @@ SET(DGTAL_TOOLS_DEVEL_SRC vol2meshAndNormals meshAxisCutter graph2vol - meshTrunkTransform + trunkMeshTransform ) diff --git a/geometry3d/meshTrunkTransform.cpp b/geometry3d/trunkMeshTransform.cpp similarity index 97% rename from geometry3d/meshTrunkTransform.cpp rename to geometry3d/trunkMeshTransform.cpp index 7ace941..ffe765d 100644 --- a/geometry3d/meshTrunkTransform.cpp +++ b/geometry3d/trunkMeshTransform.cpp @@ -22,7 +22,7 @@ * * @date 2024/03/05 * - * Source file of the tool meshTrunkTransform + * Source file of the tool trunkMeshTransform * * This file is part of the DGtal library/DGtalTools-contrib Project. */ @@ -49,11 +49,11 @@ using namespace DGtal; /** - @page meshTrunkTransformmeshTrunkTransform + @page trunkMeshTransform trunkMeshTransform @brief Description of the tool... à)d - @b Usage: meshTrunkTransform [input] + @b Usage: trunkMeshTransform [input] @b Allowed @b options @b are : @@ -66,13 +66,13 @@ using namespace DGtal; @b Example: @code - meshTrunkTransform -i $DGtal/examples/samples/.... + trunkMeshTransform -i $DGtal/examples/samples/.... @endcode - @image html resmeshTrunkTransform.png "Example of result. " + @image html restrunkMeshTransform.png "Example of result. " @see - @ref meshTrunkTransform.cpp + @ref trunkMeshTransform.cpp */ double @@ -171,7 +171,7 @@ int main( int argc, char** argv ) Z3i::RealPoint mainDir {1.0,0.0,0.0}; std::vector mainDirV {1.0,0.0,0.0}; usage << "Usage: " << argv[0] << " [input]\n" - << "Typical use example:\n \t meshTrunkTransform -i ... \n"; + << "Typical use example:\n \t trunkMeshTransform -i ... \n"; // parse command line using CLI------------------------------------------------------- CLI::App app; app.description("Transform an input mesh into points cloud simulating acquisition process like lidar Scan .\n" + usage.str() ); From 2ac576223af32aaa8254749ca5ac10dc0da9c8fc Mon Sep 17 00:00:00 2001 From: Bertrand Kerautret Date: Mon, 11 Mar 2024 16:27:08 +0100 Subject: [PATCH 11/14] changelog, readme and doc --- ChangeLog.md | 2 ++ README.md | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index 0f57d40..8ce12a8 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -26,6 +26,8 @@ (Bertrand Kerautret [#71](https://github.com/DGtal-team/DGtalTools-contrib/pull/71)) - meshAxisCutter: new option to select range meshes. (Bertrand Kerautret [#80](https://github.com/DGtal-team/DGtalTools-contrib/pull/80)) + - meshAxisCutter: new tools to transform trunk mesh from input centerline and cylinder coordinates. + (Bertrand Kerautret [#82](https://github.com/DGtal-team/DGtalTools-contrib/pull/82)) - *visualisation* - polyMeshEdit: tool to edit a mesh (add local noise, remove selected faces). diff --git a/README.md b/README.md index 25bf72b..20c6354 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,8 @@ As the previous section but in 3d, it contains actually these tools: - off2obj: tool to convert a mesh represented in off format into obj format. - obj2off: tool to convert a .obj mesh into the .off format. - off2sdp: converts a mesh into a set of points (.sdp). - - volFillCCSize: fills each Connected Component with a value corresponding to the number of voxels of the CC. + - volFillCCSize: fills each Connected Component with a value corresponding to the number of voxels of the CC. + - meshAxisCutter: tools to transform trunk mesh from input centerline and cylinder coordinates.
result after olFillCSSize From 88111752c3866bc4c0b26e7cb0653e92ed39495a Mon Sep 17 00:00:00 2001 From: Bertrand Kerautret Date: Mon, 11 Mar 2024 16:30:35 +0100 Subject: [PATCH 12/14] changelog, readme and doc --- geometry3d/trunkMeshTransform.cpp | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/geometry3d/trunkMeshTransform.cpp b/geometry3d/trunkMeshTransform.cpp index ffe765d..b9008d9 100644 --- a/geometry3d/trunkMeshTransform.cpp +++ b/geometry3d/trunkMeshTransform.cpp @@ -51,12 +51,33 @@ using namespace DGtal; /** @page trunkMeshTransform trunkMeshTransform - @brief Description of the tool... - à)d + @brief Transform an input mesh into points cloud simulating acquisition process like lidar Scan . + @b Usage: trunkMeshTransform [input] + Usage: ./geometry3d/trunkMeshTransform [input] + + Typical use example: + trunkMeshTransform ../Samples/TrunkSample/chene1.off -c ../Samples/TrunkSample/chene1-cyl -p ../Samples/TrunkSample/chene1_centerline.xyz resTransform.off -s 200 1 --outputPoints resTransform.pts -F 0.8 -P 5.0 --mainDir 0 -1 0 + + Positionals: + 1 TEXT:FILE REQUIRED Input file + 2 TEXT:FILE REQUIRED Input file containing cylinder coordinates + 3 TEXT:FILE REQUIRED Input file containing pith coordinates + 3 TEXT Output mesh file name. + @b Allowed @b options @b are : - + -h,--help Print this help message and exit + -i,--inputMesh TEXT:FILE REQUIRED Input file + -c,--InputCCoords TEXT:FILE REQUIRED Input file containing cylinder coordinates + -p,--InputPithCoords TEXT:FILE REQUIRED + Input file containing pith coordinates + -F,--filterFaceNormal FLOAT Filter mesh faces using their normal vector: the accepted face orientations are defined the face normal and filtering direction (see option --filterDir). + -P,--filterFacePosition FLOAT Filter mesh faces using angle defined from the face barycenter position with its associated pith center (angle in radians) and the filtering direction (see option --filterDir). + -s,--shiftFacePos [FLOAT,FLOAT] Shift face position using maximal amplitude (first parameter value) of sector shift (sector size defined with the second parameter value). + -m,--mainDir FLOAT x 3 Define the main direction to define the filtering angle based (see --filterFacePosition and --filterFaceNormal + -o,--outputMesh TEXT Output mesh file name. + --outputPoints TEXT Output pts file name @code -h [ --help ] display this message -i [ --input ] arg an input file... @@ -171,7 +192,9 @@ int main( int argc, char** argv ) Z3i::RealPoint mainDir {1.0,0.0,0.0}; std::vector mainDirV {1.0,0.0,0.0}; usage << "Usage: " << argv[0] << " [input]\n" - << "Typical use example:\n \t trunkMeshTransform -i ... \n"; + << "Typical use example:\n \t trunkMeshTransform ../Samples/TrunkSample/chene1.off -c ../Samples/TrunkSample/chene1-cyl " + <<"-p ../Samples/TrunkSample/chene1_centerline.xyz resTransform.off -s 200 1 --outputPoints resTransform.pts " + << "-F 0.8 -P 5.0 --mainDir 0 -1 0 \n"; // parse command line using CLI------------------------------------------------------- CLI::App app; app.description("Transform an input mesh into points cloud simulating acquisition process like lidar Scan .\n" + usage.str() ); From 6a22f55c2d579a04b158035fda87d8065a9a9388 Mon Sep 17 00:00:00 2001 From: Bertrand Kerautret Date: Tue, 12 Mar 2024 16:21:28 +0100 Subject: [PATCH 13/14] add vertical resoution filter --- geometry3d/trunkMeshTransform.cpp | 65 ++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/geometry3d/trunkMeshTransform.cpp b/geometry3d/trunkMeshTransform.cpp index b9008d9..4d1cbe3 100644 --- a/geometry3d/trunkMeshTransform.cpp +++ b/geometry3d/trunkMeshTransform.cpp @@ -96,6 +96,9 @@ using namespace DGtal; @ref trunkMeshTransform.cpp */ + +typedef DGtal::Mesh Mesh3D; + double gaussF(double x, double mu, double sigma){ double max = exp((-(mu)*(mu))/(2.0*sigma*sigma))*(1.0/(sigma*sqrt(2*M_PI))); @@ -142,6 +145,35 @@ struct PithSectionCenter { }; +struct TrunkAngularSamplor { + double myDistanceScan; + double myAngularVSize; // the vertical angular scan resolution + Mesh3D myMesh; + double myAngularToleranceFactor = 0.1; // the angle factor sensibility to consider a face is intersected by a laser bean. For instance a factor of 1 will consider all faces where face barycenters exactly intersect the laser bean while a value of 0.1 will a marge of myAngularVSize/10. + TrunkAngularSamplor(const Mesh3D &aMesh, const PithSectionCenter &pSectCenter, + double distanceScan, double angularTolFact=0.1, + double angularVSize = 0.1, bool estimateScanVRes = true): + myDistanceScan(distanceScan), + myMesh(aMesh), myAngularVSize(angularVSize){ + if (estimateScanVRes) { + double dz = pSectCenter.myPith[0][2]-pSectCenter.myPith[1][2]; + double l = sqrt(dz*dz+myDistanceScan*myDistanceScan); + myAngularVSize = abs(asin(dz/l)); + trace.info() << "estimated vertical scan angle size: " << myAngularVSize << std::endl; + } + } + bool isScanned(Mesh3D::Index aFaceId){ + auto p = myMesh.getFaceBarycenter(aFaceId); + double l = sqrt( (p[2])*(p[2]) + myDistanceScan*myDistanceScan); + double a = asin(p[2]/l); + double rS = ceil(a / myAngularVSize)*myAngularVSize-a; + double rI = a-floor(a / myAngularVSize)*myAngularVSize; + + return abs(rI) < myAngularVSize*myAngularToleranceFactor || + abs(rS) < myAngularVSize*myAngularToleranceFactor ; + } +}; + struct TrunkDeformator { double mySectorSize; double myMinZ, myMaxZ; @@ -217,12 +249,11 @@ int main( int argc, char** argv ) CLI11_PARSE(app, argc, argv); // END parse command line using CLI ---------------------------------------------- - DGtal::Mesh resultingMesh; + Mesh3D resultingMesh; // Reading input mesh -------------------------------------------------- - DGtal::Mesh aMesh; + Mesh3D aMesh; - DGtal::Mesh aCenterLine; std::vector cylCoordinates; std::vector pith; @@ -253,10 +284,8 @@ int main( int argc, char** argv ) for (auto it = aMesh.vertexBegin(); it != aMesh.vertexEnd(); it++){ resultingMesh.addVertex(*it); } - - - // First sector extraction + // First sector extraction mainDir = mainDir.getNormalized(); @@ -272,32 +301,41 @@ int main( int argc, char** argv ) pt[0] = newP[0]; pt[1] = newP[1]; pt[2] = newP[2]; } } - //b) filter faces from face normal vector - for (auto it = aMesh.faceBegin(); - it!= aMesh.faceEnd(); it++){ - DGtal::Mesh::MeshFace aFace = *it; + TrunkAngularSamplor tSamplor (aMesh, pSct, 5000); + //b) filter faces from face normal vector and c) applying sampling simulation + for (unsigned int i = 0; i< aMesh.nbFaces(); i++){ + + DGtal::Mesh::MeshFace aFace = aMesh.getFace(i); bool okOrientation = true; + bool okSampling = true; + Z3i::RealPoint p0 = aMesh.getVertex(aFace.at(1)); Z3i::RealPoint p1 = aMesh.getVertex(aFace.at(0)); Z3i::RealPoint p2 = aMesh.getVertex(aFace.at(2)); - if (filterFaceNormal -> count() > 0 ){ + okSampling = tSamplor.isScanned(i); + + if (filterFaceNormal -> count() > 0 && okSampling ){ Z3i::RealPoint vectNormal = ((p1-p0).crossProduct(p2 - p0)).getNormalized(); vectNormal /= vectNormal.norm(); okOrientation = vectNormal.dot(mainDir) > cos(normalAngleRange/2.0); } bool sectorCompatible = true; - if (filterFacePosition -> count() > 0 ){ + if (filterFacePosition -> count() > 0 && okSampling){ auto pB = (p0+p1+p2)/3.0; Z3i::RealPoint pC = pSct.pithRepresentant(pB); Z3i::RealPoint vectDir = pB - pC; vectDir /= vectDir.norm(); sectorCompatible = vectDir.dot(mainDir) > cos(posAngleRange/2.0); } - if( okOrientation && sectorCompatible ){ + + if( okOrientation && sectorCompatible && okSampling){ resultingMesh.addFace(aFace); } } + + + trace.info() << "Cleaning isolated vertices from " << resultingMesh.nbVertex(); resultingMesh.removeIsolatedVertices(); trace.info() << " to " << resultingMesh.nbVertex() << " [done]" << std::endl; @@ -322,3 +360,4 @@ int main( int argc, char** argv ) } + From 5b682bde9a5504e6ba43fe1c8e33a9584379637b Mon Sep 17 00:00:00 2001 From: Bertrand Kerautret Date: Tue, 12 Mar 2024 17:07:20 +0100 Subject: [PATCH 14/14] add option to set the smapling --- geometry3d/trunkMeshTransform.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/geometry3d/trunkMeshTransform.cpp b/geometry3d/trunkMeshTransform.cpp index 4d1cbe3..b8b3bda 100644 --- a/geometry3d/trunkMeshTransform.cpp +++ b/geometry3d/trunkMeshTransform.cpp @@ -223,6 +223,11 @@ int main( int argc, char** argv ) std::pair shiftFacePosParams {1.0, 1.0}; Z3i::RealPoint mainDir {1.0,0.0,0.0}; std::vector mainDirV {1.0,0.0,0.0}; + + double vSampleDist {5000.0}; + double vSampleAngularResol = 0.001; + double vSampleAngularSensi = 0.1; + usage << "Usage: " << argv[0] << " [input]\n" << "Typical use example:\n \t trunkMeshTransform ../Samples/TrunkSample/chene1.off -c ../Samples/TrunkSample/chene1-cyl " <<"-p ../Samples/TrunkSample/chene1_centerline.xyz resTransform.off -s 200 1 --outputPoints resTransform.pts " @@ -243,6 +248,11 @@ int main( int argc, char** argv ) ->expected(1); auto mainDirOpt = app.add_option("--mainDir,-m", mainDirV, "Define the main direction to define the filtering angle based (see --filterFacePosition and --filterFaceNormal ") ->expected(3); + auto vertSampleOpt = app.add_flag("--verticalSampling", "Apply a vertical sampling simulation by considering the laser scan."); + app.add_option("--scannerDistance", vSampleDist, "Define the laser scan position distance. (effect only with --verticalSampling) .", true); + + auto vROpt = app.add_option("--vSampleAngularResol", vSampleAngularResol, "Define the vertical angular resolution of the laser scanner. (effect only with --verticalSampling) "); + auto vSOpt = app.add_option("--vSampleAngularSensi", vSampleAngularSensi, "Defines the vertical angular sensibility laser scan intersection detection. (effect only with --verticalSampling) "); auto outMesh = app.add_option("--outputMesh,-o,3", outputMesh, "Output mesh file name."); auto outPts = app.add_option("--outputPoints", outputPts, "Output pts file name"); app.get_formatter()->column_width(40); @@ -301,7 +311,15 @@ int main( int argc, char** argv ) pt[0] = newP[0]; pt[1] = newP[1]; pt[2] = newP[2]; } } - TrunkAngularSamplor tSamplor (aMesh, pSct, 5000); + + TrunkAngularSamplor tSamplor (aMesh, pSct, vSampleDist); + if (vROpt -> count()>0){ + tSamplor.myAngularVSize = vSampleAngularResol; + } + if (vSOpt -> count()>0){ + tSamplor.myAngularToleranceFactor = vSampleAngularSensi; + } + //b) filter faces from face normal vector and c) applying sampling simulation for (unsigned int i = 0; i< aMesh.nbFaces(); i++){ @@ -312,7 +330,7 @@ int main( int argc, char** argv ) Z3i::RealPoint p0 = aMesh.getVertex(aFace.at(1)); Z3i::RealPoint p1 = aMesh.getVertex(aFace.at(0)); Z3i::RealPoint p2 = aMesh.getVertex(aFace.at(2)); - okSampling = tSamplor.isScanned(i); + okSampling = (vertSampleOpt-> count()>0) ? tSamplor.isScanned(i) : true; if (filterFaceNormal -> count() > 0 && okSampling ){ Z3i::RealPoint vectNormal = ((p1-p0).crossProduct(p2 - p0)).getNormalized();