32 #include "DGtal/base/Common.h"
33 #include "DGtal/helpers/StdDefs.h"
34 #include "DGtal/topology/helpers/Surfaces.h"
36 #include "DGtal/images/ImageContainerBySTLVector.h"
37 #include "DGtal/io/readers/GenericReader.h"
38 #include "DGtal/io/writers/GenericWriter.h"
39 #include "DGtal/images/IntervalForegroundPredicate.h"
40 #include <DGtal/topology/SetOfSurfels.h>
41 #include "DGtal/topology/DigitalSurface.h"
42 #include "DGtal/topology/helpers/Surfaces.h"
48 using namespace DGtal;
103 typedef ImageContainerBySTLVector < Z3i::Domain, unsigned char > Image3D;
105 std::vector<unsigned int> getHistoFromImage(
const Image3D &image){
106 const Image3D::Domain &imgDom = image.domain();
107 std::vector<unsigned int> vectHisto(UCHAR_MAX);
108 for(Image3D::Domain::ConstIterator it=imgDom.begin(); it!= imgDom.end(); ++it){
109 vectHisto[image(*it)]++;
116 getOtsuThreshold(
const Image3D &image){
117 std::vector<unsigned int> histo = getHistoFromImage(image);
118 unsigned int imageSize = image.domain().size();
119 unsigned int sumA = 0;
120 unsigned int sumB = imageSize;
123 unsigned int sumMuAll= 0;
124 for(
unsigned int t=0; t< histo.size();t++){
125 sumMuAll+=histo[t]*t;
128 unsigned int thresholdRes=0;
130 for(
unsigned int t=0; t< histo.size(); t++){
141 double muAr=muA/(double)sumA;
142 double muBr=muB/(double)sumB;
143 double sigma= (double)sumA*(
double)sumB*(muAr-muBr)*(muAr-muBr);
154 int main(
int argc,
char** argv )
156 typedef Z3i::KSpace::SurfelSet SurfelSet;
157 typedef SetOfSurfels< Z3i::KSpace, SurfelSet > MySetOfSurfels;
161 std::string inputFileName;
162 std::string outputFileName {
"result.vol"};
163 bool labelBackground {
false};
167 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");
168 app.add_option(
"-i,--input,1", inputFileName,
"volumetric input file (.vol, .pgm, .pgm3d, .longvol)." )
170 ->check(CLI::ExistingFile);
171 app.add_option(
"-o,--output,2", outputFileName,
"volumetric output file (.vol, .pgm, .pgm3d, .longvol)",
true);
172 auto labelOpt = app.add_flag(
"--labelBackground",labelBackground,
"option to define a label to regions associated to object background.");
173 app.add_option(
"-m,--thresholdMin",minTh,
"min threshold (if not given the max threshold is computed with Otsu algorithm).",
true );
174 auto maxThOpt = app.add_option(
"-M,--thresholdMax", maxTh,
"max threshold",
true );
177 app.get_formatter()->column_width(40);
178 CLI11_PARSE(app, argc, argv);
181 trace.info() <<
"Reading input file " << inputFileName ;
182 Image3D inputImage = DGtal::GenericReader<Image3D>::import(inputFileName);
183 Image3D imageResuSegmentation(inputImage.domain());
185 trace.info() <<
" [done] " << std::endl ;
186 std::ofstream outStream;
187 outStream.open(outputFileName.c_str());
188 if(maxThOpt->count()==0){
189 maxTh = getOtsuThreshold(inputImage);
190 trace.info() <<
"maximal threshold value not specified, using Otsu value: " << maxTh << std::endl;
192 trace.info() <<
"Processing image to output file " << outputFileName << std::endl;
194 functors::IntervalForegroundPredicate<Image3D> simplePredicate ( inputImage, minTh, maxTh );
195 SurfelAdjacency< Z3i::KSpace::dimension > SAdj (
true );
197 bool space_ok = K.init( inputImage.domain().lowerBound(), inputImage.domain().upperBound(),
false );
199 trace.error() <<
"problem initializing 3d space" << endl;
202 std::vector< std::vector<Z3i::SCell > > vectConnectedSCell;
203 Surfaces<Z3i::KSpace>::extractAllConnectedSCell(vectConnectedSCell,K, SAdj, simplePredicate,
false);
204 trace.progressBar(0, vectConnectedSCell.size());
205 for(
unsigned int i = 0; i<vectConnectedSCell.size(); i++)
207 trace.progressBar(i, vectConnectedSCell.size());
208 MySetOfSurfels aSet(K, SAdj);
209 Z3i::Point lowerPoint, upperPoint;
212 for(std::vector<Z3i::SCell>::const_iterator it= vectConnectedSCell.at(i).begin(); it != vectConnectedSCell.at(i).end(); ++it)
214 aSet.surfelSet().insert(aSet.surfelSet().begin(), *it);
215 unsigned int orth_dir = K.sOrthDir( *it );
216 p1 = K.sCoords( K.sIncident( *it, orth_dir,
true ) );
217 p2 = K.sCoords( K.sIncident( *it, orth_dir,
false ) );
218 if(p1[0] < lowerPoint[0]) lowerPoint[0]= p1[0];
219 if(p1[1] < lowerPoint[1]) lowerPoint[1]= p1[1];
220 if(p1[2] < lowerPoint[2]) lowerPoint[2]= p1[2];
222 if(p1[0] > upperPoint[0]) upperPoint[0]= p1[0];
223 if(p1[1] > upperPoint[1]) upperPoint[1]= p1[1];
224 if(p1[2] > upperPoint[2]) upperPoint[2]= p1[2];
226 if(p2[0] < lowerPoint[0]) lowerPoint[0]= p2[0];
227 if(p2[1] < lowerPoint[1]) lowerPoint[1]= p2[1];
228 if(p2[2] < lowerPoint[2]) lowerPoint[2]= p2[2];
230 if(p2[0] > upperPoint[0]) upperPoint[0]= p2[0];
231 if(p2[1] > upperPoint[1]) upperPoint[1]= p2[1];
232 if(p2[2] > upperPoint[2]) upperPoint[2]= p2[2];
237 kRestr.init( lowerPoint, upperPoint,
false );
238 if(simplePredicate(p2)){
239 DGtal::Surfaces<Z3i::KSpace>::uFillInterior( kRestr, aSet.surfelPredicate(),
240 imageResuSegmentation,
242 }
else if (labelOpt->count() > 0){
243 DGtal::Surfaces<Z3i::KSpace>::uFillExterior( kRestr, aSet.surfelPredicate(),
244 imageResuSegmentation,
248 trace.progressBar(vectConnectedSCell.size(), vectConnectedSCell.size());
249 trace.info() << std::endl;
250 GenericWriter<Image3D>::exportFile(outputFileName, imageResuSegmentation);