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