Skip to content

Commit

Permalink
First pass at voxelization test
Browse files Browse the repository at this point in the history
  • Loading branch information
kephale committed Sep 14, 2023
1 parent 7c6c029 commit 0267b05
Showing 1 changed file with 89 additions and 15 deletions.
104 changes: 89 additions & 15 deletions src/test/java/net/imagej/ops/geom/MeshFeatureTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,18 @@
import net.imagej.mesh.Triangle;
import net.imagej.ops.Ops;
import net.imagej.ops.features.AbstractFeatureTest;
import net.imagej.ops.geom.geom3d.DefaultBoxivityMesh;
import net.imagej.ops.geom.geom3d.DefaultCompactness;
import net.imagej.ops.geom.geom3d.DefaultConvexityMesh;
import net.imagej.ops.geom.geom3d.DefaultMainElongation;
import net.imagej.ops.geom.geom3d.DefaultMarchingCubes;
import net.imagej.ops.geom.geom3d.DefaultMedianElongation;
import net.imagej.ops.geom.geom3d.DefaultSolidityMesh;
import net.imagej.ops.geom.geom3d.DefaultSparenessMesh;
import net.imagej.ops.geom.geom3d.DefaultSphericity;
import net.imagej.ops.geom.geom3d.DefaultSurfaceArea;
import net.imagej.ops.geom.geom3d.DefaultSurfaceAreaConvexHullMesh;
import net.imagej.ops.geom.geom3d.DefaultVerticesCountConvexHullMesh;
import net.imagej.ops.geom.geom3d.DefaultVerticesCountMesh;
import net.imagej.ops.geom.geom3d.DefaultVolumeConvexHullMesh;
import net.imagej.ops.geom.geom3d.DefaultVolumeMesh;
import net.imagej.ops.geom.geom3d.*;
import net.imagej.ops.morphology.fillHoles.DefaultFillHoles;
import net.imagej.ops.morphology.floodFill.DefaultFloodFill;
import net.imglib2.Cursor;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.img.Img;
import net.imglib2.img.array.ArrayImgs;
import net.imglib2.roi.labeling.LabelRegion;
import net.imglib2.type.logic.BitType;
import net.imglib2.type.numeric.real.DoubleType;

import net.imglib2.view.Views;
import org.junit.BeforeClass;
import org.junit.Test;

Expand Down Expand Up @@ -199,8 +193,88 @@ public void verticesCountMesh() {

}

/**
* Creates a 3D binary image of a sphere.
*
* @param r The radius of the sphere.
* @return A RandomAccessibleInterval representing the sphere.
*/
public RandomAccessibleInterval<BitType> generateSphere(int r) {
long[] dims = new long[] {-r, r, -r, r, -r, r}; // Dimensions of the bounding box of the sphere
Img<BitType> sphereImg = ArrayImgs.bits(dims);

Cursor<BitType> cursor = sphereImg.localizingCursor();

// Center of the sphere
int cx = r;
int cy = r;
int cz = r;

while (cursor.hasNext()) {
cursor.fwd();
int x = cursor.getIntPosition(0) - cx;
int y = cursor.getIntPosition(1) - cy;
int z = cursor.getIntPosition(2) - cz;

if (x * x + y * y + z * z <= r * r) {
cursor.get().set(true);
}
}

return sphereImg;
}

public long compareImages(RandomAccessibleInterval<BitType> img1, RandomAccessibleInterval<BitType> img2) {
long diff = 0;
Cursor<BitType> cursor1 = Views.iterable(img1).cursor();
Cursor<BitType> cursor2 = Views.iterable(img2).cursor();

while (cursor1.hasNext() && cursor2.hasNext()) {
cursor1.fwd();
cursor2.fwd();

if (!cursor1.get().valueEquals(cursor2.get())) {
diff++;
}
}
return diff;
}

@Test
public void voxelization3D() {
// https://github.com/imagej/imagej-ops/issues/422
RandomAccessibleInterval<BitType> sphere = generateSphere(20);
final Mesh result = (Mesh) ops.run(DefaultMarchingCubes.class, sphere);
assertEquals(mesh.triangles().size(), result.triangles().size());
final Iterator<Triangle> expectedFacets = mesh.triangles().iterator();
final Iterator<Triangle> actualFacets = result.triangles().iterator();
while (expectedFacets.hasNext() && actualFacets.hasNext()) {
final Triangle expected = expectedFacets.next();
final Triangle actual = actualFacets.next();
assertEquals(expected.v0x(), actual.v0x(), EPSILON);
assertEquals(expected.v0y(), actual.v0y(), EPSILON);
assertEquals(expected.v0z(), actual.v0z(), EPSILON);
assertEquals(expected.v1x(), actual.v1x(), EPSILON);
assertEquals(expected.v1y(), actual.v1y(), EPSILON);
assertEquals(expected.v1z(), actual.v1z(), EPSILON);
assertEquals(expected.v2x(), actual.v2x(), EPSILON);
assertEquals(expected.v2y(), actual.v2y(), EPSILON);
assertEquals(expected.v2z(), actual.v2z(), EPSILON);
}
assertTrue(!expectedFacets.hasNext() && !actualFacets.hasNext());

// The mesh is good by now, let's check the voxelization
RandomAccessibleInterval<BitType> voxelization = (RandomAccessibleInterval<BitType>) ops.run(DefaultVoxelization3D.class, result);

// Flood fill (ops implementation starts from borders)
RandomAccessibleInterval<BitType> filledVoxelization = (RandomAccessibleInterval<BitType>) ops.run(DefaultFillHoles.class, voxelization);

// Compare inverted image
// Comparison
long diff = compareImages(sphere, filledVoxelization);
long total = ROI.size();

assertTrue("Voxelization does not match the original image closely enough.", diff / (double) total < 0.07);

}
}

0 comments on commit 0267b05

Please sign in to comment.