Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for expressing Mesh as RealMask #21

Open
ctrueden opened this issue Jul 11, 2019 · 1 comment
Open

Add support for expressing Mesh as RealMask #21

ctrueden opened this issue Jul 11, 2019 · 1 comment

Comments

@ctrueden
Copy link
Member

ctrueden commented Jul 11, 2019

The imglib2-roi library has a RealMask API for working with ROIs.

If we have a Mesh that is closed and convex, we can express this as a 3D RealMask by triangulating the convex hull and then testing points for inclusion by walking the triangles.

@ctrueden
Copy link
Member Author

ctrueden commented Jul 11, 2019

Here is a proof-of-concept implementation that uses VisAD. You can add the org.scijava:visad:2.0.0-scijava-1 artifact to your Maven dependencies.

Mesh-to-RealMask

import java.util.function.BiConsumer;

import net.imagej.Dataset;
import net.imagej.ImageJ;
import net.imagej.mesh.Mesh;
import net.imagej.mesh.Vertex;
import net.imglib2.Localizable;
import net.imglib2.RandomAccessible;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.RealLocalizable;
import net.imglib2.RealPoint;
import net.imglib2.RealRandomAccessible;
import net.imglib2.position.FunctionRandomAccessible;
import net.imglib2.roi.Masks;
import net.imglib2.roi.RealMask;
import net.imglib2.type.logic.BitType;
import net.imglib2.type.logic.BoolType;
import net.imglib2.view.Views;

import visad.Irregular3DSet;
import visad.RealTupleType;
import visad.RealType;
import visad.VisADException;

public class MeshToRealMask {

	public static void main(String[] args) throws Throwable {
		ImageJ ij = new ImageJ();

		// Generate a silly binary image.
		BiConsumer<Localizable, BitType> function = (l, t) -> {
			long x = l.getLongPosition(0);
			long y = l.getLongPosition(1);
			long z = l.getLongPosition(2);
			t.set(
				x + y > 50 && x + y < 80 ||
				x + z > 70 && x + z < 110
			);
		};
		FunctionRandomAccessible<BitType> fra = //
			new FunctionRandomAccessible<>(3, function, () -> new BitType());
		long[] min = {0, 0, 0};
		long[] max = {256, 256, 64};
		RandomAccessibleInterval<BitType> image = Views.interval(fra, min, max);
		Dataset dataset = ij.dataset().create(image);
		System.out.println("Dataset created: " + dataset);

		// Compute mesh.
		Mesh mesh = ij.op().geom().marchingCubes(image);
		System.out.println("Mesh computed");

		// Compute convex hull.
		Mesh hull = (Mesh) ij.op().geom().convexHull(mesh).get(0);
		System.out.println("Hull computed");

		// Compute Delaunay triangulation.
		long vertexCount = hull.vertices().size();
		if (vertexCount > Integer.MAX_VALUE) throw new RuntimeException("Hull too large");
		float[][] samples = new float[3][(int) vertexCount];
		int i = 0;
		for (Vertex vertex : hull.vertices()) {
			samples[0][i] = vertex.xf();
			samples[1][i] = vertex.yf();
			samples[2][i] = vertex.zf();
			i++;
		}
		RealType xType = RealType.getRealType("X");
		RealType yType = RealType.getRealType("Y");
		RealType zType = RealType.getRealType("Z");
		RealType[] xyz = {xType, yType, zType};
		RealTupleType xyzType = new RealTupleType(xyz);
		Irregular3DSet set = new Irregular3DSet(xyzType, samples);
		System.out.println("Triangulation computed");

		// Wrap as a RealMask.
		RealMask roi = new RealMask() {
			private ThreadLocal<float[][]> value = new ThreadLocal<float[][]>() {
				@Override
				public float[][] initialValue() {
					return new float[3][1];
				}
			};

			@Override
			public boolean test(RealLocalizable t) {
				float[][] v = value.get();
				try {
					v[0][0] = t.getFloatPosition(0);
					v[1][0] = t.getFloatPosition(1);
					v[2][0] = t.getFloatPosition(2);
					int[] tri = set.valueToTri(v);
					return tri[0] >= 0;
				}
				catch (VisADException exc) {
					throw new RuntimeException(exc);
				}
			}

			@Override
			public int numDimensions() { return 3; }
		};
		System.out.println("ROI created: " + roi);

		class Tester {
			private RealPoint point = new RealPoint(3);
			public void inside(double... p) {
				point.setPosition(p[0], 0);
				point.setPosition(p[1], 1);
				point.setPosition(p[2], 2);
				System.out.println(point + " -> " + roi.test(point));
			}
		}
		Tester t = new Tester();
		System.out.println("Testing some points:");
		t.inside(1, 2, 3);
		t.inside(50, 45, 40);
		t.inside(100, 55, 10);

		// Display the results.
		ij.ui().showUI();
//		ij.get(ROIService.class).add(roi, dataset);
		ij.ui().show("Dataset", dataset);

		// Use ImageJ1 to display the ROI. :-(
//		ImagePlus imp = IJ.getImage();
//		Roi ij1Roi = ij.convert().convert(roi, Roi.class);
//		System.out.println("IJ1 roi = " + ij1Roi);
//		imp.setRoi(ij1Roi);

		// Use BDV to display the ROI. :-/
//		RealRandomAccessible<BoolType> maskRA = Masks.toRealRandomAccessible(roi);
//		BdvFunctions.show(maskRA, dataset, "ROI");

		// Wrap the ROI in a RandomAccessibleInterval and display that.
		RealRandomAccessible<BoolType> roiRA = Masks.toRealRandomAccessible(roi);
		RandomAccessible<BoolType> roiRaster = Views.raster(roiRA);
		RandomAccessibleInterval<BoolType> roiRAI = Views.interval(roiRaster, dataset);
		ij.ui().show("ROI", roiRAI);
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant