bool checkPlane( Integer a, Integer b, Integer c, Integer d, int diameter, unsigned int nbpoints, Statistic<double> & stats ) { typedef typename NaivePlaneComputer::Point Point; typedef typename Point::Component PointInteger; IntegerComputer<Integer> ic; Integer absA = ic.abs( a ); Integer absB = ic.abs( b ); Integer absC = ic.abs( c ); Integer x, y, z; Dimension axis; if ( ( absA >= absB ) && ( absA >= absC ) ) axis = 0; else if ( ( absB >= absA ) && ( absB >= absC ) ) axis = 1; else axis = 2; Point p; NaivePlaneComputer plane; plane.init( axis, diameter, 1, 1 ); // Checks that points within the naive plane are correctly recognized. unsigned int nb = 0; unsigned int nbok = 0; unsigned int nbchanges = 0; unsigned int complexity = plane.complexity(); while ( nb != nbpoints ) { p[ 0 ] = getRandomInteger<PointInteger>( -diameter+1, diameter ); p[ 1 ] = getRandomInteger<PointInteger>( -diameter+1, diameter ); p[ 2 ] = getRandomInteger<PointInteger>( -diameter+1, diameter ); x = (Integer) p[ 0 ]; y = (Integer) p[ 1 ]; z = (Integer) p[ 2 ]; switch ( axis ) { case 0: p[ 0 ] = NumberTraits<Integer>::castToInt64_t( ic.ceilDiv( d - b * y - c * z, a ) ); break; case 1: p[ 1 ] = NumberTraits<Integer>::castToInt64_t( ic.ceilDiv( d - a * x - c * z, b ) ); break; case 2: p[ 2 ] = NumberTraits<Integer>::castToInt64_t( ic.ceilDiv( d - a * x - b * y, c ) ); break; } bool ok = plane.extend( p ); // should be ok ++nb, nbok += ok ? 1 : 0; if ( ! ok ) { std::cerr << "[ERROR] p=" << p << " NOT IN plane=" << plane << std::endl; break; } if ( plane.complexity() != complexity ) { complexity = plane.complexity(); ++nbchanges; } } stats.addValue( (double) nbchanges ); return nb == nbok; }
int main( int argc, char** argv ) { //! [greedy-plane-segmentation-ex3-parseCommandLine] trace.info() << "Segments the surface at given threshold within given volume into digital planes of rational width num/den." << std::endl; // Setting default options: ---------------------------------------------- // input file used: string inputFilename = examplesPath + "samples/Al.100.vol" ; trace.info() << "input file used " << inputFilename << std::endl; // parameter threshold unsigned int threshold = 0; trace.info() << "the value that defines the isosurface in the image (an integer between 0 and 255)= " << threshold<< std::endl; // parameter widthNum unsigned int widthNum = 1; trace.info() << "the numerator of the rational width (a non-null integer) =" << widthNum<< std::endl; // parameter widthDen unsigned int widthDen = 1; trace.info() << "the denominator of the rational width (a non-null integer)= " << widthDen<< std::endl; //! [greedy-plane-segmentation-ex3-parseCommandLine] //! [greedy-plane-segmentation-ex3-loadVolume] QApplication application(argc,argv); typedef ImageSelector < Domain, int>::Type Image; Image image = VolReader<Image>::importVol(inputFilename); DigitalSet set3d (image.domain()); SetFromImage<DigitalSet>::append<Image>(set3d, image, threshold,255); //! [greedy-plane-segmentation-ex3-loadVolume] //! [greedy-plane-segmentation-ex3-makeSurface] trace.beginBlock( "Set up digital surface." ); // We initializes the cellular grid space used for defining the // digital surface. KSpace ks; bool ok = ks.init( set3d.domain().lowerBound(), set3d.domain().upperBound(), true ); if ( ! ok ) std::cerr << "[KSpace.init] Failed." << std::endl; SurfelAdjacency<KSpace::dimension> surfAdj( true ); // interior in all directions. MyDigitalSurfaceContainer* ptrSurfContainer = new MyDigitalSurfaceContainer( ks, set3d, surfAdj ); MyDigitalSurface digSurf( ptrSurfContainer ); // acquired trace.endBlock(); //! [greedy-plane-segmentation-ex3-makeSurface] //! [greedy-plane-segmentation-ex3-segment] Point p; Dimension axis; unsigned int j = 0; unsigned int nb = digSurf.size(); // First pass to find biggest planes. trace.beginBlock( "1) Segmentation first pass. Computes all planes so as to sort vertices by the plane size." ); std::map<Vertex,unsigned int> v2size; NaivePlaneComputer planeComputer; std::priority_queue<VertexSize> Q; std::vector<Point> layer; for ( ConstIterator it = digSurf.begin(), itE= digSurf.end(); it != itE; ++it ) { if ( ( (++j) % 50 == 0 ) || ( j == nb ) ) trace.progressBar( j, nb ); Vertex v = *it; axis = ks.sOrthDir( v ); planeComputer.init( axis, 500, widthNum, widthDen ); // The visitor takes care of all the breadth-first traversal. Visitor visitor( digSurf, v ); layer.clear(); Visitor::Size currentSize = visitor.current().second; while ( ! visitor.finished() ) { Visitor::Node node = visitor.current(); v = node.first; axis = ks.sOrthDir( v ); p = ks.sCoords( ks.sDirectIncident( v, axis ) ); if ( node.second != currentSize ) { // std::cerr << "Layer " << currentSize << ", size=" << layer.size() << std::endl; bool isExtended = planeComputer.extend( layer.begin(), layer.end() ); if ( ! isExtended ) break; layer.clear(); currentSize = node.second; } layer.push_back( p ); visitor.expand(); } // std::cerr << v << " -> " << planeComputer.size() << std::endl; Q.push( VertexSize( v, planeComputer.size() ) ); } trace.endBlock(); trace.beginBlock( "2) Segmentation second pass. Visits vertices from the one with biggest plane to the one with smallest plane." ); std::set<Vertex> processedVertices; std::map<Vertex,SegmentedPlane*> v2plane; std::vector<SegmentedPlane*> segmentedPlanes; j = 0; while ( ! Q.empty() ) { if ( ( (++j) % 50 == 0 ) || ( j == nb ) ) trace.progressBar( j, nb ); Vertex v = Q.top().v; Q.pop(); if ( processedVertices.find( v ) != processedVertices.end() ) // already in set continue; // process to next vertex SegmentedPlane* ptrSegment = new SegmentedPlane; segmentedPlanes.push_back( ptrSegment ); // to delete them afterwards. axis = ks.sOrthDir( v ); ptrSegment->plane.init( axis, 500, widthNum, widthDen ); // The visitor takes care of all the breadth-first traversal. Visitor visitor( digSurf, v ); while ( ! visitor.finished() ) { Visitor::Node node = visitor.current(); v = node.first; if ( processedVertices.find( v ) == processedVertices.end() ) { // Vertex is not in processedVertices axis = ks.sOrthDir( v ); p = ks.sCoords( ks.sDirectIncident( v, axis ) ); bool isExtended = ptrSegment->plane.extend( p ); if ( isExtended ) { // surfel is in plane. processedVertices.insert( v ); v2plane[ v ] = ptrSegment; visitor.expand(); } else // surfel is not in plane and should not be used in the visit. visitor.ignore(); } else // surfel is already in some plane. visitor.ignore(); } // Assign random color for each plane. ptrSegment->color = Color( rand() % 256, rand() % 256, rand() % 256, 255 ); } trace.endBlock(); //! [greedy-plane-segmentation-ex3-segment] //! [greedy-plane-segmentation-ex3-visualization] Viewer3D<> viewer( ks ); viewer.show(); Color col( 255, 255, 120 ); for ( std::map<Vertex,SegmentedPlane*>::const_iterator it = v2plane.begin(), itE = v2plane.end(); it != itE; ++it ) { viewer << CustomColors3D( it->second->color, it->second->color ); viewer << ks.unsigns( it->first ); } viewer << Viewer3D<>::updateDisplay; //! [greedy-plane-segmentation-ex3-visualization] //! [greedy-plane-segmentation-ex3-freeMemory] for ( std::vector<SegmentedPlane*>::iterator it = segmentedPlanes.begin(), itE = segmentedPlanes.end(); it != itE; ++it ) delete *it; segmentedPlanes.clear(); v2plane.clear(); //! [greedy-plane-segmentation-ex3-freeMemory] return application.exec(); }
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; }