From 0f63352e4600f0f46c602dd0eb4096164c8fdd27 Mon Sep 17 00:00:00 2001 From: Albert Cardona Date: Wed, 18 Feb 2009 11:59:35 -0500 Subject: [PATCH] Added "Export arealists as labels (amira)" command. --- ini/trakem2/display/AreaList.java | 67 ++++++++++++++++++++++++++++++++++++--- ini/trakem2/display/Display.java | 18 ++++++++--- ini/trakem2/utils/Utils.java | 27 ++++++++++++++++ 3 files changed, 103 insertions(+), 9 deletions(-) diff --git a/ini/trakem2/display/AreaList.java b/ini/trakem2/display/AreaList.java index 9d74ba9e..37c6f0e6 100644 --- a/ini/trakem2/display/AreaList.java +++ b/ini/trakem2/display/AreaList.java @@ -67,9 +67,12 @@ import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Composite; import java.awt.AlphaComposite; +import java.io.File; import marchingcubes.MCTriangulator; import isosurface.Triangulator; +import amira.AmiraMeshEncoder; +import amira.AmiraParameters; import javax.vecmath.Point3f; @@ -1441,7 +1444,7 @@ public class AreaList extends ZDisplayable { } /** 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. */ - static public void exportAsLabels(final java.util.List list, final ij.gui.Roi roi, final float scale, int first_layer, int last_layer, final boolean visible_only, final boolean to_file) { + static public void exportAsLabels(final java.util.List list, 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 == list || 0 == list.size()) { Utils.log("Null or empty list."); @@ -1451,6 +1454,28 @@ public class AreaList extends ZDisplayable { Utils.log("Improper scale value. Must be 0 < scale <= 1"); return; } + + // 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:"); + StringBuffer sb = new StringBuffer(); + for (final Displayable d : list.subList(256, list.size())) { + sb.append(" ").append(d.getProject().getShortMeaningfulTitle(d)).append('\n'); + } + Utils.log(sb.toString()); + ArrayList li = new ArrayList(list); + list.clear(); + list.addAll(li.subList(0, 256)); + } + + String path = null; + if (to_file) { + String ext = as_amira_labels ? ".am" : ".tif"; + File f = Utils.chooseFile("labels", ext); + if (null == f) return; + path = f.getAbsolutePath().replace('\\','/'); + } + LayerSet layer_set = list.get(0).getLayerSet(); if (first_layer > last_layer) { int tmp = first_layer; @@ -1481,6 +1506,24 @@ public class AreaList extends ZDisplayable { } Calibration cal = layer_set.getCalibration(); + String amira_params = null; + if (as_amira_labels) { + final StringBuffer sb = new StringBuffer("Parameters {\n"); + final float[] c = new float[3]; + int value = 0; + for (final Displayable d : list) { + value++; // 0 is background + 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(); + } + int count = 1; final float len = last_layer - first_layer + 1; @@ -1490,7 +1533,7 @@ public class AreaList extends ZDisplayable { ImageProcessor ip = Utils.createProcessor(type, width, height); // paint here all arealist that paint to the layer 'la' int value = 0; - for (Displayable d : list) { + for (final Displayable d : list) { value++; // zero is background ip.setValue(value); if (visible_only && !d.isVisible()) continue; @@ -1509,12 +1552,26 @@ public class AreaList extends ZDisplayable { } stack.addSlice(la.getZ() * cal.pixelWidth + "", ip); } - Utils.showProgress(0); + Utils.showProgress(1); // Save via file dialog: ImagePlus imp = new ImagePlus("Labels", stack); + if (as_amira_labels) imp.setProperty("Info", amira_params); imp.setCalibration(layer_set.getCalibrationCopy()); - if (to_file) new FileSaver(imp).saveAsTiff(); - else imp.show(); + if (to_file) { + if (as_amira_labels) { + 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(); } public ResultsTable measure(ResultsTable rt) { diff --git a/ini/trakem2/display/Display.java b/ini/trakem2/display/Display.java index 8006ee89..2fe5afb6 100644 --- a/ini/trakem2/display/Display.java +++ b/ini/trakem2/display/Display.java @@ -2247,8 +2247,11 @@ public final class Display extends DBObject implements ActionListener, ImageList item = new JMenuItem("Project properties..."); item.addActionListener(this); menu.add(item); item = new JMenuItem("Create subproject"); item.addActionListener(this); menu.add(item); if (null == canvas.getFakeImagePlus().getRoi()) item.setEnabled(false); - item = new JMenuItem("Export arealists as labels"); item.addActionListener(this); menu.add(item); + item = new JMenuItem("Export arealists as labels (tif)"); item.addActionListener(this); menu.add(item); if (0 == layer.getParent().getZDisplayables(AreaList.class).size()) item.setEnabled(false); + item = new JMenuItem("Export arealists as labels (amira)"); item.addActionListener(this); menu.add(item); + if (0 == layer.getParent().getZDisplayables(AreaList.class).size()) item.setEnabled(false); + item = new JMenuItem("Release memory..."); item.addActionListener(this); menu.add(item); if (menu.getItemCount() > 0) popup.add(menu); menu = new JMenu("Selection"); @@ -3149,7 +3152,7 @@ public final class Display extends DBObject implements ActionListener, ImageList SwingUtilities.invokeLater(new Runnable() { public void run() { d.canvas.showCentered(new Rectangle(0, 0, (int)subls.getLayerWidth(), (int)subls.getLayerHeight())); }}); - } else if (command.equals("Export arealists as labels")) { + } else if (command.startsWith("Export arealists as labels")) { GenericDialog gd = new GenericDialog("Export labels"); gd.addSlider("Scale: ", 1, 100, 100); final String[] options = {"All area list", "Selected area lists"}; @@ -3159,15 +3162,22 @@ public final class Display extends DBObject implements ActionListener, ImageList gd.showDialog(); if (gd.wasCanceled()) return; final float scale = (float)(gd.getNextNumber() / 100); - java.util.List al = 0 == gd.getNextChoiceIndex() ? layer.getParent().getZDisplayables(AreaList.class) : selection.getSelected(AreaList.class); + java.util.List al = 0 == gd.getNextChoiceIndex() ? layer.getParent().getZDisplayables(AreaList.class) : selection.getSelected(AreaList.class); if (null == al) { Utils.log("No area lists found to export."); return; } + // Generics are ... a pain? I don't understand them? They fail when they shouldn't? And so easy to workaround that they are a shame? + al = (java.util.List) al; + int first = gd.getNextChoiceIndex(); int last = gd.getNextChoiceIndex(); boolean visible_only = gd.getNextBoolean(); - AreaList.exportAsLabels(al, canvas.getFakeImagePlus().getRoi(), scale, first, last, visible_only, false); + if (command.endsWith("(amira)")) { + AreaList.exportAsLabels(al, canvas.getFakeImagePlus().getRoi(), scale, first, last, visible_only, true, true); + } else if (command.endsWith("(tif)")) { + AreaList.exportAsLabels(al, canvas.getFakeImagePlus().getRoi(), scale, first, last, visible_only, false, false); + } } else if (command.equals("Project properties...")) { project.adjustProperties(); } else if (command.equals("Release memory...")) { diff --git a/ini/trakem2/utils/Utils.java b/ini/trakem2/utils/Utils.java index dfbbb5b2..80387c83 100644 --- a/ini/trakem2/utils/Utils.java +++ b/ini/trakem2/utils/Utils.java @@ -81,6 +81,7 @@ import java.lang.Iterable; import java.util.Iterator; import java.util.Map; import java.util.regex.Pattern; +import java.util.regex.Matcher; import javax.vecmath.Point3f; import javax.vecmath.Vector3f; @@ -1313,6 +1314,32 @@ public class Utils implements ij.plugin.PlugIn { return true; } + /** + user=> (def pat #"\b[a-zA-Z]+[\w]*\b") + #'user/pat + user=>(re-seq pat "abc def 1a334") + ("abc" "def") + user=> (re-seq pat "abc def a334") + ("abc" "def" "a334") + + Then concatenate all good words with underscores. + Returns null when nothing valid is found in 's'. + */ + static final public String makeValidIdentifier(final String s) { + if (null == s) return null; + // Concatenate all good groups with underscores: + final Pattern pat = Pattern.compile("\\b[a-zA-Z]+[\\w]*\\b"); + final Matcher m = pat.matcher(s); + final StringBuffer sb = new StringBuffer(); + while (m.find()) { + sb.append(m.group()).append('_'); + } + if (0 == sb.length()) return null; + // Remove last underscore + sb.setLength(sb.length()-1); + return sb.toString(); + } + static final public int indexOf(final Object needle, final Object[] haystack) { for (int i=0; i