DGtal  1.4.2
digpoly-curvature-measures-cnc-XY-3d.cpp
Go to the documentation of this file.
1 
114 #include <iostream>
115 #include <fstream>
116 #include <algorithm>
117 #include "DGtal/base/Common.h"
118 #include "DGtal/shapes/SurfaceMesh.h"
120 #include "DGtal/geometry/meshes/CorrectedNormalCurrentComputer.h"
122 #include "DGtal/helpers/Shortcuts.h"
123 #include "DGtal/helpers/ShortcutsGeometry.h"
124 #include "DGtal/io/writers/SurfaceMeshWriter.h"
125 #include "DGtal/io/colormaps/GradientColorMap.h"
126 #include "DGtal/io/colormaps/QuantifiedColorMap.h"
127 
129 makeColorMap( double min_value, double max_value )
130 {
131  DGtal::GradientColorMap< double > gradcmap( min_value, max_value );
132  gradcmap.addColor( DGtal::Color( 0, 0, 255 ) );
133  gradcmap.addColor( DGtal::Color( 0, 255, 255 ) );
134  gradcmap.addColor( DGtal::Color( 255, 255, 255 ) );
135  gradcmap.addColor( DGtal::Color( 255, 255, 0 ) );
136  gradcmap.addColor( DGtal::Color( 255, 0, 0 ) );
137  return gradcmap;
138 }
139 
140 void usage( int argc, char* argv[] )
141 {
142  using namespace DGtal;
143  using namespace DGtal::Z3i;
144  typedef Shortcuts< KSpace > SH;
145  std::cout << "Usage: " << std::endl
146  << "\t" << argv[ 0 ] << " <P> <B> <h> <R> <mode>" << std::endl
147  << std::endl
148  << "Computation of principal curvatures and directions on" << std::endl
149  << "a digitized implicit shape using constant or " << std::endl
150  << "interpolated corrected curvature measures (based " << std::endl
151  << "on the theory of corrected normal currents)." << std::endl
152  << "- builds the surface mesh from polynomial <P>" << std::endl
153  << "- <B> defines the digitization space size [-B,B]^3" << std::endl
154  << "- <h> is the gridstep digitization" << std::endl
155  << "- <R> is the radius of the measuring balls" << std::endl
156  << "- <mode> is either Const for constant corrected normal" << std::endl
157  << " vector field or Interp for interpolated corrected" << std::endl
158  << " normal vector field." << std::endl
159  << "It produces several OBJ files to display principal " << std::endl
160  << "curvatures and directions estimations: `example-cnc-K1.obj`" << std::endl
161  << "`example-cnc-K2.obj`, `example-cnc-D1.obj`, and" << std::endl
162  << "`example-cnc-D2.obj` as well as associated MTL files." << std::endl;
163  std::cout << "You may either write your own polynomial as 3*x^2*y-z^2*x*y+1" << std::endl
164  <<"or use a predefined polynomial in the following list:" << std::endl;
165  auto L = SH::getPolynomialList();
166  for ( const auto& p : L )
167  std::cout << p.first << " : " << p.second << std::endl;
168 }
169 
170 int main( int argc, char* argv[] )
171 {
172  if ( argc <= 1 )
173  {
174  usage( argc, argv );
175  return 0;
176  }
178  using namespace DGtal;
179  using namespace DGtal::Z3i;
182  typedef Shortcuts< KSpace > SH;
183  typedef ShortcutsGeometry< KSpace > SHG;
185  std::string poly = argv[ 1 ]; // polynomial
186  const double B = argc > 2 ? atof( argv[ 2 ] ) : 1.0; // max ||_oo bbox
187  const double h = argc > 3 ? atof( argv[ 3 ] ) : 1.0; // gridstep
188  const double R = argc > 4 ? atof( argv[ 4 ] ) : 2.0; // radius of measuring ball
189  std::string mode = argc > 5 ? argv[ 5 ] : "Const"; // either Const or Interp
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  // Read polynomial and build digital surface
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  }
235  smesh.init( vertices.cbegin(), vertices.cend(),
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  // Builds a CorrectedNormalCurrentComputer object onto the SurfaceMesh object
249  CNC cnc( smesh );
250  // Estimates normal vectors using Convolved Trivial Normal estimator
251  auto face_normals = SHG::getCTrivialNormalVectors( surface, surfels, params );
252  // Set corrected face normals => Corrected Normal Current with
253  // constant per face corrected vector field.
254  smesh.setFaceNormals( face_normals.cbegin(), face_normals.cend() ); // CCNC
255  // Set corrected vertex normals => Corrected Normal Current with
256  // smooth linearly interpolated per face corrected vector field.
257  if ( interpolated ) smesh.computeVertexNormalsFromFaceNormals(); // ICNC
258  // computes area, anisotropic XY curvature measures
259  auto mu0 = cnc.computeMu0();
260  auto muXY = cnc.computeMuXY();
262 
264  // estimates principal curvatures (K1,K2) and directions (D1,D2) by
265  // measure normalization.
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  // Remove normals for better blocky display.
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 ) );
317  const auto colormapK1 = makeQuantifiedColorMap( makeColorMap( -Kmax, Kmax ) );
318  const auto colormapK2 = makeQuantifiedColorMap( makeColorMap( -Kmax, Kmax ) );
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 }
Structure representing an RGB triple with alpha component.
Definition: Color.h:68
Aim: This class template may be used to (linearly) convert scalar values in a given range into a colo...
void addColor(const Color &color)
Aim: This class is used to simplify shape and surface creation. With it, you can create new shapes an...
Aim: This class is used to simplify shape and surface creation. With it, you can create new shapes an...
Definition: Shortcuts.h:105
std::ostream & error()
std::ostream & info()
CountedPtr< SH3::DigitalSurface > surface
int main(int argc, char *argv[])
DGtal::GradientColorMap< double > makeColorMap(double min_value, double max_value)
[curvature-measures-Includes]
void usage(int argc, char *argv[])
SMesh::Vertices Vertices
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)
Trace trace
Definition: Common.h:153
Aim: Utility class to compute curvature measures induced by (1) a corrected normal current defined by...
Aim: An helper class for writing mesh file formats (Waverfront OBJ at this point) and creating a Surf...
Aim: Represents an embedded mesh as faces and a list of vertices. Vertices may be shared among faces ...
Definition: SurfaceMesh.h:92
int max(int a, int b)
KSpace K
KSpace::Cell Cell