DGtalTools  1.5.beta
volSegment.cpp
1 
29 #include <iostream>
30 #include <fstream>
31 
32 #include "DGtal/base/Common.h"
33 #include "DGtal/helpers/StdDefs.h"
34 #include "DGtal/topology/helpers/Surfaces.h"
35 
36 #include "DGtal/images/ImageContainerBySTLVector.h"
37 #include "DGtal/io/readers/GenericReader.h"
38 #include "DGtal/io/writers/GenericWriter.h"
39 
40 #include "DGtal/images/IntervalForegroundPredicate.h"
41 #include <DGtal/topology/SetOfSurfels.h>
42 #include "DGtal/topology/DigitalSurface.h"
43 #include "DGtal/topology/helpers/Surfaces.h"
44 
45 #include "CLI11.hpp"
46 
47 
48 using namespace std;
49 using namespace DGtal;
50 
51 
107 
108 typedef Z3i::KSpace::SurfelSet SurfelSet;
110 
111 
112 std::vector<unsigned int> getHistoFromImage(const Image3D &image){
113  const Image3D::Domain &imgDom = image.domain();
114  std::vector<unsigned int> vectHisto(UCHAR_MAX);
115  for(Image3D::Domain::ConstIterator it=imgDom.begin(); it!= imgDom.end(); ++it){
116  vectHisto[image(*it)]++;
117  }
118  return vectHisto;
119 }
120 
121 
122 unsigned int
123 getOtsuThreshold(const Image3D &image){
124  std::vector<unsigned int> histo = getHistoFromImage(image);
125  unsigned int imageSize = image.domain().size();
126  unsigned int sumA = 0;
127  unsigned int sumB = imageSize;
128  unsigned int muA=0;
129  unsigned int muB=0;
130  unsigned int sumMuAll= 0;
131  for( unsigned int t=0; t< histo.size();t++){
132  sumMuAll+=histo[t]*t;
133  }
134 
135  unsigned int thresholdRes=0;
136  double valMax=0.0;
137  for( unsigned int t=0; t< histo.size(); t++){
138  sumA+=histo[t];
139  if(sumA==0)
140  continue;
141  sumB=imageSize-sumA;
142  if(sumB==0){
143  break;
144  }
145 
146  muA+=histo[t]*t;
147  muB=sumMuAll-muA;
148  double muAr=muA/(double)sumA;
149  double muBr=muB/(double)sumB;
150  double sigma= (double)sumA*(double)sumB*(muAr-muBr)*(muAr-muBr);
151  if(valMax<=sigma){
152  valMax=sigma;
153  thresholdRes=t;
154  }
155  }
156  return thresholdRes;
157 }
158 
159 
160 template<typename TImageOutput>
161 void
162 applySegmentation(TImageOutput &imageResuSegmentation, const Image3D &inputImage, CLI::Option *labelOpt, int maxTh, int minTh) {
163  functors::IntervalForegroundPredicate<Image3D> simplePredicate ( inputImage, minTh, maxTh );
165  Z3i::KSpace K;
166  bool space_ok = K.init( inputImage.domain().lowerBound(), inputImage.domain().upperBound(), false );
167  if(!space_ok){
168  trace.error() << "problem initializing 3d space" << endl;
169  }
170 
171  std::vector< std::vector<Z3i::SCell > > vectConnectedSCell;
172  Surfaces<Z3i::KSpace>::extractAllConnectedSCell(vectConnectedSCell,K, SAdj, simplePredicate, false);
173  trace.progressBar(0, vectConnectedSCell.size());
174  for(unsigned int i = 0; i<vectConnectedSCell.size(); i++)
175  {
176  trace.progressBar(i, vectConnectedSCell.size());
177  MySetOfSurfels aSet(K, SAdj);
178  Z3i::Point lowerPoint, upperPoint;
179  Z3i::Point p1;
180  Z3i::Point p2;
181  for(std::vector<Z3i::SCell>::const_iterator it= vectConnectedSCell.at(i).begin(); it != vectConnectedSCell.at(i).end(); ++it)
182  {
183  aSet.surfelSet().insert(aSet.surfelSet().begin(), *it);
184  unsigned int orth_dir = K.sOrthDir( *it );
185  p1 = K.sCoords( K.sIncident( *it, orth_dir, true ) );
186  p2 = K.sCoords( K.sIncident( *it, orth_dir, false ) );
187  if(p1[0] < lowerPoint[0]) lowerPoint[0]= p1[0];
188  if(p1[1] < lowerPoint[1]) lowerPoint[1]= p1[1];
189  if(p1[2] < lowerPoint[2]) lowerPoint[2]= p1[2];
190 
191  if(p1[0] > upperPoint[0]) upperPoint[0]= p1[0];
192  if(p1[1] > upperPoint[1]) upperPoint[1]= p1[1];
193  if(p1[2] > upperPoint[2]) upperPoint[2]= p1[2];
194 
195  if(p2[0] < lowerPoint[0]) lowerPoint[0]= p2[0];
196  if(p2[1] < lowerPoint[1]) lowerPoint[1]= p2[1];
197  if(p2[2] < lowerPoint[2]) lowerPoint[2]= p2[2];
198 
199  if(p2[0] > upperPoint[0]) upperPoint[0]= p2[0];
200  if(p2[1] > upperPoint[1]) upperPoint[1]= p2[1];
201  if(p2[2] > upperPoint[2]) upperPoint[2]= p2[2];
202 
203  }
204 
205  Z3i::KSpace kRestr ;
206  kRestr.init( lowerPoint, upperPoint, false );
207  if(simplePredicate(p2)){
208  DGtal::Surfaces<Z3i::KSpace>::uFillInterior( kRestr, aSet.surfelPredicate(),
209  imageResuSegmentation,
210  i, false, false);
211  }else if (labelOpt->count() > 0){
212  DGtal::Surfaces<Z3i::KSpace>::uFillExterior( kRestr, aSet.surfelPredicate(),
213  imageResuSegmentation,
214  i+1, false, false);
215  }
216  }
217  trace.progressBar(vectConnectedSCell.size(), vectConnectedSCell.size());
218  trace.info() << std::endl;
219 }
220 
221 
222 
223 
224 
225 int main( int argc, char** argv )
226 {
227 
228  // parse command line using CLI ----------------------------------------------
229  CLI::App app;
230  std::string inputFileName;
231  std::string outputFileName {"result.vol"};
232  bool labelBackground {false};
233  int minTh {0};
234  int maxTh {255};
235  bool outputTypeInt {false};
236 
237  app.description("Segment volumetric file from a simple threshold which can be set automatically from the otsu estimation.\n The segmentation result is given by an integer label given in the resulting image. Example:\n volSegment ${DGtal}/examples/samples/lobster.vol segmentation.vol \n");
238  app.add_option("-i,--input,1", inputFileName, "volumetric input file (.vol, .pgm, .pgm3d, .longvol)." )
239  ->required()
240  ->check(CLI::ExistingFile);
241  app.add_option("-o,--output,2", outputFileName, "volumetric output file (.vol, .pgm, .pgm3d, .longvol)", true);
242 
243  app.add_flag("--outputTypeUInt", outputTypeInt, "to specify the output image type (unsigned int) instead using the default unsigned char. If this flag is selected you have to check the output file format type (longvol, or an ITK image type if the DGtal WITH_ITK option is selected).");
244 
245  auto labelOpt = app.add_flag("--labelBackground",labelBackground, "option to define a label to regions associated to object background.");
246  app.add_option("-m,--thresholdMin",minTh, "min threshold (if not given the max threshold is computed with Otsu algorithm).", true );
247  auto maxThOpt = app.add_option("-M,--thresholdMax", maxTh, "max threshold", true );
248 
249 
250  app.get_formatter()->column_width(40);
251  CLI11_PARSE(app, argc, argv);
252  // END parse command line using CLI ----------------------------------------------
253 
254  trace.info() << "Reading input file " << inputFileName ;
255  Image3D inputImage = DGtal::GenericReader<Image3D>::import(inputFileName);
256  trace.info() << " [done] " << std::endl ;
257 
258  std::ofstream outStream;
259  outStream.open(outputFileName.c_str());
260  if(maxThOpt->count()==0){
261  maxTh = getOtsuThreshold(inputImage);
262  trace.info() << "maximal threshold value not specified, using Otsu value: " << maxTh << std::endl;
263  }
264  trace.info() << "Processing image to output file " << outputFileName << std::endl;
265 
266 
267  if (outputTypeInt)
268  {
269  Image3DI imageResuSegmentation(inputImage.domain());
270  applySegmentation(imageResuSegmentation, inputImage, labelOpt, maxTh, minTh);
271  GenericWriter<Image3DI>::exportFile(outputFileName, imageResuSegmentation);
272 
273  }else{
274  Image3D imageResuSegmentation(inputImage.domain());
275  applySegmentation(imageResuSegmentation, inputImage, labelOpt, maxTh, minTh);
276  GenericWriter<Image3D>::exportFile(outputFileName, imageResuSegmentation);
277  }
278  return 0;
279 }
280 
int main(int argc, char **argv)
const Domain & domain() const
std::vector< Value >::const_iterator ConstIterator
std::set< SCell > SurfelSet
bool init(const Point &lower, const Point &upper, bool isClosed)
Dimension sOrthDir(const SCell &s) const
SCell sIncident(const SCell &c, Dimension k, bool up) const
Point sCoords(const SCell &c) const
unsigned static int uFillInterior(const KSpace &aKSpace, const TSurfelPredicate &aSurfPred, TImageContainer &anImage, const typename TImageContainer::Value &aValue, bool empty_is_inside=false, bool incrementMode=true)
unsigned static int uFillExterior(const KSpace &aKSpace, const SurfelPredicate &aSurfPred, TImageContainer &anImage, const typename TImageContainer::Value &aValue, bool empty_is_outside=true, bool incrementMode=true)
std::ostream & error()
std::ostream & info()
void progressBar(const double currentValue, const double maximalValue)
Trace trace(traceWriterTerm)
static TContainer import(const std::string &filename, std::vector< unsigned int > dimSpace=std::vector< unsigned int >())