Developer Documentation

decimater.cc
1 /* ========================================================================= *
2 * *
3 * OpenMesh *
4 * Copyright (c) 2001-2015, RWTH-Aachen University *
5 * Department of Computer Graphics and Multimedia *
6 * All rights reserved. *
7 * www.openmesh.org *
8 * *
9 *---------------------------------------------------------------------------*
10 * This file is part of OpenMesh. *
11 *---------------------------------------------------------------------------*
12 * *
13 * Redistribution and use in source and binary forms, with or without *
14 * modification, are permitted provided that the following conditions *
15 * are met: *
16 * *
17 * 1. Redistributions of source code must retain the above copyright notice, *
18 * this list of conditions and the following disclaimer. *
19 * *
20 * 2. Redistributions in binary form must reproduce the above copyright *
21 * notice, this list of conditions and the following disclaimer in the *
22 * documentation and/or other materials provided with the distribution. *
23 * *
24 * 3. Neither the name of the copyright holder nor the names of its *
25 * contributors may be used to endorse or promote products derived from *
26 * this software without specific prior written permission. *
27 * *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 * ========================================================================= */
41
42
43
44 #if !defined(OM_USE_OSG)
45 # define OM_USE_OSG 0
46 #endif
47
48 // ----------------------------------------------------------------------------
49
50 #include <iostream>
51 #include <fstream>
52 #include <sstream>
53 #include <string>
54 #include <memory>
55 #include <map>
56 //--------------------
57 #include <OpenMesh/Core/IO/MeshIO.hh>
58 //--------------------
59 #if OM_USE_OSG
60 # include <OpenMesh/Tools/Kernel_OSG/TriMesh_OSGArrayKernelT.hh>
61 #else
62 # include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
63 #endif
64 #include <OpenMesh/Core/Utils/vector_cast.hh>
65 //--------------------
66 #include <OpenMesh/Tools/Utils/getopt.h>
67 #include <OpenMesh/Tools/Utils/Timer.hh>
68 #include <OpenMesh/Tools/Decimater/DecimaterT.hh>
69 #include <OpenMesh/Tools/Decimater/ModAspectRatioT.hh>
70 #include <OpenMesh/Tools/Decimater/ModEdgeLengthT.hh>
71 #include <OpenMesh/Tools/Decimater/ModHausdorffT.hh>
72 #include <OpenMesh/Tools/Decimater/ModNormalDeviationT.hh>
73 #include <OpenMesh/Tools/Decimater/ModNormalFlippingT.hh>
74 #include <OpenMesh/Tools/Decimater/ModQuadricT.hh>
75 #include <OpenMesh/Tools/Decimater/ModProgMeshT.hh>
76 #include <OpenMesh/Tools/Decimater/ModIndependentSetsT.hh>
77 #include <OpenMesh/Tools/Decimater/ModRoundnessT.hh>
78
79 //----------------------------------------------------------------- traits ----
80
81 #if OM_USE_OSG
82 typedef OpenMesh::Kernel_OSG::Traits MyTraits;
83 #else
84 typedef OpenMesh::DefaultTraits MyTraits;
85 #endif
86
87 //------------------------------------------------------------------- mesh ----
88
89 #if OM_USE_OSG
90 typedef OpenMesh::Kernel_OSG::TriMesh_OSGArrayKernelT<MyTraits> ArrayTriMesh;
91 #else
92 typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits> ArrayTriMesh;
93 #endif
94
95
96 //-------------------------------------------------------------- decimator ----
97
98 typedef OpenMesh::Decimater::DecimaterT<ArrayTriMesh> Decimater;
99
100
101 //---------------------------------------------------------------- globals ----
102
103 int gverbose = 0;
104 int gdebug = 0;
105
106
107 //--------------------------------------------------------------- forwards ----
108
109 void usage_and_exit(int xcode);
110
111
112 //--------------------------------------------------- decimater arguments ----
113
114 #include "CmdOption.hh"
115
116
117 struct DecOptions
118 {
119 DecOptions()
120 : n_collapses(0)
121 { }
122
123 CmdOption<bool> decorate_name;
124 CmdOption<float> n_collapses;
125
126 CmdOption<float> AR; // Aspect ratio
127 CmdOption<float> EL; // Edge length
128 CmdOption<float> HD; // Hausdorff distance
129 CmdOption<bool> IS; // Independent Sets
130 CmdOption<float> ND; // Normal deviation
131 CmdOption<float> NF; // Normal flipping
132 CmdOption<std::string> PM; // Progressive Mesh
133 CmdOption<float> Q; // Quadrics
134 CmdOption<float> R; // Roundness
135
136 template <typename T>
137 bool init( CmdOption<T>& _o, const std::string& _val )
138 {
139 if ( _val.empty() )
140 _o.enable();
141 else
142 {
143 std::istringstream istr( _val );
144
145 T v;
146
147 if ( (istr >> v).fail() )
148 return false;
149
150 _o = v;
151 }
152 return true;
153 }
154
155
156 bool parse_argument( const std::string& arg )
157 {
158 std::string::size_type pos = arg.find(':');
159
160 std::string name;
161 std::string value;
162
163 if (pos == std::string::npos)
164 name = arg;
165 else
166 {
167 name = arg.substr(0, pos);
168 value = arg.substr(pos+1, arg.size());
169 }
170 strip(name);
171 strip(value);
172
173 if (name == "AR") return init(AR, value);
174 if (name == "EL") return init(EL, value);
175 if (name == "HD") return init(HD, value);
176 if (name == "IS") return init(IS, value);
177 if (name == "ND") return init(ND, value);
178 if (name == "NF") return init(NF, value);
179 if (name == "PM") return init(PM, value);
180 if (name == "Q") return init(Q, value);
181 if (name == "R") return init(R, value);
182 return false;
183 }
184
185 std::string& strip(std::string & line)
186 {
187 std::string::size_type pos = 0;
188
189 pos = line.find_last_not_of(" \t");
190
191 if ( pos!=0 && pos!=std::string::npos )
192 {
193 ++pos;
194 line.erase( pos, line.length()-pos );
195 }
196
197 pos = line.find_first_not_of(" \t");
198 if ( pos!=0 && pos!=std::string::npos )
199 {
200 line.erase(0,pos);
201 }
202
203 return line;
204 }
205
206 };
207
208 //----------------------------------------------------- decimater wrapper ----
209 //
210 template <typename Mesh, typename DecimaterType>
211 bool
212 decimate(const std::string &_ifname,
213 const std::string &_ofname,
214 DecOptions &_opt)
215 {
216 using namespace std;
217
218 Mesh mesh;
219 OpenMesh::IO::Options readopt;
220 OpenMesh::Utils::Timer timer;
221
222 // ---------------------------------------- read source mesh
223 {
224 if (gverbose)
225 clog << "source mesh: ";
226 bool rc;
227
228 if (gverbose)
229 clog << _ifname << endl;
230 if ( !(rc = OpenMesh::IO::read_mesh(mesh, _ifname, readopt)) )
231 {
232 cerr << " ERROR: read failed!" << endl;
233 return rc;
234 }
235 }
236
237 // ---------------------------------------- do some decimation
238 {
239 // ---- 0 - For module NormalFlipping one needs face normals
240
241 if ( !readopt.check( OpenMesh::IO::Options::FaceNormal ) )
242 {
243 if ( !mesh.has_face_normals() )
244 mesh.request_face_normals();
245
246 if (gverbose)
247 clog << " updating face normals" << endl;
248 mesh.update_face_normals();
249 }
250
251 // ---- 1 - create decimater instance
252 DecimaterType decimater( mesh );
253
254 // ---- 2 - register modules
255 if (gverbose)
256 clog << " register modules" << endl;
257
258
259
260 typename OpenMesh::Decimater::ModAspectRatioT<Mesh>::Handle modAR;
261
262 if (_opt.AR.is_enabled())
263 {
264 decimater.add(modAR);
265 if (_opt.AR.has_value())
266 decimater.module( modAR ).set_aspect_ratio( _opt.AR ) ;
267 }
268
269 typename OpenMesh::Decimater::ModEdgeLengthT<Mesh>::Handle modEL;
270
271 if (_opt.EL.is_enabled())
272 {
273 decimater.add(modEL);
274 if (_opt.EL.has_value())
275 decimater.module( modEL ).set_edge_length( _opt.EL ) ;
276 decimater.module(modEL).set_binary(false);
277 }
278
279 typename OpenMesh::Decimater::ModHausdorffT <Mesh>::Handle modHD;
280
281 if (_opt.HD.is_enabled())
282 {
283 decimater.add(modHD);
284 if (_opt.HD.has_value())
285 decimater.module( modHD ).set_tolerance( _opt.HD ) ;
286
287 }
288
289 typename OpenMesh::Decimater::ModIndependentSetsT<Mesh>::Handle modIS;
290
291 if ( _opt.IS.is_enabled() )
292 decimater.add(modIS);
293
294 typename OpenMesh::Decimater::ModNormalDeviationT<Mesh>::Handle modND;
295
296 if (_opt.ND.is_enabled())
297 {
298 decimater.add(modND);
299 if (_opt.ND.has_value())
300 decimater.module( modND ).set_normal_deviation( _opt.ND );
301 decimater.module( modND ).set_binary(false);
302 }
303
304 typename OpenMesh::Decimater::ModNormalFlippingT<Mesh>::Handle modNF;
305
306 if (_opt.NF.is_enabled())
307 {
308 decimater.add(modNF);
309 if (_opt.NF.has_value())
310 decimater.module( modNF ).set_max_normal_deviation( _opt.NF );
311 }
312
313
314 typename OpenMesh::Decimater::ModProgMeshT<Mesh>::Handle modPM;
315
316 if ( _opt.PM.is_enabled() )
317 decimater.add(modPM);
318
319 typename OpenMesh::Decimater::ModQuadricT<Mesh>::Handle modQ;
320
321 if (_opt.Q.is_enabled())
322 {
323 decimater.add(modQ);
324 if (_opt.Q.has_value())
325 decimater.module( modQ ).set_max_err( _opt.Q );
326 decimater.module(modQ).set_binary(false);
327 }
328
329 typename OpenMesh::Decimater::ModRoundnessT<Mesh>::Handle modR;
330
331 if ( _opt.R.is_enabled() )
332 {
333 decimater.add( modR );
334 if ( _opt.R.has_value() )
335 decimater.module( modR ).set_min_angle( _opt.R,
336 !modQ.is_valid() ||
337 !decimater.module(modQ).is_binary());
338 }
339
340 // ---- 3 - initialize decimater
341
342 if (gverbose)
343 clog << "initializing mesh" << endl;
344
345 {
346 bool rc;
347 timer.start();
348 rc = decimater.initialize();
349 timer.stop();
350 if (!rc)
351 {
352 std::cerr << " initializing failed!" << std::endl;
353 std::cerr << " maybe no priority module or more than one were defined!" << std::endl;
354 return false;
355 }
356 }
357 if (gverbose)
358 std::clog << " Elapsed time: " << timer.as_string() << std::endl;
359
360 if (gverbose)
361 decimater.info( clog );
362
363 // ---- 4 - do it
364
365 if (gverbose)
366 {
367 std::clog << "decimating" << std::endl;
368 std::clog << " # vertices: " << mesh.n_vertices() << std::endl;
369 }
370
371 float nv_before = float(mesh.n_vertices());
372
373 timer.start();
374 size_t rc = 0;
375 if (_opt.n_collapses < 0.0)
376 rc = decimater.decimate_to( size_t(-_opt.n_collapses) );
377 else if (_opt.n_collapses >= 1.0 || _opt.n_collapses == 0.0)
378 rc = decimater.decimate( size_t(_opt.n_collapses) );
379 else if (_opt.n_collapses > 0.0f)
380 rc = decimater.decimate_to(size_t(mesh.n_vertices()*_opt.n_collapses));
381 timer.stop();
382
383 // ---- 5 - write progmesh file for progviewer (before garbage collection!)
384
385 if ( _opt.PM.has_value() )
386 decimater.module(modPM).write( _opt.PM );
387
388 // ---- 6 - throw away all tagged edges
389
390 mesh.garbage_collection();
391
392 if (gverbose)
393 {
394 std::clog << " # executed collapses: " << rc << std::endl;
395 std::clog << " # vertices: " << mesh.n_vertices() << ", "
396 << ( 100.0*mesh.n_vertices()/nv_before ) << "%\n";
397 std::clog << " Elapsed time: " << timer.as_string() << std::endl;
398 std::clog << " collapses/s : " << rc/timer.seconds() << std::endl;
399 }
400
401 }
402
403 // write resulting mesh
404 if ( ! _ofname.empty() )
405 {
406 std::string ofname(_ofname);
407
408 std::string::size_type pos = ofname.rfind('.');
409 if (pos == std::string::npos)
410 {
411 ofname += ".off";
412 pos = ofname.rfind('.');
413 }
414
415 if ( _opt.decorate_name.is_enabled() )
416 {
417 std::stringstream s; s << mesh.n_vertices();
418 std::string n; s >> n;
419 ofname.insert( pos, "-");
420 ofname.insert(++pos, n );
421 }
422
423 OpenMesh::IO::Options writeopt;
424
425 //opt += OpenMesh::IO::Options::Binary;
426
427 if ( !OpenMesh::IO::write_mesh(mesh, ofname, writeopt ) )
428 {
429 std::cerr << " Cannot write decimated mesh to file '"
430 << ofname << "'\n";
431 return false;
432 }
433 std::clog << " Exported decimated mesh to file '" << ofname << "'\n";
434 }
435
436 return true;
437 }
438
439 //------------------------------------------------------------------ main -----
440
441 int main(int argc, char* argv[])
442 {
443 std::string ifname, ofname;
444
445 DecOptions opt;
446
447 //
448 #if OM_USE_OSG
449 osg::osgInit( argc, argv );
450 #endif
451
452 //---------------------------------------- parse command line
453 {
454 int c;
455
456 while ( (c=getopt( argc, argv, "dDhi:M:n:o:v")) != -1 )
457 {
458 switch (c)
459 {
460 case 'D': opt.decorate_name = true; break;
461 case 'd': gdebug = true; break;
462 case 'h': usage_and_exit(0); break;
463 case 'i': ifname = optarg; break;
464 case 'M': opt.parse_argument( optarg ); break;
465 case 'n': opt.n_collapses = float(atof(optarg)); break;
466 case 'o': ofname = optarg; break;
467 case 'v': gverbose = true; break;
468 case '?':
469 default:
470 std::cerr << "FATAL: cannot process command line option!"
471 << std::endl;
472 exit(-1);
473 }
474 }
475 }
476
477 //----------------------------------------
478
479 if ( (-1.0f < opt.n_collapses) && (opt.n_collapses < 0.0f) )
480 {
481 std::cerr << "Error: Option -n: invalid value argument!" << std::endl;
482 usage_and_exit(2);
483 }
484
485 //----------------------------------------
486
487 if (gverbose)
488 {
489 std::clog << " Input file: " << ifname << std::endl;
490 std::clog << " Output file: " << ofname << std::endl;
491 std::clog << " #collapses: " << opt.n_collapses << std::endl;
492 }
493
494
495 //----------------------------------------
496
497
498
499 if (gverbose)
500 {
501 std::clog << "Begin decimation" << std::endl;
502 }
503
504 bool rc = decimate<ArrayTriMesh, Decimater>( ifname, ofname, opt );
505
506 if (gverbose)
507 {
508 if (!rc)
509 std::clog << "Decimation failed!" << std::endl;
510 else
511 std::clog << "Decimation done." << std::endl;
512 }
513
514 //----------------------------------------
515 return 0;
516 }
517
518
519 //-----------------------------------------------------------------------------
520
521 void usage_and_exit(int xcode)
522 {
523 std::string errmsg;
524
525 switch(xcode)
526 {
527 case 1: errmsg = "Option not supported!"; break;
528 case 2: errmsg = "Invalid output file format!"; break;
529 }
530
531 std::cerr << std::endl;
532 if (xcode) {
533 std::cerr << "Error " << xcode << ": " << errmsg << std::endl << std::endl;
534 }
535 std::cerr << "Usage: decimator [Options] -i input-file -o output-file\n"
536 << " Decimating a mesh using quadrics and normal flipping.\n" << std::endl;
537 std::cerr << "Options\n" << std::endl;
538 std::cerr << " -M \"{Module-Name}[:Value]}\"\n"
539 << " Use named module with eventually given parameterization\n"
540 << " Several modules can also be used in order to introduce further constraints\n"
541 << " Note that -M has to be given before each new module \n"
542 << " An example with ModQuadric as a priority module\n"
543 << " and ModRoundness as a binary module could look like this:\n"
544 << " commandlineDecimater -M Q -M R:40.0 -n 0.1 -i inputfile.obj -o outputfile.obj\n" << std::endl;
545 std::cerr << " -n <N>\n"
546 << " N >= 1: do N halfedge collapses.\n"
547 << " N <=-1: decimate down to |N| vertices.\n"
548 << " 0 < N < 1: decimate down to N%.\n" << std::endl;
549 std::cerr << std::endl;
550 std::cerr << "Modules:\n\n";
551 std::cerr << " AR[:ratio] - ModAspectRatio\n";
552 std::cerr << " EL[:legth] - ModEdgeLength*\n";
553 std::cerr << " HD[:distance] - ModHausdorff\n";
554 std::cerr << " IS - ModIndependentSets\n";
555 std::cerr << " ND[:angle] - ModNormalDeviation*\n";
556 std::cerr << " NF[:angle] - ModNormalFlipping\n";
557 std::cerr << " PM[:file name] - ModProgMesh\n";
558 std::cerr << " Q[:error] - ModQuadric*\n";
559 std::cerr << " R[:angle] - ModRoundness\n";
560 std::cerr << " 0 < angle < 60\n";
561 std::cerr << " *: priority module. Decimater needs one of them (not more).\n";
562
563 exit( xcode );
564 }
565
566
567
568 // end of file
569 //=============================================================================
OpenMesh::Decimater::ModProgMeshT
Definition: ModProgMeshT.hh:80
DecimaterT.hh
OpenMesh::Decimater::ModAspectRatioT::set_aspect_ratio
void set_aspect_ratio(float _f)
set aspect ratio
Definition: ModAspectRatioT.hh:109
OpenMesh::IO::write_mesh
bool write_mesh(const Mesh &_mesh, const std::string &_filename, Options _opt=Options::Default, std::streamsize _precision=6)
Write a mesh to the file _filename.
Definition: MeshIO.hh:207
ModHausdorffT.hh
ModNormalDeviationT.hh
OpenMesh::TriMesh_ArrayKernelT
Definition: PolyMesh_ArrayKernelT.hh:73
std
STL namespace.
OpenMesh::Decimater::ModNormalDeviationT
Use Normal deviation to control decimation.
Definition: ModNormalDeviationT.hh:88
OpenMesh::Decimater::ModNormalDeviationT::set_normal_deviation
void set_normal_deviation(Scalar _s)
Set normal deviation ( 0 .. 360 )
Definition: ModNormalDeviationT.hh:137
OpenMesh::Utils::Timer::as_string
std::string as_string(Format format=Automatic)
OpenMesh::Utils::Timer::seconds
double seconds(void) const
Returns measured time in seconds, if the timer is in state &#39;Stopped&#39;.
CmdOption< bool >
OpenMesh::Kernel_OSG::TriMesh_OSGArrayKernelT
Definition: TriMesh_OSGArrayKernelT.hh:102
OpenMesh::Decimater::ModIndependentSetsT
Definition: ModIndependentSetsT.hh:69
ModRoundnessT.hh
OpenMesh::Kernel_OSG::Traits
Definition: Traits.hh:84
ModProgMeshT.hh
OpenMesh::Utils::Timer::stop
void stop(void)
Stop measurement.
ModAspectRatioT.hh
OpenMesh::IO::read_mesh
bool read_mesh(Mesh &_mesh, const std::string &_filename)
Read a mesh from file _filename.
Definition: MeshIO.hh:112
OpenMesh::Decimater::ModQuadricT::set_max_err
void set_max_err(double _err, bool _binary=true)
Definition: ModQuadricT.hh:153
OpenMesh::Decimater::ModNormalFlippingT::set_max_normal_deviation
void set_max_normal_deviation(double _d)
Definition: ModNormalFlippingT.hh:179
DecOptions
Definition: decimater.cc:117
OpenMesh::Decimater::ModQuadricT
Mesh decimation module computing collapse priority based on error quadrics.
Definition: ModQuadricT.hh:75
Timer.hh
OpenMesh::Decimater::ModRoundnessT
Use Roundness of triangles to control decimation.
Definition: ModRoundnessT.hh:86
ModEdgeLengthT.hh
OpenMesh::IO::Options
Set options for reader/writer modules.
Definition: Options.hh:90
OpenMesh::Decimater::ModBaseT::is_binary
bool is_binary(void) const
Returns true if criteria returns a binary value.
Definition: ModBaseT.hh:220
MyTraits
Definition: mymesh.cc:5
OpenMesh::PolyMeshT::update_face_normals
void update_face_normals()
Update normal vectors for all faces.
Definition: PolyMeshT_impl.hh:335
ModNormalFlippingT.hh
OpenMesh::Decimater::DecimaterT
Definition: DecimaterT.hh:78
OpenMesh::Decimater::ModEdgeLengthT
Use edge length to control decimation.
Definition: ModEdgeLengthT.hh:75
OpenMesh::IO::Options::FaceNormal
Has (r) / store (w) face normals.
Definition: Options.hh:108
OpenMesh::Decimater::ModEdgeLengthT::set_edge_length
void set_edge_length(typename Mesh::Scalar _f)
set edge_length
Definition: ModEdgeLengthT.hh:91
OpenMesh::Utils::Timer::start
void start(void)
Start measurement.
OpenMesh::Utils::Timer
Definition: Timer.hh:82
OpenMesh::DefaultTraits
Definition: Traits.hh:121
OpenMesh::Decimater::ModAspectRatioT
Use aspect ratio to control decimation.
Definition: ModAspectRatioT.hh:81
OpenMesh::Decimater::ModNormalFlippingT
Definition: ModNormalFlippingT.hh:80
ModQuadricT.hh