Skip to content

Commit 6fbb363

Browse files
committed
MorphometricFeatures3D now populates table in the same order as features are specified
1 parent 21999a6 commit 6fbb363

1 file changed

Lines changed: 186 additions & 160 deletions

File tree

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

Lines changed: 186 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
import java.util.ArrayList;
3030
import java.util.Collection;
3131
import java.util.List;
32+
import java.util.stream.Stream;
3233

34+
import ij.IJ;
3335
import ij.ImagePlus;
3436
import ij.ImageStack;
3537
import ij.measure.Calibration;
@@ -65,54 +67,66 @@
6567
*/
6668
public class MorphometricFeatures3D extends AlgoStub
6769
{
68-
/**
69-
* An enumeration used to identify which features have to be
70-
* computed by the MorphometricFeatures3D class.
71-
*/
72-
public enum Feature
73-
{
74-
/** The number of voxels that compose the region.*/
75-
VOXEL_COUNT("Voxel_Count"),
76-
/** The volume occupied by the region, as the number of voxels multiplied by image resolution.*/
77-
VOLUME("Volume"),
78-
/** The surface area of the region (measured by Crofton formula) .*/
79-
SURFACE_AREA("Surface_Area"),
80-
/** The mean breadth, proportional to the integral of mean curvature .*/
81-
MEAN_BREADTH("Mean_Breadth"),
82-
/** The Euler number of the region, to quantify its topology.*/
83-
EULER_NUMBER("Euler_Number"),
84-
/** The sphericity, as normalized ratio of powers of volume and surface area.*/
85-
SPHERICITY("Sphericity"),
86-
/** The bounding box along each dimension.*/
87-
BOUNDING_BOX("Bounding_Box"),
88-
/** The centroid.*/
89-
CENTROID("Centroid"),
90-
/** The equivalent ellipsoid with same moments up to the second order as the region.*/
91-
EQUIVALENT_ELLIPSOID("Equivalent_Ellipsoid"),
92-
/** The elongation factor of the equivalent ellipsoid.*/
93-
ELLIPSOID_ELONGATIONS("Ellipsoid_Elong."),
94-
/** The radius of the largest inscribed ball.*/
95-
MAX_INSCRIBED_BALL("Max._Inscribed_Ball");
96-
97-
String label;
98-
99-
private Feature(String label)
100-
{
101-
this.label = label;
102-
}
103-
104-
/**
70+
/**
71+
* An enumeration used to identify which features have to be computed by the
72+
* MorphometricFeatures3D class.
73+
*/
74+
public enum Feature
75+
{
76+
/** The number of voxels that compose the region. */
77+
VOXEL_COUNT("Voxel_Count"),
78+
/**
79+
* The volume occupied by the region, as the number of voxels multiplied
80+
* by image resolution.
81+
*/
82+
VOLUME("Volume"),
83+
/** The surface area of the region (measured by Crofton formula) . */
84+
SURFACE_AREA("Surface_Area"),
85+
/**
86+
* The mean breadth, proportional to the integral of mean curvature .
87+
*/
88+
MEAN_BREADTH("Mean_Breadth"),
89+
/** The Euler number of the region, to quantify its topology. */
90+
EULER_NUMBER("Euler_Number"),
91+
/**
92+
* The sphericity, as normalized ratio of powers of volume and surface
93+
* area.
94+
*/
95+
SPHERICITY("Sphericity"),
96+
/** The bounding box along each dimension. */
97+
BOUNDING_BOX("Bounding_Box"),
98+
/** The centroid. */
99+
CENTROID("Centroid"),
100+
/**
101+
* The equivalent ellipsoid with same moments up to the second order as
102+
* the region.
103+
*/
104+
EQUIVALENT_ELLIPSOID("Equivalent_Ellipsoid"),
105+
/** The elongation factor of the equivalent ellipsoid. */
106+
ELLIPSOID_ELONGATIONS("Ellipsoid_Elong."),
107+
/** The radius of the largest inscribed ball. */
108+
MAX_INSCRIBED_BALL("Max._Inscribed_Ball");
109+
110+
String label;
111+
112+
private Feature(String label)
113+
{
114+
this.label = label;
115+
}
116+
117+
/**
105118
* Returns the label associated to this feature.
106119
*
107120
* @return the label associated to this feature.
108121
*/
109-
@Override
110-
public String toString()
111-
{
112-
return this.label;
113-
} };
114-
115-
122+
@Override
123+
public String toString()
124+
{
125+
return this.label;
126+
}
127+
};
128+
129+
116130
// ====================================================
117131
// Inner fields
118132

@@ -396,122 +410,134 @@ private ResultsTable populateTable(Results results)
396410
table.addLabel("" + results.labels[i]);
397411
}
398412

399-
if (features.contains(VOXEL_COUNT))
400-
{
401-
addColumnToTable(table, "VoxelCount", results.voxelCounts);
402-
}
403-
404-
if (features.contains(VOLUME))
405-
{
406-
for (int i = 0; i < nLabels; i++)
407-
{
408-
table.setValue("Volume", i, results.intrinsicVolumes[i].volume);
409-
}
410-
}
411-
412-
if (features.contains(SURFACE_AREA))
413-
{
414-
for (int i = 0; i < nLabels; i++)
415-
{
416-
table.setValue("SurfaceArea", i, results.intrinsicVolumes[i].surfaceArea);
417-
}
418-
}
419-
420-
if (features.contains(SPHERICITY))
421-
{
422-
double[] sphericities = new double[nLabels];
423-
for (int i = 0; i < nLabels; i++)
424-
{
425-
sphericities[i] = results.intrinsicVolumes[i].sphericity();
426-
}
427-
addColumnToTable(table, "Sphericity", sphericities);
428-
}
429-
430-
if (features.contains(MEAN_BREADTH))
431-
{
432-
for (int i = 0; i < nLabels; i++)
433-
{
434-
table.setValue("MeanBreadth", i, results.intrinsicVolumes[i].meanBreadth);
435-
}
436-
}
437-
438-
if (features.contains(EULER_NUMBER))
439-
{
440-
for (int i = 0; i < nLabels; i++)
441-
{
442-
table.setValue("EulerNumber", i, results.intrinsicVolumes[i].eulerNumber);
443-
}
444-
}
445-
446-
if (features.contains(BOUNDING_BOX))
447-
{
448-
for (int i = 0; i < nLabels; i++)
449-
{
450-
Box3D box = results.boundingBoxes[i];
451-
table.setValue("Box.X.Min", i, box.getXMin());
452-
table.setValue("Box.X.Max", i, box.getXMax());
453-
table.setValue("Box.Y.Min", i, box.getYMin());
454-
table.setValue("Box.Y.Max", i, box.getYMax());
455-
table.setValue("Box.Z.Min", i, box.getZMin());
456-
table.setValue("Box.Z.Max", i, box.getZMax());
457-
}
458-
}
459-
460-
if (features.contains(CENTROID))
461-
{
462-
for (int i = 0; i < nLabels; i++)
463-
{
464-
Point3D center = results.centroids[i];
465-
table.setValue("Centroid.X", i, center.getX());
466-
table.setValue("Centroid.Y", i, center.getY());
467-
table.setValue("Centroid.Z", i, center.getZ());
468-
}
469-
}
470-
471-
if (features.contains(EQUIVALENT_ELLIPSOID))
472-
{
473-
for (int i = 0; i < nLabels; i++)
474-
{
475-
Ellipsoid elli = results.ellipsoids[i];
476-
Point3D center = elli.center();
477-
table.setValue("Elli.Center.X", i, center.getX());
478-
table.setValue("Elli.Center.Y", i, center.getY());
479-
table.setValue("Elli.Center.Z", i, center.getZ());
480-
table.setValue("Elli.Radius1", i, elli.radius1());
481-
table.setValue("Elli.Radius2", i, elli.radius2());
482-
table.setValue("Elli.Radius3", i, elli.radius3());
483-
table.setValue("Elli.Azim", i, elli.phi());
484-
table.setValue("Elli.Elev", i, elli.theta());
485-
table.setValue("Elli.Roll", i, elli.psi());
486-
}
487-
}
488-
489-
if (features.contains(ELLIPSOID_ELONGATIONS))
490-
{
491-
for (int i = 0; i < nLabels; i++)
492-
{
493-
double[] elongs = results.ellipsoidElongations[i];
494-
table.setValue("Elli.R1/R2", i, elongs[0]);
495-
table.setValue("Elli.R1/R3", i, elongs[1]);
496-
table.setValue("Elli.R2/R3", i, elongs[2]);
497-
}
498-
}
499-
500-
if (features.contains(MAX_INSCRIBED_BALL))
501-
{
502-
for (int i = 0; i < nLabels; i++)
503-
{
504-
Point3D center = results.inscribedBalls[i].center();
505-
table.setValue("InscrBall.Center.X", i, center.getX());
506-
table.setValue("InscrBall.Center.Y", i, center.getY());
507-
table.setValue("InscrBall.Center.Z", i, center.getZ());
508-
table.setValue("InscrBall.Radius", i, results.inscribedBalls[i].radius());
509-
}
510-
}
511-
512-
this.fireStatusChanged(this, "");
513-
return table;
514-
}
413+
/**
414+
* Iterate over features, in the order they are specified.
415+
*/
416+
for (Feature feature : features)
417+
{
418+
switch (feature)
419+
{
420+
case VOXEL_COUNT:
421+
{
422+
addColumnToTable(table, "VoxelCount", results.voxelCounts);
423+
break;
424+
}
425+
case VOLUME:
426+
{
427+
double[] volumes = Stream.of(results.intrinsicVolumes)
428+
.mapToDouble(res -> res.volume)
429+
.toArray();
430+
addColumnToTable(table, "Volume", volumes);
431+
break;
432+
}
433+
case SURFACE_AREA:
434+
{
435+
double[] surfaces = Stream.of(results.intrinsicVolumes)
436+
.mapToDouble(res -> res.surfaceArea)
437+
.toArray();
438+
addColumnToTable(table, "SurfaceArea", surfaces);
439+
break;
440+
}
441+
case MEAN_BREADTH:
442+
{
443+
double[] meanBreadths = Stream.of(results.intrinsicVolumes)
444+
.mapToDouble(res -> res.meanBreadth)
445+
.toArray();
446+
addColumnToTable(table, "MeanBreadth", meanBreadths);
447+
break;
448+
}
449+
case SPHERICITY:
450+
{
451+
double[] sphericites = Stream.of(results.intrinsicVolumes)
452+
.mapToDouble(res -> res.sphericity())
453+
.toArray();
454+
addColumnToTable(table, "Sphericity", sphericites);
455+
break;
456+
}
457+
case BOUNDING_BOX:
458+
{
459+
for (int i = 0; i < nLabels; i++)
460+
{
461+
Box3D box = results.boundingBoxes[i];
462+
table.setValue("Box.X.Min", i, box.getXMin());
463+
table.setValue("Box.X.Max", i, box.getXMax());
464+
table.setValue("Box.Y.Min", i, box.getYMin());
465+
table.setValue("Box.Y.Max", i, box.getYMax());
466+
table.setValue("Box.Z.Min", i, box.getZMin());
467+
table.setValue("Box.Z.Max", i, box.getZMax());
468+
}
469+
break;
470+
}
471+
case CENTROID:
472+
{
473+
for (int i = 0; i < nLabels; i++)
474+
{
475+
Point3D center = results.centroids[i];
476+
table.setValue("Centroid.X", i, center.getX());
477+
table.setValue("Centroid.Y", i, center.getY());
478+
table.setValue("Centroid.Z", i, center.getZ());
479+
}
480+
break;
481+
}
482+
case EQUIVALENT_ELLIPSOID:
483+
{
484+
for (int i = 0; i < nLabels; i++)
485+
{
486+
Ellipsoid elli = results.ellipsoids[i];
487+
Point3D center = elli.center();
488+
table.setValue("Elli.Center.X", i, center.getX());
489+
table.setValue("Elli.Center.Y", i, center.getY());
490+
table.setValue("Elli.Center.Z", i, center.getZ());
491+
table.setValue("Elli.Radius1", i, elli.radius1());
492+
table.setValue("Elli.Radius2", i, elli.radius2());
493+
table.setValue("Elli.Radius3", i, elli.radius3());
494+
table.setValue("Elli.Azim", i, elli.phi());
495+
table.setValue("Elli.Elev", i, elli.theta());
496+
table.setValue("Elli.Roll", i, elli.psi());
497+
}
498+
break;
499+
}
500+
case ELLIPSOID_ELONGATIONS:
501+
{
502+
for (int i = 0; i < nLabels; i++)
503+
{
504+
double[] elongs = results.ellipsoidElongations[i];
505+
table.setValue("Elli.R1/R2", i, elongs[0]);
506+
table.setValue("Elli.R1/R3", i, elongs[1]);
507+
table.setValue("Elli.R2/R3", i, elongs[2]);
508+
}
509+
break;
510+
}
511+
case MAX_INSCRIBED_BALL:
512+
{
513+
for (int i = 0; i < nLabels; i++)
514+
{
515+
Point3D center = results.inscribedBalls[i].center();
516+
table.setValue("InscrBall.Center.X", i, center.getX());
517+
table.setValue("InscrBall.Center.Y", i, center.getY());
518+
table.setValue("InscrBall.Center.Z", i, center.getZ());
519+
table.setValue("InscrBall.Radius", i, results.inscribedBalls[i].radius());
520+
}
521+
break;
522+
}
523+
case EULER_NUMBER:
524+
{
525+
double[] eulerNumbers = Stream.of(results.intrinsicVolumes)
526+
.mapToDouble(res -> res.eulerNumber)
527+
.toArray();
528+
addColumnToTable(table, "EulerNumber", eulerNumbers);
529+
break;
530+
}
531+
default:
532+
{
533+
IJ.log("Unknown feature: " + feature.toString());
534+
}
535+
}
536+
}
537+
538+
this.fireStatusChanged(this, "");
539+
return table;
540+
}
515541

516542
private static final void addColumnToTable(ResultsTable table,
517543
String colName, double[] values)

0 commit comments

Comments
 (0)