DGtalTools  0.9.4
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 
111 int main( int argc, char** argv )
112 {
113  typedef SpaceND<3,int> Space;
117  typedef SurfelAdjacency<KSpace::dimension> MySurfelAdjacency;
118 
119  // parse command line ----------------------------------------------
120  po::options_description general_opt("Allowed options are: ");
121  general_opt.add_options()
122  ("help,h", "display this message")
123  ("input,i", po::value<std::string>(), "vol file (.vol, .longvol .p3d, .pgm3d and if WITH_ITK is selected: dicom, dcm, mha, mhd). For longvol, dicom, dcm, mha or mhd formats, the input values are linearly scaled between 0 and 255." )
124  ("output,o", po::value<std::string>(), "output obj file (.obj)" )
125  ("thresholdMin,m", po::value<int>()->default_value(0), "threshold min (excluded) to define binary shape" )
126  ("thresholdMax,M", po::value<int>()->default_value(255), "threshold max (included) to define binary shape" )
127  ("rescaleInputMin", po::value<DGtal::int64_t>()->default_value(0), "min value used to rescale the input intensity (to avoid basic cast into 8 bits image).")
128  ("rescaleInputMax", po::value<DGtal::int64_t>()->default_value(255), "max value used to rescale the input intensity (to avoid basic cast into 8 bits image).")
129  ("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.")
130  ("normalization,n", "Normalization so that the geometry fits in [-1/2,1/2]^3") ;
131 
132  bool parseOK=true;
133  po::variables_map vm;
134  try{
135  po::store(po::parse_command_line(argc, argv, general_opt), vm);
136  }catch(const std::exception& ex){
137  parseOK=false;
138  trace.info()<< "Error checking program options: "<< ex.what()<< endl;
139  }
140  po::notify(vm);
141  if( !parseOK || vm.count("help")||argc<=1)
142  {
143  std::cout << "Usage: " << argv[0] << " -i [input] -o [output]\n"
144  << "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
145  << general_opt << "\n";
146  return 0;
147  }
148 
149  if(! vm.count("input"))
150  {
151  trace.error() << " The file name was defined" << endl;
152  return 0;
153  }
154 
155  if(! vm.count("output"))
156  {
157  trace.error() << " The output filename was defined" << endl;
158  return 0;
159  }
160 
161  string inputFilename = vm["input"].as<std::string>();
162  int thresholdMin = vm["thresholdMin"].as<int>();
163  int thresholdMax = vm["thresholdMax"].as<int>();
164  string mode = vm["mode"].as<string>();
165  bool normalization = false;
166  if (vm.count("normalization"))
167  normalization = true;
168 
169  DGtal::int64_t rescaleInputMin = vm["rescaleInputMin"].as<DGtal::int64_t>();
170  DGtal::int64_t rescaleInputMax = vm["rescaleInputMax"].as<DGtal::int64_t>();
171 
172  trace.beginBlock( "Loading file.." );
174  Image image = GenericReader< Image >::importWithValueFunctor( inputFilename,RescalFCT(rescaleInputMin,
175  rescaleInputMax,
176  0, 255) );
177  trace.info() << "Image loaded: "<<image<< std::endl;
178  trace.endBlock();
179 
180  trace.beginBlock( "Construct the Khalimsky space from the image domain." );
181  Domain domain = image.domain();
182  KSpace ks;
183  bool space_ok = ks.init( domain.lowerBound(), domain.upperBound(), true );
184  if (!space_ok)
185  {
186  trace.error() << "Error in the Khamisky space construction."<<std::endl;
187  return 2;
188  }
189  trace.endBlock();
190 
191  trace.beginBlock( "Wrapping a digital set around image. " );
192  typedef functors::IntervalForegroundPredicate<Image> ThresholdedImage;
193  ThresholdedImage thresholdedImage( image, thresholdMin, thresholdMax );
194  trace.endBlock();
195 
196  trace.beginBlock( "Extracting boundary by scanning the space. " );
197  typedef KSpace::SurfelSet SurfelSet;
198  typedef SetOfSurfels< KSpace, SurfelSet > MySetOfSurfels;
199  typedef DigitalSurface< MySetOfSurfels > MyDigitalSurface;
200  MySurfelAdjacency surfAdj( true ); // interior in all directions.
201  MySetOfSurfels theSetOfSurfels( ks, surfAdj );
202  Surfaces<KSpace>::sMakeBoundary( theSetOfSurfels.surfelSet(),
203  ks, thresholdedImage,
204  domain.lowerBound(),
205  domain.upperBound() );
206  MyDigitalSurface digSurf( theSetOfSurfels );
207  trace.info() << "Digital surface has " << digSurf.size() << " surfels."
208  << std::endl;
209  trace.endBlock();
210 
211  trace.beginBlock( "Exporting everything." );
212  Board3D<Space,KSpace> board(ks);
213 
214  board << SetMode3D( ks.unsigns( *digSurf.begin() ).className(), "Basic" );
215  board << SetMode3D( (*digSurf.begin()).className(), "Basic" );
216 
217  typedef MyDigitalSurface::ConstIterator ConstIterator;
218  if ( mode == "BDRY" )
219  for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it )
220  {
221  //board << ks.unsigns( *it );
222  bool indirectFilled = thresholdedImage(ks.sCoords( ks.sIndirectIncident( *it, ks.sOrthDir( *it ) ) ));
223  bool directFilled = thresholdedImage(ks.sCoords( ks.sDirectIncident( *it, ks.sOrthDir( *it ) ) ));
224  Z3i::RealPoint sc = board.embedKS(*it);
225  Z3i::RealPoint n (0,0,0);
226  auto ip = ks.sCoords( ks.sIndirectIncident( *it, ks.sOrthDir( *it ) ) );
227  if (!indirectFilled)
228  {
229  ip = ks.sCoords( ks.sDirectIncident( *it, ks.sOrthDir( *it ) ) );
230  }
231  n = sc - Z3i::RealPoint(static_cast<Z3i::RealPoint::Component>(ip[0]),
232  static_cast<Z3i::RealPoint::Component>(ip[1]),
233  static_cast<Z3i::RealPoint::Component>(ip[2]));
234  bool xodd = ks.sIsOpen( *it, 0 );
235  bool yodd = ks.sIsOpen( *it, 1 );
236  bool zodd = ks.sIsOpen( *it, 2 );
237  board.addQuadFromSurfelCenterWithNormal
238  ( Z3i::RealPoint( sc[0]+(xodd? 0:0.5 ), sc[1]+(yodd? 0:0.5 ), sc[2]+(zodd? 0:0.5 ) ),
239  ! xodd, ! yodd, ! zodd, n, true, true, false );
240  }
241  else if ( mode == "INNER" )
242  for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it )
243  board << ks.sCoords( ks.sDirectIncident( *it, ks.sOrthDir( *it ) ) );
244  else if ( mode == "OUTER" )
245  for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it )
246  board << ks.sCoords( ks.sIndirectIncident( *it, ks.sOrthDir( *it ) ) );
247  else if (mode == "CLOSURE")
248  {
249  std::set<KSpace::Cell> container;
250  for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it )
251  {
252  container.insert( ks.unsigns( *it ) );
253  KSpace::SCells oneNeig = ks.sLowerIncident(*it);
254  //Processing linels
255  for(KSpace::SCells::ConstIterator itt = oneNeig.begin(), ittend = oneNeig.end(); itt != ittend; ++itt)
256  {
257  container.insert( ks.unsigns( *itt) );
258  KSpace::SCells oneNeig2 = ks.sLowerIncident(*itt);
259  //Processing pointels
260  for(KSpace::SCells::ConstIterator ittt = oneNeig2.begin(), itttend = oneNeig2.end(); ittt != itttend; ++ittt)
261  container.insert( ks.unsigns(*ittt) );
262  }
263  }
264  trace.info()<< "Exporting "<< container.size() << " cells"<<std::endl;
265  for(auto cell: container)
266  board << cell;
267  }
268 
269  string outputFilename = vm["output"].as<std::string>();
270 
271  board.saveOBJ(outputFilename, normalization);
272  trace.endBlock();
273 
274  return 0;
275 }
void beginBlock(const std::string &keyword="")
SpaceND< 2, Integer > Space
std::set< SCell > SurfelSet
STL namespace.
double endBlock()
SCells sLowerIncident(const SCell &c) const
SCell sDirectIncident(const SCell &p, Dimension k) const
const Point & lowerBound() const
Dimension sOrthDir(const SCell &s) const
bool init(const Point &lower, const Point &upper, bool isClosed)
Point sCoords(const SCell &c) const
Trace trace(traceWriterTerm)
std::ostream & info()
SCell sIndirectIncident(const SCell &p, Dimension k) const
const Domain & domain() const
Cell unsigns(const SCell &p) const
std::ostream & error()
bool sIsOpen(const SCell &p, Dimension k) const
boost::int64_t int64_t
AnyCellCollection< SCell > SCells
typename Self::Domain Domain