DGtalTools  0.9.3
volBoundary2obj.cpp
1 
30 #include <iostream>
32 #include <set>
33 #include <boost/program_options/options_description.hpp>
34 #include <boost/program_options/parsers.hpp>
35 #include <boost/program_options/variables_map.hpp>
36 
37 #include "DGtal/base/Common.h"
38 #include "DGtal/base/BasicFunctors.h"
39 #include "DGtal/kernel/SpaceND.h"
40 #include "DGtal/kernel/domains/HyperRectDomain.h"
41 #include "DGtal/images/ImageSelector.h"
42 #include "DGtal/images/IntervalForegroundPredicate.h"
43 #include "DGtal/topology/KhalimskySpaceND.h"
44 #include "DGtal/topology/DigitalSurface.h"
45 #include "DGtal/topology/SetOfSurfels.h"
46 #include "DGtal/io/boards/Board3D.h"
47 #include "DGtal/io/readers/PointListReader.h"
48 #include "DGtal/io/readers/GenericReader.h"
49 #ifdef WITH_ITK
50 #include "DGtal/io/readers/DicomReader.h"
51 #endif
52 #include "DGtal/io/Color.h"
53 #include "DGtal/io/colormaps/GradientColorMap.h"
54 
55 
56 using namespace std;
57 using namespace DGtal;
58 //using namespace Z3i;
59 
61 namespace po = boost::program_options;
62 
106 int main( int argc, char** argv )
107 {
108  typedef SpaceND<3,int> Space;
109  typedef KhalimskySpaceND<3,int> KSpace;
110  typedef HyperRectDomain<Space> Domain;
111  typedef ImageSelector<Domain, unsigned char>::Type Image;
112  typedef SurfelAdjacency<KSpace::dimension> MySurfelAdjacency;
113 
114  // parse command line ----------------------------------------------
115  po::options_description general_opt("Allowed options are: ");
116  general_opt.add_options()
117  ("help,h", "display this message")
118  ("input,i", po::value<std::string>(), "vol file (.vol) , pgm3d (.p3d or .pgm3d, pgm (with 3 dims)) file or sdp (sequence of discrete points)" )
119  ("output,o", po::value<std::string>(), "output obj file (.obj)" )
120  ("thresholdMin,m", po::value<int>()->default_value(0), "threshold min (excluded) to define binary shape" )
121  ("thresholdMax,M", po::value<int>()->default_value(255), "threshold max (included) to define binary shape" )
122 #ifdef WITH_ITK
123  ("dicomMin", po::value<int>()->default_value(-1000), "set minimum density threshold on Hounsfield scale")
124  ("dicomMax", po::value<int>()->default_value(3000), "set maximum density threshold on Hounsfield scale")
125 #endif
126  ("mode", po::value<std::string>()->default_value("BDRY"), "set mode for display: INNER: inner voxels, OUTER: outer voxels, BDRY: surfels (default), CLOSURE: surfels with linels and pointels.")
127  ("normalization,n", "Normalization so that the geometry fits in [-1/2,1/2]^3") ;
128 
129  bool parseOK=true;
130  po::variables_map vm;
131  try{
132  po::store(po::parse_command_line(argc, argv, general_opt), vm);
133  }catch(const std::exception& ex){
134  parseOK=false;
135  trace.info()<< "Error checking program options: "<< ex.what()<< endl;
136  }
137  po::notify(vm);
138  if( !parseOK || vm.count("help")||argc<=1)
139  {
140  std::cout << "Usage: " << argv[0] << " -i [input] -o [output]\n"
141  << "Export the boundary of a volume file to OBJ format. The mode specifies if you wish to see surface elements (BDRY), the inner voxels (INNER) or the outer voxels (OUTER) that touch the boundary."<< endl
142  << general_opt << "\n";
143  return 0;
144  }
145 
146  if(! vm.count("input"))
147  {
148  trace.error() << " The file name was defined" << endl;
149  return 0;
150  }
151 
152  if(! vm.count("output"))
153  {
154  trace.error() << " The output filename was defined" << endl;
155  return 0;
156  }
157 
158  string inputFilename = vm["input"].as<std::string>();
159  int thresholdMin = vm["thresholdMin"].as<int>();
160  int thresholdMax = vm["thresholdMax"].as<int>();
161  string mode = vm["mode"].as<string>();
162  bool normalization = false;
163  if (vm.count("normalization"))
164  normalization = true;
165 
166  string extension = inputFilename.substr(inputFilename.find_last_of(".") + 1);
167  if(extension!="vol" && extension != "p3d" && extension != "pgm3D" && extension != "pgm3d" && extension != "sdp" && extension != "pgm"
168 #ifdef WITH_ITK
169  && extension !="dcm"
170 #endif
171  ){
172  trace.info() << "File extension not recognized: "<< extension << std::endl;
173  return 0;
174  }
175 
176  if(extension=="vol" || extension=="pgm3d" || extension=="pgm3D"
177 #ifdef WITH_ITK
178  || extension =="dcm"
179 #endif
180  ){
181  trace.beginBlock( "Loading image into memory." );
182 #ifdef WITH_ITK
183  int dicomMin = vm["dicomMin"].as<int>();
184  int dicomMax = vm["dicomMax"].as<int>();
185  typedef DGtal::functors::Rescaling<int ,unsigned char > RescalFCT;
186  Image image = extension == "dcm" ? DicomReader< Image, RescalFCT >::importDicom( inputFilename,
187  RescalFCT(dicomMin,
188  dicomMax,
189  0, 255) ) :
190  GenericReader<Image>::import( inputFilename );
191 #else
192  Image image = GenericReader<Image>::import (inputFilename );
193 #endif
194  trace.info() << "Image loaded: "<<image<< std::endl;
195  trace.endBlock();
196 
197  trace.beginBlock( "Construct the Khalimsky space from the image domain." );
198  Domain domain = image.domain();
199  KSpace ks;
200  bool space_ok = ks.init( domain.lowerBound(), domain.upperBound(), true );
201  if (!space_ok)
202  {
203  trace.error() << "Error in the Khamisky space construction."<<std::endl;
204  return 2;
205  }
206  trace.endBlock();
207 
208  trace.beginBlock( "Wrapping a digital set around image. " );
209  typedef functors::IntervalForegroundPredicate<Image> ThresholdedImage;
210  ThresholdedImage thresholdedImage( image, thresholdMin, thresholdMax );
211  trace.endBlock();
212 
213  trace.beginBlock( "Extracting boundary by scanning the space. " );
214  typedef KSpace::SurfelSet SurfelSet;
215  typedef SetOfSurfels< KSpace, SurfelSet > MySetOfSurfels;
216  typedef DigitalSurface< MySetOfSurfels > MyDigitalSurface;
217  MySurfelAdjacency surfAdj( true ); // interior in all directions.
218  MySetOfSurfels theSetOfSurfels( ks, surfAdj );
219  Surfaces<KSpace>::sMakeBoundary( theSetOfSurfels.surfelSet(),
220  ks, thresholdedImage,
221  domain.lowerBound(),
222  domain.upperBound() );
223  MyDigitalSurface digSurf( theSetOfSurfels );
224  trace.info() << "Digital surface has " << digSurf.size() << " surfels."
225  << std::endl;
226  trace.endBlock();
227 
228  trace.beginBlock( "Exporting everything." );
229  Board3D<Space,KSpace> board(ks);
230 
231  board << SetMode3D( ks.unsigns( *digSurf.begin() ).className(), "Basic" );
232 
233  typedef MyDigitalSurface::ConstIterator ConstIterator;
234  if ( mode == "BDRY" )
235  for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it )
236  board << ks.unsigns( *it );
237  else if ( mode == "INNER" )
238  for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it )
239  board << ks.sCoords( ks.sDirectIncident( *it, ks.sOrthDir( *it ) ) );
240  else if ( mode == "OUTER" )
241  for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it )
242  board << ks.sCoords( ks.sIndirectIncident( *it, ks.sOrthDir( *it ) ) );
243  else if (mode == "CLOSURE")
244  {
245  std::set<KSpace::Cell> container;
246  for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it )
247  {
248  container.insert( ks.unsigns( *it ) );
249  KSpace::SCells oneNeig = ks.sLowerIncident(*it);
250  //Processing linels
251  for(KSpace::SCells::ConstIterator itt = oneNeig.begin(), ittend = oneNeig.end(); itt != ittend; ++itt)
252  {
253  container.insert( ks.unsigns( *itt) );
254  KSpace::SCells oneNeig2 = ks.sLowerIncident(*itt);
255  //Processing pointels
256  for(KSpace::SCells::ConstIterator ittt = oneNeig2.begin(), itttend = oneNeig2.end(); ittt != itttend; ++ittt)
257  container.insert( ks.unsigns(*ittt) );
258  }
259  }
260  trace.info()<< "Exporting "<< container.size() << " cells"<<std::endl;
261  for(auto cell: container)
262  board << cell;
263  }
264 
265  string outputFilename = vm["output"].as<std::string>();
266 
267  board.saveOBJ(outputFilename, normalization);
268  trace.endBlock();
269  }
270  return 0;
271 }
STL namespace.
Definition: ATu0v1.h:56