2929package net .imglib2 .mesh ;
3030
3131import net .imglib2 .mesh .alg .InertiaTensor ;
32+ import net .imglib2 .type .numeric .real .DoubleType ;
3233import org .apache .commons .math3 .geometry .euclidean .threed .Vector3D ;
3334
3435import net .imglib2 .RealPoint ;
3536import net .imglib2 .mesh .alg .hull .ConvexHull ;
3637import net .imglib2 .mesh .util .MeshUtil ;
38+ import org .apache .commons .math3 .linear .BlockRealMatrix ;
3739import org .apache .commons .math3 .linear .EigenDecomposition ;
3840import org .apache .commons .math3 .linear .RealMatrix ;
3941
@@ -47,7 +49,7 @@ public class MeshStats
4749 * Computes the volume of the specified mesh.
4850 *
4951 * @return the volume in physical units.
50- * @implNote op names='geom.size', label='Geometric (2D ): Size ',
52+ * @implNote op names='geom.size', label='Geometric (3D ): Volume ',
5153 * priority='9999.'
5254 */
5355 public static double volume ( final Mesh mesh )
@@ -84,6 +86,18 @@ public static double volume( final Mesh mesh )
8486 return Math .abs ( sum );
8587 }
8688
89+ /**
90+ * Computes the volume of the convex hull of the specified mesh.
91+ *
92+ * @param mesh a {@link Mesh}
93+ * @return the volume in physical units.
94+ * @implNote op names='geom.sizeConvexHull', label='Geometric (3D): Convex Hull Volume',
95+ * priority='9999.'
96+ */
97+ public static double convexHullVolume (final Mesh mesh ) {
98+ return volume (ConvexHull .calculate (mesh ));
99+ }
100+
87101 /**
88102 * Returns the surface area of a mesh.
89103 * <p>
@@ -113,6 +127,18 @@ public static double surfaceArea( final Mesh mesh )
113127 return total ;
114128 }
115129
130+ /**
131+ * Computes the surface area of the convex hull of the specified mesh.
132+ *
133+ * @param mesh a {@link Mesh}
134+ * @return the surface area in physical units.
135+ * @implNote op names='geom.boundarySizeConvexHull', label='Geometric (3D): Convex Hull Surface Area',
136+ * priority='9999.'
137+ */
138+ public static double convexHullSurfaceArea (final Mesh mesh ) {
139+ return surfaceArea (ConvexHull .calculate (mesh ));
140+ }
141+
116142 /**
117143 * Returns the solidity of a mesh.
118144 * <p>
@@ -256,7 +282,7 @@ public static double compactness( final Mesh mesh )
256282 * @param input
257283 * the input mesh.
258284 * @return the centroid of the mesh.
259- * @implNote op names='geom.centroid ', priority='10000.'
285+ * @implNote op names='geom.centerOfGravity ', priority='10000.'
260286 */
261287 public static RealPoint centroid ( final Mesh input )
262288 {
@@ -314,6 +340,127 @@ public static RealPoint centroid( final Mesh input )
314340 return new RealPoint ( m100 / v , m010 / v , m001 / v );
315341 }
316342
343+ /**
344+ * Describes the elogation of a {@link Mesh}. As elongation is a metric of a
345+ * two-dimensional object, this functionality describes elongation as a
346+ * {@link RealMatrix}, where each index {@code x,y} is the elongation
347+ * considering principal axes {@code x} and {@code y}.
348+ *
349+ * @param input a {@link Mesh}
350+ * @return the elongation of each subset of principal axes of {@code input}
351+ * @implNote op names='geom.elongation', label='Geometric (3D): Elongation', priority='10000.'
352+ */
353+ public static RealMatrix elongation (final Mesh input ) {
354+ final RealMatrix it = InertiaTensor .calculate (input );
355+ final EigenDecomposition ed = new EigenDecomposition (it );
356+
357+ final double l1 = ed .getRealEigenvalue (0 ) - ed .getRealEigenvalue (2 ) + ed
358+ .getRealEigenvalue (1 );
359+ final double l2 = ed .getRealEigenvalue (0 ) - ed .getRealEigenvalue (1 ) + ed
360+ .getRealEigenvalue (2 );
361+ final double l3 = ed .getRealEigenvalue (2 ) - ed .getRealEigenvalue (0 ) + ed
362+ .getRealEigenvalue (1 );
363+
364+ final double g = 1 / (8 * Math .PI / 15 );
365+
366+ final double a = Math .pow (g * l1 * l1 / Math .sqrt (l2 * l3 ), 1 / 5d );
367+ final double b = Math .pow (g * l2 * l2 / Math .sqrt (l1 * l3 ), 1 / 5d );
368+ final double c = Math .pow (g * l3 * l3 / Math .sqrt (l1 * l2 ), 1 / 5d );
369+ final BlockRealMatrix out = new BlockRealMatrix (3 , 3 );
370+ out .setRow (0 , new double [] {elongation (a , a ), elongation (a , b ), elongation (a , c )});
371+ out .setRow (1 , new double [] {elongation (b , a ), elongation (b , b ), elongation (b , c )});
372+ out .setRow (2 , new double [] {elongation (c , a ), elongation (c , b ), elongation (c , c )});
373+ return out ;
374+ }
375+
376+ private static double elongation (double major , double minor ) {
377+ // Ensure elongation contained in [0, 1]
378+ if (minor > major ) {
379+ double tmp = minor ;
380+ minor = major ;
381+ major = tmp ;
382+ }
383+ return 1 - (minor / major );
384+ }
385+
386+ /**
387+ * Describes the elongation of the two <b>larger</b> principle axes of the
388+ * minimum box containing a {@link Mesh}. Provides functionality equivalent
389+ * to the {@code "geom.mainElongation"} Op of ImageJ Ops.
390+ *
391+ * @param input a {@link Mesh}
392+ * @return the elongation of a cross-section containing the two <b>larger</b> principle axes
393+ * @implNote op names='geom.mainElongation', label='Geometric (3D): Main Elongation', priority='10000.'
394+ */
395+ public static double mainElongation (final Mesh input ){
396+ return elongation (input ).getEntry (0 , 1 );
397+ }
398+
399+ /**
400+ * Describes the elongation of the two <b>smaller</b> principle axes of the
401+ * minimum box containing a {@link Mesh}. Provides functionality equivalent
402+ * to the {@code "geom.medianElongation"} Op of ImageJ Ops.
403+ *
404+ * @param input a {@link Mesh}
405+ * @return the elongation of a cross-section containing the two <b>smaller</b> principle axes
406+ * @implNote op names='geom.medianElongation', label='Geometric (3D): Median Elongation', priority='10000.'
407+ */
408+ public static double medianElongation (final Mesh input ) {
409+ return elongation (input ).getEntry (1 , 2 );
410+ }
411+
412+ /**
413+ * Describes the number of vertices on the surface of a {@link Mesh}.
414+ *
415+ * @param input a {@link Mesh}
416+ * @return the number of vertices in {@code input}
417+ * @implNote op names='geom.verticesCount', label='Geometric (3D): Surface Vertices Count', priority='10000.'
418+ */
419+ public static long verticesCount (final Mesh input ) {
420+ return input .vertices ().size ();
421+ }
422+
423+ /**
424+ * Describes the number of vertices on the surface of the convex hull of {@link Mesh}.
425+ *
426+ * @param input a {@link Mesh}
427+ * @return the number of vertices in the convex hull of {@code input}
428+ * @implNote op names='geom.verticesCountConvexHull', label='Geometric (3D): Convex Hull Vertices Count', priority='10000.'
429+ */
430+ public static long convexHullVerticesCount (final Mesh input ) {
431+ return ConvexHull .calculate (input ).vertices ().size ();
432+ }
433+
434+
435+ /**
436+ * Describes the spareness of {@code geom.spareness}. Based on ImageJ.
437+ *
438+ * @author Tim-Oliver Buchholz (University of Konstanz)
439+ * @implNote op names='geom.spareness', label='Geometric (3D): Spareness',
440+ * priority='10000.'
441+ */
442+ public static double spareness (final Mesh input ) {
443+ final RealMatrix it = InertiaTensor .calculate (input );
444+ final EigenDecomposition ed = new EigenDecomposition (it );
445+
446+ final double l1 = ed .getRealEigenvalue (0 ) - ed .getRealEigenvalue (2 ) + ed
447+ .getRealEigenvalue (1 );
448+ final double l2 = ed .getRealEigenvalue (0 ) - ed .getRealEigenvalue (1 ) + ed
449+ .getRealEigenvalue (2 );
450+ final double l3 = ed .getRealEigenvalue (2 ) - ed .getRealEigenvalue (0 ) + ed
451+ .getRealEigenvalue (1 );
452+
453+ final double g = 1 / (8 * Math .PI / 15 );
454+
455+ final double a = Math .pow (g * l1 * l1 / Math .sqrt (l2 * l3 ), 1 / 5d );
456+ final double b = Math .pow (g * l2 * l2 / Math .sqrt (l1 * l3 ), 1 / 5d );
457+ final double c = Math .pow (g * l3 * l3 / Math .sqrt (l1 * l2 ), 1 / 5d );
458+
459+ double volumeEllipsoid = (4 / 3d * Math .PI * a * b * c );
460+
461+ return volume (input ) / volumeEllipsoid ;
462+ }
463+
317464 private MeshStats ()
318465 {}
319466}
0 commit comments