/** TrakEM2 plugin for ImageJ(C). Copyright (C) 2005-2009 Albert Cardona and Rodney Douglas. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation (http://www.gnu.org/licenses/gpl.txt ) This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. You may contact Albert Cardona at acardona at ini.phys.ethz.ch Institute of Neuroinformatics, University of Zurich / ETH, Switzerland. **/ package ini.trakem2.io; import amira.AmiraMeshDecoder; import amira.AmiraParameters; import ij.IJ; import ij.ImagePlus; import ij.ImageStack; import ij.io.FileInfo; import ij.io.OpenDialog; import ij.process.ImageProcessor; import ini.trakem2.display.AreaList; import ini.trakem2.display.Layer; import ini.trakem2.display.YesNoDialog; import ini.trakem2.utils.AreaUtils; import ini.trakem2.utils.IJError; import ini.trakem2.utils.Utils; import java.awt.Color; import java.awt.Rectangle; import java.awt.geom.Area; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import mpi.fruitfly.general.MultiThreading; /** Parses an amira labelfield and imports the labels as AreaList instances into the project tree.*/ public class AmiraImporter { /** Returns the array of AreaList or null if the file dialog is canceled. The xo,yo is the pivot of reference. */ static public Collection<AreaList> importAmiraLabels(Layer first_layer, double xo, double yo, final String default_dir) { // open file OpenDialog od = new OpenDialog("Choose Amira Labels File", default_dir, ""); String filename = od.getFileName(); if (null == filename || 0 == filename.length()) return null; String dir = od.getDirectory(); if (IJ.isWindows()) dir = dir.replace('\\', '/'); if (!dir.endsWith("/")) dir += "/"; String path = dir + filename; AmiraMeshDecoder dec = new AmiraMeshDecoder(); if (!dec.open(path)) { YesNoDialog yn = new YesNoDialog("Error", "File was not an Amira labels file.\nChoose another one?"); if (yn.yesPressed()) return importAmiraLabels(first_layer, xo, yo, default_dir); return null; } ImagePlus imp = null; if (dec.isTable()) { Utils.showMessage("Select the other file (the labels)!"); return null; } else { FileInfo fi = new FileInfo(); fi.fileName = filename; fi.directory = dir; imp = new ImagePlus("Amira", dec.getStack()); dec.parameters.setParameters(imp); } return extractAmiraLabels(imp, dec.parameters, first_layer, xo, yo); } /** Returns an ArrayList containing all AreaList objects. The xo,yo is the pivot of reference. */ static public Collection<AreaList> extractAmiraLabels(final ImagePlus labels, final AmiraParameters ap, final Layer first_layer, final double xo, final double yo) { final String[] materials = ap.getMaterialList(); // extract labels as ArrayList of Area final Map<Float,AreaList> alis = extractAreaLists(labels, first_layer, xo, yo, 0.4f, false); for (int i=0; i<materials.length; i++) { final int id = ap.getMaterialID(materials[i]); final double[] color = ap.getMaterialColor(id); final String name = ap.getMaterialName(id); if (name.equals("Exterior")) { Utils.log("Ignoring Amira's \"Exterior\" label"); continue; } final AreaList ali = alis.get(new Float(id)); if (null == ali) { Utils.log("ERROR: no AreaList for label id " + id); continue; } ali.setColor(new Color((float)color[0], (float)color[1], (float)color[2])); ali.setTitle(name); } return alis.values(); } /** Returns a map of label vs AreaList. */ static public Map<Float,AreaList> extractAreaLists(final ImagePlus imp, final Layer first_layer, final double base_x, final double base_y, final float alpha, final boolean add_background) { try { final HashMap<Integer,HashMap<Float,Area>> map = new HashMap<Integer,HashMap<Float,Area>>(); final ImageStack stack = imp.getStack(); // works even for images that are not stacks: it creates one final AtomicInteger ai = new AtomicInteger(1); final AtomicInteger completed_slices = new AtomicInteger(0); final int n_slices = imp.getNSlices(); final Thread parent = Thread.currentThread(); final Thread[] threads = MultiThreading.newThreads(); for (int ithread = 0; ithread < threads.length; ithread++) { threads[ithread] = new Thread() { public void run() { final Rectangle box = new Rectangle(0, 0, 1, 1); for (int i = ai.getAndIncrement(); i <= n_slices; i = ai.getAndIncrement()) { final ImageProcessor ip; synchronized (map) { ip = stack.getProcessor(i); } if (parent.isInterrupted()) return; final HashMap<Float,Area> layer_map = new HashMap<Float,Area>(); synchronized (map) { map.put(i, layer_map); } AreaUtils.extractAreas(ip, layer_map, add_background, box, parent, true); Utils.showProgress(completed_slices.incrementAndGet() / (float)n_slices); } } }; } MultiThreading.startAndJoin(threads); Utils.showProgress(1); if (parent.isInterrupted()) return null; final HashMap<Float,AreaList> alis = new HashMap<Float,AreaList>(); Utils.log2("Recreating arealists..."); Utils.log2("map.size() = " + map.size()); final double thickness = first_layer.getThickness(); final double first_z = first_layer.getZ(); // Recreate AreaLists for (final Map.Entry<Integer,HashMap<Float,Area>> e : map.entrySet()) { final int slice_index = e.getKey(); final HashMap<Float,Area> layer_map = e.getValue(); for (final Map.Entry<Float,Area> fa : layer_map.entrySet()) { Float label = fa.getKey(); AreaList ali = alis.get(label); if (null == ali) { ali = new AreaList(first_layer.getProject(), "Label " + label.intValue(), base_x, base_y); alis.put(label, ali); } double z = first_z + (slice_index-1) * thickness; Layer layer = first_layer.getParent().getLayer(z, thickness, true); ali.setArea(layer.getId(), fa.getValue()); } } Utils.log2("Done recreating."); first_layer.getParent().addAll(alis.values()); Utils.log2("Done adding all to LayerSet"); float hue = 0; for (final Map.Entry<Float,AreaList> e : alis.entrySet()) { final AreaList ali = e.getValue(); ali.setProperty("label", Integer.toString(e.getKey().intValue())); ali.calculateBoundingBox(null); ali.setColor(Color.getHSBColor(hue, 1, 1)); ali.setAlpha(alpha); hue += 0.38197f; // golden angle if (hue > 1) hue = hue - 1; } Utils.log2("Done setting properties"); return alis; } catch (Exception e) { IJError.print(e); } return null; } }