DGtalTools  0.9.2
3dVolMarchingCubes.cpp
1 
29 #include <iostream>
32 #include <queue>
33 #include <boost/program_options/options_description.hpp>
34 #include <boost/program_options/parsers.hpp>
35 #include <boost/program_options/variables_map.hpp>
36 #include <DGtal/kernel/sets/SetPredicate.h>
37 #include <DGtal/io/readers/VolReader.h>
38 #include <DGtal/images/ImageSelector.h>
39 #include <DGtal/images/SimpleThresholdForegroundPredicate.h>
40 #include <DGtal/images/ImageLinearCellEmbedder.h>
41 #include <DGtal/shapes/Shapes.h>
42 #include <DGtal/kernel/CanonicEmbedder.h>
43 #include <DGtal/helpers/StdDefs.h>
44 #include <DGtal/topology/helpers/Surfaces.h>
45 #include <DGtal/topology/DigitalSurface.h>
46 #include <DGtal/topology/SetOfSurfels.h>
48 
50 
51 using namespace DGtal;
52 using namespace Z3i;
53 namespace po = boost::program_options;
54 
56 
110 int main( int argc, char** argv )
111 {
113  // parse command line ----------------------------------------------
114  po::options_description general_opt("Allowed options are ");
115  general_opt.add_options()
116  ("help,h", "display this message")
117  ("input,i", po::value<std::string>(), "the volume file (.vol)" )
118  ("threshold,t", po::value<unsigned int>()->default_value(1), "the value that defines the isosurface in the image (an integer between 0 and 255)." )
119  ("adjacency,a", po::value<unsigned int>()->default_value(0), "0: interior adjacency, 1: exterior adjacency")
120  ("output,o", po::value<std::string>()->default_value( "marching-cubes.off" ), "the output OFF file that represents the geometry of the isosurface") ;
121  bool parseOK=true;
122  po::variables_map vm;
123  try{
124  po::store(po::parse_command_line(argc, argv, general_opt), vm);
125  }catch(const std::exception& ex){
126  parseOK=false;
127  trace.info()<< "Error checking program options: "<< ex.what()<< std::endl;
128  }
129  po::notify(vm);
130  if ( !parseOK || vm.count("help") || ( argc <= 1 ) )
131  {
132  std::cout << "Usage: " << argv[0]
133  << " [-i <fileName.vol>] [-t <threshold>] [-a <adjacency>] [-o <output.off>]" << std::endl
134  << "Outputs the isosurface of value <threshold> of the volume <fileName.vol> as an OFF file <output.off>. The <adjacency> (0/1) allows to choose between interior (6,18) and exterior (18,6) adjacency." << std::endl
135  << general_opt << std::endl;
136  return 0;
137  }
138  if ( ! vm.count("input") )
139  {
140  trace.error() << "The input file name was defined." << std::endl;
141  return 1;
142  }
143  std::string inputFilename = vm["input"].as<std::string>();
144  unsigned int threshold = vm["threshold"].as<unsigned int>();
145  bool intAdjacency = ( vm["adjacency"].as<unsigned int>() == 0 );
146  std::string outputFilename = vm["output"].as<std::string>();
148 
150  trace.beginBlock( "Reading vol file into an image." );
151  typedef ImageSelector < Domain, int>::Type Image;
152  Image image = VolReader<Image>::importVol(inputFilename);
153 
154  typedef functors::SimpleThresholdForegroundPredicate<Image> ThresholdedImage;
155  ThresholdedImage thresholdedImage( image, threshold );
156  // DigitalSet set3d (image.domain());
157  // SetFromImage<DigitalSet>::append<Image>(set3d, image,
158  // threshold, 255 );
159  trace.endBlock();
161 
163  trace.beginBlock( "Construct the Khalimsky space from the image domain." );
164  KSpace ks;
165  bool space_ok = ks.init( image.domain().lowerBound(),
166  image.domain().upperBound(), true );
167  if (!space_ok)
168  {
169  trace.error() << "Error in the Khamisky space construction."<<std::endl;
170  return 2;
171  }
172  trace.endBlock();
174 
176  typedef SurfelAdjacency<KSpace::dimension> MySurfelAdjacency;
177  MySurfelAdjacency surfAdj( intAdjacency ); // interior in all directions.
179 
181  trace.beginBlock( "Extracting boundary by scanning the space. " );
182  typedef KSpace::SurfelSet SurfelSet;
183  typedef SetOfSurfels< KSpace, SurfelSet > MySetOfSurfels;
184  typedef DigitalSurface< MySetOfSurfels > MyDigitalSurface;
185  MySetOfSurfels theSetOfSurfels( ks, surfAdj );
186  Surfaces<KSpace>::sMakeBoundary( theSetOfSurfels.surfelSet(),
187  ks, thresholdedImage,
188  image.domain().lowerBound(),
189  image.domain().upperBound() );
190  MyDigitalSurface digSurf( theSetOfSurfels );
191  trace.info() << "Digital surface has " << digSurf.size() << " surfels."
192  << std::endl;
193  trace.endBlock();
195 
197  trace.beginBlock( "Making OFF surface. " );
198  // Describes how voxels are embedded into Euclidean space.
199  typedef CanonicEmbedder< Space > MyEmbedder;
200  // Describes how the centroid surface elements is placed in-between embedded voxels.
201  typedef ImageLinearCellEmbedder< KSpace, Image, MyEmbedder > CellEmbedder;
202  CellEmbedder cellEmbedder;
203  MyEmbedder trivialEmbedder;
204 
205  // The +0.5 is to avoid isosurface going exactly through a voxel
206  // center, especially for binary volumes.
207  cellEmbedder.init( ks, image, trivialEmbedder,
208  ( (double) threshold ) + 0.5 );
209  std::ofstream out( outputFilename.c_str() );
210  if ( out.good() )
211  digSurf.exportEmbeddedSurfaceAs3DOFF( out, cellEmbedder );
212  out.close();
213  trace.endBlock();
215  return 0;
216 }
217