void computeEstimation
( const po::variables_map& vm,     //< command-line parameters
  const KSpace& K,                 //< cellular grid space
  const ImplicitShape& shape,      //< implicit shape "ground truth"
  const Surface& surface,          //< digital surface approximating shape
  TrueEstimator& true_estimator,   //< "ground truth" estimator
  Estimator& estimator )           //< an initialized estimator
{
  typedef typename Surface::ConstIterator ConstIterator;
  typedef typename Surface::Surfel Surfel;
  typedef typename Estimator::Quantity Quantity;
  typedef double Scalar;
  typedef DepthFirstVisitor< Surface > Visitor;
  typedef GraphVisitorRange< Visitor > VisitorRange;
  typedef typename VisitorRange::ConstIterator VisitorConstIterator;
  
  std::string fname = vm[ "output" ].as<std::string>();
  string nameEstimator = vm[ "estimator" ].as<string>();
  trace.beginBlock( "Computing " + nameEstimator + "estimations." );
  CountedPtr<VisitorRange> range( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
  std::vector<Quantity> n_estimations;
  estimator.eval( range->begin(), range->end(), std::back_inserter( n_estimations ) );
  trace.info() << "- nb estimations  = " << n_estimations.size() << std::endl;
  trace.endBlock();

  trace.beginBlock( "Computing ground truth." );
  range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
  std::vector<Quantity> n_true_estimations;
  true_estimator.eval( range->begin(), range->end(), std::back_inserter( n_true_estimations ) );
  trace.info() << "- nb estimations  = " << n_true_estimations.size() << std::endl;
  trace.endBlock();

  trace.beginBlock( "Correcting orientations." );
  ASSERT( n_estimations.size() == n_true_estimations.size() );
  for ( unsigned int i = 0; i < n_estimations.size(); ++i )
    if ( n_estimations[ i ].dot( n_true_estimations[ i ] ) < 0 )
      n_estimations[ i ] = -n_estimations[ i ];
  trace.endBlock();

  DGtal::GradientColorMap<double> grad( 0.0, 40.0 );
  // 0 metallic blue, 5 light cyan, 10 light green, 15 light
  // yellow, 20 yellow, 25 orange, 30 red, 35, dark red, 40- grey
  grad.addColor( DGtal::Color( 128, 128, 255 ) ); // 0
  grad.addColor( DGtal::Color( 128, 255, 255 ) ); // 5
  grad.addColor( DGtal::Color( 128, 255, 128 ) ); // 10
  grad.addColor( DGtal::Color( 255, 255, 128 ) ); // 15
  grad.addColor( DGtal::Color( 255, 255, 0   ) ); // 20
  grad.addColor( DGtal::Color( 255, 128, 0   ) ); // 25
  grad.addColor( DGtal::Color( 255,   0, 0   ) ); // 30
  grad.addColor( DGtal::Color( 128,   0, 0   ) ); // 35
  grad.addColor( DGtal::Color( 128, 128, 128 ) ); // 40

  if ( vm.count( "angle-deviation-stats" ) )
    {
      trace.beginBlock( "Computing angle deviation error stats." );
      std::ostringstream adev_sstr;
      adev_sstr << fname << "-" << nameEstimator << "-angle-deviation-" 
                << estimator.h() << ".txt"; 
      DGtal::Statistic<Scalar> adev_stat;
      unsigned int i = 0;
      range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
      for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
        {
          Quantity n_est = n_estimations[ i ];
          Quantity n_true_est = n_true_estimations[ i ];
          Scalar angle_error = acos( n_est.dot( n_true_est ) );
          adev_stat.addValue( angle_error );
        }
      adev_stat.terminate();
      std::ofstream adev_output( adev_sstr.str().c_str() );
      adev_output << "# Average error X of the absolute angle between two vector estimations." << std::endl;
      adev_output << "# h L1 L2 Loo E[X] Var[X] Min[X] Max[X] Nb[X]" << std::endl;
      adev_output << estimator.h() 
                  << " " << adev_stat.mean() // L1
                  << " " << sqrt( adev_stat.unbiasedVariance()
                                  + adev_stat.mean()*adev_stat.mean() ) // L2
                  << " " << adev_stat.max() // Loo
                  << " " << adev_stat.mean() // E[X] (=L1)
                  << " " << adev_stat.unbiasedVariance() // Var[X]
                  << " " << adev_stat.min() // Min[X]
                  << " " << adev_stat.max() // Max[X]
                  << " " << adev_stat.samples() // Nb[X]
                  << std::endl;
      adev_output.close();
      trace.endBlock();
    }
  if ( vm[ "export" ].as<string>() != "None" )
    {
      trace.beginBlock( "Exporting cell geometry." );
      std::ostringstream export_sstr;
      export_sstr << fname << "-" << nameEstimator << "-cells-" 
                  << estimator.h() << ".txt"; 
      std::ofstream export_output( export_sstr.str().c_str() );
      export_output << "# ImaGene viewer (viewSetOfSurfels) file format for displaying cells." << std::endl;
      bool adev =  vm[ "export" ].as<string>() == "AngleDeviation";
      unsigned int i = 0;
      range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
      for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
        {
          Quantity n_est = n_estimations[ i ];
          Quantity n_true_est = n_true_estimations[ i ];
          Scalar angle_error = acos( n_est.dot( n_true_est ) )*180.0 / 3.14159625;
          Surfel s = *it;
          export_output
            << "CellN" 
            << " " << min( 1023, max( 512+K.sKCoord( s, 0 ), 0 ) )
            << " " << min( 1023, max( 512+K.sKCoord( s, 1 ), 0 ) )
            << " " << min( 1023, max( 512+K.sKCoord( s, 2 ), 0 ) )
            << " " << K.sSign( s );
          Color c = grad( 0 );
          if ( adev ) c = grad( max( 0.0, min( angle_error, 40.0 ) ) );
          export_output << " " << ((double) c.red() / 255.0 )
                        << " " << ((double) c.green() / 255.0 )
                        << " " << ((double) c.blue() / 255.0 );
          export_output << " " << n_est[ 0 ] << " " << n_est[ 1 ] 
                        << " " << n_est[ 2 ] << std::endl;
        }
      export_output.close();
      trace.endBlock();
    }
  if ( vm.count( "normals" ) )
    {
      trace.beginBlock( "Exporting cells normals." );
      std::ostringstream export_sstr;
      export_sstr << fname << "-" << nameEstimator << "-normals-" 
                  << estimator.h() << ".txt"; 
      std::ofstream export_output( export_sstr.str().c_str() );
      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;
      unsigned int i = 0;
      range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
      for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
        {
          Quantity n_est = n_estimations[ i ];
          Quantity n_true_est = n_true_estimations[ i ];
          Surfel s = *it;
          export_output
            << K.sKCoord( s, 0 ) << " " << K.sKCoord( s, 1 ) << " " << K.sKCoord( s, 2 ) 
            << " " << K.sSign( s )
            << " " << n_est[ 0 ] << " " << n_est[ 1 ] << " " << n_est[ 2 ]
            << " " << n_true_est[ 0 ] << " " << n_true_est[ 1 ] << " " << n_true_est[ 2 ]
            << std::endl;
        }
      export_output.close();
      trace.endBlock();
    }
  if ( vm.count( "noff" ) )
    {
      trace.beginBlock( "Exporting NOFF file." );
      std::ostringstream export_sstr;
      export_sstr << fname << "-" << nameEstimator << "-noff-" 
                  << estimator.h() << ".off"; 
      std::ofstream export_output( export_sstr.str().c_str() );
      std::map<Surfel,Quantity> normals;
      unsigned int i = 0;
      range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
      for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
        {
          Quantity n_est = n_estimations[ i ];
          normals[ *it ] = n_est;
        }
      CanonicSCellEmbedder<KSpace> surfelEmbedder( K );
      typedef SCellEmbedderWithNormal< CanonicSCellEmbedder<KSpace> > Embedder;
      Embedder embedder( surfelEmbedder, normals );
      surface.exportAs3DNOFF( export_output, embedder );
      export_output.close();
      trace.endBlock();
    }
#ifdef WITH_VISU3D_QGLVIEWER
  if ( vm[ "view" ].as<string>() != "None" )
    {
      typedef typename KSpace::Space Space;
      typedef Viewer3D<Space,KSpace> MyViewever3D;
      typedef Display3DFactory<Space,KSpace> MyDisplay3DFactory;
      int argc = 1;
      char name[] = "Viewer";
      char* argv[ 1 ];
      argv[ 0 ] = name;
      Surfel s;
      QApplication application( argc, argv );
      MyViewever3D viewer( K );
      viewer.show();
      viewer << SetMode3D( s.className(), "Basic" );
      trace.beginBlock( "Viewing surface." );
      bool adev =  vm[ "view" ].as<string>() == "AngleDeviation";

      unsigned int i = 0;
      range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
      for ( VisitorConstIterator it = range->begin(), itE = range->end(); it != itE; ++it, ++i )
        {
          Quantity n_est = n_estimations[ i ];
          Quantity n_true_est = n_true_estimations[ i ];
          Scalar angle_error = acos( n_est.dot( n_true_est ) )*180.0 / 3.14159625;
          s = *it;
          Color c = grad( 0 );
          if ( adev ) c = grad( max( 0.0, min( angle_error, 40.0 ) ) );
          viewer.setFillColor( c );
          MyDisplay3DFactory::drawOrientedSurfelWithNormal( viewer, s, n_est, false );
        }
      trace.endBlock();
      viewer << MyViewever3D::updateDisplay;
      application.exec();
    }
#endif

}
bool testCellularGridSpaceND()
{
  typedef typename KSpace::Cell Cell;
  typedef typename KSpace::SCell SCell;
  typedef typename KSpace::Point Point;
  typedef typename KSpace::DirIterator DirIterator;
  typedef typename KSpace::Cells Cells;
  typedef typename KSpace::SCells SCells;
  unsigned int nbok = 0;
  unsigned int nb = 0;
  
  trace.beginBlock ( "Testing block KSpace instantiation and scan ..." );
  KSpace K;
  int xlow[ 4 ] = { -3, -2, -2, -1 };
  int xhigh[ 4 ] = { 5, 3, 2, 3 };
  Point low( xlow );
  Point high( xhigh ); 
  bool space_ok = K.init( low, high, true );
  nbok += space_ok ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
         << "K.init( low, high )" << std::endl;
  trace.info() << "K.dim()=" << K.dimension << endl;
  int spel[ 4 ] = { 1, 1, 1, 1 }; // pixel
  Point kp( spel );
  Cell center = K.uCell( kp );
  Cell c1 = K.uCell( kp );
  Cell clow = K.uCell( low, kp );
  Cell chigh = K.uCell( high, kp );
  trace.info() << c1 << clow << chigh 
         << " topo(c1)=" << K.uTopology( c1 ) << " dirs=";
  for ( DirIterator q = K.uDirs( clow ); q != 0; ++q )
    trace.info() << " " << *q;
  trace.info() << endl;
  Cell f = K.uFirst( c1 );
  Cell l = K.uLast( c1 );
  trace.info() << "Loop in " << clow << chigh << endl;
  c1 = f;
  unsigned int nbelems = 0;
  do {
    ++nbelems;
    // trace.info() << c1;
  } while ( K.uNext( c1, f, l ) );
  trace.info() << " -> " << nbelems << " elements." << endl;
  unsigned int exp_nbelems = 1;
  for ( Dimension i = 0; i < K.dimension; ++i )
    exp_nbelems *= K.size( i );
  nbok += nbelems == exp_nbelems ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
         << nbelems << " scanned elements == "
         << exp_nbelems << " space size."
         << std::endl;
  trace.endBlock();
  trace.beginBlock ( "Testing neighborhoods in KSpace..." );
  Cells N = K.uNeighborhood( center );
  nbok += N.size() == ( K.dimension*2 + 1 ) ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
         << N.size() << "(neighborhood size) == "
         << ( K.dimension*2 + 1 ) << "(2*dim()+1)" << endl;
  Cells Np = K.uProperNeighborhood( center );
  nbok += Np.size() == ( K.dimension*2 ) ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
         << Np.size() << "(proper neighborhood size) == "
         << ( K.dimension*2 ) << "(2*dim())" << endl;
  trace.endBlock();

  trace.beginBlock ( "Testing faces in KSpace..." );
  Cells Nf = K.uFaces( center );
  nbok += Nf.size() == ceil( std::pow( 3.0 ,(int) K.dimension ) - 1 ) ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
         << Nf.size() << "(faces size) == "
         << floor( std::pow( 3.0, (int)K.dimension ) - 1 ) << "(3^dim()-1)" << endl;
  trace.endBlock();
  
  trace.beginBlock ( "Testing block Incidence in KSpace..." );
  SCell sspel = K.sCell( kp, K.POS );
  for ( DirIterator q1 = K.sDirs( sspel ); q1 != 0; ++q1 )
    for ( DirIterator q2 = K.sDirs( sspel ); q2 != 0; ++q2 )
      {
  if ( *q1 != *q2 )
    {
      SCell s0 = K.sIncident( sspel, *q1, true );
      SCell s1 = K.sIncident( sspel, *q2, true );
      SCell l10 = K.sIncident( s0, *q2, true );
      SCell l01 = K.sIncident( s1, *q1, true );
      trace.info() << "D+_" << *q2 << "(D+_" << *q1 << "(V))=" << l10 
       << " D+_" << *q1 << "(D+_" << *q2 << "(V))=" << l01
       << endl;
      nbok += l10 == K.sOpp( l01 ) ? 1 : 0; 
      nb++;
    }
      }
  trace.info() << "(" << nbok << "/" << nb << ") "
         << "anti-commutativity of incidence operators." << std::endl;
  trace.endBlock();

  trace.beginBlock ( "Testing direct Incidence in KSpace..." );
  for ( DirIterator q1 = K.sDirs( sspel ); q1 != 0; ++q1 )
    for ( DirIterator q2 = K.sDirs( sspel ); q2 != 0; ++q2 )
      {
  if ( *q1 != *q2 )
    {
      SCell s0 = K.sDirectIncident( sspel, *q1 );
      SCell l10 = K.sDirectIncident( s0, *q2 );
      SCell s1 = K.sDirectIncident( sspel, *q2 );
      SCell l01 = K.sDirectIncident( s1, *q1 );
      trace.info() << "Dd_" << *q2 << "(Dd_" << *q1 << "(V))=" << l10 
       << " Dd_" << *q1 << "(Dd_" << *q2 << "(V))=" << l01
       << endl;
      nbok += l10 != l01 ? 1 : 0; 
      nbok += K.sSign( s0 ) == K.POS ? 1 : 0;
      nbok += K.sSign( s1 ) == K.POS ? 1 : 0;
      nbok += K.sSign( l10 ) == K.POS ? 1 : 0;
      nbok += K.sSign( l01 ) == K.POS ? 1 : 0;
      nbok += s0 == K.sIncident( sspel, *q1, K.sDirect( sspel, *q1 ) )
        ? 1 : 0;
      nbok += s1 == K.sIncident( sspel, *q2, K.sDirect( sspel, *q2 ) )
        ? 1 : 0;
      nbok += l10 == K.sIncident( s0, *q2, K.sDirect( s0, *q2 ) )
        ? 1 : 0;
      nbok += l01 == K.sIncident( s1, *q1, K.sDirect( s1, *q1 ) )
        ? 1 : 0;
      nb += 9;
    }
      }
  trace.info() << "(" << nbok << "/" << nb << ") "
         << "correctness of direct and indirect orientations." << std::endl;
  
  trace.endBlock();
  
  
  return nbok == nb;
}