Computing 3D surface area of binary object using imageJ-ops

This question is related to

I think that Knime Feature Calculator 3D uses imageJ ops to compute the surface area. In Knime I see an issue as it does not seem to account for the calibration in XYZ.

How is it done in the imageJ ops? Does it uses the calibration?

I would like to test this and there is the ops geom3d.DefaultSurfaceArea that requires a mesh. Could someone of you give a short example on how I could generate the mesh?



The marchingCubes op generates a mesh from a BooleanType image (e.g. a thresholded image).

Here a Groovy script illustrating this:

#@ OpService ops
#@ Dataset input

thresholded = ops.threshold().otsu(input)
mesh = ops.geom().marchingCubes(thresholded)
surfaceArea = ops.geom().boundarySize(mesh)

println surfaceArea

No, it doesn’t seem to respect pixel spacing calibration. I just tested with the Bat Cochlea Volume sample image by changing the image properties before running the above script.


Hello Michael (@mdoube),
in your BoneJ2 you use the ops for the surface area computation. I think it does not uses the Z calibration either. Probably it is not possible when using a mesh. At least you give a warning.

So there is no real IJ2 way of computing the surface area of an object that has not been acquired isotropically? One could reslice but for binary it is not that nice or downsize.

Thanks for your help


1 Like

I have to defer to @rimadoma on this point, who did the implementation in BoneJ2. IIRC in BoneJ 1.x anisotropic pixel spacing was respected during surface meshing by the 3D Viewer’s methods.

1 Like

@apoliti You’re correct, Surface area in BoneJ2 doesn’t use calibration info, because it uses the ops mentioned in @imagejan’s reply above.

@mdoube Potentially we could take the Mesh that comes out from the marching cubes op, and then in Surface area apply calibration info when calculating the areas of the triangles ourselves like here. I didn’t know that old 3D Viewer already applied calibration to the meshes it created.

1 Like

Perhaps the best solution would be to add an overloaded or new calibratedMesh() (or something) method to the Op that takes the pixel spacing as input and returns a mesh with points in real rather than pixel space?

I suspect that for most users this would be the preferred default behaviour.

1 Like

Hi all,

I’ve hit this issue some time ago (4 years, omg) and wrote a method which modifies a mesh by multiplying the point coordinates with the voxel size. Unfortunately, the Mesh-API broke later on and that’s why I’m not sure if the code still works. But it could be a starting point, instead of implementing something from scratch :wink:

I hope that helps.


1 Like

Hm, I guess DefaultMarchingCubes could be modified to have an additional

@Parameter(type = ItemIO.INPUT, required = false)
private CalibratedAxis[] axes;

parameter. The op could then apply the scaling if the array was not null.

1 Like

I started a PR for adding calibration to the meshes from DefaultMarchingCubes. We can discuss the particulars of the implementation there.