DGtalTools  0.9.4
contourGenerator.cpp
1 
31 #include <cmath>
33 #include <iostream>
34 #include <iomanip>
35 #include <vector>
36 #include <string>
37 
38 #include <boost/program_options/options_description.hpp>
39 #include <boost/program_options/parsers.hpp>
40 #include <boost/program_options/variables_map.hpp>
41 
42 #include "DGtal/base/Common.h"
43 #include "DGtal/kernel/domains/HyperRectDomain.h"
44 
45 #include "DGtal/shapes/ShapeFactory.h"
46 #include "DGtal/shapes/Shapes.h"
47 #include "DGtal/helpers/StdDefs.h"
48 
49 
50 #include "DGtal/io/colormaps/GrayscaleColorMap.h"
51 #include "DGtal/images/imagesSetsUtils/ImageFromSet.h"
52 #include "DGtal/images/imagesSetsUtils/SetFromImage.h"
53 #include "DGtal/images/ImageContainerBySTLVector.h"
54 #include "DGtal/io/boards/Board2D.h"
55 
56 #include "DGtal/shapes/GaussDigitizer.h"
57 #include "DGtal/geometry/curves/GridCurve.h"
58 #include "DGtal/geometry/curves/estimation/TrueLocalEstimatorOnPoints.h"
59 #include "DGtal/geometry/curves/estimation/TrueGlobalEstimatorOnPoints.h"
60 #include "DGtal/geometry/curves/estimation/ParametricShapeCurvatureFunctor.h"
61 #include "DGtal/geometry/curves/estimation/ParametricShapeTangentFunctor.h"
62 #include "DGtal/geometry/curves/estimation/ParametricShapeArcLengthFunctor.h"
63 
64 #include "DGtal/topology/helpers/Surfaces.h"
65 
66 
67 using namespace DGtal;
68 
149 std::vector<std::string> shapes2D;
150 std::vector<std::string> shapesDesc;
151 std::vector<std::string> shapesParam1;
152 std::vector<std::string> shapesParam2;
153 std::vector<std::string> shapesParam3;
154 std::vector<std::string> shapesParam4;
155 
156 
161 void createList()
162 {
163  shapes2D.push_back("ball");
164  shapesDesc.push_back("Ball for the Euclidean metric.");
165  shapesParam1.push_back("--radius [-R]");
166  shapesParam2.push_back("");
167  shapesParam3.push_back("");
168  shapesParam4.push_back("");
169 
170  shapes2D.push_back("square");
171  shapesDesc.push_back("square (no signature).");
172  shapesParam1.push_back("--width [-w]");
173  shapesParam2.push_back("");
174  shapesParam3.push_back("");
175  shapesParam4.push_back("");
176 
177  shapes2D.push_back("lpball");
178  shapesDesc.push_back("Ball for the l_power metric (no signature).");
179  shapesParam1.push_back("--radius [-R],");
180  shapesParam2.push_back("--power [-p]");
181  shapesParam3.push_back("");
182  shapesParam4.push_back("");
183 
184  shapes2D.push_back("flower");
185  shapesDesc.push_back("Flower with k petals with radius ranging from R+/-v.");
186  shapesParam1.push_back("--radius [-R],");
187  shapesParam2.push_back("--varsmallradius [-v],");
188  shapesParam3.push_back("--k [-k],");
189  shapesParam4.push_back("--phi");
190 
191  shapes2D.push_back("ngon");
192  shapesDesc.push_back("Regular k-gon.");
193  shapesParam1.push_back("--radius [-R],");
194  shapesParam2.push_back("--k [-k],");
195  shapesParam3.push_back("--phi");
196  shapesParam4.push_back("");
197 
198  shapes2D.push_back("accflower");
199  shapesDesc.push_back("Accelerated Flower with k petals.");
200  shapesParam1.push_back("--radius [-R],");
201  shapesParam2.push_back("--varsmallradius [-v],");
202  shapesParam3.push_back("--k [-k],");
203  shapesParam4.push_back("--phi");
204 
205  shapes2D.push_back("ellipse");
206  shapesDesc.push_back("Ellipse.");
207  shapesParam1.push_back("--axis1 [-A],");
208  shapesParam2.push_back("--axis2 [-a],");
209  shapesParam3.push_back("--phi");
210  shapesParam4.push_back("");
211 
212 
213 }
214 
219 void displayList()
220 {
221  trace.emphase()<<"2D Shapes:"<<std::endl;
222  for(unsigned int i=0; i<shapes2D.size(); ++i)
223  trace.info()<<"\t"<<shapes2D[i]<<"\t"
224  <<shapesDesc[i]<<std::endl
225  <<"\t\tRequired parameter(s): "
226  << shapesParam1[i]<<" "
227  << shapesParam2[i]<<" "
228  << shapesParam3[i]<<" "
229  << shapesParam4[i]<<std::endl;
230 
231 }
232 
233 
242 unsigned int checkAndReturnIndex(const std::string &shapeName)
243 {
244  unsigned int pos=0;
245 
246  while ((pos < shapes2D.size()) && (shapes2D[pos] != shapeName))
247  pos++;
248 
249  if (pos == shapes2D.size())
250  {
251  trace.error() << "The specified shape has not found.";
252  trace.info()<<std::endl;
253  exit(1);
254  }
255 
256  return pos;
257 }
258 
259 
271 template <typename Shape, typename Range, typename Point, typename Quantity>
272 void
273 estimateGeometry(Shape& s,
274  const double& h,
275  const Range& r,
276  std::vector<Point>& points,
277  std::vector<Point>& tangents,
278  std::vector<Quantity>& curvatures) {
279 
280  typedef typename Range::ConstIterator ConstIterator;
281  for (ConstIterator i = r.begin(); i != r.end(); ++i) {
282  Point p( *i );
283  p *= h;
284  points.push_back(p);
285  }
286 
287  typedef typename Range::ConstCirculator ConstCirculator;
288 
289  typedef ParametricShapeTangentFunctor< Shape > TangentFunctor;
291  trueTangentEstimator;
292  trueTangentEstimator.attach(&s);
293  trueTangentEstimator.init( h, r.c(), r.c());
294  trueTangentEstimator.eval(r.c(), r.c(), std::back_inserter(tangents) );
295 
296  typedef ParametricShapeCurvatureFunctor< Shape > CurvatureFunctor;
298  trueCurvatureEstimator;
299  trueCurvatureEstimator.attach(&s);
300  trueCurvatureEstimator.init( h, r.c(), r.c());
301  trueCurvatureEstimator.eval(r.c(), r.c(), std::back_inserter(curvatures) );
302 
303 }
304 
305 template <typename Space, typename Shape>
306 bool
307 generateContour(
308  Shape & aShape,
309  double h,
310  const std::string & outputFormat,
311  bool withGeom,
312  const std::string & outputFileName )
313 {
314  // Types
315  typedef typename Space::Point Point;
316  typedef typename Space::Vector Vector;
317  typedef typename Space::RealPoint RealPoint;
318  typedef typename Space::Integer Integer;
321  typedef typename KSpace::SCell SCell;
322  typedef typename GridCurve<KSpace>::PointsRange Range;
323  typedef typename Range::ConstIterator ConstIteratorOnPoints;
324  typedef typename GridCurve<KSpace>::MidPointsRange MidPointsRange;
325 
326  // Digitizer
328  dig.attach( aShape ); // attaches the shape.
329  Vector vlow(-1,-1); Vector vup(1,1);
330  dig.init( aShape.getLowerBound()+vlow, aShape.getUpperBound()+vup, h );
331  Domain domain = dig.getDomain();
332  // Create cellular space
333  KSpace K;
334  bool ok = K.init( dig.getLowerBound(), dig.getUpperBound(), true );
335  if ( ! ok )
336  {
337  std::cerr << "[generateContour]"
338  << " error in creating KSpace." << std::endl;
339  return false;
340  }
341  try {
342  // Extracts shape boundary
344  SCell bel = Surfaces<KSpace>::findABel( K, dig, 10000 );
345  // Getting the consecutive surfels of the 2D boundary
346  std::vector<Point> points;
347  Surfaces<KSpace>::track2DBoundaryPoints( points, K, SAdj, dig, bel );
348  // Create GridCurve
349  GridCurve<KSpace> gridcurve;
350  gridcurve.initFromVector( points );
351  // gridcurve contains the digital boundary to analyze.
352  Range r = gridcurve.getPointsRange(); //building range
353 
354  if ( outputFormat == "pts" )
355  {
356 
357  for ( ConstIteratorOnPoints it = r.begin(), it_end = r.end();
358  it != it_end; ++it )
359  {
360  Point p = *it;
361  std::cout << p[ 0 ] << " " << p[ 1 ] << std::endl;
362  }
363  }
364  else if ( outputFormat == "fc" )
365  {
366  ConstIteratorOnPoints it = r.begin();
367  Point p = *it++;
368  std::cout << p[ 0 ] << " " << p[ 1 ] << " ";
369  for ( ConstIteratorOnPoints it_end = r.end(); it != it_end; ++it )
370  {
371  Point p2 = *it;
372  Vector v = p2 - p;
373  if ( v[0 ]== 1 ) std::cout << '0';
374  if ( v[ 1 ] == 1 ) std::cout << '1';
375  if ( v[ 0 ] == -1 ) std::cout << '2';
376  if ( v[ 1 ] == -1 ) std::cout << '3';
377  p = p2;
378  }
379  // close freemanchain if necessary.
380  Point p2= *(r.begin());
381  Vector v = p2 - p;
382  if ( v.norm1() == 1 )
383  {
384  if ( v[ 0 ] == 1 ) std::cout << '0';
385  if ( v[ 1 ] == 1 ) std::cout << '1';
386  if ( v[ 0 ] == -1 ) std::cout << '2';
387  if ( v[ 1 ] == -1 ) std::cout << '3';
388  }
389  std::cout << std::endl;
390  }
391 
392  if (withGeom)
393  {
394  // write geometry of the shape
395  std::stringstream s;
396  s << outputFileName << ".geom";
397  std::ofstream outstream(s.str().c_str()); //output stream
398  if (!outstream.is_open()) return false;
399  else {
400  outstream << "# " << outputFileName << std::endl;
401  outstream << "# Pointel (x,y), Midpoint of the following linel (x',y')" << std::endl;
402  outstream << "# id x y tangentx tangenty curvaturexy"
403  << " x' y' tangentx' tangenty' curvaturex'y'" << std::endl;
404 
405  std::vector<RealPoint> truePoints, truePoints2;
406  std::vector<RealPoint> trueTangents, trueTangents2;
407  std::vector<double> trueCurvatures, trueCurvatures2;
408 
409  estimateGeometry<Shape, Range, RealPoint, double>
410  (aShape, h, r, truePoints, trueTangents, trueCurvatures);
411 
412  estimateGeometry<Shape, MidPointsRange, RealPoint, double>
413  (aShape, h, gridcurve.getMidPointsRange(), truePoints2, trueTangents2, trueCurvatures2);
414 
415 
416  unsigned int n = (unsigned int)r.size();
417  for (unsigned int i = 0; i < n; ++i ) {
418  outstream << std::setprecision( 15 ) << i
419  << " " << truePoints[ i ][ 0 ]
420  << " " << truePoints[ i ][ 1 ]
421  << " " << trueTangents[ i ][ 0 ]
422  << " " << trueTangents[ i ][ 1 ]
423  << " " << trueCurvatures[ i ]
424  << " " << truePoints2[ i ][ 0 ]
425  << " " << truePoints2[ i ][ 1 ]
426  << " " << trueTangents2[ i ][ 0 ]
427  << " " << trueTangents2[ i ][ 1 ]
428  << " " << trueCurvatures2[ i ]
429  << std::endl;
430  }
431 
432  }
433  outstream.close();
434  }
435 
437 
438  }
439  catch ( InputException e )
440  {
441  std::cerr << "[generateContour]"
442  << " error in finding a bel." << std::endl;
443  return false;
444  }
445  return true;
446 }
447 
453 void missingParam(std::string param)
454 {
455  trace.error() <<" Parameter: "<<param<<" is required..";
456  trace.info()<<std::endl;
457  exit(1);
458 }
459 
461 namespace po = boost::program_options;
462 
463 int main( int argc, char** argv )
464 {
465  // parse command line ----------------------------------------------
466  po::options_description general_opt("Allowed options are");
467  general_opt.add_options()
468  ("help,h", "display this message")
469  ("list,l", "List all available shapes")
470  ("shape,s", po::value<std::string>(), "Shape name")
471  ("radius,R", po::value<double>(), "Radius of the shape" )
472  ("axis1,A", po::value<double>(), "Half big axis of the shape (ellipse)" )
473  ("axis2,a", po::value<double>(), "Half small axis of the shape (ellipse)" )
474  ("smallradius,r", po::value<double>()->default_value(5), "Small radius of the shape" )
475  ("varsmallradius,v", po::value<double>()->default_value(5), "Variable small radius of the shape" )
476  ("k,k", po::value<unsigned int>()->default_value(3), "Number of branches or corners the shape" )
477  ("phi", po::value<double>()->default_value(0.0), "Phase of the shape (in radian)" )
478  ("width,w", po::value<double>()->default_value(10.0), "Width of the shape" )
479  ("power,p", po::value<double>()->default_value(2.0), "Power of the metric (double)" )
480  ("center_x,x", po::value<double>()->default_value(0.0), "x-coordinate of the shape center (double)" )
481  ("center_y,y", po::value<double>()->default_value(0.0), "y-coordinate of the shape center (double)" )
482  ("gridstep,g", po::value<double>()->default_value(1.0), "Gridstep for the digitization" )
483  ("format,f", po::value<std::string>()->default_value("pts"), "Output format:\n\t List of pointel coordinates {pts}\n\t Freman chaincode Vector {fc}" )
484  ("outputGeometry,o", po::value<std::string>(), "Base name of the file containing the shape geometry (points, tangents, curvature)" );
485 
486  bool parseOK=true;
487  po::variables_map vm;
488  try{
489  po::store(po::parse_command_line(argc, argv, general_opt), vm);
490  }catch(const std::exception& ex){
491  parseOK=false;
492  trace.info()<< "Error checking program options: "<< ex.what()<< std::endl;
493  }
494 
495  po::notify(vm);
496  if(!parseOK || vm.count("help")||argc<=1)
497  {
498  trace.info()<< "Generate contours of 2d digital shapes using DGtal library" <<std::endl
499  << "Basic usage: "<<std::endl
500  << "\tcontourGenerator --shape <shapeName> [requiredParam] [otherOptions]"<<std::endl
501  << general_opt << "\n";
502  return 0;
503  }
504 
505  //List creation
506  createList();
507 
508  if (vm.count("list"))
509  {
510  displayList();
511  return 0;
512  }
513 
514  //Parse options
515  if (!(vm.count("shape"))) missingParam("--shape");
516  std::string shapeName = vm["shape"].as<std::string>();
517 
518  bool withGeom = true;
519  std::string outputFileName;
520  if (!(vm.count("outputGeometry"))) withGeom = false;
521  else outputFileName = vm["outputGeometry"].as<std::string>();
522 
523  if (!(vm.count("format"))) missingParam("--format");
524  std::string outputFormat = vm["format"].as<std::string>();
525 
526  //We check that the shape is known
527  unsigned int id = checkAndReturnIndex(shapeName);
528 
529  // standard types
530  typedef Z2i::Space Space;
531  typedef Space::RealPoint RealPoint;
532 
533  RealPoint center( vm["center_x"].as<double>(),
534  vm["center_y"].as<double>() );
535  double h = vm["gridstep"].as<double>();
536  if (id ==0)
537  {
538  if (!(vm.count("radius"))) missingParam("--radius");
539  double radius = vm["radius"].as<double>();
540  Ball2D<Space> ball(Z2i::Point(0,0), radius);
541  generateContour<Space>( ball, h, outputFormat, withGeom, outputFileName );
542  }
543  else if (id ==1)
544  {
545  if (!(vm.count("width"))) missingParam("--width");
546  double width = vm["width"].as<double>();
547  ImplicitHyperCube<Space> object(Z2i::Point(0,0), width/2);
548  trace.error()<< "Not available.";
549  trace.info()<<std::endl;
550  }
551  else if (id ==2)
552  {
553  if (!(vm.count("power"))) missingParam("--power");
554  if (!(vm.count("radius"))) missingParam("--radius");
555  double radius = vm["radius"].as<double>();
556  double power = vm["power"].as<double>();
557  ImplicitRoundedHyperCube<Space> ball(Z2i::Point(0,0), radius, power);
558  trace.error()<< "Not available.";
559  trace.info()<<std::endl;
560  }
561  else if (id ==3)
562  {
563  if (!(vm.count("varsmallradius"))) missingParam("--varsmallradius");
564  if (!(vm.count("radius"))) missingParam("--radius");
565  if (!(vm.count("k"))) missingParam("--k");
566  if (!(vm.count("phi"))) missingParam("--phi");
567  double radius = vm["radius"].as<double>();
568  double varsmallradius = vm["varsmallradius"].as<double>();
569  unsigned int k = vm["k"].as<unsigned int>();
570  double phi = vm["phi"].as<double>();
571  Flower2D<Space> flower( center, radius, varsmallradius, k, phi );
572  generateContour<Space>( flower, h, outputFormat, withGeom, outputFileName );
573  }
574  else if (id ==4)
575  {
576  if (!(vm.count("radius"))) missingParam("--radius");
577  if (!(vm.count("k"))) missingParam("--k");
578  if (!(vm.count("phi"))) missingParam("--phi");
579  double radius = vm["radius"].as<double>();
580  unsigned int k = vm["k"].as<unsigned int>();
581  double phi = vm["phi"].as<double>();
582  NGon2D<Space> object( center, radius, k, phi );
583  generateContour<Space>( object, h, outputFormat, withGeom, outputFileName );
584  }
585  else if (id ==5)
586  {
587  if (!(vm.count("varsmallradius"))) missingParam("--varsmallradius");
588  if (!(vm.count("radius"))) missingParam("--radius");
589  if (!(vm.count("k"))) missingParam("--k");
590  if (!(vm.count("phi"))) missingParam("--phi");
591  double radius = vm["radius"].as<double>();
592  double varsmallradius = vm["varsmallradius"].as<double>();
593  unsigned int k = vm["k"].as<unsigned int>();
594  double phi = vm["phi"].as<double>();
595  AccFlower2D<Space> accflower( center, radius, varsmallradius, k, phi );
596  generateContour<Space>( accflower, h, outputFormat, withGeom, outputFileName );
597  }
598  else if (id ==6)
599  {
600  if (!(vm.count("axis1"))) missingParam("--axis1");
601  if (!(vm.count("axis2"))) missingParam("--axis2");
602  if (!(vm.count("phi"))) missingParam("--phi");
603  double a1 = vm["axis1"].as<double>();
604  double a2 = vm["axis2"].as<double>();
605  double phi = vm["phi"].as<double>();
606  Ellipse2D<Space> ellipse( center, a1, a2, phi );
607  generateContour<Space>( ellipse, h, outputFormat, withGeom, outputFileName );
608  }
609 }
DGtal::int32_t Integer
PointsRange getPointsRange() const
MidPointsRange getMidPointsRange() const
const Point & getUpperBound() const
SpaceND< 2, Integer > Space
Quantity eval(const ConstIterator &it) const
void attach(ParametricShape *aShapePtr)
T power(const T &aVal, const unsigned int exponent)
static SCell findABel(const KSpace &K, const PointPredicate &pp, unsigned int nbtries=1000)
void attach(const EuclideanShape &shape)
bool initFromVector(const std::vector< Point > &aVectorOfPoints)
void init(const RealPoint &xLow, const RealPoint &xUp, typename RealVector::Component gridStep)
void init(const double h, const ConstIterator &itb, const ConstIterator &ite)
std::ostream & emphase()
bool init(const Point &lower, const Point &upper, bool isClosed)
Domain getDomain() const
ConstIterator begin() const
Trace trace(traceWriterTerm)
std::ostream & info()
const Point & getLowerBound() const
static void track2DBoundaryPoints(std::vector< Point > &aVectorOfPoints, const KSpace &K, const SurfelAdjacency< KSpace::dimension > &surfel_adj, const PointPredicate &pp, const SCell &start_surfel)
typename Self::Point Point
std::ostream & error()
Space::Vector Vector
typename Self::Domain Domain