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