Beispiel #1
0
  static 
  void exportSignature(const Shape & aShape,const Set &aSet, const Z2i::Domain &aDomain)
  {
    SetPredicate<Set> aSetPredicate( aSet );
    trace.beginBlock("Extracting the boundary");
    Z2i::KSpace ks;
    bool space_ok = ks.init( aDomain.lowerBound(),aDomain.upperBound(), true );
    SurfelAdjacency<2> sAdj( true );

    ASSERT(space_ok);
    trace.info() << aSet << std::endl;
    trace.info() << ks 
		 << ( space_ok ? " Successfully instantiated" : " Error" )
		 << std::endl;

    std::vector< std::vector< Z2i::Point >  >  vectContoursBdryPointels;
    Surfaces<Z2i::KSpace>::extractAllPointContours4C( vectContoursBdryPointels,
						      ks, aSetPredicate, sAdj );  
    trace.endBlock();
    
    ///Export
    std::cout<<"## shapeGenerator signature export"<<std::endl;
    std::cout<<"## shape: "<<aShape<<std::endl;
    std::cout<<"## x\ty\tdx\tdy\tddx\tddy"<<std::endl;
    for(unsigned int i=0; i<vectContoursBdryPointels.size(); i++)
      for(unsigned int j=0 ; j< vectContoursBdryPointels.at(i).size() - 1; j++)
	{
	  Z2i::Space::Point point = (vectContoursBdryPointels.at(i).at(j) 
				     + vectContoursBdryPointels.at(i).at(j+1));
	  Z2i::Space::RealPoint midpoint (point[0]/2.0,point[1]/2.0);

	  Z2i::Space::RealPoint xp,xpp;
	  double t = aShape.parameter(midpoint);
	  xp = aShape.xp( t );
	  xpp = aShape.xpp( t );
	  
	  std::cout<< midpoint[0]<<"\t"<<midpoint[1]<<"\t"
		   << xp[0]<<"\t"<<xp[1]<<"\t"
	    	   << xpp[0]<<"\t"<<xpp[1]<<std::endl;
	    
	}
    	  
  }
Beispiel #2
0
int main( int argc, char** argv )
{
  // parse command line ----------------------------------------------
  po::options_description general_opt("Allowed options are: ");
  general_opt.add_options()
    ("help,h", "display this message")
    ("image,i", po::value<std::string>(), "image file name")
    ("min,m", po::value<int>(), "min image threshold value (default 128)")
    ("max,M", po::value<int>(), "max image threshold value (default 255)")
    
    ("minSize,s", po::value<int>(), "minSize of the extracted freeman chain (default 0)")
    ("contourSelect,s", po::value<vector <int> >()->multitoken(), 
     "Select contour according reference point and maximal distance:  ex. --contourSelect X Y distanceMax")
    ("thresholdRangeMin,r", po::value<vector <int> >()->multitoken(), 
     "use a range interval as threshold (from min) : --thresholdRangeMin min increment max : for each possible i, it define a digital sets [min, min+((i+1)*increment)] such that min+((i+1)*increment)< max  and extract their boundary. ")
    ("thresholdRangeMax,R", po::value<vector <int> >()->multitoken(), 
     "use a range interval as threshold (from max) : --thresholdRangeMax min increment max : for each possible i, it define a digital sets [ max-((i)*increment), max] such that max-((i)*increment)>min  and extract their boundary. ");
  
  
  
  po::variables_map vm;
  po::store(po::parse_command_line(argc, argv, general_opt), vm);  
  po::notify(vm);    
  if(vm.count("help")||argc<=1)
    {
      trace.info()<< "Extract FreemanChains from thresholded image" <<std::endl << "Basic usage: "<<std::endl
      << "\t image2freeman [options] --image <imageName> -min 128 -max 255 > contours.fc"<<std::endl
      << general_opt << "\n";
      return 0;
    }
  
  
  double minThreshold = 128;
  double maxThreshold = 255;
  unsigned int minSize =0;
  bool select=false;
  bool thresholdRange=vm.count("thresholdRangeMin")||vm.count("thresholdRangeMax");
  Z2i::Point selectCenter;
  unsigned int selectDistanceMax = 0; 
  

  //Parse options
  if (!(vm.count("image"))){
    trace.info() << "Image file name needed"<< endl;
    return 0;
  } 
  
  if(vm.count("min")){
    minThreshold= vm["min"].as<int>();
  } 
  if(vm.count("max")){
    maxThreshold= vm["max"].as<int>();
  } 
  if(vm.count("minSize")){
    minSize = vm["minSize"].as<int>();
  } 
  if(vm.count("contourSelect")){
    select=true;
    vector<int> cntConstraints= vm["contourSelect"].as<vector <int> >();
    if(cntConstraints.size()!=3){
      trace.info() << "Incomplete option \"--contourSelect\""<< endl;
      return 0;
    }
    selectCenter[0]= cntConstraints.at(0);
    selectCenter[1]= cntConstraints.at(1);
    selectDistanceMax= (unsigned int) cntConstraints.at(2);
  }

  int min, max, increment;
  if(! thresholdRange){
    min=(int)minThreshold;
    max= (int)maxThreshold;
    increment =  (int)(maxThreshold- minThreshold);
  }else{
    vector<int> vectRange;
    if ( vm.count("thresholdRangeMax")){
      vectRange= vm["thresholdRangeMax"].as<vector <int> >();
    }else{
      vectRange= vm["thresholdRangeMin"].as<vector <int> >();
    }
    if(vectRange.size()!=3){
      trace.info() << "Incomplete option \"--thresholdRange\""<< endl;
      return 0;
    }
    min=vectRange.at(0);
    increment=vectRange.at(1);
    max = vectRange.at(2);
    minThreshold=min;
    maxThreshold=max;
  }

 

  
  typedef ImageSelector < Z2i::Domain, unsigned char>::Type Image;
  typedef IntervalThresholder<Image::Value> Binarizer; 
  string imageFileName = vm["image"].as<std::string>();
  Image image = PNMReader<Image>::importPGM( imageFileName ); 
  
  Z2i::KSpace ks;
  if(! ks.init( image.domain().lowerBound(), 
		image.domain().upperBound(), true )){
    trace.error() << "Problem in KSpace initialisation"<< endl;
  }
  
  
  if (!thresholdRange){
    Binarizer b(min, max); 
    PointFunctorPredicate<Image,Binarizer> predicate(image, b); 
    trace.info() << "DGtal contour extraction from thresholds ["<<  min << "," << max << "]" ;
    SurfelAdjacency<2> sAdj( true );
    std::vector< std::vector< Z2i::Point >  >  vectContoursBdryPointels;
    Surfaces<Z2i::KSpace>::extractAllPointContours4C( vectContoursBdryPointels,
						      ks, predicate, sAdj );  
    if(select){
      saveSelContoursAsFC(vectContoursBdryPointels,  minSize, selectCenter,  selectDistanceMax);
    }else{
      saveAllContoursAsFc(vectContoursBdryPointels,  minSize); 
    }
  }else{
    for(int i=0; minThreshold+i*increment< maxThreshold; i++){
      if(vm.count("thresholdRangeMin")){
	min = (int)(minThreshold+(i)*increment);
      }
      if(vm.count("thresholdRangeMax")){
	max = (int)(maxThreshold-(i)*increment);
      }
      Binarizer b(min, max); 
      PointFunctorPredicate<Image,Binarizer> predicate(image, b); 
      
      trace.info() << "DGtal contour extraction from thresholds ["<<  min << "," << max << "]" ;
      SurfelAdjacency<2> sAdj( true );
      std::vector< std::vector< Z2i::Point >  >  vectContoursBdryPointels;
      Surfaces<Z2i::KSpace>::extractAllPointContours4C( vectContoursBdryPointels,
							ks, predicate, sAdj );  
      if(select){
	saveSelContoursAsFC(vectContoursBdryPointels,  minSize, selectCenter,  selectDistanceMax);
      }else{
	saveAllContoursAsFc(vectContoursBdryPointels,  minSize); 
      }
      trace.info() << " [done]" << endl;
    }
  }

    
  
}
/**
 * Algorithms that computes the alpha-shape
 * of a point set
 */
void alphaShape()
{

  //Digitization of a disk of radius 8
  Ball2D<Z2i::Space> ball(Z2i::Point(0,0), 8);
  Z2i::Domain domain(ball.getLowerBound(), ball.getUpperBound());
  Z2i::DigitalSet digitalSet(domain);   
  Shapes<Z2i::Domain>::euclideanShaper(digitalSet, ball);
  
  //Contour of the digital set
  Z2i::KSpace kspace; 
  kspace.init(domain.lowerBound()-Z2i::Point(1,1), domain.upperBound()+Z2i::Point(1,1), true); 
  typedef DigitalSetBoundary<Z2i::KSpace, Z2i::DigitalSet> DigitalSurfaceContainer;
  typedef DigitalSurface<DigitalSurfaceContainer> CustomDigitalSurface; 
  DigitalSurfaceContainer digitalSurfaceContainer( kspace, digitalSet ); 
  CustomDigitalSurface digitalSurface( digitalSurfaceContainer ); 

  //Grid curve
  Z2i::Curve gridCurve; 
  typedef DepthFirstVisitor<DigitalSurface<DigitalSurfaceContainer> > CustomVisitor;
  CustomVisitor visitor( digitalSurface, *digitalSurface.begin() );
  while ( ! visitor.finished() )
    {
      gridCurve.pushBack( visitor.current().first );
      visitor.expand();
    }

  //Point set defined as the set of (not duplicated) inner points  
  typedef Z2i::Curve::InnerPointsRange PointRange; 
  PointRange pointsRange = gridCurve.getInnerPointsRange();
  vector<Z2i::Point> border; 
  unique_copy( pointsRange.begin(), pointsRange.end(), back_inserter( border ) ); 

  //namespace for hull functions
  using namespace functions::Hull2D; 

  { //alpha = 0
    trace.info() << " alpha == 0 " << endl; 
    vector<Z2i::Point> res; 
    
    //! [Hull2D-RadiusPredicateInf]
    typedef AvnaimEtAl2x2DetSignComputer<DGtal::int64_t> DetComputer; 
    typedef InGeneralizedDiskOfGivenRadius<Z2i::Point, DetComputer> Functor; 
    Functor functor(true, 1, 0); //alpha = 0; 1/alpha -> +inf 
    typedef PredicateFromOrientationFunctor2<Functor> Predicate; 
    Predicate predicate( functor ); 
    //! [Hull2D-RadiusPredicateInf]

    //! [Hull2D-ClosedGrahamScan]
    closedGrahamScanFromAnyPoint( border.begin(), border.end(), back_inserter( res ), predicate );   
    //! [Hull2D-ClosedGrahamScan]

    //display
    Board2D board;
    drawPolygon( res.begin(), res.end(), board ); 
    board.saveSVG( "AlphaShape0.svg" );  
#ifdef WITH_CAIRO
    board.saveCairo("AlphaShape0.png", Board2D::CairoPNG);
#endif

   }

  { //alpha = 0
    trace.info() << " alpha == 0 " << endl; 
    vector<Z2i::Point> res; 
    
    //comparator and functor
    typedef InHalfPlaneBySimple3x3Matrix<Z2i::Point, DGtal::int64_t> Functor;  
    Functor functor; 
    typedef PredicateFromOrientationFunctor2<Functor> Predicate; 
    Predicate predicate( functor ); 

    closedGrahamScanFromAnyPoint( border.begin(), border.end(), back_inserter( res ), predicate );   
    
    //display
    Board2D board;
    drawPolygon( res.begin(), res.end(), board ); 
    board.saveSVG( "AlphaShape0bis.svg" );  
#ifdef WITH_CAIRO
    board.saveCairo("AlphaShape0bis.png", Board2D::CairoPNG);
#endif

   }

  //negative alpha shape
  { //alpha = -1
    trace.info() << " alpha == -1 " << endl; 
    vector<Z2i::Point> res; 

    typedef AvnaimEtAl2x2DetSignComputer<DGtal::int64_t> DetComputer; 
    typedef InGeneralizedDiskOfGivenRadius<Z2i::Point, DetComputer> Functor; 
    //! [Hull2D-RadiusPredicateM1]
    Functor functor(false, 1, 1); //1/alpha = -sqrt(1/1) = -1 
    //! [Hull2D-RadiusPredicateM1]
    typedef PredicateFromOrientationFunctor2<Functor> Predicate; 
    Predicate predicate( functor ); 

    closedGrahamScanFromAnyPoint( border.begin(), border.end(), back_inserter( res ), predicate );   
    
    //display
    Board2D board;
    drawPolygon( res.begin(), res.end(), board ); 
    board.saveSVG( "AlphaShapeM1.svg" );  
#ifdef WITH_CAIRO
    board.saveCairo("AlphaShapeM1.png", Board2D::CairoPNG);
#endif

   }

  { //alpha = -sqrt(5)
    trace.info() << " alpha == -sqrt(5) " << endl; 
    vector<Z2i::Point> res; 

    typedef AvnaimEtAl2x2DetSignComputer<DGtal::int64_t> DetComputer; 
    typedef InGeneralizedDiskOfGivenRadius<Z2i::Point, DetComputer> Functor; 
    //! [Hull2D-RadiusPredicateMsqrt5]
    Functor functor(false, 5, 1); //1/alpha = -sqrt(5) 
    //! [Hull2D-RadiusPredicateMsqrt5]
    typedef PredicateFromOrientationFunctor2<Functor> Predicate; 
    Predicate predicate( functor ); 

    closedGrahamScanFromAnyPoint( border.begin(), border.end(), back_inserter( res ), predicate );   
    
    //display
    Board2D board;
    drawPolygon( res.begin(), res.end(), board ); 
    board.saveSVG( "AlphaShapeMSqrt5.svg" );  
#ifdef WITH_CAIRO
    board.saveCairo("AlphaShapeMSqrt5.png", Board2D::CairoPNG);
#endif

  }

  { //alpha = -5
    trace.info() << " alpha == -5 " << endl; 
    vector<Z2i::Point> res; 

    typedef AvnaimEtAl2x2DetSignComputer<DGtal::int64_t> DetComputer; 
    typedef InGeneralizedDiskOfGivenRadius<Z2i::Point, DetComputer> Functor; 
    //! [Hull2D-RadiusPredicateM5]
    Functor functor(false, 25, 1); //1/alpha = -sqrt(25/1) = -5 
    //! [Hull2D-RadiusPredicateM5]
    typedef PredicateFromOrientationFunctor2<Functor> Predicate; 
    Predicate predicate( functor ); 

    closedGrahamScanFromAnyPoint( border.begin(), border.end(), back_inserter( res ), predicate );   
    
    //display
    Board2D board;
    drawPolygon( res.begin(), res.end(), board ); 
    board.saveSVG( "AlphaShapeM5.svg" );  
#ifdef WITH_CAIRO
    board.saveCairo("AlphaShapeM5.png", Board2D::CairoPNG);
#endif

  }

  //positive alpha shape
  { 
    trace.info() << " alpha == 8 " << endl; 
    vector<Z2i::Point> res; 

    //! [Hull2D-RadiusPredicateP8]
    typedef AvnaimEtAl2x2DetSignComputer<DGtal::int64_t> DetComputer; 
    typedef InGeneralizedDiskOfGivenRadius<Z2i::Point, DetComputer> Functor; 
    Functor functor(true, 64, 1); //1/alpha = sqrt(64/1) = 8 
    typedef PredicateFromOrientationFunctor2<Functor, false, true> Predicate; 
    Predicate predicate( functor ); 
    //! [Hull2D-RadiusPredicateP8]

    closedGrahamScanFromAnyPoint( border.begin(), border.end(), back_inserter( res ), predicate );   

    //display
    Board2D board;
    drawPolygon( res.begin(), res.end(), board ); 
    board.saveSVG( "AlphaShapeP8.svg" );  
#ifdef WITH_CAIRO
    board.saveCairo("AlphaShapeP8.png", Board2D::CairoPNG);
#endif

  }

  //positive alpha shape
  { 
    trace.info() << " alpha == 9 " << endl; 
    vector<Z2i::Point> res; 

    typedef AvnaimEtAl2x2DetSignComputer<DGtal::int64_t> DetComputer; 
    typedef InGeneralizedDiskOfGivenRadius<Z2i::Point, DetComputer> Functor; 
    //! [Hull2D-RadiusPredicateP9]
    Functor functor(true, 81, 1); //1/alpha = sqrt(81/1) = 9 
    //! [Hull2D-RadiusPredicateP9]
    typedef PredicateFromOrientationFunctor2<Functor> Predicate; 
    Predicate predicate( functor ); 

    closedGrahamScanFromAnyPoint( border.begin(), border.end(), back_inserter( res ), predicate );   
    
    //display
    Board2D board;
    drawPolygon( res.begin(), res.end(), board ); 
    board.saveSVG( "AlphaShapeP9.svg" );  
#ifdef WITH_CAIRO
    board.saveCairo("AlphaShapeP9.png", Board2D::CairoPNG);
#endif

  }

}
int main()
{

    //! [freemanChainFromImage-imageImport]
    typedef DGtal::ImageContainerBySTLVector< DGtal::Z2i::Domain, unsigned char> Image;
    std::string filename =  examplesPath + "samples/circleR10modif.pgm";
    Image image = DGtal::PGMReader<Image>::importPGM(filename);
    //! [freemanChainFromImage-imageImport]

    //![freemanChainFromImage-ksspace]
    Z2i::KSpace ks;
    ks.init( image.domain().lowerBound(), image.domain().upperBound(), true );
    //![freemanChainFromImage-ksspace]

    //! [freemanChainFromImage-setAppend]
    Z2i::DigitalSet set2d (image.domain());
    SetFromImage<Z2i::DigitalSet>::append<Image>(set2d, image, 1, 255);
    //! [freemanChainFromImage-setAppend]


    //! [freemanChainFromImage-displaySet]
    Board2D aBoard;
    aBoard << set2d;
    aBoard << image.domain();
    //! [freemanChainFromImage-displaySet]

    //! [freemanChainFromImage-adj]
    SurfelAdjacency<2> sAdj( true );
    //! [freemanChainFromImage-adj]

    //! [freemanChainFromImage-extraction]
    std::vector< std::vector< Z2i::Point >  >  vectContoursBdryPointels;
    Surfaces<Z2i::KSpace>::extractAllPointContours4C( vectContoursBdryPointels,
            ks, set2d, sAdj );
    //! [freemanChainFromImage-extraction]


    GradientColorMap<int> cmap_grad( 0, (const int)vectContoursBdryPointels.size() );
    cmap_grad.addColor( Color( 50, 50, 255 ) );
    cmap_grad.addColor( Color( 255, 0, 0 ) );
    cmap_grad.addColor( Color( 255, 255, 10 ) );
    cmap_grad.addColor( Color( 25, 255, 255 ) );
    cmap_grad.addColor( Color( 255, 25, 255 ) );
    cmap_grad.addColor( Color( 25, 25, 25 ) );

    //! [freemanChainFromImage-fcConstruction]
    for(unsigned int i=0; i<vectContoursBdryPointels.size(); i++) {
        //  Constructing and displaying FreemanChains from contours.
        FreemanChain<Z2i::Integer> fc (vectContoursBdryPointels.at(i));
        //! [freemanChainFromImage-fcConstruction]
        //! [freemanChainFromImage-fcdysplay]
        aBoard << SetMode( fc.className(), "InterGrid" );
        aBoard<< CustomStyle( fc.className(),
                              new CustomColors(  cmap_grad(i),  Color::None ) );
        aBoard << fc;
        //! [freemanChainFromImage-fcdysplay]
    }

    aBoard.saveEPS("freemanChainFromImage.eps");
    return 0;
}
int main(int argc, char** argv)
{

  Point ptL(-1, -1);
  Point ptU(3, 3);
  Domain d (ptL, ptU);    
    
  Point pt0(0,0);
  Point pt1(1,0);
  Point pt2(0,1);
  Point pt3(2,1);
  Point pt4(1,2);
  Point pt5(2,2);
  
  
  DigitalSet aSet( d );
  aSet.insert(pt0);
  aSet.insert(pt1);
  aSet.insert(pt2);
  aSet.insert(pt3);
  aSet.insert(pt4);
  
  Board2D boardAdj;

  boardAdj << d;    

  
  std::set<SCell> bdry;
  Z2i::KSpace ks;  
  ks.init( ptL, ptU, true );

  boardAdj << aSet;
  
  // Extracting the surface boundary of the shape
  Surfaces<Z2i::KSpace>::sMakeBoundary( bdry, ks, aSet, ks.lowerBound(), ks.upperBound() );

  SurfelAdjacency<Z2i::KSpace::dimension>  sAdjInt( true );
  SurfelAdjacency<Z2i::KSpace::dimension>  sAdjExt( false );
  

  //Displaying the boundary bels
  std::set<SCell>::iterator itBoundary;
  int i=0;

  Board2D boardDisplayAll;
  boardDisplayAll<< d;
  boardAdj.saveFIG("illustrationAdjSRC.fig");
  for(itBoundary= bdry.begin(); itBoundary!= bdry.end(); itBoundary++){
    boardDisplayAll << *itBoundary;
  }
  boardDisplayAll.saveFIG("illustrationAdjBdr.fig");

  itBoundary = bdry.begin();
  for (int i =0; i<10; i++) itBoundary++;
  SCell surfel = *itBoundary;
  
  // Defining surfel Neighborhood given an surfel Adjacency
  SurfelNeighborhood<KSpace> sNeighInt;
  sNeighInt.init( &ks, &sAdjInt, surfel );
  
  SurfelNeighborhood<KSpace> sNeighExt;
  sNeighExt.init( &ks, &sAdjExt, surfel );
  
  
  SCell surfelFollowerInt;
  SCell surfelFollowerExt;

  sNeighInt.getAdjacentOnDigitalSet ( surfelFollowerInt, aSet, *(ks.sDirs(surfel)), true);
  sNeighExt.getAdjacentOnDigitalSet ( surfelFollowerExt, aSet, *(ks.sDirs(surfel)), true);
  
  
  
  boardAdj <<  CustomStyle( surfel.className() ,
			    new CustomColors( Color::Blue,
					      Color::Gray ));
  boardAdj << surfel;
  
  boardAdj <<  CustomStyle( surfel.className() ,
			    new CustomColors( Color::Red,
					      Color::Black ));

  boardAdj << surfelFollowerInt;
  boardAdj <<  CustomStyle( surfel.className() ,
			    new CustomColors( Color::Green,
					      Color::White ));
  boardAdj << surfelFollowerExt;

  
  boardAdj.saveFIG("illustrationAdjIntExt.fig");
  

  
  // Extraction of contour
  std::set<SCell> aContour1;
  Surfaces<Z2i::KSpace>::trackBoundary( aContour1, ks, 
					sAdjExt,
					aSet, surfel ); 
		    
  Board2D boardContour1;
  boardContour1 << d;
  std::set<SCell>::iterator iterOnContour;
  for( iterOnContour= aContour1.begin(); iterOnContour != aContour1.end(); iterOnContour++){
    if(*iterOnContour != surfel){
      boardContour1 <<  CustomStyle( surfel.className() ,
				     new CustomColors( Color::Green,
						       Color::White ));
      boardContour1<< *iterOnContour;
    }else{
				 
      boardContour1 <<  CustomStyle( surfel.className() ,
				     new CustomColors( Color::Blue,
						       Color::Gray ));
      boardContour1<< *iterOnContour;
    }
  }
  boardContour1.saveFIG("illustrationAdjContour.fig");



  // Extraction of contour
  std::set<SCell> aContour2;
  Surfaces<Z2i::KSpace>::trackBoundary( aContour2, ks, 
					sAdjInt,
					aSet, surfel ); 
  Board2D boardContour2;
  boardContour2 << d;
  std::set<SCell>::iterator iterOnContour2;
  for( iterOnContour2= aContour2.begin(); iterOnContour2 != aContour2.end(); iterOnContour2++){
    if(*iterOnContour2 != surfel){
      boardContour2 <<  CustomStyle( surfel.className() ,
				     new CustomColors( Color::Red,
						       Color::White ));
      boardContour2<< *iterOnContour2;
    }else{
      
      boardContour2 <<  CustomStyle( surfel.className() ,
				     new CustomColors( Color::Blue,
						       Color::Gray ));
      boardContour2<< *iterOnContour2;
    }
  }
  boardContour2.saveFIG("illustrationAdjContour2.fig");









  return 0;
}
int main ()
{
  
  Delaunay t;
  
  trace.beginBlock("Construction the shape");
  typedef Ellipse2D<Z2i::Space> Ellipse; 
  int a = 5, b = 3;
  Ellipse2D<Z2i::Space> ellipse(Z2i::Point(0,0), a, b, 0.3 );
  // Ellipse2D<Z2i::Space> ellipse(Z2i::Point(0,0), 5.5, 5.5, 0 );
  double h = 0.25; 
  GaussDigitizer<Z2i::Space,Ellipse> dig;  
  dig.attach( ellipse );
  dig.init( ellipse.getLowerBound()+Z2i::Vector(-1,-1),
            ellipse.getUpperBound()+Z2i::Vector(1,1), h ); 
  // typedef Flower2D<Z2i::Space> Flower; 
  // Flower2D<Z2i::Space> flower(Z2i::Point(0,0), 15, 2, 5, 0);
  // double h = 0.25; 
  // GaussDigitizer<Z2i::Space,Flower> dig;  
  // dig.attach( flower );
  // dig.init( flower.getLowerBound()+Z2i::Vector(-1,-1),
  //           flower.getUpperBound()+Z2i::Vector(1,1), h ); 
  Z2i::KSpace ks;
  ks.init( dig.getLowerBound(), dig.getUpperBound(), true );
  SurfelAdjacency<2> sAdj( true );
  Z2i::SCell bel = Surfaces<Z2i::KSpace>::findABel( ks, dig, 1000 );
  std::vector<Z2i::Point> boundaryPoints;
  Surfaces<Z2i::KSpace>
    ::track2DBoundaryPoints( boundaryPoints, ks, sAdj, dig, bel );
  Z2i::Curve c;
  c.initFromVector( boundaryPoints );  
  typedef Z2i::Curve::PointsRange Range; 
  Range r = c.getPointsRange(); 
  trace.endBlock();


  trace.beginBlock("Delaunay");
  for(Range::ConstIterator it=r.begin(), itend=r.end(); it != itend;
      ++it)
    { 
      t.insert( Point( (*it)[0], (*it)[1]));
      t.insert( Point( (*it)[0] + 3 + (int) ceil( ((double)b)/h ),
		       (*it)[1] - 3 - (int) ceil( ((double)a)/h ) ));
    }
  trace.endBlock();

  std::cout << "number of vertices :  " ;
  std::cout << t.number_of_vertices() << std::endl;
  std::cout << "number of faces :  " ;
  std::cout << t.number_of_faces() << std::endl;
  
  trace.beginBlock("Area minimizing triangulation");
  Edge_iterator itnext;
  bool flip = true;
  bool inverse = false;
  unsigned int pass = 0;
  while ( flip ) {
    std::cout << "----------- pass " << pass << " -------------------" << std::endl;
    inverse = false;
    flip = false;
    int nb_flip = 0;
    int nb_random_flip = 0;
    for( Edge_iterator it = t.edges_begin(), itend=t.edges_end();
	 it != itend; it = itnext )
      {
	// vertex(cw(i)) and vertex(ccw(i)) of f.
	itnext = it; ++itnext;
	Edge e1 = *it;
	if ( isEdgeElementary( t,
			       e1.first->vertex( t.ccw( e1.second ) ),
			       e1.first->vertex( t.cw( e1.second ) ) ) )
	  continue;
	Edge e2 = t.mirror_edge( e1 );
	if ( ! isQuadrilateral( t, 
				e1.first->vertex( e1.second ),
				e1.first->vertex( t.ccw( e1.second ) ),
				e2.first->vertex( e2.second ),
				e1.first->vertex( t.cw( e1.second ) ) ) )
	  continue;
	int nb_f1 = twiceNbLatticePointsInTriangle( t, e1.first );
	int nb_f2 = twiceNbLatticePointsInTriangle( t, e2.first );
	int nb_flip_f1 = twiceNbLatticePointsInTriangle( t,
							 e1.first->vertex( e1.second ), 
							 e1.first->vertex( t.ccw( e1.second ) ),
							 e2.first->vertex( e2.second ) );
	int nb_flip_f2 = twiceNbLatticePointsInTriangle( t,
							 e1.first->vertex( e1.second ), 
							 e1.first->vertex( t.cw( e1.second ) ),
							 e2.first->vertex( e2.second ) );
	int nb_min = nb_f1 <= nb_f2 ? nb_f1 : nb_f2;
	int nb_flip_min = nb_flip_f1 <= nb_flip_f2 ? nb_flip_f1 : nb_flip_f2;
	if ( nb_flip_min < nb_min )
	  {
	    std::cout << "flipped " << e1.first->vertex( e1.second )->point()
		      << "->" << e1.first->vertex( e1.second )->point()
		      << std::endl;
	    t.flip( e1.first, e1.second );
	    nb_flip++;
	    flip = true;
	  }
	if ( nb_flip_min == nb_min )
	  {
	    inverse = true;
	    if ( random() % 2 == 1 )
	      {
		std::cout << "Random flipped " << e1.first->vertex( e1.second )->point()
			  << "->" << e1.first->vertex( e1.second )->point()
			  << std::endl;
		t.flip( e1.first, e1.second );
		nb_random_flip++;
	      }
	  }
	// if ( ( empty_f1 == false )
	//      && ( empty_f2 == false ) )
	//   { // try if flip is better.
	//     bool empty_flip_f1 
	//       = twiceNbLatticePointsInTriangle( t,
	// 					e1.first->vertex( e1.second ), 
	// 					e1.first->vertex( t.ccw( e1.second ) ),
	// 					e2.first->vertex( e2.second ) ) == 0;
	//     bool empty_flip_f2 
	//       = twiceNbLatticePointsInTriangle( t,
	// 					e2.first->vertex( e2.second ), 
	// 					e2.first->vertex( t.ccw( e2.second ) ),
	// 					e1.first->vertex( e1.second ) ) == 0;
	//     if ( empty_flip_f1 || empty_flip_f2 )
	//       {
	// 	if ( isEdgeElementary( t,
	// 			       e1.first->vertex( t.ccw( e1.second ) ),
	// 			       e1.first->vertex( t.cw( e1.second ) ) ) )
	// 	  {
	// 	    std::cout << "Flip forbidden:  " << e1.first->vertex( e1.second )->point()
	// 		  << "->" << e1.first->vertex( e1.second )->point()
	// 		  << std::endl;
	// 	  }
	// 	else
	// 	  {
	// 	    std::cout << "flipped " << e1.first->vertex( e1.second )->point()
	// 		      << "->" << e1.first->vertex( e1.second )->point()
	// 		      << std::endl;
	// 	    t.flip( e1.first, e1.second );
	// 	    flip = true;
	// 	  }
	//       }
	//   }
      }
    std::cout << "----------- nb_flip " << nb_flip 
	      << ", nb_random " << nb_random_flip << " -------------" << std::endl;
    ++pass;
    if ( inverse && ( (nb_random_flip+4) > log(pass) ) ) 
      flip = true;
  }  
  trace.endBlock();


  // GridCurve
  Z2i::Curve gc;
  gc.initFromPointsRange( r.begin(), r.end() );
  typedef Z2i::Curve::PointsRange::ConstIterator ConstIterator;
  typedef ArithmeticalDSS<ConstIterator,int,4> DSS4;
  typedef SaturatedSegmentation<DSS4> Segmentation;
  //Segmentation
  Z2i::Curve::PointsRange range = gc.getPointsRange();
  DSS4 dss4RecognitionAlgorithm;
  Segmentation theSegmentation( range.begin(), range.end(), dss4RecognitionAlgorithm );
         

  DGtal::Board2D board;

  Z2i::Point dP;
  board << CustomStyle( dP.className(), 
                        new CustomPen( Color(0,0,0), Color(230,230,230), 1, 
                                       Board2D::Shape::SolidStyle,
                                       Board2D::Shape::RoundCap,
                                       Board2D::Shape::RoundJoin ));
  for(Range::ConstIterator it=r.begin(), itend=r.end(); it != itend;
      ++it)
    board << *it;
  
  for(Faces_iterator it = t.finite_faces_begin(), itend=t.finite_faces_end();
      it != itend; ++it)
    {
      Z2i::Point a( toDGtal(it->vertex(0)->point())),
	b(toDGtal(it->vertex(1)->point())),
	c(toDGtal(it->vertex(2)->point()));

      // Z2i::Vector ab( b - a ), ac( c - a );
      // int d = ab[ 0 ] * ac[ 1 ] - ab[ 1 ] * ac[ 0 ];
      if ( emptyLatticeTriangle( t, it ) ) //( ( d == 1 ) || (d == -1 ) )
        {
          board.setPenColor(DGtal::Color::Blue);
          board.setFillColor( DGtal::Color::None );
          board.setLineWidth( 3.0 );
          board.drawTriangle(a[0],a[1],b[0],b[1],c[0],c[1]);
        }
      else
        {
          board.setPenColor(DGtal::Color::Red);
          board.setFillColor( DGtal::Color::None );
          //          board.setFillColorRGBi(200,200,200,128);
          board.setLineWidth( 2.0 );
          board.drawTriangle(a[0],a[1],b[0],b[1],c[0],c[1]);
        }
    }

  Segmentation::SegmentComputerIterator i = theSegmentation.begin();
  Segmentation::SegmentComputerIterator end = theSegmentation.end();
  board.setPenColor(DGtal::Color::Green);
  board.setFillColor( DGtal::Color::None );
  board << SetMode( "ArithmeticalDSS", "BoundingBox" );
  std::string aStyleName = "ArithmeticalDSS/BoundingBox";
  for ( ; i != end; ++i) {
    DSS4 current(*i);
    board << CustomStyle( aStyleName, 
                          new CustomPenColor( DGtal::Color::Green ) )
          << current;
  } 

  // Display Voronoi.
  // for(Edge_iterator it = t.edges_begin(), itend=t.edges_end();
  //     it != itend; ++it)
  //   {
  //     // vertex(cw(i)) and vertex(ccw(i)) of f.
  //     Face_handle itf = it->first;
  //     int i = it->second;
  //     Z2i::Point a( toDGtal(itf->vertex( t.cw( i ) )->point()));
  //     Z2i::Point b( toDGtal(itf->vertex( t.ccw( i ) )->point()));

  //     CGAL::Object o = t.dual( it );
  //     if (CGAL::object_cast<K::Segment_2>(&o)) 
  //       {
  //         const K::Segment_2* ptrSegment = CGAL::object_cast<K::Segment_2>(&o);
  //         board.setPenColor(DGtal::Color::Black);
  //         board.setFillColor( DGtal::Color::None );
  //         board.setLineWidth( 2.0 );
  //         board.drawLine( ptrSegment->source().x(),
  //                         ptrSegment->source().y(),
  //                         ptrSegment->target().x(),
  //                         ptrSegment->target().y() );
  //       }
  //     else if (CGAL::object_cast<K::Ray_2>(&o)) 
  //       {
  //         const K::Ray_2* ptrRay = CGAL::object_cast<K::Ray_2>(&o);
  //         board.setPenColor(DGtal::Color::Black);
  //         board.setFillColor( DGtal::Color::None );
  //         board.setLineWidth( 2.0 );
  //         double dx = ptrRay->to_vector().x();
  //         double dy = ptrRay->to_vector().y();
  //         double norm = sqrt( dx*dx+dy*dy );
  //         dx = 5.0 * dx / norm;
  //         dy = 5.0 * dy / norm;
  //         board.drawArrow( ptrRay->source().x(),
  //                          ptrRay->source().y(),
  //                          ptrRay->source().x() + dx, //1*ptrRay->to_vector().x(),
  //                          ptrRay->source().y() + dy ); //1*ptrRay->to_vector().y() );
  //       }
  //   }

  
  board.saveSVG("delaunay.svg");
  board.saveEPS("delaunay.eps");
  
  return 0;
}
int main()
{
  //shape
  typedef Flower2D<Z2i::Space> Flower; 
  Flower2D<Z2i::Space> flower(Z2i::Point(0,0), 20, 5, 5, 0);
  
  //! [shapeGridCurveEstimator-dig]
  //implicit digitization of a shape of type Flower 
  //into a digital space of type Space
  double h = 1; 
  GaussDigitizer<Z2i::Space,Flower> dig;  
  dig.attach( flower );
  dig.init( flower.getLowerBound()+Z2i::Vector(-1,-1),
            flower.getUpperBound()+Z2i::Vector(1,1), h ); 
  //! [shapeGridCurveEstimator-dig]
  
  //! [shapeGridCurveEstimator-prepareTracking]
  //Khalimsky space
  Z2i::KSpace ks;
  ks.init( dig.getLowerBound(), dig.getUpperBound(), true );
  //adjacency (4-connectivity)
  SurfelAdjacency<2> sAdj( true );
  //! [shapeGridCurveEstimator-prepareTracking]

  //! [shapeGridCurveEstimator-tracking]
  //searching for one boundary element
  Z2i::SCell bel = Surfaces<Z2i::KSpace>::findABel( ks, dig, 1000 );
  //tracking
  vector<Z2i::Point> boundaryPoints;
  Surfaces<Z2i::KSpace>
    ::track2DBoundaryPoints( boundaryPoints, ks, sAdj, dig, bel );
  //! [shapeGridCurveEstimator-tracking]

  //! [shapeGridCurveEstimator-instantiation]
  Z2i::Curve c;
  c.initFromVector( boundaryPoints );  
  //! [shapeGridCurveEstimator-instantiation]
  
  DGtal::Board2D aBoard;
  aBoard << c; 
  aBoard.saveEPS("DisplayGridCurve1.eps");  
  
  //! [shapeGridCurveEstimator-getRange]
  //range of points
  typedef Z2i::Curve::PointsRange Range; 
  Range r = c.getPointsRange(); 
  //! [shapeGridCurveEstimator-getRange]
  
  //! [shapeGridCurveEstimator-lengthEstimation]
  //length estimation
  DSSLengthEstimator< Range::ConstIterator > DSSlength;
  DSSlength.init( h, r.begin(), r.end(), c.isClosed() );
  double length1 = DSSlength.eval();
  trace.info() << "Length (h=" << h << "): " << length1 << endl; 
  //! [shapeGridCurveEstimator-lengthEstimation]

//@TODO correct init method of trueLengthEstimator (remove &flower)
  //! [shapeGridCurveEstimator-trueLengthEstimation]
  typedef ParametricShapeArcLengthFunctor< Flower > Length;
  TrueGlobalEstimatorOnPoints< 
    Range::ConstIterator, 
    Flower, 
    Length  >  trueLengthEstimator;
  trueLengthEstimator.init( h, r.begin(), r.end(), &flower, c.isClosed());
  double trueLength = trueLengthEstimator.eval(); 
  trace.info() << "ground truth: " << trueLength << endl; 
  //! [shapeGridCurveEstimator-trueLengthEstimation]

  //! [shapeGridCurveEstimator-higher]
  //implicit digitization at higher resolution
  h = 0.1; 
  dig.init( flower.getLowerBound()+Z2i::Vector(-1,-1),
            flower.getUpperBound()+Z2i::Vector(1,1), h ); 
  //a greater domain is needed in the Khalimsky space
  ks.init( dig.getLowerBound(), dig.getUpperBound(), true );
  //searching for one boundary element
  bel = Surfaces<Z2i::KSpace>::findABel( ks, dig, 10000 );
  //tracking
  Surfaces<Z2i::KSpace>
    ::track2DBoundaryPoints( boundaryPoints, ks, sAdj, dig, bel );
  //reset grid curve and its points range
  c.initFromVector( boundaryPoints );
  Range r2 = c.getPointsRange(); 
  //estimate length
  DSSlength.init( h, r2.begin(), r2.end(), c.isClosed() );
  double length2 = DSSlength.eval();
  trace.info() << "Length (h=" << h << "): " << length2 << endl;  
  //! [shapeGridCurveEstimator-higher]
  
  aBoard.clear(); 
  aBoard << c; 
  aBoard.saveEPS("DisplayGridCurve01.eps");  
  
  return 0;

}
int main( int argc, char** argv )
{
  // parse command line ----------------------------------------------
  po::options_description general_opt("Allowed options are: ");
  general_opt.add_options()
    ("help,h", "display this message")
    ("image,i", po::value<std::string>(), "image file name")
    ("min,m", po::value<int>(), "min image threshold value (default 128)")
    ("max,M", po::value<int>(), "max image threshold value (default 255)")
    
    ("minSize,s", po::value<int>(), "minSize of the extracted freeman chain (default 0)")
    ("contourSelect,s", po::value<vector <int> >()->multitoken(), 
     "Select contour according reference point and maximal distance:  ex. --contourSelect X Y distanceMax")
    ("thresholdRange,R", po::value<vector <int> >()->multitoken(), 
     "use a range interval as threshold : --thresholdRange min increment max : for each possible i, it define a digital sets [min+(i*increment),min+((i+1)*increment)] and extract their boundary. ");
  
  
  
  po::variables_map vm;
  po::store(po::parse_command_line(argc, argv, general_opt), vm);  
  po::notify(vm);    
  if(vm.count("help")||argc<=1)
    {
      trace.info()<< "Extract FreemanChains from thresholded image" <<std::endl << "Basic usage: "<<std::endl
      << "\t image2freeman [options] --image <imageName> -min 128 -max 255 > contours.fc"<<std::endl
      << general_opt << "\n";
      return 0;
    }
  
  
  double minThreshold = 128;
  double maxThreshold = 255;
  unsigned int minSize =0;
  bool select=false;
  bool thresholdRange=vm.count("thresholdRange");
  Z2i::Point selectCenter;
  unsigned int selectDistanceMax = 0; 
  
  
  //Parse options
  if (!(vm.count("image"))){
    trace.info() << "Image file name needed"<< endl;
    return 0;
  } 
  
  if(vm.count("min")){
    minThreshold= vm["min"].as<int>();
  } 
  if(vm.count("max")){
    maxThreshold= vm["max"].as<int>();
  } 
  if(vm.count("minSize")){
    minSize = vm["minSize"].as<int>();
  } 
  if(vm.count("contourSelect")){
    select=true;
    vector<int> cntConstraints= vm["contourSelect"].as<vector <int> >();
    if(cntConstraints.size()!=3){
      trace.info() << "Incomplete option \"--contourSelect\""<< endl;
      return 0;
    }
    selectCenter[0]= cntConstraints.at(0);
    selectCenter[1]= cntConstraints.at(1);
    selectDistanceMax= (unsigned int) cntConstraints.at(2);
  }
  
  int min, max, increment;
  if(! thresholdRange){
    min=(int)minThreshold;
    max= (int)maxThreshold;
    increment =  (int)(maxThreshold- minThreshold);
  }else{
    vector<int> vectRange= vm["thresholdRange"].as<vector <int> >();
    if(vectRange.size()!=3){
      trace.info() << "Incomplete option \"--thresholdRange\""<< endl;
      return 0;
    }
    min=vectRange.at(0);
    increment=vectRange.at(1);
    max = vectRange.at(2);
  }



  typedef ImageSelector < Z2i::Domain, unsigned char>::Type Image;
  string imageFileName = vm["image"].as<std::string>();
  Image image = PNMReader<Image>::importPGM( imageFileName ); 
  Z2i::DigitalSet set2d (image.domain());
  SetPredicate<Z2i::DigitalSet> set2dPredicate( set2d );

  for(int i=0; minThreshold+i*increment< maxThreshold; i++){
    min = (int)(minThreshold+i*increment);
    max = (int)(minThreshold+(i+1)*increment);
    
    
    SetFromImage<Z2i::DigitalSet>::append<Image>(set2d, image, min, max);
    trace.info() << "DGtal set imported from thresholds ["<<  min << "," << max << "]" << endl;
    Z2i::KSpace ks;
    if(! ks.init( image.domain().lowerBound(), 
      image.domain().upperBound(), true )){
      trace.error() << "Problem in KSpace initialisation"<< endl;
    }
    SurfelAdjacency<2> sAdj( true );
  
    std::vector< std::vector< Z2i::Point >  >  vectContoursBdryPointels;
    Surfaces<Z2i::KSpace>::extractAllPointContours4C( vectContoursBdryPointels,
                  ks, set2dPredicate, sAdj );  
    for(unsigned int k=0; k<vectContoursBdryPointels.size(); k++){
      if(vectContoursBdryPointels.at(k).size()>minSize){
  if(select){
    Z2i::Point ptMean = ContourHelper::getMeanPoint(vectContoursBdryPointels.at(k));
    unsigned int distance = (unsigned int)ceil(sqrt((double)(ptMean[0]-selectCenter[0])*(ptMean[0]-selectCenter[0])+
            (ptMean[1]-selectCenter[1])*(ptMean[1]-selectCenter[1])));
    if(distance<=selectDistanceMax){
      FreemanChain<Z2i::Integer> fc (vectContoursBdryPointels.at(k));    
      cout << fc.x0 << " " << fc.y0   << " " << fc.chain << endl; 
    }
  }else{
    FreemanChain<Z2i::Integer> fc (vectContoursBdryPointels.at(k));    
    cout << fc.x0 << " " << fc.y0   << " " << fc.chain << endl; 
  }
      }

    }
  
    

  }

    
  
}