DGtalTools  1.5.beta
criticalKernelsThinning3D.cpp
1 
76 #include <iostream>
77 #include <chrono>
78 #include <unordered_map>
79 #ifdef WITH_QGLVIEWER
80 #include <DGtal/io/viewers/Viewer3D.h>
81 #endif
82 #include <DGtal/base/Common.h>
83 #include <DGtal/helpers/StdDefs.h>
84 #include <DGtal/io/Color.h>
85 #include <DGtal/io/readers/GenericReader.h>
86 #include <DGtal/io/writers/GenericWriter.h>
87 #include "DGtal/images/imagesSetsUtils/SetFromImage.h"
88 #include "DGtal/images/SimpleThresholdForegroundPredicate.h"
89 #include "DGtal/images/ImageSelector.h"
90 #include "DGtal/images/imagesSetsUtils/ImageFromSet.h"
91 
92 #include <DGtal/topology/SurfelAdjacency.h>
93 #include <DGtal/topology/CubicalComplex.h>
94 #include <DGtal/topology/CubicalComplexFunctions.h>
95 #include <DGtal/io/boards/Board3D.h>
96 
97 #include <DGtal/topology/VoxelComplex.h>
98 #include <DGtal/topology/VoxelComplexFunctions.h>
99 #include "DGtal/topology/NeighborhoodConfigurations.h"
100 #include "DGtal/topology/tables/NeighborhoodTables.h"
101 
102 // Distance Map
103 #include "DGtal/geometry/volumes/distance/ExactPredicateLpSeparableMetric.h"
104 #include "DGtal/geometry/volumes/distance/VoronoiMap.h"
105 #include "DGtal/geometry/volumes/distance/DistanceTransformation.h"
106 
107 #include "CLI11.hpp"
108 
109 
110 using namespace DGtal;
111 using namespace std;
112 using namespace DGtal::Z3i;
113 
114 int main(int argc, char* const argv[]){
115 
116  // parse command line using CLI ----------------------------------------------
117  CLI::App app;
118  std::string inputFileName;
119  std::string sk_string {"1isthmus"};
120  string select_string {"dmax"};
121  string foreground {"black"};
122  string outputFilenameImg;
123  string outputFilenameSDP;
124  string outputFilenameOBJ;
125  string outputFilenameInputOBJ;
126 
127  int thresholdMin {0};
128  int thresholdMax {255};
129  int persistence {0};
130  bool profile {false};
131  bool verbose {false};
132  bool visualize {false};
133  bool useInputImgToExp {false};
134 
135  app.description("Compute the thinning of a volume using the CriticalKernels framework\nBasic usage: criticalKernelsThinning3D --input <volFileName> --skel <ulti,end, 1isthmus, isthmus> --select [ -f <white,black> -m <minlevel> -M <maxlevel> -v ] [--persistence <value> ] --persistence <value> ] \n options for --skel {ulti end 1isthmus isthmus} \n options for --select = {dmax random first} \n Example: \n criticalKernelsThinning3D --input ${DGtal}/examples/samples/Al.100.vol --select dmax --skel 1isthmus --persistence 1 --visualize --verbose --exportImage ./Al100_dmax_1isthmus_p1.vol \n");
136  app.add_option("-i,--input,1", inputFileName, "Input vol file." )
137  ->required()
138  ->check(CLI::ExistingFile);
139 
140  app.add_option("--skel,-s", sk_string,"Type of skeletonization. Options: 1isthmus, isthmus, end, ulti.", true )
141  -> check(CLI::IsMember({"ulti", "end","isthmus", "1isthmus"}));
142  app.add_option("--select,-c", select_string, "Select the ordering for skeletonization. Options: dmax, random, first", true)
143  -> check(CLI::IsMember({"random", "dmax", "first"}));
144  app.add_option("--foreground,-f",foreground, "Foreground color in binary image", true )
145  -> check(CLI::IsMember({"white", "black"}));
146  app.add_option("--thresholdMin,-m", thresholdMin, "Threshold min (excluded) to define binary shape", true );
147  app.add_option("--thresholdMax,-M", thresholdMax, "Threshold max (included) to define binary shape", true );
148  app.add_option("--persistence,-p",persistence,"Persistence value, implies use of persistence algorithm if p>=1", true )
149  ->check(CLI::PositiveNumber);
150  app.add_flag("--profile", profile, "Profile algorithm");
151  app.add_flag("--verbose,-v",verbose, "Verbose output");
152  app.add_option("--exportImage,-o",outputFilenameImg, "Export the resulting set of points to a image compatible with GenericWriter.");
153  app.add_option("--exportSDP,-e",outputFilenameSDP, "Export the resulting set of points in a simple (sequence of discrete point (sdp))." );
154  app.add_option("--exportOBJ,-O",outputFilenameOBJ, "Export the resulting set of points in an OBJ file." );
155  app.add_option("--exportInputOBJ,-I",outputFilenameInputOBJ, "Export the input set of points in an OBJ file." );
156 #ifdef WITH_QGLVIEWER
157  app.add_flag("--visualize,-t", visualize, "Visualize result in viewer");
158 #endif
159  app.add_flag("--useInputImgToExp,-k", useInputImgToExp, "Use input image type to export result (allowing to keep same domain (and same image spacing when using ITK)).");
160 
161  app.get_formatter()->column_width(40);
162  CLI11_PARSE(app, argc, argv);
163  // END parse command line using CLI ----------------------------------------------
164 
165 
166 
167  if(verbose){
168  std::cout << "Skel: " << sk_string << std::endl;
169  std::cout << "Select: " << select_string << std::endl;
170  std::cout << "Persistence: " << persistence << std::endl;
171  std::cout << "Input: " << inputFileName << std::endl;
172  }
173  trace.beginBlock("Reading input");
174  using Domain = Z3i::Domain ;
175 
176 #ifdef WITH_ITK
178 #else
180 #endif
181 
182 
183  Image image = GenericReader<Image>::import(inputFileName);
184  trace.endBlock();
185 
186  DigitalSet image_set (image.domain());
188  image_set, image,
189  thresholdMin, thresholdMax);
190 
191 
192  // Create a VoxelComplex from the set
193 
194  using DigitalTopology = DT26_6;
195  using DigitalSet =
197  std::unordered_set< typename Domain::Point> >;
198  using Complex = DGtal::VoxelComplex<KSpace>;
199 
200  auto & sk = sk_string;
201  KSpace ks;
203  ks.init(image.domain().lowerBound() - d1 ,
204  image.domain().upperBound() + d1 , true);
205 
206  trace.beginBlock("construct with table");
207  Complex vc(ks);
208  vc.construct(image_set, functions::loadTable(simplicity::tableSimple26_6 ));
209  trace.endBlock();
210  trace.beginBlock("load isthmus table");
211  boost::dynamic_bitset<> isthmus_table;
212  if (sk == "isthmus")
213  isthmus_table = *functions::loadTable(isthmusicity::tableIsthmus);
214  else if (sk == "1isthmus")
215  isthmus_table = *functions::loadTable(isthmusicity::tableOneIsthmus);
216  auto pointMap = *functions::mapZeroPointNeighborhoodToConfigurationMask<Point>();
217 
218  trace.endBlock();
219  using namespace DGtal::functions ;
220  // SKEL FUNCTION:
221  std::function< bool(const Complex&, const Cell&) > Skel ;
222  if (sk == "ulti") Skel = skelUltimate<Complex>;
223  else if (sk == "end") Skel = skelEnd<Complex>;
224  else if (sk == "isthmus" || sk == "1isthmus")
225  Skel = [&isthmus_table, &pointMap](const Complex & fc,
226  const Complex::Cell & c){
227  return skelWithTable(isthmus_table, pointMap, fc, c);
228  };
229  else throw std::runtime_error("Invalid skel string");
230  auto start = std::chrono::system_clock::now();
231 
232  // SELECT FUNCTION
233  /*
234  * Calculate distance map even if not requested:
235  */
236  trace.beginBlock("Create Distance Map");
239  L3Metric l3;
240  DT dt(image.domain(), image_set, l3);
241  trace.endBlock();
242 
243  std::function< std::pair<typename Complex::Cell, typename Complex::Data>(const Complex::Clique&) > Select ;
244  auto & sel = select_string;
245  if (sel == "random") Select = selectRandom<Complex>;
246  else if (sel == "first") Select = selectFirst<Complex>;
247  else if (sel == "dmax"){
248  Select =
249  [&dt](const Complex::Clique & clique){
250  return selectMaxValue<DT, Complex>(dt,clique);
251  };
252  } else throw std::runtime_error("Invalid skel string");
253 
254  trace.beginBlock("Thinning");
255  Complex vc_new(ks);
256  if (persistence == 0)
257  vc_new = asymetricThinningScheme< Complex >(
258  vc, Select, Skel, verbose);
259  else
260  vc_new = persistenceAsymetricThinningScheme< Complex >(
261  vc, Select, Skel, persistence, verbose);
262  trace.endBlock();
263 
264  auto end = std::chrono::system_clock::now();
265  auto elapsed = std::chrono::duration_cast<std::chrono::seconds> (end - start) ;
266  if (profile) std::cout <<"Time elapsed: " << elapsed.count() << std::endl;
267 
268 
269  DigitalSet thin_set(image.domain());
270  vc_new.dumpVoxels(thin_set);
271  const auto & all_set = image_set;
272 
273  if (outputFilenameSDP != "")
274  {
275  std::ofstream out;
276  out.open(outputFilenameSDP.c_str());
277  for (auto &p : thin_set)
278  {
279  out << p[0] << " " << p[1] << " " << p[2] << std::endl;
280  }
281  }
282 
283 
284  //-------------- export OBJ -------------------------------------------
285  if ( outputFilenameInputOBJ != "" )
286  {
287  Board3D< Space,KSpace > board( ks );
288  // Display lines that are not in the mesh.
289  SCell surfel;
290  board << SetMode3D( surfel.className(), "Basic");
291  board.setLineColor( Color::Black );
292  board.setFillColor( Color( 200, 200, 255, 255 ) );
293  for ( auto p : all_set )
294  {
295  SCell voxel = ks.sSpel( p );
296  for ( Dimension k = 0; k < 3; k++ ) {
297  Point q = p;
298  q[ k ] += 1;
299  if ( all_set.find( q ) == all_set.end() )
300  board << ks.sIncident( voxel, k, true );
301  q[ k ] -= 2;
302  if ( all_set.find( q ) == all_set.end() )
303  board << ks.sIncident( voxel, k, false );
304  }
305  }
306  board.saveOBJ( outputFilenameInputOBJ );
307  }
308 
309  //-------------- export OBJ -------------------------------------------
310  if ( outputFilenameOBJ != "" )
311  {
312  Board3D< Space,KSpace > board( ks );
313  // Display lines that are not in the mesh.
314  SCell surfel;
315  board << SetMode3D( surfel.className(), "Basic");
316  board.setLineColor( Color::Black );
317  board.setFillColor( Color::Red );
318  for ( auto p : thin_set )
319  {
320  SCell voxel = ks.sSpel( p );
321  for ( Dimension k = 0; k < 3; k++ ) {
322  Point q = p;
323  q[ k ] += 1;
324  if ( thin_set.find( q ) == thin_set.end() )
325  board << ks.sIncident( voxel, k, true );
326  q[ k ] -= 2;
327  if ( thin_set.find( q ) == thin_set.end() )
328  board << ks.sIncident( voxel, k, false );
329  }
330  }
331  board.saveOBJ( outputFilenameOBJ );
332  }
333 
334 if (outputFilenameImg != "")
335  {
336  if(verbose)
337  std::cout << "outputFilename" << outputFilenameImg << std::endl;
338 
339  unsigned int foreground_value = 255;
340  if (useInputImgToExp){
341  for(auto p: image.domain()){image.setValue(p, 0);}
342  ImageFromSet<Image>::append(image, thin_set, foreground_value);
343  image >> outputFilenameImg;
344  }else{
345  auto thin_image = ImageFromSet<Image>::create(thin_set, foreground_value, false, useInputImgToExp);
346  thin_image >> outputFilenameImg;
347  }
348  }
349 #ifdef WITH_QGLVIEWER
350 if(visualize)
351  {
352  int argc(1);
353  char** argv(nullptr);
354  QApplication app(argc, argv);
355  Viewer3D<> viewer( ks );
356  viewer.setWindowTitle("criticalKernelsThinning3D");
357  viewer.show();
358 
359  viewer.setFillColor(Color(255, 255, 255, 255));
360  viewer << thin_set;
361 
362  // All kspace voxels
363  viewer.setFillColor(Color(40, 200, 55, 10));
364  viewer << all_set;
365 
366  viewer << Viewer3D<>::updateDisplay;
367 
368  app.exec();
369  }
370 #endif
371 
372 }
int main(int argc, char **argv)
static const Color Red
static const Color Black
const Domain & domain() const
void setValue(const Point &aPoint, const Value &aValue)
typename Self::Domain Domain
typename Self::Point Point
bool init(const Point &lower, const Point &upper, bool isClosed)
SCell sSpel(Point p, Sign sign=POS) const
SCell sIncident(const SCell &c, Dimension k, bool up) const
static Self diagonal(Component val=1)
void beginBlock(const std::string &keyword="")
double endBlock()
HyperRectDomain< Space > Domain
DGtal::CountedPtr< boost::dynamic_bitset<> > loadTable(const std::string &input_filename, unsigned int known_size)
const std::string tableSimple26_6
Trace trace(traceWriterTerm)
DGtal::uint32_t Dimension
static TContainer import(const std::string &filename, std::vector< unsigned int > dimSpace=std::vector< unsigned int >())
static Image create(const Set &aSet, const Value &defaultValue, const bool addBorder, typename Set::ConstIterator itBegin, typename Set::ConstIterator itEnd)
static void append(Image &aImage, const Value &defaultValue, typename Set::ConstIterator itBegin, typename Set::ConstIterator itEnd)
std::string className() const