171{
  172  if ( argc <= 1 )
  173    {
  175      return 0;
  176    }
  178  using namespace DGtal;
 
  185  std::string  poly = argv[ 1 ]; 
  186  const double    B = argc > 2 ? atof( argv[ 2 ] ) : 1.0; 
 
  187  const double    h = argc > 3 ? atof( argv[ 3 ] ) : 1.0; 
  188  const double    R = argc > 4 ? atof( argv[ 4 ] ) : 2.0; 
  189  std::string  mode = argc > 5 ? argv[ 5 ] : "Const"; 
  190  bool interpolated = mode == "Interp";
  191  if ( interpolated )
  192    std::cout << "Using vertex-*Interpolated* Corrected Normal Current" << std::endl;
  193  else
  194    std::cout << "Using face-*Constant* Corrected Normal Current" << std::endl;
  196  
  197  auto params = SH::defaultParameters() | SHG::defaultParameters();
  198  params( "t-ring", 3 )( "surfaceTraversal", "Default" );
  199  params( "polynomial", poly )( "gridstep", h ); 
  200  params( 
"minAABB", -
B )( 
"maxAABB", 
B );
 
  201  params( "offset", 3.0 );
  202  auto shape       = SH::makeImplicitShape3D( params );
  203  auto K           = SH::getKSpace( params );
 
  204  auto dshape      = SH::makeDigitizedImplicitShape3D( shape, params );
  205  auto bimage      = SH::makeBinaryImage( dshape, params );
  206  if ( bimage == nullptr ) 
  207    {
  208      trace.error() <<  
"Unable to read polynomial <" 
  209                    << poly.c_str() << ">" << std::endl;
  210      return 1;
  211    }
  212  auto sembedder   = SH::getSCellEmbedder( 
K );
 
  213  auto embedder    = SH::getCellEmbedder( 
K );
 
  214  auto surface     = SH::makeDigitalSurface( bimage, 
K, params );
 
  215  auto surfels     = SH::getSurfelRange( 
surface, params );
 
  216  trace.info() << 
"- surface has " << surfels.size()<< 
" surfels." << std::endl;
 
  218
  220  SM smesh;
  221  std::vector< SM::Vertices > faces;
  222  SH::Cell2Index c2i;
  223  auto pointels = SH::getPointelRange( c2i, 
surface );
 
  224  auto vertices = SH::RealPoints( pointels.size() );
 
  225  std::transform( pointels.cbegin(), pointels.cend(), 
vertices.begin(),
 
  226                  [&] (const SH::Cell& c) { return h * embedder( c ); } ); 
  227  for ( 
auto&& surfel : *
surface )
 
  228    {
  229      const auto primal_surfel_vtcs = SH::getPointelRange( 
K, surfel );
 
  230      SM::Vertices face;              
  231      for ( auto&& primal_vtx : primal_surfel_vtcs )
  232        face.push_back( c2i[ primal_vtx ] );
  233      faces.push_back( face );
  234    }
  236              faces.cbegin(),    faces.cend() );
  237  trace.info() << smesh << std::endl;
 
  239
  241  auto exp_K1 = SHG::getFirstPrincipalCurvatures ( shape, 
K, surfels, params );
 
  242  auto exp_K2 = SHG::getSecondPrincipalCurvatures( shape, 
K, surfels, params );
 
  243  auto exp_D1 = SHG::getFirstPrincipalDirections ( shape, 
K, surfels, params );
 
  244  auto exp_D2 = SHG::getSecondPrincipalDirections( shape, 
K, surfels, params );
 
  246
  248  
  249  CNC cnc( smesh );
  250  
  251  auto face_normals = SHG::getCTrivialNormalVectors( 
surface, surfels, params );
 
  252  
  253  
  254  smesh.setFaceNormals( face_normals.cbegin(), face_normals.cend() ); 
  255  
  256  
  257  if ( interpolated ) smesh.computeVertexNormalsFromFaceNormals();    
  258  
  259  auto mu0  = cnc.computeMu0();
  260  auto muXY = cnc.computeMuXY();
  262
  264  
  265  
  266  std::vector< double > K1( smesh.nbFaces() );
  267  std::vector< double > 
K2( smesh.nbFaces() );
 
  268  std::vector< RealVector > D1( smesh.nbFaces() );
  269  std::vector< RealVector > D2( smesh.nbFaces() );
  270  for ( auto f = 0; f < smesh.nbFaces(); ++f )
  271    {
  272      const auto b    = smesh.faceCentroid( f );
  273      const auto N    = smesh.faceNormals()[ f ];
  274      const auto area = mu0 .measure( b, R, f );
  275      const auto M    = muXY.measure( b, R, f );
  276      std::tie( K1[ f ], K2[ f ], D1[ f ], D2[ f ] )
  277        = cnc.principalCurvatures( area, M, N );
  278    }
  280
  282  auto exp_K1_min_max = std::minmax_element( exp_K1.cbegin(), exp_K1.cend() );
  283  auto exp_K2_min_max = std::minmax_element( exp_K2.cbegin(), exp_K2.cend() );
  284  auto K1_min_max = std::minmax_element( K1.cbegin(), K1.cend() );
  285  auto K2_min_max = std::minmax_element( 
K2.cbegin(), 
K2.cend() );
 
  286  std::cout << "Expected K1 curvatures:"
  287            << " min=" << *exp_K1_min_max.first << " max=" << *exp_K1_min_max.second
  288            << std::endl;
  289  std::cout << "Computed k1 curvatures:"
  290            << " min=" << *K1_min_max.first << " max=" << *K1_min_max.second
  291            << std::endl;
  292  std::cout << "Expected k2 curvatures:"
  293            << " min=" << *exp_K2_min_max.first << " max=" << *exp_K2_min_max.second
  294            << std::endl;
  295  std::cout << "Computed k2 curvatures:"
  296            << " min=" << *K2_min_max.first << " max=" << *K2_min_max.second
  297            << std::endl;
  298  const auto      error_K1 = SHG::getScalarsAbsoluteDifference( K1, exp_K1 );
  299  const auto stat_error_K1 = SHG::getStatistic( error_K1 );
  300  const auto   error_K1_l2 = SHG::getScalarsNormL2( K1, exp_K1 );
  301  trace.info() << 
"|K1-K1_CNC|_oo = " << stat_error_K1.max() << std::endl;
 
  302  trace.info() << 
"|K1-K1_CNC|_2  = " << error_K1_l2 << std::endl;
 
  303  const auto      error_K2 = SHG::getScalarsAbsoluteDifference( K2, exp_K2 );
  304  const auto stat_error_K2 = SHG::getStatistic( error_K2 );
  305  const auto   error_K2_l2 = SHG::getScalarsNormL2( K2, exp_K2 );
  306  trace.info() << 
"|K2-K2_CNC|_oo = " << stat_error_K2.max() << std::endl;
 
  307  trace.info() << 
"|K2-K2_CNC|_2  = " << error_K2_l2 << std::endl;
 
  309
  311  
  312  smesh.vertexNormals() = SH::RealVectors();
  313  smesh.faceNormals()   = SH::RealVectors();
  315  const double    Kmax = std::max( fabs( *exp_K1_min_max.first ),
  316                                   fabs( *exp_K2_min_max.second ) );
  319  auto colorsK1 = SMW::Colors( smesh.nbFaces() );
  320  auto colorsK2 = SMW::Colors( smesh.nbFaces() );
  321  for ( auto i = 0; i < smesh.nbFaces(); i++ )
  322    {
  323      colorsK1[ i ] = colormapK1( K1[ i ] );
  324      colorsK2[ i ] = colormapK2( K2[ i ] );
  325    }
  326  SMW::writeOBJ( "example-cnc-K1", smesh, colorsK1 );
  327  SMW::writeOBJ( "example-cnc-K2", smesh, colorsK2 );
  328  const auto avg_e = smesh.averageEdgeLength();
  329  SH::RealPoints positions( smesh.nbFaces() );
  330  for ( auto f = 0; f < positions.size(); ++f )
  331    {
  332      D1[ f ] *= smesh.localWindow( f );
  333      positions[ f ] = smesh.faceCentroid( f ) - 0.5 * D1[ f ];
  334    }
  335  SH::saveVectorFieldOBJ( positions, D1, 0.05 * avg_e, SH::Colors(),
  336                          "example-cnc-D1",
  337                          SH::Color::Black, SH::Color( 0, 128, 0 ) );
  338  for ( auto f = 0; f < positions.size(); ++f )
  339    {
  340      D2[ f ] *= smesh.localWindow( f );
  341      positions[ f ] = smesh.faceCentroid( f ) - 0.5 * D2[ f ];
  342    }
  343  SH::saveVectorFieldOBJ( positions, D2, 0.05 * avg_e, SH::Colors(),
  344                          "example-cnc-D2",
  345                          SH::Color::Black, SH::Color(128, 0,128 ) );
  347  return 0;
  348}
CountedPtr< SH3::DigitalSurface > surface
 
DGtal::GradientColorMap< double > makeColorMap(double min_value, double max_value)
[curvature-measures-Includes]
 
Z3i this namespace gathers the standard of types for 3D imagery.
 
DGtal is the top-level namespace which contains all DGtal functions and types.
 
QuantifiedColorMap< TColorMap > makeQuantifiedColorMap(TColorMap colormap, int nb=50)
 
std::pair< typename graph_traits< DGtal::DigitalSurface< TDigitalSurfaceContainer > >::vertex_iterator, typename graph_traits< DGtal::DigitalSurface< TDigitalSurfaceContainer > >::vertex_iterator > vertices(const DGtal::DigitalSurface< TDigitalSurfaceContainer > &digSurf)