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"
48 using namespace DGtal;
105 template<
typename TPo
int,
typename TEmbeder>
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]);
119 return ((t.inverse())*vect);
125 template<
typename TImage,
typename TVectorField,
typename TPo
int>
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);
139 int main(
int argc,
char** argv )
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;
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;
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)" )
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).");
197 app.get_formatter()->column_width(40);
198 CLI11_PARSE(app, argc, argv);
201 widthImageScan *= meshScale;
202 heightImageScan *= meshScale;
203 maxScan *= meshScale;
204 centerX *= meshScale;
205 centerY *= meshScale;
206 centerZ *= meshScale;
209 trace.info() <<
"Reading input file " << inputFileName ;
210 Mesh<Z3i::RealPoint> inputMesh(
true);
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);
219 inputMesh.quadToTriangularFaces();
220 inputMesh.changeScale(meshScale);
221 trace.info() <<
" [done] " << std::endl ;
222 double maxArea = triangleAreaUnit+1.0 ;
223 while(maxArea> triangleAreaUnit)
225 trace.info()<<
"Iterating mesh subdivision ... "<< maxArea;
226 maxArea = inputMesh.subDivideTriangularFaces(triangleAreaUnit);
227 trace.info() <<
" [done]"<< std::endl;
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));
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++)
241 meshVolImage.setValue(*it, 0);
242 meshNormalImage.setValue(*it, z);
246 for(
unsigned int i =0; i< inputMesh.nbFaces(); i++)
248 trace.progressBar(i, inputMesh.nbFaces());
249 typename Mesh<Z3i::RealPoint>::MeshFace aFace = inputMesh.getFace(i);
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);
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);
266 if(orientAutoFrontX || orientAutoFrontY || orientAutoFrontZ)
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];
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) ;
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);
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);
294 functors::Rescaling<unsigned int, Image2D::Value> scaleFctDepth(0, maxScan, 0, 255);
295 if(maxScan > std::numeric_limits<Image2D::Value>::max())
297 trace.info()<<
"Max depth value outside image intensity range: " << maxScan
298 <<
" (use a rescaling functor which implies a loss of precision)" << std::endl;
300 trace.info() <<
"Processing image to output file " << outputFileName;
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);
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);
315 DGtal::functors::Identity idV;
317 unsigned int maxDepthFound = 0;
318 for(
unsigned int k=0; k < maxScan; k++)
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(),
326 ImageAdapterExtractor extractedImage(meshVolImage, aDomain2D, embedder, idV);
327 for(Image2D::Domain::ConstIterator it = extractedImage.domain().begin();
328 it != extractedImage.domain().end(); it++)
330 if(resultingImage(*it)== 0 && extractedImage(*it)!=0)
333 resultingImage.setValue(*it, scaleFctDepth(maxScan-k));
334 resultingVectorField.setValue(*it, getNormal(meshNormalImage(embedder(*it)), embedder));
338 if (setBackgroundLastDepth)
340 for(Image2D::Domain::ConstIterator it = resultingImage.domain().begin();
341 it != resultingImage.domain().end(); it++){
342 if( resultingImage(*it)== 0 )
344 resultingImage.setValue(*it, scaleFctDepth(maxScan-maxDepthFound));
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))
353 resultingVectorField.setValue(*it,Z3i::RealPoint(0, 0, inverBgNormal?-1: 1));
356 resultingImage >> outputFileName;
358 std::stringstream ss;
359 ss << outputFileName <<
".normals";
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];
371 return EXIT_SUCCESS;;