30 #include <DGtal/base/Common.h>
31 #include <DGtal/io/readers/GenericReader.h>
32 #include <DGtal/io/writers/GenericWriter.h>
33 #include <DGtal/helpers/StdDefs.h>
34 #include <DGtal/images/Image.h>
35 #include <DGtal/images/ImageContainerBySTLVector.h>
36 #include <DGtal/images/imagesSetsUtils/SetFromImage.h>
37 #include <DGtal/geometry/volumes/distance/DistanceTransformation.h>
38 #include <DGtal/math/Statistic.h>
40 #include <boost/program_options/options_description.hpp>
41 #include <boost/program_options/parsers.hpp>
42 #include <boost/program_options/variables_map.hpp>
47 using namespace DGtal;
50 namespace po = boost::program_options;
131 isDiff(
const Image3D &imageA,
int aMin,
int aMax,
const Image3D &imageB,
132 int bMin,
int bMax,
const Point &pt){
133 bool isRefOn = (imageA(pt)<= aMax) && (imageA(pt) >= aMin);
134 bool isCompOn = (imageB(pt)<= bMax) && (imageB(pt) >= bMin);
135 return ((!isRefOn && isCompOn) || (isRefOn && !isCompOn));
142 const Image3D & imageB,
int bMin,
int bMax,
143 bool statOnFalsePositiveOnly=
false,
Point *ptMax=0){
153 const NegPredicate aPredicate( set3dRef );
154 DTL2 dtL2( imageA.domain(), aPredicate, Z3i::l2Metric );
160 unsigned int nbAdded=0;
164 if((!statOnFalsePositiveOnly) || (isDiff(imageA, aMin, aMax, imageB, bMin, bMax, *it))){
165 DTL2::Value distance = dtL2(*it);
168 if(maxDist<distance){
171 (*ptMax)[0]=(*it)[0];
172 (*ptMax)[1]=(*it)[1];
173 (*ptMax)[2]=(*it)[2];
180 trace.
error() <<
"No point added to statistics, will failed..." << endl;
186 getVoxelsStats(
const Image3D &imageA,
int aMin,
int aMax,
const Image3D &imageB,
187 int bMin,
int bMax,
bool exportStatVoxels, std::vector<Point> &vectPtBinA,
188 std::vector<Point> &vectPtCompBinCompA, std::vector<Point> &vectPtBnotInA,
189 std::vector<Point> &vectPtnotBInA,
bool precisionRecallFMean ){
191 int numCompBinCompA = 0;
196 int numTotalinCompA = 0;
197 int numTotalinCompB = 0;
201 if(imageA(*it) <= aMax && imageA(*it) >= aMin){
204 if(imageB(*it) <= bMax && imageB(*it) >= bMin){
207 if(exportStatVoxels) vectPtBinA.push_back(*it);
211 if(exportStatVoxels) vectPtnotBInA.push_back(*it);
217 if(imageB(*it) <= bMax && imageB(*it) >= bMin){
220 if(exportStatVoxels) vectPtBnotInA.push_back(*it);
224 if(exportStatVoxels) vectPtCompBinCompA.push_back(*it);
229 std::cout << numBinA <<
" " << numCompBinCompA <<
" " << numBnotInA
230 <<
" " << numNotBinA <<
" " << numTotalInA <<
" "
231 << numTotalInB <<
" " << numTotalinCompA <<
" " << numTotalinCompB;
232 if(precisionRecallFMean){
233 double precision = (double)numBinA/(numBinA + numBnotInA);
234 double recall = (double)numBinA/(numBinA+numNotBinA);
235 double fmean = (2.0*precision*recall)/(precision+recall);
236 std::cout <<
" " << precision<<
" " << recall <<
" " << fmean ;
247 getVoxelsStats(
const Image3D &imageA,
int aMin,
int aMax,
const Image3D &imageB,
248 int bMin,
int bMax,
bool precisionRecallFMean ){
249 std::vector<Point> v1, v2, v3, v4;
250 return getVoxelsStats(imageA, aMin, aMax, imageB, bMin, bMax,
false, v1, v2, v3, v4, precisionRecallFMean);
258 exportSetofPoints(
string filename, std::vector<Point> aVectPoint){
260 ofs.open(filename.c_str(), std::ofstream::out );
261 ofs<<
"# Set of 3d points with format: x y z" << std::endl;
262 for (
unsigned int i =0; i< aVectPoint.size(); i++){
263 ofs << aVectPoint.at(i)[0] <<
" " << aVectPoint.at(i)[1] <<
" "<< aVectPoint.at(i)[2] << std::endl;
274 int main(
int argc,
char**argv)
281 po::options_description general_opt (
"Allowed options are: " );
282 general_opt.add_options()
283 (
"help,h",
"display this message." )
284 (
"volA,a", po::value<std::string>(),
"Input filename of volume A (vol format, and other pgm3d can also be used)." )
285 (
"volB,b", po::value<std::string>(),
"Input filename of volume B (vol format, and other pgm3d can also be used)." )
286 (
"aMin", po::value<int>()->default_value(0),
"min threshold for a voxel to be considered as belonging to the object of volume A. (default 0)" )
287 (
"aMax", po::value<int>()->default_value(128),
"max threshold for a voxel to be considered as belonging to the object of volume A. (default 128)" )
288 (
"bMin", po::value<int>()->default_value(0),
"min threshold for a voxel to be considered as belonging to the object of volume B. (default 0)" )
289 (
"bMax", po::value<int>()->default_value(128),
"max threshold for a voxel to be considered as belonging to the object of volume B. (default 128)" )
290 (
"noDistanceComparisons",
"to avoid to apply distance map computation if the distance comparaison are not needed.")
291 (
"distancesFromBnotInAOnly",
"apply distance map measures only for voxels of B which are not in A (else the measure are given from all distances of the object B).")
292 (
"displayTFstats",
"Change the comparison diplay by using the true/false/positive/negative notation and considering the shape A as reference. It also display precision/recall/f-mean statistics.")
293 (
"exportSDP",
"Export voxels belonging to each categorie (voxels of ( B in A) , (NOT in B and NOT in A), (B and NOT in A) and (Voxels of NOT in B and in A)). ") ;
295 po::variables_map vm;
297 po::store(po::parse_command_line(argc, argv, general_opt), vm);
298 }
catch(
const std::exception& ex){
300 trace.
info()<<
"Error checking program options: "<< ex.what()<< endl;
304 if ( !parseOK || vm.count (
"help" ) || ! vm.count(
"volA")||! vm.count(
"volB") )
306 trace.
info() <<
"Apply shape measures for comparing two volumetric images A and B (shape defined from thresholds). " << std::endl
307 <<
"It can compute:" << std::endl
308 <<
" - voxel count from voxel partition (number of voxel from (B-A), (A-B) ...): usefull to determine classical statistics like false positive related stats."<<std::endl
309 <<
" - euclidean distance between two volumetric images A and B " << std::endl
310 << std::endl <<
"Basic usage: "<< std::endl
311 <<
"\t volShapeMetrics --volA <volAFilename> --volB <volBFilename> "<< std::endl
312 << general_opt <<
"\n"
313 <<
"Typical use :\n volShapeMetrics -a imageA.vol --aMin 128 --aMax 255 -b imageB.vol --bMin 128 --bMax 255 --distancesFromBnotInAOnly \n" ;
318 if(! vm.count(
"volA")||! vm.count(
"volB"))
320 trace.
error() <<
" The two volume filename are needed to be defined" << endl;
324 std::string volAFilename = vm[
"volA"].as<std::string>();
325 std::string volBFilename = vm[
"volB"].as<std::string>();
327 int aMin = vm[
"aMin"].as<
int>();
328 int aMax = vm[
"aMax"].as<
int>();
329 int bMin = vm[
"bMin"].as<
int>();
330 int bMax = vm[
"bMax"].as<
int>();
336 std::cout <<
"# Shape comparisons (generated with volShapeMetrics) given with the reference shape A: "<< volAFilename
337 <<
" (defined with threshold min: " << aMin <<
" and max: " << aMax <<
" )"<< endl
338 <<
"# and with the compared shape B: "<< volBFilename <<
" (defined with threshold min: " << bMin <<
" and max: " << bMax <<
" )"<< endl;
339 if(vm.count(
"displayTFstats")){
340 std::cout <<
"# #True_Positive #TrueNegative #FalsePositive #FalseNegative #TotalinA #TotalInB #TotalComplementOfRef #TotalComplementOfComp Precision Recall F-Mean ";
342 std::cout <<
"# #(Voxels of B in A) #(Voxels of NOT in B and NOT in A) #(Voxels of B and NOT in A) #(Voxels of NOT in B and in A) #(Voxels in A) #(Voxels in B) #(Voxels not in A) #(Voxels not in B) ";
346 if(!vm.count(
"noDistanceComparisons")){
347 std::cout <<
" Max(MinDistance(shape B to shape A) Mean(MinDistance(shape B to shape A) Variance(MinDistance(shape B to shape A)) "
348 <<
" Mediane(MinDistance(shape B to shape A) Farthest point of B to A ";
349 if(vm.count(
"distancesFromBnotInAOnly")){
350 std::cout <<
"*** for parts of B which are not in A only ***" ;
353 std::cout << std::endl;
356 if(vm.count(
"exportSDP")){
357 std::vector<Point> voxelsBinA, voxelsNotInBNotInA, voxelsBNotInA, voxelsNotInBInA;
358 if(vm.count(
"displayTFstats")){
359 getVoxelsStats(imageA, aMin, aMax, imageB, bMin, bMax,
true, voxelsBinA, voxelsNotInBNotInA, voxelsBNotInA, voxelsNotInBInA,
true);
360 exportSetofPoints(
"truePos.sdp", voxelsBinA);
361 exportSetofPoints(
"trueNeg.sdp", voxelsNotInBNotInA);
362 exportSetofPoints(
"falsePos.sdp", voxelsBNotInA);
363 exportSetofPoints(
"falseNeg.sdp", voxelsNotInBInA);
365 std::vector<Point> voxelsBinA, voxelsNotInBNotInA, voxelsBNotInA, voxelsNotInBInA;
366 getVoxelsStats(imageA, aMin, aMax, imageB, bMin, bMax,
true, voxelsBinA, voxelsNotInBNotInA, voxelsBNotInA, voxelsNotInBInA,
false);
367 exportSetofPoints(
"inBinA.sdp", voxelsBinA);
368 exportSetofPoints(
"notinBnotinA.sdp", voxelsNotInBNotInA);
369 exportSetofPoints(
"inBnotinA.sdp", voxelsBNotInA);
370 exportSetofPoints(
"notinBinA.sdp", voxelsNotInBInA);
373 getVoxelsStats(imageA, aMin, aMax, imageB, bMin, bMax, vm.count(
"displayTFstats"));
377 if(!vm.count(
"noDistanceComparisons")){
378 trace.
info() <<
"Computing Distance Map stats ...";
381 getStatsFromDistanceMap(statDistances, imageA, aMin, aMax, imageB, bMin, bMax, vm.count(
"statsFromFalsePosOnly"), &ptMax );
382 std::cout <<
" " << statDistances.max() <<
" " << statDistances.mean() <<
" " << statDistances.variance() <<
" "<< statDistances.median() <<
" " << ptMax[0] <<
" " << ptMax[1] <<
" " << ptMax[2] << std::endl;
void addValue(Quantity v)
Trace trace(traceWriterTerm)
const Domain & domain() const
typename Self::Point Point
std::vector< Value >::const_iterator ConstIterator
Container::const_iterator ConstIterator