DGtalTools 2.1.0
Loading...
Searching...
No Matches
generic3dNormalEstimators.cpp
1
32#include <iostream>
33#include <fstream>
34#include <sstream>
35
36#include "CLI11.hpp"
37
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
61 // Polyscope disponible
62#include "DGtal/io/viewers/PolyscopeViewer.h"
63#endif
64
65
66using namespace std;
67using namespace DGtal;
68
69
70
207struct AllParams{
208 double noise {0.0};
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};
214 double alpha {0.0};
215 double trivial_radius {3.0};
216 int embedding {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;
224 bool normals;
225 bool noff;
226};
227
228
229
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 )
236 : myMap( map ) {}
237 RealVector operator()( const Argument& arg ) const
238 {
239 typename SCell2RealVectorMap::const_iterator it = myMap->find( arg );
240 if ( it != myMap->end() ) return it->second;
241 else return RealVector();
242 }
243 CountedConstPtrOrConstPtr<SCell2RealVectorMap> myMap;
244};
245
246template <typename SCellEmbedder>
247struct SCellEmbedderWithNormal : public SCellEmbedder
248{
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;
259
260 SCellEmbedderWithNormal( ConstAlias<SCellEmbedder> embedder,
261 ConstAlias<SCell2RealVectorMap> map )
262 : SCellEmbedder( embedder ), myMap( map )
263 {}
264
265 GradientMap gradientMap() const
266 {
267 return GradientMap( myMap );
268 }
269
270 CountedConstPtrOrConstPtr<SCell2RealVectorMap> myMap;
271};
272
273template <typename DigitalSurface,
274 typename Estimator>
275void exportNOFFSurface( const DigitalSurface& surface,
276 const Estimator& estimator,
277 std::ostream& output )
278{
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 )
286 {
287 Quantity n_est = estimator.eval( it );
288 normals[ *it ] = n_est;
289 }
290 CanonicSCellEmbedder<KSpace> surfelEmbedder( ks );
291 typedef SCellEmbedderWithNormal< CanonicSCellEmbedder<KSpace> > Embedder;
292 Embedder embedder( surfelEmbedder, normals );
293 surface.exportAs3DNOFF( output, embedder );
294}
295
296
300template <typename KSpace,
301 typename ImplicitShape,
302 typename Surface,
303 typename TrueEstimator,
304 typename Estimator>
305void computeEstimation
306( const AllParams &params, //< command-line parameters
307 const KSpace& K, //< cellular grid space
308 const ImplicitShape& shape, //< implicit shape "ground truth"
309 const Surface& surface, //< digital surface approximating shape
310 TrueEstimator& true_estimator, //< "ground truth" estimator
311 Estimator& estimator ) //< an initialized estimator
312{
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;
319
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;
327 trace.endBlock();
328
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;
334 trace.endBlock();
335
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 ];
341 trace.endBlock();
342
343 DGtal::GradientColorMap<double> grad( 0.0, 40.0 );
344 // 0 metallic blue, 5 light cyan, 10 light green, 15 light
345 // yellow, 20 yellow, 25 orange, 30 red, 35, dark red, 40- grey
346 grad.addColor( DGtal::Color( 128, 128, 255 ) ); // 0
347 grad.addColor( DGtal::Color( 128, 255, 255 ) ); // 5
348 grad.addColor( DGtal::Color( 128, 255, 128 ) ); // 10
349 grad.addColor( DGtal::Color( 255, 255, 128 ) ); // 15
350 grad.addColor( DGtal::Color( 255, 255, 0 ) ); // 20
351 grad.addColor( DGtal::Color( 255, 128, 0 ) ); // 25
352 grad.addColor( DGtal::Color( 255, 0, 0 ) ); // 30
353 grad.addColor( DGtal::Color( 128, 0, 0 ) ); // 35
354 grad.addColor( DGtal::Color( 128, 128, 128 ) ); // 40
355
356 if ( params.angle_deviation_stats )
357 {
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;
363 unsigned int i = 0;
364 range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
365 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
366 {
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 );
371 }
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() // L1
378 << " " << sqrt( adev_stat.unbiasedVariance()
379 + adev_stat.mean()*adev_stat.mean() ) // L2
380 << " " << adev_stat.max() // Loo
381 << " " << adev_stat.mean() // E[X] (=L1)
382 << " " << adev_stat.unbiasedVariance() // Var[X]
383 << " " << adev_stat.min() // Min[X]
384 << " " << adev_stat.max() // Max[X]
385 << " " << adev_stat.samples() // Nb[X]
386 << std::endl;
387 adev_output.close();
388 trace.endBlock();
389 }
390 if ( params.exportX != "None" )
391 {
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";
399 unsigned int i = 0;
400 range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
401 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
402 {
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;
406 Surfel s = *it;
407 export_output
408 << "CellN"
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 );
413 Color c = grad( 0 );
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;
420 }
421 export_output.close();
422 trace.endBlock();
423 }
424 if ( params.normals )
425 {
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;
432 unsigned int i = 0;
433 range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
434 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
435 {
436 Quantity n_est = n_estimations[ i ];
437 Quantity n_true_est = n_true_estimations[ i ];
438 Surfel s = *it;
439 export_output
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 ]
444 << std::endl;
445 }
446 export_output.close();
447 trace.endBlock();
448 }
449 if ( params.noff )
450 {
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;
457 unsigned int i = 0;
458 range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
459 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
460 {
461 Quantity n_est = n_estimations[ i ];
462 normals[ *it ] = n_est;
463 }
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();
469 trace.endBlock();
470 }
471
472#ifdef DGTAL_WITH_POLYSCOPE_VIEWER
473 if ( params.view != "None" )
474 {
475 typedef typename KSpace::Space Space;
476 typedef PolyscopeViewer<Space,KSpace> MyViewever3D;
477 int argc = 1;
478 char name[] = "Viewer";
479 char* argv[ 1 ];
480 argv[ 0 ] = name;
481 Surfel s;
482 polyscope::options::programName = "generic3dNormalEstimators - DGtalTools ";
483 MyViewever3D viewer( K );
484 viewer.drawAsSimplified();
485 viewer.allowReuseList = true;
486
487 trace.beginBlock( "Viewing surface." );
488 bool adev = params.view == "AngleDeviation";
489
490 unsigned int i = 0;
491 range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
492 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
493 {
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;
497 s = *it;
498 Color c = grad( 0 );
499 if ( adev ) c = grad( max( 0.0, min( angle_error, 40.0 ) ) );
500 viewer.drawColor( c );
501 if ( params.view == "Normals" )
502 {
503 viewer << WithQuantity(s, "normal", n_est);
504 }
505 else if ( params.view == "AngleDeviation" )
506 {
507 viewer << WithQuantity(s, "angle deviation", angle_error);
508 }
509 }
510 trace.endBlock();
511 viewer.show();
512
513 }
514#endif
515
516}
517
518template <typename KSpace,
519 typename ImplicitShape,
520 typename Surface,
521 typename KernelFunction,
522 typename PointPredicate>
523void chooseEstimator
524( const AllParams &params, //< command-line parameters
525 const KSpace& K, //< cellular grid space
526 const ImplicitShape& shape, //< implicit shape "ground truth"
527 const Surface& surface, //< digital surface approximating shape
528 const KernelFunction& chi, //< the kernel function
529 const PointPredicate& ptPred ) //< analysed implicit digital shape as a PointPredicate
530{
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" )
540 {
541 trace.beginBlock( "Chosen estimator is: True." );
542 typedef TrueDigitalSurfaceLocalEstimator<KSpace, ImplicitShape, NormalFunctor> Estimator;
543 Estimator estimator;
544 estimator.attach( shape );
545 estimator.setParams( K, NormalFunctor(), 20, 0.1, 0.01 );
546 estimator.init( h, surface.begin(), surface.end() );
547 trace.endBlock();
548 computeEstimation( params, K, shape, surface, true_estimator, estimator );
549 }
550 else if ( nameEstimator == "VCM" )
551 {
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() );
575 trace.endBlock();
576 computeEstimation( params, K, shape, surface, true_estimator, estimator );
577 }
578 else if ( nameEstimator == "II" )
579 {
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 )
596 {
597 image.setValue( *it, ptPred( *it ) );
598 }
599 trace.endBlock();
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() );
605 trace.endBlock();
606 trace.endBlock();
607 computeEstimation( params, K, shape, surface, true_estimator, ii_estimator );
608 }
609
610}
611
612template <typename KSpace,
613 typename ImplicitShape,
614 typename Surface,
615 typename PointPredicate>
616void chooseKernel
617( const AllParams& params, //< command-line parameters
618 const KSpace& K, //< cellular grid space
619 const ImplicitShape& shape, //< implicit shape "ground truth"
620 const Surface& surface, //< digital surface approximating shape
621 const PointPredicate& ptPred ) //< analysed implicit digital shape as a PointPredicate
622{
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 );
638 }
639}
640
641template <typename KSpace,
642 typename ImplicitShape,
643 typename ImplicitDigitalShape >
644int chooseSurface
645( const AllParams &params, //< command-line parameters
646 const KSpace& K, //< cellular grid space
647 const ImplicitShape& shape, //< implicit shape "ground truth"
648 const ImplicitDigitalShape& dshape ) //< analysed implicit digital shape
649{
650 // Selecting a model of surface depending on noise / not noise.
651 typedef double Scalar;
652 if ( params.noise == 0.0 )
653 { // no noise
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 );
659 Surfel bel;
660 try {
661 bel = Surfaces<KSpace>::findABel( K, dshape, 10000 );
662 } catch (DGtal::InputException e) {
663 trace.error() << "ERROR Unable to find bel." << std::endl;
664 return 3;
665 }
666 SurfaceContainer* surfaceContainer = new SurfaceContainer( K, dshape, surfAdj, bel );
667 CountedPtr<Surface> ptrSurface( new Surface( surfaceContainer ) ); // acquired
668 trace.info() << "- surface component has " << ptrSurface->size() << " surfels." << std::endl;
669 trace.endBlock();
670 chooseKernel( params, K, shape, *ptrSurface, dshape );
671 }
672 else
673 { // noise
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 );
681 Surfel bel;
682 const Domain shapeDomain = dshape.getDomain();
683 KanungoPredicate* noisified_dshape = new KanungoPredicate( dshape, shapeDomain, params.noise );
684 // We have to search for a big connected component.
685 CountedPtr<Surface> ptrSurface;
686 double minsize = dshape.getUpperBound()[0] - dshape.getLowerBound()[0];
687 unsigned int nb_surfels = 0;
688 unsigned int tries = 0;
689 do {
690 try { // Search initial bel
691 bel = Surfaces<KSpace>::findABel( K, *noisified_dshape, 10000 );
692 } catch (DGtal::InputException e) {
693 trace.error() << "ERROR Unable to find bel." << std::endl;
694 return 3;
695 }
696 SurfaceContainer* surfaceContainer = new SurfaceContainer( K, *noisified_dshape, surfAdj, bel );
697 ptrSurface = CountedPtr<Surface>( new Surface( surfaceContainer ) ); // acquired
698 nb_surfels = ptrSurface->size();
699 } while ( ( nb_surfels < 2 * minsize ) && ( tries++ < 150 ) );
700 if( tries >= 150 )
701 {
702 trace.error() << "ERROR cannot find a proper bel in a big enough component." << std::endl;
703 return 4;
704 }
705 trace.info() << "- surface component has " << nb_surfels << " surfels." << std::endl;
706 trace.endBlock();
707 chooseKernel( params, K, shape, *ptrSurface, *noisified_dshape );
708 }
709 return 0;
710}
711
712
714int main( int argc, char** argv )
715{
716
717 AllParams allParams;
718
719 // parse command line ----------------------------------------------
720 CLI::App app;
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.";
723
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
741 << 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;
745
746 app.description(ss_descr.str());
747
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>.");
763
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.");
769#endif
770
771
772 app.get_formatter()->column_width(40);
773 CLI11_PARSE(app, argc, argv);
774 // END parse command line using CLI ----------------------------------------------
775
776
777
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;
784 Polynomial3 poly;
785 Polynomial3Reader reader;
786 string::const_iterator iter = reader.read( poly, allParams.polynomials.begin(), allParams.polynomials.end() );
787 if ( iter != allParams.polynomials.end() )
788 {
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;
791 return 2;
792 }
793 CountedPtr<ImplicitShape> shape( new ImplicitShape( poly ) ); // smart pointer
794 trace.endBlock();
795
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();
810 KSpace K;
811 K.init( domain.lowerBound(), domain.upperBound(), true );
812 trace.info() << "- domain is " << domain << std::endl;
813 trace.endBlock();
814
815 chooseSurface( allParams, K, *shape, *dshape );
816
817 return 0;
818}
819
Definition ATu0v1.h:57