Search in sources :

Example 66 with Layer

use of ini.trakem2.display.Layer in project TrakEM2 by trakem2.

the class Project method findById.

public DBObject findById(final long id) {
    if (this.id == id)
        return this;
    DBObject dbo = layer_set.findById(id);
    if (null != dbo)
        return dbo;
    // could call findObject(id), but all objects must exist in layer sets anyway.
    dbo = root_pt.findChild(id);
    if (null != dbo)
        return dbo;
    return (DBObject) root_tt.findChild(id);
}
Also used : DBObject(ini.trakem2.persistence.DBObject)

Example 67 with Layer

use of ini.trakem2.display.Layer in project TrakEM2 by trakem2.

the class AreaList method measure.

/**
 * Returns a double array with 0=volume, 1=lower_bound_surface, 2=upper_bound_surface_smoothed, 3=upper_bound_surface, 4=max_diameter, 5=all_tops_and_bottoms
 *  All measures are approximate.
 *  [0] Volume: sum(area * thickness) for all sections
 *  [1] Lower Bound Surface: measure area per section, compute radius of circumference of identical area, compute then area of the sides of the truncated cone of height thickness, for each section. Plus top and bottom areas when visiting sections without a painted area.
 *  [2] Upper Bound Surface Smoothed: measure smoothed perimeter lengths per section, multiply by thickness to get lateral area. Plus tops and bottoms.
 *  [3] Upper Bound Surface: measure raw pixelated perimeter lengths per section, multiply by thickness to get lateral area. Plus top and bottoms.
 *  [4] Maximum diameter: longest distance between any two points in the contours of all painted areas.
 *  [5] All tops and bottoms: Sum of all included surface areas that are not part of side area.
 *  [6] X coordinate of the center of mass.
 *  [7] Y coordinate of the center of mass.
 *  [8] Z coordinate of the center of mass.
 */
public double[] measure() {
    // zeros
    if (0 == ht_areas.size())
        return new double[6];
    // prepare suitable transform
    final AffineTransform aff = new AffineTransform(this.at);
    AffineTransform aff2 = new AffineTransform();
    // remove translation (for no reason other than historical, and that it may
    // help avoid numerical overflows)
    final Rectangle box = getBoundingBox(null);
    aff2.translate(-box.x, -box.y);
    aff.preConcatenate(aff2);
    aff2 = null;
    double volume = 0;
    double lower_bound_surface_h = 0;
    double upper_bound_surface = 0;
    double upper_bound_surface_smoothed = 0;
    double prev_surface = 0;
    double prev_perimeter = 0;
    double prev_smooth_perimeter = 0;
    double prev_thickness = 0;
    // i.e. surface area that is not part of the side area
    double all_tops_and_bottoms = 0;
    final Calibration cal = layer_set.getCalibration();
    final double pixelWidth = cal.pixelWidth;
    final double pixelHeight = cal.pixelHeight;
    // Put areas in order of their layer index:
    final TreeMap<Integer, Area> ias = new TreeMap<Integer, Area>();
    for (final Map.Entry<Long, Area> e : ht_areas.entrySet()) {
        final int ilayer = layer_set.indexOf(layer_set.getLayer(e.getKey()));
        if (-1 == ilayer) {
            Utils.log("Could not find a layer with id " + e.getKey());
            continue;
        }
        ias.put(ilayer, e.getValue());
    }
    final ArrayList<Layer> layers = layer_set.getLayers();
    int last_layer_index = -1;
    final ArrayList<Point3f> points = new ArrayList<Point3f>();
    final float[] coords = new float[6];
    final float fpixelWidth = (float) pixelWidth;
    final float fpixelHeight = (float) pixelHeight;
    final float resampling_delta = project.getProperty("measurement_resampling_delta", 1.0f);
    // for each area, measure its area and its perimeter, to compute volume and surface
    for (final Map.Entry<Integer, Area> e : ias.entrySet()) {
        // fetch Layer
        final int layer_index = e.getKey();
        if (layer_index > layers.size()) {
            Utils.log("Could not find a layer at index " + layer_index);
            continue;
        }
        final Layer la = layers.get(layer_index);
        // fetch Area
        Area area = e.getValue();
        if (UNLOADED == area)
            area = loadLayer(la.getId());
        // Transform area to world coordinates
        area = area.createTransformedArea(aff);
        // measure surface
        final double pixel_area = Math.abs(AreaCalculations.area(area.getPathIterator(null)));
        final double surface = pixel_area * pixelWidth * pixelHeight;
        // Utils.log2(layer_index + " pixel_area: " + pixel_area + "  surface " + surface);
        // measure volume
        // the last one is NOT pixelDepth because layer thickness and Z are in pixels
        final double thickness = la.getThickness() * pixelWidth;
        volume += surface * thickness;
        final double pix_perimeter = AreaCalculations.circumference(area.getPathIterator(null));
        final double perimeter = pix_perimeter * pixelWidth;
        double smooth_perimeter = 0;
        // smoothed perimeter:
        {
            double smooth_pix_perimeter = 0;
            for (final Polygon pol : M.getPolygons(area)) {
                try {
                    if (pol.npoints < 5) {
                        // No point in smoothing out such a short polygon:
                        // (Plus can't convolve it with a gaussian that needs 5 points adjacent)
                        smooth_perimeter += new PolygonRoi(pol, PolygonRoi.POLYGON).getLength();
                        continue;
                    }
                    /*
						// Works but it is not the best smoothing of the Area's countour
						double[] xp = new double[pol.npoints];
						double[] yp = new double[pol.npoints];
						for (int p=0; p<pol.npoints; p++) {
							xp[p] = pol.xpoints[p];
							yp[p] = pol.ypoints[p];
						}
						VectorString2D v = new VectorString2D(xp, yp, 0, true);
						v.resample(resampling_delta);
						smooth_pix_perimeter += v.length() * resampling_delta;
						*/
                    // The best solution I've found:
                    // 1. Run getInterpolatedPolygon with an interval of 1 to get a point at every pixel
                    // 2. convolve with a gaussian
                    // Resample to 1 so that at every one pixel of the contour there is a point
                    FloatPolygon fpol = new FloatPolygon(new float[pol.npoints], new float[pol.npoints], pol.npoints);
                    for (int i = 0; i < pol.npoints; ++i) {
                        fpol.xpoints[i] = pol.xpoints[i];
                        fpol.ypoints[i] = pol.ypoints[i];
                    }
                    fpol = M.createInterpolatedPolygon(fpol, 1, false);
                    final FloatPolygon fp;
                    if (fpol.npoints < 5) {
                        smooth_pix_perimeter += fpol.getLength(false);
                        fp = fpol;
                    } else {
                        // Convolve with a sigma of 1 to smooth it out
                        final FloatPolygon gpol = new FloatPolygon(new float[fpol.npoints], new float[fpol.npoints], fpol.npoints);
                        final CircularSequence seq = new CircularSequence(fpol.npoints);
                        M.convolveGaussianSigma1(fpol.xpoints, gpol.xpoints, seq);
                        M.convolveGaussianSigma1(fpol.ypoints, gpol.ypoints, seq);
                        // Resample it to the desired resolution (also facilitates measurement: npoints * resampling_delta)
                        if (gpol.npoints > resampling_delta) {
                            fp = M.createInterpolatedPolygon(gpol, resampling_delta, false);
                        } else {
                            fp = gpol;
                        }
                        // Measure perimeter: last line segment is potentially shorter or longer than resampling_delta
                        smooth_pix_perimeter += (fp.npoints - 1) * resampling_delta + Math.sqrt(Math.pow(fp.xpoints[0] - fp.xpoints[fp.npoints - 1], 2) + Math.pow(fp.ypoints[0] - fp.ypoints[fp.npoints - 1], 2));
                    }
                // TEST:
                // ij.plugin.frame.RoiManager.getInstance().addRoi(new PolygonRoi(fp, PolygonRoi.POLYGON));
                // TESTING: make a polygon roi and show it
                // ... just in case to see that resampling works as expected, without weird endings
                /*
						int[] x = new int[v.length()];
						int[] y = new int[x.length];
						double[] xd = v.getPoints(0);
						double[] yd = v.getPoints(1);
						for (int p=0; p<x.length; p++) {
							x[p] = (int)xd[p];
							y[p] = (int)yd[p];
						}
						PolygonRoi proi = new PolygonRoi(x, y, x.length, PolygonRoi.POLYGON);
						Rectangle b = proi.getBounds();
						for (int p=0; p<x.length; p++) {
							x[p] -= b.x;
							y[p] -= b.y;
						}
						ImagePlus imp = new ImagePlus("test", new ByteProcessor(b.width, b.height));
						imp.setRoi(new PolygonRoi(x, y, x.length, PolygonRoi.POLYGON));
						imp.show();
						*/
                } catch (final Exception le) {
                    le.printStackTrace();
                }
            }
            smooth_perimeter = smooth_pix_perimeter * pixelWidth;
        }
        if (-1 == last_layer_index) {
            // Start of the very first continuous set:
            lower_bound_surface_h += surface;
            upper_bound_surface += surface;
            upper_bound_surface_smoothed += surface;
            all_tops_and_bottoms += surface;
        } else if (layer_index - last_layer_index > 1) {
            // End of a continuous set ...
            // Sum the last surface and its side:
            // (2x + 2x) / 2   ==   2x
            lower_bound_surface_h += prev_surface + prev_thickness * 2 * Math.sqrt(prev_surface * Math.PI);
            upper_bound_surface += prev_surface + prev_perimeter * prev_thickness;
            upper_bound_surface_smoothed += prev_surface + prev_smooth_perimeter * prev_thickness;
            all_tops_and_bottoms += prev_surface;
            // ... and start of a new set
            lower_bound_surface_h += surface;
            upper_bound_surface += surface;
            upper_bound_surface_smoothed += surface;
            all_tops_and_bottoms += surface;
        } else {
            // Continuation of a set: use this Area and the previous as continuous
            final double diff_surface = Math.abs(prev_surface - surface);
            upper_bound_surface += prev_perimeter * (prev_thickness / 2) + perimeter * (prev_thickness / 2) + diff_surface;
            upper_bound_surface_smoothed += prev_smooth_perimeter * (prev_thickness / 2) + smooth_perimeter * (prev_thickness / 2) + diff_surface;
            // Compute area of the mantle of the truncated cone defined by the radiuses of the circles of same area as the two areas
            // PI * s * (r1 + r2) where s is the hypothenusa
            final double r1 = Math.sqrt(prev_surface / Math.PI);
            final double r2 = Math.sqrt(surface / Math.PI);
            final double hypothenusa = Math.sqrt(Math.pow(Math.abs(r1 - r2), 2) + Math.pow(thickness, 2));
            lower_bound_surface_h += Math.PI * hypothenusa * (r1 + r2);
            // Adjust volume too:
            volume += diff_surface * prev_thickness / 2;
        }
        // store for next iteration:
        prev_surface = surface;
        prev_perimeter = perimeter;
        prev_smooth_perimeter = smooth_perimeter;
        last_layer_index = layer_index;
        prev_thickness = thickness;
        // Iterate points:
        final float z = (float) la.getZ();
        for (final PathIterator pit = area.getPathIterator(null); !pit.isDone(); pit.next()) {
            switch(pit.currentSegment(coords)) {
                case PathIterator.SEG_MOVETO:
                case PathIterator.SEG_LINETO:
                case PathIterator.SEG_CLOSE:
                    points.add(new Point3f(coords[0] * fpixelWidth, coords[1] * fpixelHeight, z * fpixelWidth));
                    break;
                default:
                    Utils.log2("WARNING: unhandled seg type.");
                    break;
            }
        }
    }
    // finish last:
    lower_bound_surface_h += prev_surface + prev_perimeter * prev_thickness;
    upper_bound_surface += prev_surface + prev_perimeter * prev_thickness;
    upper_bound_surface_smoothed += prev_surface + prev_smooth_perimeter * prev_thickness;
    all_tops_and_bottoms += prev_surface;
    // Compute maximum diameter
    final boolean measure_largest_diameter = project.getBooleanProperty("measure_largest_diameter");
    double max_diameter_sq = measure_largest_diameter ? 0 : Double.NaN;
    final int lp = points.size();
    final Point3f c;
    if (lp > 0) {
        // center of mass
        c = new Point3f(points.get(0));
        for (int i = 0; i < lp; i++) {
            final Point3f p = points.get(i);
            if (measure_largest_diameter) {
                for (int j = i; j < lp; j++) {
                    final double len = p.distanceSquared(points.get(j));
                    if (len > max_diameter_sq)
                        max_diameter_sq = len;
                }
            }
            if (0 == i)
                continue;
            c.x += p.x;
            c.y += p.y;
            c.z += p.z;
        }
    } else {
        c = new Point3f(Float.NaN, Float.NaN, Float.NaN);
    }
    // Translate the center of mass
    c.x = box.x + c.x / lp;
    c.y = box.y + c.y / lp;
    c.z /= lp;
    return new double[] { volume, lower_bound_surface_h, upper_bound_surface_smoothed, upper_bound_surface, Math.sqrt(max_diameter_sq), all_tops_and_bottoms, c.x, c.y, c.z };
}
Also used : PathIterator(java.awt.geom.PathIterator) Rectangle(java.awt.Rectangle) ArrayList(java.util.ArrayList) PolygonRoi(ij.gui.PolygonRoi) Point3f(org.scijava.vecmath.Point3f) FloatPolygon(ij.process.FloatPolygon) Polygon(java.awt.Polygon) Calibration(ij.measure.Calibration) TreeMap(java.util.TreeMap) Point(java.awt.Point) USHORTPaint(ini.trakem2.display.paint.USHORTPaint) NoninvertibleTransformException(java.awt.geom.NoninvertibleTransformException) Area(java.awt.geom.Area) AffineTransform(java.awt.geom.AffineTransform) Map(java.util.Map) HashMap(java.util.HashMap) TreeMap(java.util.TreeMap) FloatPolygon(ij.process.FloatPolygon) CircularSequence(ini.trakem2.utils.CircularSequence)

Example 68 with Layer

use of ini.trakem2.display.Layer in project TrakEM2 by trakem2.

the class AreaList method exportAsLabels.

/**
 * Export all given AreaLists as one per pixel value, what is called a "labels" file; a file dialog is offered to save the image as a tiff stack.
 */
public static void exportAsLabels(final List<Displayable> listToPaint, final ij.gui.Roi roi, final float scale, int first_layer, int last_layer, final boolean visible_only, final boolean to_file, final boolean as_amira_labels) {
    // survive everything:
    if (null == listToPaint || 0 == listToPaint.size()) {
        Utils.log("Null or empty list.");
        return;
    }
    if (scale < 0 || scale > 1) {
        Utils.log("Improper scale value. Must be 0 < scale <= 1");
        return;
    }
    // Select the subset to paint
    final ArrayList<AreaList> list = new ArrayList<AreaList>();
    for (final Displayable d : listToPaint) {
        if (visible_only && !d.isVisible())
            continue;
        if (d instanceof AreaList)
            list.add((AreaList) d);
    }
    Utils.log2("exportAsLabels: list.size() is " + list.size());
    // Current AmiraMeshEncoder supports ByteProcessor only: 256 labels max, including background at zero.
    if (as_amira_labels && list.size() > 255) {
        Utils.log("Saving ONLY first 255 AreaLists!\nDiscarded:");
        final StringBuilder sb = new StringBuilder();
        for (final Displayable d : list.subList(255, list.size())) {
            sb.append("    ").append(d.getProject().getShortMeaningfulTitle(d)).append('\n');
        }
        Utils.log(sb.toString());
        final ArrayList<AreaList> li = new ArrayList<AreaList>(list);
        list.clear();
        list.addAll(li.subList(0, 255));
    }
    String path = null;
    if (to_file) {
        final String ext = as_amira_labels ? ".am" : ".tif";
        final File f = Utils.chooseFile("labels", ext);
        if (null == f)
            return;
        path = f.getAbsolutePath().replace('\\', '/');
    }
    final LayerSet layer_set = list.get(0).getLayerSet();
    if (first_layer > last_layer) {
        final int tmp = first_layer;
        first_layer = last_layer;
        last_layer = tmp;
        if (first_layer < 0)
            first_layer = 0;
        if (last_layer >= layer_set.size())
            last_layer = layer_set.size() - 1;
    }
    // Create image according to roi and scale
    final int width, height;
    final Rectangle broi;
    if (null == roi) {
        broi = null;
        width = (int) (layer_set.getLayerWidth() * scale);
        height = (int) (layer_set.getLayerHeight() * scale);
    } else {
        broi = roi.getBounds();
        width = (int) (broi.width * scale);
        height = (int) (broi.height * scale);
    }
    // Compute highest label value, which affects of course the stack image type
    final TreeSet<Integer> label_values = new TreeSet<Integer>();
    for (final Displayable d : list) {
        final String label = d.getProperty("label");
        if (null != label)
            label_values.add(Integer.parseInt(label));
    }
    int lowest = 0, highest = 0;
    if (label_values.size() > 0) {
        lowest = label_values.first();
        highest = label_values.last();
    }
    final int n_non_labeled = list.size() - label_values.size();
    final int max_label_value = highest + n_non_labeled;
    int type_ = ImagePlus.GRAY8;
    if (max_label_value > 255) {
        type_ = ImagePlus.GRAY16;
        if (max_label_value > 65535) {
            type_ = ImagePlus.GRAY32;
        }
    }
    final int type = type_;
    final ImageStack stack = new ImageStack(width, height);
    final Calibration cal = layer_set.getCalibration();
    String amira_params = null;
    if (as_amira_labels) {
        final StringBuilder sb = new StringBuilder("CoordType \"uniform\"\nMaterials {\nExterior {\n Id 0,\nColor 0 0 0\n}\n");
        final float[] c = new float[3];
        int value = 0;
        for (final Displayable d : list) {
            // 0 is background
            value++;
            d.getColor().getRGBColorComponents(c);
            String s = d.getProject().getShortMeaningfulTitle(d);
            s = s.replace('-', '_').replaceAll(" #", " id");
            sb.append(Utils.makeValidIdentifier(s)).append(" {\n").append("Id ").append(value).append(",\n").append("Color ").append(c[0]).append(' ').append(c[1]).append(' ').append(c[2]).append("\n}\n");
        }
        sb.append("}\n");
        amira_params = sb.toString();
    }
    final float len = last_layer - first_layer + 1;
    // Assign labels
    final HashMap<AreaList, Integer> labels = new HashMap<AreaList, Integer>();
    for (final AreaList d : list) {
        final String slabel = d.getProperty("label");
        int label;
        if (null != slabel) {
            label = Integer.parseInt(slabel);
        } else {
            // 0 is background
            label = (++highest);
        }
        labels.put(d, label);
    }
    final ExecutorService exec = Utils.newFixedThreadPool("labels");
    final Map<Integer, ImageProcessor> slices = Collections.synchronizedMap(new TreeMap<Integer, ImageProcessor>());
    final List<Future<?>> fus = new ArrayList<Future<?>>();
    final List<Layer> layers = layer_set.getLayers().subList(first_layer, last_layer + 1);
    for (int k = 0; k < layers.size(); k++) {
        final Layer la = layers.get(k);
        final int slice = k;
        fus.add(exec.submit(new Runnable() {

            @Override
            public void run() {
                Utils.showProgress(slice / len);
                final ImageProcessor ip;
                if (ImagePlus.GRAY8 == type) {
                    final BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
                    final Graphics2D g = bi.createGraphics();
                    for (final AreaList ali : list) {
                        final Area area = ali.getArea(la);
                        if (null == area || area.isEmpty())
                            continue;
                        // Transform: the scale and the roi
                        final AffineTransform aff = new AffineTransform();
                        /* 3 - To scale: */
                        if (1 != scale)
                            aff.scale(scale, scale);
                        /* 2 - To roi coordinates: */
                        if (null != broi)
                            aff.translate(-broi.x, -broi.y);
                        /* 1 - To world coordinates: */
                        aff.concatenate(ali.at);
                        g.setTransform(aff);
                        final int label = labels.get(ali);
                        g.setColor(new Color(label, label, label));
                        g.fill(area);
                    }
                    g.dispose();
                    ip = new ByteProcessor(bi);
                    bi.flush();
                } else if (ImagePlus.GRAY16 == type) {
                    final USHORTPaint paint = new USHORTPaint((short) 0);
                    final BufferedImage bi = new BufferedImage(paint.getComponentColorModel(), paint.getComponentColorModel().createCompatibleWritableRaster(width, height), false, null);
                    final Graphics2D g = bi.createGraphics();
                    // final ColorSpace ugray = ColorSpace.getInstance(ColorSpace.CS_GRAY);
                    int painted = 0;
                    for (final AreaList ali : list) {
                        final Area area = ali.getArea(la);
                        if (null == area || area.isEmpty())
                            continue;
                        // Transform: the scale and the roi
                        final AffineTransform aff = new AffineTransform();
                        /* 3 - To scale: */
                        if (1 != scale)
                            aff.scale(scale, scale);
                        /* 2 - To roi coordinates: */
                        if (null != broi)
                            aff.translate(-broi.x, -broi.y);
                        /* 1 - To world coordinates: */
                        aff.concatenate(ali.at);
                        // Fill
                        g.setTransform(aff);
                        // The color doesn't work: paints in a stretched 8-bit mode
                        // g.setColor(new Color(ugray, new float[]{((float)labels.get(d)) / range}, 1));
                        Utils.log2("value: " + labels.get(ali).shortValue());
                        paint.setValue(labels.get(ali).shortValue());
                        g.setPaint(paint);
                        // .createTransformedArea(aff));
                        g.fill(area);
                        painted += 1;
                    }
                    g.dispose();
                    ip = new ShortProcessor(bi);
                    bi.flush();
                    Utils.log2("painted: " + painted);
                } else {
                    // Option 1: could use the same as above, but shifted by 65536, so that 65537 is 1, 65538 is 2, etc.
                    // and keep doing it until no more need to be shifted.
                    // The PROBLEM: cannot keep the order without complicated gymnastics to remember
                    // which label in which image has to be merged to the final image, which prevent
                    // a simple one-pass blitter.
                    // 
                    // Option 2: paint each arealist, extract the image, use it as a mask for filling:
                    final FloatProcessor fp = new FloatProcessor(width, height);
                    final float[] fpix = (float[]) fp.getPixels();
                    ip = fp;
                    final BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
                    final Graphics2D gbi = bi.createGraphics();
                    for (final AreaList ali : list) {
                        final Area area = ali.getArea(la);
                        if (null == area || area.isEmpty()) {
                            continue;
                        }
                        // Transform: the scale and the roi
                        // reverse order of transformations:
                        final AffineTransform aff = new AffineTransform();
                        /* 3 - To scale: */
                        if (1 != scale)
                            aff.scale(scale, scale);
                        /* 2 - To ROI coordinates: */
                        if (null != broi)
                            aff.translate(-broi.x, -broi.y);
                        /* 1 - To world coordinates: */
                        aff.concatenate(ali.at);
                        final Area s = area.createTransformedArea(aff);
                        final Rectangle sBounds = s.getBounds();
                        // Need to paint at all?
                        if (0 == sBounds.width || 0 == sBounds.height || !sBounds.intersects(0, 0, width, height))
                            continue;
                        // Paint shape
                        gbi.setColor(Color.white);
                        gbi.fill(s);
                        // Read out painted region
                        final int x0 = Math.max(0, sBounds.x);
                        final int y0 = Math.max(0, sBounds.y);
                        final int xN = Math.min(width, sBounds.x + sBounds.width);
                        final int yN = Math.min(height, sBounds.y + sBounds.height);
                        // Get the array
                        final byte[] bpix = ((DataBufferByte) bi.getRaster().getDataBuffer()).getData();
                        final float value = labels.get(ali);
                        // For every non-black pixel, set a 'value' pixel in the FloatProcessor
                        for (int y = y0; y < yN; ++y) {
                            for (int x = x0; x < xN; ++x) {
                                final int pos = y * width + x;
                                // black
                                if (0 == bpix[pos])
                                    continue;
                                fpix[pos] = value;
                            }
                        }
                        // Clear image region
                        gbi.setColor(Color.black);
                        gbi.fill(s);
                    }
                    gbi.dispose();
                    bi.flush();
                }
                slices.put(slice, ip);
            }
        }));
    }
    Utils.wait(fus);
    exec.shutdownNow();
    for (final Map.Entry<Integer, ImageProcessor> e : slices.entrySet()) {
        final Layer la = layers.get(e.getKey());
        stack.addSlice(la.getZ() * cal.pixelWidth + "", e.getValue());
        if (ImagePlus.GRAY8 != type) {
            e.getValue().setMinAndMax(lowest, highest);
        }
    }
    Utils.showProgress(1);
    // Save via file dialog:
    final ImagePlus imp = new ImagePlus("Labels", stack);
    if (as_amira_labels)
        imp.setProperty("Info", amira_params);
    imp.setCalibration(layer_set.getCalibrationCopy());
    if (to_file) {
        if (as_amira_labels) {
            final AmiraMeshEncoder ame = new AmiraMeshEncoder(path);
            if (!ame.open()) {
                Utils.log("Could not write to file " + path);
                return;
            }
            if (!ame.write(imp)) {
                Utils.log("Error in writing Amira file!");
                return;
            }
        } else {
            new FileSaver(imp).saveAsTiff(path);
        }
    } else
        imp.show();
}
Also used : ByteProcessor(ij.process.ByteProcessor) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Rectangle(java.awt.Rectangle) BufferedImage(java.awt.image.BufferedImage) ImageProcessor(ij.process.ImageProcessor) USHORTPaint(ini.trakem2.display.paint.USHORTPaint) FileSaver(ij.io.FileSaver) TreeSet(java.util.TreeSet) ImageStack(ij.ImageStack) FloatProcessor(ij.process.FloatProcessor) Color(java.awt.Color) Calibration(ij.measure.Calibration) ImagePlus(ij.ImagePlus) Point(java.awt.Point) USHORTPaint(ini.trakem2.display.paint.USHORTPaint) Graphics2D(java.awt.Graphics2D) ShortProcessor(ij.process.ShortProcessor) Area(java.awt.geom.Area) ExecutorService(java.util.concurrent.ExecutorService) AmiraMeshEncoder(amira.AmiraMeshEncoder) Future(java.util.concurrent.Future) AffineTransform(java.awt.geom.AffineTransform) File(java.io.File) Map(java.util.Map) HashMap(java.util.HashMap) TreeMap(java.util.TreeMap)

Example 69 with Layer

use of ini.trakem2.display.Layer in project TrakEM2 by trakem2.

the class DisplayCanvas method keyPressed.

@Override
public void keyPressed(final KeyEvent ke) {
    final Displayable active = display.getActive();
    if (null != freehandProfile && ProjectToolbar.getToolId() == ProjectToolbar.PENCIL && ke.getKeyCode() == KeyEvent.VK_ESCAPE && null != freehandProfile) {
        freehandProfile.abort();
        ke.consume();
        return;
    }
    final int keyCode = ke.getKeyCode();
    try {
        // Enable tagging system for any alphanumeric key:
        if (!input_disabled && null != active && active instanceof Tree<?> && ProjectToolbar.isDataEditTool(ProjectToolbar.getToolId())) {
            if (tagging) {
                if (KeyEvent.VK_0 == keyCode && KeyEvent.VK_0 != last_keyCode) {
                // do nothing: keep tagging as true
                } else {
                    // last step of tagging: a char after t or after t and a number (and the char itself can be a number)
                    tagging = false;
                }
                active.keyPressed(ke);
                return;
            } else if (KeyEvent.VK_T == keyCode) {
                tagging = true;
                active.keyPressed(ke);
                return;
            }
        }
    } finally {
        last_keyCode = keyCode;
    }
    tagging = false;
    if (ke.isConsumed())
        return;
    if (!zoom_and_pan) {
        if (KeyEvent.VK_ESCAPE == keyCode) {
            cancelAnimations();
        }
        return;
    }
    final int keyChar = ke.getKeyChar();
    boolean used = false;
    switch(keyChar) {
        case '+':
        case '=':
            zoomIn();
            used = true;
            break;
        case '-':
        case '_':
            zoomOut();
            used = true;
            break;
        default:
            break;
    }
    if (used) {
        // otherwise ImageJ would use it!
        ke.consume();
        return;
    }
    if (input_disabled) {
        if (KeyEvent.VK_ESCAPE == keyCode) {
            // cancel last job if any
            if (Utils.checkYN("Really cancel job?")) {
                display.getProject().getLoader().quitJob(null);
                display.repairGUI();
            }
        }
        ke.consume();
        // only zoom is enabled, above
        return;
    }
    if (KeyEvent.VK_S == keyCode && 0 == ke.getModifiers() && display.getProject().getLoader().isAsynchronous()) {
        display.getProject().getLoader().saveTask(display.getProject(), "Save");
        ke.consume();
        return;
    } else if (KeyEvent.VK_F == keyCode && Utils.isControlDown(ke)) {
        Search.showWindow();
        ke.consume();
        return;
    }
    // if display is not read-only, check for other keys:
    switch(keyChar) {
        case '<':
        case // select next Layer up
        ',':
            // repaints as well
            display.previousLayer(ke.getModifiers());
            ke.consume();
            return;
        case '>':
        case // select next Layer down
        '.':
            display.nextLayer(ke.getModifiers());
            ke.consume();
            return;
    }
    if (null == active && null != imp.getRoi() && KeyEvent.VK_A != keyCode) {
        // control+a and a roi should select under roi
        IJ.getInstance().keyPressed(ke);
        return;
    }
    // end here if display is read-only
    if (display.isReadOnly()) {
        ke.consume();
        display.repaintAll();
        return;
    }
    if (KeyEvent.VK_ENTER == keyCode) {
        if (isTransforming()) {
            applyTransform();
            ke.consume();
            return;
        } else {
            IJ.getInstance().toFront();
            ke.consume();
            return;
        }
    }
    // check preconditions (or the keys are meaningless). Allow 'enter' to
    // bring forward the ImageJ window, and 'v' to paste a patch.
    /*if (null == active && KeyEvent.VK_ENTER != keyCode && KeyEvent.VK_V != keyCode && KeyEvent) {
			return;
		}*/
    final Layer layer = display.getLayer();
    final int mod = ke.getModifiers();
    switch(keyCode) {
        case KeyEvent.VK_COMMA:
        case // select next Layer up
        0xbc:
            display.nextLayer(ke.getModifiers());
            break;
        case KeyEvent.VK_PERIOD:
        case // select next Layer down
        0xbe:
            display.previousLayer(ke.getModifiers());
            break;
        case KeyEvent.VK_Z:
            // UNDO: shift+z or ctrl+z
            if (0 == (mod ^ Event.SHIFT_MASK) || 0 == (mod ^ Utils.getControlModifier())) {
                Bureaucrat.createAndStart(new Worker.Task("Undo") {

                    @Override
                    public void exec() {
                        if (isTransforming())
                            display.getMode().undoOneStep();
                        else
                            display.getLayerSet().undoOneStep();
                        Display.repaint(display.getLayerSet());
                    }
                }, display.getProject());
                ke.consume();
            // REDO: alt+z or ctrl+shift+z
            } else if (0 == (mod ^ Event.ALT_MASK) || 0 == (mod ^ (Event.SHIFT_MASK | Utils.getControlModifier()))) {
                Bureaucrat.createAndStart(new Worker.Task("Redo") {

                    @Override
                    public void exec() {
                        if (isTransforming())
                            display.getMode().redoOneStep();
                        else
                            display.getLayerSet().redoOneStep();
                        Display.repaint(display.getLayerSet());
                    }
                }, display.getProject());
                ke.consume();
            }
            // else, the 'z' command restores the image using ImageJ internal undo
            break;
        case KeyEvent.VK_T:
            // Enable with any tool to the left of the PENCIL
            if (null != active && !isTransforming() && ProjectToolbar.getToolId() < ProjectToolbar.PENCIL) {
                ProjectToolbar.setTool(ProjectToolbar.SELECT);
                if (0 == ke.getModifiers()) {
                    display.setMode(new AffineTransformMode(display));
                } else if (Event.SHIFT_MASK == ke.getModifiers()) {
                    for (final Displayable d : display.getSelection().getSelected()) {
                        if (d.isLinked()) {
                            Utils.showMessage("Can't enter manual non-linear transformation mode:\nat least one image is linked.");
                            return;
                        }
                    }
                    display.setMode(new NonLinearTransformMode(display));
                }
                ke.consume();
            }
            // else, let ImageJ grab the ROI into the Manager, if any
            break;
        case KeyEvent.VK_A:
            if (0 == (ke.getModifiers() ^ Utils.getControlModifier())) {
                final Roi roi = getFakeImagePlus().getRoi();
                if (null != roi)
                    display.getSelection().selectAll(roi, true);
                else
                    display.getSelection().selectAllVisible();
                Display.repaint(display.getLayer(), display.getSelection().getBox(), 0);
                ke.consume();
                // INSIDE the 'if' block, so that it can bleed to the default block which forwards to active!
                break;
            } else if (null != active) {
                active.keyPressed(ke);
                if (ke.isConsumed())
                    break;
            // TODO this is just a hack really. Should just fall back to default switch option.
            // The whole keyPressed method needs revision: should not break from it when not using the key.
            }
            break;
        case // cancel transformation
        KeyEvent.VK_ESCAPE:
            if (isTransforming())
                cancelTransform();
            else {
                // deselect
                display.select(null);
                // repaint out the brush if present
                if (ProjectToolbar.BRUSH == ProjectToolbar.getToolId()) {
                    repaint(old_brush_box, 0);
                }
            }
            ke.consume();
            break;
        case KeyEvent.VK_SPACE:
            if (0 == ke.getModifiers()) {
                if (null != active) {
                    invalidateVolatile();
                    if (Math.abs(active.getAlpha() - 0.5f) > 0.001f)
                        active.setAlpha(0.5f);
                    else
                        active.setAlpha(1.0f);
                    display.setTransparencySlider(active.getAlpha());
                    Display.repaint();
                    ke.consume();
                }
            } else {
                // ;)
                final int kem = ke.getModifiers();
                if (0 != (kem & KeyEvent.SHIFT_MASK) && 0 != (kem & KeyEvent.ALT_MASK) && 0 != (kem & KeyEvent.CTRL_MASK)) {
                    Utils.showMessage("A mathematician, like a painter or poet,\nis a maker of patterns.\nIf his patterns are more permanent than theirs,\nit is because they are made with ideas\n \nG. H. Hardy.");
                    ke.consume();
                }
            }
            break;
        case KeyEvent.VK_S:
            if (ke.isAltDown()) {
                snapping = true;
                ke.consume();
            } else if (dragging) {
                // ignore improper 's' that open ImageJ's save dialog (linux problem ... in macosx, a single dialog opens with lots of 'ssss...' in the text field)
                ke.consume();
            }
            break;
        case KeyEvent.VK_H:
            handleHide(ke);
            ke.consume();
            break;
        case KeyEvent.VK_J:
            if (!display.getSelection().isEmpty()) {
                display.adjustMinAndMaxGUI();
                ke.consume();
            }
            break;
        case KeyEvent.VK_F1:
        case KeyEvent.VK_F2:
        case KeyEvent.VK_F3:
        case KeyEvent.VK_F4:
        case KeyEvent.VK_F5:
        case KeyEvent.VK_F6:
        case KeyEvent.VK_F7:
        case KeyEvent.VK_F8:
        case KeyEvent.VK_F9:
        case KeyEvent.VK_F10:
        case KeyEvent.VK_F11:
        case KeyEvent.VK_F12:
            ProjectToolbar.keyPressed(ke);
            ke.consume();
            break;
        case KeyEvent.VK_M:
            if (0 == ke.getModifiers() && ProjectToolbar.getToolId() == ProjectToolbar.SELECT) {
                display.getSelection().measure();
                ke.consume();
            }
            break;
    }
    switch(keyChar) {
        case ':':
        case ';':
            if (null != active && active instanceof ZDisplayable) {
                if (null != display.getProject().getProjectTree().tryAddNewConnector(active, true)) {
                    ProjectToolbar.setTool(ProjectToolbar.PEN);
                }
                ke.consume();
            }
            break;
    }
    if (ke.isConsumed())
        return;
    if (null != active) {
        if (display.getMode().getClass() == DefaultMode.class) {
            active.keyPressed(ke);
        }
        if (ke.isConsumed())
            return;
    }
    // Else:
    switch(keyCode) {
        case KeyEvent.VK_G:
            if (browseToNodeLayer(ke.isShiftDown())) {
                ke.consume();
            }
            break;
        case KeyEvent.VK_I:
            if (ke.isAltDown()) {
                if (ke.isShiftDown())
                    display.importImage();
                else
                    display.importNextImage();
                ke.consume();
            }
            break;
        case // as in Inkscape
        KeyEvent.VK_PAGE_UP:
            if (null != active) {
                update_graphics = true;
                layer.getParent().addUndoMoveStep(active);
                layer.getParent().move(LayerSet.UP, active);
                layer.getParent().addUndoMoveStep(active);
                Display.repaint(layer, active, 5);
                Display.updatePanelIndex(layer, active);
                ke.consume();
            }
            break;
        case // as in Inkscape
        KeyEvent.VK_PAGE_DOWN:
            if (null != active) {
                update_graphics = true;
                layer.getParent().addUndoMoveStep(active);
                layer.getParent().move(LayerSet.DOWN, active);
                layer.getParent().addUndoMoveStep(active);
                Display.repaint(layer, active, 5);
                Display.updatePanelIndex(layer, active);
                ke.consume();
            }
            break;
        case // as in Inkscape
        KeyEvent.VK_HOME:
            if (null != active) {
                update_graphics = true;
                layer.getParent().addUndoMoveStep(active);
                layer.getParent().move(LayerSet.TOP, active);
                layer.getParent().addUndoMoveStep(active);
                Display.repaint(layer, active, 5);
                Display.updatePanelIndex(layer, active);
                ke.consume();
            }
            break;
        case // as in Inkscape
        KeyEvent.VK_END:
            if (null != active) {
                update_graphics = true;
                layer.getParent().addUndoMoveStep(active);
                layer.getParent().move(LayerSet.BOTTOM, active);
                layer.getParent().addUndoMoveStep(active);
                Display.repaint(layer, active, 5);
                Display.updatePanelIndex(layer, active);
                ke.consume();
            }
            break;
        case KeyEvent.VK_V:
            if (0 == ke.getModifiers()) {
                if (null == active || active.getClass() == Patch.class) {
                    // paste a new image
                    final ImagePlus clipboard = ImagePlus.getClipboard();
                    if (null != clipboard) {
                        final ImagePlus imp = new ImagePlus(clipboard.getTitle() + "_" + System.currentTimeMillis(), clipboard.getProcessor().crop());
                        final Object info = clipboard.getProperty("Info");
                        if (null != info)
                            imp.setProperty("Info", (String) info);
                        final double x = srcRect.x + srcRect.width / 2 - imp.getWidth() / 2;
                        final double y = srcRect.y + srcRect.height / 2 - imp.getHeight() / 2;
                        // save the image somewhere:
                        final Patch pa = display.getProject().getLoader().addNewImage(imp, x, y);
                        display.getLayer().add(pa);
                        ke.consume();
                    }
                // TODO there isn't much ImageJ integration in the pasting. Can't paste to a selected image, for example.
                } else {
                    // Each type may know how to paste data from the copy buffer into itself:
                    active.keyPressed(ke);
                    ke.consume();
                }
            }
            break;
        case KeyEvent.VK_P:
            if (0 == ke.getModifiers()) {
                display.getLayerSet().color_cues = !display.getLayerSet().color_cues;
                Display.repaint(display.getLayerSet());
                ke.consume();
            }
            break;
        case KeyEvent.VK_F:
            if (0 == (ke.getModifiers() ^ KeyEvent.SHIFT_MASK)) {
                // toggle visibility of tags
                display.getLayerSet().paint_tags = !display.getLayerSet().paint_tags;
                Display.repaint();
                ke.consume();
            } else if (0 == (ke.getModifiers() ^ KeyEvent.ALT_MASK)) {
                // toggle visibility of edge arrows
                display.getLayerSet().paint_arrows = !display.getLayerSet().paint_arrows;
                Display.repaint();
                ke.consume();
            } else if (0 == ke.getModifiers()) {
                // toggle visibility of edge confidence boxes
                display.getLayerSet().paint_edge_confidence_boxes = !display.getLayerSet().paint_edge_confidence_boxes;
                Display.repaint();
                ke.consume();
            }
            break;
        case KeyEvent.VK_DELETE:
            if (0 == ke.getModifiers()) {
                display.getSelection().deleteAll();
            }
            break;
        case KeyEvent.VK_B:
            if (0 == ke.getModifiers() && null != active && active.getClass() == Profile.class) {
                display.duplicateLinkAndSendTo(active, 0, active.getLayer().getParent().previous(layer));
                ke.consume();
            }
            break;
        case KeyEvent.VK_N:
            if (0 == ke.getModifiers() && null != active && active.getClass() == Profile.class) {
                display.duplicateLinkAndSendTo(active, 1, active.getLayer().getParent().next(layer));
                ke.consume();
            }
            break;
        case KeyEvent.VK_1:
        case KeyEvent.VK_2:
        case KeyEvent.VK_3:
        case KeyEvent.VK_4:
        case KeyEvent.VK_5:
        case KeyEvent.VK_6:
        case KeyEvent.VK_7:
        case KeyEvent.VK_8:
        case KeyEvent.VK_9:
            // run a plugin, if any
            if (null != Utils.launchTPlugIn(ke, "Display", display.getProject(), display.getActive())) {
                ke.consume();
                break;
            }
    }
    if (!(keyCode == KeyEvent.VK_UNDEFINED || keyChar == KeyEvent.CHAR_UNDEFINED) && !ke.isConsumed() && null != active && active instanceof Patch) {
        // TODO should allow forwarding for all, not just Patch
        // forward to ImageJ for a final try
        IJ.getInstance().keyPressed(ke);
        repaint(active, 5);
        ke.consume();
    }
// Utils.log2("keyCode, keyChar: " + keyCode + ", " + keyChar + " ref: " + KeyEvent.VK_UNDEFINED + ", " + KeyEvent.CHAR_UNDEFINED);
}
Also used : Roi(ij.gui.Roi) ImagePlus(ij.ImagePlus) Point(java.awt.Point) Worker(ini.trakem2.utils.Worker)

Example 70 with Layer

use of ini.trakem2.display.Layer in project TrakEM2 by trakem2.

the class Display method importNextImage.

protected Bureaucrat importNextImage() {
    final Worker worker = new // / all this verbosity is what happens when functions are not first class citizens. I could abstract it away by passing a string name "importImage" and invoking it with reflection, but that is an even bigger PAIN
    Worker(// / all this verbosity is what happens when functions are not first class citizens. I could abstract it away by passing a string name "importImage" and invoking it with reflection, but that is an even bigger PAIN
    "Import image") {

        @Override
        public void run() {
            startedWorking();
            try {
                final Rectangle srcRect = canvas.getSrcRect();
                // - imp.getWidth() / 2;
                final int x = srcRect.x + srcRect.width / 2;
                // - imp.getHeight()/ 2;
                final int y = srcRect.y + srcRect.height / 2;
                final Patch p = project.getLoader().importNextImage(project, x, y);
                if (null == p) {
                    Utils.showMessage("Could not open next image.");
                    finishedWorking();
                    return;
                }
                Display.this.getLayerSet().addLayerContentStep(layer);
                // will add it to the proper Displays
                layer.add(p);
                Display.this.getLayerSet().addLayerContentStep(layer);
            } catch (final Exception e) {
                IJError.print(e);
            }
            finishedWorking();
        }
    };
    return Bureaucrat.createAndStart(worker, getProject());
}
Also used : Rectangle(java.awt.Rectangle) Worker(ini.trakem2.utils.Worker) Point(java.awt.Point) NoninvertibleTransformException(java.awt.geom.NoninvertibleTransformException)

Aggregations

Layer (ini.trakem2.display.Layer)61 ArrayList (java.util.ArrayList)52 Patch (ini.trakem2.display.Patch)43 Rectangle (java.awt.Rectangle)34 HashSet (java.util.HashSet)27 ImagePlus (ij.ImagePlus)26 Displayable (ini.trakem2.display.Displayable)25 AffineTransform (java.awt.geom.AffineTransform)23 GenericDialog (ij.gui.GenericDialog)22 Worker (ini.trakem2.utils.Worker)22 Point (mpicbg.models.Point)22 HashMap (java.util.HashMap)21 Point (java.awt.Point)19 Area (java.awt.geom.Area)19 NoninvertibleTransformException (java.awt.geom.NoninvertibleTransformException)18 File (java.io.File)16 Future (java.util.concurrent.Future)16 LayerSet (ini.trakem2.display.LayerSet)15 ExecutorService (java.util.concurrent.ExecutorService)14 Collection (java.util.Collection)13