31 #include "DGtal/base/Common.h"
38 #include "DGtal/io/readers/GenericReader.h"
39 #include "DGtal/images/ImageSelector.h"
40 #include "DGtal/images/imagesSetsUtils/SetFromImage.h"
41 #include "DGtal/images/IntervalForegroundPredicate.h"
42 #include "DGtal/topology/SurfelAdjacency.h"
43 #include "DGtal/topology/helpers/Surfaces.h"
44 #include "DGtal/topology/LightImplicitDigitalSurface.h"
45 #include <DGtal/topology/SetOfSurfels.h>
47 #include "DGtal/images/ImageHelper.h"
48 #include "DGtal/topology/DigitalSurface.h"
49 #include "DGtal/graph/DepthFirstVisitor.h"
50 #include "DGtal/graph/GraphVisitorRange.h"
53 #include "DGtal/geometry/surfaces/estimation/IIGeometricFunctors.h"
54 #include "DGtal/geometry/surfaces/estimation/IntegralInvariantVolumeEstimator.h"
55 #include "DGtal/geometry/surfaces/estimation/IntegralInvariantCovarianceEstimator.h"
58 #include "DGtal/io/boards/Board3D.h"
59 #include "DGtal/io/colormaps/GradientColorMap.h"
61 #ifdef WITH_VISU3D_QGLVIEWER
62 #include "DGtal/io/viewers/Viewer3D.h"
65 using namespace DGtal;
66 using namespace functors;
142 const Color AXIS_COLOR_RED( 200, 20, 20, 255 );
143 const Color AXIS_COLOR_GREEN( 20, 200, 20, 255 );
144 const Color AXIS_COLOR_BLUE( 20, 20, 200, 255 );
145 const double AXIS_LINESIZE = 0.05;
153 void missingParam( std::string param )
155 trace.
error() <<
" Parameter: " << param <<
" is required.";
160 int main(
int argc,
char** argv )
164 std::string inputFileName;
165 double re_convolution_kernel;
166 double noiseLevel {0.5};
167 unsigned int threshold {8};
168 int minImageThreshold {0};
169 int maxImageThreshold {255};
170 std::string mode {
"mean"};
171 std::string export_obj_filename;
172 std::string export_dat_filename;
173 bool exportOnly {
false};
174 std::vector< double> vectScale;
175 bool normalization {
false};
177 app.description(
"Visualisation of 3d curvature from .vol file using curvature from Integral Invarian\nBasic usage:\n \t3dCurvatureViewerNoise file.vol --radius 5 --mode mean \n Below are the different available modes: \n\t - \"mean\" for the mean curvature \n \t - \"mean\" for the mean curvature\n\t - \"gaussian\" for the Gaussian curvature\n\t - \"k1\" for the first principal curvature\n\t - \"k2\" for the second principal curvature\n\t - \"prindir1\" for the first principal curvature direction\n\t - \"prindir2\" for the second principal curvature direction\n\t - \"normal\" for the normal vector");
181 app.add_option(
"-i,--input,1", inputFileName,
"vol file (.vol, .longvol .p3d, .pgm3d and if WITH_ITK is selected: dicom, dcm, mha, mhd). For longvol, dicom, dcm, mha or mhd formats, the input values are linearly scaled between 0 and 255." )
183 ->check(CLI::ExistingFile);
185 app.add_option(
"--radius,-r", re_convolution_kernel,
"Kernel radius for IntegralInvariant" )
187 app.add_option(
"--threshold,-t", threshold,
"Min size of SCell boundary of an object",
true);
188 app.add_option(
"--minImageThreshold,-l",minImageThreshold,
"set the minimal image threshold to define the image object (object defined by the voxel with intensity belonging to ]minImageThreshold, maxImageThreshold ] ).",
true);
189 app.add_option(
"--maxImageThreshold,-u",maxImageThreshold,
"set the maximal image threshold to define the image object (object defined by the voxel with intensity belonging to ]minImageThreshold, maxImageThreshold] ).",
true);
190 app.add_option(
"--mode,-m", mode,
"type of output : mean, gaussian, k1, k2, prindir1, prindir2 or normal(default mean)",
true)
191 -> check(CLI::IsMember({
"mean",
"gaussian",
"k1",
"k2",
"prindir1",
"prindir2",
"normal" }));
192 app.add_option(
"--exportOBJ,-o", export_obj_filename,
"Export the scene to specified OBJ/MTL filename (extensions added).");
193 app.add_option(
"--exportDAT,-d",export_dat_filename,
"Export resulting curvature (for mean, gaussian, k1 or k2 mode) in a simple data file each line representing a surfel." );
194 app.add_flag(
"--exportOnly", exportOnly,
"Used to only export the result without the 3d Visualisation (usefull for scripts).");
196 app.add_option(
"--imageScale,-s", vectScale,
"scaleX, scaleY, scaleZ: re sample the source image according with a grid of size 1.0/scale (usefull to compute curvature on image defined on anisotropic grid). Set by default to 1.0 for the three axis.")
199 app.add_option(
"--normalization,-n",normalization,
"When exporting to OBJ, performs a normalization so that the geometry fits in [-1/2,1/2]^3") ;
201 app.get_formatter()->column_width(40);
202 CLI11_PARSE(app, argc, argv);
205 bool neededArgsGiven=
true;
208 #ifndef WITH_VISU3D_QGLVIEWER
209 bool enable_visu =
false;
211 bool enable_visu = !exportOnly;
213 bool enable_obj = export_obj_filename !=
"";
214 bool enable_dat = export_dat_filename !=
"";
216 if( !enable_visu && !enable_obj && !enable_dat )
218 #ifndef WITH_VISU3D_QGLVIEWER
219 trace.
error() <<
"You should specify what you want to export with --export and/or --exportDat." << std::endl;
221 trace.
error() <<
"You should specify what you want to export with --export and/or --exportDat, or remove --exportOnly." << std::endl;
223 neededArgsGiven =
false;
230 if( export_obj_filename.find(
".obj") == std::string::npos )
232 std::ostringstream oss;
233 oss << export_obj_filename <<
".obj" << std::endl;
234 export_obj_filename = oss.str();
239 std::vector< double > aGridSizeReSample;
240 if( vectScale.size() == 3)
242 aGridSizeReSample.push_back(1.0/vectScale.at(0));
243 aGridSizeReSample.push_back(1.0/vectScale.at(1));
244 aGridSizeReSample.push_back(1.0/vectScale.at(2));
249 aGridSizeReSample.push_back(1.0);
250 aGridSizeReSample.push_back(1.0);
251 aGridSizeReSample.push_back(1.0);
276 aGridSizeReSample, shiftVector3D);
278 SamplerImageAdapter sampledImage ( image, reSampler.getSubSampledDomain(), reSampler, identityFunctor );
279 ImagePredicate predicateIMG = ImagePredicate( sampledImage, minImageThreshold, maxImageThreshold );
282 Predicate predicate(domainPredicate, predicateIMG, andF );
287 bool space_ok = K.
init( domain.
lowerBound()-Z3i::Domain::Point::diagonal(),
288 domain.
upperBound()+Z3i::Domain::Point::diagonal(),
true );
291 trace.
error() <<
"Error in the Khalimsky space construction."<<std::endl;
306 std::vector< std::vector<SCell > > vectConnectedSCell;
308 std::ofstream outDat;
311 trace.
info() <<
"Exporting curvature as dat file: "<< export_dat_filename <<std::endl;
312 outDat.open( export_dat_filename.c_str() );
313 outDat <<
"# data exported from 3dCurvatureViewer implementing the II curvature estimator (Coeurjolly, D.; Lachaud, J.O; Levallois, J., (2013). Integral based Curvature"
314 <<
" Estimators in Digital Geometry. DGCI 2013.) " << std::endl;
315 outDat <<
"# format: surfel coordinates (in Khalimsky space) curvature: "<< mode << std::endl;
318 trace.
info()<<
"Number of components= "<<vectConnectedSCell.size()<<std::endl;
321 if( vectConnectedSCell.size() == 0 )
323 trace.
error()<<
"No surface component exists. Please check the vol file threshold parameter.";
328 #ifdef WITH_VISU3D_QGLVIEWER
329 QApplication application( argc, argv );
334 #ifdef WITH_VISU3D_QGLVIEWER
339 #ifdef WITH_VISU3D_QGLVIEWER
346 for(
unsigned int i = 0; i<vectConnectedSCell.size(); ++i )
348 if( vectConnectedSCell[i].size() <= threshold )
355 for( std::vector<SCell>::const_iterator it = vectConnectedSCell.at(i).begin();
356 it != vectConnectedSCell.at(i).end();
359 aSet.surfelSet().insert( *it);
362 MyDigitalSurface digSurf( aSet );
367 typedef VisitorRange::ConstIterator SurfelConstIterator;
368 VisitorRange range(
new Visitor( digSurf, *digSurf.begin() ) );
369 SurfelConstIterator abegin = range.begin();
370 SurfelConstIterator aend = range.end();
372 VisitorRange range2(
new Visitor( digSurf, *digSurf.begin() ) );
373 SurfelConstIterator abegin2 = range2.begin();
376 if( ( mode.compare(
"gaussian") == 0 ) || ( mode.compare(
"mean") == 0 )
377 || ( mode.compare(
"k1") == 0 ) || ( mode.compare(
"k2") == 0 ))
379 typedef double Quantity;
380 std::vector< Quantity > results;
381 std::back_insert_iterator< std::vector< Quantity > > resultsIterator( results );
382 if ( mode.compare(
"mean") == 0 )
387 MyIICurvatureFunctor functor;
388 functor.
init( h, re_convolution_kernel );
390 MyIIEstimator estimator( functor );
391 estimator.attach( K, predicate );
392 estimator.setParams( re_convolution_kernel/h );
393 estimator.init( h, abegin, aend );
395 estimator.eval( abegin, aend, resultsIterator );
397 else if ( mode.compare(
"gaussian") == 0 )
402 MyIICurvatureFunctor functor;
403 functor.
init( h, re_convolution_kernel );
405 MyIIEstimator estimator( functor ); estimator.attach( K,
406 predicate ); estimator.setParams( re_convolution_kernel/h );
407 estimator.init( h, abegin, aend );
409 estimator.eval( abegin, aend, resultsIterator );
411 else if ( mode.compare(
"k1") == 0 )
416 MyIICurvatureFunctor functor;
417 functor.
init( h, re_convolution_kernel );
419 MyIIEstimator estimator( functor );
420 estimator.attach( K, predicate );
421 estimator.setParams( re_convolution_kernel/h );
422 estimator.init( h, abegin, aend );
424 estimator.eval( abegin, aend, resultsIterator );
426 else if ( mode.compare(
"k2") == 0 )
431 MyIICurvatureFunctor functor;
432 functor.
init( h, re_convolution_kernel );
434 MyIIEstimator estimator( functor );
435 estimator.attach( K, predicate );
436 estimator.setParams( re_convolution_kernel/h );
437 estimator.init( h, abegin, aend );
439 estimator.eval( abegin, aend, resultsIterator );
446 Quantity min = results[ 0 ];
447 Quantity max = results[ 0 ];
448 for (
unsigned int i = 1; i < results.size(); ++i )
450 if ( results[ i ] < min )
454 else if ( results[ i ] > max )
459 trace.
info() <<
"Max value= "<<max<<
" min value= "<<min<<std::endl;
460 ASSERT( min <= max );
462 Gradient cmap_grad( min, (max==min)? max+1: max );
463 cmap_grad.addColor(
Color( 50, 50, 255 ) );
464 cmap_grad.addColor(
Color( 255, 0, 0 ) );
465 cmap_grad.addColor(
Color( 255, 255, 10 ) );
467 #ifdef WITH_VISU3D_QGLVIEWER
470 viewer <<
SetMode3D((*abegin2).className(),
"Basic" );
479 for (
unsigned int i = 0; i < results.size(); ++i )
481 #ifdef WITH_VISU3D_QGLVIEWER
498 outDat << kCoords[0] <<
" " << kCoords[1] <<
" " << kCoords[2] <<
" " << results[i] << std::endl;
507 std::vector< Quantity > results;
508 std::back_insert_iterator< std::vector< Quantity > > resultsIterator( results );
510 if( mode.compare(
"prindir1") == 0 )
515 MyIICurvatureFunctor functor;
516 functor.
init( h, re_convolution_kernel );
518 MyIIEstimator estimator( functor );
519 estimator.attach( K, predicate );
520 estimator.setParams( re_convolution_kernel/h );
521 estimator.init( h, abegin, aend );
523 estimator.eval( abegin, aend, resultsIterator );
525 else if( mode.compare(
"prindir2") == 0 )
530 MyIICurvatureFunctor functor;
531 functor.
init( h, re_convolution_kernel );
533 MyIIEstimator estimator( functor );
534 estimator.attach( K, predicate );
535 estimator.setParams( re_convolution_kernel/h );
536 estimator.init( h, abegin, aend );
538 estimator.eval( abegin, aend, resultsIterator );
541 if( mode.compare(
"normal") == 0 )
546 MyIICurvatureFunctor functor;
547 functor.
init( h, re_convolution_kernel );
549 MyIIEstimator estimator( functor );
550 estimator.attach( K, predicate );
551 estimator.setParams( re_convolution_kernel/h );
552 estimator.init( h, abegin, aend );
554 estimator.eval( abegin, aend, resultsIterator );
561 #ifdef WITH_VISU3D_QGLVIEWER
573 for (
unsigned int i = 0; i < results.size(); ++i )
577 if ( predicate(
Z3i::Point(embedder(outer),functors::Round<>()) ))
584 #ifdef WITH_VISU3D_QGLVIEWER
603 outDat << kCoords[0] <<
" " << kCoords[1] <<
" " << kCoords[2] <<
" "
604 << results[i][0] <<
" " << results[i][1] <<
" " << results[i][2]
610 #ifdef WITH_VISU3D_QGLVIEWER
613 if( mode.compare(
"prindir1") == 0 )
615 viewer.setLineColor( AXIS_COLOR_BLUE );
617 else if( mode.compare(
"prindir2") == 0 )
619 viewer.setLineColor( AXIS_COLOR_RED );
621 else if( mode.compare(
"normal") == 0 )
623 viewer.setLineColor( AXIS_COLOR_GREEN );
629 center[0] - 0.5 * results[i][0],
630 center[1] - 0.5 * results[i][1],
631 center[2] - 0.5 * results[i][2]
634 center[0] + 0.5 * results[i][0],
635 center[1] + 0.5 * results[i][1],
636 center[2] + 0.5 * results[i][2]
644 if( mode.compare(
"prindir1") == 0 )
646 board.setFillColor( AXIS_COLOR_BLUE );
648 else if( mode.compare(
"prindir2") == 0 )
650 board.setFillColor( AXIS_COLOR_RED );
652 else if( mode.compare(
"normal") == 0 )
654 board.setFillColor( AXIS_COLOR_GREEN );
659 center[0] - 0.5 * results[i][0],
660 center[1] - 0.5 * results[i][1],
661 center[2] - 0.5 * results[i][2]),
663 center[0] + 0.5 * results[i][0],
664 center[1] + 0.5 * results[i][1],
665 center[2] + 0.5 * results[i][2]),
675 #ifdef WITH_VISU3D_QGLVIEWER
678 viewer << Viewer3D<>::updateDisplay;
683 trace.
info()<<
"Exporting object: " << export_obj_filename <<
" ...";
684 board.saveOBJ(export_obj_filename,normalization);
692 #ifdef WITH_VISU3D_QGLVIEWER
695 return application.exec();
int main(int argc, char **argv)
const Point & upperBound() const
const Point & lowerBound() const
const Domain & domain() const
TImageContainer::Domain Domain
TImageContainer::Value Value
void init(const double _h, SurfelConstIterator itb, SurfelConstIterator ite)
void init(const double _h, SurfelConstIterator itb, SurfelConstIterator ite)
typename Self::Point Point
std::set< SCell > SurfelSet
bool init(const Point &lower, const Point &upper, bool isClosed)
Dimension sOrthDir(const SCell &s) const
const Point & sKCoords(const SCell &c) const
SCell sDirectIncident(const SCell &p, Dimension k) const
const Point & uKCoords(const Cell &c) const
Cell unsigns(const SCell &p) const
Cell uCell(const PreCell &c) const
SCell sIndirectIncident(const SCell &p, Dimension k) const
static void extractAllConnectedSCell(std::vector< std::vector< SCell > > &aVectConnectedSCell, const KSpace &aKSpace, const SurfelAdjacency< KSpace::dimension > &aSurfelAdj, const PointPredicate &pp, bool forceOrientCellExterior=false)
void beginBlock(const std::string &keyword="")
Trace trace(traceWriterTerm)
DGtal::uint32_t Dimension
static TContainer import(const std::string &filename, std::vector< unsigned int > dimSpace=std::vector< unsigned int >())
std::string className() const