DGtalTools  1.2.0
mesh2heightfield.cpp
1 
30 #include <iostream>
31 #include <fstream>
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"
42 
43 #include "CLI11.hpp"
44 
45 
46 
47 using namespace std;
48 using namespace DGtal;
49 
50 
51 
105 template<typename TPoint, typename TEmbeder>
106 TPoint
107 getNormal(const TPoint &vect, const TEmbeder &emb ){
108  Z3i::RealPoint p0 = emb(Z2i::RealPoint(0.0,0.0), false);
109  Z3i::RealPoint px = emb(Z2i::RealPoint(10.0,0.0), false);
110  Z3i::RealPoint py = emb(Z2i::RealPoint(0.0,10.0), false);
111  Z3i::RealPoint u = px-p0; u /= u.norm();
112  Z3i::RealPoint v = py-p0; v /= v.norm();
113  Z3i::RealPoint w = u.crossProduct(v); w /= w.norm();
114  SimpleMatrix<double, 3, 3> t;
115  t.setComponent(0,0, u[0]); t.setComponent(0,1, v[0]); t.setComponent(0, 2, w[0]);
116  t.setComponent(1,0, u[1]); t.setComponent(1,1, v[1]); t.setComponent(1, 2, w[1]);
117  t.setComponent(2,0, u[2]); t.setComponent(2,1, v[2]); t.setComponent(2, 2, w[2]);
118 
119  return ((t.inverse())*vect);
120 }
121 
122 
123 
124 
125 template<typename TImage, typename TVectorField, typename TPoint>
126 void
127 fillPointArea(TImage &anImage, TVectorField &imageVectorField,
128  const TPoint aPoint, const unsigned int size, const unsigned int value, const Z3i::RealPoint &n){
129  typename TImage::Domain aDom(aPoint-TPoint::diagonal(size), aPoint+TPoint::diagonal(size));
130  for(typename TImage::Domain::ConstIterator it= aDom.begin(); it != aDom.end(); it++){
131  if (anImage.domain().isInside(*it)){
132  anImage.setValue(*it, value);
133  imageVectorField.setValue(*it, n);
134  }
135  }
136 }
137 
138 
139 int main( int argc, char** argv )
140 {
141  typedef ImageContainerBySTLVector < Z3i::Domain, Z3i::RealPoint > VectorFieldImage3D;
142  typedef ImageContainerBySTLVector < Z2i::Domain, Z3i::RealPoint > VectorFieldImage2D;
143  typedef ImageContainerBySTLVector < Z3i::Domain, unsigned char > Image3D;
144  typedef ImageContainerBySTLVector < Z2i::Domain, unsigned char> Image2D;
145  typedef DGtal::ConstImageAdapter<Image3D, Z2i::Domain, DGtal::functors::Point2DEmbedderIn3D<DGtal::Z3i::Domain>,
146  Image3D::Value, DGtal::functors::Identity > ImageAdapterExtractor;
147 
148 
149 // parse command line using CLI ----------------------------------------------
150  CLI::App app;
151  std::string inputFileName;
152  std::string outputFileName {"result.pgm"};
153  double meshScale = 1.0;
154  double triangleAreaUnit = 0.5;
155  unsigned int widthImageScan = {100};
156  unsigned int heightImageScan = {100};
157  bool orientAutoFrontX = false;
158  bool orientAutoFrontY = false;
159  bool orientAutoFrontZ = false;
160  bool orientBack = false;
161  bool exportNormals = false;
162  bool setBackgroundLastDepth = false;
163  bool backgroundNormalBack = false;
164  int maxScan {255};
165  int centerX {0};
166  int centerY {0};
167  int centerZ {1};
168 
169  double nx{0};
170  double ny{0};
171  double nz{1};
172 
173 
174  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 ${DGtal}/examples/samples/tref.off heighMap.pgm --orientAutoFrontZ --width 25 --height 25 -s 10 \n");
175  app.add_option("-i,--input,1", inputFileName, "mesh file (.off)" )
176  ->required()
177  ->check(CLI::ExistingFile);
178  app.add_option("-o,--output,2", outputFileName, "sequence of discrete point file (.sdp) ", true );
179  app.add_option("--meshScale,-s", meshScale, "change the default mesh scale (each vertex multiplied by the scale) ");
180  app.add_option("--heightFieldMaxScan", maxScan, "set the maximal scan deep.", true );
181  app.add_option("-x,--centerX",centerX, "choose x center of the projected image.", true);
182  app.add_option("-y,--centerY",centerY, "choose y center of the projected image.", true);
183  app.add_option("-z,--centerZ",centerZ, "choose z center of the projected image.", true);
184  app.add_option("--nx", nx, "set the x component of the projection direction.", true);
185  app.add_option("--ny", ny, "set the y component of the projection direction.", true);
186  app.add_option("--nz", nz, "set the z component of the projection direction.", true);
187  app.add_option("--width", widthImageScan, "set the width of the area to be extracted as an height field image.", true );
188  app.add_option("--height", heightImageScan, "set the height of the area to extracted as an height field image.", true );
189  app.add_flag("--orientAutoFrontX", orientAutoFrontX,"automatically orients the camera in front according the x axis." );
190  app.add_flag("--orientAutoFrontY", orientAutoFrontY,"automatically orients the camera in front according the y axis." );
191  app.add_flag("--orientAutoFrontZ", orientAutoFrontZ,"automatically orients the camera in front according the z axis." );
192  app.add_flag("--orientBack", orientBack, "change the camera direction to back instead front as given in orientAutoFront{X,Y,Z} options.");
193  app.add_flag("--exportNormals", exportNormals, "export mesh normal vectors (given in the image height field basis).");
194  app.add_flag("--backgroundNormalBack", backgroundNormalBack, "set the normals of background in camera opposite direction (to obtain a black background in rendering).");
195  app.add_flag("--setBackgroundLastDepth", setBackgroundLastDepth, "change the default background (black with the last filled intensity).");
196 
197  app.get_formatter()->column_width(40);
198  CLI11_PARSE(app, argc, argv);
199  // END parse command line using CLI ----------------------------------------------
200 
201  widthImageScan *= meshScale;
202  heightImageScan *= meshScale;
203  maxScan *= meshScale;
204  centerX *= meshScale;
205  centerY *= meshScale;
206  centerZ *= meshScale;
207 
208 
209  trace.info() << "Reading input file " << inputFileName ;
210  Mesh<Z3i::RealPoint> inputMesh(true);
211 
212  DGtal::MeshReader<Z3i::RealPoint>::importOFFFile(inputFileName, inputMesh);
213  std::pair<Z3i::RealPoint, Z3i::RealPoint> b = inputMesh.getBoundingBox();
214  double diagDist = (b.first-b.second).norm();
215  if(diagDist<2.0*sqrt(2.0)){
216  inputMesh.changeScale(2.0*sqrt(2.0)/diagDist);
217  }
218 
219  inputMesh.quadToTriangularFaces();
220  inputMesh.changeScale(meshScale);
221  trace.info() << " [done] " << std::endl ;
222  double maxArea = triangleAreaUnit+1.0 ;
223  while(maxArea> triangleAreaUnit)
224  {
225  trace.info()<< "Iterating mesh subdivision ... "<< maxArea;
226  maxArea = inputMesh.subDivideTriangularFaces(triangleAreaUnit);
227  trace.info() << " [done]"<< std::endl;
228  }
229 
230  std::pair<Z3i::RealPoint, Z3i::RealPoint> bb = inputMesh.getBoundingBox();
231  Image3D::Domain meshDomain( bb.first-Z3i::Point::diagonal(1),
232  bb.second+Z3i::Point::diagonal(1));
233 
234  // vol image filled from the mesh vertex.
235  Image3D meshVolImage(meshDomain);
236  VectorFieldImage3D meshNormalImage(meshDomain);
237  Z3i::RealPoint z(0.0,0.0,0.0);
238  for(Image3D::Domain::ConstIterator it = meshVolImage.domain().begin();
239  it != meshVolImage.domain().end(); it++)
240  {
241  meshVolImage.setValue(*it, 0);
242  meshNormalImage.setValue(*it, z);
243  }
244 
245  // Filling mesh faces in volume.
246  for(unsigned int i =0; i< inputMesh.nbFaces(); i++)
247  {
248  trace.progressBar(i, inputMesh.nbFaces());
249  typename Mesh<Z3i::RealPoint>::MeshFace aFace = inputMesh.getFace(i);
250  if(aFace.size()==3)
251  {
252  Z3i::RealPoint p1 = inputMesh.getVertex(aFace[0]);
253  Z3i::RealPoint p2 = inputMesh.getVertex(aFace[1]);
254  Z3i::RealPoint p3 = inputMesh.getVertex(aFace[2]);
255  Z3i::RealPoint n = (p2-p1).crossProduct(p3-p1);
256  n /= n.norm();
257  Z3i::RealPoint c = (p1+p2+p3)/3.0;
258  fillPointArea(meshVolImage, meshNormalImage, p1, 1, 1, n);
259  fillPointArea(meshVolImage, meshNormalImage, p2, 1, 1, n);
260  fillPointArea(meshVolImage, meshNormalImage, p3, 1, 1, n);
261  fillPointArea(meshVolImage, meshNormalImage, c, 1, 1, n);
262  }
263  }
264 
265 
266  if(orientAutoFrontX || orientAutoFrontY || orientAutoFrontZ)
267  {
268  Z3i::Point ptL = meshVolImage.domain().lowerBound();
269  Z3i::Point ptU = meshVolImage.domain().upperBound();
270  Z3i::Point ptC = (ptL+ptU)/2;
271  centerX=ptC[0]; centerY=ptC[1]; centerZ=ptC[2];
272  nx=0; ny=0; nz=0;
273  }
274 
275  if(orientAutoFrontX)
276  {
277  nx=(orientBack?-1.0:1.0);
278  maxScan = meshVolImage.domain().upperBound()[0]- meshVolImage.domain().lowerBound()[0];
279  centerX = centerX + (orientBack? maxScan/2: -maxScan/2) ;
280  }
281  if(orientAutoFrontY)
282  {
283  ny=(orientBack?-1.0:1.0);
284  maxScan = meshVolImage.domain().upperBound()[1]- meshVolImage.domain().lowerBound()[1];
285  centerY = centerY + (orientBack? maxScan/2: -maxScan/2);
286  }
287  if(orientAutoFrontZ)
288  {
289  nz=(orientBack?-1.0:1.0);
290  maxScan = meshVolImage.domain().upperBound()[2]-meshVolImage.domain().lowerBound()[2];
291  centerZ = centerZ + (orientBack? maxScan/2: -maxScan/2);
292  }
293 
294  functors::Rescaling<unsigned int, Image2D::Value> scaleFctDepth(0, maxScan, 0, 255);
295  if(maxScan > std::numeric_limits<Image2D::Value>::max())
296  {
297  trace.info()<< "Max depth value outside image intensity range: " << maxScan
298  << " (use a rescaling functor which implies a loss of precision)" << std::endl;
299  }
300  trace.info() << "Processing image to output file " << outputFileName;
301 
302  Image2D::Domain aDomain2D(DGtal::Z2i::Point(0,0),
303  DGtal::Z2i::Point(widthImageScan, heightImageScan));
304  Z3i::Point ptCenter (centerX, centerY, centerZ);
305  Z3i::RealPoint normalDir (nx, ny, nz);
306  Image2D resultingImage(aDomain2D);
307  VectorFieldImage2D resultingVectorField(aDomain2D);
308 
309 
310  for(Image2D::Domain::ConstIterator it = resultingImage.domain().begin();
311  it != resultingImage.domain().end(); it++){
312  resultingImage.setValue(*it, 0);
313  resultingVectorField.setValue(*it, z);
314  }
315  DGtal::functors::Identity idV;
316 
317  unsigned int maxDepthFound = 0;
318  for(unsigned int k=0; k < maxScan; k++)
319  {
320  trace.progressBar(k, maxScan);
321  Z3i::Point c (ptCenter+normalDir*k, DGtal::functors::Round<>());
322  DGtal::functors::Point2DEmbedderIn3D<DGtal::Z3i::Domain > embedder(meshVolImage.domain(),
323  c,
324  normalDir,
325  widthImageScan);
326  ImageAdapterExtractor extractedImage(meshVolImage, aDomain2D, embedder, idV);
327  for(Image2D::Domain::ConstIterator it = extractedImage.domain().begin();
328  it != extractedImage.domain().end(); it++)
329  {
330  if(resultingImage(*it)== 0 && extractedImage(*it)!=0)
331  {
332  maxDepthFound = k;
333  resultingImage.setValue(*it, scaleFctDepth(maxScan-k));
334  resultingVectorField.setValue(*it, getNormal(meshNormalImage(embedder(*it)), embedder));
335  }
336  }
337  }
338  if (setBackgroundLastDepth)
339  {
340  for(Image2D::Domain::ConstIterator it = resultingImage.domain().begin();
341  it != resultingImage.domain().end(); it++){
342  if( resultingImage(*it)== 0 )
343  {
344  resultingImage.setValue(*it, scaleFctDepth(maxScan-maxDepthFound));
345  }
346  }
347  }
348  bool inverBgNormal = backgroundNormalBack;
349  for(Image2D::Domain::ConstIterator it = resultingImage.domain().begin();
350  it != resultingImage.domain().end(); it++){
351  if(resultingVectorField(*it)==Z3i::RealPoint(0.0, 0.0, 0.0))
352  {
353  resultingVectorField.setValue(*it,Z3i::RealPoint(0, 0, inverBgNormal?-1: 1));
354  }
355  }
356  resultingImage >> outputFileName;
357  if(exportNormals){
358  std::stringstream ss;
359  ss << outputFileName << ".normals";
360  std::ofstream outN;
361  outN.open(ss.str().c_str(), std::ofstream::out);
362  for(Image2D::Domain::ConstIterator it = resultingImage.domain().begin();
363  it != resultingImage.domain().end(); it++){
364  outN << (*it)[0] << " " << (*it)[1] << " " << 0 << std::endl;
365  outN <<(*it)[0]+ resultingVectorField(*it)[0] << " "
366  <<(*it)[1]+resultingVectorField(*it)[1] << " "
367  << resultingVectorField(*it)[2];
368  outN << std::endl;
369  }
370  }
371  return EXIT_SUCCESS;;
372 }
Definition: ATu0v1.h:57