int main( int argc, char** argv ) { QApplication application(argc,argv); typedef Z3i::Space Space; typedef Z3i::KSpace KSpace; typedef Z3i::Point Point; typedef Z3i::RealPoint RealPoint; typedef Z3i::RealVector RealVector; typedef HyperRectDomain<Space> Domain; typedef KSpace::Surfel Surfel; typedef KSpace::Cell Cell; typedef ImageSelector<Domain, unsigned char>::Type Image; typedef functors::IntervalForegroundPredicate<Image> ThresholdedImage; typedef ImplicitDigitalSurface< KSpace, ThresholdedImage > DigitalSurfaceContainer; //! [DVCM3D-typedefs] typedef ExactPredicateLpSeparableMetric<Space, 2> Metric; // L2-metric type typedef functors::HatPointFunction<Point,double> KernelFunction; // chi function type typedef VoronoiCovarianceMeasureOnDigitalSurface< DigitalSurfaceContainer, Metric, KernelFunction > VCMOnSurface; typedef VCMOnSurface::Surfel2Normals::const_iterator S2NConstIterator; //! [DVCM3D-typedefs] string inputFilename = examplesPath + "samples/Al.100.vol"; trace.info() << "File = " << inputFilename << std::endl; int thresholdMin = 0; trace.info() << "Min image thres. = " << thresholdMin << std::endl; int thresholdMax = 1; trace.info() << "Max image thres. = " << thresholdMax << std::endl; const double R = 20; trace.info() << "Big radius R = " << R << std::endl; const double r = 3; trace.info() << "Small radius r = " << r << std::endl; const double trivial_r = 3; trace.info() << "Trivial radius t = " << trivial_r << std::endl; // for orienting the directions given by the tensor. const double T = 0.1; trace.info() << "Feature thres. T = " << T << std::endl; // threshold for displaying features as red. const double size = 1.0; // size of displayed normals. KSpace ks; // Reads the volume trace.beginBlock( "Loading image into memory and build digital surface." ); Image image = GenericReader<Image>::import(inputFilename ); ThresholdedImage thresholdedImage( image, thresholdMin, thresholdMax ); trace.endBlock(); trace.beginBlock( "Extracting boundary by scanning the space. " ); ks.init( image.domain().lowerBound(), image.domain().upperBound(), true ); SurfelAdjacency<KSpace::dimension> surfAdj( true ); // interior in all directions. Surfel bel = Surfaces<KSpace>::findABel( ks, thresholdedImage, 10000 ); DigitalSurfaceContainer* container = new DigitalSurfaceContainer( ks, thresholdedImage, surfAdj, bel, false ); DigitalSurface< DigitalSurfaceContainer > surface( container ); //acquired trace.info() << "Digital surface has " << surface.size() << " surfels." << std::endl; trace.endBlock(); //! [DVCM3D-instantiation] Surfel2PointEmbedding embType = Pointels; // Could be Pointels|InnerSpel|OuterSpel; Metric l2; // Euclidean L2 metric KernelFunction chi( 1.0, r ); // hat function with support of radius r VCMOnSurface vcm_surface( surface, embType, R, r, chi, trivial_r, l2, true /* verbose */ ); //! [DVCM3D-instantiation] trace.beginBlock( "Displaying VCM" ); Viewer3D<> viewer( ks ); Cell dummy; viewer.setWindowTitle("3D VCM viewer"); viewer << SetMode3D( dummy.className(), "Basic" ); viewer.show(); GradientColorMap<double> grad( 0, T ); grad.addColor( Color( 128, 128, 255 ) ); grad.addColor( Color( 255, 255, 255 ) ); grad.addColor( Color( 255, 255, 0 ) ); grad.addColor( Color( 255, 0, 0 ) ); RealVector lambda; // eigenvalues of chi-vcm for ( S2NConstIterator it = vcm_surface.mapSurfel2Normals().begin(), itE = vcm_surface.mapSurfel2Normals().end(); it != itE; ++it ) { Surfel s = it->first; Point kp = ks.sKCoords( s ); RealPoint rp( 0.5 * (double) kp[ 0 ], 0.5 * (double) kp[ 1 ], 0.5 * (double) kp[ 2 ] ); RealVector n = it->second.vcmNormal; vcm_surface.getChiVCMEigenvalues( lambda, s ); double ratio = lambda[ 1 ] / ( lambda[ 0 ] + lambda[ 1 ] + lambda[ 2 ] ); viewer.setFillColor( grad( ratio > T ? T : ratio ) ); viewer << ks.unsigns( s ); n *= size; viewer.setLineColor( Color::Black ); viewer.addLine( rp + n, rp - n, 0.1 ); } viewer << Viewer3D<>::updateDisplay; application.exec(); trace.endBlock(); return 0; }
bool testUmbrellaComputer() { using namespace Z3i; typedef Space::RealPoint RealPoint; typedef ImplicitBall<Space> EuclideanShape; typedef GaussDigitizer<Space,EuclideanShape> DigitalShape; typedef GaussDigitizer<Space,EuclideanShape>::Domain Domain; typedef LightImplicitDigitalSurface<KSpace,DigitalShape> Boundary; typedef Boundary::SurfelConstIterator ConstIterator; //typedef Boundary::Tracker Tracker; typedef Boundary::Surfel Surfel; typedef Boundary::DigitalSurfaceTracker DigitalSurfaceTracker; typedef DigitalSurface<Boundary> MyDigitalSurface; typedef UmbrellaComputer<DigitalSurfaceTracker> MyUmbrellaComputer; unsigned int nbok = 0; unsigned int nb = 0; trace.beginBlock ( "Testing block ... UmbrellaComputer" ); // Creating shape Point c( 0, 0, 0 ); EuclideanShape ball( c, 2 ); // ball r=4 DigitalShape shape; shape.attach( ball ); shape.init( RealPoint( -10.0, -10.0, -10.0 ), RealPoint( 10.0, 10.0, 10.0 ), 1.0 ); // Creating cellular grid space around. Domain domain = shape.getDomain(); KSpace K; nbok += K.init( domain.lowerBound(), domain.upperBound(), true ) ? 1 : 0; nb++; trace.info() << "(" << nbok << "/" << nb << ") " << "K.init() is ok" << std::endl; // Find start surfel on surface. Surfel bel = Surfaces<KSpace>::findABel( K, shape, 10000 ); // Define surface container then surface itself. Boundary boundary( K, // cellular space shape, // point predicate SurfelAdjacency<KSpace::dimension>( true ), // adjacency bel // starting surfel ); MyDigitalSurface digSurf( boundary ); // boundary is cloned // Get tracker on surface. DigitalSurfaceTracker* ptrTracker = boundary.newTracker( bel ); MyUmbrellaComputer umbrella; KSpace::DirIterator dirIt = K.sDirs( bel ); Dimension k = *dirIt; Dimension j = *(++dirIt); trace.beginBlock ( "Testing block ... forward umbrella" ); umbrella.init( *ptrTracker, k, true, j ); unsigned int nb_forward = 0; Surfel init_bel = bel; do { Point x = K.sKCoords( bel ); trace.info() << x << std::endl; umbrella.next(); ++nb_forward; bel = umbrella.surfel(); } while ( bel != init_bel ); trace.endBlock(); trace.beginBlock ( "Testing block ... backward umbrella" ); unsigned int nb_backward = 0; do { Point x = K.sKCoords( bel ); trace.info() << x << std::endl; umbrella.previous(); ++nb_backward; bel = umbrella.surfel(); } while ( bel != init_bel ); nb++, nbok += nb_forward == nb_backward ? 1 : 0; trace.info() << "(" << nbok << "/" << nb << ") " << " nb_forward(" << nb_forward << ") == nb_backward(" << nb_backward << ")" << std::endl; trace.endBlock(); unsigned int nbsurfels = 0; for ( ConstIterator it = boundary.begin(), it_end = boundary.end(); it != it_end; ++it ) { ++nbsurfels; } trace.info() << nbsurfels << " surfels found." << std::endl; trace.endBlock(); delete ptrTracker; return nbok == nb; }
int main( int argc, char** argv ) { QApplication application(argc,argv); string inputFilename = argc > 1 ? argv[ 1 ] : examplesPath+"/samples/Al.100.vol"; int threshold = argc > 2 ? atoi( argv[ 2 ] ) : 0; int widthNum = argc > 3 ? atoi( argv[ 3 ] ) : 2; int widthDen = argc > 4 ? atoi( argv[ 4 ] ) : 1; //! [polyhedralizer-readVol] trace.beginBlock( "Reading vol file into an image." ); typedef ImageContainerBySTLVector< Domain, int> Image; Image image = VolReader<Image>::importVol(inputFilename); typedef functors::SimpleThresholdForegroundPredicate<Image> DigitalObject; DigitalObject digitalObject( image, threshold ); trace.endBlock(); //! [polyhedralizer-readVol] //! [polyhedralizer-KSpace] trace.beginBlock( "Construct the Khalimsky space from the image domain." ); KSpace ks; bool space_ok = ks.init( image.domain().lowerBound(), image.domain().upperBound(), true ); if (!space_ok) { trace.error() << "Error in the Khamisky space construction."<<endl; return 2; } trace.endBlock(); //! [polyhedralizer-KSpace] //! [polyhedralizer-SurfelAdjacency] typedef SurfelAdjacency<KSpace::dimension> MySurfelAdjacency; MySurfelAdjacency surfAdj( false ); // exterior in all directions. //! [polyhedralizer-SurfelAdjacency] //! [polyhedralizer-ExtractingSurface] trace.beginBlock( "Extracting boundary by tracking the surface. " ); typedef KSpace::Surfel Surfel; Surfel start_surfel = Surfaces<KSpace>::findABel( ks, digitalObject, 100000 ); typedef ImplicitDigitalSurface< KSpace, DigitalObject > MyContainer; typedef DigitalSurface< MyContainer > MyDigitalSurface; typedef MyDigitalSurface::ConstIterator ConstIterator; MyContainer container( ks, digitalObject, surfAdj, start_surfel ); MyDigitalSurface digSurf( container ); trace.info() << "Digital surface has " << digSurf.size() << " surfels." << endl; trace.endBlock(); //! [polyhedralizer-ExtractingSurface] //! [polyhedralizer-ComputingPlaneSize] // First pass to find biggest planes. trace.beginBlock( "Decomposition first pass. Computes all planes so as to sort vertices by the plane size." ); typedef BreadthFirstVisitor<MyDigitalSurface> Visitor; typedef ChordGenericNaivePlaneComputer<Z3,Z3::Point, DGtal::int64_t> NaivePlaneComputer; map<Surfel,unsigned int> v2size; for ( ConstIterator it = digSurf.begin(), itE= digSurf.end(); it != itE; ++it ) v2size[ *it ] = 0; int j = 0; int nb = digSurf.size(); NaivePlaneComputer planeComputer; vector<Point> layer; vector<Surfel> layer_surfel; for ( ConstIterator it = digSurf.begin(), itE= digSurf.end(); it != itE; ++it ) { if ( ( (++j) % 50 == 0 ) || ( j == nb ) ) trace.progressBar( j, nb ); Surfel v = *it; planeComputer.init( widthNum, widthDen ); // The visitor takes care of all the breadth-first traversal. Visitor visitor( digSurf, v ); layer.clear(); layer_surfel.clear(); Visitor::Size currentSize = visitor.current().second; while ( ! visitor.finished() ) { Visitor::Node node = visitor.current(); v = node.first; int axis = ks.sOrthDir( v ); Point p = ks.sCoords( ks.sDirectIncident( v, axis ) ); if ( node.second != currentSize ) { bool isExtended = planeComputer.extend( layer.begin(), layer.end() ); if ( isExtended ) { for ( vector<Surfel>::const_iterator it_layer = layer_surfel.begin(), it_layer_end = layer_surfel.end(); it_layer != it_layer_end; ++it_layer ) { ++v2size[ *it_layer ]; } layer_surfel.clear(); layer.clear(); currentSize = node.second; } else break; } layer_surfel.push_back( v ); layer.push_back( p ); visitor.expand(); } } // Prepare queue typedef PairSorted2nd<Surfel,int> SurfelWeight; priority_queue<SurfelWeight> Q; for ( ConstIterator it = digSurf.begin(), itE= digSurf.end(); it != itE; ++it ) Q.push( SurfelWeight( *it, v2size[ *it ] ) ); trace.endBlock(); //! [polyhedralizer-ComputingPlaneSize] //! [polyhedralizer-segment] // Segmentation into planes trace.beginBlock( "Decomposition second pass. Visits vertices from the one with biggest plane to the one with smallest plane." ); typedef Triple<NaivePlaneComputer, Color, pair<RealVector,double> > RoundPlane; set<Surfel> processedVertices; vector<RoundPlane*> roundPlanes; map<Surfel,RoundPlane*> v2plane; j = 0; while ( ! Q.empty() ) { if ( ( (++j) % 50 == 0 ) || ( j == nb ) ) trace.progressBar( j, nb ); Surfel v = Q.top().first; Q.pop(); if ( processedVertices.find( v ) != processedVertices.end() ) // already in set continue; // process to next vertex RoundPlane* ptrRoundPlane = new RoundPlane; roundPlanes.push_back( ptrRoundPlane ); // to delete them afterwards. v2plane[ v ] = ptrRoundPlane; ptrRoundPlane->first.init( widthNum, widthDen ); ptrRoundPlane->third = make_pair( RealVector::zero, 0.0 ); // The visitor takes care of all the breadth-first traversal. Visitor visitor( digSurf, v ); layer.clear(); layer_surfel.clear(); Visitor::Size currentSize = visitor.current().second; while ( ! visitor.finished() ) { Visitor::Node node = visitor.current(); v = node.first; Dimension axis = ks.sOrthDir( v ); Point p = ks.sCoords( ks.sDirectIncident( v, axis ) ); if ( node.second != currentSize ) { bool isExtended = ptrRoundPlane->first.extend( layer.begin(), layer.end() ); if ( isExtended ) { for ( vector<Surfel>::const_iterator it_layer = layer_surfel.begin(), it_layer_end = layer_surfel.end(); it_layer != it_layer_end; ++it_layer ) { Surfel s = *it_layer; processedVertices.insert( s ); if ( v2plane.find( s ) == v2plane.end() ) v2plane[ s ] = ptrRoundPlane; } layer.clear(); layer_surfel.clear(); currentSize = node.second; } else break; } layer_surfel.push_back( v ); layer.push_back( p ); if ( processedVertices.find( v ) != processedVertices.end() ) // surfel is already in some plane. visitor.ignore(); else visitor.expand(); } if ( visitor.finished() ) { for ( vector<Surfel>::const_iterator it_layer = layer_surfel.begin(), it_layer_end = layer_surfel.end(); it_layer != it_layer_end; ++it_layer ) { Surfel s = *it_layer; processedVertices.insert( s ); if ( v2plane.find( s ) == v2plane.end() ) v2plane[ s ] = ptrRoundPlane; } } // Assign random color for each plane. ptrRoundPlane->second = Color( rand() % 192 + 64, rand() % 192 + 64, rand() % 192 + 64, 255 ); } trace.endBlock(); //! [polyhedralizer-segment] //! [polyhedralizer-lsf] for ( vector<RoundPlane*>::iterator it = roundPlanes.begin(), itE = roundPlanes.end(); it != itE; ++it ) { NaivePlaneComputer& computer = (*it)->first; RealVector normal; double mu = LSF( normal, computer.begin(), computer.end() ); (*it)->third = make_pair( normal, mu ); } //! [polyhedralizer-lsf] //! [polyhedralizer-projection] map<Surfel, RealPoint> coordinates; for ( map<Surfel,RoundPlane*>::const_iterator it = v2plane.begin(), itE = v2plane.end(); it != itE; ++it ) { Surfel v = it->first; RoundPlane* rplane = it->second; Point p = ks.sKCoords( v ); RealPoint rp( (double)p[ 0 ]/2.0, (double)p[ 1 ]/2.0, (double)p[ 2 ]/2.0 ); double mu = rplane->third.second; RealVector normal = rplane->third.first; double lambda = mu - rp.dot( normal ); coordinates[ v ] = rp + lambda*normal; } typedef vector<Surfel> SurfelRange; map<Surfel, RealPoint> new_coordinates; for ( ConstIterator it = digSurf.begin(), itE= digSurf.end(); it != itE; ++it ) { Surfel s = *it; SurfelRange neighbors; back_insert_iterator<SurfelRange> writeIt = back_inserter( neighbors ); digSurf.writeNeighbors( writeIt, *it ); RealPoint x = RealPoint::zero; for ( SurfelRange::const_iterator its = neighbors.begin(), itsE = neighbors.end(); its != itsE; ++its ) x += coordinates[ *its ]; new_coordinates[ s ] = x / neighbors.size(); } //! [polyhedralizer-projection] //! [polyhedralizer-MakeMesh] typedef unsigned int Number; typedef Mesh<RealPoint> MyMesh; typedef MyMesh::MeshFace MeshFace; typedef MyDigitalSurface::FaceSet FaceSet; typedef MyDigitalSurface::VertexRange VertexRange; map<Surfel, Number> index; // Numbers all vertices. Number nbv = 0; MyMesh polyhedron( true ); // Insert all projected surfels as vertices of the polyhedral surface. for ( ConstIterator it = digSurf.begin(), itE= digSurf.end(); it != itE; ++it ) { polyhedron.addVertex( new_coordinates[ *it ] ); index[ *it ] = nbv++; } // Define faces of the mesh. Outputs closed faces. FaceSet faces = digSurf.allClosedFaces(); for ( typename FaceSet::const_iterator itf = faces.begin(), itf_end = faces.end(); itf != itf_end; ++itf ) { MeshFace mface( itf->nbVertices ); VertexRange vtcs = digSurf.verticesAroundFace( *itf ); int i = 0; for ( typename VertexRange::const_iterator itv = vtcs.begin(), itv_end = vtcs.end(); itv != itv_end; ++itv ) { mface[ i++ ] = index[ *itv ]; } polyhedron.addFace( mface, Color( 255, 243, 150, 255 ) ); } //! [polyhedralizer-MakeMesh] //! [polyhedralizer-visualization] typedef Viewer3D<Space,KSpace> MyViewer3D; MyViewer3D viewer( ks ); viewer.show(); bool isOK = polyhedron >> "test.off"; bool isOK2 = polyhedron >> "test.obj"; viewer << polyhedron; viewer << MyViewer3D::updateDisplay; application.exec(); //! [polyhedralizer-visualization] //! [polyhedralizer-freeMemory] for ( vector<RoundPlane*>::iterator it = roundPlanes.begin(), itE = roundPlanes.end(); it != itE; ++it ) delete *it; //! [polyhedralizer-freeMemory] if (isOK && isOK2) return 0; else return 1; }
bool testCombinatorialSurface() { using namespace Z3i; typedef Space::RealPoint RealPoint; typedef ImplicitBall<Space> EuclideanShape; typedef GaussDigitizer<Space,EuclideanShape> DigitalShape; typedef GaussDigitizer<Space,EuclideanShape>::Domain Domain; typedef LightImplicitDigitalSurface<KSpace,DigitalShape> Boundary; typedef Boundary::SurfelConstIterator ConstIterator; //typedef Boundary::Tracker Tracker; typedef Boundary::Surfel Surfel; //typedef Boundary::DigitalSurfaceTracker DigitalSurfaceTracker; typedef DigitalSurface<Boundary> MyDigitalSurface; //typedef UmbrellaComputer<DigitalSurfaceTracker> MyUmbrellaComputer; typedef DigitalSurface<Boundary>::Face Face; typedef DigitalSurface<Boundary>::Arc Arc; typedef DigitalSurface<Boundary>::Vertex Vertex; unsigned int nbok = 0; unsigned int nb = 0; trace.beginBlock ( "Testing block ... Combinatorial surface" ); // Creating shape Point c( 0, 0, 0 ); EuclideanShape ball( c, 8 ); // ball r=4 DigitalShape shape; shape.attach( ball ); shape.init( RealPoint( -2.0, -3.0, -10.0 ), RealPoint( 10.0, 10.0, 10.0 ), 1.0 ); // Creating cellular grid space around. Domain domain = shape.getDomain(); KSpace K; nbok += K.init( domain.lowerBound(), domain.upperBound(), true ) ? 1 : 0; nb++; trace.info() << "(" << nbok << "/" << nb << ") " << "K.init() is ok" << std::endl; // Find start surfel on surface. Surfel bel = Surfaces<KSpace>::findABel( K, shape, 10000 ); // Define surface container then surface itself. Boundary boundary( K, // cellular space shape, // point predicate SurfelAdjacency<KSpace::dimension>( true ), // adjacency bel // starting surfel ); MyDigitalSurface digSurf( boundary ); // boundary is cloned trace.beginBlock ( "Testing block ... Digital surface faces." ); MyDigitalSurface::FaceSet all_faces = digSurf.allFaces(); for ( MyDigitalSurface::FaceSet::const_iterator it = all_faces.begin(), it_end = all_faces.end(); it != it_end; ++it ) { std::cerr << " face=" << K.sKCoords( digSurf.pivot( *it ) ) << ":"; std::cerr << "(" << it->nbVertices << ")" << (it->isClosed() ? "C": "O"); MyDigitalSurface::VertexRange vtx = digSurf.verticesAroundFace( *it ); for ( unsigned int i = 0; i < vtx.size(); ++i ) { std::cerr << " " << K.sKCoords( vtx[ i ] ); } std::cerr << std::endl; } trace.endBlock(); // Checks that vertices of a face are in the same order as the // incident arcs. trace.beginBlock( "Testing block ...Check order faces/arcs" ); unsigned int nbvtcs = 0; unsigned int nbarcs = 0; unsigned int nbfaces = 0; for ( ConstIterator it = boundary.begin(), it_end = boundary.end(); it != it_end; ++it, ++nbvtcs ) { const Vertex & vtx = *it; MyDigitalSurface::ArcRange arcs = digSurf.outArcs( vtx ); for ( unsigned int i = 0; i < arcs.size(); ++i, ++nbarcs ) { const Arc & arc = arcs[ i ]; MyDigitalSurface::FaceRange faces = digSurf.facesAroundArc( arc ); for ( unsigned int j = 0; j < faces.size(); ++j, ++nbfaces ) { const Face & face = faces[ j ]; // search vertex in face. MyDigitalSurface::VertexRange vertices = digSurf.verticesAroundFace( face ); unsigned int k = 0; while ( ( k < vertices.size() ) && ( vertices[ k ] != vtx ) ) ++k; ++nb; if ( k == vertices.size() ) trace.info() << "Error at vertex " << vtx << ". Vertex not found in incident face." << std::endl; else ++nbok; ++nb; if ( digSurf.head( arc ) != vertices[ (k+1) % vertices.size() ] ) trace.info() << "Error at vertex " << vtx << ". Arc is not in incident face." << std::endl; else ++nbok; } } } trace.info() << "(" << nbok << "/" << nb << ") " << "Tested nbvtcs=" << nbvtcs << " nbarcs=" << nbarcs << " nbfaces=" << nbfaces << std::endl; trace.endBlock(); trace.beginBlock( "Testing block ... export as OFF: ex-digital-surface.off" ); ofstream fout( "ex-digital-surface.off" ); if ( fout.good() ) digSurf.exportSurfaceAs3DOFF( fout ); fout.close(); trace.endBlock(); unsigned int nbsurfels = 0; for ( ConstIterator it = boundary.begin(), it_end = boundary.end(); it != it_end; ++it ) { ++nbsurfels; } trace.info() << nbsurfels << " surfels found." << std::endl; trace.endBlock(); return nbok == nb; }