31 #include "DGtal/base/Common.h"
34 #include <boost/program_options/options_description.hpp>
35 #include <boost/program_options/parsers.hpp>
36 #include <boost/program_options/variables_map.hpp>
39 #include "DGtal/io/readers/GenericReader.h"
40 #include "DGtal/images/ImageSelector.h"
41 #include "DGtal/images/imagesSetsUtils/SetFromImage.h"
42 #include "DGtal/images/IntervalForegroundPredicate.h"
43 #include "DGtal/topology/SurfelAdjacency.h"
44 #include "DGtal/topology/helpers/Surfaces.h"
45 #include "DGtal/topology/LightImplicitDigitalSurface.h"
46 #include <DGtal/topology/SetOfSurfels.h>
48 #include "DGtal/images/ImageHelper.h"
49 #include "DGtal/topology/DigitalSurface.h"
50 #include "DGtal/graph/DepthFirstVisitor.h"
51 #include "DGtal/graph/GraphVisitorRange.h"
54 #include "DGtal/geometry/volumes/KanungoNoise.h"
57 #include "DGtal/geometry/surfaces/estimation/IIGeometricFunctors.h"
58 #include "DGtal/geometry/surfaces/estimation/IntegralInvariantVolumeEstimator.h"
59 #include "DGtal/geometry/surfaces/estimation/IntegralInvariantCovarianceEstimator.h"
62 #include "DGtal/io/boards/Board3D.h"
63 #include "DGtal/io/colormaps/GradientColorMap.h"
65 #ifdef WITH_VISU3D_QGLVIEWER
66 #include "DGtal/io/viewers/Viewer3D.h"
69 using namespace DGtal;
164 const Color AXIS_COLOR_RED( 200, 20, 20, 255 );
165 const Color AXIS_COLOR_GREEN( 20, 200, 20, 255 );
166 const Color AXIS_COLOR_BLUE( 20, 20, 200, 255 );
167 const double AXIS_LINESIZE = 0.05;
176 void missingParam( std::string param )
178 trace.
error() <<
" Parameter: " << param <<
" is required.";
182 namespace po = boost::program_options;
184 int main(
int argc,
char** argv )
187 po::options_description general_opt(
"Allowed options are");
188 general_opt.add_options()
189 (
"help,h",
"display this message")
190 (
"input,i", po::value< std::string >(),
".vol file")
191 (
"radius,r", po::value< double >(),
"Kernel radius for IntegralInvariant" )
192 (
"noise,k", po::value< double >()->default_value(0.5),
"Level of Kanungo noise ]0;1[" )
193 (
"threshold,t", po::value< unsigned int >()->default_value(8),
"Min size of SCell boundary of an object" )
194 (
"minImageThreshold,l", po::value< int >()->default_value(0),
"set the minimal image threshold to define the image object (object defined by the voxel with intensity belonging to ]minImageThreshold, maxImageThreshold ] )." )
195 (
"maxImageThreshold,u", po::value< int >()->default_value(255),
"set the minimal image threshold to define the image object (object defined by the voxel with intensity belonging to ]minImageThreshold, maxImageThreshold] )." )
196 (
"mode,m", po::value< std::string >()->default_value(
"mean"),
"type of output : mean, gaussian, k1, k2, prindir1, prindir2 or normal(default mean)")
197 (
"exportOBJ,o", po::value< std::string >(),
"Export the scene to specified OBJ/MTL filename (extensions added)." )
198 (
"exportDAT,d", po::value<std::string>(),
"Export resulting curvature (for mean, gaussian, k1 or k2 mode) in a simple data file each line representing a surfel. ")
199 (
"exportOnly",
"Used to only export the result without the 3d Visualisation (usefull for scripts)." )
200 (
"imageScale,s", po::value<std::vector<double> >()->multitoken(),
"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. ")
201 (
"normalization,n",
"When exporting to OBJ, performs a normalization so that the geometry fits in [-1/2,1/2]^3") ;
204 po::variables_map vm;
207 po::store( po::parse_command_line( argc, argv, general_opt ), vm );
209 catch(
const std::exception & ex )
212 trace.
error() <<
" Error checking program options: " << ex.what() << std::endl;
214 bool neededArgsGiven=
true;
216 if (parseOK && !(vm.count(
"input"))){
217 missingParam(
"--input");
218 neededArgsGiven=
false;
220 if (parseOK && !(vm.count(
"radius"))){
221 missingParam(
"--radius");
222 neededArgsGiven=
false;
225 bool normalization =
false;
226 if (parseOK && vm.count(
"normalization"))
227 normalization =
true;
231 mode = vm[
"mode"].as< std::string >();
232 if ( parseOK && ( mode.compare(
"gaussian") != 0 ) && ( mode.compare(
"mean") != 0 ) &&
233 ( mode.compare(
"k1") != 0 ) && ( mode.compare(
"k2") != 0 ) &&
234 ( mode.compare(
"prindir1") != 0 ) && ( mode.compare(
"prindir2") != 0 )&& ( mode.compare(
"normal") != 0 ))
237 trace.
error() <<
" The selected mode ("<<mode <<
") is not defined."<<std::endl;
240 double noiseLevel = vm[
"noise"].as<
double >();
241 if( noiseLevel < 0.0 || noiseLevel > 1.0 )
244 trace.
error() <<
"The noise level should be in the interval: ]0, 1["<< std::endl;
247 #ifndef WITH_VISU3D_QGLVIEWER
248 bool enable_visu =
false;
250 bool enable_visu = !vm.count(
"exportOnly");
252 bool enable_obj = vm.count(
"exportOBJ");
253 bool enable_dat = vm.count(
"exportDAT");
255 if( !enable_visu && !enable_obj && !enable_dat )
257 #ifndef WITH_VISU3D_QGLVIEWER
258 trace.
error() <<
"You should specify what you want to export with --export and/or --exportDat." << std::endl;
260 trace.
error() <<
"You should specify what you want to export with --export and/or --exportDat, or remove --exportOnly." << std::endl;
262 neededArgsGiven =
false;
265 if(!neededArgsGiven || !parseOK || vm.count(
"help") || argc <= 1 )
267 trace.
info()<<
"Visualisation of 3d curvature from .vol file using curvature from Integral Invariant" <<std::endl
268 << general_opt <<
"\n"
269 <<
"Basic usage: "<<std::endl
270 <<
"\t3dCurvatureViewerNoise -i file.vol --radius 5 --mode mean --noise 0.5"<<std::endl
272 <<
"Below are the different available modes: " << std::endl
273 <<
"\t - \"mean\" for the mean curvature" << std::endl
274 <<
"\t - \"gaussian\" for the Gaussian curvature" << std::endl
275 <<
"\t - \"k1\" for the first principal curvature" << std::endl
276 <<
"\t - \"k2\" for the second principal curvature" << std::endl
277 <<
"\t - \"prindir1\" for the first principal curvature direction" << std::endl
278 <<
"\t - \"prindir2\" for the second principal curvature direction" << std::endl
279 <<
"\t - \"normal\" for the normal vector" << std::endl
283 unsigned int threshold = vm[
"threshold"].as<
unsigned int >();
284 int minImageThreshold = vm[
"minImageThreshold"].as<
int >();
285 int maxImageThreshold = vm[
"maxImageThreshold"].as<
int >();
289 std::string export_obj_filename;
290 std::string export_dat_filename;
294 export_obj_filename = vm[
"exportOBJ"].as< std::string >();
295 if( export_obj_filename.find(
".obj") == std::string::npos )
297 std::ostringstream oss;
298 oss << export_obj_filename <<
".obj" << std::endl;
299 export_obj_filename = oss.str();
306 export_dat_filename = vm[
"exportDAT"].as<std::string>();
309 double re_convolution_kernel = vm[
"radius"].as<
double >();
312 std::vector< double > aGridSizeReSample;
313 if( vm.count(
"imageScale" ))
315 std::vector< double> vectScale = vm[
"imageScale"].as<std::vector<double > >();
316 if( vectScale.size() != 3 )
318 trace.
error() <<
"The grid size should contains 3 elements" << std::endl;
323 aGridSizeReSample.push_back(1.0/vectScale.at(0));
324 aGridSizeReSample.push_back(1.0/vectScale.at(1));
325 aGridSizeReSample.push_back(1.0/vectScale.at(2));
330 aGridSizeReSample.push_back(1.0);
331 aGridSizeReSample.push_back(1.0);
332 aGridSizeReSample.push_back(1.0);
353 std::string filename = vm[
"input"].as< std::string >();
359 aGridSizeReSample, shiftVector3D);
361 SamplerImageAdapter sampledImage ( image, reSampler.getSubSampledDomain(), reSampler, identityFunctor );
362 ImagePredicate predicateIMG = ImagePredicate( sampledImage, minImageThreshold, maxImageThreshold );
363 KanungoPredicate noisifiedPredicateIMG( predicateIMG, sampledImage.domain(), noiseLevel );
366 Predicate predicate(domainPredicate, noisifiedPredicateIMG, andF );
371 bool space_ok = K.
init( domain.
lowerBound()-Z3i::Domain::Point::diagonal(),
372 domain.
upperBound()+Z3i::Domain::Point::diagonal(), true );
375 trace.
error() <<
"Error in the Khalimsky space construction."<<std::endl;
392 std::vector< std::vector<SCell > > vectConnectedSCell;
394 std::ofstream outDat;
397 trace.
info() <<
"Exporting curvature as dat file: "<< export_dat_filename <<std::endl;
398 outDat.open( export_dat_filename.c_str() );
399 outDat <<
"# data exported from 3dCurvatureViewer implementing the II curvature estimator (Coeurjolly, D.; Lachaud, J.O; Levallois, J., (2013). Integral based Curvature"
400 <<
" Estimators in Digital Geometry. DGCI 2013.) " << std::endl;
401 outDat <<
"# format: surfel coordinates (in Khalimsky space) curvature: "<< mode << std::endl;
404 trace.
info()<<
"Number of components= "<<vectConnectedSCell.size()<<std::endl;
407 if( vectConnectedSCell.size() == 0 )
409 trace.
error()<<
"No surface component exists. Please check the vol file threshold parameter.";
414 #ifdef WITH_VISU3D_QGLVIEWER
415 QApplication application( argc, argv );
420 #ifdef WITH_VISU3D_QGLVIEWER
425 #ifdef WITH_VISU3D_QGLVIEWER
433 unsigned int max_size = 0;
434 for(
unsigned int ii = 0; ii<vectConnectedSCell.size(); ++ii )
436 if( vectConnectedSCell[ii].size() <= threshold )
440 if( vectConnectedSCell[ii].size() > max_size )
442 max_size = vectConnectedSCell[ii].size();
447 MySetOfSurfels aSet(K, Sadj);
449 for( std::vector<SCell>::const_iterator it = vectConnectedSCell.at(i).begin();
450 it != vectConnectedSCell.at(i).end();
453 aSet.surfelSet().insert( *it);
456 MyDigitalSurface digSurf( aSet );
461 typedef VisitorRange::ConstIterator SurfelConstIterator;
462 VisitorRange range(
new Visitor( digSurf, *digSurf.begin() ) );
463 SurfelConstIterator abegin = range.begin();
464 SurfelConstIterator aend = range.end();
466 VisitorRange range2(
new Visitor( digSurf, *digSurf.begin() ) );
467 SurfelConstIterator abegin2 = range2.begin();
470 if( ( mode.compare(
"gaussian") == 0 ) || ( mode.compare(
"mean") == 0 )
471 || ( mode.compare(
"k1") == 0 ) || ( mode.compare(
"k2") == 0 ))
473 typedef double Quantity;
474 std::vector< Quantity > results;
475 std::back_insert_iterator< std::vector< Quantity > > resultsIterator( results );
476 if ( mode.compare(
"mean") == 0 )
481 MyIICurvatureFunctor functor;
482 functor.
init( h, re_convolution_kernel );
484 MyIIEstimator estimator( functor );
485 estimator.attach( K, predicate );
486 estimator.setParams( re_convolution_kernel/h );
487 estimator.init( h, abegin, aend );
489 estimator.eval( abegin, aend, resultsIterator );
491 else if ( mode.compare(
"gaussian") == 0 )
496 MyIICurvatureFunctor functor;
497 functor.
init( h, re_convolution_kernel );
499 MyIIEstimator estimator( functor ); estimator.attach( K,
500 predicate ); estimator.setParams( re_convolution_kernel/h );
501 estimator.init( h, abegin, aend );
503 estimator.eval( abegin, aend, resultsIterator );
505 else if ( mode.compare(
"k1") == 0 )
510 MyIICurvatureFunctor functor;
511 functor.
init( h, re_convolution_kernel );
513 MyIIEstimator estimator( functor );
514 estimator.attach( K, predicate );
515 estimator.setParams( re_convolution_kernel/h );
516 estimator.init( h, abegin, aend );
518 estimator.eval( abegin, aend, resultsIterator );
520 else if ( mode.compare(
"k2") == 0 )
525 MyIICurvatureFunctor functor;
526 functor.
init( h, re_convolution_kernel );
528 MyIIEstimator estimator( functor );
529 estimator.attach( K, predicate );
530 estimator.setParams( re_convolution_kernel/h );
531 estimator.init( h, abegin, aend );
533 estimator.eval( abegin, aend, resultsIterator );
540 Quantity min = results[ 0 ];
541 Quantity max = results[ 0 ];
542 for (
unsigned int i = 1; i < results.size(); ++i )
544 if ( results[ i ] < min )
548 else if ( results[ i ] > max )
553 trace.
info() <<
"Max value= "<<max<<
" min value= "<<min<<std::endl;
554 ASSERT( min <= max );
556 Gradient cmap_grad( min, (max==min)? max+1: max );
557 cmap_grad.addColor(
Color( 50, 50, 255 ) );
558 cmap_grad.addColor(
Color( 255, 0, 0 ) );
559 cmap_grad.addColor(
Color( 255, 255, 10 ) );
561 #ifdef WITH_VISU3D_QGLVIEWER
564 viewer <<
SetMode3D((*abegin2).className(),
"Basic" );
573 for (
unsigned int i = 0; i < results.size(); ++i )
575 #ifdef WITH_VISU3D_QGLVIEWER
592 outDat << kCoords[0] <<
" " << kCoords[1] <<
" " << kCoords[2] <<
" " << results[i] << std::endl;
601 std::vector< Quantity > results;
602 std::back_insert_iterator< std::vector< Quantity > > resultsIterator( results );
604 if( mode.compare(
"prindir1") == 0 )
609 MyIICurvatureFunctor functor;
610 functor.
init( h, re_convolution_kernel );
612 MyIIEstimator estimator( functor );
613 estimator.attach( K, predicate );
614 estimator.setParams( re_convolution_kernel/h );
615 estimator.init( h, abegin, aend );
617 estimator.eval( abegin, aend, resultsIterator );
619 else if( mode.compare(
"prindir2") == 0 )
624 MyIICurvatureFunctor functor;
625 functor.
init( h, re_convolution_kernel );
627 MyIIEstimator estimator( functor );
628 estimator.attach( K, predicate );
629 estimator.setParams( re_convolution_kernel/h );
630 estimator.init( h, abegin, aend );
632 estimator.eval( abegin, aend, resultsIterator );
633 }
else if( mode.compare(
"normal") == 0 )
638 MyIICurvatureFunctor functor;
639 functor.
init( h, re_convolution_kernel );
641 MyIIEstimator estimator( functor );
642 estimator.attach( K, predicate );
643 estimator.setParams( re_convolution_kernel/h );
644 estimator.init( h, abegin, aend );
646 estimator.eval( abegin, aend, resultsIterator );
651 #ifdef WITH_VISU3D_QGLVIEWER
663 for (
unsigned int i = 0; i < results.size(); ++i )
667 if ( predicate(embedder(outer)) )
674 #ifdef WITH_VISU3D_QGLVIEWER
693 outDat << kCoords[0] <<
" " << kCoords[1] <<
" " << kCoords[2] <<
" "
694 << results[i][0] <<
" " << results[i][1] <<
" " << results[i][2]
698 RealPoint center = embedder( outer );
700 #ifdef WITH_VISU3D_QGLVIEWER
703 if( mode.compare(
"prindir1") == 0 )
705 viewer.setLineColor( AXIS_COLOR_BLUE );
707 else if( mode.compare(
"prindir2") == 0 )
709 viewer.setLineColor( AXIS_COLOR_RED );
711 else if( mode.compare(
"normal") == 0 )
713 viewer.setLineColor( AXIS_COLOR_GREEN );
718 center[0] - 0.5 * results[i][0],
719 center[1] - 0.5 * results[i][1],
720 center[2] - 0.5 * results[i][2]
723 center[0] + 0.5 * results[i][0],
724 center[1] + 0.5 * results[i][1],
725 center[2] + 0.5 * results[i][2]
733 if( mode.compare(
"prindir1") == 0 )
735 board.setFillColor( AXIS_COLOR_BLUE );
737 else if( mode.compare(
"prindir2") == 0 )
739 board.setFillColor( AXIS_COLOR_RED );
741 else if( mode.compare(
"normal") == 0 )
743 board.setFillColor( AXIS_COLOR_GREEN );
748 center[0] - 0.5 * results[i][0],
749 center[1] - 0.5 * results[i][1],
750 center[2] - 0.5 * results[i][2]),
752 center[0] + 0.5 * results[i][0],
753 center[1] + 0.5 * results[i][1],
754 center[2] + 0.5 * results[i][2]),
763 #ifdef WITH_VISU3D_QGLVIEWER
766 viewer << Viewer3D<>::updateDisplay;
771 trace.
info()<<
"Exporting object: " << export_obj_filename <<
" ...";
772 board.saveOBJ(export_obj_filename,normalization);
780 #ifdef WITH_VISU3D_QGLVIEWER
783 return application.exec();
void beginBlock(const std::string &keyword="")
const Point & sKCoords(const SCell &c) const
std::set< SCell > SurfelSet
DGtal::uint32_t Dimension
SCell sDirectIncident(const SCell &p, Dimension k) const
static TContainer import(const std::string &filename, std::vector< unsigned int > dimSpace=std::vector< unsigned int >())
Cell uCell(const PreCell &c) const
void init(const double _h, SurfelConstIterator itb, SurfelConstIterator ite)
const Point & uKCoords(const Cell &c) const
Dimension sOrthDir(const SCell &s) const
bool init(const Point &lower, const Point &upper, bool isClosed)
static void extractAllConnectedSCell(std::vector< std::vector< SCell > > &aVectConnectedSCell, const KSpace &aKSpace, const SurfelAdjacency< KSpace::dimension > &aSurfelAdj, const PointPredicate &pp, bool forceOrientCellExterior=false)
const Point & upperBound() const
Trace trace(traceWriterTerm)
SCell sIndirectIncident(const SCell &p, Dimension k) const
typename Self::Point Point
void init(const double _h, SurfelConstIterator itb, SurfelConstIterator ite)
Cell unsigns(const SCell &p) const
TImageContainer::Value Value
const Point & lowerBound() const
TImageContainer::Domain Domain