DGtal  1.2.0
Local digital surface estimators from surfel functors
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:

  • a digital surface in which the computation is performed;
  • a functor to call on surfels in the neighborhood;
  • a metric to specify the shape of the neighborhood to consider;
  • a radius to define the neighborhood (ball for the specified metric);
  • as any estimator, a gridstep h.

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:

  • init(h,radius): to init the estimators for a given gridstep and a given kernel neighborhood.
  • eval( aSurfelIterator ): the evaluate the functor at aSurfelIterator.
  • eval( aSurfelItBegin, aSurfelItEnd): evaluate the estimator on a range of surfels.

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;
Point p1( -10, -10, -10 );
Point p2( 10, 10, 10 );
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
bool init(const Point &lower, const Point &upper, bool isClosed)
Specifies the upper and lower bounds for the maximal cells in this space.
static SCell findABel(const KSpace &K, const PointPredicate &pp, unsigned int nbtries=1000)
SH3::DigitalSurface Surface
MyPointD Point
Definition: testClone2.cpp:383
KSpace K

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, LpMetric<Z3i::Space>, FunctorGaussian, ConstFunctor> ReporterK;
typedef LocalEstimatorFromSurfelFunctorAdapter<SurfaceContainer, LpMetric<Z3i::Space>, FunctorMean, ConstFunctor> ReporterH;
typedef LocalEstimatorFromSurfelFunctorAdapter<SurfaceContainer, LpMetric<Z3i::Space>, FunctorNormal, ConstFunctor> ReporterNormal;
typedef LocalEstimatorFromSurfelFunctorAdapter<SurfaceContainer, LpMetric<Z3i::Space>, FunctorNormalLeast, ConstFunctor> ReporterNormalLeast;
#endif
typedef functors::ElementaryConvolutionNormalVectorEstimator<Surfel, CanonicSCellEmbedder<KSpace> > FunctorNormalElem;
typedef LocalEstimatorFromSurfelFunctorAdapter<SurfaceContainer, LpMetric<Z3i::Space>,
FunctorNormalElem, DGtal::functors::GaussianKernel> ReporterNormalElem;
Aim: Estimates Gaussian curvature using CGAL Jet Fitting and Monge Form.
Aim: defines a functor on double numbers which corresponds to a Gaussian convolution kernel....

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
LpMetric<Z3i::Space> l2(2.0);
FunctorNormalElem estimatorNormalElem(embedder,1.0);
ReporterNormalElem reporterElem(surface, l2,
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(l2, estimatorK, constFunctor, 5.0);
reporterH.setParams(l2, estimatorH, constFunctor, 5.0);
reporterN.setParams(l2, estimatorN, constFunctor, 5.0);
reporterL.setParams(l2, 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(l2,
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;
std::ostream & info()
Trace trace
Definition: Common.h:154
See also
exampleEstimatorFromSurfelFunctors.cpp