80#include <unordered_map>
82#include <DGtal/io/viewers/PolyscopeViewer.h>
83#include <DGtal/base/Common.h>
84#include <DGtal/helpers/StdDefs.h>
85#include <DGtal/io/Color.h>
86#include <DGtal/io/readers/GenericReader.h>
87#include <DGtal/io/writers/GenericWriter.h>
88#include "DGtal/images/imagesSetsUtils/SetFromImage.h"
89#include "DGtal/images/SimpleThresholdForegroundPredicate.h"
90#include "DGtal/images/ImageSelector.h"
91#include "DGtal/images/imagesSetsUtils/ImageFromSet.h"
93#include <DGtal/topology/SurfelAdjacency.h>
94#include <DGtal/topology/CubicalComplex.h>
95#include <DGtal/topology/CubicalComplexFunctions.h>
97#include <DGtal/topology/VoxelComplex.h>
98#include <DGtal/topology/VoxelComplexFunctions.h>
99#include "DGtal/topology/NeighborhoodConfigurations.h"
100#include "DGtal/topology/tables/NeighborhoodTables.h"
103#include "DGtal/geometry/volumes/distance/ExactPredicateLpSeparableMetric.h"
104#include "DGtal/geometry/volumes/distance/VoronoiMap.h"
105#include "DGtal/geometry/volumes/distance/DistanceTransformation.h"
110using namespace DGtal;
112using namespace DGtal::Z3i;
114int main(
int argc,
char*
const argv[]){
118 std::string inputFileName;
119 std::string sk_string {
"1isthmus"};
120 string select_string {
"dmax"};
121 string foreground {
"black"};
122 string outputFilenameImg;
123 string outputFilenameSDP;
125 int thresholdMin {0};
126 int thresholdMax {255};
128 bool profile {
false};
129 bool verbose {
false};
130 bool visualize {
true};
131 bool useInputImgToExp {
false};
133 app.description(
"Compute the thinning of a volume using the CriticalKernels framework\nBasic usage: criticalKernelsThinning3D --input <volFileName> --skel <ulti,end, 1isthmus, isthmus> --select [ -f <white,black> -m <minlevel> -M <maxlevel> -v ] [--persistence <value> ] --persistence <value> ] \n options for --skel {ulti end 1isthmus isthmus} \n options for --select = {dmax random first} \n Example: \n criticalKernelsThinning3D --input ${DGtal}/examples/samples/Al.100.vol --select dmax --skel 1isthmus --persistence 1 --visualize --verbose --exportImage ./Al100_dmax_1isthmus_p1.vol \n");
134 app.add_option(
"-i,--input,1", inputFileName,
"Input vol file." )
136 ->check(CLI::ExistingFile);
138 app.add_option(
"--skel,-s", sk_string,
"Type of skeletonization. Options: 1isthmus, isthmus, end, ulti." )
139 -> check(CLI::IsMember({
"ulti",
"end",
"isthmus",
"1isthmus"}));
140 app.add_option(
"--select,-c", select_string,
"Select the ordering for skeletonization. Options: dmax, random, first")
141 -> check(CLI::IsMember({
"random",
"dmax",
"first"}));
142 app.add_option(
"--foreground,-f",foreground,
"Foreground color in binary image" )
143 -> check(CLI::IsMember({
"white",
"black"}));
144 app.add_option(
"--thresholdMin,-m", thresholdMin,
"Threshold min (excluded) to define binary shape" );
145 app.add_option(
"--thresholdMax,-M", thresholdMax,
"Threshold max (included) to define binary shape" );
146 app.add_option(
"--persistence,-p",persistence,
"Persistence value, implies use of persistence algorithm if p>=1" )
147 ->check(CLI::PositiveNumber);
148 app.add_flag(
"--profile", profile,
"Profile algorithm");
149 app.add_flag(
"--verbose,-v",verbose,
"Verbose output");
150 app.add_option(
"--exportImage,-o",outputFilenameImg,
"Export the resulting set of points to a image compatible with GenericWriter.");
151 app.add_option(
"--exportSDP,-e",outputFilenameSDP,
"Export the resulting set of points in a simple (sequence of discrete point (sdp))." );
152 app.add_flag(
"--visualize,-t", visualize,
"Visualize result in viewer");
153 app.add_flag(
"--useInputImgToExp,-k", useInputImgToExp,
"Use input image type to export result (allowing to keep same domain (and same image spacing when using ITK)).");
155 app.get_formatter()->column_width(40);
156 CLI11_PARSE(app, argc, argv);
162 std::cout <<
"Skel: " << sk_string << std::endl;
163 std::cout <<
"Select: " << select_string << std::endl;
164 std::cout <<
"Persistence: " << persistence << std::endl;
165 std::cout <<
"Input: " << inputFileName << std::endl;
167 trace.beginBlock(
"Reading input");
168 using Domain = Z3i::Domain ;
171 using Image = ImageSelector < Z3i::Domain, unsigned char, ITKIMAGEDATA_CONTAINER_I>::Type ;
173 using Image = ImageSelector < Z3i::Domain, unsigned char>::Type ;
177 Image image = GenericReader<Image>::import(inputFileName);
180 DigitalSet image_set (image.domain());
181 SetFromImage<Z3i::DigitalSet>::append<Image>(
183 thresholdMin, thresholdMax);
188 using DigitalTopology = DT26_6;
190 DGtal::DigitalSetByAssociativeContainer<Domain ,
191 std::unordered_set< typename Domain::Point> >;
192 using Complex = DGtal::VoxelComplex<KSpace>;
194 auto & sk = sk_string;
196 KSpace::Point d1( KSpace::Point::diagonal( 1 ) );
197 ks.init(image.domain().lowerBound() - d1 ,
198 image.domain().upperBound() + d1 ,
true);
200 trace.beginBlock(
"construct with table");
202 vc.construct(image_set, functions::loadTable(simplicity::tableSimple26_6 ));
204 trace.beginBlock(
"load isthmus table");
205 boost::dynamic_bitset<> isthmus_table;
207 isthmus_table = *functions::loadTable(isthmusicity::tableIsthmus);
208 else if (sk ==
"1isthmus")
209 isthmus_table = *functions::loadTable(isthmusicity::tableOneIsthmus);
210 auto pointMap = *functions::mapZeroPointNeighborhoodToConfigurationMask<Point>();
215 std::function< bool(
const Complex&,
const Cell&) > Skel ;
216 if (sk ==
"ulti") Skel = skelUltimate<Complex>;
217 else if (sk ==
"end") Skel = skelEnd<Complex>;
218 else if (sk ==
"isthmus" || sk ==
"1isthmus")
219 Skel = [&isthmus_table, &pointMap](
const Complex & fc,
220 const Complex::Cell & c){
221 return skelWithTable(isthmus_table, pointMap, fc, c);
223 else throw std::runtime_error(
"Invalid skel string");
224 auto start = std::chrono::system_clock::now();
230 trace.beginBlock(
"Create Distance Map");
231 using L3Metric = ExactPredicateLpSeparableMetric<Z3i::Space, 3>;
232 using DT = DistanceTransformation<Z3i::Space, DigitalSet, L3Metric>;
234 DT dt(image.domain(), image_set, l3);
237 std::function< std::pair<typename Complex::Cell, typename Complex::Data>(
const Complex::Clique&) > Select ;
238 auto & sel = select_string;
239 if (sel ==
"random") Select = selectRandom<Complex>;
240 else if (sel ==
"first") Select = selectFirst<Complex>;
241 else if (sel ==
"dmax"){
243 [&dt](
const Complex::Clique & clique){
244 return selectMaxValue<DT, Complex>(dt,clique);
246 }
else throw std::runtime_error(
"Invalid skel string");
248 trace.beginBlock(
"Thinning");
250 if (persistence == 0)
251 vc_new = asymetricThinningScheme< Complex >(
252 vc, Select, Skel, verbose);
254 vc_new = persistenceAsymetricThinningScheme< Complex >(
255 vc, Select, Skel, persistence, verbose);
258 auto end = std::chrono::system_clock::now();
259 auto elapsed = std::chrono::duration_cast<std::chrono::seconds> (end - start) ;
260 if (profile) std::cout <<
"Time elapsed: " << elapsed.count() << std::endl;
263 DigitalSet thin_set(image.domain());
264 vc_new.dumpVoxels(thin_set);
265 const auto & all_set = image_set;
267 if (outputFilenameSDP !=
"")
270 out.open(outputFilenameSDP.c_str());
271 for (
auto &p : thin_set)
273 out << p[0] <<
" " << p[1] <<
" " << p[2] << std::endl;
277 if (outputFilenameImg !=
"")
280 std::cout <<
"outputFilename" << outputFilenameImg << std::endl;
282 unsigned int foreground_value = 255;
283 if (useInputImgToExp){
284 for(
auto p: image.domain()){image.setValue(p, 0);}
285 ImageFromSet<Image>::append(image, thin_set, foreground_value);
286 image >> outputFilenameImg;
288 auto thin_image = ImageFromSet<Image>::create(thin_set, foreground_value,
false, useInputImgToExp);
289 thin_image >> outputFilenameImg;
295 polyscope::options::programName =
"DGtalTools: criticalKernelsThinning3D";
296 polyscope::view::setNavigateStyle(polyscope::NavigateStyle::Free);
297 PolyscopeViewer<> viewer( ks );
299 viewer.drawColor(Color(255, 255, 255, 255));
303 viewer.drawColor(Color(40, 200, 55, 10));