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_VIEWER
62#include "DGtal/io/viewers/PolyscopeViewer.h"
209 double minAABB {-10.0};
210 double maxAABB {10.0};
211 double gridstep {1.0};
212 double R_radius {5.0};
213 double r_radius {3.0};
215 double trivial_radius {3.0};
217 std::string polynomials;
218 std::string estimator {
"True"};
219 std::string kernel {
"hat"};
220 std::string output {
"output"};
221 std::string exportX {
"None"};
222 std::string view {
"None"};
223 bool angle_deviation_stats;
230template <
typename SCell,
typename RealVector>
231struct GradientMapAdapter {
232 typedef std::map<SCell,RealVector> SCell2RealVectorMap;
233 typedef SCell Argument;
234 typedef RealVector Value;
235 GradientMapAdapter( ConstAlias<SCell2RealVectorMap> map )
237 RealVector operator()(
const Argument& arg )
const
239 typename SCell2RealVectorMap::const_iterator it = myMap->find( arg );
240 if ( it != myMap->end() )
return it->second;
241 else return RealVector();
243 CountedConstPtrOrConstPtr<SCell2RealVectorMap> myMap;
246template <
typename SCellEmbedder>
247struct SCellEmbedderWithNormal :
public SCellEmbedder
249 using SCellEmbedder::space;
250 using SCellEmbedder::operator();
251 typedef typename SCellEmbedder::KSpace KSpace;
252 typedef typename SCellEmbedder::SCell SCell;
253 typedef typename SCellEmbedder::RealPoint RealPoint;
254 typedef SCell Argument;
255 typedef RealPoint Value;
256 typedef typename KSpace::Space::RealVector RealVector;
257 typedef std::map<SCell,RealVector> SCell2RealVectorMap;
258 typedef GradientMapAdapter<SCell,RealVector> GradientMap;
260 SCellEmbedderWithNormal( ConstAlias<SCellEmbedder> embedder,
261 ConstAlias<SCell2RealVectorMap> map )
262 : SCellEmbedder( embedder ), myMap( map )
265 GradientMap gradientMap()
const
267 return GradientMap( myMap );
270 CountedConstPtrOrConstPtr<SCell2RealVectorMap> myMap;
273template <
typename DigitalSurface,
275void exportNOFFSurface(
const DigitalSurface& surface,
276 const Estimator& estimator,
277 std::ostream& output )
279 typedef typename DigitalSurface::KSpace KSpace;
280 typedef typename DigitalSurface::ConstIterator ConstIterator;
281 typedef typename DigitalSurface::Surfel Surfel;
282 typedef typename Estimator::Quantity Quantity;
283 const KSpace& ks = surface.container().space();
284 std::map<Surfel,Quantity> normals;
285 for ( ConstIterator it = surface.begin(), itE = surface.end(); it != itE; ++it )
287 Quantity n_est = estimator.eval( it );
288 normals[ *it ] = n_est;
290 CanonicSCellEmbedder<KSpace> surfelEmbedder( ks );
291 typedef SCellEmbedderWithNormal< CanonicSCellEmbedder<KSpace> > Embedder;
292 Embedder embedder( surfelEmbedder, normals );
293 surface.exportAs3DNOFF( output, embedder );
300template <
typename KSpace,
301 typename ImplicitShape,
303 typename TrueEstimator,
305void computeEstimation
306(
const AllParams ¶ms,
308 const ImplicitShape& shape,
309 const Surface& surface,
310 TrueEstimator& true_estimator,
311 Estimator& estimator )
313 typedef typename Surface::Surfel Surfel;
314 typedef typename Estimator::Quantity Quantity;
315 typedef double Scalar;
316 typedef DepthFirstVisitor< Surface > Visitor;
317 typedef GraphVisitorRange< Visitor > VisitorRange;
318 typedef typename VisitorRange::ConstIterator VisitorConstIterator;
320 std::string fname = params.output;
321 string nameEstimator = params.estimator;
322 trace.beginBlock(
"Computing " + nameEstimator +
"estimations." );
323 CountedPtr<VisitorRange> range(
new VisitorRange(
new Visitor( surface, *(surface.begin()) )) );
324 std::vector<Quantity> n_estimations;
325 estimator.eval( range->begin(), range->end(), std::back_inserter( n_estimations ) );
326 trace.info() <<
"- nb estimations = " << n_estimations.size() << std::endl;
329 trace.beginBlock(
"Computing ground truth." );
330 range = CountedPtr<VisitorRange>(
new VisitorRange(
new Visitor( surface, *(surface.begin()) )) );
331 std::vector<Quantity> n_true_estimations;
332 true_estimator.eval( range->begin(), range->end(), std::back_inserter( n_true_estimations ) );
333 trace.info() <<
"- nb estimations = " << n_true_estimations.size() << std::endl;
336 trace.beginBlock(
"Correcting orientations." );
337 ASSERT( n_estimations.size() == n_true_estimations.size() );
338 for (
unsigned int i = 0; i < n_estimations.size(); ++i )
339 if ( n_estimations[ i ].dot( n_true_estimations[ i ] ) < 0 )
340 n_estimations[ i ] = -n_estimations[ i ];
343 DGtal::GradientColorMap<double> grad( 0.0, 40.0 );
346 grad.addColor( DGtal::Color( 128, 128, 255 ) );
347 grad.addColor( DGtal::Color( 128, 255, 255 ) );
348 grad.addColor( DGtal::Color( 128, 255, 128 ) );
349 grad.addColor( DGtal::Color( 255, 255, 128 ) );
350 grad.addColor( DGtal::Color( 255, 255, 0 ) );
351 grad.addColor( DGtal::Color( 255, 128, 0 ) );
352 grad.addColor( DGtal::Color( 255, 0, 0 ) );
353 grad.addColor( DGtal::Color( 128, 0, 0 ) );
354 grad.addColor( DGtal::Color( 128, 128, 128 ) );
356 if ( params.angle_deviation_stats )
358 trace.beginBlock(
"Computing angle deviation error stats." );
359 std::ostringstream adev_sstr;
360 adev_sstr << fname <<
"-" << nameEstimator <<
"-angle-deviation-"
361 << estimator.h() <<
".txt";
362 DGtal::Statistic<Scalar> adev_stat;
364 range = CountedPtr<VisitorRange>(
new VisitorRange(
new Visitor( surface, *(surface.begin()) )) );
365 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
367 Quantity n_est = n_estimations[ i ];
368 Quantity n_true_est = n_true_estimations[ i ];
369 Scalar angle_error = acos( n_est.dot( n_true_est ) );
370 adev_stat.addValue( angle_error );
372 adev_stat.terminate();
373 std::ofstream adev_output( adev_sstr.str().c_str() );
374 adev_output <<
"# Average error X of the absolute angle between two vector estimations." << std::endl;
375 adev_output <<
"# h L1 L2 Loo E[X] Var[X] Min[X] Max[X] Nb[X]" << std::endl;
376 adev_output << estimator.h()
377 <<
" " << adev_stat.mean()
378 <<
" " << sqrt( adev_stat.unbiasedVariance()
379 + adev_stat.mean()*adev_stat.mean() )
380 <<
" " << adev_stat.max()
381 <<
" " << adev_stat.mean()
382 <<
" " << adev_stat.unbiasedVariance()
383 <<
" " << adev_stat.min()
384 <<
" " << adev_stat.max()
385 <<
" " << adev_stat.samples()
390 if ( params.exportX !=
"None" )
392 trace.beginBlock(
"Exporting cell geometry." );
393 std::ostringstream export_sstr;
394 export_sstr << fname <<
"-" << nameEstimator <<
"-cells-"
395 << estimator.h() <<
".txt";
396 std::ofstream export_output( export_sstr.str().c_str() );
397 export_output <<
"# ImaGene viewer (viewSetOfSurfels) file format for displaying cells." << std::endl;
398 bool adev = params.exportX ==
"AngleDeviation";
400 range = CountedPtr<VisitorRange>(
new VisitorRange(
new Visitor( surface, *(surface.begin()) )) );
401 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
403 Quantity n_est = n_estimations[ i ];
404 Quantity n_true_est = n_true_estimations[ i ];
405 Scalar angle_error = acos( n_est.dot( n_true_est ) )*180.0 / 3.14159625;
409 <<
" " << min( 1023, max( 512+K.sKCoord( s, 0 ), 0 ) )
410 <<
" " << min( 1023, max( 512+K.sKCoord( s, 1 ), 0 ) )
411 <<
" " << min( 1023, max( 512+K.sKCoord( s, 2 ), 0 ) )
412 <<
" " << K.sSign( s );
414 if ( adev ) c = grad( max( 0.0, min( angle_error, 40.0 ) ) );
415 export_output <<
" " << ((double) c.red() / 255.0 )
416 <<
" " << ((
double) c.green() / 255.0 )
417 <<
" " << ((
double) c.blue() / 255.0 );
418 export_output <<
" " << n_est[ 0 ] <<
" " << n_est[ 1 ]
419 <<
" " << n_est[ 2 ] << std::endl;
421 export_output.close();
424 if ( params.normals )
426 trace.beginBlock(
"Exporting cells normals." );
427 std::ostringstream export_sstr;
428 export_sstr << fname <<
"-" << nameEstimator <<
"-normals-"
429 << estimator.h() <<
".txt";
430 std::ofstream export_output( export_sstr.str().c_str() );
431 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;
433 range = CountedPtr<VisitorRange>(
new VisitorRange(
new Visitor( surface, *(surface.begin()) )) );
434 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
436 Quantity n_est = n_estimations[ i ];
437 Quantity n_true_est = n_true_estimations[ i ];
440 << K.sKCoord( s, 0 ) <<
" " << K.sKCoord( s, 1 ) <<
" " << K.sKCoord( s, 2 )
441 <<
" " << K.sSign( s )
442 <<
" " << n_est[ 0 ] <<
" " << n_est[ 1 ] <<
" " << n_est[ 2 ]
443 <<
" " << n_true_est[ 0 ] <<
" " << n_true_est[ 1 ] <<
" " << n_true_est[ 2 ]
446 export_output.close();
451 trace.beginBlock(
"Exporting NOFF file." );
452 std::ostringstream export_sstr;
453 export_sstr << fname <<
"-" << nameEstimator <<
"-noff-"
454 << estimator.h() <<
".off";
455 std::ofstream export_output( export_sstr.str().c_str() );
456 std::map<Surfel,Quantity> normals;
458 range = CountedPtr<VisitorRange>(
new VisitorRange(
new Visitor( surface, *(surface.begin()) )) );
459 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
461 Quantity n_est = n_estimations[ i ];
462 normals[ *it ] = n_est;
464 CanonicSCellEmbedder<KSpace> surfelEmbedder( K );
465 typedef SCellEmbedderWithNormal< CanonicSCellEmbedder<KSpace> > Embedder;
466 Embedder embedder( surfelEmbedder, normals );
467 surface.exportAs3DNOFF( export_output, embedder );
468 export_output.close();
472#ifdef DGTAL_WITH_POLYSCOPE_VIEWER
473 if ( params.view !=
"None" )
475 typedef typename KSpace::Space Space;
476 typedef PolyscopeViewer<Space,KSpace> MyViewever3D;
478 char name[] =
"Viewer";
482 polyscope::options::programName =
"generic3dNormalEstimators - DGtalTools ";
483 MyViewever3D viewer( K );
484 viewer.drawAsSimplified();
485 viewer.allowReuseList =
true;
487 trace.beginBlock(
"Viewing surface." );
488 bool adev = params.view ==
"AngleDeviation";
491 range = CountedPtr<VisitorRange>(
new VisitorRange(
new Visitor( surface, *(surface.begin()) )) );
492 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
494 Quantity n_est = n_estimations[ i ];
495 Quantity n_true_est = n_true_estimations[ i ];
496 Scalar angle_error = acos( n_est.dot( n_true_est ) )*180.0 / 3.14159625;
499 if ( adev ) c = grad( max( 0.0, min( angle_error, 40.0 ) ) );
500 viewer.drawColor( c );
501 if ( params.view ==
"Normals" )
503 viewer << WithQuantity(s,
"normal", n_est);
505 else if ( params.view ==
"AngleDeviation" )
507 viewer << WithQuantity(s,
"angle deviation", angle_error);
518template <
typename KSpace,
519 typename ImplicitShape,
521 typename KernelFunction,
522 typename PointPredicate>
524(
const AllParams ¶ms,
526 const ImplicitShape& shape,
527 const Surface& surface,
528 const KernelFunction& chi,
529 const PointPredicate& ptPred )
531 string nameEstimator = params.estimator;
532 double h = params.gridstep;
533 typedef functors::ShapeGeometricFunctors::ShapeNormalVectorFunctor<ImplicitShape> NormalFunctor;
534 typedef TrueDigitalSurfaceLocalEstimator<KSpace, ImplicitShape, NormalFunctor> TrueEstimator;
535 TrueEstimator true_estimator;
536 true_estimator.attach( shape );
537 true_estimator.setParams( K, NormalFunctor(), 20, 0.1, 0.01 );
538 true_estimator.init( h, surface.begin(), surface.end() );
539 if ( nameEstimator ==
"True" )
541 trace.beginBlock(
"Chosen estimator is: True." );
542 typedef TrueDigitalSurfaceLocalEstimator<KSpace, ImplicitShape, NormalFunctor> Estimator;
544 estimator.attach( shape );
545 estimator.setParams( K, NormalFunctor(), 20, 0.1, 0.01 );
546 estimator.init( h, surface.begin(), surface.end() );
548 computeEstimation( params, K, shape, surface, true_estimator, estimator );
550 else if ( nameEstimator ==
"VCM" )
552 trace.beginBlock(
"Chosen estimator is: VCM." );
553 typedef typename KSpace::Space Space;
554 typedef typename Surface::DigitalSurfaceContainer SurfaceContainer;
555 typedef ExactPredicateLpSeparableMetric<Space,2> Metric;
556 typedef VoronoiCovarianceMeasureOnDigitalSurface<SurfaceContainer,Metric,
557 KernelFunction> VCMOnSurface;
558 typedef functors::VCMNormalVectorFunctor<VCMOnSurface> NormalFunctor;
559 typedef VCMDigitalSurfaceLocalEstimator<SurfaceContainer,Metric,
560 KernelFunction, NormalFunctor> VCMNormalEstimator;
561 int embedding = params.embedding;
562 Surfel2PointEmbedding embType = embedding == 0 ? Pointels :
563 embedding == 1 ? InnerSpel : OuterSpel;
564 double R = params.R_radius;
565 double r = params.r_radius;
566 double t = params.trivial_radius;
567 double alpha = params.alpha;
568 if ( alpha != 0.0 ) R *= pow( h, alpha-1.0 );
569 if ( alpha != 0.0 ) r *= pow( h, alpha-1.0 );
570 trace.info() <<
"- R=" << R <<
" r=" << r <<
" t=" << t << std::endl;
571 VCMNormalEstimator estimator;
572 estimator.attach( surface );
573 estimator.setParams( embType, R, r, chi, t, Metric(),
true );
574 estimator.init( h, surface.begin(), surface.end() );
576 computeEstimation( params, K, shape, surface, true_estimator, estimator );
578 else if ( nameEstimator ==
"II" )
580 trace.beginBlock(
"Chosen estimator is: II." );
581 typedef typename KSpace::Space Space;
582 typedef HyperRectDomain<Space> Domain;
583 typedef ImageContainerBySTLVector< Domain, bool> Image;
584 typedef typename Domain::ConstIterator DomainConstIterator;
585 typedef functors::SimpleThresholdForegroundPredicate<Image> ThresholdedImage;
586 typedef functors::IINormalDirectionFunctor<Space> IINormalFunctor;
587 typedef IntegralInvariantCovarianceEstimator<KSpace, ThresholdedImage, IINormalFunctor> IINormalEstimator;
588 double r = params.r_radius;
589 double alpha = params.alpha;
590 if ( alpha != 0.0 ) r *= pow( h, alpha-1.0 );
591 trace.info() <<
" r=" << r << std::endl;
592 trace.beginBlock(
"Preparing characteristic set." );
593 Domain domain( K.lowerBound(), K.upperBound() );
594 Image image( domain );
595 for ( DomainConstIterator it = domain.begin(), itE = domain.end(); it != itE; ++it )
597 image.setValue( *it, ptPred( *it ) );
600 trace.beginBlock(
"Initialize II estimator." );
601 ThresholdedImage thresholdedImage( image,
false );
602 IINormalEstimator ii_estimator( K, thresholdedImage );
603 ii_estimator.setParams( r );
604 ii_estimator.init( h, surface.begin(), surface.end() );
607 computeEstimation( params, K, shape, surface, true_estimator, ii_estimator );
612template <
typename KSpace,
613 typename ImplicitShape,
615 typename PointPredicate>
617(
const AllParams& params,
619 const ImplicitShape& shape,
620 const Surface& surface,
621 const PointPredicate& ptPred )
623 string kernel = params.kernel;
624 double h = params.gridstep;
625 double r = params.r_radius;
626 double alpha = params.alpha;
627 if ( alpha != 0.0 ) r *= pow( h, alpha-1.0 );
628 if ( kernel ==
"hat" ) {
629 typedef typename KSpace::Point Point;
630 typedef functors::HatPointFunction<Point,double> KernelFunction;
631 KernelFunction chi_r( 1.0, r );
632 chooseEstimator( params, K, shape, surface, chi_r, ptPred );
633 }
else if ( kernel ==
"ball" ) {
634 typedef typename KSpace::Point Point;
635 typedef functors::BallConstantPointFunction<Point,double> KernelFunction;
636 KernelFunction chi_r( 1.0, r );
637 chooseEstimator( params, K, shape, surface, chi_r, ptPred );
641template <
typename KSpace,
642 typename ImplicitShape,
643 typename ImplicitDigitalShape >
645(
const AllParams ¶ms,
647 const ImplicitShape& shape,
648 const ImplicitDigitalShape& dshape )
651 typedef double Scalar;
652 if ( params.noise == 0.0 )
654 trace.beginBlock(
"Make digital surface..." );
655 typedef LightImplicitDigitalSurface<KSpace,ImplicitDigitalShape> SurfaceContainer;
656 typedef DigitalSurface< SurfaceContainer > Surface;
657 typedef typename Surface::Surfel Surfel;
658 SurfelAdjacency< KSpace::dimension > surfAdj(
true );
661 bel = Surfaces<KSpace>::findABel( K, dshape, 10000 );
662 }
catch (DGtal::InputException e) {
663 trace.error() <<
"ERROR Unable to find bel." << std::endl;
666 SurfaceContainer* surfaceContainer =
new SurfaceContainer( K, dshape, surfAdj, bel );
667 CountedPtr<Surface> ptrSurface(
new Surface( surfaceContainer ) );
668 trace.info() <<
"- surface component has " << ptrSurface->size() <<
" surfels." << std::endl;
670 chooseKernel( params, K, shape, *ptrSurface, dshape );
674 trace.beginBlock(
"Make digital surface..." );
675 typedef typename ImplicitDigitalShape::Domain Domain;
676 typedef KanungoNoise< ImplicitDigitalShape, Domain > KanungoPredicate;
677 typedef LightImplicitDigitalSurface< KSpace, KanungoPredicate > SurfaceContainer;
678 typedef DigitalSurface< SurfaceContainer > Surface;
679 typedef typename Surface::Surfel Surfel;
680 SurfelAdjacency< KSpace::dimension > surfAdj(
true );
682 const Domain shapeDomain = dshape.getDomain();
683 KanungoPredicate* noisified_dshape =
new KanungoPredicate( dshape, shapeDomain, params.noise );
685 CountedPtr<Surface> ptrSurface;
686 double minsize = dshape.getUpperBound()[0] - dshape.getLowerBound()[0];
687 unsigned int nb_surfels = 0;
688 unsigned int tries = 0;
691 bel = Surfaces<KSpace>::findABel( K, *noisified_dshape, 10000 );
692 }
catch (DGtal::InputException e) {
693 trace.error() <<
"ERROR Unable to find bel." << std::endl;
696 SurfaceContainer* surfaceContainer =
new SurfaceContainer( K, *noisified_dshape, surfAdj, bel );
697 ptrSurface = CountedPtr<Surface>(
new Surface( surfaceContainer ) );
698 nb_surfels = ptrSurface->size();
699 }
while ( ( nb_surfels < 2 * minsize ) && ( tries++ < 150 ) );
702 trace.error() <<
"ERROR cannot find a proper bel in a big enough component." << std::endl;
705 trace.info() <<
"- surface component has " << nb_surfels <<
" surfels." << std::endl;
707 chooseKernel( params, K, shape, *ptrSurface, *noisified_dshape );
714int main(
int argc,
char** argv )
721 std::stringstream ss_descr;
722 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.";
724 ss_descr <<
"Example of use:\n"
725 <<
"./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
726 <<
"Example of implicit surfaces (specified by -p):" << endl
727 <<
" - ellipse : 90-3*x^2-2*y^2-z^2 " << endl
728 <<
" - torus : -1*(x^2+y^2+z^2+6*6-2*2)^2+4*6*6*(x^2+y^2) " << endl
729 <<
" - rcube : 6561-x^4-y^4-z^4" << endl
730 <<
" - goursat : 8-0.03*x^4-0.03*y^4-0.03*z^4+2*x^2+2*y^2+2*z^2" << endl
731 <<
" - distel : 10000-(x^2+y^2+z^2+1000*(x^2+y^2)*(x^2+z^2)*(y^2+z^2))" << endl
732 <<
" - leopold : 100-(x^2*y^2*z^2+4*x^2+4*y^2+3*z^2)" << endl
733 <<
" - diabolo : x^2-(y^2+z^2)^2" << endl
734 <<
" - heart : -1*(x^2+2.25*y^2+z^2-1)^3+x^2*z^3+0.1125*y^2*z^3" << endl
735 <<
" - crixxi : -0.9*(y^2+z^2-1)^2-(x^2+y^2-1)^3" << endl << endl;
736 ss_descr <<
"Implemented estimators (specified by -e):" << endl
737 <<
" - True : supposed to be the ground truth for computations. Of course, it is only approximations." << endl
738 <<
" - VCM : normal estimator by digital Voronoi Covariance Matrix. Radii parameters are given by -R, -r." << endl
739 <<
" - II : normal estimator by Integral Invariants. Radius parameter is given by -r." << endl
740 <<
" - Trivial : the normal obtained by average trivial surfel normals in a ball neighborhood. Radius parameter is given by -r." << endl
742 ss_descr <<
"Note:" << endl
743 <<
" - 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
744 <<
" - 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;
746 app.description(ss_descr.str());
748 app.add_option(
"--polynomial,-p",allParams.polynomials,
"the implicit polynomial whose zero-level defines the shape of interest.") ->required();
749 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." );
750 app.add_option(
"--minAABB,-a", allParams.minAABB,
"the min value of the AABB bounding box (domain)" );
751 app.add_option(
"--maxAABB,-A", allParams.maxAABB,
"the max value of the AABB bounding box (domain)" );
752 app.add_option(
"--gridstep,-g", allParams.gridstep,
"the gridstep that defines the digitization (often called h).");
753 app.add_option(
"--estimator,-e", allParams.estimator,
"the chosen normal estimator: True | VCM | II | Trivial")
754 -> check(CLI::IsMember({
"True",
"VCM",
"II",
"Trivial"}));
755 app.add_option(
"--R-radius,-R", allParams.R_radius,
"the constant for parameter R in R(h)=R h^alpha (VCM).");
756 app.add_option(
"--r-radius,-r", allParams.r_radius,
"the constant for parameter r in r(h)=r h^alpha (VCM,II,Trivial).");
757 app.add_option(
"--kernel,-k", allParams.kernel,
"the function chi_r, either hat or ball.");
758 app.add_option(
"--alpha", allParams.alpha,
"the parameter alpha in r(h)=r h^alpha (VCM).");
759 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.");
760 app.add_option(
"--embedding,-E",allParams.embedding,
"the surfel -> point embedding for VCM estimator: 0: Pointels, 1: InnerSpel, 2: OuterSpel.");
761 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.");
762 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>.");
764 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" );
765 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>.");
766 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>..");
767#ifdef DGTAL_WITH_POLYSCOPE_VIEWER
768 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.");
772 app.get_formatter()->column_width(40);
773 CLI11_PARSE(app, argc, argv);
778 trace.beginBlock(
"Make implicit shape..." );
779 typedef Z3i::Space Space;
780 typedef double Scalar;
781 typedef MPolynomial< 3, Scalar > Polynomial3;
782 typedef MPolynomialReader<3, Scalar> Polynomial3Reader;
783 typedef ImplicitPolynomial3Shape<Space> ImplicitShape;
785 Polynomial3Reader reader;
786 string::const_iterator iter = reader.read( poly, allParams.polynomials.begin(), allParams.polynomials.end() );
787 if ( iter != allParams.polynomials.end() )
789 trace.error() <<
"ERROR reading polynomial: I read only <" << allParams.polynomials.substr( 0, iter - allParams.polynomials.begin() )
790 <<
">, and I built P=" << poly << std::endl;
793 CountedPtr<ImplicitShape> shape(
new ImplicitShape( poly ) );
796 trace.beginBlock(
"Make implicit digital shape..." );
797 typedef Z3i::KSpace KSpace;
798 typedef Space::RealPoint RealPoint;
799 typedef GaussDigitizer< Space, ImplicitShape > ImplicitDigitalShape;
800 typedef ImplicitDigitalShape::Domain Domain;
801 Scalar min_x = allParams.minAABB;
802 Scalar max_x = allParams.maxAABB;
803 Scalar h = allParams.gridstep;
804 RealPoint p1( min_x, min_x, min_x );
805 RealPoint p2( max_x, max_x, max_x );
806 CountedPtr<ImplicitDigitalShape> dshape(
new ImplicitDigitalShape() );
807 dshape->attach( *shape );
808 dshape->init( p1, p2, h );
809 Domain domain = dshape->getDomain();
811 K.init( domain.lowerBound(), domain.upperBound(),
true );
812 trace.info() <<
"- domain is " << domain << std::endl;
815 chooseSurface( allParams, K, *shape, *dshape );