DGtalTools 2.0.0
Loading...
Searching...
No Matches
generic3dNormalEstimators.cpp
1
32#include <iostream>
33#include <fstream>
34#include <sstream>
35
36#include "CLI11.hpp"
37
38#include "DGtal/base/Common.h"
39#include "DGtal/base/CountedPtr.h"
40#include "DGtal/base/CountedConstPtrOrConstPtr.h"
41#include "DGtal/helpers/StdDefs.h"
42#include "DGtal/math/Statistic.h"
43#include "DGtal/math/MPolynomial.h"
44#include "DGtal/topology/LightImplicitDigitalSurface.h"
45#include "DGtal/graph/DepthFirstVisitor.h"
46#include "DGtal/graph/GraphVisitorRange.h"
47#include "DGtal/geometry/surfaces/estimation/VoronoiCovarianceMeasureOnDigitalSurface.h"
48#include "DGtal/geometry/surfaces/estimation/VCMDigitalSurfaceLocalEstimator.h"
49#include "DGtal/geometry/surfaces/estimation/TrueDigitalSurfaceLocalEstimator.h"
50#include "DGtal/geometry/surfaces/estimation/IIGeometricFunctors.h"
51#include "DGtal/geometry/surfaces/estimation/IntegralInvariantCovarianceEstimator.h"
52#include "DGtal/geometry/volumes/KanungoNoise.h"
53#include "DGtal/shapes/GaussDigitizer.h"
54#include "DGtal/shapes/ShapeGeometricFunctors.h"
55#include "DGtal/shapes/implicit/ImplicitPolynomial3Shape.h"
56#include "DGtal/images/ImageContainerBySTLVector.h"
57#include "DGtal/images/SimpleThresholdForegroundPredicate.h"
58#include "DGtal/io/readers/MPolynomialReader.h"
59#include "DGtal/io/colormaps/GradientColorMap.h"
60#ifdef DGTAL_WITH_POLYSCOPE
61#include "DGtal/io/viewers/PolyscopeViewer.h"
62#endif
63
64using namespace std;
65using namespace DGtal;
66
67
68
198struct AllParams{
199 double noise {0.0};
200 double minAABB {-10.0};
201 double maxAABB {10.0};
202 double gridstep {1.0};
203 double R_radius {5.0};
204 double r_radius {3.0};
205 double alpha {0.0};
206 double trivial_radius {3.0};
207 int embedding {0};
208 std::string polynomials;
209 std::string estimator {"True"};
210 std::string kernel {"hat"};
211 std::string output {"output"};
212 std::string exportX {"None"};
213 std::string view {"None"};
214 bool angle_deviation_stats;
215 bool normals;
216 bool noff;
217};
218
219
220
221template <typename SCell, typename RealVector>
222struct GradientMapAdapter {
223 typedef std::map<SCell,RealVector> SCell2RealVectorMap;
224 typedef SCell Argument;
225 typedef RealVector Value;
226 GradientMapAdapter( ConstAlias<SCell2RealVectorMap> map )
227 : myMap( map ) {}
228 RealVector operator()( const Argument& arg ) const
229 {
230 typename SCell2RealVectorMap::const_iterator it = myMap->find( arg );
231 if ( it != myMap->end() ) return it->second;
232 else return RealVector();
233 }
234 CountedConstPtrOrConstPtr<SCell2RealVectorMap> myMap;
235};
236
237template <typename SCellEmbedder>
238struct SCellEmbedderWithNormal : public SCellEmbedder
239{
240 using SCellEmbedder::space;
241 using SCellEmbedder::operator();
242 typedef typename SCellEmbedder::KSpace KSpace;
243 typedef typename SCellEmbedder::SCell SCell;
244 typedef typename SCellEmbedder::RealPoint RealPoint;
245 typedef SCell Argument;
246 typedef RealPoint Value;
247 typedef typename KSpace::Space::RealVector RealVector;
248 typedef std::map<SCell,RealVector> SCell2RealVectorMap;
249 typedef GradientMapAdapter<SCell,RealVector> GradientMap;
250
251 SCellEmbedderWithNormal( ConstAlias<SCellEmbedder> embedder,
252 ConstAlias<SCell2RealVectorMap> map )
253 : SCellEmbedder( embedder ), myMap( map )
254 {}
255
256 GradientMap gradientMap() const
257 {
258 return GradientMap( myMap );
259 }
260
261 CountedConstPtrOrConstPtr<SCell2RealVectorMap> myMap;
262};
263
264template <typename DigitalSurface,
265 typename Estimator>
266void exportNOFFSurface( const DigitalSurface& surface,
267 const Estimator& estimator,
268 std::ostream& output )
269{
270 typedef typename DigitalSurface::KSpace KSpace;
271 typedef typename DigitalSurface::ConstIterator ConstIterator;
272 typedef typename DigitalSurface::Surfel Surfel;
273 typedef typename Estimator::Quantity Quantity;
274 const KSpace& ks = surface.container().space();
275 std::map<Surfel,Quantity> normals;
276 for ( ConstIterator it = surface.begin(), itE = surface.end(); it != itE; ++it )
277 {
278 Quantity n_est = estimator.eval( it );
279 normals[ *it ] = n_est;
280 }
281 CanonicSCellEmbedder<KSpace> surfelEmbedder( ks );
282 typedef SCellEmbedderWithNormal< CanonicSCellEmbedder<KSpace> > Embedder;
283 Embedder embedder( surfelEmbedder, normals );
284 surface.exportAs3DNOFF( output, embedder );
285}
286
287
291template <typename KSpace,
292 typename ImplicitShape,
293 typename Surface,
294 typename TrueEstimator,
295 typename Estimator>
296void computeEstimation
297( const AllParams &params, //< command-line parameters
298 const KSpace& K, //< cellular grid space
299 const ImplicitShape& shape, //< implicit shape "ground truth"
300 const Surface& surface, //< digital surface approximating shape
301 TrueEstimator& true_estimator, //< "ground truth" estimator
302 Estimator& estimator ) //< an initialized estimator
303{
304 typedef typename Surface::Surfel Surfel;
305 typedef typename Estimator::Quantity Quantity;
306 typedef double Scalar;
307 typedef DepthFirstVisitor< Surface > Visitor;
308 typedef GraphVisitorRange< Visitor > VisitorRange;
309 typedef typename VisitorRange::ConstIterator VisitorConstIterator;
310
311 std::string fname = params.output;
312 string nameEstimator = params.estimator;
313 trace.beginBlock( "Computing " + nameEstimator + "estimations." );
314 CountedPtr<VisitorRange> range( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
315 std::vector<Quantity> n_estimations;
316 estimator.eval( range->begin(), range->end(), std::back_inserter( n_estimations ) );
317 trace.info() << "- nb estimations = " << n_estimations.size() << std::endl;
318 trace.endBlock();
319
320 trace.beginBlock( "Computing ground truth." );
321 range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
322 std::vector<Quantity> n_true_estimations;
323 true_estimator.eval( range->begin(), range->end(), std::back_inserter( n_true_estimations ) );
324 trace.info() << "- nb estimations = " << n_true_estimations.size() << std::endl;
325 trace.endBlock();
326
327 trace.beginBlock( "Correcting orientations." );
328 ASSERT( n_estimations.size() == n_true_estimations.size() );
329 for ( unsigned int i = 0; i < n_estimations.size(); ++i )
330 if ( n_estimations[ i ].dot( n_true_estimations[ i ] ) < 0 )
331 n_estimations[ i ] = -n_estimations[ i ];
332 trace.endBlock();
333
334 DGtal::GradientColorMap<double> grad( 0.0, 40.0 );
335 // 0 metallic blue, 5 light cyan, 10 light green, 15 light
336 // yellow, 20 yellow, 25 orange, 30 red, 35, dark red, 40- grey
337 grad.addColor( DGtal::Color( 128, 128, 255 ) ); // 0
338 grad.addColor( DGtal::Color( 128, 255, 255 ) ); // 5
339 grad.addColor( DGtal::Color( 128, 255, 128 ) ); // 10
340 grad.addColor( DGtal::Color( 255, 255, 128 ) ); // 15
341 grad.addColor( DGtal::Color( 255, 255, 0 ) ); // 20
342 grad.addColor( DGtal::Color( 255, 128, 0 ) ); // 25
343 grad.addColor( DGtal::Color( 255, 0, 0 ) ); // 30
344 grad.addColor( DGtal::Color( 128, 0, 0 ) ); // 35
345 grad.addColor( DGtal::Color( 128, 128, 128 ) ); // 40
346
347 if ( params.angle_deviation_stats )
348 {
349 trace.beginBlock( "Computing angle deviation error stats." );
350 std::ostringstream adev_sstr;
351 adev_sstr << fname << "-" << nameEstimator << "-angle-deviation-"
352 << estimator.h() << ".txt";
353 DGtal::Statistic<Scalar> adev_stat;
354 unsigned int i = 0;
355 range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
356 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
357 {
358 Quantity n_est = n_estimations[ i ];
359 Quantity n_true_est = n_true_estimations[ i ];
360 Scalar angle_error = acos( n_est.dot( n_true_est ) );
361 adev_stat.addValue( angle_error );
362 }
363 adev_stat.terminate();
364 std::ofstream adev_output( adev_sstr.str().c_str() );
365 adev_output << "# Average error X of the absolute angle between two vector estimations." << std::endl;
366 adev_output << "# h L1 L2 Loo E[X] Var[X] Min[X] Max[X] Nb[X]" << std::endl;
367 adev_output << estimator.h()
368 << " " << adev_stat.mean() // L1
369 << " " << sqrt( adev_stat.unbiasedVariance()
370 + adev_stat.mean()*adev_stat.mean() ) // L2
371 << " " << adev_stat.max() // Loo
372 << " " << adev_stat.mean() // E[X] (=L1)
373 << " " << adev_stat.unbiasedVariance() // Var[X]
374 << " " << adev_stat.min() // Min[X]
375 << " " << adev_stat.max() // Max[X]
376 << " " << adev_stat.samples() // Nb[X]
377 << std::endl;
378 adev_output.close();
379 trace.endBlock();
380 }
381 if ( params.exportX != "None" )
382 {
383 trace.beginBlock( "Exporting cell geometry." );
384 std::ostringstream export_sstr;
385 export_sstr << fname << "-" << nameEstimator << "-cells-"
386 << estimator.h() << ".txt";
387 std::ofstream export_output( export_sstr.str().c_str() );
388 export_output << "# ImaGene viewer (viewSetOfSurfels) file format for displaying cells." << std::endl;
389 bool adev = params.exportX == "AngleDeviation";
390 unsigned int i = 0;
391 range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
392 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
393 {
394 Quantity n_est = n_estimations[ i ];
395 Quantity n_true_est = n_true_estimations[ i ];
396 Scalar angle_error = acos( n_est.dot( n_true_est ) )*180.0 / 3.14159625;
397 Surfel s = *it;
398 export_output
399 << "CellN"
400 << " " << min( 1023, max( 512+K.sKCoord( s, 0 ), 0 ) )
401 << " " << min( 1023, max( 512+K.sKCoord( s, 1 ), 0 ) )
402 << " " << min( 1023, max( 512+K.sKCoord( s, 2 ), 0 ) )
403 << " " << K.sSign( s );
404 Color c = grad( 0 );
405 if ( adev ) c = grad( max( 0.0, min( angle_error, 40.0 ) ) );
406 export_output << " " << ((double) c.red() / 255.0 )
407 << " " << ((double) c.green() / 255.0 )
408 << " " << ((double) c.blue() / 255.0 );
409 export_output << " " << n_est[ 0 ] << " " << n_est[ 1 ]
410 << " " << n_est[ 2 ] << std::endl;
411 }
412 export_output.close();
413 trace.endBlock();
414 }
415 if ( params.normals )
416 {
417 trace.beginBlock( "Exporting cells normals." );
418 std::ostringstream export_sstr;
419 export_sstr << fname << "-" << nameEstimator << "-normals-"
420 << estimator.h() << ".txt";
421 std::ofstream export_output( export_sstr.str().c_str() );
422 export_output << "# kx ky kz sign n_est[0] n_est[1] n_est[2] n_true[0] n_true[1] n_true[2]" << std::endl;
423 unsigned int i = 0;
424 range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
425 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
426 {
427 Quantity n_est = n_estimations[ i ];
428 Quantity n_true_est = n_true_estimations[ i ];
429 Surfel s = *it;
430 export_output
431 << K.sKCoord( s, 0 ) << " " << K.sKCoord( s, 1 ) << " " << K.sKCoord( s, 2 )
432 << " " << K.sSign( s )
433 << " " << n_est[ 0 ] << " " << n_est[ 1 ] << " " << n_est[ 2 ]
434 << " " << n_true_est[ 0 ] << " " << n_true_est[ 1 ] << " " << n_true_est[ 2 ]
435 << std::endl;
436 }
437 export_output.close();
438 trace.endBlock();
439 }
440 if ( params.noff )
441 {
442 trace.beginBlock( "Exporting NOFF file." );
443 std::ostringstream export_sstr;
444 export_sstr << fname << "-" << nameEstimator << "-noff-"
445 << estimator.h() << ".off";
446 std::ofstream export_output( export_sstr.str().c_str() );
447 std::map<Surfel,Quantity> normals;
448 unsigned int i = 0;
449 range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
450 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
451 {
452 Quantity n_est = n_estimations[ i ];
453 normals[ *it ] = n_est;
454 }
455 CanonicSCellEmbedder<KSpace> surfelEmbedder( K );
456 typedef SCellEmbedderWithNormal< CanonicSCellEmbedder<KSpace> > Embedder;
457 Embedder embedder( surfelEmbedder, normals );
458 surface.exportAs3DNOFF( export_output, embedder );
459 export_output.close();
460 trace.endBlock();
461 }
462#ifdef DGTAL_WITH_POLYSCOPE
463 if ( params.exportX != "None" )
464 {
465 typedef typename KSpace::Space Space;
466 typedef PolyscopeViewer<Space,KSpace> MyViewever3D;
467 int argc = 1;
468 char name[] = "Viewer";
469 char* argv[ 1 ];
470 argv[ 0 ] = name;
471 Surfel s;
472 MyViewever3D viewer( K );
473 viewer.drawAsSimplified();
474 viewer.allowReuseList = true;
475
476 trace.beginBlock( "Viewing surface." );
477 bool adev = params.view == "AngleDeviation";
478
479 unsigned int i = 0;
480 range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
481 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
482 {
483 Quantity n_est = n_estimations[ i ];
484 Quantity n_true_est = n_true_estimations[ i ];
485 Scalar angle_error = acos( n_est.dot( n_true_est ) )*180.0 / 3.14159625;
486 s = *it;
487 Color c = grad( 0 );
488 if ( adev ) c = grad( max( 0.0, min( angle_error, 40.0 ) ) );
489 viewer.drawColor( c );
490 viewer << WithQuantity(s, "normal", n_est);
491 }
492 trace.endBlock();
493 viewer.show();
494 }
495#endif
496
497}
498
499template <typename KSpace,
500 typename ImplicitShape,
501 typename Surface,
502 typename KernelFunction,
503 typename PointPredicate>
504void chooseEstimator
505( const AllParams &params, //< command-line parameters
506 const KSpace& K, //< cellular grid space
507 const ImplicitShape& shape, //< implicit shape "ground truth"
508 const Surface& surface, //< digital surface approximating shape
509 const KernelFunction& chi, //< the kernel function
510 const PointPredicate& ptPred ) //< analysed implicit digital shape as a PointPredicate
511{
512 string nameEstimator = params.estimator;
513 double h = params.gridstep;
514 typedef functors::ShapeGeometricFunctors::ShapeNormalVectorFunctor<ImplicitShape> NormalFunctor;
515 typedef TrueDigitalSurfaceLocalEstimator<KSpace, ImplicitShape, NormalFunctor> TrueEstimator;
516 TrueEstimator true_estimator;
517 true_estimator.attach( shape );
518 true_estimator.setParams( K, NormalFunctor(), 20, 0.1, 0.01 );
519 true_estimator.init( h, surface.begin(), surface.end() );
520 if ( nameEstimator == "True" )
521 {
522 trace.beginBlock( "Chosen estimator is: True." );
523 typedef TrueDigitalSurfaceLocalEstimator<KSpace, ImplicitShape, NormalFunctor> Estimator;
524 Estimator estimator;
525 estimator.attach( shape );
526 estimator.setParams( K, NormalFunctor(), 20, 0.1, 0.01 );
527 estimator.init( h, surface.begin(), surface.end() );
528 trace.endBlock();
529 computeEstimation( params, K, shape, surface, true_estimator, estimator );
530 }
531 else if ( nameEstimator == "VCM" )
532 {
533 trace.beginBlock( "Chosen estimator is: VCM." );
534 typedef typename KSpace::Space Space;
535 typedef typename Surface::DigitalSurfaceContainer SurfaceContainer;
536 typedef ExactPredicateLpSeparableMetric<Space,2> Metric;
537 typedef VoronoiCovarianceMeasureOnDigitalSurface<SurfaceContainer,Metric,
538 KernelFunction> VCMOnSurface;
539 typedef functors::VCMNormalVectorFunctor<VCMOnSurface> NormalFunctor;
540 typedef VCMDigitalSurfaceLocalEstimator<SurfaceContainer,Metric,
541 KernelFunction, NormalFunctor> VCMNormalEstimator;
542 int embedding = params.embedding;
543 Surfel2PointEmbedding embType = embedding == 0 ? Pointels :
544 embedding == 1 ? InnerSpel : OuterSpel;
545 double R = params.R_radius;
546 double r = params.r_radius;
547 double t = params.trivial_radius;
548 double alpha = params.alpha;
549 if ( alpha != 0.0 ) R *= pow( h, alpha-1.0 );
550 if ( alpha != 0.0 ) r *= pow( h, alpha-1.0 );
551 trace.info() << "- R=" << R << " r=" << r << " t=" << t << std::endl;
552 VCMNormalEstimator estimator;
553 estimator.attach( surface );
554 estimator.setParams( embType, R, r, chi, t, Metric(), true );
555 estimator.init( h, surface.begin(), surface.end() );
556 trace.endBlock();
557 computeEstimation( params, K, shape, surface, true_estimator, estimator );
558 }
559 else if ( nameEstimator == "II" )
560 {
561 trace.beginBlock( "Chosen estimator is: II." );
562 typedef typename KSpace::Space Space;
563 typedef HyperRectDomain<Space> Domain;
564 typedef ImageContainerBySTLVector< Domain, bool> Image;
565 typedef typename Domain::ConstIterator DomainConstIterator;
566 typedef functors::SimpleThresholdForegroundPredicate<Image> ThresholdedImage;
567 typedef functors::IINormalDirectionFunctor<Space> IINormalFunctor;
568 typedef IntegralInvariantCovarianceEstimator<KSpace, ThresholdedImage, IINormalFunctor> IINormalEstimator;
569 double r = params.r_radius;
570 double alpha = params.alpha;
571 if ( alpha != 0.0 ) r *= pow( h, alpha-1.0 );
572 trace.info() << " r=" << r << std::endl;
573 trace.beginBlock( "Preparing characteristic set." );
574 Domain domain( K.lowerBound(), K.upperBound() );
575 Image image( domain );
576 for ( DomainConstIterator it = domain.begin(), itE = domain.end(); it != itE; ++it )
577 {
578 image.setValue( *it, ptPred( *it ) );
579 }
580 trace.endBlock();
581 trace.beginBlock( "Initialize II estimator." );
582 ThresholdedImage thresholdedImage( image, false );
583 IINormalEstimator ii_estimator( K, thresholdedImage );
584 ii_estimator.setParams( r );
585 ii_estimator.init( h, surface.begin(), surface.end() );
586 trace.endBlock();
587 trace.endBlock();
588 computeEstimation( params, K, shape, surface, true_estimator, ii_estimator );
589 }
590
591}
592
593template <typename KSpace,
594 typename ImplicitShape,
595 typename Surface,
596 typename PointPredicate>
597void chooseKernel
598( const AllParams& params, //< command-line parameters
599 const KSpace& K, //< cellular grid space
600 const ImplicitShape& shape, //< implicit shape "ground truth"
601 const Surface& surface, //< digital surface approximating shape
602 const PointPredicate& ptPred ) //< analysed implicit digital shape as a PointPredicate
603{
604 string kernel = params.kernel;
605 double h = params.gridstep;
606 double r = params.r_radius;
607 double alpha = params.alpha;
608 if ( alpha != 0.0 ) r *= pow( h, alpha-1.0 );
609 if ( kernel == "hat" ) {
610 typedef typename KSpace::Point Point;
611 typedef functors::HatPointFunction<Point,double> KernelFunction;
612 KernelFunction chi_r( 1.0, r );
613 chooseEstimator( params, K, shape, surface, chi_r, ptPred );
614 } else if ( kernel == "ball" ) {
615 typedef typename KSpace::Point Point;
616 typedef functors::BallConstantPointFunction<Point,double> KernelFunction;
617 KernelFunction chi_r( 1.0, r );
618 chooseEstimator( params, K, shape, surface, chi_r, ptPred );
619 }
620}
621
622template <typename KSpace,
623 typename ImplicitShape,
624 typename ImplicitDigitalShape >
625int chooseSurface
626( const AllParams &params, //< command-line parameters
627 const KSpace& K, //< cellular grid space
628 const ImplicitShape& shape, //< implicit shape "ground truth"
629 const ImplicitDigitalShape& dshape ) //< analysed implicit digital shape
630{
631 // Selecting a model of surface depending on noise / not noise.
632 typedef double Scalar;
633 if ( params.noise == 0.0 )
634 { // no noise
635 trace.beginBlock( "Make digital surface..." );
636 typedef LightImplicitDigitalSurface<KSpace,ImplicitDigitalShape> SurfaceContainer;
637 typedef DigitalSurface< SurfaceContainer > Surface;
638 typedef typename Surface::Surfel Surfel;
639 SurfelAdjacency< KSpace::dimension > surfAdj( true );
640 Surfel bel;
641 try {
642 bel = Surfaces<KSpace>::findABel( K, dshape, 10000 );
643 } catch (DGtal::InputException e) {
644 trace.error() << "ERROR Unable to find bel." << std::endl;
645 return 3;
646 }
647 SurfaceContainer* surfaceContainer = new SurfaceContainer( K, dshape, surfAdj, bel );
648 CountedPtr<Surface> ptrSurface( new Surface( surfaceContainer ) ); // acquired
649 trace.info() << "- surface component has " << ptrSurface->size() << " surfels." << std::endl;
650 trace.endBlock();
651 chooseKernel( params, K, shape, *ptrSurface, dshape );
652 }
653 else
654 { // noise
655 trace.beginBlock( "Make digital surface..." );
656 typedef typename ImplicitDigitalShape::Domain Domain;
657 typedef KanungoNoise< ImplicitDigitalShape, Domain > KanungoPredicate;
658 typedef LightImplicitDigitalSurface< KSpace, KanungoPredicate > SurfaceContainer;
659 typedef DigitalSurface< SurfaceContainer > Surface;
660 typedef typename Surface::Surfel Surfel;
661 SurfelAdjacency< KSpace::dimension > surfAdj( true );
662 Surfel bel;
663 const Domain shapeDomain = dshape.getDomain();
664 KanungoPredicate* noisified_dshape = new KanungoPredicate( dshape, shapeDomain, params.noise );
665 // We have to search for a big connected component.
666 CountedPtr<Surface> ptrSurface;
667 double minsize = dshape.getUpperBound()[0] - dshape.getLowerBound()[0];
668 unsigned int nb_surfels = 0;
669 unsigned int tries = 0;
670 do {
671 try { // Search initial bel
672 bel = Surfaces<KSpace>::findABel( K, *noisified_dshape, 10000 );
673 } catch (DGtal::InputException e) {
674 trace.error() << "ERROR Unable to find bel." << std::endl;
675 return 3;
676 }
677 SurfaceContainer* surfaceContainer = new SurfaceContainer( K, *noisified_dshape, surfAdj, bel );
678 ptrSurface = CountedPtr<Surface>( new Surface( surfaceContainer ) ); // acquired
679 nb_surfels = ptrSurface->size();
680 } while ( ( nb_surfels < 2 * minsize ) && ( tries++ < 150 ) );
681 if( tries >= 150 )
682 {
683 trace.error() << "ERROR cannot find a proper bel in a big enough component." << std::endl;
684 return 4;
685 }
686 trace.info() << "- surface component has " << nb_surfels << " surfels." << std::endl;
687 trace.endBlock();
688 chooseKernel( params, K, shape, *ptrSurface, *noisified_dshape );
689 }
690 return 0;
691}
692
693
695int main( int argc, char** argv )
696{
697
698 AllParams allParams;
699
700 // parse command line ----------------------------------------------
701 CLI::App app;
702 std::stringstream ss_descr;
703 ss_descr << "Computes a normal vector field over a digitized 3D implicit surface for several estimators (II|VCM|Trivial|True), specified with -e. You may add Kanungo noise with option -N. These estimators are compared with ground truth. You may then: 1) visualize the normals or the angle deviations with -V (if WITH_QGL_VIEWER is enabled), 2) outputs them as a list of cells/estimations with -n, 3) outputs them as a ImaGene file with -O, 4) outputs them as a NOFF file with -O, 5) computes estimation statistics with option -S.";
704
705 ss_descr << "Example of use:\n"
706 << "./generic3dNormalEstimator -p \"90-3*x^2-2*y^2-z^2\" -o VCM-ellipse -a -10 -A 10 -e VCM -R 3 -r 3 -t 2 -E 0 -x Normals" << endl << endl
707 << "Example of implicit surfaces (specified by -p):" << endl
708 << " - ellipse : 90-3*x^2-2*y^2-z^2 " << endl
709 << " - torus : -1*(x^2+y^2+z^2+6*6-2*2)^2+4*6*6*(x^2+y^2) " << endl
710 << " - rcube : 6561-x^4-y^4-z^4" << endl
711 << " - goursat : 8-0.03*x^4-0.03*y^4-0.03*z^4+2*x^2+2*y^2+2*z^2" << endl
712 << " - distel : 10000-(x^2+y^2+z^2+1000*(x^2+y^2)*(x^2+z^2)*(y^2+z^2))" << endl
713 << " - leopold : 100-(x^2*y^2*z^2+4*x^2+4*y^2+3*z^2)" << endl
714 << " - diabolo : x^2-(y^2+z^2)^2" << endl
715 << " - heart : -1*(x^2+2.25*y^2+z^2-1)^3+x^2*z^3+0.1125*y^2*z^3" << endl
716 << " - crixxi : -0.9*(y^2+z^2-1)^2-(x^2+y^2-1)^3" << endl << endl;
717 ss_descr << "Implemented estimators (specified by -e):" << endl
718 << " - True : supposed to be the ground truth for computations. Of course, it is only approximations." << endl
719 << " - VCM : normal estimator by digital Voronoi Covariance Matrix. Radii parameters are given by -R, -r." << endl
720 << " - II : normal estimator by Integral Invariants. Radius parameter is given by -r." << endl
721 << " - Trivial : the normal obtained by average trivial surfel normals in a ball neighborhood. Radius parameter is given by -r." << endl
722 << endl;
723 ss_descr << "Note:" << endl
724 << " - This is a normal *direction* evaluator more than a normal vector evaluator. Orientations of normals are deduced from ground truth. This is due to the fact that II and VCM only estimates normal directions." << endl
725 << " - This tool only analyses one surface component, and one that contains at least as many surfels as the width of the digital bounding box. This is required when analysing noisy data, where a lot of the small components are spurious. The drawback is that you cannot analyse the normals on a surface with several components." << endl;
726
727 app.description(ss_descr.str());
728
729 app.add_option("--polynomial,-p",allParams.polynomials, "the implicit polynomial whose zero-level defines the shape of interest.") ->required();
730 app.add_option("--noise,-N", allParams.noise,"the Kanungo noise level l=arg, with l^d the probability that a point at distance d is flipped inside/outside." );
731 app.add_option("--minAABB,-a", allParams.minAABB, "the min value of the AABB bounding box (domain)" );
732 app.add_option("--maxAABB,-A", allParams.maxAABB, "the max value of the AABB bounding box (domain)" );
733 app.add_option("--gridstep,-g", allParams.gridstep, "the gridstep that defines the digitization (often called h).");
734 app.add_option("--estimator,-e", allParams.estimator, "the chosen normal estimator: True | VCM | II | Trivial")
735 -> check(CLI::IsMember({"True", "VCM", "II", "Trivial"}));
736 app.add_option("--R-radius,-R", allParams.R_radius, "the constant for parameter R in R(h)=R h^alpha (VCM).");
737 app.add_option("--r-radius,-r", allParams.r_radius, "the constant for parameter r in r(h)=r h^alpha (VCM,II,Trivial).");
738 app.add_option("--kernel,-k", allParams.kernel, "the function chi_r, either hat or ball.");
739 app.add_option("--alpha", allParams.alpha, "the parameter alpha in r(h)=r h^alpha (VCM).");
740 app.add_option("--trivial-radius,-t", allParams.trivial_radius, "the parameter t defining the radius for the Trivial estimator. Also used for reorienting the VCM.");
741 app.add_option("--embedding,-E",allParams.embedding, "the surfel -> point embedding for VCM estimator: 0: Pointels, 1: InnerSpel, 2: OuterSpel.");
742 app.add_option("--output,-o", allParams.output, "the output basename. All generated files will have the form <arg>-*, for instance <arg>-angle-deviation-<gridstep>.txt, <arg>-normals-<gridstep>.txt, <arg>-cells-<gridstep>.txt, <arg>-noff-<gridstep>.off.");
743 app.add_flag("--angle-deviation-stats,-S", allParams.angle_deviation_stats, "computes angle deviation error and outputs them in file <basename>-angle-deviation-<gridstep>.txt, as specified by -o <basename>.");
744
745 app.add_option("--export,-x",allParams.exportX, "exports surfel normals which can be viewed with ImaGene tool 'viewSetOfSurfels' in file <basename>-cells-<gridstep>.txt, as specified by -o <basename>. Parameter <arg> is None|Normals|AngleDeviation. The color depends on the angle deviation in degree: 0 metallic blue, 5 light cyan, 10 light green, 15 light yellow, 20 yellow, 25 orange, 30 red, 35, dark red, 40- grey" );
746 app.add_flag("--normals,-n", allParams.normals, "outputs every surfel, its estimated normal, and the ground truth normal in file <basename>-normals-<gridstep>.txt, as specified by -o <basename>.");
747 app.add_flag("--noff,-O", allParams.noff, "exports the digital surface with normals as NOFF file <basename>-noff-<gridstep>.off, as specified by -o <basename>..");
748#ifdef DGTAL_WITH_VISU3D_QGLVIEWER
749 app.add_option("--view,-V", allParams.view, "view the digital surface with normals. Parameter <arg> is None|Normals|AngleDeviation. The color depends on the angle deviation in degree: 0 metallic blue, 5 light cyan, 10 light green, 15 light yellow, 20 yellow, 25 orange, 30 red, 35, dark red, 40- grey.");
750#endif
751
752
753 app.get_formatter()->column_width(40);
754 CLI11_PARSE(app, argc, argv);
755 // END parse command line using CLI ----------------------------------------------
756
757
758
759 trace.beginBlock( "Make implicit shape..." );
760 typedef Z3i::Space Space;
761 typedef double Scalar;
762 typedef MPolynomial< 3, Scalar > Polynomial3;
763 typedef MPolynomialReader<3, Scalar> Polynomial3Reader;
764 typedef ImplicitPolynomial3Shape<Space> ImplicitShape;
765 Polynomial3 poly;
766 Polynomial3Reader reader;
767 string::const_iterator iter = reader.read( poly, allParams.polynomials.begin(), allParams.polynomials.end() );
768 if ( iter != allParams.polynomials.end() )
769 {
770 trace.error() << "ERROR reading polynomial: I read only <" << allParams.polynomials.substr( 0, iter - allParams.polynomials.begin() )
771 << ">, and I built P=" << poly << std::endl;
772 return 2;
773 }
774 CountedPtr<ImplicitShape> shape( new ImplicitShape( poly ) ); // smart pointer
775 trace.endBlock();
776
777 trace.beginBlock( "Make implicit digital shape..." );
778 typedef Z3i::KSpace KSpace;
779 typedef Space::RealPoint RealPoint;
780 typedef GaussDigitizer< Space, ImplicitShape > ImplicitDigitalShape;
781 typedef ImplicitDigitalShape::Domain Domain;
782 Scalar min_x = allParams.minAABB;
783 Scalar max_x = allParams.maxAABB;
784 Scalar h = allParams.gridstep;
785 RealPoint p1( min_x, min_x, min_x );
786 RealPoint p2( max_x, max_x, max_x );
787 CountedPtr<ImplicitDigitalShape> dshape( new ImplicitDigitalShape() );
788 dshape->attach( *shape );
789 dshape->init( p1, p2, h );
790 Domain domain = dshape->getDomain();
791 KSpace K;
792 K.init( domain.lowerBound(), domain.upperBound(), true );
793 trace.info() << "- domain is " << domain << std::endl;
794 trace.endBlock();
795
796 chooseSurface( allParams, K, *shape, *dshape );
797
798 return 0;
799}
800
Definition ATu0v1.h:57