|
| 1 | +============================= |
| 2 | +3D Analysis and Visualization |
| 3 | +============================= |
| 4 | + |
| 5 | +In this example, we will use SciJava Ops to construct a 3D mesh from a binary dataset, passing the result into `3D Viewer`_ for visualization. We use the `bat cochlea volume`_ dataset (more information `here <https://imagej.net/images/bat-cochlea-volume.txt>`_) from the ImageJ sample images, which users can either download using the link or open from the ``File → Open Samples → Bat Cochlea Volume`` menu selection within Fiji. |
| 6 | + |
| 7 | +.. figure:: https://media.scijava.org/scijava-ops/1.0.1/mesh-visualization.png |
| 8 | + |
| 9 | + **Left**: The original binary bat cochlea volume, displayed as an Image. **Right**: The convex hull generated by SciJava Ops, overlaid on the original binary bat cochlea volume. |
| 10 | + |
| 11 | + |
| 12 | +.. TODO: Update SciJava Ops Image -> imglib2-mesh |
| 13 | +
|
| 14 | +The following script accepts the binary dataset as its sole input, and creates the mesh using the `marching cubes`_ algorithm, which is included within SciJava Ops Image. We then use SciJava Ops to compute mesh volume, and then convert the mesh into a ``CustomTriangleMesh`` that can be passed to the 3DViewer. |
| 15 | + |
| 16 | +.. tabs:: |
| 17 | + |
| 18 | + .. code-tab:: scijava-groovy |
| 19 | + |
| 20 | + #@ OpEnvironment ops |
| 21 | + #@ UIService ui |
| 22 | + #@ Dataset image |
| 23 | + #@ ImagePlus imp |
| 24 | + #@ StatusService status |
| 25 | + |
| 26 | + import java.util.ArrayList |
| 27 | + import java.util.List |
| 28 | + |
| 29 | + import net.imagej.mesh.Mesh |
| 30 | + import net.imagej.mesh.Triangle |
| 31 | + import net.imglib2.RandomAccessibleInterval |
| 32 | + import net.imglib2.type.BooleanType |
| 33 | + import net.imglib2.util.Util |
| 34 | + |
| 35 | + import org.scijava.vecmath.Point3f |
| 36 | + |
| 37 | + import customnode.CustomTriangleMesh |
| 38 | + import ij3d.Image3DUniverse |
| 39 | + |
| 40 | + if (image.getType() instanceof BooleanType) { |
| 41 | + // Input image is a binary image. |
| 42 | + mask = image |
| 43 | + } |
| 44 | + else { |
| 45 | + // Binarize the image using Otsu's threshold. |
| 46 | + status.showStatus("Thresholding...") |
| 47 | + bitType = ops.op("create.bit").producer().create() |
| 48 | + mask = ops.op("create.img").input(image, bitType).apply() |
| 49 | + ops.op("threshold.otsu").input(image).output(mask).compute() |
| 50 | + } |
| 51 | + println("Mask = $mask [type=${Util.getTypeFromInterval(mask).getClass().getName()}]") |
| 52 | + |
| 53 | + //ui.show(mask) |
| 54 | + |
| 55 | + // Compute surface mesh using marching cubes. |
| 56 | + status.showStatus("Computing surface...") |
| 57 | + mesh = ops.op("geom.marchingCubes").input(mask).apply() |
| 58 | + println("mesh = ${mesh} [${mesh.triangles().size()} triangles, ${mesh.vertices().size()} vertices]") |
| 59 | + |
| 60 | + meshVolume = ops.op("geom.size").input(mesh).apply().getRealDouble() |
| 61 | + println("mesh volume = " + meshVolume) |
| 62 | + |
| 63 | + hull = ops.op("geom.convexHull").input(mesh).apply() |
| 64 | + println("hull = ${hull} [${hull.triangles().size()} triangles, ${hull.vertices().size()} vertices]") |
| 65 | + |
| 66 | + hullVolume = ops.op("geom.size").input(hull).apply().getRealDouble() |
| 67 | + println("hull volume = $hullVolume") |
| 68 | + |
| 69 | + // Display original image and meshes in 3D Viewer. |
| 70 | + |
| 71 | + def opsMeshToCustomMesh(opsMesh) { |
| 72 | + points = [] |
| 73 | + for (t in hull.triangles()) { |
| 74 | + points.add(new Point3f(t.v0xf(), t.v0yf(), t.v0zf())) |
| 75 | + points.add(new Point3f(t.v1xf(), t.v1yf(), t.v1zf())) |
| 76 | + points.add(new Point3f(t.v2xf(), t.v2yf(), t.v2zf())) |
| 77 | + } |
| 78 | + return new CustomTriangleMesh(points) |
| 79 | + } |
| 80 | + |
| 81 | + mesh_hull = opsMeshToCustomMesh(hull) |
| 82 | + println("Hull volume according to 3D Viewer: ${mesh_hull.getVolume()}"); |
| 83 | + |
| 84 | + univ = new Image3DUniverse() |
| 85 | + univ.addVoltex(imp, 1) |
| 86 | + univ.addCustomMesh(mesh_hull, "Convex Hull") |
| 87 | + univ.show() |
| 88 | + |
| 89 | +.. _3D Viewer: https://imagej.net/plugins/3d-viewer/ |
| 90 | +.. _bat cochlea volume: https://imagej.net/images/bat-cochlea-volume.zip |
| 91 | +.. _bat cochlea info: https://imagej.net/images/bat-cochlea-volume.txt |
| 92 | +.. _marching cubes: https://en.wikipedia.org/wiki/Marching_cubes |
0 commit comments