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"
106typedef ImageContainerBySTLVector < Z3i::Domain, unsigned char > Image3D;
107typedef ImageContainerBySTLVector < Z3i::Domain, DGtal::uint64_t > Image3DI;
109typedef Z3i::KSpace::SurfelSet SurfelSet;
110typedef SetOfSurfels< Z3i::KSpace, SurfelSet > MySetOfSurfels;
113std::vector<unsigned int> getHistoFromImage(
const Image3D &image){
114 const Image3D::Domain &imgDom = image.domain();
115 std::vector<unsigned int> vectHisto(UCHAR_MAX);
116 for(Image3D::Domain::ConstIterator it=imgDom.begin(); it!= imgDom.end(); ++it){
117 vectHisto[image(*it)]++;
124getOtsuThreshold(
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;
131 unsigned int sumMuAll= 0;
132 for(
unsigned int t=0; t< histo.size();t++){
133 sumMuAll+=histo[t]*t;
136 unsigned int thresholdRes=0;
138 for(
unsigned int t=0; t< histo.size(); t++){
149 double muAr=muA/(double)sumA;
150 double muBr=muB/(double)sumB;
151 double sigma= (double)sumA*(
double)sumB*(muAr-muBr)*(muAr-muBr);
161template<
typename TImageOutput>
163applySegmentation(TImageOutput &imageResuSegmentation,
const Image3D &inputImage, CLI::Option *labelOpt,
int maxTh,
int minTh) {
164 functors::IntervalForegroundPredicate<Image3D> simplePredicate ( inputImage, minTh, maxTh );
165 SurfelAdjacency< Z3i::KSpace::dimension > SAdj (
true );
167 bool space_ok = K.init( inputImage.domain().lowerBound(), inputImage.domain().upperBound(),
false );
169 trace.error() <<
"problem initializing 3d space" << endl;
172 std::vector< std::vector<Z3i::SCell > > vectConnectedSCell;
173 Surfaces<Z3i::KSpace>::extractAllConnectedSCell(vectConnectedSCell,K, SAdj, simplePredicate,
false);
174 trace.progressBar(0, vectConnectedSCell.size());
175 for(
unsigned int i = 0; i<vectConnectedSCell.size(); i++)
177 trace.progressBar(i, vectConnectedSCell.size());
178 MySetOfSurfels aSet(K, SAdj);
179 Z3i::Point lowerPoint, upperPoint;
182 for(std::vector<Z3i::SCell>::const_iterator it= vectConnectedSCell.at(i).begin(); it != vectConnectedSCell.at(i).end(); ++it)
184 aSet.surfelSet().insert(aSet.surfelSet().begin(), *it);
185 unsigned int orth_dir = K.sOrthDir( *it );
186 p1 = K.sCoords( K.sIncident( *it, orth_dir,
true ) );
187 p2 = K.sCoords( K.sIncident( *it, orth_dir,
false ) );
188 if(p1[0] < lowerPoint[0]) lowerPoint[0]= p1[0];
189 if(p1[1] < lowerPoint[1]) lowerPoint[1]= p1[1];
190 if(p1[2] < lowerPoint[2]) lowerPoint[2]= p1[2];
192 if(p1[0] > upperPoint[0]) upperPoint[0]= p1[0];
193 if(p1[1] > upperPoint[1]) upperPoint[1]= p1[1];
194 if(p1[2] > upperPoint[2]) upperPoint[2]= p1[2];
196 if(p2[0] < lowerPoint[0]) lowerPoint[0]= p2[0];
197 if(p2[1] < lowerPoint[1]) lowerPoint[1]= p2[1];
198 if(p2[2] < lowerPoint[2]) lowerPoint[2]= p2[2];
200 if(p2[0] > upperPoint[0]) upperPoint[0]= p2[0];
201 if(p2[1] > upperPoint[1]) upperPoint[1]= p2[1];
202 if(p2[2] > upperPoint[2]) upperPoint[2]= p2[2];
207 kRestr.init( lowerPoint, upperPoint,
false );
208 if(simplePredicate(p2)){
209 DGtal::Surfaces<Z3i::KSpace>::uFillInterior( kRestr, aSet.surfelPredicate(),
210 imageResuSegmentation,
212 }
else if (labelOpt->count() > 0){
213 DGtal::Surfaces<Z3i::KSpace>::uFillExterior( kRestr, aSet.surfelPredicate(),
214 imageResuSegmentation,
218 trace.progressBar(vectConnectedSCell.size(), vectConnectedSCell.size());
219 trace.info() << std::endl;
226int main(
int argc,
char** argv )
231 std::string inputFileName;
232 std::string outputFileName {
"result.vol"};
233 bool labelBackground {
false};
236 bool outputTypeInt {
false};
238 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");
239 app.add_option(
"-i,--input,1", inputFileName,
"volumetric input file (.vol, .pgm, .pgm3d, .longvol)." )
241 ->check(CLI::ExistingFile);
242 app.add_option(
"-o,--output,2", outputFileName,
"volumetric output file (.vol, .pgm, .pgm3d, .longvol)");
244 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).");
246 auto labelOpt = app.add_flag(
"--labelBackground",labelBackground,
"option to define a label to regions associated to object background.");
247 app.add_option(
"-m,--thresholdMin",minTh,
"min threshold (if not given the max threshold is computed with Otsu algorithm)." );
248 auto maxThOpt = app.add_option(
"-M,--thresholdMax", maxTh,
"max threshold" );
251 app.get_formatter()->column_width(40);
252 CLI11_PARSE(app, argc, argv);
255 trace.info() <<
"Reading input file " << inputFileName ;
256 Image3D inputImage = DGtal::GenericReader<Image3D>::import(inputFileName);
257 trace.info() <<
" [done] " << std::endl ;
259 std::ofstream outStream;
260 outStream.open(outputFileName.c_str());
261 if(maxThOpt->count()==0){
262 maxTh = getOtsuThreshold(inputImage);
263 trace.info() <<
"maximal threshold value not specified, using Otsu value: " << maxTh << std::endl;
265 trace.info() <<
"Processing image to output file " << outputFileName << std::endl;
270 Image3DI imageResuSegmentation(inputImage.domain());
271 applySegmentation(imageResuSegmentation, inputImage, labelOpt, maxTh, minTh);
272 GenericWriter<Image3DI>::exportFile(outputFileName, imageResuSegmentation);
275 Image3D imageResuSegmentation(inputImage.domain());
276 applySegmentation(imageResuSegmentation, inputImage, labelOpt, maxTh, minTh);
277 GenericWriter<Image3D>::exportFile(outputFileName, imageResuSegmentation);