3838import ij .measure .ResultsTable ;
3939import inra .ijpb .algo .AlgoStub ;
4040import inra .ijpb .algo .DefaultAlgoListener ;
41+ import inra .ijpb .binary .distmap .ChamferMask3D ;
4142import inra .ijpb .geometry .Box3D ;
4243import inra .ijpb .geometry .Ellipsoid ;
4344import inra .ijpb .geometry .Point3D ;
45+ import inra .ijpb .geometry .PointPair3D ;
4446import inra .ijpb .geometry .Sphere ;
4547import inra .ijpb .label .LabelImages ;
4648import inra .ijpb .util .Tables ;
@@ -105,8 +107,16 @@ public enum Feature
105107 EQUIVALENT_ELLIPSOID ("Equivalent_Ellipsoid" ),
106108 /** The elongation factor of the equivalent ellipsoid. */
107109 ELLIPSOID_ELONGATIONS ("Ellipsoid_Elong." ),
110+ /** The largest Feret diameter.*/
111+ MAX_FERET_DIAMETER ("Max._Feret_Diameter" ),
112+ /** The largest geodesic diameter within the region.*/
113+ GEODESIC_DIAMETER ("Geodesic_Diameter" ),
114+ /** The ratio of geodesic diameter over Feret diameter.*/
115+ TORTUOSITY ("Tortuosity" ),
108116 /** The radius of the largest inscribed ball. */
109- MAX_INSCRIBED_BALL ("Max._Inscribed_Ball" );
117+ MAX_INSCRIBED_BALL ("Max._Inscribed_Ball" ),
118+ /** The ratio of geodesic diameter over diameter of inscribed ball.*/
119+ GEODESIC_ELONGATION ("Geodesic_Elong." );
110120
111121 String label ;
112122
@@ -374,18 +384,36 @@ else if (contains(CENTROID))
374384 DefaultAlgoListener .monitor (algo );
375385 results .centroids = algo .analyzeRegions (image , labels , calib );
376386 }
377-
378- // compute position and radius of maximal inscribed ball
379- if (contains (MAX_INSCRIBED_BALL ))
380- {
381- this .fireStatusChanged (this , "Inscribed circles" );
382- LargestInscribedBall algo = new LargestInscribedBall ();
383- DefaultAlgoListener .monitor (algo );
384- results .inscribedBalls = algo .analyzeRegions (image , labels , calib );
385- }
386-
387- return results ;
388- }
387+
388+ // compute max Feret diameter
389+ if (containsAny (MAX_FERET_DIAMETER , TORTUOSITY ))
390+ {
391+ this .fireStatusChanged (this , "Max Feret Diameters" );
392+ MaxFeretDiameter3D algo = new MaxFeretDiameter3D ();
393+ DefaultAlgoListener .monitor (algo );
394+ results .maxFeretDiameters = algo .analyzeRegions (image , labels , calib );
395+ }
396+
397+ // compute geodesic diameter
398+ if (containsAny (GEODESIC_DIAMETER , TORTUOSITY , GEODESIC_ELONGATION ))
399+ {
400+ this .fireStatusChanged (this , "Max Feret Diameters" );
401+ GeodesicDiameter3D algo = new GeodesicDiameter3D (ChamferMask3D .SVENSSON_3_4_5_7 );
402+ DefaultAlgoListener .monitor (algo );
403+ results .geodesicDiameters = algo .analyzeRegions (image , labels , calib );
404+ }
405+
406+ // compute position and radius of maximal inscribed ball
407+ if (containsAny (MAX_INSCRIBED_BALL , GEODESIC_ELONGATION ))
408+ {
409+ this .fireStatusChanged (this , "Inscribed circles" );
410+ LargestInscribedBall algo = new LargestInscribedBall ();
411+ DefaultAlgoListener .monitor (algo );
412+ results .inscribedBalls = algo .analyzeRegions (image , labels , calib );
413+ }
414+
415+ return results ;
416+ }
389417
390418 /**
391419 * Creates the results table that summarizes the features computed on the
@@ -509,6 +537,32 @@ private ResultsTable populateTable(Results results)
509537 }
510538 break ;
511539 }
540+ case MAX_FERET_DIAMETER :
541+ {
542+ for (int i = 0 ; i < nLabels ; i ++)
543+ {
544+ table .setValue ("MaxFeretDiam" , i , results .maxFeretDiameters [i ].diameter ());
545+ }
546+ break ;
547+ }
548+ case GEODESIC_DIAMETER :
549+ {
550+ double [] diameter = Stream .of (results .geodesicDiameters )
551+ .mapToDouble (gdData -> gdData .diameter )
552+ .toArray ();
553+ Tables .addColumnToTable (table , "GeodesicDiameter" , diameter );
554+ break ;
555+ }
556+ case TORTUOSITY :
557+ {
558+ double [] tortuosity = new double [nLabels ];
559+ for (int i = 0 ; i < nLabels ; i ++)
560+ {
561+ tortuosity [i ] = results .geodesicDiameters [i ].diameter / results .maxFeretDiameters [i ].diameter ();
562+ }
563+ Tables .addColumnToTable (table , "Tortuosity" , tortuosity );
564+ break ;
565+ }
512566 case MAX_INSCRIBED_BALL :
513567 {
514568 for (int i = 0 ; i < nLabels ; i ++)
@@ -521,6 +575,16 @@ private ResultsTable populateTable(Results results)
521575 }
522576 break ;
523577 }
578+ case GEODESIC_ELONGATION :
579+ {
580+ double [] elong = new double [nLabels ];
581+ for (int i = 0 ; i < nLabels ; i ++)
582+ {
583+ elong [i ] = results .geodesicDiameters [i ].diameter / (results .inscribedBalls [i ].radius () * 2 );
584+ }
585+ Tables .addColumnToTable (table , "GeodesicElongation" , elong );
586+ break ;
587+ }
524588 case EULER_NUMBER :
525589 {
526590 double [] eulerNumbers = Stream .of (results .intrinsicVolumes )
@@ -559,6 +623,8 @@ private static final class Results
559623 public Point3D [] centroids = null ;
560624 public Ellipsoid [] ellipsoids = null ;
561625 public double [][] ellipsoidElongations = null ;
626+ public PointPair3D [] maxFeretDiameters = null ;
627+ public GeodesicDiameter3D .Result [] geodesicDiameters = null ;
562628 public Sphere [] inscribedBalls = null ;
563629 }
564630}
0 commit comments