int main( int argc, char** argv ) { // parse command line ---------------------------------------------- po::options_description general_opt("Allowed options are"); general_opt.add_options() ("help,h", "display this message") ("input,i", po::value< std::string >(), ".vol file") ("radius,r", po::value< double >(), "Kernel radius for IntegralInvariant" ) ("threshold,t", po::value< unsigned int >()->default_value(8), "Min size of SCell boundary of an object" ) ("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 ] )." ) ("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] )." ) ("mode,m", po::value< std::string >()->default_value("mean"), "type of output : mean, gaussian, k1, k2, prindir1, prindir2 or normal (default mean)") ("exportOBJ,o", po::value< std::string >(), "Export the scene to specified OBJ/MTL filename (extensions added)." ) ("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. ") ("exportOnly", "Used to only export the result without the 3d Visualisation (usefull for scripts)." ) ("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. ") ("normalization,n", "When exporting to OBJ, performs a normalization so that the geometry fits in [-1/2,1/2]^3") ; bool parseOK = true; po::variables_map vm; try { po::store( po::parse_command_line( argc, argv, general_opt ), vm ); } catch( const std::exception & ex ) { parseOK = false; trace.error() << " Error checking program options: " << ex.what() << std::endl; } bool neededArgsGiven=true; if (parseOK && !(vm.count("input"))){ missingParam("--input"); neededArgsGiven=false; } if (parseOK && !(vm.count("radius"))){ missingParam("--radius"); neededArgsGiven=false; } bool normalization = false; if (parseOK && vm.count("normalization")) normalization = true; std::string mode; if( parseOK ) mode = vm["mode"].as< std::string >(); if ( parseOK && ( mode.compare("gaussian") != 0 ) && ( mode.compare("mean") != 0 ) && ( mode.compare("k1") != 0 ) && ( mode.compare("k2") != 0 ) && ( mode.compare("prindir1") != 0 ) && ( mode.compare("prindir2") != 0 ) && ( mode.compare("normal") != 0 )) { parseOK = false; trace.error() << " The selected mode ("<<mode << ") is not defined."<<std::endl; } #ifndef WITH_VISU3D_QGLVIEWER bool enable_visu = false; #else bool enable_visu = !vm.count("exportOnly"); ///<! Default QGLViewer viewer. Disabled if exportOnly is set. #endif bool enable_obj = vm.count("exportOBJ"); ///<! Export to a .obj file. bool enable_dat = vm.count("exportDAT"); ///<! Export to a .dat file. if( !enable_visu && !enable_obj && !enable_dat ) { #ifndef WITH_VISU3D_QGLVIEWER trace.error() << "You should specify what you want to export with --export and/or --exportDat." << std::endl; #else trace.error() << "You should specify what you want to export with --export and/or --exportDat, or remove --exportOnly." << std::endl; #endif neededArgsGiven = false; } if(!neededArgsGiven || !parseOK || vm.count("help") || argc <= 1 ) { trace.info()<< "Visualisation of 3d curvature from .vol file using curvature from Integral Invariant" <<std::endl << general_opt << "\n" << "Basic usage: "<<std::endl << "\t3dCurvatureViewer -i file.vol --radius 5 --mode mean"<<std::endl << std::endl << "Below are the different available modes: " << std::endl << "\t - \"mean\" for the mean curvature" << std::endl << "\t - \"gaussian\" for the Gaussian curvature" << std::endl << "\t - \"k1\" for the first principal curvature" << std::endl << "\t - \"k2\" for the second principal curvature" << std::endl << "\t - \"prindir1\" for the first principal curvature direction" << std::endl << "\t - \"prindir2\" for the second principal curvature direction" << std::endl << "\t - \"normal\" for the normal vector" << std::endl << std::endl; return 0; } unsigned int threshold = vm["threshold"].as< unsigned int >(); int minImageThreshold = vm["minImageThreshold"].as< int >(); int maxImageThreshold = vm["maxImageThreshold"].as< int >(); double h = 1.0; std::string export_obj_filename; std::string export_dat_filename; if( enable_obj ) { export_obj_filename = vm["exportOBJ"].as< std::string >(); if( export_obj_filename.find(".obj") == std::string::npos ) { std::ostringstream oss; oss << export_obj_filename << ".obj" << std::endl; export_obj_filename = oss.str(); } } if( enable_dat ) { export_dat_filename = vm["exportDAT"].as<std::string>(); } double re_convolution_kernel = vm["radius"].as< double >(); std::vector< double > aGridSizeReSample; if( vm.count( "imageScale" )) { std::vector< double> vectScale = vm["imageScale"].as<std::vector<double > >(); if( vectScale.size() != 3 ) { trace.error() << "The grid size should contains 3 elements" << std::endl; return 0; } else { aGridSizeReSample.push_back(1.0/vectScale.at(0)); aGridSizeReSample.push_back(1.0/vectScale.at(1)); aGridSizeReSample.push_back(1.0/vectScale.at(2)); } } else { aGridSizeReSample.push_back(1.0); aGridSizeReSample.push_back(1.0); aGridSizeReSample.push_back(1.0); } // Construction of the shape from vol file typedef Z3i::Space::RealPoint RealPoint; typedef Z3i::Point Point; typedef ImageSelector< Z3i::Domain, int>::Type Image; typedef DGtal::functors::BasicDomainSubSampler< HyperRectDomain<SpaceND<3, int> >, DGtal::int32_t, double > ReSampler; typedef DGtal::ConstImageAdapter<Image, Image::Domain, ReSampler, Image::Value, DGtal::functors::Identity > SamplerImageAdapter; typedef IntervalForegroundPredicate< SamplerImageAdapter > ImagePredicate; typedef BinaryPointPredicate<DomainPredicate<Image::Domain>, ImagePredicate, AndBoolFct2 > Predicate; typedef Z3i::KSpace KSpace; typedef KSpace::SCell SCell; typedef KSpace::Cell Cell; trace.beginBlock("Loading the file"); std::string filename = vm["input"].as< std::string >(); Image image = GenericReader<Image>::import( filename ); PointVector<3,int> shiftVector3D( 0 ,0, 0 ); DGtal::functors::BasicDomainSubSampler< HyperRectDomain< SpaceND< 3, int > >, DGtal::int32_t, double > reSampler(image.domain(), aGridSizeReSample, shiftVector3D); const functors::Identity identityFunctor{}; SamplerImageAdapter sampledImage ( image, reSampler.getSubSampledDomain(), reSampler, identityFunctor ); ImagePredicate predicateIMG = ImagePredicate( sampledImage, minImageThreshold, maxImageThreshold ); DomainPredicate<Z3i::Domain> domainPredicate( sampledImage.domain() ); AndBoolFct2 andF; Predicate predicate(domainPredicate, predicateIMG, andF ); Z3i::Domain domain = sampledImage.domain(); Z3i::KSpace K; bool space_ok = K.init( domain.lowerBound()-Z3i::Domain::Point::diagonal(), domain.upperBound()+Z3i::Domain::Point::diagonal(), true ); if (!space_ok) { trace.error() << "Error in the Khalimsky space construction."<<std::endl; return 2; } CanonicSCellEmbedder< KSpace > embedder( K ); SurfelAdjacency< Z3i::KSpace::dimension > Sadj( true ); trace.endBlock(); // Viewer settings // Extraction of components typedef KSpace::SurfelSet SurfelSet; typedef SetOfSurfels< KSpace, SurfelSet > MySetOfSurfels; typedef DigitalSurface< MySetOfSurfels > MyDigitalSurface; trace.beginBlock("Extracting surfaces"); std::vector< std::vector<SCell > > vectConnectedSCell; Surfaces<KSpace>::extractAllConnectedSCell(vectConnectedSCell,K, Sadj, predicate, false); std::ofstream outDat; if( enable_dat ) { trace.info() << "Exporting curvature as dat file: "<< export_dat_filename <<std::endl; outDat.open( export_dat_filename.c_str() ); outDat << "# data exported from 3dCurvatureViewer implementing the II curvature estimator (Coeurjolly, D.; Lachaud, J.O; Levallois, J., (2013). Integral based Curvature" << " Estimators in Digital Geometry. DGCI 2013.) " << std::endl; outDat << "# format: surfel coordinates (in Khalimsky space) curvature: "<< mode << std::endl; } trace.info()<<"Number of components= "<<vectConnectedSCell.size()<<std::endl; trace.endBlock(); if( vectConnectedSCell.size() == 0 ) { trace.error()<< "No surface component exists. Please check the vol file threshold parameter."; trace.info()<<std::endl; exit(2); } #ifdef WITH_VISU3D_QGLVIEWER QApplication application( argc, argv ); typedef Viewer3D<Z3i::Space, Z3i::KSpace> Viewer; #endif typedef Board3D<Z3i::Space, Z3i::KSpace> Board; #ifdef WITH_VISU3D_QGLVIEWER Viewer viewer( K ); #endif Board board( K ); #ifdef WITH_VISU3D_QGLVIEWER if( enable_visu ) { viewer.show(); } #endif for( unsigned int i = 0; i<vectConnectedSCell.size(); ++i ) { if( vectConnectedSCell[i].size() <= threshold ) { continue; } MySetOfSurfels aSet(K, Sadj); for( std::vector<SCell>::const_iterator it = vectConnectedSCell.at(i).begin(); it != vectConnectedSCell.at(i).end(); ++it ) { aSet.surfelSet().insert( *it); } MyDigitalSurface digSurf( aSet ); typedef DepthFirstVisitor<MyDigitalSurface> Visitor; typedef GraphVisitorRange< Visitor > VisitorRange; typedef VisitorRange::ConstIterator SurfelConstIterator; VisitorRange range( new Visitor( digSurf, *digSurf.begin() ) ); SurfelConstIterator abegin = range.begin(); SurfelConstIterator aend = range.end(); VisitorRange range2( new Visitor( digSurf, *digSurf.begin() ) ); SurfelConstIterator abegin2 = range2.begin(); trace.beginBlock("Curvature computation on a component"); if( ( mode.compare("gaussian") == 0 ) || ( mode.compare("mean") == 0 ) || ( mode.compare("k1") == 0 ) || ( mode.compare("k2") == 0 )) { typedef double Quantity; std::vector< Quantity > results; std::back_insert_iterator< std::vector< Quantity > > resultsIterator( results ); if ( mode.compare("mean") == 0 ) { typedef functors::IIMeanCurvature3DFunctor<Z3i::Space> MyIICurvatureFunctor; typedef IntegralInvariantVolumeEstimator<Z3i::KSpace, Predicate, MyIICurvatureFunctor> MyIIEstimator; MyIICurvatureFunctor functor; functor.init( h, re_convolution_kernel ); MyIIEstimator estimator( functor ); estimator.attach( K, predicate ); estimator.setParams( re_convolution_kernel/h ); estimator.init( h, abegin, aend ); estimator.eval( abegin, aend, resultsIterator ); } else if ( mode.compare("gaussian") == 0 ) { typedef functors::IIGaussianCurvature3DFunctor<Z3i::Space> MyIICurvatureFunctor; typedef IntegralInvariantCovarianceEstimator<Z3i::KSpace, Predicate, MyIICurvatureFunctor> MyIIEstimator; MyIICurvatureFunctor functor; functor.init( h, re_convolution_kernel ); MyIIEstimator estimator( functor ); estimator.attach( K, predicate ); estimator.setParams( re_convolution_kernel/h ); estimator.init( h, abegin, aend ); estimator.eval( abegin, aend, resultsIterator ); } else if ( mode.compare("k1") == 0 ) { typedef functors::IIFirstPrincipalCurvature3DFunctor<Z3i::Space> MyIICurvatureFunctor; typedef IntegralInvariantCovarianceEstimator<Z3i::KSpace, Predicate, MyIICurvatureFunctor> MyIIEstimator; MyIICurvatureFunctor functor; functor.init( h, re_convolution_kernel ); MyIIEstimator estimator( functor ); estimator.attach( K, predicate ); estimator.setParams( re_convolution_kernel/h ); estimator.init( h, abegin, aend ); estimator.eval( abegin, aend, resultsIterator ); } else if ( mode.compare("k2") == 0 ) { typedef functors::IISecondPrincipalCurvature3DFunctor<Z3i::Space> MyIICurvatureFunctor; typedef IntegralInvariantCovarianceEstimator<Z3i::KSpace, Predicate, MyIICurvatureFunctor> MyIIEstimator; MyIICurvatureFunctor functor; functor.init( h, re_convolution_kernel ); MyIIEstimator estimator( functor ); estimator.attach( K, predicate ); estimator.setParams( re_convolution_kernel/h ); estimator.init( h, abegin, aend ); estimator.eval( abegin, aend, resultsIterator ); } trace.endBlock(); // Drawing results trace.beginBlock("Visualisation"); Quantity min = results[ 0 ]; Quantity max = results[ 0 ]; for ( unsigned int i = 1; i < results.size(); ++i ) { if ( results[ i ] < min ) { min = results[ i ]; } else if ( results[ i ] > max ) { max = results[ i ]; } } trace.info() << "Max value= "<<max<<" min value= "<<min<<std::endl; ASSERT( min <= max ); typedef GradientColorMap< Quantity > Gradient; Gradient cmap_grad( min, (max==min)? max+1: max ); cmap_grad.addColor( Color( 50, 50, 255 ) ); cmap_grad.addColor( Color( 255, 0, 0 ) ); cmap_grad.addColor( Color( 255, 255, 10 ) ); #ifdef WITH_VISU3D_QGLVIEWER if( enable_visu ) { viewer << SetMode3D((*abegin2).className(), "Basic" ); } #endif if( enable_obj ) { board << SetMode3D((K.unsigns(*abegin2)).className(), "Basic" ); } for ( unsigned int i = 0; i < results.size(); ++i ) { #ifdef WITH_VISU3D_QGLVIEWER if( enable_visu ) { viewer << CustomColors3D( Color::Black, cmap_grad( results[ i ] )); viewer << *abegin2; } #endif if( enable_obj ) { board << CustomColors3D( Color::Black, cmap_grad( results[ i ] )); board << K.unsigns(*abegin2); } if( enable_dat ) { Point kCoords = K.uKCoords(K.unsigns(*abegin2)); outDat << kCoords[0] << " " << kCoords[1] << " " << kCoords[2] << " " << results[i] << std::endl; } ++abegin2; } } else { typedef Z3i::Space::RealVector Quantity; std::vector< Quantity > results; std::back_insert_iterator< std::vector< Quantity > > resultsIterator( results ); if( mode.compare("prindir1") == 0 ) { typedef functors::IIFirstPrincipalDirectionFunctor<Z3i::Space> MyIICurvatureFunctor; typedef IntegralInvariantCovarianceEstimator<Z3i::KSpace, Predicate, MyIICurvatureFunctor> MyIIEstimator; MyIICurvatureFunctor functor; functor.init( h, re_convolution_kernel ); MyIIEstimator estimator( functor ); estimator.attach( K, predicate ); estimator.setParams( re_convolution_kernel/h ); estimator.init( h, abegin, aend ); estimator.eval( abegin, aend, resultsIterator ); } else if( mode.compare("prindir2") == 0 ) { typedef functors::IISecondPrincipalDirectionFunctor<Z3i::Space> MyIICurvatureFunctor; typedef IntegralInvariantCovarianceEstimator<Z3i::KSpace, Predicate, MyIICurvatureFunctor> MyIIEstimator; MyIICurvatureFunctor functor; functor.init( h, re_convolution_kernel ); MyIIEstimator estimator( functor ); estimator.attach( K, predicate ); estimator.setParams( re_convolution_kernel/h ); estimator.init( h, abegin, aend ); estimator.eval( abegin, aend, resultsIterator ); } else if( mode.compare("normal") == 0 ) { typedef functors::IINormalDirectionFunctor<Z3i::Space> MyIICurvatureFunctor; typedef IntegralInvariantCovarianceEstimator<Z3i::KSpace, Predicate, MyIICurvatureFunctor> MyIIEstimator; MyIICurvatureFunctor functor; functor.init( h, re_convolution_kernel ); MyIIEstimator estimator( functor ); estimator.attach( K, predicate ); estimator.setParams( re_convolution_kernel/h ); estimator.init( h, abegin, aend ); estimator.eval( abegin, aend, resultsIterator ); } ///Visualizaton / export #ifdef WITH_VISU3D_QGLVIEWER if( enable_visu ) { viewer << SetMode3D(K.uCell( K.sKCoords(*abegin2) ).className(), "Basic" ); } #endif if( enable_obj ) { board << SetMode3D(K.uCell( K.sKCoords(*abegin2) ).className(), "Basic" ); } for ( unsigned int i = 0; i < results.size(); ++i ) { DGtal::Dimension kDim = K.sOrthDir( *abegin2 ); SCell outer = K.sIndirectIncident( *abegin2, kDim); if ( predicate(embedder(outer)) ) { outer = K.sDirectIncident( *abegin2, kDim); } Cell unsignedSurfel = K.uCell( K.sKCoords(*abegin2) ); #ifdef WITH_VISU3D_QGLVIEWER if( enable_visu ) { viewer << CustomColors3D( DGtal::Color(255,255,255,255), DGtal::Color(255,255,255,255)) << unsignedSurfel; } #endif if( enable_obj ) { board << CustomColors3D( DGtal::Color(255,255,255,255), DGtal::Color(255,255,255,255)) << unsignedSurfel; } if( enable_dat ) { Point kCoords = K.uKCoords(K.unsigns(*abegin2)); outDat << kCoords[0] << " " << kCoords[1] << " " << kCoords[2] << " " << results[i][0] << " " << results[i][1] << " " << results[i][2] << std::endl; } RealPoint center = embedder( outer ); #ifdef WITH_VISU3D_QGLVIEWER if( enable_visu ) { if( mode.compare("prindir1") == 0 ) { viewer.setLineColor( AXIS_COLOR_BLUE ); } else if( mode.compare("prindir2") == 0 ) { viewer.setLineColor( AXIS_COLOR_RED ); } else if( mode.compare("normal") == 0 ) { viewer.setLineColor( AXIS_COLOR_GREEN ); } viewer.addLine ( RealPoint( center[0] - 0.5 * results[i][0], center[1] - 0.5 * results[i][1], center[2] - 0.5 * results[i][2] ), RealPoint( center[0] + 0.5 * results[i][0], center[1] + 0.5 * results[i][1], center[2] + 0.5 * results[i][2] ), AXIS_LINESIZE ); } #endif if( enable_obj ) { if( mode.compare("prindir1") == 0 ) { board.setFillColor( AXIS_COLOR_BLUE ); } else if( mode.compare("prindir2") == 0 ) { board.setFillColor( AXIS_COLOR_RED ); } else if( mode.compare("normal") == 0 ) { board.setFillColor( AXIS_COLOR_GREEN ); } board.addCylinder ( RealPoint( center[0] - 0.5 * results[i][0], center[1] - 0.5 * results[i][1], center[2] - 0.5 * results[i][2]), RealPoint( center[0] + 0.5 * results[i][0], center[1] + 0.5 * results[i][1], center[2] + 0.5 * results[i][2]), 0.2 ); } ++abegin2; } } trace.endBlock(); } #ifdef WITH_VISU3D_QGLVIEWER if( enable_visu ) { viewer << Viewer3D<>::updateDisplay; } #endif if( enable_obj ) { trace.info()<< "Exporting object: " << export_obj_filename << " ..."; board.saveOBJ(export_obj_filename,normalization); trace.info() << "[done]" << std::endl; } if( enable_dat ) { outDat.close(); } #ifdef WITH_VISU3D_QGLVIEWER if( enable_visu ) { return application.exec(); } #endif return 0; }
int main( int argc, char** argv ) { const double h = 1; const double radiusBall = 12.0; const double radiusII = 6; const double trueAreaSurface = 4.0*M_PI*radiusBall*radiusBall; trace.beginBlock( "Make parametric shape..." ); typedef Ball3D< Z3i::Space > Shape; Z3i::RealPoint center( 0.0, 0.0, 0.0 ); Shape ball( center, radiusBall ); trace.endBlock(); trace.beginBlock( "Make digital shape..." ); typedef GaussDigitizer< Z3i::Space, Shape > DigitalShape; typedef DigitalShape::Domain Domain; DigitalShape digitalBall; digitalBall.attach( ball ); digitalBall.init( ball.getLowerBound() - Z3i::RealPoint( 1.0, 1.0, 1.0 ), ball.getUpperBound() + Z3i::RealPoint( 1.0, 1.0, 1.0 ), h ); Domain domain = digitalBall.getDomain(); Z3i::KSpace kspace; kspace.init( domain.lowerBound(), domain.upperBound(), true ); trace.endBlock(); trace.beginBlock( "Make digital surface..." ); typedef LightImplicitDigitalSurface< Z3i::KSpace, DigitalShape > LightDigitalSurface; typedef DigitalSurface< LightDigitalSurface > DigitalSurface; typedef DepthFirstVisitor< DigitalSurface > DepthFirstVisitor; typedef GraphVisitorRange< DepthFirstVisitor > GraphVisitorRange; typedef GraphVisitorRange::ConstIterator SurfelConstIterator; typedef Z3i::KSpace::Surfel Surfel; Surfel bel = Surfaces< Z3i::KSpace >::findABel( kspace, digitalBall, 500 ); SurfelAdjacency< Z3i::KSpace::dimension > surfelAdjacency( true ); LightDigitalSurface lightDigitalSurface( kspace, digitalBall, surfelAdjacency, bel ); DigitalSurface digitalSurface( lightDigitalSurface ); GraphVisitorRange graphVisitorRange( new DepthFirstVisitor( digitalSurface, *digitalSurface.begin() ) ); SurfelConstIterator sbegin = graphVisitorRange.begin(); SurfelConstIterator send = graphVisitorRange.end(); std::vector< Surfel > v_border; while( sbegin != send ) { v_border.push_back( *sbegin ); ++sbegin; } trace.endBlock(); trace.beginBlock( "Computation with normal estimation ..." ); typedef IIGeometricFunctors::IINormalDirectionFunctor< Z3i::Space > NormalFunctor; typedef IntegralInvariantCovarianceEstimator< Z3i::KSpace, DigitalShape, NormalFunctor > IINormalEstimator; NormalFunctor normalFunctor; IINormalEstimator normalEstimator( normalFunctor ); normalEstimator.attach( kspace, digitalBall ); normalEstimator.setParams( radiusII / h ); normalEstimator.init( h, v_border.begin(), v_border.end() ); double areaSurfaceEstimated = 0.0; for( unsigned int i_position = 0; i_position < v_border.size(); ++i_position ) { Z3i::RealPoint normalEstimated = normalEstimator.eval( &(v_border[i_position]) ); Z3i::RealPoint normalSurfel = kspace.sKCoords( kspace.sDirectIncident( v_border[i_position], kspace.sOrthDir( v_border[i_position] ))) - kspace.sKCoords( v_border[i_position] ); normalEstimated = normalEstimated.getNormalized(); areaSurfaceEstimated += std::abs( normalEstimated.dot( normalSurfel )) * h * h; } trace.endBlock(); trace.info() << "Area Surface estimated : " << areaSurfaceEstimated << std::endl; trace.info() << "True areaSurface : " << trueAreaSurface << std::endl; trace.info() << "Ratio : " << areaSurfaceEstimated / trueAreaSurface << std::endl; return 0; }