Skip to content

Commit 6cc918c

Browse files
committed
AnalyzeRegions3D can now computes Max Feret Diameter, GeodesicDiameter, and related features (tortuosity, geodesic elongation)
1 parent ce879c7 commit 6cc918c

2 files changed

Lines changed: 85 additions & 13 deletions

File tree

‎src/main/java/inra/ijpb/measure/region3d/MorphometricFeatures3D.java‎

Lines changed: 79 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@
3838
import ij.measure.ResultsTable;
3939
import inra.ijpb.algo.AlgoStub;
4040
import inra.ijpb.algo.DefaultAlgoListener;
41+
import inra.ijpb.binary.distmap.ChamferMask3D;
4142
import inra.ijpb.geometry.Box3D;
4243
import inra.ijpb.geometry.Ellipsoid;
4344
import inra.ijpb.geometry.Point3D;
45+
import inra.ijpb.geometry.PointPair3D;
4446
import inra.ijpb.geometry.Sphere;
4547
import inra.ijpb.label.LabelImages;
4648
import 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
}

‎src/main/java/inra/ijpb/plugins/AnalyzeRegions3D.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,13 @@ public class AnalyzeRegions3D implements PlugIn
9090
featureList.add(Feature.EQUIVALENT_ELLIPSOID);
9191
featureList.add(Feature.ELLIPSOID_ELONGATIONS);
9292

93+
featureList.add(Feature.MAX_FERET_DIAMETER);
94+
featureList.add(Feature.GEODESIC_DIAMETER);
95+
9396
featureList.add(Feature.MAX_INSCRIBED_BALL);
97+
featureList.add(Feature.GEODESIC_ELONGATION);
98+
99+
featureList.add(Feature.TORTUOSITY);
94100
featureList.add(Feature.EULER_NUMBER);
95101
}
96102

0 commit comments

Comments
 (0)