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