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