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"
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"
105typedef ImageContainerBySTLVector < Z3i::Domain, unsigned char > Image3D;
106typedef ImageContainerBySTLVector < Z3i::Domain, DGtal::uint64_t > Image3DI;
108typedef Z3i::KSpace::SurfelSet SurfelSet;
109typedef SetOfSurfels< Z3i::KSpace, SurfelSet > MySetOfSurfels;
112std::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)]++;
123getOtsuThreshold(
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;
130 unsigned int sumMuAll= 0;
131 for(
unsigned int t=0; t< histo.size();t++){
132 sumMuAll+=histo[t]*t;
135 unsigned int thresholdRes=0;
137 for(
unsigned int t=0; t< histo.size(); t++){
148 double muAr=muA/(double)sumA;
149 double muBr=muB/(double)sumB;
150 double sigma= (double)sumA*(
double)sumB*(muAr-muBr)*(muAr-muBr);
160template<
typename TImageOutput>
162applySegmentation(TImageOutput &imageResuSegmentation,
const Image3D &inputImage, CLI::Option *labelOpt,
int maxTh,
int minTh) {
163 functors::IntervalForegroundPredicate<Image3D> simplePredicate ( inputImage, minTh, maxTh );
164 SurfelAdjacency< Z3i::KSpace::dimension > SAdj (
true );
166 bool space_ok = K.init( inputImage.domain().lowerBound(), inputImage.domain().upperBound(),
false );
168 trace.error() <<
"problem initializing 3d space" << endl;
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++)
176 trace.progressBar(i, vectConnectedSCell.size());
177 MySetOfSurfels aSet(K, SAdj);
178 Z3i::Point lowerPoint, upperPoint;
181 for(std::vector<Z3i::SCell>::const_iterator it= vectConnectedSCell.at(i).begin(); it != vectConnectedSCell.at(i).end(); ++it)
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];
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];
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];
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];
206 kRestr.init( lowerPoint, upperPoint,
false );
207 if(simplePredicate(p2)){
208 DGtal::Surfaces<Z3i::KSpace>::uFillInterior( kRestr, aSet.surfelPredicate(),
209 imageResuSegmentation,
211 }
else if (labelOpt->count() > 0){
212 DGtal::Surfaces<Z3i::KSpace>::uFillExterior( kRestr, aSet.surfelPredicate(),
213 imageResuSegmentation,
217 trace.progressBar(vectConnectedSCell.size(), vectConnectedSCell.size());
218 trace.info() << std::endl;
225int main(
int argc,
char** argv )
230 std::string inputFileName;
231 std::string outputFileName {
"result.vol"};
232 bool labelBackground {
false};
235 bool outputTypeInt {
false};
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)." )
240 ->check(CLI::ExistingFile);
241 app.add_option(
"-o,--output,2", outputFileName,
"volumetric output file (.vol, .pgm, .pgm3d, .longvol)");
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 DGTAL_WITH_ITK option is selected).");
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)." );
247 auto maxThOpt = app.add_option(
"-M,--thresholdMax", maxTh,
"max threshold" );
250 app.get_formatter()->column_width(40);
251 CLI11_PARSE(app, argc, argv);
254 trace.info() <<
"Reading input file " << inputFileName ;
255 Image3D inputImage = DGtal::GenericReader<Image3D>::import(inputFileName);
256 trace.info() <<
" [done] " << std::endl ;
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;
264 trace.info() <<
"Processing image to output file " << outputFileName << std::endl;
269 Image3DI imageResuSegmentation(inputImage.domain());
270 applySegmentation(imageResuSegmentation, inputImage, labelOpt, maxTh, minTh);
271 GenericWriter<Image3DI>::exportFile(outputFileName, imageResuSegmentation);
274 Image3D imageResuSegmentation(inputImage.domain());
275 applySegmentation(imageResuSegmentation, inputImage, labelOpt, maxTh, minTh);
276 GenericWriter<Image3D>::exportFile(outputFileName, imageResuSegmentation);