35 #include <boost/program_options/options_description.hpp>
36 #include <boost/program_options/parsers.hpp>
37 #include <boost/program_options/variables_map.hpp>
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"
68 using namespace DGtal;
69 namespace po = boost::program_options;
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 )
247 RealVector operator()(
const Argument& arg )
const
249 typename SCell2RealVectorMap::const_iterator it = myMap->find( arg );
250 if ( it != myMap->end() )
return it->second;
251 else return RealVector();
253 CountedConstPtrOrConstPtr<SCell2RealVectorMap> myMap;
256 template <
typename SCellEmbedder>
257 struct SCellEmbedderWithNormal :
public SCellEmbedder
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;
270 SCellEmbedderWithNormal( ConstAlias<SCellEmbedder> embedder,
271 ConstAlias<SCell2RealVectorMap> map )
272 : SCellEmbedder( embedder ), myMap( map )
275 GradientMap gradientMap()
const
277 return GradientMap( myMap );
280 CountedConstPtrOrConstPtr<SCell2RealVectorMap> myMap;
283 template <
typename DigitalSurface,
285 void exportNOFFSurface(
const DigitalSurface& surface,
286 const Estimator& estimator,
287 std::ostream& output )
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 )
297 Quantity n_est = estimator.eval( it );
298 normals[ *it ] = n_est;
300 CanonicSCellEmbedder<KSpace> surfelEmbedder( ks );
301 typedef SCellEmbedderWithNormal< CanonicSCellEmbedder<KSpace> > Embedder;
302 Embedder embedder( surfelEmbedder, normals );
303 surface.exportAs3DNOFF( output, embedder );
310 template <
typename KSpace,
311 typename ImplicitShape,
313 typename TrueEstimator,
315 void computeEstimation
316 (
const po::variables_map& vm,
318 const ImplicitShape& shape,
319 const Surface& surface,
320 TrueEstimator& true_estimator,
321 Estimator& estimator )
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;
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;
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;
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 ];
353 DGtal::GradientColorMap<double> grad( 0.0, 40.0 );
356 grad.addColor( DGtal::Color( 128, 128, 255 ) );
357 grad.addColor( DGtal::Color( 128, 255, 255 ) );
358 grad.addColor( DGtal::Color( 128, 255, 128 ) );
359 grad.addColor( DGtal::Color( 255, 255, 128 ) );
360 grad.addColor( DGtal::Color( 255, 255, 0 ) );
361 grad.addColor( DGtal::Color( 255, 128, 0 ) );
362 grad.addColor( DGtal::Color( 255, 0, 0 ) );
363 grad.addColor( DGtal::Color( 128, 0, 0 ) );
364 grad.addColor( DGtal::Color( 128, 128, 128 ) );
366 if ( vm.count(
"angle-deviation-stats" ) )
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;
374 range = CountedPtr<VisitorRange>(
new VisitorRange(
new Visitor( surface, *(surface.begin()) )) );
375 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
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 );
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()
388 <<
" " << sqrt( adev_stat.unbiasedVariance()
389 + adev_stat.mean()*adev_stat.mean() )
390 <<
" " << adev_stat.max()
391 <<
" " << adev_stat.mean()
392 <<
" " << adev_stat.unbiasedVariance()
393 <<
" " << adev_stat.min()
394 <<
" " << adev_stat.max()
395 <<
" " << adev_stat.samples()
400 if ( vm[
"export" ].as<string>() !=
"None" )
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";
410 range = CountedPtr<VisitorRange>(
new VisitorRange(
new Visitor( surface, *(surface.begin()) )) );
411 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
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;
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 );
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;
431 export_output.close();
434 if ( vm.count( "normals" ) )
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;
443 range = CountedPtr<VisitorRange>(
new VisitorRange(
new Visitor( surface, *(surface.begin()) )) );
444 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
446 Quantity n_est = n_estimations[ i ];
447 Quantity n_true_est = n_true_estimations[ i ];
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 ]
456 export_output.close();
459 if ( vm.count(
"noff" ) )
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;
468 range = CountedPtr<VisitorRange>(
new VisitorRange(
new Visitor( surface, *(surface.begin()) )) );
469 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
471 Quantity n_est = n_estimations[ i ];
472 normals[ *it ] = n_est;
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();
481 #ifdef WITH_VISU3D_QGLVIEWER
482 if ( vm[
"view" ].as<string>() !=
"None" )
484 typedef typename KSpace::Space Space;
485 typedef Viewer3D<Space,KSpace> MyViewever3D;
486 typedef Display3DFactory<Space,KSpace> MyDisplay3DFactory;
488 char name[] =
"Viewer";
492 QApplication application( argc, argv );
493 MyViewever3D viewer( K );
495 viewer << SetMode3D( s.className(),
"Basic" );
496 trace.beginBlock(
"Viewing surface." );
497 bool adev = vm[
"view" ].as<
string>() ==
"AngleDeviation";
500 range = CountedPtr<VisitorRange>(
new VisitorRange(
new Visitor( surface, *(surface.begin()) )) );
501 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
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;
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 );
513 viewer << MyViewever3D::updateDisplay;
520 template <
typename KSpace,
521 typename ImplicitShape,
523 typename KernelFunction,
524 typename PointPredicate>
526 (
const po::variables_map& vm,
528 const ImplicitShape& shape,
529 const Surface& surface,
530 const KernelFunction& chi,
531 const PointPredicate& ptPred )
533 string nameEstimator = vm[
"estimator" ].as<
string>();
534 double h = vm[
"gridstep"].as<
double>();
535 typedef functors::ShapeGeometricFunctors::ShapeNormalVectorFunctor<ImplicitShape> NormalFunctor;
536 typedef TrueDigitalSurfaceLocalEstimator<KSpace, ImplicitShape, NormalFunctor> TrueEstimator;
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" )
543 trace.beginBlock(
"Chosen estimator is: True." );
544 typedef TrueDigitalSurfaceLocalEstimator<KSpace, ImplicitShape, NormalFunctor> Estimator;
546 estimator.attach( shape );
547 estimator.setParams( K, NormalFunctor(), 20, 0.1, 0.01 );
548 estimator.init( h, surface.begin(), surface.end() );
550 computeEstimation( vm, K, shape, surface, true_estimator, estimator );
552 else if ( nameEstimator ==
"VCM" )
554 trace.beginBlock(
"Chosen estimator is: VCM." );
555 typedef typename KSpace::Space Space;
556 typedef typename Surface::DigitalSurfaceContainer SurfaceContainer;
557 typedef ExactPredicateLpSeparableMetric<Space,2> Metric;
558 typedef VoronoiCovarianceMeasureOnDigitalSurface<SurfaceContainer,Metric,
559 KernelFunction> VCMOnSurface;
560 typedef functors::VCMNormalVectorFunctor<VCMOnSurface> NormalFunctor;
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() );
578 computeEstimation( vm, K, shape, surface, true_estimator, estimator );
580 else if ( nameEstimator ==
"II" )
582 trace.beginBlock(
"Chosen estimator is: II." );
583 typedef typename KSpace::Space Space;
584 typedef HyperRectDomain<Space> Domain;
585 typedef ImageContainerBySTLVector< Domain, bool> Image;
586 typedef typename Domain::ConstIterator DomainConstIterator;
587 typedef functors::SimpleThresholdForegroundPredicate<Image> ThresholdedImage;
588 typedef functors::IINormalDirectionFunctor<Space> IINormalFunctor;
589 typedef IntegralInvariantCovarianceEstimator<KSpace, ThresholdedImage, IINormalFunctor> IINormalEstimator;
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 )
599 image.setValue( *it, ptPred( *it ) );
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() );
609 computeEstimation( vm, K, shape, surface, true_estimator, ii_estimator );
614 template <
typename KSpace,
615 typename ImplicitShape,
617 typename PointPredicate>
619 (
const po::variables_map& vm,
621 const ImplicitShape& shape,
622 const Surface& surface,
623 const PointPredicate& ptPred )
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;
637 typedef functors::BallConstantPointFunction<Point,double> KernelFunction;
638 KernelFunction chi_r( 1.0, r );
639 chooseEstimator( vm, K, shape, surface, chi_r, ptPred );
643 template <
typename KSpace,
644 typename ImplicitShape,
645 typename ImplicitDigitalShape >
647 (
const po::variables_map& vm,
649 const ImplicitShape& shape,
650 const ImplicitDigitalShape& dshape )
653 typedef double Scalar;
654 Scalar noiseLevel = vm[
"noise" ].as<
double>();
655 if ( noiseLevel == 0.0 )
657 trace.beginBlock(
"Make digital surface..." );
658 typedef LightImplicitDigitalSurface<KSpace,ImplicitDigitalShape> SurfaceContainer;
659 typedef DigitalSurface< SurfaceContainer > Surface;
660 typedef typename Surface::Surfel Surfel;
661 SurfelAdjacency< KSpace::dimension > surfAdj(
true );
664 bel = Surfaces<KSpace>::findABel( K, dshape, 10000 );
665 }
catch (DGtal::InputException e) {
666 trace.error() <<
"ERROR Unable to find bel." << std::endl;
669 SurfaceContainer* surfaceContainer =
new SurfaceContainer( K, dshape, surfAdj, bel );
670 CountedPtr<Surface> ptrSurface(
new Surface( surfaceContainer ) );
671 trace.info() <<
"- surface component has " << ptrSurface->size() <<
" surfels." << std::endl;
673 chooseKernel( vm, K, shape, *ptrSurface, dshape );
677 trace.beginBlock(
"Make digital surface..." );
678 typedef typename ImplicitDigitalShape::Domain Domain;
679 typedef KanungoNoise< ImplicitDigitalShape, Domain > KanungoPredicate;
680 typedef LightImplicitDigitalSurface< KSpace, KanungoPredicate > SurfaceContainer;
681 typedef DigitalSurface< SurfaceContainer > Surface;
682 typedef typename Surface::Surfel Surfel;
683 SurfelAdjacency< KSpace::dimension > surfAdj(
true );
685 const Domain shapeDomain = dshape.getDomain();
686 KanungoPredicate* noisified_dshape =
new KanungoPredicate( dshape, shapeDomain, noiseLevel );
688 CountedPtr<Surface> ptrSurface;
689 double minsize = dshape.getUpperBound()[0] - dshape.getLowerBound()[0];
690 unsigned int nb_surfels = 0;
691 unsigned int tries = 0;
694 bel = Surfaces<KSpace>::findABel( K, *noisified_dshape, 10000 );
695 }
catch (DGtal::InputException e) {
696 trace.error() <<
"ERROR Unable to find bel." << std::endl;
699 SurfaceContainer* surfaceContainer =
new SurfaceContainer( K, *noisified_dshape, surfAdj, bel );
700 ptrSurface = CountedPtr<Surface>(
new Surface( surfaceContainer ) );
701 nb_surfels = ptrSurface->size();
702 }
while ( ( nb_surfels < 2 * minsize ) && ( tries++ < 150 ) );
705 trace.error() <<
"ERROR cannot find a proper bel in a big enough component." << std::endl;
708 trace.info() <<
"- surface component has " << nb_surfels <<
" surfels." << std::endl;
710 chooseKernel( vm, K, shape, *ptrSurface, *noisified_dshape );
717 int main(
int argc,
char** argv )
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." )
746 po::variables_map vm;
748 po::store(po::parse_command_line(argc, argv, general_opt), vm);
749 }
catch(
const exception& ex){
751 cerr <<
"Error checking program options: "<< ex.what()<< endl;
754 if( ! parseOK || vm.count(
"help") || ! vm.count(
"polynomial" ) )
756 if ( ! vm.count(
"polynomial" ) )
757 cerr <<
"Need parameter --polynomial / -p" << endl;
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."
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
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;
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>();
795 Polynomial3Reader reader;
796 string::const_iterator iter = reader.read( poly, poly_str.begin(), poly_str.end() );
797 if ( iter != poly_str.end() )
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;
803 CountedPtr<ImplicitShape> shape(
new ImplicitShape( poly ) );
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();
821 K.init( domain.lowerBound(), domain.upperBound(), true );
822 trace.info() <<
"- domain is " << domain << std::endl;
825 chooseSurface( vm, K, *shape, *dshape );