Search in sources :

Example 1 with USHORTPaint

use of ini.trakem2.display.paint.USHORTPaint 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)

Aggregations

AmiraMeshEncoder (amira.AmiraMeshEncoder)1 ImagePlus (ij.ImagePlus)1 ImageStack (ij.ImageStack)1 FileSaver (ij.io.FileSaver)1 Calibration (ij.measure.Calibration)1 ByteProcessor (ij.process.ByteProcessor)1 FloatProcessor (ij.process.FloatProcessor)1 ImageProcessor (ij.process.ImageProcessor)1 ShortProcessor (ij.process.ShortProcessor)1 USHORTPaint (ini.trakem2.display.paint.USHORTPaint)1 Color (java.awt.Color)1 Graphics2D (java.awt.Graphics2D)1 Point (java.awt.Point)1 Rectangle (java.awt.Rectangle)1 AffineTransform (java.awt.geom.AffineTransform)1 Area (java.awt.geom.Area)1 BufferedImage (java.awt.image.BufferedImage)1 File (java.io.File)1 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1