DGtal 1.4.0
Loading...
Searching...
No Matches
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
129makeColorMap( 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
140void 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
170int 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
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.
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 main()
Definition testBits.cpp:56
KSpace K