package ij.plugin;
import ij.plugin.frame.Recorder;
import ij.*;
import ij.gui.*;
import ij.process.*;
import ij.measure.Calibration;
import ij.macro.Interpreter;
import ij.io.FileInfo;
import java.awt.Color;
public class ImagesToStack implements PlugIn {
private static final int rgb = 33;
private static final int COPY_CENTER=0, COPY_TOP_LEFT=1, SCALE_SMALL=2, SCALE_LARGE=3;
private static final String[] methods = {"Copy (center)", "Copy (top-left)", "Scale (smallest)", "Scale (largest)"};
private static int staticMethod = COPY_CENTER;
private static boolean staticBicubic;
private static boolean staticKeep;
private static boolean staticTitlesAsLabels = true;
private int method = COPY_CENTER;
private boolean bicubic;
private boolean keep;
private boolean titlesAsLabels = true;
private String filter;
private int width, height;
private int maxWidth, maxHeight;
private int minWidth, minHeight;
private int minSize, maxSize;
private boolean allInvertedLuts;
private Calibration cal2;
private int stackType;
private ImagePlus[] images;
private String name = "Stack";
private Color fillColor;
public static ImagePlus run(ImagePlus[] images) {
ImagesToStack itos = new ImagesToStack();
int count = itos.findMinMaxSize(images, images.length);
return itos.convert(images, count);
}
public void run(String arg) {
convertImagesToStack();
}
public void convertImagesToStack() {
boolean scale = false;
int[] wList = WindowManager.getIDList();
if (wList==null) {
IJ.error("No images are open.");
return;
}
int count = 0;
int stackCount = 0;
images = new ImagePlus[wList.length];
for (int i=0; i<wList.length; i++) {
ImagePlus imp = WindowManager.getImage(wList[i]);
if (imp.getStackSize()==1)
images[count++] = imp;
else
stackCount++;
}
if (count<2) {
String msg = "";
if (stackCount>1)
msg = "\n \nUse the Image>Stacks>Tools>Concatenate\ncommand to combine stacks.";
IJ.error("Images to Stack", "There must be at least two open 2D images."+msg);
return;
}
filter = null;
count = findMinMaxSize(images, count);
boolean sizesDiffer = width!=minWidth||height!=minHeight;
boolean showDialog = true;
String macroOptions = Macro.getOptions();
if (IJ.macroRunning() && macroOptions==null) {
if (sizesDiffer) {
IJ.error("Images are not all the same size");
return;
}
showDialog = false;
}
if (showDialog) {
GenericDialog gd = new GenericDialog("Images to Stack");
if (sizesDiffer) {
String msg = "The "+count+" images differ in size (smallest="+minWidth+"x"+minHeight
+",\nlargest="+maxWidth+"x"+maxHeight+"). They will be converted\nto a stack using the specified method.";
gd.setInsets(0,0,5);
gd.addMessage(msg);
gd.addChoice("Method:", methods, methods[staticMethod]);
}
gd.setSmartRecording(true);
gd.addStringField("Name:", name, 12);
gd.addStringField("Title contains:", "", 12);
gd.addStringField("Fill color:", "", 12);
if (sizesDiffer)
gd.addCheckbox("Bicubic interpolation", staticBicubic);
gd.addCheckbox("Use titles as labels", staticTitlesAsLabels);
gd.addCheckbox("Keep source images", staticKeep);
gd.showDialog();
if (gd.wasCanceled()) return;
if (sizesDiffer)
method = gd.getNextChoiceIndex();
name = gd.getNextString();
filter = gd.getNextString();
String fillc = gd.getNextString();
fillColor = Colors.decode(fillc, null);
if (sizesDiffer)
bicubic = gd.getNextBoolean();
titlesAsLabels = gd.getNextBoolean();
keep = gd.getNextBoolean();
if (filter!=null && (filter.equals("") || filter.equals("*")))
filter = null;
if (filter!=null) {
count = findMinMaxSize(images, count);
if (count==0) {
IJ.error("Images to Stack", "None of the images have a title containing \""+filter+"\"");
}
}
if (!IJ.isMacro()) {
staticMethod = method;
staticBicubic = bicubic;
staticKeep = keep;
staticTitlesAsLabels = titlesAsLabels;
}
if (Recorder.record)
Recorder.recordCall("imp = ImagesToStack.run(arrayOfImages);");
} else
keep = false;
if (method==SCALE_SMALL) {
width = minWidth;
height = minHeight;
} else if (method==SCALE_LARGE) {
width = maxWidth;
height = maxHeight;
}
ImagePlus stack = convert(images, count);
if (stack!=null)
stack.show();
}
private ImagePlus convert(ImagePlus[] images, int count) {
double min = Double.MAX_VALUE;
double max = -Double.MAX_VALUE;
ImageStack stack = new ImageStack(width, height);
FileInfo fi = images[0].getOriginalFileInfo();
if (fi!=null && fi.directory==null) fi = null;
Overlay overlay = new Overlay();
for (int i=0; i<count; i++) {
ImageProcessor ip = images[i].getProcessor();
boolean invertedLut = ip.isInvertedLut();
if (ip.getMin()<min) min = ip.getMin();
if (ip.getMax()>max) max = ip.getMax();
String label = titlesAsLabels?images[i].getTitle():null;
if (label==null)
label = images[i].getProp("Slice_Label");
if (label!=null) {
String info = (String)images[i].getProperty("Info");
if (info!=null) label += "\n" + info;
}
if (fi!=null) {
FileInfo fi2 = images[i].getOriginalFileInfo();
if (fi2!=null && !fi.directory.equals(fi2.directory))
fi = null;
}
switch (stackType) {
case 16: ip = ip.convertToShort(false); break;
case 32: ip = ip.convertToFloat(); break;
case rgb: ip = ip.convertToRGB(); break;
default: break;
}
if (invertedLut && !allInvertedLuts) {
if (keep)
ip = ip.duplicate();
ip.invert();
}
if (ip.getWidth()!=width||ip.getHeight()!=height) {
switch (method) {
case COPY_TOP_LEFT: case COPY_CENTER:
ImageProcessor ip2 = null;
switch (stackType) {
case 8: ip2 = new ByteProcessor(width, height); break;
case 16: ip2 = new ShortProcessor(width, height); break;
case 32: ip2 = new FloatProcessor(width, height); break;
case rgb: ip2 = new ColorProcessor(width, height); break;
}
if (fillColor!=null) {
ip2.setColor(fillColor);
ip2.fill();
}
int xoff=0, yoff=0;
if (method==COPY_CENTER) {
xoff = (width-ip.getWidth())/2;
yoff = (height-ip.getHeight())/2;
}
ip2.insert(ip, xoff, yoff);
ip = ip2;
break;
case SCALE_SMALL: case SCALE_LARGE:
ip.setInterpolationMethod((bicubic?ImageProcessor.BICUBIC:ImageProcessor.BILINEAR));
ip.resetRoi();
ip = ip.resize(width, height);
break;
}
} else {
if (keep)
ip = ip.duplicate();
Overlay overlay2 = images[i].getOverlay();
if (overlay2!=null) {
for (int j=0; j<overlay2.size(); j++) {
Roi roi = overlay2.get(j);
roi.setPosition(i+1);
overlay.add((Roi)roi.clone());
}
}
}
stack.addSlice(label, ip);
if (i==0 && invertedLut && !allInvertedLuts)
stack.setColorModel(null);
if (!keep) {
images[i].changes = false;
images[i].close();
}
}
if (stack.size()==0)
return null;
ImagePlus imp = new ImagePlus(name, stack);
if (stackType==16 || stackType==32)
imp.getProcessor().setMinAndMax(min, max);
if (cal2!=null)
imp.setCalibration(cal2);
if (fi!=null) {
fi.fileName = "";
fi.nImages = imp.getStackSize();
imp.setFileInfo(fi);
}
if (overlay.size()>0)
imp.setOverlay(overlay);
return imp;
}
private int findMinMaxSize(ImagePlus[] images, int count) {
int index = 0;
stackType = 8;
width = 0;
height = 0;
cal2 = images[0].getCalibration();
maxWidth = 0;
maxHeight = 0;
minWidth = Integer.MAX_VALUE;
minHeight = Integer.MAX_VALUE;
minSize = Integer.MAX_VALUE;
allInvertedLuts = true;
maxSize = 0;
for (int i=0; i<count; i++) {
if (exclude(images[i].getTitle())) continue;
if (images[i].getType()==ImagePlus.COLOR_256)
stackType = rgb;
if (!images[i].getProcessor().isInvertedLut())
allInvertedLuts = false;
int type = images[i].getBitDepth();
if (type==24) type = rgb;
if (type>stackType) stackType = type;
int w=images[i].getWidth(), h=images[i].getHeight();
if (w>width) width = w;
if (h>height) height = h;
int size = w*h;
if (size<minSize) {
minSize = size;
minWidth = w;
minHeight = h;
}
if (size>maxSize) {
maxSize = size;
maxWidth = w;
maxHeight = h;
}
Calibration cal = images[i].getCalibration();
if (!images[i].getCalibration().equals(cal2))
cal2 = null;
images[index++] = images[i];
}
return index;
}
final boolean exclude(String title) {
return filter!=null && title!=null && title.indexOf(filter)==-1;
}
}