DGtalTools  0.9.4
volSegment.cpp
1 
29 #include <iostream>
31 #include <fstream>
32 
33 #include "DGtal/base/Common.h"
34 #include "DGtal/helpers/StdDefs.h"
35 #include "DGtal/topology/helpers/Surfaces.h"
36 
37 #include "DGtal/images/ImageContainerBySTLVector.h"
38 #include "DGtal/io/readers/GenericReader.h"
39 #include "DGtal/io/writers/GenericWriter.h"
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 
46 #include <boost/program_options/options_description.hpp>
47 #include <boost/program_options/parsers.hpp>
48 #include <boost/program_options/variables_map.hpp>
49 
50 using namespace std;
51 using namespace DGtal;
52 
53 
55 namespace po = boost::program_options;
56 
57 
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 
123 unsigned int
124 getOtsuThreshold(const Image3D &image){
125  std::vector<unsigned int> histo = getHistoFromImage(image);
126  unsigned int imageSize = image.domain().size();
127  unsigned int sumA = 0;
128  unsigned int sumB = imageSize;
129  unsigned int muA=0;
130  unsigned int muB=0;
131  unsigned int sumMuAll= 0;
132  for( unsigned int t=0; t< histo.size();t++){
133  sumMuAll+=histo[t]*t;
134  }
135 
136  unsigned int thresholdRes=0;
137  double valMax=0.0;
138  for( unsigned int t=0; t< histo.size(); t++){
139  sumA+=histo[t];
140  if(sumA==0)
141  continue;
142  sumB=imageSize-sumA;
143  if(sumB==0){
144  break;
145  }
146 
147  muA+=histo[t]*t;
148  muB=sumMuAll-muA;
149  double muAr=muA/(double)sumA;
150  double muBr=muB/(double)sumB;
151  double sigma= (double)sumA*(double)sumB*(muAr-muBr)*(muAr-muBr);
152  if(valMax<=sigma){
153  valMax=sigma;
154  thresholdRes=t;
155  }
156  }
157  return thresholdRes;
158 }
159 
160 
161 
162 int main( int argc, char** argv )
163 {
164 
165  typedef Z3i::KSpace::SurfelSet SurfelSet;
166  typedef SetOfSurfels< Z3i::KSpace, SurfelSet > MySetOfSurfels;
167 
168  // parse command line ----------------------------------------------
169  po::options_description general_opt(" \n Allowed options are ");
170  general_opt.add_options()
171  ("help,h", "display this message")
172  ("input,i", po::value<std::string>(), "volumetric input file (.vol, .pgm, .pgm3d, .longvol) " )
173  ("output,o", po::value<std::string>(), "volumetric output file (.vol, .pgm, .pgm3d, .longvol) " )
174  ("labelBackground", "option to define a label to regions associated to object background. ")
175  ("thresholdMin,m", po::value<int>()->default_value(0), "min threshold (if not given the max threshold is computed with Otsu algorithm)." )
176  ("thresholdMax,M", po::value<int>(), "max threshold (default 255)" );
177 
178 
179  bool parseOK=true;
180  po::variables_map vm;
181  try{
182  po::store(po::parse_command_line(argc, argv, general_opt), vm);
183  }catch(const std::exception& ex){
184  parseOK=false;
185  trace.info()<< "Error checking program options: "<< ex.what()<< endl;
186  }
187  po::notify(vm);
188  if( !parseOK || vm.count("help")||argc<=1)
189  {
190  std::cout << "Usage: " << argv[0] << " [input] [output]\n"
191  << "Segment volumetric file from a simple threshold which can be set automatically from the otsu estimation.\n"
192  << "The segmentation result is given by an integer label given in the resulting image."
193  << general_opt << "\n";
194  std::cout << "Example:\n"
195  << "volSegment -i ${DGtal}/examples/samples/lobster.vol -o segmentation.vol \n";
196  return 0;
197  }
198 
199 
200  if(! vm.count("input") ||! vm.count("output"))
201  {
202  trace.error() << " Input and output filename are needed to be defined" << endl;
203  return 0;
204  }
205 
206 
207  string inputFilename = vm["input"].as<std::string>();
208  string outputFilename = vm["output"].as<std::string>();
209 
210  trace.info() << "Reading input file " << inputFilename ;
211  Image3D inputImage = DGtal::GenericReader<Image3D>::import(inputFilename);
212  Image3D imageResuSegmentation(inputImage.domain());
213 
214  trace.info() << " [done] " << std::endl ;
215  std::ofstream outStream;
216  outStream.open(outputFilename.c_str());
217  int minTh = vm["thresholdMin"].as<int>();
218  int maxTh = 128;
219  if(!vm.count("thresholdMax")){
220  maxTh = getOtsuThreshold(inputImage);
221  trace.info() << "maximal threshold value not specified, using Otsu value: " << maxTh << std::endl;
222  }else{
223  maxTh = vm["thresholdMax"].as<int>();
224  }
225  trace.info() << "Processing image to output file " << outputFilename << std::endl;
226 
227  functors::IntervalForegroundPredicate<Image3D> simplePredicate ( inputImage, minTh, maxTh );
229  Z3i::KSpace K;
230  bool space_ok = K.init( inputImage.domain().lowerBound(), inputImage.domain().upperBound(), false );
231  if(!space_ok){
232  trace.error() << "problem initializing 3d space" << endl;
233  }
234 
235 
236  std::vector< std::vector<Z3i::SCell > > vectConnectedSCell;
237  Surfaces<Z3i::KSpace>::extractAllConnectedSCell(vectConnectedSCell,K, SAdj, simplePredicate, false);
238  trace.progressBar(0, vectConnectedSCell.size());
239  for(unsigned int i = 0; i<vectConnectedSCell.size(); i++)
240  {
241  trace.progressBar(i, vectConnectedSCell.size());
242  MySetOfSurfels aSet(K, SAdj);
243  Z3i::Point lowerPoint, upperPoint;
244  Z3i::Point p1;
245  Z3i::Point p2;
246  for(std::vector<Z3i::SCell>::const_iterator it= vectConnectedSCell.at(i).begin(); it != vectConnectedSCell.at(i).end(); ++it)
247  {
248  aSet.surfelSet().insert(aSet.surfelSet().begin(), *it);
249  unsigned int orth_dir = K.sOrthDir( *it );
250  p1 = K.sCoords( K.sIncident( *it, orth_dir, true ) );
251  p2 = K.sCoords( K.sIncident( *it, orth_dir, false ) );
252  if(p1[0] < lowerPoint[0]) lowerPoint[0]= p1[0];
253  if(p1[1] < lowerPoint[1]) lowerPoint[1]= p1[1];
254  if(p1[2] < lowerPoint[2]) lowerPoint[2]= p1[2];
255 
256  if(p1[0] > upperPoint[0]) upperPoint[0]= p1[0];
257  if(p1[1] > upperPoint[1]) upperPoint[1]= p1[1];
258  if(p1[2] > upperPoint[2]) upperPoint[2]= p1[2];
259 
260  if(p2[0] < lowerPoint[0]) lowerPoint[0]= p2[0];
261  if(p2[1] < lowerPoint[1]) lowerPoint[1]= p2[1];
262  if(p2[2] < lowerPoint[2]) lowerPoint[2]= p2[2];
263 
264  if(p2[0] > upperPoint[0]) upperPoint[0]= p2[0];
265  if(p2[1] > upperPoint[1]) upperPoint[1]= p2[1];
266  if(p2[2] > upperPoint[2]) upperPoint[2]= p2[2];
267 
268  }
269 
270  Z3i::KSpace kRestr ;
271  kRestr.init( lowerPoint, upperPoint, false );
272  if(simplePredicate(p2)){
273  DGtal::Surfaces<Z3i::KSpace>::uFillInterior( kRestr, aSet.surfelPredicate(),
274  imageResuSegmentation,
275  i, false, false);
276  }else if (vm.count("labelBackground")){
277  DGtal::Surfaces<Z3i::KSpace>::uFillExterior( kRestr, aSet.surfelPredicate(),
278  imageResuSegmentation,
279  i+1, false, false);
280  }
281  }
282  trace.progressBar(vectConnectedSCell.size(), vectConnectedSCell.size());
283  trace.info() << std::endl;
284  GenericWriter<Image3D>::exportFile(outputFilename, imageResuSegmentation);
285  return 0;
286 }
287 
void progressBar(const double currentValue, const double maximalValue)
std::set< SCell > SurfelSet
STL namespace.
static TContainer import(const std::string &filename, std::vector< unsigned int > dimSpace=std::vector< unsigned int >())
Dimension sOrthDir(const SCell &s) const
bool init(const Point &lower, const Point &upper, bool isClosed)
SCell sIncident(const SCell &c, Dimension k, bool up) const
Point sCoords(const SCell &c) const
Trace trace(traceWriterTerm)
std::ostream & info()
const Domain & domain() 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)
std::vector< Value >::const_iterator ConstIterator
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()