DGtal  0.9.4beta
Local digital surface estimators from surfel functors

Table of Contents

Author(s) of this documentation:
David Coeurjolly

Part of the Geometry package.

Introduction

Many estimators of local quantities, such as curvature or normal vector field, on a digital surface can be defined locally as a function which associates a quantity to a local surface patch centered at the surfel s. Such local estimators can be characterized by:

When evaluating the estimator at a given surfel, neighboring surfels are traversed (using a DistanceBreadthFirstVisitor parametrized by the specified metric) and each surfel is sent to the surfel functor.

Implementation details

In DGtal, we implement this class of estimator using the generic LocalEstimatorsFromSurfel class. Such class is parametrized by the following template parameters:

This class provides three main methods:

The core of the estimators are thus specified in the surfel functor (model of concepts::CLocalEstimatorFromSurfelFunctor). In DGtal, we have defined several functors:

Advanced:
Implementing your own surfel patch based estimator is quite simple. Please have a look to concepts::CLocalEstimatorFromSurfelFunctor or the source code of any of its models.

Usage Example

In this section, we give a step by step example (see exampleEstimatorFromSurfelFunctors.cpp) . Let's start by defining a digital surface from a implicit digital ellipse.

using namespace Z3i;
typedef ImplicitDigitalEllipse3<Point> ImplicitDigitalEllipse;
typedef LightImplicitDigitalSurface<KSpace,ImplicitDigitalEllipse> SurfaceContainer;
typedef DigitalSurface< SurfaceContainer > Surface;
typedef SurfaceContainer::Surfel Surfel;
Point p1( -10, -10, -10 );
Point p2( 10, 10, 10 );
KSpace K;
K.init( p1, p2, true );
ImplicitDigitalEllipse ellipse( 6.0, 4.5, 3.4 );
Surfel bel = Surfaces<KSpace>::findABel( K, ellipse, 10000 );
SurfaceContainer* surfaceContainer = new SurfaceContainer
( K, ellipse, SurfelAdjacency<KSpace::dimension>( true ), bel );
Surface surface( surfaceContainer ); // acquired

We then define some types (note that we use the WITH_CGAL define to make sure that the user has enabled CGAL). Since local functors based on Monge Jet Fitting and CGAL do not require any weigths, we just consider a constant weight functor returning 1.0 for each surfel. For the functors::ElementaryConvolutionNormalVectorEstimator, we consider a Gaussian kernel with \( \sigma=2.0\). Please aslo note that the distance visitor is based on a Euclidean \( l_2\) metric:

#ifdef WITH_CGAL
typedef functors::MongeJetFittingMeanCurvatureEstimator<Surfel, CanonicSCellEmbedder<KSpace> > FunctorMean;
typedef functors::MongeJetFittingNormalVectorEstimator<Surfel, CanonicSCellEmbedder<KSpace> > FunctorNormal;
typedef functors::LinearLeastSquareFittingNormalVectorEstimator<Surfel, CanonicSCellEmbedder<KSpace> > FunctorNormalLeast;
//constant convolution functor
typedef functors::ConstValue<double> ConstFunctor;
typedef LocalEstimatorFromSurfelFunctorAdapter<SurfaceContainer, Z3i::L2Metric, FunctorGaussian, ConstFunctor> ReporterK;
typedef LocalEstimatorFromSurfelFunctorAdapter<SurfaceContainer, Z3i::L2Metric, FunctorMean, ConstFunctor> ReporterH;
typedef LocalEstimatorFromSurfelFunctorAdapter<SurfaceContainer, Z3i::L2Metric, FunctorNormal, ConstFunctor> ReporterNormal;
typedef LocalEstimatorFromSurfelFunctorAdapter<SurfaceContainer, Z3i::L2Metric, FunctorNormalLeast, ConstFunctor> ReporterNormalLeast;
#endif
typedef functors::ElementaryConvolutionNormalVectorEstimator<Surfel, CanonicSCellEmbedder<KSpace> > FunctorNormalElem;
typedef LocalEstimatorFromSurfelFunctorAdapter<SurfaceContainer, Z3i::L2Metric,
FunctorNormalElem, DGtal::functors::GaussianKernel> ReporterNormalElem;

We now create the instances for gridstep \( h=1.0 \) and a kernel radius 5.0:

CanonicSCellEmbedder<KSpace> embedder(surface.container().space());
#ifdef WITH_CGAL
FunctorGaussian estimatorK(embedder,1.0);
FunctorMean estimatorH(embedder, 1.0);
FunctorNormal estimatorN(embedder,1.0);
FunctorNormalLeast estimatorL(embedder,1.0);
ConstFunctor constFunctor(1.0);
ReporterK reporterK;
ReporterH reporterH;
ReporterNormal reporterN;
ReporterNormalLeast reporterL;
#endif
FunctorNormalElem estimatorNormalElem(embedder,1.0);
ReporterNormalElem reporterElem(surface, l2Metric,
estimatorNormalElem, gaussian);

We can now estimate the quantities at a surfel given by an iterator (here, the iterator at surface.begin()):

#ifdef WITH_CGAL
reporterK.attach(surface);
reporterH.attach(surface);
reporterN.attach(surface);
reporterL.attach(surface);
reporterK.init(1, surface.begin(), surface.end());
reporterH.init(1, surface.begin(), surface.end());
reporterN.init(1, surface.begin(), surface.end());
reporterL.init(1, surface.begin(), surface.end());
reporterK.setParams(l2Metric, estimatorK, constFunctor, 5.0);
reporterH.setParams(l2Metric, estimatorH, constFunctor, 5.0);
reporterN.setParams(l2Metric, estimatorN, constFunctor, 5.0);
reporterL.setParams(l2Metric, estimatorL, constFunctor, 5.0);
FunctorGaussian::Quantity valK = reporterK.eval( surface.begin());
FunctorMean::Quantity valH = reporterH.eval( surface.begin());
FunctorNormal::Quantity valN = reporterN.eval( surface.begin());
FunctorNormalLeast::Quantity valL = reporterL.eval( surface.begin());
#endif
reporterElem.attach(surface);
reporterElem.setParams(l2Metric,
estimatorNormalElem, gaussian, 5.0);
reporterElem.init(1.0, surface.begin(), surface.end());
FunctorNormalElem::Quantity valElem = reporterElem.eval( surface.begin());
#ifdef WITH_CGAL
trace.info() << "Gaussian = "<<valK <<std::endl;
trace.info() << "Mean = "<<valH<< std::endl;
trace.info() << "Normal Vector (from Monge form) = "<<valN<< std::endl;
trace.info() << "Normal Vector (linear least square) = "<<valL<< std::endl;
#endif
trace.info() << "Normal Vector (Elementary conv) = "<<valElem<< std::endl;
See also
exampleEstimatorFromSurfelFunctors.cpp