package ij.plugin;
import ij.*;
import ij.gui.*;
import ij.process.*;
import ij.plugin.filter.*;
import ij.plugin.frame.Recorder;
import ij.measure.Measurements;
import java.lang.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
/** This plugin performs a z-projection of the input stack. Type of
output image is same as type of input image.
@author Patrick Kelly
*/
public class ZProjector implements PlugIn {
public static final int AVG_METHOD = 0;
public static final int MAX_METHOD = 1;
public static final int MIN_METHOD = 2;
public static final int SUM_METHOD = 3;
public static final int SD_METHOD = 4;
public static final int MEDIAN_METHOD = 5;
public static final String[] METHODS =
{"Average Intensity", "Max Intensity", "Min Intensity", "Sum Slices", "Standard Deviation", "Median"};
private static final String METHOD_KEY = "zproject.method";
private int method = (int)Prefs.get(METHOD_KEY, AVG_METHOD);
private static final int BYTE_TYPE = 0;
private static final int SHORT_TYPE = 1;
private static final int FLOAT_TYPE = 2;
public static final String lutMessage =
"Stacks with inverter LUTs may not project correctly.\n"
+"To create a standard LUT, invert the stack (Edit/Invert)\n"
+"and invert the LUT (Image/Lookup Tables/Invert LUT).";
/** Image to hold z-projection. */
private ImagePlus projImage = null;
/** Image stack to project. */
private ImagePlus imp = null;
/** Projection starts from this slice. */
private int startSlice = 1;
/** Projection ends at this slice. */
private int stopSlice = 1;
/** Project all time points? */
private boolean allTimeFrames = true;
private String color = "";
private boolean isHyperstack;
private boolean simpleComposite;
private int increment = 1;
private int sliceCount;
public ZProjector() {
}
/** Construction of ZProjector with image to be projected. */
public ZProjector(ImagePlus imp) {
setImage(imp);
}
/** Performs projection on the entire stack using the specified method and returns
the result, where 'method' is "avg", "min", "max", "sum", "sd" or "median".
Add " all" to 'method' to project all hyperstack time points. */
public static ImagePlus run(ImagePlus imp, String method) {
return run(imp, method, 1, imp.getStackSize());
}
/** Performs projection using the specified method and stack range, and returns
the result, where 'method' is "avg", "min", "max", "sum", "sd" or "median".
Add " all" to 'method' to project all hyperstack time points.
Example: http://imagej.nih.gov/ij/macros/js/ProjectionDemo.js
*/
public static ImagePlus run(ImagePlus imp, String method, int startSlice, int stopSlice) {
ZProjector zp = new ZProjector(imp);
zp.setStartSlice(startSlice);
zp.setStopSlice(stopSlice);
zp.isHyperstack = imp.isHyperStack();
if (zp.isHyperstack && startSlice==1 && stopSlice==imp.getStackSize())
zp.setDefaultBounds();
if (method==null) return null;
method = method.toLowerCase();
int m = -1;
if (method.startsWith("av")) m = AVG_METHOD;
else if (method.startsWith("max")) m = MAX_METHOD;
else if (method.startsWith("min")) m = MIN_METHOD;
else if (method.startsWith("sum")) m = SUM_METHOD;
else if (method.startsWith("sd")) m = SD_METHOD;
else if (method.startsWith("median")) m = MEDIAN_METHOD;
if (m<0)
throw new IllegalArgumentException("Invalid projection method: "+method);
zp.allTimeFrames = method.contains("all");
zp.setMethod(m);
zp.doProjection(true);
return zp.getProjection();
}
/** Explicitly set image to be projected. This is useful if
ZProjection_ object is to be used not as a plugin but as a
stand alone processing object. */
public void setImage(ImagePlus imp) {
this.imp = imp;
startSlice = 1;
stopSlice = imp.getStackSize();
}
public void setStartSlice(int slice) {
if (imp==null || slice < 1 || slice > imp.getStackSize())
return;
startSlice = slice;
}
public void setStopSlice(int slice) {
if (imp==null || slice < 1 || slice > imp.getStackSize())
return;
stopSlice = slice;
}
public void setMethod(int projMethod){
method = projMethod;
}
/** Retrieve results of most recent projection operation.*/
public ImagePlus getProjection() {
return projImage;
}
public void run(String arg) {
imp = IJ.getImage();
if (imp==null) {
IJ.noImage();
return;
}
// Make sure input image is a stack.
if(imp.getStackSize()==1) {
IJ.error("Z Project", "Stack required");
return;
}
// Check for inverting LUT.
if (imp.getProcessor().isInvertedLut()) {
if (!IJ.showMessageWithCancel("ZProjection", lutMessage))
return;
}
setDefaultBounds();
// Build control dialog
GenericDialog gd = buildControlDialog(startSlice,stopSlice);
gd.showDialog();
if (gd.wasCanceled()) return;
if (!imp.lock()) return; // exit if in use
long tstart = System.currentTimeMillis();
gd.setSmartRecording(true);
int startSlice2 = startSlice;
int stopSlice2 = stopSlice;
setStartSlice((int)gd.getNextNumber());
setStopSlice((int)gd.getNextNumber());
boolean rangeChanged = startSlice!=startSlice2 || stopSlice!=stopSlice2;
startSlice2 = startSlice;
stopSlice2 = stopSlice;
gd.setSmartRecording(false);
method = gd.getNextChoiceIndex();
Prefs.set(METHOD_KEY, method);
if (isHyperstack)
allTimeFrames = imp.getNFrames()>1&&imp.getNSlices()>1?gd.getNextBoolean():false;
doProjection(true);
if (arg.equals("") && projImage!=null) {
long tstop = System.currentTimeMillis();
if (simpleComposite) IJ.run(projImage, "Grays", "");
projImage.show("ZProjector: " +IJ.d2s((tstop-tstart)/1000.0,2)+" seconds");
}
imp.unlock();
IJ.register(ZProjector.class);
if (Recorder.scriptMode()) {
String m = getMethodAsString();
if (isHyperstack && allTimeFrames)
m = m + " all";
String range = "";
if (rangeChanged)
range = ","+startSlice2+","+stopSlice2;
Recorder.recordCall("imp = ZProjector.run(imp,\""+m+"\""+range+");");
}
}
private String getMethodAsString() {
switch (method) {
case AVG_METHOD: return "avg";
case MAX_METHOD: return "max";
case MIN_METHOD: return "min";
case SUM_METHOD: return "sum";
case SD_METHOD: return "sd";
case MEDIAN_METHOD: return "median";
default: return "avg";
}
}
private void setDefaultBounds() {
int stackSize = imp.getStackSize();
int channels = imp.getNChannels();
int frames = imp.getNFrames();
int slices = imp.getNSlices();
isHyperstack = imp.isHyperStack()||( ij.macro.Interpreter.isBatchMode()&&((frames>1&&frames1&&slices1)
stopSlice = nSlices;
else
stopSlice = imp.getNFrames();
} else
stopSlice = stackSize;
}
public void doRGBProjection() {
doRGBProjection(imp.getStack());
}
//Added by Marcel Boeglin 2013.09.23
public void doRGBProjection(boolean handleOverlay) {
doRGBProjection(imp.getStack());
Overlay overlay = imp.getOverlay();
if (handleOverlay && overlay!=null)
projImage.setOverlay(projectRGBHyperStackRois(overlay));
}
private void doRGBProjection(ImageStack stack) {
ImageStack[] channels = ChannelSplitter.splitRGB(stack, true);
ImagePlus red = new ImagePlus("Red", channels[0]);
ImagePlus green = new ImagePlus("Green", channels[1]);
ImagePlus blue = new ImagePlus("Blue", channels[2]);
imp.unlock();
ImagePlus saveImp = imp;
imp = red;
color = "(red)"; doProjection();
ImagePlus red2 = projImage;
imp = green;
color = "(green)"; doProjection();
ImagePlus green2 = projImage;
imp = blue;
color = "(blue)"; doProjection();
ImagePlus blue2 = projImage;
int w = red2.getWidth(), h = red2.getHeight(), d = red2.getStackSize();
if (method==SD_METHOD) {
ImageProcessor r = red2.getProcessor();
ImageProcessor g = green2.getProcessor();
ImageProcessor b = blue2.getProcessor();
double max = 0;
double rmax = r.getStats().max; if (rmax>max) max=rmax;
double gmax = g.getStats().max; if (gmax>max) max=gmax;
double bmax = b.getStats().max; if (bmax>max) max=bmax;
double scale = 255/max;
r.multiply(scale); g.multiply(scale); b.multiply(scale);
red2.setProcessor(r.convertToByte(false));
green2.setProcessor(g.convertToByte(false));
blue2.setProcessor(b.convertToByte(false));
}
RGBStackMerge merge = new RGBStackMerge();
ImageStack stack2 = merge.mergeStacks(w, h, d, red2.getStack(), green2.getStack(), blue2.getStack(), true);
imp = saveImp;
projImage = new ImagePlus(makeTitle(), stack2);
}
/** Builds dialog to query users for projection parameters.
@param start starting slice to display
@param stop last slice */
protected GenericDialog buildControlDialog(int start, int stop) {
GenericDialog gd = new GenericDialog("ZProjection");
gd.addNumericField("Start slice:",startSlice,0/*digits*/);
gd.addNumericField("Stop slice:",stopSlice,0/*digits*/);
gd.addChoice("Projection type", METHODS, METHODS[method]);
if (isHyperstack && imp.getNFrames()>1&& imp.getNSlices()>1)
gd.addCheckbox("All time frames", allTimeFrames);
return gd;
}
/** Performs actual projection using specified method. */
public void doProjection() {
if (imp==null)
return;
if (imp.getBitDepth()==24) {
doRGBProjection();
return;
}
sliceCount = 0;
if (methodMEDIAN_METHOD)
method = AVG_METHOD;
for (int slice=startSlice; slice<=stopSlice; slice+=increment)
sliceCount++;
if (method==MEDIAN_METHOD) {
projImage = doMedianProjection();
return;
}
// Create new float processor for projected pixels.
FloatProcessor fp = new FloatProcessor(imp.getWidth(),imp.getHeight());
ImageStack stack = imp.getStack();
RayFunction rayFunc = getRayFunction(method, fp);
if (IJ.debugMode==true) {
IJ.log("\nProjecting stack from: "+startSlice
+" to: "+stopSlice);
}
// Determine type of input image. Explicit determination of
// processor type is required for subsequent pixel
// manipulation. This approach is more efficient than the
// more general use of ImageProcessor's getPixelValue and
// putPixel methods.
int ptype;
if (stack.getProcessor(1) instanceof ByteProcessor) ptype = BYTE_TYPE;
else if (stack.getProcessor(1) instanceof ShortProcessor) ptype = SHORT_TYPE;
else if (stack.getProcessor(1) instanceof FloatProcessor) ptype = FLOAT_TYPE;
else {
IJ.error("Z Project", "Non-RGB stack required");
return;
}
// Do the projection
int sliceCount = 0;
for (int n=startSlice; n<=stopSlice; n+=increment) {
if (!isHyperstack) {
IJ.showStatus("ZProjection " + color +": " + n + "/" + stopSlice);
IJ.showProgress(n-startSlice, stopSlice-startSlice);
}
projectSlice(stack.getPixels(n), rayFunc, ptype);
sliceCount++;
}
// Finish up projection.
if (method==SUM_METHOD) {
if (imp.getCalibration().isSigned16Bit())
fp.subtract(sliceCount*32768.0);
fp.resetMinAndMax();
projImage = new ImagePlus(makeTitle(), fp);
} else if (method==SD_METHOD) {
rayFunc.postProcess();
fp.resetMinAndMax();
projImage = new ImagePlus(makeTitle(), fp);
} else {
rayFunc.postProcess();
projImage = makeOutputImage(imp, fp, ptype);
}
if(projImage==null)
IJ.error("Z Project", "Error computing projection.");
}
//Added by Marcel Boeglin 2013.09.23
/** Performs actual projection using specified method.
If handleOverlay, adds stack overlay
elements from startSlice to stopSlice to projection. */
public void doProjection(boolean handleOverlay) {
if (isHyperstack)
doHyperStackProjection(allTimeFrames);
else if (imp.getType()==ImagePlus.COLOR_RGB)
doRGBProjection(handleOverlay);
else {
doProjection();
Overlay overlay = imp.getOverlay();
if (handleOverlay && overlay!=null)
projImage.setOverlay(projectStackRois(overlay));
}
if (projImage!=null)
projImage.setCalibration(imp.getCalibration());
}
//Added by Marcel Boeglin 2013.09.23
private Overlay projectStackRois(Overlay overlay) {
if (overlay==null) return null;
Overlay overlay2 = overlay.create();
Roi roi;
int s;
for (Roi r : overlay.toArray()) {
s = r.getPosition();
roi = (Roi)r.clone();
if (s>=startSlice && s<=stopSlice || s==0) {
roi.setPosition(s);
overlay2.add(roi);
}
}
return overlay2;
}
public void doHyperStackProjection(boolean allTimeFrames) {
int start = startSlice;
int stop = stopSlice;
int firstFrame = 1;
int lastFrame = imp.getNFrames();
if (!allTimeFrames)
firstFrame = lastFrame = imp.getFrame();
ImageStack stack = new ImageStack(imp.getWidth(), imp.getHeight());
int channels = imp.getNChannels();
int slices = imp.getNSlices();
if (slices==1) {
slices = imp.getNFrames();
firstFrame = lastFrame = 1;
}
int frames = lastFrame-firstFrame+1;
increment = channels;
boolean rgb = imp.getBitDepth()==24;
for (int frame=firstFrame; frame<=lastFrame; frame++) {
IJ.showStatus(""+ (frame-firstFrame) + "/" + (lastFrame-firstFrame));
IJ.showProgress(frame-firstFrame, lastFrame-firstFrame);
for (int channel=1; channel<=channels; channel++) {
startSlice = (frame-1)*channels*slices + (start-1)*channels + channel;
stopSlice = (frame-1)*channels*slices + (stop-1)*channels + channel;
if (rgb)
doHSRGBProjection(imp);
else
doProjection();
stack.addSlice(null, projImage.getProcessor());
}
}
projImage = new ImagePlus(makeTitle(), stack);
projImage.setDimensions(channels, 1, frames);
if (channels>1) {
projImage = new CompositeImage(projImage, 0);
((CompositeImage)projImage).copyLuts(imp);
if (method==SUM_METHOD || method==SD_METHOD)
((CompositeImage)projImage).resetDisplayRanges();
}
if (frames>1)
projImage.setOpenAsHyperStack(true);
Overlay overlay = imp.getOverlay();
if (overlay!=null) {
startSlice = start;
stopSlice = stop;
if (imp.getType()==ImagePlus.COLOR_RGB)
projImage.setOverlay(projectRGBHyperStackRois(overlay));
else
projImage.setOverlay(projectHyperStackRois(overlay));
}
IJ.showProgress(1, 1);
}
//Added by Marcel Boeglin 2013.09.22
private Overlay projectRGBHyperStackRois(Overlay overlay) {
if (overlay==null) return null;
int frames = projImage.getNFrames();
int t1 = imp.getFrame();
Overlay overlay2 = overlay.create();
Roi roi;
int c, z, t;
for (Roi r : overlay.toArray()) {
c = r.getCPosition();
z = r.hasHyperStackPosition()?r.getZPosition():0;
t = r.getTPosition();
roi = (Roi)r.clone();
if (z>=startSlice && z<=stopSlice || z==0 || c==0 || t==0) {
if (frames==1 && t!=t1 && t!=0)//current time frame
continue;
roi.setPosition(t);
overlay2.add(roi);
}
}
return overlay2;
}
//Added by Marcel Boeglin 2013.09.22
private Overlay projectHyperStackRois(Overlay overlay) {
if (overlay==null) return null;
int t1 = imp.getFrame();
int channels = projImage.getNChannels();
int slices = 1;
int frames = projImage.getNFrames();
Overlay overlay2 = overlay.create();
Roi roi;
int c, z, t;
int size = channels * slices * frames;
for (Roi r : overlay.toArray()) {
c = r.getCPosition();
z = r.getZPosition();
t = r.getTPosition();
roi = (Roi)r.clone();
if (size==channels) {//current time frame
if (z>=startSlice && z<=stopSlice && t==t1 || c==0) {
roi.setPosition(c);
overlay2.add(roi);
}
}
else if (size==frames*channels) {//all time frames
if (z>=startSlice && z<=stopSlice)
roi.setPosition(c, 1, t);
else if (z==0)
roi.setPosition(c, 0, t);
else continue;
overlay2.add(roi);
}
}
return overlay2;
}
private void doHSRGBProjection(ImagePlus rgbImp) {
ImageStack stack = rgbImp.getStack();
ImageStack stack2 = new ImageStack(stack.getWidth(), stack.getHeight());
for (int i=startSlice; i<=stopSlice; i++)
stack2.addSlice(null, stack.getProcessor(i));
startSlice = 1;
stopSlice = stack2.getSize();
doRGBProjection(stack2);
}
private RayFunction getRayFunction(int method, FloatProcessor fp) {
switch (method) {
case AVG_METHOD: case SUM_METHOD:
return new AverageIntensity(fp, sliceCount);
case MAX_METHOD:
return new MaxIntensity(fp);
case MIN_METHOD:
return new MinIntensity(fp);
case SD_METHOD:
return new StandardDeviation(fp, sliceCount);
default:
IJ.error("Z Project", "Unknown method.");
return null;
}
}
/** Generate output image whose type is same as input image. */
private ImagePlus makeOutputImage(ImagePlus imp, FloatProcessor fp, int ptype) {
int width = imp.getWidth();
int height = imp.getHeight();
float[] pixels = (float[])fp.getPixels();
ImageProcessor oip=null;
// Create output image consistent w/ type of input image.
int size = pixels.length;
switch (ptype) {
case BYTE_TYPE:
oip = imp.getProcessor().createProcessor(width,height);
byte[] pixels8 = (byte[])oip.getPixels();
for(int i=0; ifpixels[i])
fpixels[i] = (pixels[i]&0xff);
}
}
public void projectSlice(short[] pixels) {
for(int i=0; ifpixels[i])
fpixels[i] = pixels[i]&0xffff;
}
}
public void projectSlice(float[] pixels) {
for(int i=0; ifpixels[i])
fpixels[i] = pixels[i];
}
}
} // end MaxIntensity
/** Compute min intensity projection. */
class MinIntensity extends RayFunction {
private float[] fpixels;
private int len;
/** Simple constructor since no preprocessing is necessary. */
public MinIntensity(FloatProcessor fp) {
fpixels = (float[])fp.getPixels();
len = fpixels.length;
for (int i=0; i1) {
stdDev = (n*sum2[i]-sum[i]*sum[i])/n;
if (stdDev>0.0)
result[i] = (float)Math.sqrt(stdDev/(n-1.0));
else
result[i] = 0f;
} else
result[i] = 0f;
}
}
} // end StandardDeviation
} // end ZProjection
Ads help maintain this website.