Exemple #1
0
void
ballGenerator(const int& size, double aCx, double aCy, double aR, GridCurve<TKSpace>& gc)
{

  ASSERT( aR < (double) size ); 
 
  // Types
  typedef TKSpace KSpace;  
  typedef typename KSpace::SCell SCell;
  typedef typename KSpace::Space Space;  
  typedef typename Space::Point Point;

  KSpace K;
  bool ok = K.init( Point(-size,-size), Point(size,size), true );
  if ( ! ok )
    {
      std::cerr << " error in creating KSpace." << std::endl;
    }
  try 
    {
      BallPredicate<Point> dig(aCx, aCy, aR); 
      // Extracts shape boundary
      SurfelAdjacency<KSpace::dimension> SAdj( true );
      SCell bel = Surfaces<KSpace>::findABel( K, dig, 10000 );
      // Getting the consecutive surfels of the 2D boundary
      std::vector<Point> points;
      Surfaces<KSpace>::track2DBoundaryPoints( points, K, SAdj, dig, bel );
      gc.initFromVector(points); 
    }
  catch ( InputException e )
    {
      std::cerr << " error in finding a bel." << std::endl;
    }
}
int main( int argc, char** argv )
{
  if ( argc < 4 )
    {
      usage( argc, argv );
      return 1;
    }
  std::string inputFilename = argv[ 1 ];
  unsigned int minThreshold = atoi( argv[ 2 ] );
  unsigned int maxThreshold = atoi( argv[ 3 ] );

  //! [volScanBoundary-readVol]
  trace.beginBlock( "Reading vol file into an image." );
  typedef ImageSelector < Domain, int>::Type Image;
  Image image = VolReader<Image>::importVol(inputFilename);
  DigitalSet set3d (image.domain());
  SetFromImage<DigitalSet>::append<Image>(set3d, image, 
                                          minThreshold, maxThreshold);
  trace.endBlock();
  //! [volScanBoundary-readVol]
  
  
  //! [volScanBoundary-KSpace]
  trace.beginBlock( "Construct the Khalimsky space from the image domain." );
  KSpace ks;
  bool space_ok = ks.init( image.domain().lowerBound(), 
                           image.domain().upperBound(), true );
  if (!space_ok)
    {
      trace.error() << "Error in the Khamisky space construction."<<std::endl;
      return 2;
    }
  trace.endBlock();
  //! [volScanBoundary-KSpace]

  //! [volScanBoundary-ExtractingSurface]
  trace.beginBlock( "Extracting boundary by scanning the space. " );
  KSpace::SCellSet boundary;
  Surfaces<KSpace>::sMakeBoundary( boundary, ks, set3d,
                                   image.domain().lowerBound(), 
                                   image.domain().upperBound() );
  trace.endBlock();
  //! [volScanBoundary-ExtractingSurface]

  //! [volScanBoundary-DisplayingSurface]
  trace.beginBlock( "Displaying surface in Viewer3D." );
  QApplication application(argc,argv);
  Viewer3D viewer;
  viewer.show(); 
  viewer << CustomColors3D(Color(250, 0, 0 ), Color( 128, 128, 128 ) );
  unsigned long nbSurfels = 0;
  for ( KSpace::SCellSet::const_iterator it = boundary.begin(),
          it_end = boundary.end(); it != it_end; ++it, ++nbSurfels )
    viewer << *it;
  viewer << Viewer3D::updateDisplay;
  trace.info() << "nb surfels = " << nbSurfels << std::endl;
  trace.endBlock();
  return application.exec();
  //! [volScanBoundary-DisplayingSurface]
}
bool testFindABel()
{
  typedef typename KSpace::Point Point;
  typedef SpaceND< KSpace::dimension, typename KSpace::Integer > Space;
  typedef HyperRectDomain<Space> Domain;
  typedef typename DigitalSetSelector< Domain, BIG_DS+HIGH_BEL_DS >::Type DigitalSet;
  typedef typename KSpace::SCell SCell;
  typedef SetPredicate<DigitalSet> PointPredicate;

  trace.beginBlock("Test FindABel");
  Point low(-3,-3,-3), high(3,3,3);
  Domain domain( low, high );
  DigitalSet shape_set( domain );
  PointPredicate pp( shape_set );
  KSpace K;
  K.init( low, high, true );

  Point p000(0,0,0), p001(0,0,1), p010(0,1,0), p011(0,1,1),
        p100(1,0,0), p101(1,0,1), p110(1,1,0), p111(1,1,1);

  shape_set.insert( p000 );
  shape_set.insert( p100 );

  Surfaces<KSpace>::findABel( K, pp , p000 , p011 );
  Surfaces<KSpace>::findABel( K, pp , p000 , p110 );
  Surfaces<KSpace>::findABel( K, pp , p000 , p111 );
  Surfaces<KSpace>::findABel( K, pp , p000 , p101 );
  SCell s010 = Surfaces<KSpace>::findABel( K, pp , p000 , p010 );
  SCell s001 = Surfaces<KSpace>::findABel( K, pp , p000 , p001 );

  trace.endBlock();
  return ( (s010 == SCell( Point(1,2,1), true  ) ) &&
           (s001 == SCell( Point(1,1,2), false ) ) );
}
Exemple #4
0
bool testNormaliation()
{
  trace.beginBlock ( "Testing normalization ..." );

  Point p1( 0, 0, 0 );
  Point p2( 0, 10 , 0);
  Point p3( 10, 10, 0);
  Point p4(10, 0, 100 );
  Point p5( 20, 0 , 0);
  Point p6( 20, 10, 0);
  
  KSpace k;

  k.init(Point(2,2,2), Point(4,4,4), true);

  Board3D<Space,KSpace> board(k);
  
  board << p1<<p2<<p3<<p4; 

  board.saveOBJ("dgtalBoard3D-norm.obj", true);
  board.saveOBJ("dgtalBoard3D-wonorm.obj");

  trace.endBlock();

  return true;
}
void displayProj2d( Viewer3D<space, kspace> & viewer,
		    const KSpace & ks, const StandardDSS6Computer & dss3d,
		    const DGtal::Color & color2d )
{
  typedef typename StandardDSS6Computer::ArithmeticalDSSComputer2d ArithmeticalDSSComputer2d;
  typedef typename ArithmeticalDSSComputer2d::ConstIterator ConstIterator2d;
  typedef typename ArithmeticalDSSComputer2d::Point Point2d;
  typedef typename KSpace::Cell Cell;
  typedef typename KSpace::Point Point3d;
  Point3d b = ks.lowerBound();
  for ( DGtal::Dimension i = 0; i < 3; ++i )
    {
      const ArithmeticalDSSComputer2d & dss2d = dss3d.arithmeticalDSS2d( i );
      for ( ConstIterator2d itP = dss2d.begin(), itPEnd = dss2d.end(); itP != itPEnd; ++itP )
	{
	  Point2d p = *itP;
	  Point3d q;
	  switch (i) {
	  case 0: q = Point3d( 2*b[ i ]  , 2*p[ 0 ]+1, 2*p[ 1 ]+1 ); break;
	  case 1: q = Point3d( 2*p[ 0 ]+1, 2*b[ i ]  , 2*p[ 1 ]+1 ); break;
	  case 2: q = Point3d( 2*p[ 0 ]+1, 2*p[ 1 ]+1, 2*b[ i ]   ); break;
	  }
	  Cell c = ks.uCell( q );
	  viewer << CustomColors3D( color2d, color2d ) << c;
	}
    }
}
//-----------------------------------------------------------------------------
// Testing LightImplicitDigitalSurface
//-----------------------------------------------------------------------------
bool testLightImplicitDigitalSurface()
{
  using namespace Z3i;
  typedef ImplicitDigitalEllipse3<Point> ImplicitDigitalEllipse;
  typedef LightImplicitDigitalSurface<KSpace,ImplicitDigitalEllipse> Boundary;
  typedef Boundary::SurfelConstIterator ConstIterator;
  typedef Boundary::Tracker Tracker;
  typedef Boundary::Surfel Surfel;

  unsigned int nbok = 0;
  unsigned int nb = 0;
  trace.beginBlock ( "Testing block ... LightImplicitDigitalSurface" );
  Point p1( -10, -10, -10 );
  Point p2( 10, 10, 10 );
  KSpace K;
  nbok += K.init( p1, p2, true ) ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
	       << "K.init() is ok" << std::endl;
  ImplicitDigitalEllipse ellipse( 6.0, 4.5, 3.4 );
  Surfel bel = Surfaces<KSpace>::findABel( K, ellipse, 10000 );
  Boundary boundary( K, ellipse, 
                     SurfelAdjacency<KSpace::dimension>( true ), bel );
  unsigned int nbsurfels = 0;
  for ( ConstIterator it = boundary.begin(), it_end = boundary.end();
        it != it_end; ++it )
    {
      ++nbsurfels;
    }
  trace.info() << nbsurfels << " surfels found." << std::endl;
  trace.beginBlock ( "Checks if adjacent surfels are part of the surface." );

  for ( ConstIterator it = boundary.begin(), it_end = boundary.end();
        it != it_end; ++it )
    {
      Tracker* ptrTracker = boundary.newTracker( *it );
      Surfel s = ptrTracker->current();
      Dimension trackDir = * K.sDirs( s );
      Surfel s1, s2;
      // unsigned int m1 = 
      ptrTracker->adjacent( s1, trackDir, true ); 
      // unsigned int m2 = 
      ptrTracker->adjacent( s2, trackDir, false ); 
      // trace.info() << "s = " << s << std::endl;
      // trace.info() << "s1 = " << s1 << " m1 = " << m1 << std::endl;
      // trace.info() << "s2 = " << s2 << " m2 = " << m2 << std::endl;
      nb++, nbok += boundary.isInside( s1 ) ? 1 : 0;
      // trace.info() << "(" << nbok << "/" << nb << ") "
      //              << "boundary.isInside( s1 )" << std::endl;
      nb++, nbok += boundary.isInside( s2 ) ? 1 : 0;
      // trace.info() << "(" << nbok << "/" << nb << ") "
      //              << "boundary.isInside( s2 )" << std::endl;
      delete ptrTracker;
    }
  trace.info() << "(" << nbok << "/" << nb << ") isInside tests." << std::endl;
  trace.endBlock();
  trace.endBlock();
  return nbok == nb;
}
/**
 * Example of a test. To be completed.
 *
 */
bool testDigitalSetBoundary()
{
  unsigned int nbok = 0;
  unsigned int nb = 0;
  
  trace.beginBlock ( "Testing block ... DigitalSetBoundary" );
  using namespace Z2i;
  typedef DigitalSetBoundary<KSpace,DigitalSet> Boundary;
  typedef Boundary::SurfelConstIterator ConstIterator;
  typedef Boundary::Tracker Tracker;
  typedef Boundary::Surfel Surfel;
  Point p1( -10, -10 );
  Point p2( 10, 10 );
  Domain domain( p1, p2 );
  DigitalSet dig_set( domain );
  Shapes<Domain>::addNorm2Ball( dig_set, Point( 0, 0 ), 5 );
  Shapes<Domain>::removeNorm2Ball( dig_set, Point( 0, 0 ), 1 );
  KSpace K;
  nbok += K.init( domain.lowerBound(), domain.upperBound(), true ) ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
	       << "K.init() is ok" << std::endl;
  Boundary boundary( K, dig_set );
  unsigned int nbsurfels = 0;
  for ( ConstIterator it = boundary.begin(), it_end = boundary.end();
        it != it_end; ++it )
    {
      ++nbsurfels;
    }
  trace.info() << nbsurfels << " surfels found." << std::endl;
  nb++, nbok += nbsurfels == ( 12 + 44 ) ? 1 : 0;
  trace.info() << "(" << nbok << "/" << nb << ") "
	       << "nbsurfels == (12 + 44 )" << std::endl;
  for ( ConstIterator it = boundary.begin(), it_end = boundary.end();
        it != it_end; ++it )
    {
      Tracker* ptrTracker = boundary.newTracker( *it );
      Surfel s = ptrTracker->current();
      Dimension trackDir = * K.sDirs( s );
      Surfel s1, s2;
      unsigned int m1 = ptrTracker->adjacent( s1, trackDir, true ); 
      unsigned int m2 = ptrTracker->adjacent( s2, trackDir, false ); 
      trace.info() << "s = " << s << std::endl;
      trace.info() << "s1 = " << s1 << " m1 = " << m1 << std::endl;
      trace.info() << "s2 = " << s2 << " m2 = " << m2 << std::endl;
      nb++, nbok += boundary.isInside( s1 ) ? 1 : 0;
      trace.info() << "(" << nbok << "/" << nb << ") "
                   << "boundary.isInside( s1 )" << std::endl;
      nb++, nbok += boundary.isInside( s2 ) ? 1 : 0;
      trace.info() << "(" << nbok << "/" << nb << ") "
                   << "boundary.isInside( s2 )" << std::endl;
      delete ptrTracker;
    }
  trace.endBlock();
  return nbok == nb;
}
Exemple #8
0
int main()
{
  KSpace K;
  Point plow(-3,-2);
  Point pup(5,3);
  Domain domain( plow, pup );
  Board2D board; // for 2D display
  K.init( plow, pup, true );
  board << SetMode( domain.styleName(), "Paving" )
	<< domain;
  Cell pixlow = K.uSpel( plow ); // pixel (-3*2+1,-2*2+1)
  Cell ptlow = K.uPointel( plow ); // pointel (-3*2,-2*2)
  Cell pixup = K.uSpel( pup ); // pixel (5*2+1,3*2+1)
  Cell ptup1 = K.uPointel( pup );   // pointel (5*2,3*2)
  Cell ptup2 = K.uTranslation( ptup1, Point::diagonal() ); // pointel (6*2,4*2)
  Cell linelb = K.uCell( Point( 1, 0 ) ); // linel (1,0) bottom
  Cell linelt = K.uCell( Point( 1, 2 ) ); // linel (1,2) top
  Cell linell = K.uCell( Point( 0, 1 ) ); // linel (0,1) left
  Cell linelr = K.uCell( Point( 2, 1 ) ); // linel (2,1) right
  board << CustomStyle( ptlow.styleName(), 
			new CustomColors( Color( 0, 0, 200 ), 
					  Color( 100, 100, 255 ) ) )
	<< ptlow << ptup2;
  board << CustomStyle( pixlow.styleName(), 
			new CustomColors( Color( 200, 0, 0 ), 
					  Color( 255, 100, 100 ) ) )
	<< pixlow << pixup;
  board << CustomStyle( linelb.styleName(), 
			new CustomColors( Color( 0, 200, 0 ), 
					  Color( 100, 255, 100 ) ) )
	<< linelb << linelt << linell << linelr;
  board.saveSVG("ctopo-1.svg");
  board.saveEPS("ctopo-1.eps");
  return 0;
}
bool testBallQuad()
{
  unsigned int nbok = 0;
  unsigned int nb = 0;

  trace.beginBlock ( "Testing... Ball with quadnormal");
  using namespace Z3i;
  typedef ImplicitDigitalBall3<Point> ImplicitDigitalBall;
  typedef ImplicitDigitalSurface<KSpace,ImplicitDigitalBall> Boundary;
  typedef Boundary::SurfelConstIterator ConstIterator;
  typedef Boundary::Surfel Surfel;
  Point p1( -50, -50, -50 );
  Point p2( 50, 50, 50 );
  KSpace K;
  nbok += K.init( p1, p2, true ) ? 1 : 0;
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
               << "K.init() is ok" << std::endl;
  ImplicitDigitalBall ball( 30.0 );
  Surfel bel = Surfaces<KSpace>::findABel( K, ball, 10000 );
  Boundary boundary( K, ball,
                     SurfelAdjacency<KSpace::dimension>( true ), bel );
  unsigned int nbsurfels = 0;

  Board3D<Space,KSpace> board(K);

  for ( ConstIterator it = boundary.begin(), it_end = boundary.end();
        it != it_end; ++it )
    {
      ++nbsurfels;

      Display3DFactory<>::drawOrientedSurfelWithNormal(board,
                                                       *it,
                                                       board.embedKS(*it).getNormalized());
    }

  trace.info() << nbsurfels << " surfels found." << std::endl;

  board.saveOBJ("testball.obj");


  nbok += true ? 1 : 0;
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
               << "true == true" << std::endl;
  trace.endBlock();

  return nbok == nb;
}
bool testBallQuad(int argc, char **argv)
{
  unsigned int nbok = 0;
  unsigned int nb = 0;

  QApplication application(argc, argv);

  trace.beginBlock ( "Testing... Ball with quadnormal");
  using namespace Z3i;
  typedef ImplicitDigitalBall3<Point> ImplicitDigitalBall;
  typedef ImplicitDigitalSurface<KSpace,ImplicitDigitalBall> Boundary;
  typedef Boundary::SurfelConstIterator ConstIterator;
  typedef Boundary::Surfel Surfel;
  Point p1( -100, -100, -100 );
  Point p2( 100, 100, 100 );
  KSpace K;
  nbok += K.init( p1, p2, true ) ? 1 : 0;
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
               << "K.init() is ok" << std::endl;
  ImplicitDigitalBall ball( 60.0 );
  Surfel bel = Surfaces<KSpace>::findABel( K, ball, 10000 );
  Boundary boundary( K, ball,
                     SurfelAdjacency<KSpace::dimension>( true ), bel );
  unsigned int nbsurfels = 0;

  Viewer3D<Space,KSpace> viewer(K);
  viewer.setWindowTitle("simpleViewer");
  viewer.show();


  for ( ConstIterator it = boundary.begin(), it_end = boundary.end();
        it != it_end; ++it )
    {
      ++nbsurfels;

      Display3DFactory<>::drawOrientedSurfelWithNormal(viewer,
                                                       *it,
                                                       viewer.embedKS(*it).getNormalized());
    }

  trace.info() << nbsurfels << " surfels found." << std::endl;
  viewer  << Display3D<Space, KSpace>::updateDisplay;

  bool res = application.exec();

  return res;
}
Exemple #11
0
    FixtureComplex &create_complex_from_object(FixtureObject &input_obj) {

        ks_fixture.init(input_obj.domain().lowerBound(),
                        input_obj.domain().upperBound(), true);
        complex_fixture = FixtureComplex(ks_fixture);
        complex_fixture.construct(input_obj);
        return complex_fixture;
    }
    FixtureComplex &create_complex_from_set(const FixtureDigitalSet &input_set) {

        ks_fixture.init(input_set.domain().lowerBound(),
                        input_set.domain().upperBound(), true);
        complex_fixture = FixtureComplex(ks_fixture);
        complex_fixture.construct(input_set);
        return complex_fixture;
    }
Exemple #13
0
bool testQuadNorm()
{
  unsigned int nbok = 0;
  unsigned int nb = 0;
  trace.beginBlock ( "Testing Board3D Quads ..." );



  Point p1( 0, 0, 0 );
  Point p2( 0, 1 , 0);
  Point p3( 1, 1, 0);
  Point p4(1, 0, 0 );
  Point p5( 2, 0 , 0);
  Point p6( 2, 1, 0);
  RealVector n(1,1,1);
  RealVector n2(0,1,1);

  KSpace k;

  k.init(Point(2,2,2), Point(4,4,4), true);

  Board3D<Space,KSpace> board(k);
  board << CustomColors3D(Color(0, 255,0),Color(0, 255, 0));
  board.addQuadWithNormal(p1,p2,p3,p4, n.getNormalized(), true);
  board << CustomColors3D(Color(0, 0, 255),Color(0, 0, 255));
  board.addQuadWithNormal(p4,p5,p6,p3, n2.getNormalized(), true);

  Cell surfel = k.uCell( Point( 4,5,5) );
  Display3DFactory<Space,KSpace>::drawUnorientedSurfelWithNormal( board, surfel, n2.getNormalized());

  board.saveOBJ("dgtalBoard3D.quad.obj");


  nbok += true ? 1 : 0;
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
	       << "true == true" << std::endl;
  trace.endBlock();

  return nbok == nb;
}
bool testRaySurfelIntersection()
{
  unsigned int nbok = 0;
  unsigned int nb = 0;
  
  trace.beginBlock ( "Testing RaySurfel ..." );

  using namespace Z3i;
  
  KSpace k;

  k.init(Point(0,0,0), Point(10,10,10), true);
  
  typedef RayIntersectionPredicate<KSpace::Cell::Point> Ray;
  Ray  ray(KSpace::Cell::Point(0,0,0), 
           KSpace::Cell::Point(2,1,1));

  KSpace::Surfel surf =  k.sCell( Point( 2,1,1) );
  KSpace::Surfel surf2 =  k.sCell( Point( 2,7,7) );
  
  trace.info() << "Ray intersection with surf   "<<std::endl;
  nbok += ray(surf)  ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
               << "true " << std::endl;
  trace.info()<<std::endl;
  
  trace.info() << "Ray intersection with surf2  "<<std::endl;
  nbok += !ray(surf2 ) ? 1 : 0; 
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
	       << "false " << std::endl;
  trace.info()<<std::endl;

  trace.endBlock();
  
  return nbok == nb;
}
int main( int, char** )
{
  using namespace Z3i;
  typedef DGtal::ImplicitDigitalEllipse3<Point> ImplicitDigitalEllipse;
  typedef KSpace::SCell Surfel;
  bool res;
  trace.beginBlock ( "Testing class Object" );
  Point p1( -200, -200, -200 );
  Point p2( 200, 200, 200 );
  KSpace K;
  if ( K.init( p1, p2, true ) )
    {
      ImplicitDigitalEllipse ellipse( 180.0, 135.0, 102.0 );
      Surfel bel = Surfaces<KSpace>::findABel( K, ellipse, 10000 );
      res = testLightImplicitDigitalSurface<KSpace, ImplicitDigitalEllipse>
        ( K, ellipse, bel );
    }
  else
    res = false;
  trace.emphase() << ( res ? "Passed." : "Error." ) << endl;
  trace.endBlock();
  return res ? 0 : 1;
}
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
  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 areas." );
  range = CountedPtr<VisitorRange>( new VisitorRange( new Visitor( surface, *(surface.begin()) )) );
  double area_est   = 0.0; // normal integration with absolute value.
  unsigned int i = 0;
  for ( typename VisitorRange::ConstIterator it = range->begin(), itE = range->end(); 
        it != itE; ++it, ++i )
    {
      Surfel s = *it;
      Dimension k = K.sOrthDir( s );
      area_est  += abs( n_estimations[ i ][ k ] );
    }  
  double h = vm["gridstep"].as<double>();
  trace.info() << setprecision(10) << "- Area_est     " << ( area_est * h * h )   << std::endl;
  std::ostringstream area_sstr;
  area_sstr << fname << "-" << nameEstimator << "-area-" << h << ".txt"; 
  std::ofstream area_output( area_sstr.str().c_str() );
  area_output << "# Area estimation by digital surface integration." << std::endl;
  area_output << "# X: " << nameEstimator << std::endl;
  area_output << "# h Area[X] nb_surf" << std::endl;
  area_output << setprecision(10) << h
              << " " << ( area_est * h * h )
              << " " << i << std::endl;
  area_output.close();
  trace.endBlock();
}
void displayDSS2d( Viewer3D<space, kspace> & viewer,
		   const KSpace & ks, const StandardDSS6Computer & dss3d,
		   const DGtal::Color & color2d )
{
  typedef typename StandardDSS6Computer::ConstIterator ConstIterator3d;
  typedef typename StandardDSS6Computer::ArithmeticalDSSComputer2d ArithmeticalDSSComputer2d;
  typedef typename ArithmeticalDSSComputer2d::ConstIterator ConstIterator2d;
  typedef typename ArithmeticalDSSComputer2d::Point Point2d;
  typedef typename KSpace::Cell Cell;
  typedef typename KSpace::Point Point3d;
  typedef DGtal::PointVector<2,double> PointD2d;
  typedef typename Display3D<>::BallD3D PointD3D;
  Point3d b = ks.lowerBound();
  for ( DGtal::Dimension i = 0; i < 3; ++i )
    {
      const typename ArithmeticalDSSComputer2d::Primitive & dss2d 
	= dss3d.arithmeticalDSS2d( i ).primitive();
      // draw 2D bounding boxes for each arithmetical dss 2D.
      std::vector<PointD2d> pts2d;
      pts2d.push_back( dss2d.project(dss2d.back(), dss2d.Uf()) );
      pts2d.push_back( dss2d.project(dss2d.back(), dss2d.Lf()) );
      pts2d.push_back( dss2d.project(dss2d.front(), dss2d.Lf()) );
      pts2d.push_back( dss2d.project(dss2d.front(), dss2d.Uf()) );
      std::vector<PointD3D> bb;
      PointD3D p3;
      for ( unsigned int j = 0; j < pts2d.size(); ++j )
	{
	  switch (i) {
	  case 0: p3.center[0] = (double) b[ i ]-0.5; p3.center[1] = pts2d[ j ][ 0 ];  p3.center[2] = pts2d[ j ][ 1 ]; break;
	  case 1: p3.center[0] = pts2d[ j ][ 0 ];  p3.center[1] = (double) b[ i ]-0.5; p3.center[2] = pts2d[ j ][ 1 ];     break;
	  case 2: p3.center[0] = pts2d[ j ][ 0 ];  p3.center[1] = pts2d[ j ][ 1 ];     p3.center[2] = (double) b[ i ]-0.5; break;
	  }
	  bb.push_back( p3 );
	}
      for ( unsigned int j = 0; j < pts2d.size(); ++j ){
	viewer.setLineColor(color2d);
	viewer.addLine( DGtal::Z3i::RealPoint(bb[ j ].center[0], bb[ j ].center[1], bb[ j ].center[2]),
                        DGtal::Z3i::RealPoint(bb[ (j+1)%4 ].center[0], bb[ (j+1)%4 ].center[1], bb[ (j+1)%4 ].center[2]),
			MS3D_LINESIZE );
      }
    } // for ( DGtal::Dimension i = 0; i < 3; ++i )
}
int main( int argc, char** argv )
{

  typedef DGtal::ImageContainerBySTLVector<DGtal::Z3i::Domain,  unsigned char > Image3D;
  typedef DGtal::ImageContainerBySTLVector<DGtal::Z2i::Domain,  unsigned char > Image2D;


  // parse command line ----------------------------------------------
  po::options_description general_opt("Allowed options are ");
  general_opt.add_options()
    ("help,h", "display this message")
    ("input,i", po::value<std::string>(), "vol file (.vol) , pgm3d (.p3d or .pgm3d) file or sdp (sequence of discrete points)" )
    ("grid", "draw slice images using grid mode. " )
    ("intergrid", "draw slice images using inter grid mode. " )
    ("emptyMode", "remove the default boundingbox display " )
    ("thresholdImage", "threshold the image to define binary shape" )
    ("thresholdMin,m",  po::value<int>()->default_value(0), "threshold min to define binary shape" )
    ("thresholdMax,M",  po::value<int>()->default_value(255), "threshold max to define binary shape" )
    ("displaySDP,s", po::value<std::string>(), "display a set of discrete points (.sdp)" )
    ("SDPindex", po::value<std::vector <unsigned int> >()->multitoken(), "specify the sdp index (by default 0,1,2).")
    ("SDPball", po::value<double>()->default_value(0.5), "use balls to display a set of discrete points (used with displaySDP option)")
    ("displayMesh", po::value<std::string>(), "display a Mesh given in OFF or OFS format. " )
    ("displayDigitalSurface", "display the digital surface instead of display all the set of voxels (used with thresholdImage or displaySDP options)" )
    ("colorizeCC", "colorize each Connected Components of the surface displayed by displayDigitalSurface option." )
    ("colorSDP,c", po::value<std::vector <int> >()->multitoken(), "set the color  discrete points: r g b a " )
    ("colorMesh", po::value<std::vector <int> >()->multitoken(), "set the color of Mesh (given from displayMesh option) : r g b a " )
    ("scaleX,x",  po::value<float>()->default_value(1.0), "set the scale value in the X direction (default 1.0)" )
    ("scaleY,y",  po::value<float>()->default_value(1.0), "set the scale value in the Y direction (default 1.0)" )
    ("scaleZ,z",  po::value<float>()->default_value(1.0), "set the scale value in the Z direction (default 1.0)")
#ifdef WITH_ITK
    ("dicomMin", po::value<int>()->default_value(-1000), "set minimum density threshold on Hounsfield scale")
    ("dicomMax", po::value<int>()->default_value(3000), "set maximum density threshold on Hounsfield scale")
#endif
    ("transparency,t",  po::value<uint>()->default_value(255), "transparency") ;

  bool parseOK=true;
  po::variables_map vm;
  try{
    po::store(po::parse_command_line(argc, argv, general_opt), vm);
  }catch(const std::exception& ex){
    parseOK=false;
    trace.info()<< "Error checking program options: "<< ex.what()<< endl;
  }
  po::notify(vm);
  if( !parseOK || vm.count("help")||argc<=1)
    {
      std::cout << "Usage: " << argv[0] << " [input]\n"
    << "Displays volume file as a voxel set by using QGLviewer"
    << general_opt << "\n";
      return 0;
    }

  if(! vm.count("input"))
    {
      trace.error() << " The file name was defined" << endl;
      return 0;
    }
  string inputFilename = vm["input"].as<std::string>();
  int thresholdMin = vm["thresholdMin"].as<int>();
  int thresholdMax = vm["thresholdMax"].as<int>();
  unsigned char transp = vm["transparency"].as<uint>();

  QApplication application(argc,argv);
  
  float sx = vm["scaleX"].as<float>();
  float sy = vm["scaleY"].as<float>();
  float sz = vm["scaleZ"].as<float>();

  double ballRadius = vm["SDPball"].as<double>();
  string extension = inputFilename.substr(inputFilename.find_last_of(".") + 1);
  if(extension!="vol" && extension != "p3d" && extension != "pgm3D" && extension != "pgm3d" && extension != "sdp" && extension != "pgm"
#ifdef WITH_ITK
     && extension !="dcm"
#endif
     ){
    trace.info() << "File extension not recognized: "<< extension << std::endl;
    return 0;
  }
  Viewer3DImage<>::ModeVisu mode;
  if(vm.count("emptyMode"))
    mode=Viewer3DImage<>::Empty;
  else if(vm.count("grid"))
    mode=Viewer3DImage<>::Grid;
  else if(vm.count("intergrid"))
    mode=Viewer3DImage<>::InterGrid;
  else
    mode=Viewer3DImage<>::BoundingBox;

  Viewer3DImage<> viewer(mode);
  viewer.setWindowTitle("simple Volume Viewer");
  viewer.show();
  viewer.setGLScale(sx, sy, sz);

#ifdef WITH_ITK
  int dicomMin = vm["dicomMin"].as<int>();
  int dicomMax = vm["dicomMax"].as<int>();
  typedef DGtal::functors::Rescaling<int ,unsigned char > RescalFCT;

  Image3D image = extension == "dcm" ? DicomReader< Image3D,  RescalFCT  >::importDicom( inputFilename,
                                                                                         RescalFCT(dicomMin,
                                                                                                   dicomMax,
                                                                                                   0, 255) ) :
    GenericReader<Image3D>::import( inputFilename );
#else
  Image3D image = GenericReader<Image3D>::import( inputFilename );
#endif
  Domain domain = image.domain();

  trace.info() << "Image loaded: "<<image<< std::endl;
  viewer.setVolImage(&image);
  viewer << Z3i::Point(512, 512, 0);
  // Used to display 3D surface
  Z3i::DigitalSet set3d(domain);



  viewer << Viewer3D<>::updateDisplay;
  if(vm.count("thresholdImage")){
    GradientColorMap<long> gradient( thresholdMin, thresholdMax);
    gradient.addColor(Color::Blue);
    gradient.addColor(Color::Green);
    gradient.addColor(Color::Yellow);
    gradient.addColor(Color::Red);
    for(Domain::ConstIterator it = domain.begin(), itend=domain.end(); it!=itend; ++it){
      unsigned char  val= image( (*it) );
      Color c= gradient(val);
      if(val<=thresholdMax && val >=thresholdMin){
  if(!vm.count("displayDigitalSurface")){
          viewer <<  CustomColors3D(Color((float)(c.red()), (float)(c.green()),(float)(c.blue()), transp),
                                    Color((float)(c.red()), (float)(c.green()),(float)(c.blue()), transp));
          viewer << *it;
  }
      }else{
  set3d.insert(*it);
      }
    }
  }

  if(vm.count("displaySDP")){
    if(vm.count("colorSDP")){
      std::vector<int> vcol= vm["colorSDP"].as<std::vector<int > >();
      if(vcol.size()<4){
        trace.error() << "Not enough parameter: color specification should contains four elements: red, green, blue and alpha values."  << std::endl;
        return 0;
      }
      Color c(vcol[0], vcol[1], vcol[2], vcol[3]);
      viewer << CustomColors3D(c, c);
    }

    vector<Z3i::Point> vectVoxels;
    if(vm.count("SDPindex")) {
      std::vector<unsigned int > vectIndex = vm["SDPindex"].as<std::vector<unsigned int > >();
      if(vectIndex.size()!=3){
        trace.error() << "you need to specify the three indexes of vertex." << std::endl;
        return 0;
      }
      vectVoxels = PointListReader<Z3i::Point>::getPointsFromFile(vm["displaySDP"].as<std::string>(), vectIndex);
    }else{
      vectVoxels = PointListReader<Z3i::Point>::getPointsFromFile(vm["displaySDP"].as<std::string>());
    }
    for(unsigned int i=0;i< vectVoxels.size(); i++){
      if(!vm.count("displayDigitalSurface")){
        if(vm.count("SDPball")){
          viewer.addBall (vectVoxels.at(i), ballRadius);
        }else{
          viewer << vectVoxels.at(i);
        }
      }else{
        set3d.insert(vectVoxels.at(i));
      }
    }
  }

  if(vm.count("displayMesh")){
    if(vm.count("colorMesh")){
      std::vector<int> vcol= vm["colorMesh"].as<std::vector<int > >();
      if(vcol.size()<4){
        trace.error() << "Not enough parameter: color specification should contains four elements: red, green, blue and alpha values." << std::endl;
        return 0;
      }
      Color c(vcol[0], vcol[1], vcol[2], vcol[3]);
      viewer.setFillColor(c);
    }

    DGtal::Mesh<Z3i::RealPoint> aMesh(!vm.count("colorMesh"));
    MeshReader<Z3i::RealPoint>::importOFFFile(vm["displayMesh"].as<std::string>(), aMesh);
    viewer << aMesh;
  }

  if(vm.count("displayDigitalSurface")){
    KSpace K;
    Point low = domain.lowerBound(); low[0]=low[0]-1; low[1]=low[1]-1; low[2]=low[2]-1;
    Point upp = domain.upperBound(); upp[0]=upp[0]+1; upp[1]=upp[1]+1; upp[2]=upp[2]+1;
    K.init(low, upp , true);
    SurfelAdjacency<3> SAdj( true );
    vector<vector<SCell> > vectConnectedSCell;
    trace.info() << "Extracting surface  set ... " ;
    Surfaces<KSpace>::extractAllConnectedSCell(vectConnectedSCell,K, SAdj, set3d, true);
    trace.info()<< " [done] " <<std::endl;
    GradientColorMap<long> gradient( 0, vectConnectedSCell.size());
    gradient.addColor(DGtal::Color::Red);
    gradient.addColor(DGtal::Color::Yellow);
    gradient.addColor(DGtal::Color::Green);
    gradient.addColor(DGtal::Color::Cyan);
    gradient.addColor(DGtal::Color::Blue);
    gradient.addColor(DGtal::Color::Magenta);
    gradient.addColor(DGtal::Color::Red);

    viewer << DGtal::SetMode3D(vectConnectedSCell.at(0).at(0).className(), "Basic");
    for(unsigned int i= 0; i <vectConnectedSCell.size(); i++){
      for(unsigned int j= 0; j <vectConnectedSCell.at(i).size(); j++){
  if(vm.count("colorizeCC")){
    DGtal::Color c= gradient(i);
    viewer << CustomColors3D(Color(250, 0,0, transp), Color(c.red(),
                  c.green(),
                  c.blue(), transp));
  }else  if(vm.count("colorSDP")){
    std::vector<int> vcol= vm["colorSDP"].as<std::vector<int > >();
    Color c(vcol[0], vcol[1], vcol[2], vcol[3]);
    viewer << CustomColors3D(c, c);
  }

  viewer << vectConnectedSCell.at(i).at(j);
      }
    }
  }
  
  viewer << Viewer3D<>::updateDisplay;
  return application.exec();
}
bool
compareShapeEstimators( const string & name,
      Shape & aShape, 
      double h )
{
  // Types
  typedef typename Space::Point Point;
  typedef typename Space::Vector Vector;
  typedef typename Space::RealPoint RealPoint;
  typedef typename Space::Integer Integer;
  typedef HyperRectDomain<Space> Domain;
  typedef KhalimskySpaceND<Space::dimension,Integer> KSpace;
  typedef typename KSpace::SCell SCell;
  typedef typename GridCurve<KSpace>::PointsRange PointsRange;
  typedef typename GridCurve<KSpace>::ArrowsRange ArrowsRange;
  typedef typename PointsRange::ConstIterator ConstIteratorOnPoints;


  // Digitizer
  GaussDigitizer<Space,Shape> dig;  
  dig.attach( aShape ); // attaches the shape.
  Vector vlow(-1,-1); Vector vup(1,1);
  dig.init( aShape.getLowerBound()+vlow, aShape.getUpperBound()+vup, h ); 
  Domain domain = dig.getDomain();

  // Create cellular space
  KSpace K;
  bool ok = K.init( dig.getLowerBound(), dig.getUpperBound(), true );
  if ( ! ok )
    {
      std::cerr << "[compareShapeEstimators]"
    << " error in creating KSpace." << std::endl;
      return false;
    }
  try {
    // Extracts shape boundary
    SurfelAdjacency<KSpace::dimension> SAdj( true );
    SCell bel = Surfaces<KSpace>::findABel( K, dig, 10000 );
    // Getting the consecutive surfels of the 2D boundary
    std::vector<Point> points;
    Surfaces<KSpace>::track2DBoundaryPoints( points, K, SAdj, dig, bel );
    // Create GridCurve
    GridCurve<KSpace> gridcurve;
    gridcurve.initFromVector( points );
    // Ranges
    PointsRange r = gridcurve.getPointsRange(); 
    std::cout << "# range size = " << r.size() << std::endl;  

    // Estimations
    // True values
    std::cout << "# True values computation" << std::endl;  
    typedef ParametricShapeTangentFunctor< Shape > TangentFunctor;
    typedef ParametricShapeCurvatureFunctor< Shape > CurvatureFunctor;
  
    TrueLocalEstimatorOnPoints< ConstIteratorOnPoints, Shape, TangentFunctor >  
      trueTangentEstimator;
    TrueLocalEstimatorOnPoints< ConstIteratorOnPoints, Shape, CurvatureFunctor >  
      trueCurvatureEstimator;
  
    trueTangentEstimator.init( h, r.begin(), r.end(), &aShape, gridcurve.isClosed());
    std::vector<RealPoint> trueTangents = 
      estimateQuantity( trueTangentEstimator, r.begin(), r.end() );
    trueCurvatureEstimator.init( h, r.begin(), r.end(), &aShape, gridcurve.isClosed());
    std::vector<double> trueCurvatures = 
      estimateQuantity( trueCurvatureEstimator, r.begin(), r.end() );
  
    // Maximal Segments
    std::cout << "# Maximal DSS tangent estimation" << std::endl;  
    typedef ArithmeticalDSS<ConstIteratorOnPoints,Integer,4> SegmentComputer;
    typedef TangentFromDSSFunctor<SegmentComputer> SCFunctor;
    SegmentComputer sc;
    SCFunctor f; 
    MostCenteredMaximalSegmentEstimator<SegmentComputer,SCFunctor> MSTangentEstimator(sc, f); 
   
    Clock c;
    
    c.startClock();
    MSTangentEstimator.init( h, r.begin(), r.end(), gridcurve.isClosed() );
    std::vector<typename SCFunctor::Value> MSTangents = 
      estimateQuantity( MSTangentEstimator, r.begin(), r.end() );
    double TMST = c.stopClock();


    // Binomial
    std::cout << "# Tangent and curvature estimation from binomial convolution" << std::endl;
    typedef BinomialConvolver<ConstIteratorOnPoints, double> MyBinomialConvolver;
    std::cout << "# mask size = " << 
      MyBinomialConvolver::suggestedSize( h, r.begin(), r.end() ) << std::endl;
    typedef TangentFromBinomialConvolverFunctor< MyBinomialConvolver, RealPoint >
      TangentBCFct;
    typedef CurvatureFromBinomialConvolverFunctor< MyBinomialConvolver, double >
      CurvatureBCFct;
    BinomialConvolverEstimator< MyBinomialConvolver, TangentBCFct> BCTangentEstimator;
    BinomialConvolverEstimator< MyBinomialConvolver, CurvatureBCFct> BCCurvatureEstimator;
    
    c.startClock();
    BCTangentEstimator.init( h, r.begin(), r.end(), gridcurve.isClosed() );
    std::vector<RealPoint> BCTangents = 
      estimateQuantity( BCTangentEstimator, r.begin(), r.end() );
    double TBCTan = c.stopClock();

    c.startClock();
    BCCurvatureEstimator.init( h, r.begin(), r.end(), gridcurve.isClosed() );
    std::vector<double> BCCurvatures =
      estimateQuantity( BCCurvatureEstimator, r.begin(), r.end() );
    double TBCCurv = c.stopClock();

    // Output
    std::cout << "# Shape = "<< name <<std::endl
        << "# Time-BCtangent = "<<TBCTan <<std::endl
        << "# Time-BCcurvature = "<<TBCCurv<<std::endl
        << "# Time-MStangent = "<<TMST<<std::endl
        << "# id x y tangentx tangenty curvature"
        << " BCtangentx BCtangenty BCcurvature"
        << " MStangentx MStangenty"
        << std::endl;  
    unsigned int i = 0;
    for ( ConstIteratorOnPoints it = r.begin(), it_end = r.end();
    it != it_end; ++it, ++i )
      {
  Point p = *it;
  std::cout << i << setprecision( 15 )
      << " " << p[ 0 ] << " " << p[ 1 ] 
      << " " << trueTangents[ i ][ 0 ]
      << " " << trueTangents[ i ][ 1 ]
      << " " << trueCurvatures[ i ]
      << " " << BCTangents[ i ][ 0 ]
      << " " << BCTangents[ i ][ 1 ]
      << " " << BCCurvatures[ i ]
      << " " << MSTangents[ i ][ 0 ]
      << " " << MSTangents[ i ][ 1 ]
      << std::endl;
      }
    return true;
  }    
  catch ( InputException e )
    {
      std::cerr << "[compareShapeEstimators]"
    << " error in finding a bel." << std::endl;
      return false;
    }
}
GridCurve<TKSpace>
ballGenerator(double aCx, double aCy, double aR, bool aFlagIsCW)
{

  // Types
  typedef TKSpace KSpace;  
  typedef typename KSpace::SCell SCell;
  typedef GridCurve<KSpace> GridCurve; 
  typedef typename KSpace::Space Space;  
  typedef Ball2D<Space> Shape;
  typedef typename Space::Point Point;
  typedef typename Space::RealPoint RealPoint;
  typedef HyperRectDomain<Space> Domain;

  //Forme
  Shape aShape(Point(aCx,aCy), aR);

  // Window for the estimation
  RealPoint xLow ( -aR-1, -aR-1 );
  RealPoint xUp( aR+1, aR+1 );
  GaussDigitizer<Space,Shape> dig;  
  dig.attach( aShape ); // attaches the shape.
  dig.init( xLow, xUp, 1 ); 
  Domain domain = dig.getDomain();
  // Create cellular space
  KSpace K;
  bool ok = K.init( dig.getLowerBound(), dig.getUpperBound(), true );
  if ( ! ok )
  {
      std::cerr << " "
    << " error in creating KSpace." << std::endl;
      return GridCurve();
  }
  try 
  {

    // Extracts shape boundary
    SurfelAdjacency<KSpace::dimension> SAdj( true );
    SCell bel = Surfaces<KSpace>::findABel( K, dig, 10000 );
    // Getting the consecutive surfels of the 2D boundary
    std::vector<Point> points, points2;
    Surfaces<KSpace>::track2DBoundaryPoints( points, K, SAdj, dig, bel );
    //counter-clockwise oriented by default
    GridCurve c; 
    if (aFlagIsCW)
    {
      points2.assign( points.rbegin(), points.rend() );
      c.initFromVector(points2); 
    } 
    else 
    {
      c.initFromVector(points); 
    }
    return c;
  }
  catch ( InputException& e )
  {
      std::cerr << " "
    << " error in finding a bel." << std::endl;
      return GridCurve();
  }
}
int main(int argc, char* const argv[]) {
  /*-------------- Parse command line -----------------------------*/
  po::options_description opt_desc("Allowed options are: ");
  opt_desc.add_options()("help,h", "display this message.");
  opt_desc.add_options()("input,i", po::value<string>()->required(),
                         "Input thin image.");
  opt_desc.add_options()("reduceGraph,r",
                         po::bool_switch()->default_value(false),
                         "Reduce obj graph into a new SpatialGraph, converting "
                         "chain nodes (degree=2) into edge_points.");
  opt_desc.add_options()(
      "removeExtraEdges,c", po::bool_switch()->default_value(false),
      "Remove extra edges created because connectivity of object.");
  opt_desc.add_options()(
      "mergeThreeConnectedNodes,m", po::bool_switch()->default_value(false),
      "Merge three connected nodes (between themselves) into one node.");
  opt_desc.add_options()(
      "mergeFourConnectedNodes,q", po::bool_switch()->default_value(false),
      "Merge 4 connected nodes (between themselves) into one node.");
  opt_desc.add_options()(
      "mergeTwoThreeConnectedNodes,l", po::bool_switch()->default_value(false),
      "Merge 2 connected nodes of degree 3 (and edge with no "
      "points) into one node.");
  opt_desc.add_options()("checkParallelEdges,e",
                         po::bool_switch()->default_value(false),
                         "Check and print info about parallel edges in the "
                         "graph. Use verbose option for output.");
  opt_desc.add_options()("ignoreAngleBetweenParallelEdges,g",
                         po::bool_switch()->default_value(false),
                         "Don't compute angles between parallel edges.");
  opt_desc.add_options()(
      "ignoreEdgesShorterThan,s", po::value<size_t>()->default_value(0),
      "Ignore distance and angles between edges shorter than this value.");
  opt_desc.add_options()("ignoreEdgesToEndNodes,x",
                         po::bool_switch()->default_value(false),
                         "Ignore distance and angles between edges to/from end "
                         "nodes (degree = 1).");
  opt_desc.add_options()(
      "avoid_transformToPhysicalPoints,p",
      po::bool_switch()->default_value(false),
      "Positions in Spatial Graph takes into account metadata of the "
      "(origin,spacing,direction) itk image.");
  opt_desc.add_options()("spacing", po::value<string>()->default_value(""),
                         "Provide external spacing between voxels. Ignores "
                         "metadata of itk image and apply it.");
  opt_desc.add_options()(
      "output_filename_simple,z", po::bool_switch()->default_value(false),
      "Filename does not contain the parameters used for this filter.");
  opt_desc.add_options()("exportReducedGraph,o", po::value<string>(),
                         "Write .dot file with the reduced spatial graph.");
  opt_desc.add_options()(
      "exportData,d", po::value<string>(),
      "Write degrees, ete_distances, contour_lengths, etc. Histograms can be "
      "generated from these files afterwards.");
  opt_desc.add_options()(
      "exportSerialized,u", po::value<string>(),
      "Write serialized graph with the reduced spatial graph.");
#ifdef VISUALIZE
  opt_desc.add_options()("visualize,t", po::bool_switch()->default_value(false),
                         "Visualize object with DGtal. Requires VISUALIZE "
                         "option enabled at build.");
#endif
  // ( "exportHistograms,e", po::value<string>(), "Export histogram." )
  // ( "binsHistoDegrees,d", po::value<size_t>()->default_value(0), "Bins for
  // the histogram of degrees. Default [0] get the breaks between 0 and
  // max_degree" ) ( "widthHistoDistances,l",
  // po::value<double>()->default_value(0.3), "Width between breaks for
  // histogram of ete distances. Use 0.0 to automatically compute breaks (not
  // recommended)." ) ( "binsHistoAngles,a",
  // po::value<size_t>()->default_value(100), "Bins for the histogram of angles
  // . Use 0 for automatic computation of breaks (not recommended)" ) (
  // "binsHistoCosines,n", po::value<size_t>()->default_value(100), "Bins for
  // the histogram of cosines .Use 0 for automatic computation of breaks (not
  // recommended)" )
  opt_desc.add_options()("verbose,v", po::bool_switch()->default_value(false),
                         "verbose output.");

  po::variables_map vm;
  try {
    po::store(po::parse_command_line(argc, argv, opt_desc), vm);
    if(vm.count("help") || argc <= 1) {
      std::cout << "Basic usage:\n" << opt_desc << "\n";
      return EXIT_SUCCESS;
    }
    po::notify(vm);
  } catch(const std::exception& e) {
    std::cerr << e.what() << std::endl;
    return EXIT_FAILURE;
  }

  string filename = vm["input"].as<string>();
  bool verbose = vm["verbose"].as<bool>();
  if(verbose) std::cout << "Filename: " << filename << std::endl;
  bool output_filename_simple = vm["output_filename_simple"].as<bool>();
  bool reduceGraph = vm["reduceGraph"].as<bool>();
  bool avoid_transformToPhysicalPoints =
      vm["avoid_transformToPhysicalPoints"].as<bool>();
  string spacing = vm["spacing"].as<string>();
  bool removeExtraEdges = vm["removeExtraEdges"].as<bool>();
  bool mergeThreeConnectedNodes = vm["mergeThreeConnectedNodes"].as<bool>();
  bool mergeFourConnectedNodes = vm["mergeFourConnectedNodes"].as<bool>();
  bool mergeTwoThreeConnectedNodes =
      vm["mergeTwoThreeConnectedNodes"].as<bool>();
  bool checkParallelEdges = vm["checkParallelEdges"].as<bool>();
  size_t ignoreEdgesShorterThan = vm["ignoreEdgesShorterThan"].as<size_t>();
  bool ignoreAngleBetweenParallelEdges =
      vm["ignoreAngleBetweenParallelEdges"].as<bool>();
  bool ignoreEdgesToEndNodes = vm["ignoreEdgesToEndNodes"].as<bool>();
  bool exportReducedGraph = vm.count("exportReducedGraph");
  bool exportSerialized = vm.count("exportSerialized");
  bool exportData = vm.count("exportData");
  // bool exportHistograms = vm.count("exportHistograms");
  // size_t binsHistoDegrees = vm["binsHistoDegrees"].as<size_t>();
  // double widthHistoDistances = vm["widthHistoDistances"].as<double>();
  // size_t binsHistoAngles = vm["binsHistoAngles"].as<size_t>();
  // size_t binsHistoCosines = vm["binsHistoCosines"].as<size_t>();

#ifdef VISUALIZE
  bool visualize = vm["visualize"].as<bool>();
#endif
  // Get filename without extension (and without folders).
  const fs::path input_stem = fs::path(filename).stem();
  const fs::path output_file_path = fs::path(input_stem.string() + "_REDUCED");

  using Domain = Z3i::Domain;
  using Image = ImageContainerByITKImage<Domain, unsigned char>;
  const unsigned int Dim = 3;
  using PixelType = unsigned char;
  using ItkImageType = itk::Image<PixelType, Dim>;
  // Read Image using ITK
  using ReaderType = itk::ImageFileReader<ItkImageType>;
  auto reader = ReaderType::New();
  reader->SetFileName(filename);
  reader->Update();

  // Convert to DGtal Container
  Image image(reader->GetOutput());

  using DigitalTopology = DT26_6;
  using DigitalSet = DGtal::DigitalSetByAssociativeContainer<
      Domain, std::unordered_set<typename Domain::Point> >;
  using Object = DGtal::Object<DigitalTopology, DigitalSet>;

  DigitalSet image_set(image.domain());
  SetFromImage<Z3i::DigitalSet>::append<Image>(image_set, image, 0, 255);

  KSpace ks;
  // Domain of kspace must be padded.
  ks.init(image.domain().lowerBound(), image.domain().upperBound(), true);
  DigitalTopology::ForegroundAdjacency adjF;
  DigitalTopology::BackgroundAdjacency adjB;
  DigitalTopology topo(adjF, adjB, DGtal::DigitalTopologyProperties::JORDAN_DT);
  Object obj(topo, image_set);

#ifdef VISUALIZE
  if(visualize) {
    int argc(1);
    char** argv(nullptr);
    QApplication app(argc, argv);
    Viewer3D<> viewer(ks);
    viewer.show();

    viewer.setFillColor(Color(255, 255, 255, 255));
    viewer << image_set;

    // All kspace voxels
    // viewer.setFillColor(Color(40, 200, 55, 10));
    // viewer << all_set;

    viewer << Viewer3D<>::updateDisplay;

    app.exec();
  }
#endif

  if(reduceGraph) {
    using Graph = Object;
    const Graph& graph = obj;
    using SpatialGraph = SG::GraphAL;
    SpatialGraph sg =
        SG::spatial_graph_from_object<Object, SpatialGraph>(graph);
    // Remove extra edges
    if(removeExtraEdges) {
      if(verbose) std::cout << "Removing extra edges" << std::endl;
      size_t iterations = 0;
      while(true) {
        bool any_edge_removed = SG::remove_extra_edges(sg);
        if(any_edge_removed)
          iterations++;
        else
          break;
      }
      if(verbose)
        std::cout << "Removed extra edges iteratively " << iterations
                  << " times" << std::endl;
    }
    SpatialGraph reduced_g = SG::reduce_spatial_graph_via_dfs(sg);

    bool inPlace = true;
    if(mergeThreeConnectedNodes) {
      if(verbose) {
        std::cout << "Merging three connecting nodes... " << std::endl;
      }
      auto nodes_merged = SG::merge_three_connected_nodes(reduced_g, inPlace);
      if(verbose) {
        std::cout << nodes_merged
                  << " interconnected nodes with degree 3 were merged."
                     "Those nodes have now degree 0 if inPlace is not set"
                  << std::endl;
      }
    }

    if(mergeFourConnectedNodes) {
      if(verbose) {
        std::cout << "Merging four connecting nodes... " << std::endl;
      }
      auto nodes_merged = SG::merge_four_connected_nodes(reduced_g, inPlace);
      if(verbose) {
        std::cout << nodes_merged
                  << " interconnected nodes with degree 4 were merged."
                     "Those nodes have now degree 0 if inPlace is not set"
                  << std::endl;
      }
    }

    if(mergeTwoThreeConnectedNodes) {
      if(verbose) {
        std::cout << "Merging two degree 3 nodes... " << std::endl;
      }
      auto nodes_merged =
          SG::merge_two_three_connected_nodes(reduced_g, inPlace);
      if(verbose) {
        std::cout << nodes_merged
                  << " two interconnected nodes with degree 3 "
                     "and no edge points between them  were merged. "
                     "Those nodes have now degree 0 if inPlace is not set"
                  << std::endl;
      }
    }

    if(checkParallelEdges) {
      if(verbose) std::cout << "Checking parallel edges... " << std::endl;

      auto parallel_edges = SG::get_parallel_edges(reduced_g);
      auto equal_parallel_edges =
          SG::get_equal_parallel_edges(parallel_edges, reduced_g);
      if(verbose) {
        std::cout << "Found " << parallel_edges.size() << " parallel edges. "
                  << equal_parallel_edges.size() << " are equal!." << std::endl;

        if(!equal_parallel_edges.empty()) {
          std::cout << "Equal parallel edges between vertex:\n";
          for(const auto& edge_pair : equal_parallel_edges)
            std::cout << boost::source(edge_pair.first, reduced_g) << "---"
                      << boost::target(edge_pair.first, reduced_g) << std::endl;
        }
      }
    }

    ItkImageType::SpacingType itk_spacing;
    itk_spacing.Fill(1.0);
    if(!avoid_transformToPhysicalPoints) {
      if(verbose) {
        // Print metadata of itk image
        std::cout << "Origin: " << reader->GetOutput()->GetOrigin()
                  << std::endl;
        std::cout << "Spacing: " << reader->GetOutput()->GetSpacing()
                  << std::endl;
        std::cout << "Direction:\n " << reader->GetOutput()->GetDirection()
                  << std::endl;
      }
      itk_spacing = reader->GetOutput()->GetSpacing();
      SG::transform_graph_to_physical_space<ItkImageType>(reduced_g,
                                                          reader->GetOutput());
      if(spacing != "") {
        std::istringstream in(spacing);
        double sp;
        for(size_t i = 0; i < ItkImageType::ImageDimension; i++) {
          in >> sp;
          itk_spacing[i] = sp;
        }

        if(verbose) {
          std::cout << "Changing Spacing to: " << itk_spacing << std::endl;
        }

        reader->GetOutput()->SetSpacing(itk_spacing);
        SG::transform_graph_to_physical_space<ItkImageType>(
            reduced_g, reader->GetOutput());
      }
    }
    // Format itk_spacing into a string:
    std::ostringstream sp_stream;
    sp_stream << itk_spacing[0] << "_" << itk_spacing[1] << "_"
              << itk_spacing[2];
    auto sp_string = sp_stream.str();
    std::cout << "spacing: " << sp_string << std::endl;

    // Check unique points of graph
    auto repeated_points = SG::check_unique_points_in_graph(reduced_g);
    if(repeated_points.second) {
      std::cout << "Warning: duplicated points exist in reduced_g"
                   "Repeated Points: "
                << repeated_points.first.size() << std::endl;
      for(const auto& p : repeated_points.first) {
        SG::print_pos(std::cout, p);
        std::cout << std::endl;
      }
    }

    if(exportReducedGraph) {
      string exportReducedGraph_filename =
          vm["exportReducedGraph"].as<string>();
      boost::dynamic_properties dp;
      dp.property("node_id", boost::get(boost::vertex_index, reduced_g));
      dp.property("spatial_node", boost::get(boost::vertex_bundle, reduced_g));
      dp.property("spatial_edge", boost::get(boost::edge_bundle, reduced_g));
      {
        const fs::path output_folder_path{exportReducedGraph_filename};
        if(!fs::exists(output_folder_path)) {
          throw std::runtime_error("output folder doesn't exist : " +
                                   output_folder_path.string());
        }
        std::string output_full_string = output_file_path.string();
        if(!output_filename_simple) {
          output_full_string += "_sp" + sp_string +
                                (removeExtraEdges ? "_c" : "") +
                                (mergeThreeConnectedNodes ? "_m" : "");
        }
        fs::path output_full_path =
            output_folder_path / fs::path(output_full_string + ".dot");
        std::ofstream out;
        out.open(output_full_path.string().c_str());

        boost::write_graphviz_dp(out, reduced_g, dp);
        if(verbose)
          std::cout << "Output reduced graph (graphviz) to: "
                    << output_full_path.string() << std::endl;
      }
    }

    if(exportSerialized) {
      string exportReducedGraph_filename =
          vm["exportReducedGraph"].as<string>();
      const fs::path output_folder_path{exportReducedGraph_filename};
      if(!fs::exists(output_folder_path)) {
        throw std::runtime_error("output folder doesn't exist : " +
                                 output_folder_path.string());
      }

      std::string output_full_string = output_file_path.string();
      if(!output_filename_simple) {
        output_full_string += "_sp" + sp_string +
                              (removeExtraEdges ? "_c" : "") +
                              (mergeThreeConnectedNodes ? "_m" : "");
      }
      fs::path output_full_path =
          output_folder_path / fs::path(output_full_string + ".txt");
      SG::write_serialized_graph(reduced_g, output_full_path.string());
      if(verbose)
        std::cout << "Output reduced graph (serialized) to: "
                  << output_full_path.string() << std::endl;
    }
#ifdef VISUALIZE
    if(visualize) {
      SG::visualize_spatial_graph(reduced_g);
      // itk::Testing::ViewImage(reader->GetOutput());
      SG::visualize_spatial_graph_with_image(reduced_g, reader->GetOutput());
    }
#endif

    if(exportData) {
      fs::path data_output_folder_path =
          fs::path(vm["exportData"].as<string>());

      // string exportHistograms_filename = vm["exportHistograms"].as<string>();
      // const fs::path histo_output_folder_path{exportHistograms_filename};
      // if(!fs::exists(histo_output_folder_path)) {
      //     throw std::runtime_error("histo_output folder doesn't exist : " +
      //     histo_output_folder_path.string());
      // }
      // fs::path histo_output_full_path = histo_output_folder_path / fs::path(
      //     output_file_path.string() +
      //     ( removeExtraEdges ? "_c" : "")   +
      //     ( mergeThreeConnectedNodes ? "_m" : "")   +
      //     ( ignoreAngleBetweenParallelEdges ? "_iPA" : "")   +
      //     ( ignoreEdgesToEndNodes ? "_x" : "")   +
      //     ( ignoreEdgesShorterThan ?
      //        "_iShort" + std::to_string(ignoreEdgesShorterThan)  : "") +
      //     "_bD" + std::to_string(binsHistoDegrees)  +
      //     "_bA" + std::to_string(binsHistoAngles)  +
      //     "_bC" + std::to_string(binsHistoCosines)  +
      //     "_wL" + std::to_string(widthHistoDistances)  +
      //     ".histo");
      // std::ofstream histo_out;
      // histo_out.open(histo_output_full_path.string().c_str());

      if(!fs::exists(data_output_folder_path)) {
        throw std::runtime_error("data_output folder doesn't exist : " +
                                 data_output_folder_path.string());
      }

      std::string data_output_full_string = output_file_path.string() + "_DATA";
      if(!output_filename_simple) {
        data_output_full_string +=
            ("_sp" + sp_string) + (removeExtraEdges ? "_c" : "") +
            (mergeThreeConnectedNodes ? "_m" : "") +
            (ignoreAngleBetweenParallelEdges ? "_iPA" : "") +
            (ignoreEdgesToEndNodes ? "_x" : "") +
            (ignoreEdgesShorterThan
                 ? "_iShort" + std::to_string(ignoreEdgesShorterThan)
                 : "");
      }
      fs::path data_output_full_path =
          data_output_folder_path / fs::path(data_output_full_string + ".txt");
      std::ofstream data_out;
      data_out.setf(std::ios_base::fixed, std::ios_base::floatfield);
      data_out.open(data_output_full_path.string().c_str());
      // Degrees
      {
        auto degrees = SG::compute_degrees(reduced_g);
        // auto histo_degrees = SG::histogram_degrees(degrees,
        // binsHistoDegrees); SG::print_histogram(histo_degrees, histo_out);
        {
          data_out.precision(
              std::numeric_limits<decltype(degrees)::value_type>::max_digits10);
          data_out << "# degrees" << std::endl;
          std::ostream_iterator<decltype(degrees)::value_type> out_iter(
              data_out, " ");
          std::copy(std::begin(degrees), std::end(degrees), out_iter);
          data_out << std::endl;
        }
      }
      // EndToEnd Distances
      {
        auto ete_distances = SG::compute_ete_distances(
            reduced_g, ignoreEdgesShorterThan, ignoreEdgesToEndNodes);

        auto range_ptr =
            std::minmax_element(ete_distances.begin(), ete_distances.end());
        if(verbose) {
          std::cout << "Min Distance: " << *range_ptr.first << std::endl;
          std::cout << "Max Distance: " << *range_ptr.second << std::endl;
        }
        // auto histo_ete_distances = SG::histogram_ete_distances(ete_distances,
        // widthHistoDistances); SG::print_histogram(histo_ete_distances,
        // histo_out);
        {
          data_out.precision(std::numeric_limits<decltype(
                                 ete_distances)::value_type>::max_digits10);
          data_out << "# ete_distances" << std::endl;
          std::ostream_iterator<decltype(ete_distances)::value_type> out_iter(
              data_out, " ");
          std::copy(std::begin(ete_distances), std::end(ete_distances),
                    out_iter);
          data_out << std::endl;
        }
      }
      // Angles between adjacent edges
      {
        auto angles = SG::compute_angles(reduced_g, ignoreEdgesShorterThan,
                                         ignoreAngleBetweenParallelEdges,
                                         ignoreEdgesToEndNodes);
        // auto histo_angles = SG::histogram_angles( angles, binsHistoAngles );
        // SG::print_histogram(histo_angles, histo_out);
        {
          data_out.precision(
              std::numeric_limits<decltype(angles)::value_type>::max_digits10);
          data_out << "# angles" << std::endl;
          std::ostream_iterator<decltype(angles)::value_type> out_iter(data_out,
                                                                       " ");
          std::copy(std::begin(angles), std::end(angles), out_iter);
          data_out << std::endl;
        }
        // Cosines of those angles
        {
          auto cosines = SG::compute_cosines(angles);
          // auto histo_cosines = SG::histogram_cosines( cosines,
          // binsHistoCosines ); SG::print_histogram(histo_cosines, histo_out);
          {
            data_out.precision(std::numeric_limits<decltype(
                                   cosines)::value_type>::max_digits10);
            data_out << "# cosines" << std::endl;
            std::ostream_iterator<decltype(cosines)::value_type> out_iter(
                data_out, " ");
            std::copy(std::begin(cosines), std::end(cosines), out_iter);
            data_out << std::endl;
          }
        }
      }
      // Contour length
      {
        auto contour_lengths = SG::compute_contour_lengths(
            reduced_g, ignoreEdgesShorterThan, ignoreEdgesToEndNodes);
        // auto histo_contour_lengths =
        // SG::histogram_contour_lengths(contour_lengths, widthHistoDistances);
        // SG::print_histogram(histo_contour_lengths, histo_out);
        {
          data_out.precision(std::numeric_limits<decltype(
                                 contour_lengths)::value_type>::max_digits10);
          data_out << "# contour_lengths" << std::endl;
          std::ostream_iterator<decltype(contour_lengths)::value_type> out_iter(
              data_out, " ");
          std::copy(std::begin(contour_lengths), std::end(contour_lengths),
                    out_iter);
          data_out << std::endl;
        }
      }
      if(verbose) {
        std::cout << "Output data to: " << data_output_full_path.string()
                  << std::endl;
        // std::cout << "Output histograms to: " <<
        // histo_output_full_path.string() << std::endl;
      }
    }  // end export histograms
  }
/**
 * Example of a test. To be completed.
 *
 */
bool testLocalConvolutionNormalVectorEstimator ( int /*argc*/, char**/*argv*/ )
{
    unsigned int nbok = 0;
    unsigned int nb = 0;

    trace.beginBlock ( "Testing convolution neighborhood ..." );

    std::string filename = testPath + "samples/cat10.vol";

    typedef ImageSelector < Z3i::Domain, int>::Type Image;
    Image image = VolReader<Image>::importVol ( filename );
    trace.info() <<image<<std::endl;
    DigitalSet set3d ( image.domain() );
    SetFromImage<DigitalSet>::append<Image> ( set3d, image,
            0,256 );

    KSpace ks;
    bool space_ok = ks.init ( image.domain().lowerBound(),
                              image.domain().upperBound(), true );
    if ( !space_ok )
    {
        trace.error() << "Error in the Khamisky space construction."<<std::endl;
        return 2;
    }
    trace.endBlock();
    typedef SurfelAdjacency<KSpace::dimension> MySurfelAdjacency;
    MySurfelAdjacency surfAdj ( true ); // interior in all directions.

    trace.beginBlock ( "Set up digital surface." );
    typedef LightImplicitDigitalSurface<KSpace, DigitalSet >
      MyDigitalSurfaceContainer;
    typedef DigitalSurface<MyDigitalSurfaceContainer> MyDigitalSurface;
    SCell bel = Surfaces<KSpace>::findABel ( ks, set3d, 100000 );
    MyDigitalSurfaceContainer* ptrSurfContainer =
        new MyDigitalSurfaceContainer ( ks, set3d, surfAdj, bel );
    MyDigitalSurface digSurf ( ptrSurfContainer ); // acquired
    MyDigitalSurface::ConstIterator it = digSurf.begin();
    trace.endBlock();

    trace.beginBlock ( "Compute and output surface <cat10-constant.off> with trivial normals." );
    //Convolution kernel
    ConstantConvolutionWeights<MyDigitalSurface::Size> kernel;

    //Estimator definition
    typedef LocalConvolutionNormalVectorEstimator
    < MyDigitalSurface,
    ConstantConvolutionWeights<MyDigitalSurface::Size> > MyConstantEstimator;
    BOOST_CONCEPT_ASSERT ( ( CNormalVectorEstimator< MyConstantEstimator > ) );
    MyConstantEstimator myNormalEstimator ( digSurf, kernel );

    // Embedder definition
    typedef CanonicDigitalSurfaceEmbedder<MyDigitalSurface> SurfaceEmbedder;
    SurfaceEmbedder surfaceEmbedder ( digSurf );
    typedef DigitalSurfaceEmbedderWithNormalVectorEstimator
    < SurfaceEmbedder, MyConstantEstimator > SurfaceEmbedderWithTrivialNormal;
    SurfaceEmbedderWithTrivialNormal mySurfelEmbedder ( surfaceEmbedder,
            myNormalEstimator );

    // Compute normal vector field and displays it.
    myNormalEstimator.init ( 1.0, 2 );

    MyConstantEstimator::Quantity res = myNormalEstimator.eval ( it );
    trace.info() << "Normal vector at begin() : "<< res << std::endl;

    ofstream out ( "cat10-constant.off" );
    if ( out.good() )
        digSurf.exportAs3DNOFF ( out,mySurfelEmbedder );
    out.close();
    trace.endBlock();

    trace.beginBlock ( "Compute and output surface <cat10-gaussian.off> with gaussian convoluted normals." );

    //Convolution kernel
    GaussianConvolutionWeights < MyDigitalSurface::Size > Gkernel ( 4.0 );

    //Estimator definition
    typedef LocalConvolutionNormalVectorEstimator  < MyDigitalSurface,
            GaussianConvolutionWeights< MyDigitalSurface::Size>  > MyGaussianEstimator;
    BOOST_CONCEPT_ASSERT ( ( CNormalVectorEstimator< MyGaussianEstimator > ) );
    MyGaussianEstimator myNormalEstimatorG ( digSurf, Gkernel );

    // Embedder definition
    typedef DigitalSurfaceEmbedderWithNormalVectorEstimator<SurfaceEmbedder,MyGaussianEstimator> SurfaceEmbedderWithGaussianNormal;
    SurfaceEmbedderWithGaussianNormal mySurfelEmbedderG ( surfaceEmbedder, myNormalEstimatorG );

    // Compute normal vector field and displays it.
    myNormalEstimatorG.init ( 1.0, 5 );

    MyGaussianEstimator::Quantity res2 = myNormalEstimatorG.eval ( it );
    trace.info() << "Normal vector at begin() : "<< res2 << std::endl;
    std::vector<MyGaussianEstimator::Quantity> allNormals;
    myNormalEstimatorG.evalAll ( std::back_inserter ( allNormals ) );
    trace.info() << "Normal vector field of size "<< allNormals.size() << std::endl;

    ofstream out2 ( "cat10-gaussian.off" );
    if ( out2.good() )
        digSurf.exportAs3DNOFF ( out2 ,mySurfelEmbedderG );
    out2.close();

    nbok += true ? 1 : 0;
    nb++;
    trace.info() << "(" << nbok << "/" << nb << ") "
                 << "true == true" << std::endl;
    trace.endBlock();

    return true;
}
/**
 * Example of a test. To be completed.
 *
 */
bool testFitting()
{
  unsigned int nbok = 0;
  unsigned int nb = 0;
  trace.beginBlock ( "Testing init ..." );

  using namespace Z3i;

  trace.beginBlock("Creating Surface");
  Point p1( -20, -20, -20 );
  Point p2( 20, 20, 20 );
   
  ImplicitBall<Z3i::Space> shape( RealPoint(6.0,0,0), 4);
  typedef GaussDigitizer<Z3i::Space, ImplicitBall<Z3i::Space> > Gauss;
  Gauss gauss;
  gauss.attach(shape);
  gauss.init(p1, p2, 1);
  
  typedef LightImplicitDigitalSurface<KSpace,  Gauss > SurfaceContainer;
  typedef DigitalSurface<SurfaceContainer> Surface;
  typedef Surface::Surfel Surfel;


  KSpace K;
  nbok += K.init( p1, p2, true ) ? 1 : 0;
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
               << "K.init() is ok" << std::endl;
  Surfel bel = Surfaces<KSpace>::findABel( K, gauss, 10000 );
  SurfaceContainer* surfaceContainer = new SurfaceContainer
    ( K, gauss, SurfelAdjacency<KSpace::dimension>( true ), bel );
  Surface surface( surfaceContainer ); // acquired
  CanonicSCellEmbedder<KSpace> embedder(surface.container().space());
  trace.endBlock();

  trace.beginBlock("Normal vector field computation");
  typedef functors::ElementaryConvolutionNormalVectorEstimator<Surfel, CanonicSCellEmbedder<KSpace> > FunctorNormal;
  typedef LocalEstimatorFromSurfelFunctorAdapter<SurfaceContainer, Z3i::L2Metric,
                                                 FunctorNormal,
                                                 DGtal::functors::GaussianKernel> ReporterNormal;
  typedef EstimatorCache<ReporterNormal> NormalCache;

  //estimator
  DGtal::functors::GaussianKernel gaussKernelFunc(5.0);
  FunctorNormal functorNormal(embedder, 1.0);
  ReporterNormal reporterNormal;
  reporterNormal.attach(surface);
  reporterNormal.setParams(l2Metric, functorNormal, gaussKernelFunc, 5.0);

  //caching normal field
  NormalCache normalCache(reporterNormal);
  normalCache.init( 1, surface.begin(), surface.end());
  trace.info() << "Normal vector field cached... "<< normalCache << std::endl;
  trace.endBlock();

  trace.beginBlock("Creating  sphere fitting adapter from normal vector field");
  typedef functors::SphereFittingEstimator<Surfel, CanonicSCellEmbedder<KSpace> , NormalCache> Functor;
  typedef functors::ConstValue< double > ConvFunctor;
  typedef LocalEstimatorFromSurfelFunctorAdapter<SurfaceContainer, Z3i::L2Metric, Functor, ConvFunctor> Reporter;

  Functor fitter(embedder,1.0, 5.0, normalCache);
  ConvFunctor convFunc(1.0);
  Reporter reporter;
  reporter.attach(surface);
  reporter.setParams(l2Metric, fitter , convFunc, 15.0);
  
  reporter.init(1, surface.begin(), surface.end());
  for(Surface::ConstIterator it = surface.begin(), ite=surface.end(); it!=ite; ++it)
    {
      Functor::Quantity val = reporter.eval( it );
      trace.info() << "Fitting = "<<val.center <<" rad="<<val.radius<<std::endl;
    }
  trace.endBlock();


  trace.endBlock();

  nbok += true ? 1 : 0;
  nb++;
  trace.info() << "(" << nbok << "/" << nb << ") "
	       << "true == true" << std::endl;

  return nbok == nb;
}
int main( int argc, char** argv )
{
    //! [greedy-plane-segmentation-ex3-parseCommandLine]
    trace.info() << "Segments the surface at given threshold within given volume into digital planes of rational width num/den." << std::endl;
    // Setting default options: ----------------------------------------------
    // input file used:
    string inputFilename =   examplesPath + "samples/Al.100.vol" ;
    trace.info() << "input file used " << inputFilename << std::endl;
    // parameter threshold
    unsigned int threshold = 0;
    trace.info() << "the value that defines the isosurface in the image (an integer between 0 and 255)= " << threshold<< std::endl;
    // parameter widthNum
    unsigned int widthNum = 1;
    trace.info() << "the numerator of the rational width (a non-null integer) =" << widthNum<< std::endl;
    // parameter widthDen
    unsigned int widthDen = 1;
    trace.info() << "the denominator of the rational width (a non-null integer)= " << widthDen<< std::endl;
    //! [greedy-plane-segmentation-ex3-parseCommandLine]

    //! [greedy-plane-segmentation-ex3-loadVolume]
    QApplication application(argc,argv);
    typedef ImageSelector < Domain, int>::Type Image;
    Image image = VolReader<Image>::importVol(inputFilename);
    DigitalSet set3d (image.domain());
    SetFromImage<DigitalSet>::append<Image>(set3d, image, threshold,255);
    //! [greedy-plane-segmentation-ex3-loadVolume]

    //! [greedy-plane-segmentation-ex3-makeSurface]
    trace.beginBlock( "Set up digital surface." );
    // We initializes the cellular grid space used for defining the
    // digital surface.
    KSpace ks;
    bool ok = ks.init( set3d.domain().lowerBound(),
                       set3d.domain().upperBound(), true );
    if ( ! ok ) std::cerr << "[KSpace.init] Failed." << std::endl;
    SurfelAdjacency<KSpace::dimension> surfAdj( true ); // interior in all directions.
    MyDigitalSurfaceContainer* ptrSurfContainer =
        new MyDigitalSurfaceContainer( ks, set3d, surfAdj );
    MyDigitalSurface digSurf( ptrSurfContainer ); // acquired
    trace.endBlock();
    //! [greedy-plane-segmentation-ex3-makeSurface]

    //! [greedy-plane-segmentation-ex3-segment]
    Point p;
    Dimension axis;
    unsigned int j = 0;
    unsigned int nb = digSurf.size();

    // First pass to find biggest planes.
    trace.beginBlock( "1) Segmentation first pass. Computes all planes so as to sort vertices by the plane size." );
    std::map<Vertex,unsigned int> v2size;
    NaivePlaneComputer planeComputer;
    std::priority_queue<VertexSize> Q;
    std::vector<Point> layer;
    for ( ConstIterator it = digSurf.begin(), itE= digSurf.end(); it != itE; ++it )
    {
        if ( ( (++j) % 50 == 0 ) || ( j == nb ) ) trace.progressBar( j, nb );
        Vertex v = *it;
        axis = ks.sOrthDir( v );
        planeComputer.init( axis, 500, widthNum, widthDen );
        // The visitor takes care of all the breadth-first traversal.
        Visitor visitor( digSurf, v );
        layer.clear();
        Visitor::Size currentSize = visitor.current().second;
        while ( ! visitor.finished() )
        {
            Visitor::Node node = visitor.current();
            v = node.first;
            axis = ks.sOrthDir( v );
            p = ks.sCoords( ks.sDirectIncident( v, axis ) );
            if ( node.second != currentSize )
            {
                // std::cerr << "Layer " << currentSize << ", size=" << layer.size() << std::endl;
                bool isExtended = planeComputer.extend( layer.begin(), layer.end() );
                if ( ! isExtended )
                    break;
                layer.clear();
                currentSize = node.second;
            }
            layer.push_back( p );
            visitor.expand();
        }
        // std::cerr << v << " -> " << planeComputer.size() << std::endl;
        Q.push( VertexSize( v, planeComputer.size() ) );
    }
    trace.endBlock();

    trace.beginBlock( "2) Segmentation second pass. Visits vertices from the one with biggest plane to the one with smallest plane." );
    std::set<Vertex> processedVertices;
    std::map<Vertex,SegmentedPlane*> v2plane;
    std::vector<SegmentedPlane*> segmentedPlanes;
    j = 0;
    while ( ! Q.empty() )
    {
        if ( ( (++j) % 50 == 0 ) || ( j == nb ) ) trace.progressBar( j, nb );
        Vertex v = Q.top().v;
        Q.pop();
        if ( processedVertices.find( v ) != processedVertices.end() ) // already in set
            continue; // process to next vertex

        SegmentedPlane* ptrSegment = new SegmentedPlane;
        segmentedPlanes.push_back( ptrSegment ); // to delete them afterwards.
        axis = ks.sOrthDir( v );
        ptrSegment->plane.init( axis, 500, widthNum, widthDen );
        // The visitor takes care of all the breadth-first traversal.
        Visitor visitor( digSurf, v );
        while ( ! visitor.finished() )
        {
            Visitor::Node node = visitor.current();
            v = node.first;
            if ( processedVertices.find( v ) == processedVertices.end() )
            {   // Vertex is not in processedVertices
                axis = ks.sOrthDir( v );
                p = ks.sCoords( ks.sDirectIncident( v, axis ) );
                bool isExtended = ptrSegment->plane.extend( p );
                if ( isExtended )
                {   // surfel is in plane.
                    processedVertices.insert( v );
                    v2plane[ v ] = ptrSegment;
                    visitor.expand();
                }
                else // surfel is not in plane and should not be used in the visit.
                    visitor.ignore();
            }
            else // surfel is already in some plane.
                visitor.ignore();
        }
        // Assign random color for each plane.
        ptrSegment->color = Color( rand() % 256, rand() % 256, rand() % 256, 255 );
    }
    trace.endBlock();
    //! [greedy-plane-segmentation-ex3-segment]

    //! [greedy-plane-segmentation-ex3-visualization]
    Viewer3D<> viewer( ks );
    viewer.show();
    Color col( 255, 255, 120 );
    for ( std::map<Vertex,SegmentedPlane*>::const_iterator
            it = v2plane.begin(), itE = v2plane.end();
            it != itE; ++it )
    {
        viewer << CustomColors3D( it->second->color, it->second->color );
        viewer << ks.unsigns( it->first );
    }
    viewer << Viewer3D<>::updateDisplay;
    //! [greedy-plane-segmentation-ex3-visualization]

    //! [greedy-plane-segmentation-ex3-freeMemory]
    for ( std::vector<SegmentedPlane*>::iterator
            it = segmentedPlanes.begin(), itE = segmentedPlanes.end();
            it != itE; ++it )
        delete *it;
    segmentedPlanes.clear();
    v2plane.clear();
    //! [greedy-plane-segmentation-ex3-freeMemory]

    return application.exec();
}
int main( int argc, char** argv )
{
  if ( argc < 4 )
    {
      usage( argc, argv );
      return 1;
    }
  std::string inputFilename = argv[ 1 ];
  unsigned int minThreshold = atoi( argv[ 2 ] );
  unsigned int maxThreshold = atoi( argv[ 3 ] );
  unsigned int idxP = (argc <= 4) ? 0 : atoi( argv[ 4 ] );

  //! [volDistanceTraversal-readVol]
  trace.beginBlock( "Reading vol file into an image." );
  typedef ImageSelector < Domain, int>::Type Image;
  Image image = VolReader<Image>::importVol(inputFilename);
  DigitalSet set3d (image.domain());
  SetFromImage<DigitalSet>::append<Image>(set3d, image, 
                                          minThreshold, maxThreshold);
  trace.endBlock();
  //! [volDistanceTraversal-readVol]
  
  
  //! [volDistanceTraversal-KSpace]
  trace.beginBlock( "Construct the Khalimsky space from the image domain." );
  KSpace ks;
  bool space_ok = ks.init( image.domain().lowerBound(), 
                           image.domain().upperBound(), true );
  if (!space_ok)
    {
      trace.error() << "Error in the Khamisky space construction."<<std::endl;
      return 2;
    }
  trace.endBlock();
  //! [volDistanceTraversal-KSpace]

  //! [volDistanceTraversal-SurfelAdjacency]
  typedef SurfelAdjacency<KSpace::dimension> MySurfelAdjacency;
  MySurfelAdjacency surfAdj( true ); // interior in all directions.
  //! [volDistanceTraversal-SurfelAdjacency]

  //! [volDistanceTraversal-SetUpDigitalSurface]
  trace.beginBlock( "Set up digital surface." );
  typedef LightImplicitDigitalSurface<KSpace, DigitalSet > 
    MyDigitalSurfaceContainer;
  typedef DigitalSurface<MyDigitalSurfaceContainer> MyDigitalSurface;
  SCell bel = Surfaces<KSpace>::findABel( ks, set3d, 100000 );
  MyDigitalSurfaceContainer* ptrSurfContainer = 
    new MyDigitalSurfaceContainer( ks, set3d, surfAdj, bel );
  MyDigitalSurface digSurf( ptrSurfContainer ); // acquired
  trace.endBlock();
  // Find first bel.
  MyDigitalSurface::ConstIterator it = digSurf.begin();
  for ( idxP = idxP % digSurf.size(); idxP != 0; --idxP ) ++it;
  bel = *it;
  //! [volDistanceTraversal-SetUpDigitalSurface]

  //! [volDistanceTraversal-ExtractingSurface]
  trace.beginBlock( "Extracting boundary by distance tracking from an initial bel." );
  typedef CanonicSCellEmbedder<KSpace> SCellEmbedder;
  typedef SCellEmbedder::Value RealPoint;
  typedef RealPoint::Coordinate Scalar;
  typedef ExactPredicateLpSeparableMetric<Space,2> Distance;

  typedef std::binder1st< Distance > DistanceToPoint; 
  typedef Composer<SCellEmbedder, DistanceToPoint, Scalar> VertexFunctor;
  typedef DistanceBreadthFirstVisitor< MyDigitalSurface, VertexFunctor, std::set<SCell> > 
    MyDistanceVisitor;
  typedef MyDistanceVisitor::Node MyNode;
  typedef MyDistanceVisitor::Scalar MySize;

  SCellEmbedder embedder;
  Distance distance;
  DistanceToPoint distanceToPoint = std::bind1st( distance, embedder( bel ) );
  VertexFunctor vfunctor( embedder, distanceToPoint );
  MyDistanceVisitor visitor( digSurf, vfunctor, bel );

  unsigned long nbSurfels = 0;
  MyNode node;
  while ( ! visitor.finished() )
    {
      node = visitor.current();
      ++nbSurfels;
      visitor.expand();
    }
  MySize maxDist = node.second;
  trace.endBlock();
  //! [volDistanceTraversal-ExtractingSurface]

  //! [volDistanceTraversal-DisplayingSurface]
  trace.beginBlock( "Displaying surface in Viewer3D." );
  QApplication application(argc,argv);
  Viewer3D<> viewer;
  viewer.show(); 
  HueShadeColorMap<MySize,1> hueShade( 0, maxDist );
  MyDistanceVisitor visitor2( digSurf, vfunctor, bel );
  viewer << CustomColors3D( Color::Black, Color::White )
         << ks.unsigns( bel );
  visitor2.expand();
  std::vector< MyDistanceVisitor::Node > layer;
  while ( ! visitor2.finished() )
    {
      MyNode n = visitor2.current(); 
      Color c = hueShade( n.second );
      viewer << CustomColors3D( Color::Red, c )
             << ks.unsigns( n.first );
      visitor2.expand();
    }
  viewer << Viewer3D<>::updateDisplay;
  trace.info() << "nb surfels = " << nbSurfels << std::endl;
  trace.endBlock();
  return application.exec();
  //! [volDistanceTraversal-DisplayingSurface]
}
Exemple #26
0
int main( int argc, char** argv )
{
  if ( argc < 5 )
    {
      usage( argc, argv );
      return 1;
    }
  std::string inputFilename = argv[ 1 ];
  unsigned int minThreshold = atoi( argv[ 2 ] );
  unsigned int maxThreshold = atoi( argv[ 3 ] );
  bool intAdjacency = atoi( argv[ 4 ] ) == 0;

  typedef ImageSelector < Domain, int>::Type Image;
      
  //! [viewMarchingCubes-readVol]
  trace.beginBlock( "Reading vol file into an image." );
  Image image = VolReader<Image>::importVol(inputFilename);
  DigitalSet set3d (image.domain());
  SetFromImage<DigitalSet>::append<Image>(set3d, image,
                                          minThreshold, maxThreshold);
  trace.endBlock();
  //! [viewMarchingCubes-readVol]


  //! [viewMarchingCubes-KSpace]
  trace.beginBlock( "Construct the Khalimsky space from the image domain." );
  KSpace ks;
  bool space_ok = ks.init( image.domain().lowerBound(),
                           image.domain().upperBound(), true );
  if (!space_ok)
    {
      trace.error() << "Error in the Khamisky space construction."<<std::endl;
      return 2;
    }
  trace.endBlock();
  //! [viewMarchingCubes-KSpace]

  //! [viewMarchingCubes-SurfelAdjacency]
  typedef SurfelAdjacency<KSpace::dimension> MySurfelAdjacency;
  MySurfelAdjacency surfAdj( intAdjacency ); // interior in all directions.
  //! [viewMarchingCubes-SurfelAdjacency]

  //! [viewMarchingCubes-ExtractingSurface]
  trace.beginBlock( "Extracting boundary by scanning the space. " );
  typedef KSpace::SurfelSet SurfelSet;
  typedef SetOfSurfels< KSpace, SurfelSet > MySetOfSurfels;
  typedef DigitalSurface< MySetOfSurfels > MyDigitalSurface;
  MySetOfSurfels theSetOfSurfels( ks, surfAdj );
  Surfaces<KSpace>::sMakeBoundary( theSetOfSurfels.surfelSet(),
                                   ks, set3d,
                                   image.domain().lowerBound(),
                                   image.domain().upperBound() );
  MyDigitalSurface digSurf( theSetOfSurfels );
  trace.info() << "Digital surface has " << digSurf.size() << " surfels."
               << std::endl;
  trace.endBlock();
  //! [viewMarchingCubes-ExtractingSurface]

  //! [viewMarchingCubes-makingMesh]
  trace.beginBlock( "Making triangulated surface. " );
  typedef CanonicEmbedder< Space >                                  TrivialEmbedder;
  typedef ImageLinearCellEmbedder< KSpace, Image, TrivialEmbedder > CellEmbedder;
  typedef CellEmbedder::Value                                       RealPoint;
  typedef TriangulatedSurface< RealPoint >                          TriMesh;
  typedef Mesh< RealPoint >                                         ViewMesh;
  typedef std::map< MyDigitalSurface::Vertex, TriMesh::Index >      VertexMap;
  TriMesh         trimesh;
  ViewMesh        viewmesh;
  TrivialEmbedder trivialEmbedder;
  CellEmbedder    cellEmbedder;
  // The +0.5 is to avoid isosurface going exactly through a voxel
  // center, especially for binary volumes.
  cellEmbedder.init( ks, image, trivialEmbedder, 
                     ( (double) minThreshold ) + 0.5 );
  VertexMap vmap; // stores the map Vertex -> Index
  MeshHelpers::digitalSurface2DualTriangulatedSurface
    ( digSurf, cellEmbedder, trimesh, vmap );
  trace.info() << "Triangulated surface is " << trimesh << std::endl;
  MeshHelpers::triangulatedSurface2Mesh( trimesh, viewmesh );
  trace.info() << "Mesh has " << viewmesh.nbVertex()
               << " vertices and " << viewmesh.nbFaces() << " faces." << std::endl;
  trace.endBlock();
  //! [viewMarchingCubes-makingMesh]

  QApplication application(argc,argv);
  Viewer3D<> viewer;
  viewer.show();
  viewer.setLineColor(Color(150,0,0,254));
  viewer << viewmesh;
  viewer << Viewer3D<>::updateDisplay;
  application.exec();
  
}
bool
lengthEstimators( const std::string & /*name*/,
      Shape & aShape, 
      double h )
{
  // Types
  typedef typename Space::Point Point;
  typedef typename Space::Vector Vector;
  typedef typename Space::RealPoint RealPoint;
  typedef typename Space::Integer Integer;
  typedef HyperRectDomain<Space> Domain;
  typedef KhalimskySpaceND<Space::dimension,Integer> KSpace;
  typedef typename KSpace::SCell SCell;
  typedef typename GridCurve<KSpace>::PointsRange PointsRange;
  typedef typename GridCurve<KSpace>::ArrowsRange ArrowsRange;

  // Digitizer
  GaussDigitizer<Space,Shape> dig;  
  dig.attach( aShape ); // attaches the shape.
  Vector vlow(-1,-1); Vector vup(1,1);
  dig.init( aShape.getLowerBound()+vlow, aShape.getUpperBound()+vup, h ); 
  Domain domain = dig.getDomain();

  // Create cellular space
  KSpace K;
  bool ok = K.init( dig.getLowerBound(), dig.getUpperBound(), true );
  if ( ! ok )
    {
      std::cerr << "[lengthEstimators]"
    << " error in creating KSpace." << std::endl;
      return false;
    }
  try {
    // Extracts shape boundary
    SurfelAdjacency<KSpace::dimension> SAdj( true );
    SCell bel = Surfaces<KSpace>::findABel( K, dig, 10000 );
    // Getting the consecutive surfels of the 2D boundary
    std::vector<Point> points;
    Surfaces<KSpace>::track2DBoundaryPoints( points, K, SAdj, dig, bel );
    // Create GridCurve
    GridCurve<KSpace> gridcurve;
    gridcurve.initFromVector( points );
    // Ranges
    ArrowsRange ra = gridcurve.getArrowsRange(); 
    PointsRange rp = gridcurve.getPointsRange(); 


    // Estimations
    typedef typename PointsRange::ConstIterator ConstIteratorOnPoints; 
    typedef ParametricShapeArcLengthFunctor< Shape > Length;
    TrueGlobalEstimatorOnPoints< ConstIteratorOnPoints, Shape, Length  >  trueLengthEstimator;
    trueLengthEstimator.init( h, rp.begin(), rp.end(), &aShape, gridcurve.isClosed());

    L1LengthEstimator< typename ArrowsRange::ConstCirculator > l1length;
    DSSLengthEstimator< typename PointsRange::ConstCirculator > DSSlength;
    MLPLengthEstimator< typename PointsRange::ConstIterator > MLPlength;
    FPLengthEstimator< typename PointsRange::ConstIterator > FPlength;
    BLUELocalLengthEstimator< typename ArrowsRange::ConstIterator > BLUElength;
    RosenProffittLocalLengthEstimator< typename ArrowsRange::ConstIterator > RosenProffittlength;
  
    // Output
    double trueValue = trueLengthEstimator.eval();
    double l1, blue, rosen,dss,mlp,fp;
    double Tl1, Tblue, Trosen,Tdss,Tmlp,Tfp;
    
    Clock c;

    //Length evaluation & timing
    c.startClock();
    l1length.init(h, ra.c(), ra.c());
    l1 = l1length.eval();
    Tl1 = c.stopClock();
    
    c.startClock();
    BLUElength.init(h, ra.begin(), ra.end(), gridcurve.isClosed());
    blue = BLUElength.eval();
    Tblue = c.stopClock();
    
    c.startClock();
    RosenProffittlength.init(h, ra.begin(), ra.end(), gridcurve.isClosed());
    rosen = RosenProffittlength.eval();
    Trosen = c.stopClock();
    
    c.startClock();
    DSSlength.init(h, rp.c(), rp.c());
    dss = DSSlength.eval();
    Tdss = c.stopClock();
    
    c.startClock();
    MLPlength.init(h, rp.begin(), rp.end(), gridcurve.isClosed());
    mlp = MLPlength.eval();
    Tmlp = c.stopClock();

    c.startClock();
    FPlength.init(h, rp.begin(), rp.end(), gridcurve.isClosed());
    fp = FPlength.eval();
    Tfp = c.stopClock();

    std::cout << std::setprecision( 15 ) << h << " " << rp.size() << " " << trueValue 
   << " " << l1
   << " " << blue
   << " " << rosen
   << " " << dss
   << " " << mlp   
   << " " << fp
         << " " << Tl1
   << " " << Tblue
   << " " << Trosen
   << " " << Tdss
   << " " << Tmlp
   << " " << Tfp     
   << std::endl;
    return true;
  }    
  catch ( InputException e )
    {
      std::cerr << "[lengthEstimators]"
    << " error in finding a bel." << std::endl;
      return false;
    }
}
Exemple #28
0
int main( int argc, char** argv )
{
  QApplication application(argc,argv);

  typedef Z3i::Space Space;
  typedef Z3i::KSpace KSpace;
  typedef Z3i::Point Point;
  typedef Z3i::RealPoint RealPoint;
  typedef Z3i::RealVector RealVector;
  typedef HyperRectDomain<Space> Domain;
  typedef KSpace::Surfel Surfel;
  typedef KSpace::Cell Cell;

  typedef ImageSelector<Domain, unsigned char>::Type Image;
  typedef functors::IntervalForegroundPredicate<Image> ThresholdedImage;
  typedef ImplicitDigitalSurface< KSpace, ThresholdedImage > DigitalSurfaceContainer;

  //! [DVCM3D-typedefs]
  typedef ExactPredicateLpSeparableMetric<Space, 2> Metric;          // L2-metric type
  typedef functors::HatPointFunction<Point,double>  KernelFunction;  // chi function type 
  typedef VoronoiCovarianceMeasureOnDigitalSurface< DigitalSurfaceContainer, Metric,
                                                    KernelFunction > VCMOnSurface;
  typedef VCMOnSurface::Surfel2Normals::const_iterator S2NConstIterator;
  //! [DVCM3D-typedefs]

  string inputFilename = examplesPath + "samples/Al.100.vol";
  trace.info() << "File             = " << inputFilename << std::endl;
  int thresholdMin = 0;
  trace.info() << "Min image thres. = " << thresholdMin << std::endl;
  int thresholdMax = 1;
  trace.info() << "Max image thres. = " << thresholdMax << std::endl;
  const double R = 20;
  trace.info() << "Big radius     R = " << R << std::endl;
  const double r = 3;
  trace.info() << "Small radius   r = " << r << std::endl;
  const double trivial_r = 3;
  trace.info() << "Trivial radius t = " << trivial_r << std::endl; // for orienting the directions given by the tensor.
  const double T = 0.1;
  trace.info() << "Feature thres. T = " << T << std::endl; // threshold for displaying features as red.

  const double size = 1.0; // size of displayed normals.

  KSpace ks;
  // Reads the volume
  trace.beginBlock( "Loading image into memory and build digital surface." );
  Image image = GenericReader<Image>::import(inputFilename );
  ThresholdedImage thresholdedImage( image, thresholdMin, thresholdMax );
  trace.endBlock();
  trace.beginBlock( "Extracting boundary by scanning the space. " );
  ks.init( image.domain().lowerBound(),
                           image.domain().upperBound(), true );
  SurfelAdjacency<KSpace::dimension> surfAdj( true ); // interior in all directions.
  Surfel bel = Surfaces<KSpace>::findABel( ks, thresholdedImage, 10000 );
  DigitalSurfaceContainer* container = 
    new DigitalSurfaceContainer( ks, thresholdedImage, surfAdj, bel, false  );
  DigitalSurface< DigitalSurfaceContainer > surface( container ); //acquired
  trace.info() << "Digital surface has " << surface.size() << " surfels." << std::endl;
  trace.endBlock();

  //! [DVCM3D-instantiation]
  Surfel2PointEmbedding embType = Pointels; // Could be Pointels|InnerSpel|OuterSpel; 
  Metric l2;                                // Euclidean L2 metric 
  KernelFunction chi( 1.0, r );             // hat function with support of radius r
  VCMOnSurface vcm_surface( surface, embType, R, r, 
                            chi, trivial_r, l2, true /* verbose */ );
  //! [DVCM3D-instantiation]

  trace.beginBlock( "Displaying VCM" );
  Viewer3D<> viewer( ks );
  Cell dummy;
  viewer.setWindowTitle("3D VCM viewer");
  viewer << SetMode3D( dummy.className(), "Basic" );
  viewer.show();

  GradientColorMap<double> grad( 0, T );
  grad.addColor( Color( 128, 128, 255 ) );
  grad.addColor( Color( 255, 255, 255 ) );
  grad.addColor( Color( 255, 255, 0 ) );
  grad.addColor( Color( 255, 0, 0 ) );
  RealVector lambda; // eigenvalues of chi-vcm
  for ( S2NConstIterator it = vcm_surface.mapSurfel2Normals().begin(), 
          itE = vcm_surface.mapSurfel2Normals().end(); it != itE; ++it )
    {
      Surfel s = it->first;
      Point kp = ks.sKCoords( s );
      RealPoint rp( 0.5 * (double) kp[ 0 ], 0.5 * (double) kp[ 1 ], 0.5 * (double) kp[ 2 ] );
      RealVector n = it->second.vcmNormal;
      vcm_surface.getChiVCMEigenvalues( lambda, s );
      double ratio = lambda[ 1 ] / ( lambda[ 0 ] + lambda[ 1 ] + lambda[ 2 ] ); 
      viewer.setFillColor( grad( ratio > T ? T : ratio ) );
      viewer << ks.unsigns( s );
      n *= size;
      viewer.setLineColor( Color::Black );
      viewer.addLine( rp + n, rp - n, 0.1 );
     }
  viewer << Viewer3D<>::updateDisplay;
  application.exec();
  trace.endBlock();
  return 0;
}
Exemple #29
0
int main( int argc, char** argv )
{
  //! [greedy-plane-segmentation-parseCommandLine]
  // parse command line ----------------------------------------------
  po::options_description general_opt("Allowed options are: ");
  general_opt.add_options()
    ("help,h", "display this message")
    ("input-file,i", po::value<std::string>()->default_value( examplesPath + "samples/Al.100.vol" ), "the volume file (.vol)" )
    ("threshold,t",  po::value<unsigned int>()->default_value(1), "the value that defines the isosurface in the image (an integer between 0 and 255)." )
    ("width-num,w",  po::value<unsigned int>()->default_value(1), "the numerator of the rational width (a non-null integer)." )
    ("width-den,d",  po::value<unsigned int>()->default_value(1), "the denominator of the rational width (a non-null integer)." );
  
  bool parseOK = true;
  po::variables_map vm;
  try {
    po::store(po::parse_command_line(argc, argv, general_opt), vm);  
  } catch ( const std::exception & ex ) {
    parseOK = false;
    trace.info() << "Error checking program options: "<< ex.what()<< endl;
  }
  po::notify(vm);
  if ( ! parseOK || vm.count("help") || ( argc <= 1 ) )
    {
      std::cout << "Usage: " << argv[0]
                << " [-i <fileName.vol>] [-t <threshold>] [-w <num>] [-d <den>]" << std::endl
                << "Segments the surface at given threshold within given volume into digital planes of rational width num/den." << std::endl
                << general_opt << std::endl;
      return 0;
    }
  string inputFilename = vm["input-file"].as<std::string>();
  unsigned int threshold = vm["threshold"].as<unsigned int>();
  unsigned int widthNum = vm["width-num"].as<unsigned int>();
  unsigned int widthDen = vm["width-den"].as<unsigned int>();
  //! [greedy-plane-segmentation-parseCommandLine]

  //! [greedy-plane-segmentation-loadVolume]
  QApplication application(argc,argv);
  typedef ImageSelector < Domain, int>::Type Image;
  Image image = VolReader<Image>::importVol(inputFilename);
  DigitalSet set3d (image.domain());
  SetFromImage<DigitalSet>::append<Image>(set3d, image, threshold,255);
  //! [greedy-plane-segmentation-loadVolume]

  //! [greedy-plane-segmentation-makeSurface]
  trace.beginBlock( "Set up digital surface." );
  // We initializes the cellular grid space used for defining the
  // digital surface.
  KSpace ks;
  bool ok = ks.init( set3d.domain().lowerBound(),
                     set3d.domain().upperBound(), true );
  if ( ! ok ) std::cerr << "[KSpace.init] Failed." << std::endl;
  SurfelAdjacency<KSpace::dimension> surfAdj( true ); // interior in all directions.
  MyDigitalSurfaceContainer* ptrSurfContainer = 
    new MyDigitalSurfaceContainer( ks, set3d, surfAdj );
  MyDigitalSurface digSurf( ptrSurfContainer ); // acquired
  trace.endBlock();
  //! [greedy-plane-segmentation-makeSurface]

  //! [greedy-plane-segmentation-segment]
  trace.beginBlock( "Segment into planes." );
  std::set<Vertex> processedVertices;
  std::vector<SegmentedPlane*> segmentedPlanes;
  std::map<Vertex,SegmentedPlane*> v2plane;
  Point p;
  Dimension axis;
  unsigned int j = 0;
  unsigned int nb = digSurf.size();
  for ( ConstIterator it = digSurf.begin(), itE= digSurf.end(); it != itE; ++it )
    {
      if ( ( (++j) % 50 == 0 ) || ( j == nb ) ) trace.progressBar( j, nb );
      Vertex v = *it;
      if ( processedVertices.find( v ) != processedVertices.end() ) // already in set
        continue; // process to next vertex
      
      SegmentedPlane* ptrSegment = new SegmentedPlane;
      segmentedPlanes.push_back( ptrSegment ); // to delete them afterwards.
      axis = ks.sOrthDir( v );
      ptrSegment->plane.init( axis, 500, widthNum, widthDen );
      // The visitor takes care of all the breadth-first traversal.
      Visitor visitor( digSurf, v );
      while ( ! visitor.finished() )
        {
          Visitor::Node node = visitor.current();
          v = node.first;
          if ( processedVertices.find( v ) == processedVertices.end() )
            { // Vertex is not in processedVertices
              axis = ks.sOrthDir( v );
              p = ks.sCoords( ks.sDirectIncident( v, axis ) );
              bool isExtended = ptrSegment->plane.extend( p );
              if ( isExtended ) 
                { // surfel is in plane.
                  processedVertices.insert( v );
                  v2plane[ v ] = ptrSegment;
                  visitor.expand();
                }
              else // surfel is not in plane and should not be used in the visit.
                visitor.ignore();
            }
          else // surfel is already in some plane.
            visitor.ignore();
        }
      // Assign random color for each plane.
      ptrSegment->color = Color( random() % 256, random() % 256, random() % 256, 255 );
    }
  trace.endBlock();
  //! [greedy-plane-segmentation-segment]

  //! [greedy-plane-segmentation-visualization]
  Viewer3D viewer;
  viewer.show(); 
  for ( std::map<Vertex,SegmentedPlane*>::const_iterator 
          it = v2plane.begin(), itE = v2plane.end();
        it != itE; ++it )
    {
      viewer << CustomColors3D( it->second->color, it->second->color );
      viewer << ks.unsigns( it->first );
    }
  viewer << Display3D::updateDisplay;
  //! [greedy-plane-segmentation-visualization]

  //! [greedy-plane-segmentation-freeMemory]
  for ( std::vector<SegmentedPlane*>::iterator 
          it = segmentedPlanes.begin(), itE = segmentedPlanes.end(); 
        it != itE; ++it )
    delete *it;
  segmentedPlanes.clear();
  v2plane.clear();
  //! [greedy-plane-segmentation-freeMemory]

  return application.exec();
}
Exemple #30
0
int main( int argc, char** argv )
{
  // for 3D display with Viewer3D
  QApplication application(argc,argv);

  KSpace K;
  Point plow(0,0,0);
  Point pup(3,3,2);
  Domain domain( plow, pup );
  K.init( plow, pup, true );
  //
  typedef Viewer3D<Space, KSpace> MyViewer;
  MyViewer viewer(K);
  viewer.show();
  viewer << SetMode3D( domain.className(), "Paving" );

  Cell ptlow = K.uPointel( plow ); // pointel (0*2,0*2, 0*2)
  Cell ptup1 = K.uPointel( pup ); // pointel (3*2,3*2, 2*2)
  Cell ptup2 = K.uTranslation( ptup1, Point::diagonal() ); // pointel (4*2, 4*2, 3*2)

  viewer << ptlow << ptup1 << ptup2;

  // drawing cells of dimension 0
  Cell p1= K.uCell(Point(0,0,2));  // pointel (0*2,0*2,2*2)
  Cell p2= K.uCell(Point(0,2,2));  // ...
  Cell p3= K.uCell(Point(2,2,2));
  Cell p4= K.uCell(Point(2,0,2));
  Cell p5= K.uCell(Point(0,0,4));
  Cell p6= K.uCell(Point(0,2,4));
  Cell p7= K.uCell(Point(2,2,4));
  Cell p8= K.uCell(Point(2,0,4));
  viewer << p1 << p2 << p3 << p4 << p5 << p6 << p7 << p8;

  // drawing Cells of dimension 1
  Cell linel0 = K.uCell( Point( 1, 0, 2 ) ); // linel (2*1+1, 0, 2*2)
  Cell linel1 = K.uCell( Point( 1, 2, 2 ) ); // ...
  Cell linel2 = K.uCell( Point( 0, 1, 2 ) );
  Cell linel3 = K.uCell( Point( 2, 1, 2 ) );

  Cell linel4 = K.uCell( Point( 1, 0, 4 ) );
  Cell linel5 = K.uCell( Point( 1, 2, 4 ) );
  Cell linel6 = K.uCell( Point( 0, 1, 4 ) );
  Cell linel7 = K.uCell( Point( 2, 1, 4 ) );

  Cell linel8 = K.uCell( Point( 0, 0, 3 ) );
  Cell linel9 = K.uCell( Point( 0, 2, 3 ) );
  Cell linel10 = K.uCell( Point( 2, 0, 3 ) );
  Cell linel11 = K.uCell( Point( 2, 2, 3 ) );


  Cell linel12 = K.uCell( Point( 3, 2, 2 ) );

  viewer << linel0<< linel1<< linel2 << linel3 ;
  viewer << linel4<< linel5<< linel6 << linel7 ;
  viewer << linel8<< linel9<< linel10 << linel11 << linel12;

  // drawing cells of dimension 2

  Cell surfelA = K.uCell( Point( 2, 1, 3 ) ); // surfel (2*2,2*1+1,2*3+1)
  Cell surfelB = K.uCell( Point( 1, 0, 1 ) ); // surfel (2*1,2*0,2*1+1)
  Cell surfelC = K.uCell( Point( 2, 1, 1 ) ); // surfel (2*2,2*1+1,2*1+1)
  viewer << surfelA << surfelB << surfelC;

  // drawing cells of dimension 3
  Cell vox1 = K.uCell( Point( 3, 3, 3 ) ); // voxel (2*3+1,2*3+1,2*3+1)
  Cell vox2 = K.uCell( Point( 1, 1, 3 ) ); // voxel (2*1+1,2*1+1,2*3+1)
  viewer << vox1 << vox2;

  viewer<< MyViewer::updateDisplay;
  return application.exec();

}