32#include "DGtal/base/Common.h"
33#include "DGtal/base/BasicFunctors.h"
34#include "DGtal/kernel/SpaceND.h"
35#include "DGtal/kernel/domains/HyperRectDomain.h"
36#include "DGtal/images/ImageSelector.h"
37#include "DGtal/images/IntervalForegroundPredicate.h"
38#include "DGtal/topology/KhalimskySpaceND.h"
39#include "DGtal/topology/DigitalSurface.h"
40#include "DGtal/topology/SetOfSurfels.h"
41#include "DGtal/io/viewers/PolyscopeViewer.h"
42#include "DGtal/io/readers/PointListReader.h"
43#include "DGtal/io/readers/GenericReader.h"
45#include "DGtal/io/readers/DicomReader.h"
47#include "DGtal/io/Color.h"
48#include "DGtal/io/colormaps/GradientColorMap.h"
104int main(
int argc,
char** argv )
106 typedef SpaceND<3,int> Space;
107 typedef KhalimskySpaceND<3,int> KSpace;
108 typedef HyperRectDomain<Space> Domain;
109 typedef ImageSelector<Domain, unsigned char>::Type Image;
110 typedef DigitalSetSelector< Domain, BIG_DS+HIGH_BEL_DS >::Type DigitalSet;
111 typedef SurfelAdjacency<KSpace::dimension> MySurfelAdjacency;
115 app.description(
"Display the boundary of a volume file by using PolyscopeViewer. The mode specifies if you wish to see surface elements (BDRY), the inner voxels (INNER) or the outer voxels (OUTER) that touch the boundary. \n \t Example: 3dVolBoundaryViewer $DGtal/examples/samples/lobster.vol -m 60");
116 std::string inputFileName;
117 DGtal::int64_t rescaleInputMin {0};
118 DGtal::int64_t rescaleInputMax {255};
119 int dicomMin {-1000};
121 int thresholdMin {0};
122 int thresholdMax {255};
123 std::string mode {
"INNER"};
125 app.add_option(
"-i,--input,1", inputFileName,
"vol file (.vol, .longvol .p3d, .pgm3d and if DGTAL_WITH_ITK is selected: dicom, dcm, mha, mhd). For longvol, dicom, dcm, mha or mhd formats, the input values are linearly scaled between 0 and 255." )
127 ->check(CLI::ExistingFile);
129 app.add_option(
"--thresholdMin,-m", thresholdMin,
"threshold min (excluded) to define binary shape.");
130 app.add_option(
"--thresholdMax,-M", thresholdMax,
"threshold max (included) to define binary shape.");
132 app.add_option(
"--dicomMin",dicomMin,
"set minimum density threshold on Hounsfield scale" );
133 app.add_option(
"--dicomMax",dicomMin,
"set maximum density threshold on Hounsfield scale" );
135 app.add_option(
"--mode", mode,
"set mode for display: INNER: inner voxels, OUTER: outer voxels, BDRY: surfels" )
136 -> check(CLI::IsMember({
"INNER",
"OUTER",
"BDRY"}));
139 app.get_formatter()->column_width(40);
140 CLI11_PARSE(app, argc, argv);
144 string extension = inputFileName.substr(inputFileName.find_last_of(
".") + 1);
145 if(extension!=
"vol" && extension !=
"p3d" && extension !=
"pgm3D" && extension !=
"pgm3d" && extension !=
"sdp" && extension !=
"pgm"
150 trace.info() <<
"File extension not recognized: "<< extension << std::endl;
154 if(extension==
"vol" || extension==
"pgm3d" || extension==
"pgm3D"
159 trace.beginBlock(
"Loading image into memory." );
161 typedef DGtal::functors::Rescaling<int ,unsigned char > RescalFCT;
162 Image image = extension ==
"dcm" ? DicomReader< Image, RescalFCT >::importDicom( inputFileName,
166 GenericReader<Image>::import( inputFileName );
168 Image image = GenericReader<Image>::import (inputFileName );
170 trace.info() <<
"Image loaded: "<<image<< std::endl;
174 trace.beginBlock(
"Construct the Khalimsky space from the image domain." );
175 Domain domain = image.domain();
177 bool space_ok = ks.init( domain.lowerBound(), domain.upperBound(),
true );
180 trace.error() <<
"Error in the Khamisky space construction."<<std::endl;
187 trace.beginBlock(
"Wrapping a digital set around image. " );
188 typedef functors::IntervalForegroundPredicate<Image> ThresholdedImage;
189 ThresholdedImage thresholdedImage( image, thresholdMin, thresholdMax );
194 trace.beginBlock(
"Extracting boundary by scanning the space. " );
195 typedef KSpace::SurfelSet SurfelSet;
196 typedef SetOfSurfels< KSpace, SurfelSet > MySetOfSurfels;
197 typedef DigitalSurface< MySetOfSurfels > MyDigitalSurface;
198 MySurfelAdjacency surfAdj(
true );
199 MySetOfSurfels theSetOfSurfels( ks, surfAdj );
200 Surfaces<KSpace>::sMakeBoundary( theSetOfSurfels.surfelSet(),
201 ks, thresholdedImage,
203 domain.upperBound() );
204 MyDigitalSurface digSurf( theSetOfSurfels );
205 trace.info() <<
"Digital surface has " << digSurf.size() <<
" surfels."
211 trace.beginBlock(
"Displaying everything. " );
213 s <<
"3dVolBoundaryViewer - DGtalTools: ";
214 string name = inputFileName.substr(inputFileName.find_last_of(
"/")+1,inputFileName.size()) ;
216 polyscope::options::programName = s.str();
217 polyscope::options::groundPlaneMode = polyscope::GroundPlaneMode::None;
218 polyscope::view::setNavigateStyle(polyscope::NavigateStyle::Free);
220 PolyscopeViewer<Space,KSpace> viewer(ks);
221 viewer.allowReuseList =
true;
222 typedef MyDigitalSurface::ConstIterator ConstIterator;
223 if ( mode ==
"BDRY" )
225 viewer.drawAsSimplified();
226 for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it )
227 viewer << ks.unsigns( *it );
228 }
else if ( mode ==
"INNER" )
229 for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it )
230 viewer << ks.sCoords( ks.sDirectIncident( *it, ks.sOrthDir( *it ) ) );
231 else if ( mode ==
"OUTER" )
232 for ( ConstIterator it = digSurf.begin(), itE = digSurf.end(); it != itE; ++it )
233 viewer << ks.sCoords( ks.sIndirectIncident( *it, ks.sOrthDir( *it ) ) );
235 trace.error() <<
"Warning display mode (" << mode <<
") not implemented." << std::endl;
236 trace.error() <<
"The display will be empty." << std::endl;