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" 42 #include <boost/program_options/options_description.hpp> 43 #include <boost/program_options/parsers.hpp> 44 #include <boost/program_options/variables_map.hpp> 47 using namespace DGtal;
51 namespace po = boost::program_options;
115 template<
typename TPo
int,
typename TEmbeder>
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();
124 SimpleMatrix<double, 3, 3> t;
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]);
129 return ((t.inverse())*vect);
135 template<
typename TImage,
typename TVectorField,
typename TPo
int>
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);
149 int main(
int argc,
char** argv )
151 typedef ImageContainerBySTLVector < Z3i::Domain, Z3i::RealPoint > VectorFieldImage3D;
152 typedef ImageContainerBySTLVector < Z2i::Domain, Z3i::RealPoint > VectorFieldImage2D;
153 typedef ImageContainerBySTLVector < Z3i::Domain, unsigned char > Image3D;
154 typedef ImageContainerBySTLVector < Z2i::Domain, unsigned char> Image2D;
155 typedef DGtal::ConstImageAdapter<Image3D, Z2i::Domain, DGtal::functors::Point2DEmbedderIn3D<DGtal::Z3i::Domain>,
156 Image3D::Value, DGtal::functors::Identity > ImageAdapterExtractor;
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). " )
182 (
"setBackgroundLastDepth",
"change the default background (black with the last filled intensity).");
185 double triangleAreaUnit = 0.5;
187 po::variables_map vm;
189 po::store(po::parse_command_line(argc, argv, general_opt), vm);
190 }
catch(
const std::exception& ex){
192 trace.info()<<
"Error checking program options: "<< ex.what()<< endl;
195 if( !parseOK || vm.count(
"help")||argc<=1)
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";
205 if(! vm.count(
"input") ||! vm.count(
"output"))
207 trace.error() <<
" Input and output filename are needed to be defined" << endl;
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);
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);
224 inputMesh.quadToTriangularFaces();
225 inputMesh.changeScale(meshScale);
226 trace.info() <<
" [done] " << std::endl ;
227 double maxArea = triangleAreaUnit+1.0 ;
228 while(maxArea> triangleAreaUnit)
230 trace.info()<<
"Iterating mesh subdivision ... "<< maxArea;
231 maxArea = inputMesh.subDivideTriangularFaces(triangleAreaUnit);
232 trace.info() <<
" [done]"<< std::endl;
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));
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++)
246 meshVolImage.setValue(*it, 0);
247 meshNormalImage.setValue(*it, z);
251 for(
unsigned int i =0; i< inputMesh.nbFaces(); i++)
253 trace.progressBar(i, inputMesh.nbFaces());
254 typename Mesh<Z3i::RealPoint>::MeshFace aFace = inputMesh.getFace(i);
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);
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);
270 unsigned int widthImageScan = vm[
"height"].as<
unsigned int>()*meshScale;
271 unsigned int heightImageScan = vm[
"width"].as<
unsigned int>()*meshScale;
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;
278 double nx = vm[
"nx"].as<
double>();
279 double ny = vm[
"ny"].as<
double>();
280 double nz = vm[
"nz"].as<
double>();
282 if(vm.count(
"orientAutoFrontX")|| vm.count(
"orientAutoFrontY") ||
283 vm.count(
"orientAutoFrontZ"))
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];
291 bool opp = vm.count(
"orientBack");
292 if(vm.count(
"orientAutoFrontX"))
295 maxScan = meshVolImage.domain().upperBound()[0]- meshVolImage.domain().lowerBound()[0];
296 centerX = centerX + (opp? maxScan/2: -maxScan/2) ;
298 if(vm.count(
"orientAutoFrontY"))
301 maxScan = meshVolImage.domain().upperBound()[1]- meshVolImage.domain().lowerBound()[1];
302 centerY = centerY + (opp? maxScan/2: -maxScan/2);
304 if(vm.count(
"orientAutoFrontZ"))
307 maxScan = meshVolImage.domain().upperBound()[2]-meshVolImage.domain().lowerBound()[2];
308 centerZ = centerZ + (opp? maxScan/2: -maxScan/2);
311 functors::Rescaling<unsigned int, Image2D::Value> scaleFctDepth(0, maxScan, 0, 255);
312 if(maxScan > std::numeric_limits<Image2D::Value>::max())
314 trace.info()<<
"Max depth value outside image intensity range: " << maxScan
315 <<
" (use a rescaling functor which implies a loss of precision)" << std::endl;
317 trace.info() <<
"Processing image to output file " << outputFilename;
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);
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);
332 DGtal::functors::Identity idV;
334 unsigned int maxDepthFound = 0;
335 for(
unsigned int k=0; k < maxScan; k++)
337 trace.progressBar(k, maxScan);
338 DGtal::functors::Point2DEmbedderIn3D<DGtal::Z3i::Domain > embedder(meshVolImage.domain(),
339 ptCenter+normalDir*k,
342 ImageAdapterExtractor extractedImage(meshVolImage, aDomain2D, embedder, idV);
343 for(Image2D::Domain::ConstIterator it = extractedImage.domain().begin();
344 it != extractedImage.domain().end(); it++)
346 if(resultingImage(*it)== 0 && extractedImage(*it)!=0)
349 resultingImage.setValue(*it, scaleFctDepth(maxScan-k));
350 resultingVectorField.setValue(*it, getNormal(meshNormalImage(embedder(*it)), embedder));
354 if (vm.count(
"setBackgroundLastDepth"))
356 for(Image2D::Domain::ConstIterator it = resultingImage.domain().begin();
357 it != resultingImage.domain().end(); it++){
358 if( resultingImage(*it)== 0 )
360 resultingImage.setValue(*it, scaleFctDepth(maxScan-maxDepthFound));
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))
369 resultingVectorField.setValue(*it,Z3i::RealPoint(0, 0, inverBgNormal?-1: 1));
372 resultingImage >> outputFilename;
373 if(vm.count(
"exportNormals")){
374 std::stringstream ss;
375 ss << outputFilename <<
".normals";
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];