use of ini.trakem2.display.Stack in project TrakEM2 by trakem2.
the class AreaList method getStack.
/**
* Returns a stack of images representing the pixel data of this LayerSet inside this AreaList.
*/
public ImagePlus getStack(final int type, final double scale) {
final ImageProcessor ref_ip = Utils.createProcessor(type, 2, 2);
if (null == ref_ip) {
Utils.log("AreaList.getStack: Unknown type " + type);
return null;
}
final Rectangle b = getBoundingBox();
final int w = (int) (0.5 + b.width * scale);
final int h = (int) (0.5 + b.height * scale);
final ImageStack stack = new ImageStack(w, h);
for (final Layer la : getLayerRange()) {
final Area area = getArea(la);
final double z = layer.getZ();
project.getLoader().releaseToFit(w * h * 10);
final ImageProcessor ip = ref_ip.createProcessor(w, h);
if (null == area) {
stack.addSlice(Double.toString(z), ip);
continue;
}
// Create a ROI from the area at Layer la:
final AffineTransform aff = getAffineTransformCopy();
aff.translate(-b.x, -b.y);
aff.scale(scale, scale);
final ShapeRoi roi = new ShapeRoi(area.createTransformedArea(aff));
// Create a cropped snapshot of the images at Layer la under the area:
final ImageProcessor flat = Patch.makeFlatImage(type, la, b, scale, la.getAll(Patch.class), Color.black);
flat.setRoi(roi);
final Rectangle rb = roi.getBounds();
ip.insert(flat.crop(), rb.x, rb.y);
// Clear the outside
final ImagePlus bimp = new ImagePlus("", ip);
bimp.setRoi(roi);
ip.setValue(0);
ip.setBackgroundValue(0);
IJ.run(bimp, "Clear Outside", "");
stack.addSlice(Double.toString(z), ip);
}
final ImagePlus imp = new ImagePlus("AreaList stack for " + this, stack);
imp.setCalibration(layer_set.getCalibrationCopy());
return imp;
}
use of ini.trakem2.display.Stack in project TrakEM2 by trakem2.
the class Project method newFSProject.
public static Project newFSProject(String arg, TemplateThing template_root, String storage_folder, boolean autocreate_one_layer) {
if (Utils.wrongImageJVersion())
return null;
FSLoader loader = null;
try {
String dir_project = storage_folder;
if (null == dir_project || !new File(dir_project).isDirectory()) {
DirectoryChooser dc = new DirectoryChooser("Select storage folder");
dir_project = dc.getDirectory();
// user cancelled dialog
if (null == dir_project)
return null;
if (!Loader.canReadAndWriteTo(dir_project)) {
Utils.showMessage("Can't read/write to the selected storage folder.\nPlease check folder permissions.");
return null;
}
if (IJ.isWindows())
dir_project = dir_project.replace('\\', '/');
}
loader = new FSLoader(dir_project);
Project project = createNewProject(loader, !("blank".equals(arg) || "amira".equals(arg)), template_root);
// help the helpless users:
if (autocreate_one_layer && null != project && ControlWindow.isGUIEnabled()) {
Utils.log2("Creating automatic Display.");
// add a default layer
Layer layer = new Layer(project, 0, 1, project.layer_set);
project.layer_set.add(layer);
project.layer_tree.addLayer(project.layer_set, layer);
layer.recreateBuckets();
Display.createDisplay(project, layer);
}
try {
// waiting cheaply for asynchronous swing calls
Thread.sleep(200);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
if ("amira".equals(arg) || "stack".equals(arg)) {
// forks into a task thread
loader.importStack(project.layer_set.getLayer(0), null, true);
}
project.restartAutosaving();
return project;
} catch (Exception e) {
IJError.print(e);
if (null != loader)
loader.destroy();
}
return null;
}
use of ini.trakem2.display.Stack 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();
}
use of ini.trakem2.display.Stack in project TrakEM2 by trakem2.
the class Loader method createLazyFlyThrough.
/**
* Each slice is generated on demand, one slice per Region instance.
*/
public <I> ImagePlus createLazyFlyThrough(final List<? extends Region<I>> regions, final double magnification, final int type, final Displayable active) {
final Region<I> first = regions.get(0);
final int w = (int) (first.r.width * magnification), h = (int) (first.r.height * magnification);
final LazyVirtualStack stack = new LazyVirtualStack(w, h, regions.size());
for (final Region<I> r : regions) {
stack.addSlice(new Callable<ImageProcessor>() {
@Override
public ImageProcessor call() {
return getFlatImage(r.layer, r.r, magnification, 0xffffffff, type, Displayable.class, null, true, Color.black, active).getProcessor();
}
});
}
return new ImagePlus("Fly-Through", stack);
}
use of ini.trakem2.display.Stack in project TrakEM2 by trakem2.
the class Loader method importImage.
/**
* Import a new image at the given coordinates; does not puts it into any layer, unless it's a stack -in which case importStack is called with the current front layer of the given project as target. If a path is not provided it will be asked for.
*/
public Patch importImage(final Project project, final double x, final double y, String path, final boolean synch_mipmap_generation) {
if (null == path) {
final OpenDialog od = new OpenDialog("Import image", "");
final String name = od.getFileName();
if (null == name || 0 == name.length())
return null;
String dir = od.getDirectory().replace('\\', '/');
if (!dir.endsWith("/"))
dir += "/";
path = dir + name;
} else {
path = path.replace('\\', '/');
}
// avoid opening trakem2 projects
if (path.toLowerCase().endsWith(".xml")) {
Utils.showMessage("Cannot import " + path + " as a stack.");
return null;
}
releaseToFit(new File(path).length() * 3);
IJ.redirectErrorMessages();
final ImagePlus imp = openImagePlus(path);
if (null == imp)
return null;
if (imp.getNSlices() > 1) {
// a stack!
final Layer layer = Display.getFrontLayer(project);
if (null == layer)
return null;
importStack(layer, x, y, imp, true, path, true);
return null;
}
if (0 == imp.getWidth() || 0 == imp.getHeight()) {
Utils.showMessage("Can't import image of zero width or height.");
flush(imp);
return null;
}
final Patch p = new Patch(project, imp.getTitle(), x, y, imp);
addedPatchFrom(path, p);
// WARNING may be altered concurrently
last_opened_path = path;
if (isMipMapsRegenerationEnabled()) {
if (synch_mipmap_generation) {
try {
// wait
regenerateMipMaps(p).get();
} catch (final Exception e) {
IJError.print(e);
}
} else
// queue for regeneration
regenerateMipMaps(p);
}
return p;
}
Aggregations