Skip to content

Instantly share code, notes, and snippets.

@bogovicj
Last active March 4, 2018 17:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bogovicj/1dd644b74486b3dddea61797ca09ba4a to your computer and use it in GitHub Desktop.
Save bogovicj/1dd644b74486b3dddea61797ca09ba4a to your computer and use it in GitHub Desktop.
trakem2_exportAreaLists-asMasks.bsh
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import amira.AmiraMeshEncoder;
import ij.IJ;
import ij.ImageJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.gui.Roi;
import ij.io.FileSaver;
import ij.measure.Calibration;
import ij.process.ByteProcessor;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
import ij.process.ShortProcessor;
import ini.trakem2.Project;
import ini.trakem2.display.*;
import ini.trakem2.display.paint.USHORTPaint;
import ini.trakem2.tree.ProjectThing;
import ini.trakem2.utils.Utils;
/*
* See:
* http://forum.imagej.net/t/trakem2-scripting-export-arealists-as-tiffs-with-template-architecture/9461
*/
destdir="/home/john/tmp/tem2/";
public static void writeArealists( String destdir, ProjectThing root, String prefix,
LayerSet layer_set,
Roi roi, float scale, int first, int last )
{
// put a check here to be sure root is okay
if( root == null )
return;
// add this object's title to the file name
String thisprefix = prefix + "_" + root.getTitle();
// get this project's object and see if it's an AreaList
Object obj = root.getObject();
if( obj instanceof AreaList )
{
// if so, write a tif
ImagePlus imp = exportAsMask( (AreaList)obj, layer_set, roi, scale, first, last );
System.out.println( "the imp mask : " + imp );
System.out.println( "writing: " + thisprefix );
IJ.save( imp, destdir + thisprefix + ".tif" );
}
// recurse
children = root.getChildren();
if( children != null )
{
for( int i = 0; i < children.size(); i++ )
{
ProjectThing c = children.get( i );
writeArealists( destdir, c, thisprefix, layer_set, roi, scale, first, last );
}
}
}
static public ImagePlus exportAsMask( AreaList al, LayerSet layer_set, ij.gui.Roi roi, float scale, int first_layer, int last_layer )
{
list = new ArrayList();
list.add( al );
// Compute highest label value, which affects of course the stack image type
label_values = new TreeSet();
for (Displayable d : list) {
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();
}
int n_non_labeled = list.size() - label_values.size();
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;
}
}
int type = type_;
int width,height;
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);
}
ImageStack stack = new ImageStack(width, height);
Calibration cal = layer_set.getCalibration();
float len = last_layer - first_layer + 1;
layers = layer_set.getLayers().subList(first_layer, last_layer+1);
slices = Collections.synchronizedMap(new TreeMap());
for (int k = 0; k < layers.size(); k++) {
Layer la = layers.get(k);
int slice = k;
Utils.showProgress(slice / len);
ImageProcessor ip;
if (ImagePlus.GRAY8 == type) {
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g = bi.createGraphics();
for (AreaList ali : list) {
Area area = ali.getArea(la);
if (null == area || area.isEmpty()) continue;
// Transform: the scale and the roi
AffineTransform aff = new AffineTransform();
// reverse order of transformations:
/* 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.getAffineTransform());
g.setTransform(aff);
int label = 255;
g.setColor(new Color(label, label, label));
g.fill(area);
}
g.dispose();
ip = new ByteProcessor(bi);
bi.flush();
} else if (ImagePlus.GRAY16 == type) {
USHORTPaint paint = new USHORTPaint((short)0);
BufferedImage bi = new BufferedImage(paint.getComponentColorModel(), paint.getComponentColorModel().createCompatibleWritableRaster(width, height), false, null);
Graphics2D g = bi.createGraphics();
//ColorSpace ugray = ColorSpace.getInstance(ColorSpace.CS_GRAY);
int painted = 0;
for (AreaList ali : list) {
Area area = ali.getArea(la);
if (null == area || area.isEmpty()) continue;
// Transform: the scale and the roi
AffineTransform aff = new AffineTransform();
// reverse order of transformations:
/* 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));
short ls = (short)255;
paint.setValue(ls);
g.setPaint(paint);
g.fill(area); //.createTransformedArea(aff));
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:
FloatProcessor fp = new FloatProcessor(width, height);
float[] fpix = (float[]) fp.getPixels();
ip = fp;
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
Graphics2D gbi = bi.createGraphics();
for (AreaList ali : list) {
Area area = ali.getArea(la);
if (null == area || area.isEmpty()) {
continue;
}
// Transform: the scale and the roi
// reverse order of transformations:
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);
Area s = area.createTransformedArea(aff);
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
int x0 = Math.max(0, sBounds.x);
int y0 = Math.max(0, sBounds.y);
int xN = Math.min(width, sBounds.x + sBounds.width);
int yN = Math.min(height, sBounds.y + sBounds.height);
// Get the array
byte[] bpix = ((DataBufferByte)bi.getRaster().getDataBuffer()).getData();
float value = 255f;
// 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) {
int pos = y * width + x;
if (0 == bpix[pos]) continue; // black
fpix[pos] = value;
}
}
// Clear image region
gbi.setColor(Color.black);
gbi.fill(s);
}
gbi.dispose();
bi.flush();
}
slices.put(slice, ip);
}
for ( e : slices.entrySet()) {
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:
ImagePlus imp = new ImagePlus("Labels", stack);
imp.setCalibration(layer_set.getCalibrationCopy());
return imp;
}
// get the root ProjectThing
project = Project.getProjects().get(0);
root = project.getRootProjectThing();
layer_set = project.getRootLayerSet();
first = 0;
last = layer_set.size() - 1;
scale = 1.0f;
prefix = "";
// call the function
writeArealists( destdir, root, prefix,
layer_set, null, scale, first, last );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment