Search in sources :

Example 1 with ShapeListCached

use of mpicbg.imglib.container.shapelist.ShapeListCached in project TrakEM2 by trakem2.

the class AreaUtils method manyToManyInterpolation.

public static final Area[] manyToManyInterpolation(final Area a1, final Area a2, final int nInterpolates) throws InterruptedException, ExecutionException {
    final Rectangle b = a1.getBounds();
    b.add(a2.getBounds());
    final AffineTransform translate = new AffineTransform(1, 0, 0, 1, -b.x, -b.y);
    final ShapeList<BitType> shapeList1 = new ShapeListCached<BitType>(new int[] { b.width, b.height }, new BitType(false), 32);
    shapeList1.addShape(a1.createTransformedArea(translate), new BitType(true), new int[] { 0 });
    final Image<BitType> img1 = new Image<BitType>(shapeList1, shapeList1.getBackground(), "ShapeListContainer");
    final ShapeList<BitType> shapeList2 = new ShapeListCached<BitType>(new int[] { b.width, b.height }, new BitType(false), 32);
    shapeList2.addShape(a2.createTransformedArea(translate), new BitType(true), new int[] { 0 });
    final Image<BitType> img2 = new Image<BitType>(shapeList2, shapeList2.getBackground(), "ShapeListContainer");
    final float inc = 1.0f / (nInterpolates + 1);
    final BinaryInterpolation2D interpol = new BinaryInterpolation2D(img1, img2, inc);
    if (!interpol.checkInput()) {
        System.out.println("Error: " + interpol.getErrorMessage());
        return null;
    }
    final Area[] as = new Area[nInterpolates];
    final AffineTransform back = new AffineTransform(1, 0, 0, 1, b.x, b.y);
    // TODO parallelize, which needs the means to call process() in parallel too--currently it cannot,
    // the result would get overwritten.
    final ExecutorService exec = Executors.newFixedThreadPool(Math.min(nInterpolates, Runtime.getRuntime().availableProcessors()));
    final ArrayList<Future<Area>> fus = new ArrayList<Future<Area>>();
    try {
        for (int i = 1; i <= nInterpolates; i++) {
            final float weight = 1 - inc * i;
            fus.add(exec.submit(new Callable<Area>() {

                @Override
                public Area call() throws Exception {
                    final Image<BitType> imb = interpol.process(weight);
                    final ImagePlus imp = ImageJFunctions.copyToImagePlus(imb, ImagePlus.GRAY8);
                    // BitType gets copied to 0 and 255 in 8-bit ByteProcessor
                    final ThresholdToSelection ts = new ThresholdToSelection();
                    ts.setup("", imp);
                    final ImageProcessor ip = imp.getProcessor();
                    ip.setThreshold(1, 255, ImageProcessor.NO_LUT_UPDATE);
                    ts.run(ip);
                    final Roi roi = imp.getRoi();
                    return null == roi ? new Area() : M.getArea(roi).createTransformedArea(back);
                }
            }));
        }
        int i = 0;
        for (final Future<Area> fu : fus) {
            as[i++] = fu.get();
        }
    } catch (final Throwable t) {
        IJError.print(t);
    } finally {
        exec.shutdown();
    }
    return as;
}
Also used : Rectangle(java.awt.Rectangle) ArrayList(java.util.ArrayList) ShapeListCached(mpicbg.imglib.container.shapelist.ShapeListCached) Image(mpicbg.imglib.image.Image) ImagePlus(ij.ImagePlus) Roi(ij.gui.Roi) Callable(java.util.concurrent.Callable) BinaryInterpolation2D(ini.trakem2.imaging.BinaryInterpolation2D) ImageProcessor(ij.process.ImageProcessor) Area(java.awt.geom.Area) BitType(mpicbg.imglib.type.logic.BitType) ExecutorService(java.util.concurrent.ExecutorService) AffineTransform(java.awt.geom.AffineTransform) Future(java.util.concurrent.Future) ThresholdToSelection(ij.plugin.filter.ThresholdToSelection)

Example 2 with ShapeListCached

use of mpicbg.imglib.container.shapelist.ShapeListCached in project TrakEM2 by trakem2.

the class AreaUtils method generateTriangles.

/**
 * Expects areas in local coordinates to the Displayable @param d.
 *  @param d
 *  @param scale The scaling of the entire universe, to limit the overall box
 *  @param resample_ The optimization parameter for marching cubes (i.e. a value of 2 will scale down to half, then apply marching cubes, then scale up by 2 the vertices coordinates).
 *  @param areas
 *  @return The List of triangles involved, specified as three consecutive vertices. A list of Point3f vertices.
 */
public static List<Point3f> generateTriangles(final Displayable d, final double scale, final int resample_, final Map<Layer, Area> areas) {
    // in the LayerSet, layers are ordered by Z already.
    try {
        int n = areas.size();
        if (0 == n)
            return null;
        final int resample;
        if (resample_ <= 0) {
            resample = 1;
            Utils.log2("Fixing zero or negative resampling value to 1.");
        } else
            resample = resample_;
        final LayerSet layer_set = d.getLayerSet();
        final AffineTransform aff = d.getAffineTransformCopy();
        final Rectangle r = d.getBoundingBox(null);
        // remove translation from a copy of the Displayable's AffineTransform
        final AffineTransform at_translate = new AffineTransform();
        at_translate.translate(-r.x, -r.y);
        aff.preConcatenate(at_translate);
        // incorporate resampling scaling into the transform
        final AffineTransform atK = new AffineTransform();
        // Utils.log("resample: " + resample + "  scale: " + scale);
        // 'scale' is there to limit gigantic universes
        final double K = (1.0 / resample) * scale;
        atK.scale(K, K);
        aff.preConcatenate(atK);
        final Calibration cal = layer_set.getCalibrationCopy();
        // Find first layer, compute depth, and fill in the depth vs area map
        Layer first_layer = null, last_layer = null;
        final int w = (int) Math.ceil(r.width * K);
        final int h = (int) Math.ceil(r.height * K);
        int depth = 0;
        final Map<Integer, Area> ma = new HashMap<Integer, Area>();
        for (final Layer la : layer_set.getLayers()) {
            // layers sorted by Z ASC
            final Area area = areas.get(la);
            if (null != area) {
                ma.put(depth, area);
                if (null == first_layer) {
                    first_layer = la;
                }
                // Utils.log("area at depth " + depth + " for layer " + la);
                depth++;
                n--;
            } else if (0 != depth) {
                // Utils.log("Empty area at depth " + depth);
                // an empty layer
                depth++;
            }
            if (0 == n) {
                last_layer = la;
                // no more areas to paint
                break;
            }
        }
        if (0 == depth) {
            Utils.log("ERROR could not find any areas for " + d);
            return null;
        }
        if (0 != n) {
            Utils.log("WARNING could not find all areas for " + d);
        }
        // No zero-padding: Marching Cubes now can handle edges
        final ShapeList<ByteType> shapeList = new ShapeListCached<ByteType>(new int[] { w, h, depth }, new ByteType(), 32);
        final Image<ByteType> shapeListImage = new Image<ByteType>(shapeList, shapeList.getBackground(), "ShapeListContainer");
        // 255 or -1 don't work !? So, giving the highest value (127) that is both a byte and an int.
        final ByteType intensity = new ByteType((byte) 127);
        for (final Map.Entry<Integer, Area> e : ma.entrySet()) {
            Area a = e.getValue();
            if (!aff.isIdentity()) {
                a = M.areaInIntsByRounding(a.createTransformedArea(aff));
            }
            shapeList.addShape(a, intensity, new int[] { e.getKey() });
        }
        // debug:
        // ImagePlus imp = ImageJFunctions.displayAsVirtualStack(shapeListImage);
        // imp.getProcessor().setMinAndMax( 0, 255 );
        // imp.show();
        // Utils.log2("Using imglib Shape List Image Container");
        // Now marching cubes
        // origins at 0,0,0: uncalibrated
        final List<Point3f> list = new MCTriangulator().getTriangles(shapeListImage, 1, new float[3]);
        // The list of triangles has coordinates:
        // - in x,y: in pixels, scaled by K = (1 / resample) * scale,
        // translated by r.x, r.y (the top-left coordinate of this AreaList bounding box)
        // - in z: in stack slice indices
        // So all x,y,z must be corrected in x,y and z of the proper layer
        // final double offset = first_layer.getZ();
        final int i_first_layer = layer_set.indexOf(first_layer);
        // The x,y translation to correct each point by:
        final float dx = (float) (r.x * scale * cal.pixelWidth);
        final float dy = (float) (r.y * scale * cal.pixelHeight);
        // Correct x,y by resampling and calibration, but not scale
        // scale is already in the pixel coordinates
        final float rsw = (float) (resample * cal.pixelWidth);
        final float rsh = (float) (resample * cal.pixelHeight);
        // no resampling in Z. and Uses pixelWidth, not pixelDepth.
        final double sz = scale * cal.pixelWidth;
        // debug:
        /*
			// which p.z types exist?
			final TreeSet<Float> ts = new TreeSet<Float>();
			for (final Iterator it = list.iterator(); it.hasNext(); ) {
				ts.add(((Point3f)it.next()).z);
			}
			for (final Float pz : ts) Utils.log2("A z: " + pz);
			*/
        // debug: How many different Z?
        /*
			HashSet<Float> zs = new HashSet<Float>();
			for (Point3f p : list) {
				zs.add(p.z);
			}
			ArrayList<Float> a = new ArrayList<Float>(zs);
			java.util.Collections.sort(a);
			for (Float f : a) {
				Utils.log("f: " + f);
			}
			*/
        // Utils.log2("Number of slices: " + imp.getNSlices());
        // Fix all points:
        // Read from list, modify and put into verts
        // and don't modify it if the verts already has it (it's just coincident)
        final Point3f[] verts = new Point3f[list.size()];
        // Utils.log("number of verts: " + verts.length + " mod 3: " + (verts.length % 3));
        final TreeMap<Integer, Point3f> output = new TreeMap<Integer, Point3f>();
        // The first section generates vertices at -1 and 0
        // The last section generates them at last_section_index and last_section_index +1
        // Capture from -1 to 0
        fix3DPoints(list, output, verts, first_layer.getZ(), 0, -1, dx, dy, rsw, rsh, sz, 1);
        int slice_index = 0;
        for (final Layer la : layer_set.getLayers().subList(i_first_layer, i_first_layer + depth)) {
            // If layer is empty, continue
            /* // YEAH don't! At least the immediate next layer would have points, like the extra Z level after last layer, to account for the thickness of the layer!
				if (empty_layers.contains(la)) {
					slice_index++;
					continue;
				}
				*/
            fix3DPoints(list, output, verts, la.getZ(), la.getThickness(), slice_index, dx, dy, rsw, rsh, sz, 1);
            slice_index++;
        }
        // Do the last layer again. The last layer has two Z planes in which it has pixels:
        try {
            // Capture from last_section_index to last_section_index+1, inclusive
            fix3DPoints(list, output, verts, last_layer.getZ() + last_layer.getThickness(), 0, slice_index, dx, dy, rsw, rsh, sz, 2);
        } catch (final Exception ee) {
            IJError.print(ee);
        }
        // Handle potential errors:
        if (0 != list.size() - output.size()) {
            Utils.log2("Unprocessed/unused points: " + (list.size() - output.size()));
            for (int i = 0; i < verts.length; i++) {
                if (null == verts[i]) {
                    final Point3f p = (Point3f) list.get(i);
                    Utils.log2("verts[" + i + "] = " + p.x + ", " + p.y + ", " + p.z + "  p.z as int: " + ((int) (p.z + 0.05f)));
                }
            }
            return new ArrayList<Point3f>(output.values());
        } else {
            return java.util.Arrays.asList(verts);
        }
    } catch (final Exception e) {
        e.printStackTrace();
    }
    return null;
}
Also used : HashMap(java.util.HashMap) Rectangle(java.awt.Rectangle) ArrayList(java.util.ArrayList) ShapeListCached(mpicbg.imglib.container.shapelist.ShapeListCached) ByteType(mpicbg.imglib.type.numeric.integer.ByteType) Image(mpicbg.imglib.image.Image) Point3f(org.scijava.vecmath.Point3f) LayerSet(ini.trakem2.display.LayerSet) Calibration(ij.measure.Calibration) TreeMap(java.util.TreeMap) Layer(ini.trakem2.display.Layer) ExecutionException(java.util.concurrent.ExecutionException) Area(java.awt.geom.Area) AffineTransform(java.awt.geom.AffineTransform) HashMap(java.util.HashMap) Map(java.util.Map) TreeMap(java.util.TreeMap)

Aggregations

Rectangle (java.awt.Rectangle)2 AffineTransform (java.awt.geom.AffineTransform)2 Area (java.awt.geom.Area)2 ArrayList (java.util.ArrayList)2 ShapeListCached (mpicbg.imglib.container.shapelist.ShapeListCached)2 Image (mpicbg.imglib.image.Image)2 ImagePlus (ij.ImagePlus)1 Roi (ij.gui.Roi)1 Calibration (ij.measure.Calibration)1 ThresholdToSelection (ij.plugin.filter.ThresholdToSelection)1 ImageProcessor (ij.process.ImageProcessor)1 Layer (ini.trakem2.display.Layer)1 LayerSet (ini.trakem2.display.LayerSet)1 BinaryInterpolation2D (ini.trakem2.imaging.BinaryInterpolation2D)1 HashMap (java.util.HashMap)1 Map (java.util.Map)1 TreeMap (java.util.TreeMap)1 Callable (java.util.concurrent.Callable)1 ExecutionException (java.util.concurrent.ExecutionException)1 ExecutorService (java.util.concurrent.ExecutorService)1