35 #include <boost/program_options/options_description.hpp>
36 #include <boost/program_options/parsers.hpp>
37 #include <boost/program_options/variables_map.hpp>
39 #include "DGtal/base/Common.h"
40 #include "DGtal/base/CountedPtr.h"
41 #include "DGtal/base/CountedConstPtrOrConstPtr.h"
42 #include "DGtal/helpers/StdDefs.h"
43 #include "DGtal/math/Statistic.h"
44 #include "DGtal/math/MPolynomial.h"
45 #include "DGtal/topology/LightImplicitDigitalSurface.h"
46 #include "DGtal/graph/DepthFirstVisitor.h"
47 #include "DGtal/graph/GraphVisitorRange.h"
48 #include "DGtal/geometry/surfaces/estimation/CNormalVectorEstimator.h"
49 #include "DGtal/geometry/surfaces/estimation/VoronoiCovarianceMeasureOnDigitalSurface.h"
50 #include "DGtal/geometry/surfaces/estimation/VCMDigitalSurfaceLocalEstimator.h"
51 #include "DGtal/geometry/surfaces/estimation/TrueDigitalSurfaceLocalEstimator.h"
52 #include "DGtal/geometry/surfaces/estimation/IIGeometricFunctors.h"
53 #include "DGtal/geometry/surfaces/estimation/IntegralInvariantCovarianceEstimator.h"
54 #include "DGtal/geometry/volumes/KanungoNoise.h"
55 #include "DGtal/shapes/GaussDigitizer.h"
56 #include "DGtal/shapes/ShapeGeometricFunctors.h"
57 #include "DGtal/shapes/implicit/ImplicitPolynomial3Shape.h"
58 #include "DGtal/images/ImageContainerBySTLVector.h"
59 #include "DGtal/images/SimpleThresholdForegroundPredicate.h"
60 #include "DGtal/io/readers/MPolynomialReader.h"
61 #include "DGtal/io/colormaps/GradientColorMap.h"
62 #ifdef WITH_VISU3D_QGLVIEWER
63 #include "DGtal/io/viewers/Viewer3D.h"
64 #include "DGtal/io/Display3DFactory.h"
68 using namespace DGtal;
69 namespace po = boost::program_options;
240 template <
typename SCell,
typename RealVector>
241 struct GradientMapAdapter {
242 typedef std::map<SCell,RealVector> SCell2RealVectorMap;
243 typedef SCell Argument;
247 RealVector operator()(
const Argument& arg )
const
249 typename SCell2RealVectorMap::const_iterator it = myMap->find( arg );
250 if ( it != myMap->end() )
return it->second;
256 template <
typename SCellEmbedder>
260 using SCellEmbedder::operator();
264 typedef SCell Argument;
265 typedef RealPoint Value;
267 typedef std::map<SCell,RealVector> SCell2RealVectorMap;
268 typedef GradientMapAdapter<SCell,RealVector> GradientMap;
275 GradientMap gradientMap()
const
277 return GradientMap( myMap );
286 const Estimator& estimator,
287 std::ostream& output )
292 typedef typename Estimator::Quantity Quantity;
293 const KSpace& ks = surface.
container().space();
294 std::map<Surfel,Quantity> normals;
295 for ( ConstIterator it = surface.
begin(), itE = surface.
end(); it != itE; ++it )
297 Quantity n_est = estimator.eval( it );
298 normals[ *it ] = n_est;
301 typedef SCellEmbedderWithNormal< CanonicSCellEmbedder<KSpace> > Embedder;
302 Embedder embedder( surfelEmbedder, normals );
310 template <
typename KSpace,
311 typename ImplicitShape,
313 typename TrueEstimator,
315 void computeEstimation
316 (
const po::variables_map& vm,
318 const ImplicitShape& shape,
319 const Surface& surface,
320 TrueEstimator& true_estimator,
321 Estimator& estimator )
323 typedef typename Surface::Surfel Surfel;
324 typedef typename Estimator::Quantity Quantity;
325 typedef double Scalar;
328 typedef typename VisitorRange::ConstIterator VisitorConstIterator;
330 std::string fname = vm[
"output" ].as<std::string>();
331 string nameEstimator = vm[
"estimator" ].as<
string>();
334 std::vector<Quantity> n_estimations;
335 estimator.eval( range->begin(), range->end(), std::back_inserter( n_estimations ) );
336 trace.
info() <<
"- nb estimations = " << n_estimations.size() << std::endl;
341 std::vector<Quantity> n_true_estimations;
342 true_estimator.eval( range->begin(), range->end(), std::back_inserter( n_true_estimations ) );
343 trace.
info() <<
"- nb estimations = " << n_true_estimations.size() << std::endl;
347 ASSERT( n_estimations.size() == n_true_estimations.size() );
348 for (
unsigned int i = 0; i < n_estimations.size(); ++i )
349 if ( n_estimations[ i ].dot( n_true_estimations[ i ] ) < 0 )
350 n_estimations[ i ] = -n_estimations[ i ];
366 if ( vm.count(
"angle-deviation-stats" ) )
369 std::ostringstream adev_sstr;
370 adev_sstr << fname <<
"-" << nameEstimator <<
"-angle-deviation-"
371 << estimator.h() <<
".txt";
375 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
377 Quantity n_est = n_estimations[ i ];
378 Quantity n_true_est = n_true_estimations[ i ];
379 Scalar angle_error = acos( n_est.dot( n_true_est ) );
383 std::ofstream adev_output( adev_sstr.str().c_str() );
384 adev_output <<
"# Average error X of the absolute angle between two vector estimations." << std::endl;
385 adev_output <<
"# h L1 L2 Loo E[X] Var[X] Min[X] Max[X] Nb[X]" << std::endl;
386 adev_output << estimator.h()
387 <<
" " << adev_stat.
mean()
389 + adev_stat.
mean()*adev_stat.
mean() )
390 <<
" " << adev_stat.
max()
391 <<
" " << adev_stat.
mean()
393 <<
" " << adev_stat.
min()
394 <<
" " << adev_stat.
max()
400 if ( vm[
"export" ].as<string>() !=
"None" )
403 std::ostringstream export_sstr;
404 export_sstr << fname <<
"-" << nameEstimator <<
"-cells-"
405 << estimator.h() <<
".txt";
406 std::ofstream export_output( export_sstr.str().c_str() );
407 export_output <<
"# ImaGene viewer (viewSetOfSurfels) file format for displaying cells." << std::endl;
408 bool adev = vm[
"export" ].as<
string>() ==
"AngleDeviation";
411 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
413 Quantity n_est = n_estimations[ i ];
414 Quantity n_true_est = n_true_estimations[ i ];
415 Scalar angle_error = acos( n_est.dot( n_true_est ) )*180.0 / 3.14159625;
419 <<
" " << min( 1023, max( 512+K.
sKCoord( s, 0 ), 0 ) )
420 <<
" " << min( 1023, max( 512+K.
sKCoord( s, 1 ), 0 ) )
421 <<
" " << min( 1023, max( 512+K.
sKCoord( s, 2 ), 0 ) )
422 <<
" " << K.
sSign( s );
424 if ( adev ) c = grad( max( 0.0, min( angle_error, 40.0 ) ) );
425 export_output <<
" " << ((double) c.
red() / 255.0 )
426 <<
" " << ((
double) c.green() / 255.0 )
427 << " " << ((
double) c.blue() / 255.0 );
428 export_output << " " << n_est[ 0 ] << " " << n_est[ 1 ]
429 << " " << n_est[ 2 ] <<
std::endl;
431 export_output.close();
434 if ( vm.count( "normals" ) )
437 std::ostringstream export_sstr;
438 export_sstr << fname <<
"-" << nameEstimator <<
"-normals-"
439 << estimator.h() <<
".txt";
440 std::ofstream export_output( export_sstr.str().c_str() );
441 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;
444 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
446 Quantity n_est = n_estimations[ i ];
447 Quantity n_true_est = n_true_estimations[ i ];
451 <<
" " << K.
sSign( s )
452 <<
" " << n_est[ 0 ] <<
" " << n_est[ 1 ] <<
" " << n_est[ 2 ]
453 <<
" " << n_true_est[ 0 ] <<
" " << n_true_est[ 1 ] <<
" " << n_true_est[ 2 ]
456 export_output.close();
459 if ( vm.count(
"noff" ) )
462 std::ostringstream export_sstr;
463 export_sstr << fname <<
"-" << nameEstimator <<
"-noff-"
464 << estimator.h() <<
".off";
465 std::ofstream export_output( export_sstr.str().c_str() );
466 std::map<Surfel,Quantity> normals;
469 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
471 Quantity n_est = n_estimations[ i ];
472 normals[ *it ] = n_est;
475 typedef SCellEmbedderWithNormal< CanonicSCellEmbedder<KSpace> > Embedder;
476 Embedder embedder( surfelEmbedder, normals );
477 surface.exportAs3DNOFF( export_output, embedder );
478 export_output.close();
481 #ifdef WITH_VISU3D_QGLVIEWER
482 if ( vm[
"view" ].as<string>() !=
"None" )
488 char name[] =
"Viewer";
492 QApplication application( argc, argv );
493 MyViewever3D viewer( K );
495 viewer <<
SetMode3D( s.className(),
"Basic" );
497 bool adev = vm[
"view" ].as<
string>() ==
"AngleDeviation";
501 for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
503 Quantity n_est = n_estimations[ i ];
504 Quantity n_true_est = n_true_estimations[ i ];
505 Scalar angle_error = acos( n_est.dot( n_true_est ) )*180.0 / 3.14159625;
508 if ( adev ) c = grad( max( 0.0, min( angle_error, 40.0 ) ) );
509 viewer.setFillColor( c );
510 MyDisplay3DFactory::drawOrientedSurfelWithNormal( viewer, s, n_est,
false );
513 viewer << MyViewever3D::updateDisplay;
520 template <
typename KSpace,
521 typename ImplicitShape,
523 typename KernelFunction,
524 typename PointPredicate>
526 (
const po::variables_map& vm,
528 const ImplicitShape& shape,
529 const Surface& surface,
530 const KernelFunction& chi,
531 const PointPredicate& ptPred )
533 string nameEstimator = vm[
"estimator" ].as<
string>();
534 double h = vm[
"gridstep"].as<
double>();
537 TrueEstimator true_estimator;
538 true_estimator.attach( shape );
539 true_estimator.setParams( K, NormalFunctor(), 20, 0.1, 0.01 );
540 true_estimator.
init( h, surface.begin(), surface.end() );
541 if ( nameEstimator ==
"True" )
546 estimator.
attach( shape );
547 estimator.setParams( K, NormalFunctor(), 20, 0.1, 0.01 );
548 estimator.init( h, surface.begin(), surface.end() );
550 computeEstimation( vm, K, shape, surface, true_estimator, estimator );
552 else if ( nameEstimator ==
"VCM" )
556 typedef typename Surface::DigitalSurfaceContainer SurfaceContainer;
559 KernelFunction> VCMOnSurface;
562 KernelFunction, NormalFunctor> VCMNormalEstimator;
563 int embedding = vm[
"embedding"].as<
int>();
566 double R = vm[
"R-radius"].as<
double>();
567 double r = vm[
"r-radius"].as<
double>();
568 double t = vm[
"trivial-radius"].as<
double>();
569 double alpha = vm[
"alpha"].as<
double>();
570 if ( alpha != 0.0 ) R *= pow( h, alpha-1.0 );
571 if ( alpha != 0.0 ) r *= pow( h, alpha-1.0 );
572 trace.
info() <<
"- R=" << R <<
" r=" << r <<
" t=" << t << std::endl;
573 VCMNormalEstimator estimator;
574 estimator.attach( surface );
575 estimator.setParams( embType, R, r, chi, t, Metric(),
true );
576 estimator.init( h, surface.begin(), surface.end() );
578 computeEstimation( vm, K, shape, surface, true_estimator, estimator );
580 else if ( nameEstimator ==
"II" )
586 typedef typename Domain::ConstIterator DomainConstIterator;
590 double r = vm[
"r-radius"].as<
double>();
591 double alpha = vm[
"alpha"].as<
double>();
592 if ( alpha != 0.0 ) r *= pow( h, alpha-1.0 );
596 Image image( domain );
597 for ( DomainConstIterator it = domain.begin(), itE = domain.end(); it != itE; ++it )
599 image.setValue( *it, ptPred( *it ) );
603 ThresholdedImage thresholdedImage( image,
false );
604 IINormalEstimator ii_estimator( K, thresholdedImage );
605 ii_estimator.setParams( r );
606 ii_estimator.init( h, surface.begin(), surface.end() );
609 computeEstimation( vm, K, shape, surface, true_estimator, ii_estimator );
614 template <
typename KSpace,
615 typename ImplicitShape,
617 typename PointPredicate>
619 (
const po::variables_map& vm,
621 const ImplicitShape& shape,
622 const Surface& surface,
623 const PointPredicate& ptPred )
625 string kernel = vm[
"kernel" ].as<
string>();
626 double h = vm[
"gridstep"].as<
double>();
627 double r = vm[
"r-radius"].as<
double>();
628 double alpha = vm[
"alpha"].as<
double>();
629 if ( alpha != 0.0 ) r *= pow( h, alpha-1.0 );
630 if ( kernel ==
"hat" ) {
633 KernelFunction chi_r( 1.0, r );
634 chooseEstimator( vm, K, shape, surface, chi_r, ptPred );
635 }
else if ( kernel ==
"ball" ) {
638 KernelFunction chi_r( 1.0, r );
639 chooseEstimator( vm, K, shape, surface, chi_r, ptPred );
643 template <
typename KSpace,
644 typename ImplicitShape,
645 typename ImplicitDigitalShape >
647 (
const po::variables_map& vm,
649 const ImplicitShape& shape,
650 const ImplicitDigitalShape& dshape )
653 typedef double Scalar;
654 Scalar noiseLevel = vm[
"noise" ].as<
double>();
655 if ( noiseLevel == 0.0 )
660 typedef typename Surface::Surfel Surfel;
666 trace.
error() <<
"ERROR Unable to find bel." << std::endl;
669 SurfaceContainer* surfaceContainer =
new SurfaceContainer( K, dshape, surfAdj, bel );
671 trace.
info() <<
"- surface component has " << ptrSurface->size() <<
" surfels." << std::endl;
673 chooseKernel( vm, K, shape, *ptrSurface, dshape );
678 typedef typename ImplicitDigitalShape::Domain Domain;
682 typedef typename Surface::Surfel Surfel;
685 const Domain shapeDomain = dshape.getDomain();
686 KanungoPredicate* noisified_dshape =
new KanungoPredicate( dshape, shapeDomain, noiseLevel );
689 double minsize = dshape.getUpperBound()[0] - dshape.getLowerBound()[0];
690 unsigned int nb_surfels = 0;
691 unsigned int tries = 0;
696 trace.
error() <<
"ERROR Unable to find bel." << std::endl;
699 SurfaceContainer* surfaceContainer =
new SurfaceContainer( K, *noisified_dshape, surfAdj, bel );
701 nb_surfels = ptrSurface->size();
702 }
while ( ( nb_surfels < 2 * minsize ) && ( tries++ < 150 ) );
705 trace.
error() <<
"ERROR cannot find a proper bel in a big enough component." << std::endl;
708 trace.
info() <<
"- surface component has " << nb_surfels <<
" surfels." << std::endl;
710 chooseKernel( vm, K, shape, *ptrSurface, *noisified_dshape );
717 int main(
int argc,
char** argv )
720 namespace po = boost::program_options;
721 po::options_description general_opt(
"Allowed options are: ");
722 general_opt.add_options()
723 (
"help,h",
"display this message")
724 (
"polynomial,p", po::value<string>(),
"the implicit polynomial whose zero-level defines the shape of interest." )
725 (
"noise,N", po::value<double>()->default_value( 0.0 ),
"the Kanungo noise level l=arg, with l^d the probability that a point at distance d is flipped inside/outside." )
726 (
"minAABB,a", po::value<double>()->default_value( -10.0 ),
"the min value of the AABB bounding box (domain)" )
727 (
"maxAABB,A", po::value<double>()->default_value( 10.0 ),
"the max value of the AABB bounding box (domain)" )
728 (
"gridstep,g", po::value< double >()->default_value( 1.0 ),
"the gridstep that defines the digitization (often called h). " )
729 (
"estimator,e", po::value<string>()->default_value(
"True" ),
"the chosen normal estimator: True | VCM | II | Trivial" )
730 (
"R-radius,R", po::value<double>()->default_value( 5 ),
"the constant for parameter R in R(h)=R h^alpha (VCM)." )
731 (
"r-radius,r", po::value<double>()->default_value( 3 ),
"the constant for parameter r in r(h)=r h^alpha (VCM,II,Trivial)." )
732 (
"kernel,k", po::value<string>()->default_value(
"hat" ),
"the function chi_r, either hat or ball." )
733 (
"alpha", po::value<double>()->default_value( 0.0 ),
"the parameter alpha in r(h)=r h^alpha (VCM)." )
734 (
"trivial-radius,t", po::value<double>()->default_value( 3 ),
"the parameter t defining the radius for the Trivial estimator. Also used for reorienting the VCM." )
735 (
"embedding,E", po::value<int>()->default_value( 0 ),
"the surfel -> point embedding for VCM estimator: 0: Pointels, 1: InnerSpel, 2: OuterSpel." )
736 (
"output,o", po::value<string>()->default_value(
"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." )
737 (
"angle-deviation-stats,S",
"computes angle deviation error and outputs them in file <basename>-angle-deviation-<gridstep>.txt, as specified by -o <basename>." )
738 (
"export,x", po::value<string>()->default_value(
"None" ),
"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" )
739 (
"normals,n",
"outputs every surfel, its estimated normal, and the ground truth normal in file <basename>-normals-<gridstep>.txt, as specified by -o <basename>." )
740 (
"noff,O",
"exports the digital surface with normals as NOFF file <basename>-noff-<gridstep>.off, as specified by -o <basename>.." )
741 #ifdef WITH_VISU3D_QGLVIEWER
742 (
"view,V", po::value<string>()->default_value(
"None" ),
"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." )
746 po::variables_map vm;
748 po::store(po::parse_command_line(argc, argv, general_opt), vm);
749 }
catch(
const exception& ex){
751 cerr <<
"Error checking program options: "<< ex.what()<< endl;
754 if( ! parseOK || vm.count(
"help") || ! vm.count(
"polynomial" ) )
756 if ( ! vm.count(
"polynomial" ) )
757 cerr <<
"Need parameter --polynomial / -p" << endl;
759 cerr <<
"Usage: " << argv[0] <<
" -p <polynomial> [options]\n"
760 <<
"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."
762 << general_opt <<
"\n";
763 cerr <<
"Example of use:\n"
764 <<
"./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
765 <<
"Example of implicit surfaces (specified by -p):" << endl
766 <<
" - ellipse : 90-3*x^2-2*y^2-z^2 " << endl
767 <<
" - torus : -1*(x^2+y^2+z^2+6*6-2*2)^2+4*6*6*(x^2+y^2) " << endl
768 <<
" - rcube : 6561-x^4-y^4-z^4" << endl
769 <<
" - goursat : 8-0.03*x^4-0.03*y^4-0.03*z^4+2*x^2+2*y^2+2*z^2" << endl
770 <<
" - distel : 10000-(x^2+y^2+z^2+1000*(x^2+y^2)*(x^2+z^2)*(y^2+z^2))" << endl
771 <<
" - leopold : 100-(x^2*y^2*z^2+4*x^2+4*y^2+3*z^2)" << endl
772 <<
" - diabolo : x^2-(y^2+z^2)^2" << endl
773 <<
" - heart : -1*(x^2+2.25*y^2+z^2-1)^3+x^2*z^3+0.1125*y^2*z^3" << endl
774 <<
" - crixxi : -0.9*(y^2+z^2-1)^2-(x^2+y^2-1)^3" << endl << endl;
775 cerr <<
"Implemented estimators (specified by -e):" << endl
776 <<
" - True : supposed to be the ground truth for computations. Of course, it is only approximations." << endl
777 <<
" - VCM : normal estimator by digital Voronoi Covariance Matrix. Radii parameters are given by -R, -r." << endl
778 <<
" - II : normal estimator by Integral Invariants. Radius parameter is given by -r." << endl
779 <<
" - Trivial : the normal obtained by average trivial surfel normals in a ball neighborhood. Radius parameter is given by -r." << endl
781 cerr <<
"Note:" << endl
782 <<
" - 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
783 <<
" - 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;
789 typedef double Scalar;
793 string poly_str = vm[
"polynomial" ].as<
string>();
795 Polynomial3Reader reader;
796 string::const_iterator iter = reader.read( poly, poly_str.begin(), poly_str.end() );
797 if ( iter != poly_str.end() )
799 trace.
error() <<
"ERROR reading polynomial: I read only <" << poly_str.substr( 0, iter - poly_str.begin() )
800 <<
">, and I built P=" << poly << std::endl;
810 typedef ImplicitDigitalShape::Domain Domain;
811 Scalar min_x = vm[
"minAABB" ].as<
double>();
812 Scalar max_x = vm[
"maxAABB" ].as<
double>();
813 Scalar h = vm[
"gridstep" ].as<
double>();
814 RealPoint p1( min_x, min_x, min_x );
815 RealPoint p2( max_x, max_x, max_x );
817 dshape->attach( *shape );
818 dshape->init( p1, p2, h );
819 Domain domain = dshape->getDomain();
821 K.
init( domain.lowerBound(), domain.upperBound(), true );
822 trace.
info() <<
"- domain is " << domain << std::endl;
825 chooseSurface( vm, K, *shape, *dshape );
Sign sSign(const SCell &c) const
void beginBlock(const std::string &keyword="")
void attach(ConstAlias< Shape > aShape)
DigitalSurfaceContainer::SurfelConstIterator ConstIterator
SpaceND< 2, Integer > Space
const Point & lowerBound() const
const KSpace & space() const
bool init(const Point &lower, const Point &upper, bool isClosed)
void addValue(Quantity v)
const Point & upperBound() const
DigitalSurfaceContainer::KSpace KSpace
Trace trace(traceWriterTerm)
Integer sKCoord(const SCell &c, Dimension k) const
unsigned int samples() const
typename Self::Point Point
PointVector< dim, double > RealVector
double unbiasedVariance() const
const DigitalSurfaceContainer & container() const
void exportAs3DNOFF(std::ostream &out, const SCellEmbedderWithGradientMap &scembedder) const
Space::RealVector RealVector
void red(const unsigned char aRedValue)
ConstIterator begin() const
DigitalSurfaceContainer::Surfel Surfel
ConstIterator end() const
typename Self::Domain Domain