DGtalTools  0.9.4
criticalKernelsThinning3D.cpp
1 
74 #include <iostream>
75 #include <chrono>
76 #include <unordered_map>
77 
78 #include <DGtal/io/viewers/Viewer3D.h>
79 #include <DGtal/base/Common.h>
80 #include <DGtal/helpers/StdDefs.h>
81 #include <DGtal/io/readers/GenericReader.h>
82 #include <DGtal/io/writers/GenericWriter.h>
83 #include "DGtal/images/imagesSetsUtils/SetFromImage.h"
84 #include "DGtal/images/SimpleThresholdForegroundPredicate.h"
85 #include "DGtal/images/ImageSelector.h"
86 #include "DGtal/images/imagesSetsUtils/ImageFromSet.h"
87 
88 #include <DGtal/topology/SurfelAdjacency.h>
89 #include <DGtal/io/boards/Board2D.h>
90 #include <DGtal/topology/CubicalComplex.h>
91 #include <DGtal/topology/CubicalComplexFunctions.h>
92 
93 #include <DGtal/topology/VoxelComplex.h>
94 #include <DGtal/topology/VoxelComplexFunctions.h>
95 #include "DGtal/topology/NeighborhoodConfigurations.h"
96 #include "DGtal/topology/tables/NeighborhoodTables.h"
97 
98 // boost::program_options
99 #include <boost/program_options/options_description.hpp>
100 #include <boost/program_options/parsers.hpp>
101 #include <boost/program_options/variables_map.hpp>
102 // Distance Map
103 #include "DGtal/kernel/BasicPointPredicates.h"
104 #include "DGtal/images/SimpleThresholdForegroundPredicate.h"
105 #include "DGtal/geometry/volumes/distance/ExactPredicateLpSeparableMetric.h"
106 #include "DGtal/geometry/volumes/distance/VoronoiMap.h"
107 #include "DGtal/geometry/volumes/distance/DistanceTransformation.h"
108 using namespace DGtal;
109 using namespace std;
110 using namespace DGtal::Z3i;
111 namespace po = boost::program_options;
112 
113 int main(int argc, char* const argv[]){
114 
115  /*-------------- Parse command line -----------------------------*/
116  po::options_description general_opt ( "Allowed options are: " );
117  general_opt.add_options()
118  ( "help,h", "display this message." )
119  ( "input,i", po::value<string>()->required(), "Input vol file." )
120  ( "skel,s", po::value<string>()->required(), "type of skeletonization" )
121  ( "select,c", po::value<string>()->required(), "select method for skeletonization" )
122  ( "foreground,f", po::value<string>()->default_value("black"), "foreground color in binary image" )
123  ( "thresholdMin,m", po::value<int>()->default_value(0), "threshold min (excluded) to define binary shape" )
124  ( "thresholdMax,M", po::value<int>()->default_value(255), "threshold max (included) to define binary shape" )
125  ( "persistence,p", po::value<int>()->default_value(0), "persistence value, implies use of persistence algorithm if p>=1" )
126  ( "profile", po::bool_switch()->default_value(false), "profile algorithm" )
127  ( "verbose,v", po::bool_switch()->default_value(false), "verbose output" )
128  ( "exportImage,o", po::value<std::string>(), "Export the resulting set of points to a image compatible with GenericWriter.")
129  ( "exportSDP,e", po::value<std::string>(), "Export the resulting set of points in a simple (sequence of discrete point (sdp)).")
130  ( "visualize,t", po::bool_switch()->default_value(false), "visualize result in viewer" );
131  bool parseOK=true;
132  po::variables_map vm;
133 
134  try {
135  po::store(po::parse_command_line(argc, argv, general_opt), vm);
136  po::notify ( vm );
137  } catch(const exception& ex) {
138  parseOK=false;
139  trace.info()<< "Error checking program options: "<< ex.what()<< std::endl;
140  }
141 
142  if (!parseOK || vm.count ( "help" ) || !vm.count("input"))
143  {
144  trace.info() <<
145  "Compute the thinning of a volume using the CriticalKernels framework"<< std::endl
146  << std::endl << "Basic usage: "<< std::endl
147  << "criticalKernelsThinning3D --input <volFileName> --skel <ulti,end, 1isthmus, isthmus> --select "
148  " [ -f <white,black> -m <minlevel> -M <maxlevel> -v ] "
149  " [--persistence <value> ]" << std::endl
150  << "options for --skel {ulti end 1isthmus isthmus}" << std::endl
151  << "options for --select = {dmax random first}" << std::endl
152  << general_opt << "\n"
153  << " Example: \n"
154  << "criticalKernelsThinning3D --input ${DGtal}/examples/samples/Al.100.vol --select dmax --skel 1isthmus --persistence 1 --visualize --verbose --outputImage ./Al100_dmax_1isthmus_p1.vol \n";
155  return 0;
156  }
157  //Parse options
158  string filename = vm["input"].as<string>();
159  bool verbose = vm["verbose"].as<bool>();
160  bool visualize = vm["visualize"].as<bool>();
161  bool profile = vm["profile"].as<bool>();
162  int thresholdMin = vm["thresholdMin"].as<int>();
163  int thresholdMax = vm["thresholdMax"].as<int>();
164  int persistence = vm["persistence"].as<int>();
165  if (vm.count("persistence") && persistence < 0 )
166  throw po::validation_error(po::validation_error::invalid_option_value, "persistence");
167  string foreground = vm["foreground"].as<string>();
168  if (vm.count("foreground") && (!(foreground == "white" || foreground == "black")))
169  throw po::validation_error(po::validation_error::invalid_option_value, "foreground");
170 
171  string sk_string = vm["skel"].as<string>();
172  if (vm.count("skel") &&
173  (!( sk_string == "ulti" || sk_string == "end" ||
174  sk_string == "isthmus" || sk_string == "1isthmus"))
175  )
176  throw po::validation_error(po::validation_error::invalid_option_value, "skel");
177  string select_string = vm["select"].as<string>();
178  if (vm.count("select") &&
179  (!( select_string == "random" || select_string == "dmax" ||
180  select_string == "first" ))
181  )
182  throw po::validation_error(po::validation_error::invalid_option_value, "select");
183  /*-------------- End of parse -----------------------------*/
184 
185  if(verbose){
186  std::cout << "Skel: " << sk_string << std::endl;
187  std::cout << "Select: " << select_string << std::endl;
188  std::cout << "Persistence: " << persistence << std::endl;
189  std::cout << "Input: " << filename << std::endl;
190  }
191  trace.beginBlock("Reading input");
192  using Domain = Z3i::Domain ;
194  Image image = GenericReader<Image>::import(filename);
195  trace.endBlock();
196 
197  DigitalSet image_set (image.domain());
199  image_set, image,
200  thresholdMin, thresholdMax);
201 
202 
203  // Create a VoxelComplex from the set
204 
205  using DigitalTopology = DT26_6;
206  using DigitalSet =
208  std::unordered_set< typename Domain::Point> >;
209  using Object =
211  using Complex =
212  DGtal::VoxelComplex<KSpace, Object>;
213 
214  auto & sk = sk_string;
215  KSpace ks;
217  ks.init(image.domain().lowerBound() - d1 ,
218  image.domain().upperBound() + d1 , true);
219 
222  DigitalTopology topo(adjF, adjB, DGtal::DigitalTopologyProperties::JORDAN_DT);
223  Object obj(topo,image_set);
224 
225  trace.beginBlock("construct with table");
226  Complex vc(ks);
227  vc.construct(obj.pointSet(), functions::loadTable(simplicity::tableSimple26_6 ));
228  trace.endBlock();
229  trace.beginBlock("load isthmus table");
230  boost::dynamic_bitset<> isthmus_table;
231  if (sk == "isthmus")
232  isthmus_table = *functions::loadTable(isthmusicity::tableIsthmus);
233  else if (sk == "1isthmus")
234  isthmus_table = *functions::loadTable(isthmusicity::tableOneIsthmus);
235  auto pointMap = *functions::mapZeroPointNeighborhoodToConfigurationMask<Point>();
236 
237  trace.endBlock();
238  using namespace DGtal::functions ;
239  // SKEL FUNCTION:
240  std::function< bool(const Complex&, const Cell&) > Skel ;
241  if (sk == "ulti") Skel = skelUltimate<Complex>;
242  else if (sk == "end") Skel = skelEnd<Complex>;
243  else if (sk == "isthmus" || sk == "1isthmus")
244  Skel = [&isthmus_table, &pointMap](const Complex & fc,
245  const Complex::Cell & c){
246  return skelWithTable(isthmus_table, pointMap, fc, c);
247  };
248  else throw std::runtime_error("Invalid skel string");
249  auto start = std::chrono::system_clock::now();
250 
251  // SELECT FUNCTION
252  /*
253  * Calculate distance map even if not requested:
254  */
255  trace.beginBlock("Create Distance Map");
256  using Predicate = Z3i::DigitalSet;
259  L3Metric l3;
260  DT dt(obj.domain(),obj.pointSet(), l3);
261  trace.endBlock();
262 
263  std::function< std::pair<typename Complex::Cell, typename Complex::Data>(const Complex::Clique&) > Select ;
264  auto & sel = select_string;
265  if (sel == "random") Select = selectRandom<Complex>;
266  else if (sel == "first") Select = selectFirst<Complex>;
267  else if (sel == "dmax"){
268  Select =
269  [&dt](const Complex::Clique & clique){
270  return selectMaxValue<DT, Complex>(dt,clique);
271  };
272  } else throw std::runtime_error("Invalid skel string");
273 
274  trace.beginBlock("Thinning");
275  Complex vc_new(ks);
276  if (persistence == 0)
277  vc_new = asymetricThinningScheme< Complex >(
278  vc, Select, Skel, verbose);
279  else
280  vc_new = persistenceAsymetricThinningScheme< Complex >(
281  vc, Select, Skel, persistence, verbose);
282  trace.endBlock();
283 
284  auto end = std::chrono::system_clock::now();
285  auto elapsed = std::chrono::duration_cast<std::chrono::seconds> (end - start) ;
286  if (profile) std::cout <<"Time elapsed: " << elapsed.count() << std::endl;
287 
288  const auto & thin_set = vc_new.objectSet();
289  const auto & all_set = obj.pointSet();
290 
291  if (vm.count("exportSDP"))
292  {
293  std::ofstream out;
294  out.open(vm["exportSDP"].as<std::string>().c_str());
295  for (auto &p : thin_set)
296  {
297  out << p[0] << " " << p[1] << " " << p[2] << std::endl;
298  }
299  }
300 
301  if (vm.count("exportImage"))
302  {
303  auto outputFilename = vm["exportImage"].as<std::string>();
304  if(verbose)
305  std::cout << "outputFilename" << outputFilename << std::endl;
306 
307  unsigned int foreground_value = 255;
308  auto thin_image = ImageFromSet<Image>::create(thin_set, foreground_value);
309  thin_image >> outputFilename;
310  // // ITK output
311  // typedef itk::ImageFileWriter<Image::ITKImage> ITKImageWriter;
312  // typename ITKImageWriter::Pointer writer = ITKImageWriter::New();
313  // writer->SetFileName(outputFilename.c_str());
314  // writer->SetInput(thin_image.getITKImagePointer());
315  // writer->Update();
316  }
317 
318  if(visualize)
319  {
320  int argc(1);
321  char** argv(nullptr);
322  QApplication app(argc, argv);
323  Viewer3D<> viewer;
324  viewer.setWindowTitle("criticalKernelsThinning3D");
325  viewer.show();
326 
327  viewer.setFillColor(Color(255, 255, 255, 255));
328  viewer << thin_set;
329 
330  // All kspace voxels
331  viewer.setFillColor(Color(40, 200, 55, 10));
332  viewer << all_set;
333 
334  viewer << Viewer3D<>::updateDisplay;
335 
336  app.exec();
337  }
338 }
virtual void setFillColor(DGtal::Color aColor)
void beginBlock(const std::string &keyword="")
static Self diagonal(Component val=1)
HyperRectDomain< Space > Domain
virtual void show()
TForegroundAdjacency ForegroundAdjacency
STL namespace.
double endBlock()
static TContainer import(const std::string &filename, std::vector< unsigned int > dimSpace=std::vector< unsigned int >())
DGtal::CountedPtr< boost::dynamic_bitset<> > loadTable(const std::string &input_filename, unsigned int known_size)
TBackgroundAdjacency BackgroundAdjacency
const std::string tableSimple26_6
DigitalSetSelector< Domain, BIG_DS+HIGH_BEL_DS >::Type DigitalSet
bool init(const Point &lower, const Point &upper, bool isClosed)
Trace trace(traceWriterTerm)
std::ostream & info()
const Domain & domain() const
static Image create(const Set &aSet, const Value &defaultValue, const bool addBorder, typename Set::ConstIterator itBegin, typename Set::ConstIterator itEnd)
typename Self::Domain Domain