Last active
March 4, 2018 17:50
-
-
Save bogovicj/1dd644b74486b3dddea61797ca09ba4a to your computer and use it in GitHub Desktop.
trakem2_exportAreaLists-asMasks.bsh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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