/** * Creates an image of cube edges, similar to this: * * <p>*---------* ** ** * * * * *********** * | * | * | *------|--* | / | * |/ |* *********** * * <p>Typical planes are as follow: z = 0 z=1,2,3 z = 4 X * * * * . . . . . * * * * * . . . . * . * . . . . * . . . * . . . . * . . . . . * . . . * . . . . * . . . . . * . . . * Z . . . * * . . . * * * . . . * * * <p>(reconstruction starts from the X, and terminates at the Z) */ private ImageStack createThinCubicMeshImage() { int sizeX = 5; int sizeY = 5; int sizeZ = 5; int bitDepth = 8; // create empty stack ImageStack stack = ImageStack.create(sizeX, sizeY, sizeZ, bitDepth); // First, the edges in the x direction for (int x = 0; x < 5; x++) { stack.setVoxel(x, 0, 0, 255); stack.setVoxel(x, 0, 4, 255); } // then, the edges in the y direction for (int y = 0; y < 5; y++) { stack.setVoxel(4, y, 0, 255); stack.setVoxel(0, y, 4, 255); stack.setVoxel(4, y, 4, 255); } // Finally, the edges in the z direction for (int z = 0; z < 5; z++) { stack.setVoxel(0, 4, z, 255); stack.setVoxel(4, 4, z, 255); } return stack; }
private ImageStack createLeveledCubeGraphImage() { ImageStack stack = createCubeGraphImage(); stack.setVoxel(5, 1, 1, 224); stack.setVoxel(9, 5, 1, 192); stack.setVoxel(9, 9, 5, 160); stack.setVoxel(9, 5, 9, 128); stack.setVoxel(5, 1, 9, 96); stack.setVoxel(1, 5, 9, 64); stack.setVoxel(1, 9, 5, 32); return stack; }
@Test public final void testRegionalMaxima_CubicMeshC26() { // load the reference image, and get its size ImageStack image = createCubicMeshImage(); int sizeX = image.getWidth(); int sizeY = image.getHeight(); int sizeZ = image.getSize(); // create test image: add a band with value 127 in the middle int zMid = sizeZ / 2; for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { double val = image.getVoxel(x, y, zMid); if (val == 255) image.setVoxel(x, y, zMid, 127); } } ImageStack maxima = MinimaAndMaxima3D.regionalMaxima(image, 26); for (int z = 0; z < sizeZ; z++) { for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { int v0 = (int) image.getVoxel(x, y, z); int v = (int) maxima.getVoxel(x, y, z); if (v0 == 255) assertEquals(255, v); else assertEquals(0, v); } } } }
@Test public final void testErosionCubicMeshC6() { ImageStack mask = createCubicMeshImage(); invertGray8Stack(mask); ImageStack marker = ImageStack.create(20, 20, 20, 8); marker.setVoxel(5, 5, 5, 255); invertGray8Stack(marker); GeodesicReconstruction3DHybrid0Gray8 algo = new GeodesicReconstruction3DHybrid0Gray8(); algo.setReconstructionType(GeodesicReconstructionType.BY_EROSION); algo.setConnectivity(6); ImageStack result = algo.applyTo(marker, mask); assertEquals(0, result.getVoxel(5, 15, 5), .01); assertEquals(255, result.getVoxel(0, 0, 0), .01); int sizeX = mask.getWidth(); int sizeY = mask.getHeight(); int sizeZ = mask.getSize(); for (int z = 0; z < sizeZ; z++) { for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { assertEquals(result.getVoxel(x, y, z), mask.getVoxel(x, y, z), .01); } } } }
@Test public final void testDilationCochleaVolumeC6() { String fileName = getClass().getResource("/files/bat-cochlea-volume.tif").getFile(); ImagePlus imagePlus = IJ.openImage(fileName); assertNotNull(imagePlus); assertTrue(imagePlus.getStackSize() > 0); ImageStack mask = imagePlus.getStack(); // Ensure regularity of the mask mask = Morphology.opening(mask, CubeStrel.fromRadius(1)); int width = mask.getWidth(); int height = mask.getHeight(); int depth = mask.getSize(); int bitDepth = mask.getBitDepth(); ImageStack marker = ImageStack.create(width, height, depth, bitDepth); marker.setVoxel(20, 80, 50, 255); GeodesicReconstruction3DHybrid0Gray8 algo = new GeodesicReconstruction3DHybrid0Gray8(); algo.setConnectivity(6); algo.verbose = false; ImageStack result = algo.applyTo(marker, mask); for (int z = 0; z < depth; z++) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { assertEquals(result.getVoxel(x, y, z), mask.getVoxel(x, y, z), .01); } } } }
@Test public final void testDilationHilbertCurveC26() { String fileName = getClass().getResource("/files/hilbert3d.tif").getFile(); ImagePlus imagePlus = IJ.openImage(fileName); assertNotNull(imagePlus); assertTrue(imagePlus.getStackSize() > 0); ImageStack mask = imagePlus.getStack(); int width = mask.getWidth(); int height = mask.getHeight(); int depth = mask.getSize(); int bitDepth = mask.getBitDepth(); ImageStack marker = ImageStack.create(width, height, depth, bitDepth); marker.setVoxel(3, 0, 0, 255); GeodesicReconstruction3DHybrid0Gray8 algo = new GeodesicReconstruction3DHybrid0Gray8(); algo.setConnectivity(26); algo.verbose = false; ImageStack result = algo.applyTo(marker, mask); for (int z = 0; z < depth; z++) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { assertEquals(result.getVoxel(x, y, z), mask.getVoxel(x, y, z), .01); } } } }
@Test public final void testApplyTo() { GeodesicReconstructionByDilation3DGray8 algo = new GeodesicReconstructionByDilation3DGray8(); String fileName = getClass().getResource("/files/bat-cochlea-volume.tif").getFile(); ImagePlus imagePlus = IJ.openImage(fileName); assertNotNull(imagePlus); assertTrue(imagePlus.getStackSize() > 0); ImageStack mask = imagePlus.getStack(); int width = mask.getWidth(); int height = mask.getHeight(); int depth = mask.getSize(); int bitDepth = mask.getBitDepth(); ImageStack marker = ImageStack.create(width, height, depth, bitDepth); marker.setVoxel(20, 80, 50, 255); ImageStack result = algo.applyTo(marker, mask); for (int z = 0; z < depth; z++) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { assertEquals(result.getVoxel(x, y, z), mask.getVoxel(x, y, z), .01); } } } }
private ImageStack createInvertedLeveledCubeGraphImage() { ImageStack stack = createCubeGraphImage(); for (int z = 0; z < stack.getSize(); z++) { for (int y = 0; y < stack.getHeight(); y++) { for (int x = 0; x < stack.getWidth(); x++) { stack.setVoxel(x, y, z, 255 - stack.getVoxel(x, y, z)); } } } stack.setVoxel(5, 1, 1, 32); stack.setVoxel(9, 5, 1, 64); stack.setVoxel(9, 9, 5, 96); stack.setVoxel(9, 5, 9, 128); stack.setVoxel(5, 1, 9, 160); stack.setVoxel(1, 5, 9, 192); stack.setVoxel(1, 9, 5, 224); return stack; }
@Test public final void testDilationCubicHollowMesh() { ImageStack mask = createCubicHollowMeshImage(); ImageStack marker = ImageStack.create(20, 20, 20, 8); for (int z = 0; z < 20; z++) { for (int y = 0; y < 20; y++) { for (int x = 0; x < 20; x++) { marker.setVoxel(x, y, z, 255); } } } marker.setVoxel(5, 5, 5, 0); GeodesicReconstruction3DHybrid0Gray8 algo = new GeodesicReconstruction3DHybrid0Gray8(); ImageStack result = algo.applyTo(marker, mask); assertEquals(0, result.getVoxel(5, 15, 5), .01); assertStackEquals(mask, result); }
private final void invertGray8Stack(ImageStack image) { int sizeX = image.getWidth(); int sizeY = image.getHeight(); int sizeZ = image.getSize(); for (int z = 0; z < sizeZ; z++) { for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { image.setVoxel(x, y, z, 255 - image.getVoxel(x, y, z)); } } } }
/** Creates a 3D image containing thin cube mesh. */ private ImageStack createCubeGraphImage() { int sizeX = 11; int sizeY = 11; int sizeZ = 11; int bitDepth = 8; // create empty stack ImageStack stack = ImageStack.create(sizeX, sizeY, sizeZ, bitDepth); // coordinates of the cube edges int x1 = 1; int x2 = 9; int y1 = 1; int y2 = 9; int z1 = 1; int z2 = 9; // First, the edges in the x direction for (int x = x1; x <= x2; x++) { stack.setVoxel(x, y1, z1, 255); stack.setVoxel(x, y1, z2, 255); } // then, the edges in the y direction for (int y = y1; y <= y2; y++) { stack.setVoxel(x2, y, z1, 255); stack.setVoxel(x1, y, z2, 255); stack.setVoxel(x2, y, z2, 255); } // Finally, the edges in the z direction for (int z = z1; z <= z2; z++) { stack.setVoxel(x1, y2, z, 255); stack.setVoxel(x2, y2, z, 255); } return stack; }
@Test public final void testDilationThinCubicMeshC26() { ImageStack mask = createThinCubicMeshImage(); ImageStack marker = ImageStack.create(5, 5, 5, 8); marker.setVoxel(0, 0, 0, 255); GeodesicReconstruction3DHybrid0Gray8 algo = new GeodesicReconstruction3DHybrid0Gray8(); algo.setConnectivity(26); ImageStack result = algo.applyTo(marker, mask); assertEquals(255, result.getVoxel(0, 4, 0), .01); }
@Test public final void testErosionCochleaVolumeC26() { String fileName = getClass().getResource("/files/bat-cochlea-volume.tif").getFile(); ImagePlus imagePlus = IJ.openImage(fileName); assertNotNull(imagePlus); assertTrue(imagePlus.getStackSize() > 0); ImageStack mask = imagePlus.getStack(); // Ensure regularity of the mask mask = Morphology.opening(mask, CubeStrel.fromRadius(1)); invertGray8Stack(mask); int width = mask.getWidth(); int height = mask.getHeight(); int depth = mask.getSize(); int bitDepth = mask.getBitDepth(); ImageStack marker = ImageStack.create(width, height, depth, bitDepth); marker.setVoxel(20, 80, 50, 255); invertGray8Stack(marker); GeodesicReconstruction3DHybrid0Gray8 algo = new GeodesicReconstruction3DHybrid0Gray8(GeodesicReconstructionType.BY_EROSION); algo.setConnectivity(26); ImageStack result = algo.applyTo(marker, mask); for (int z = 0; z < depth; z++) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (Math.abs(result.getVoxel(x, y, z) - mask.getVoxel(x, y, z)) > .1) { System.out.println("x=" + x + " y=" + y + " z=" + z); System.out.println(" mask = " + (int) mask.getVoxel(x, y, z)); System.out.println(" res = " + (int) result.getVoxel(x, y, z)); assertTrue(false); } } } } }
private ImageStack createCubicMeshImage() { int sizeX = 20; int sizeY = 20; int sizeZ = 20; int bitDepth = 8; // create empty stack ImageStack stack = ImageStack.create(sizeX, sizeY, sizeZ, bitDepth); // number of voxels between edges and 'tube' borders int gap = 2; // First, the edges in the x direction for (int z = 5 - gap - 1; z <= 5 + gap + 1; z++) { for (int y = 5 - gap - 1; y <= 5 + gap + 1; y++) { for (int x = 5 - gap - 1; x <= 15 + gap + 1; x++) { stack.setVoxel(x, y, z, 255); stack.setVoxel(x, y, z + 10, 255); } } } // then, the edges in the y direction for (int z = 5 - gap - 1; z <= 5 + gap + 1; z++) { for (int x = 5 - gap - 1; x <= 5 + gap + 1; x++) { for (int y = 5 - gap - 1; y <= 15 + gap + 1; y++) { stack.setVoxel(x + 10, y, z, 255); stack.setVoxel(x, y, z + 10, 255); stack.setVoxel(x + 10, y, z + 10, 255); } } } // Finally, the edges in the z direction for (int y = 5 - gap - 1; y <= 5 + gap + 1; y++) { for (int x = 5 - gap - 1; x <= 5 + gap + 1; x++) { for (int z = 5 - gap - 1; z <= 15 + gap + 1; z++) { stack.setVoxel(x, y + 10, z, 255); stack.setVoxel(x + 10, y + 10, z, 255); } } } return stack; }
@Test public final void testRegionalMaxima_BatCochlea() { String fileName = getClass().getResource("/files/bat-cochlea-volume.tif").getFile(); ImagePlus imagePlus = IJ.openImage(fileName); assertNotNull(imagePlus); assertTrue(imagePlus.getStackSize() > 0); // load the reference image, and get its size ImageStack image = imagePlus.getStack(); int sizeX = image.getWidth(); int sizeY = image.getHeight(); int sizeZ = image.getSize(); // create test image: // use cochlea volume, but add a band with value 127 in the middle int zMid = sizeZ / 2; for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { double val = image.getVoxel(x, y, zMid); if (val == 255) image.setVoxel(x, y, zMid, 127); } } ImageStack maxima = MinimaAndMaxima3D.regionalMaxima(image); for (int z = 0; z < sizeZ; z++) { for (int y = 0; y < sizeY; y++) { for (int x = 0; x < sizeX; x++) { int v0 = (int) image.getVoxel(x, y, z); int v = (int) maxima.getVoxel(x, y, z); if (v0 == 255) assertEquals(255, v); else assertEquals(0, v); } } } }
private ImageStack createCubicHollowMeshImage() { // create filled cubic mesh ImageStack stack = createCubicMeshImage(); // number of voxels between edges and 'tube' borders int gap = 2; // First, the edges in the x direction for (int z = 5 - gap; z <= 5 + gap; z++) { for (int y = 5 - gap; y <= 5 + gap; y++) { for (int x = 5 - gap; x <= 15 + gap; x++) { stack.setVoxel(x, y, z, 0); stack.setVoxel(x, y, z + 10, 0); } } } // then, the edges in the y direction for (int z = 5 - gap; z <= 5 + gap; z++) { for (int x = 5 - gap; x <= 5 + gap; x++) { for (int y = 5 - gap; y <= 15 + gap; y++) { stack.setVoxel(x + 10, y, z, 0); stack.setVoxel(x, y, z + 10, 0); stack.setVoxel(x + 10, y, z + 10, 0); } } } // Finally, the edges in the z direction for (int y = 5 - gap; y <= 5 + gap; y++) { for (int x = 5 - gap; x <= 5 + gap; x++) { for (int z = 5 - gap; z <= 15 + gap; z++) { stack.setVoxel(x, y + 10, z, 0); stack.setVoxel(x + 10, y + 10, z, 0); } } } return stack; }