32 #include "DGtal/base/Common.h"
33 #include "DGtal/helpers/StdDefs.h"
34 #include "DGtal/images/ImageContainerBySTLVector.h"
35 #include "DGtal/io/writers/GenericWriter.h"
36 #include "DGtal/io/readers/MeshReader.h"
37 #include "DGtal/io/writers/MeshWriter.h"
38 #include "DGtal/images/ConstImageAdapter.h"
39 #include "DGtal/kernel/BasicPointFunctors.h"
40 #include <DGtal/base/BasicFunctors.h>
41 #include "DGtal/math/linalg/SimpleMatrix.h"
43 #include <DGtal/math/linalg/SimpleMatrix.h>
44 #include <DGtal/math/linalg/EigenDecomposition.h>
53 using namespace DGtal;
111 for(
unsigned int i = 0; i<3; i++){
112 for(
unsigned int j = 0; j<3; j++){
120 std::pair<DGtal::Z3i::RealPoint, DGtal::Z3i::RealPoint>
123 std::pair<DGtal::Z3i::RealPoint, DGtal::Z3i::RealPoint> res;
129 for(
auto it=begin; it != end; it++){
131 centroid[0] += pt[0];
132 centroid[1] += pt[1];
133 centroid[2] += pt[2];
138 for(
auto it=begin; it != end; it++){
141 pt[0] -= centroid[0];
142 pt[1] -= centroid[1];
143 pt[2] -= centroid[2];
146 c[4] += pt[1] * pt[1];
147 c[5] += pt[1] * pt[2];
148 c[8] += pt[2] * pt[2];
166 unsigned int temp = 0;
167 unsigned int major_index = 0;
168 unsigned int middle_index = 1;
169 unsigned int minor_index = 2;
171 if (eVals(major_index) < eVals(middle_index))
174 major_index = middle_index;
177 if (eVals(major_index) < eVals(minor_index))
180 major_index = minor_index;
183 if (eVals(middle_index) < eVals(minor_index))
186 minor_index = middle_index;
190 res.first = eVects.
column(major_index);
191 res.second = eVects.
column(middle_index);
196 template<
typename TPo
int,
typename TEmbeder>
198 getNormal(
const TPoint &vect,
const TEmbeder &emb ){
215 template<
typename TImage,
typename TVectorField,
typename TPo
int>
217 fillPointArea(TImage &anImage, TVectorField &imageVectorField,
218 const TPoint aPoint,
const unsigned int size,
const unsigned int value,
const Z3i::RealPoint &n){
219 typename TImage::Domain aDom(aPoint-TPoint::diagonal(size), aPoint+TPoint::diagonal(size));
220 for(
typename TImage::Domain::ConstIterator it= aDom.begin(); it != aDom.end(); it++){
221 if (anImage.domain().isInside(*it)){
222 anImage.setValue(*it, value);
223 imageVectorField.setValue(*it, n);
229 int main(
int argc,
char** argv )
241 std::string inputFileName;
242 std::string outputFileName {
"result.pgm"};
243 double meshScale = 1.0;
244 double triangleAreaUnit = 0.01;
245 double widthImageScan = {500};
246 double heightImageScan = {500};
247 bool orientAutoFrontX =
false;
248 bool orientAutoFrontY =
false;
249 bool orientAutoFrontZ =
false;
250 bool orientAuto =
true;
251 bool invertNormal =
false;
252 bool orientBack =
false;
253 bool exportNormals =
false;
254 bool setBackgroundLastDepth =
false;
255 bool backgroundNormalBack =
false;
266 unsigned int minDiagVolSize {400};
267 app.description(
"Convert a mesh file into a projected 2D image given from a normal direction N and from a starting point P. The 3D mesh discretized and scanned in the normal direction N, starting from P with a step 1.\n Example:\n mesh2heightfield -i ${DGtal}/examples/samples/tref.off heighfield.pgm \n");
268 app.add_option(
"-i,--input,1", inputFileName,
"mesh file (.off)" )
270 ->check(CLI::ExistingFile);
271 app.add_option(
"-o,--output,2", outputFileName,
"sequence of discrete point file (.sdp) ",
true );
272 app.add_option(
"--meshScale,-s", meshScale,
"change the default mesh scale (each vertex multiplied by the scale) ");
273 app.add_option(
"--remeshMinArea,-a", triangleAreaUnit,
"ajust the remeshing min triangle are used to avoid empty areas",
true);
275 app.add_option(
"--heightFieldMaxScan", maxScan,
"set the maximal scan deep.",
true );
276 app.add_option(
"-x,--centerX",
277 centerX,
"choose x center of the projected image.",
true);
278 app.add_option(
"-y,--centerY",
279 centerY,
"choose y center of the projected image.",
true);
280 app.add_option(
"-z,--centerZ",
281 centerZ,
"choose z center of the projected image.",
true);
282 auto optNx = app.add_option(
"--nx",
283 nx,
"set the x component of the projection direction.",
true);
284 auto optNy = app.add_option(
"--ny",
285 ny,
"set the y component of the projection direction.",
true);
286 auto optNz = app.add_option(
"--nz",
287 nz,
"set the z component of the projection direction.",
true);
288 app.add_flag(
"--invertNormals,-v", invertNormal,
"invert normal vector of the mesh");
289 app.add_option(
"--width", widthImageScan,
"set the width of the area to be extracted as an height field image. (note that the resulting image width also depends of the scale parameter (option --meshScale))",
true );
290 app.add_option(
"--height", heightImageScan,
"set the height of the area to extracted as an height field image. (note that the resulting image height also depends of the scale parameter (option --meshScale))",
true );
291 app.add_flag(
"--orientAutoFrontX", orientAutoFrontX,
"automatically orients the camera in front according the x axis." );
292 app.add_flag(
"--orientAutoFrontY", orientAutoFrontY,
"automatically orients the camera in front according the y axis." );
293 app.add_flag(
"--orientAutoFrontZ", orientAutoFrontZ,
"automatically orients the camera in front according the z axis." );
294 app.add_flag(
"--orientBack", orientBack,
"change the camera direction to back instead front as given in orientAutoFront{X,Y,Z} options.");
295 app.add_flag(
"--exportNormals", exportNormals,
"export mesh normal vectors (given in the image height field basis).");
296 app.add_flag(
"--backgroundNormalBack", backgroundNormalBack,
"set the normals of background in camera opposite direction (to obtain a black background in rendering).");
297 app.add_flag(
"--setBackgroundLastDepth", setBackgroundLastDepth,
"change the default background (black with the last filled intensity).");
299 app.get_formatter()->column_width(40);
300 CLI11_PARSE(app, argc, argv);
303 orientAuto = optNx->count() == 0 && optNy->count() == 0 && optNy->count() == 0;
305 trace.
info() <<
"Reading input file " << inputFileName ;
307 inputMesh << inputFileName;
308 std::pair<Z3i::RealPoint, Z3i::RealPoint> b = inputMesh.getBoundingBox();
309 double diagDist = (b.first-b.second).norm();
310 if(diagDist<minDiagVolSize){
311 meshScale = minDiagVolSize/diagDist;
312 inputMesh.rescale(meshScale);
315 triangleAreaUnit *= meshScale;
317 inputMesh.vertexBegin();
319 inputMesh.quadToTriangularFaces();
320 trace.
info() <<
" [done] " << std::endl ;
321 int maxArea = triangleAreaUnit+1.0 ;
322 while (maxArea> triangleAreaUnit)
324 trace.
info()<<
"Iterating mesh subdivision ... "<< maxArea;
325 maxArea = inputMesh.subDivideTriangularFaces(triangleAreaUnit);
329 std::pair<Z3i::RealPoint, Z3i::RealPoint> bb = inputMesh.getBoundingBox();
331 bb.second+Z3i::Point::diagonal(1));
334 Image3D meshVolImage(meshDomain);
335 VectorFieldImage3D meshNormalImage(meshDomain);
338 it != meshVolImage.domain().end(); it++)
340 meshVolImage.setValue(*it, 0);
341 meshNormalImage.setValue(*it, z);
345 for(
unsigned int i =0; i< inputMesh.nbFaces(); i++)
355 n /= invertNormal? -n.
norm(): n.
norm();
357 fillPointArea(meshVolImage, meshNormalImage, p1, 1, 1, n);
358 fillPointArea(meshVolImage, meshNormalImage, p2, 1, 1, n);
359 fillPointArea(meshVolImage, meshNormalImage, p3, 1, 1, n);
360 fillPointArea(meshVolImage, meshNormalImage, c, 1, 1, n);
365 if(orientAutoFrontX || orientAutoFrontY || orientAutoFrontZ)
367 Z3i::Point ptL = meshVolImage.domain().lowerBound();
368 Z3i::Point ptU = meshVolImage.domain().upperBound();
370 centerX=ptC[0]; centerY=ptC[1]; centerZ=ptC[2];
376 nx=(orientBack?-1.0:1.0);
377 maxScan = meshVolImage.domain().upperBound()[0]- meshVolImage.domain().lowerBound()[0];
378 centerX = centerX + (orientBack? maxScan/2: -maxScan/2) ;
382 ny=(orientBack?-1.0:1.0);
383 maxScan = meshVolImage.domain().upperBound()[1]- meshVolImage.domain().lowerBound()[1];
384 centerY = centerY + (orientBack? maxScan/2: -maxScan/2);
388 nz=(orientBack?-1.0:1.0);
389 maxScan = meshVolImage.domain().upperBound()[2]-meshVolImage.domain().lowerBound()[2];
390 centerZ = centerZ + (orientBack? maxScan/2: -maxScan/2);
394 Z3i::Point ptL = meshVolImage.domain().lowerBound();
395 Z3i::Point ptU = meshVolImage.domain().upperBound();
397 int maxScanZ = meshVolImage.domain().upperBound()[2]-meshVolImage.domain().lowerBound()[2];
398 int maxScanY = meshVolImage.domain().upperBound()[1]-meshVolImage.domain().lowerBound()[1];
399 int maxScanX = meshVolImage.domain().upperBound()[0]-meshVolImage.domain().lowerBound()[0];
400 maxScan = std::max(std::max(maxScanX, maxScanY), maxScanZ);
401 trace.
info() <<
"Automatic orientation on volume of bounds: " << ptL <<
" " << ptU << std::endl;
403 auto dir = getMainDirsCoVar(inputMesh.vertexBegin(), inputMesh.vertexEnd());
409 centerX=ptC[0]-(double)maxScan*nx/2;
410 centerY=ptC[1]-(double)maxScan*ny/2;
411 centerZ=ptC[2]-(double)maxScan*nz/2;
416 if(maxScan > std::numeric_limits<Image2D::Value>::max())
418 trace.
info()<<
"Max depth value outside image intensity range: " << maxScan
419 <<
" (use a rescaling functor which implies a loss of precision)" << std::endl;
421 trace.
info() <<
"Processing image to output file " << outputFileName;
425 Z3i::Point ptCenter (centerX, centerY, centerZ);
428 Image2D resultingImage(aDomain2D);
429 VectorFieldImage2D resultingVectorField(aDomain2D);
433 it != resultingImage.domain().end(); it++){
434 resultingImage.setValue(*it, 0);
435 resultingVectorField.setValue(*it, z);
439 unsigned int maxDepthFound = 0;
440 Z3i::Point c (ptCenter-normalDir, DGtal::functors::Round<>());
453 bool firstFound =
false;
454 while (k < maxScan || !firstFound){
455 embedder.shiftOriginPoint(normalDir);
457 ImageAdapterExtractor extractedImage(meshVolImage, aDomain2D, embedder, idV);
459 it != extractedImage.domain().end(); it++)
461 if(resultingImage(*it)== 0 && extractedImage(*it)!=0)
468 resultingImage.setValue(*it, scaleFctDepth(maxScan-k));
469 resultingVectorField.setValue(*it, getNormal(meshNormalImage(embedder(*it)), embedder));
476 if (setBackgroundLastDepth)
479 it != resultingImage.domain().end(); it++){
480 if( resultingImage(*it)== 0 )
482 resultingImage.setValue(*it, scaleFctDepth(maxScan-maxDepthFound));
486 bool inverBgNormal = backgroundNormalBack;
488 it != resultingImage.domain().end(); it++){
491 resultingVectorField.setValue(*it,
Z3i::RealPoint(0, 0, inverBgNormal?-1: 1));
494 resultingImage >> outputFileName;
496 std::stringstream ss;
497 ss << outputFileName <<
".normals";
499 outN.open(ss.str().c_str(), std::ofstream::out);
501 it != resultingImage.domain().end(); it++){
502 outN << (*it)[0] <<
" " << (*it)[1] <<
" " << 0 << std::endl;
503 outN <<(*it)[0]+ resultingVectorField(*it)[0] <<
" "
504 <<(*it)[1]+resultingVectorField(*it)[1] <<
" "
505 << resultingVectorField(*it)[2];
509 return EXIT_SUCCESS;;
int main(int argc, char **argv)
static void getEigenDecomposition(const Matrix &matrix, Matrix &eigenVectors, Vector &eigenValues)
std::vector< Value >::const_iterator ConstIterator
std::vector< unsigned int > MeshFace
VertexStorage::iterator Iterator
Self crossProduct(const Self &v) const
double norm(const NormType type=L_2) const
SimpleMatrix< Component, TM, TN > inverse() const
void setComponent(const DGtal::Dimension i, const DGtal::Dimension j, const Component &aValue)
ColumnVector column(const DGtal::Dimension j) const
void progressBar(const double currentValue, const double maximalValue)
Trace trace(traceWriterTerm)