package ij.plugin;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.util.Tools;
import ij.plugin.frame.Recorder;
import ij.measure.Calibration;
/** This plugin implements the Image/Duplicate command.
<pre>
// test script
img1 = IJ.getImage();
img2 = new Duplicator().run(img1);
//img2 = new Duplicator().run(img1,1,10);
img2.show();
</pre>
*/
public class Duplicator implements PlugIn, TextListener {
private static boolean duplicateStack;
private boolean duplicateSubstack;
private int first, last;
private Checkbox checkbox;
private TextField rangeField;
private TextField[] rangeFields;
private int firstC, lastC, firstZ, lastZ, firstT, lastT;
public void run(String arg) {
ImagePlus imp = IJ.getImage();
int stackSize = imp.getStackSize();
String title = imp.getTitle();
String newTitle = WindowManager.getUniqueName(title);
if (!IJ.altKeyDown()||stackSize>1) {
if (imp.isHyperStack() || imp.isComposite()) {
duplicateHyperstack(imp, newTitle);
return;
} else
newTitle = showDialog(imp, "Duplicate...", "Title: ", newTitle);
}
if (newTitle==null)
return;
ImagePlus imp2;
Roi roi = imp.getRoi();
if (duplicateSubstack && (first>1||last<stackSize))
imp2 = run(imp, first, last);
else if (duplicateStack || imp.getStackSize()==1)
imp2 = run(imp);
else
imp2 = duplicateImage(imp);
Calibration cal = imp2.getCalibration();
if (roi!=null && (cal.xOrigin!=0.0||cal.yOrigin!=0.0)) {
cal.xOrigin -= roi.getBounds().x;
cal.yOrigin -= roi.getBounds().y;
}
imp2.setTitle(newTitle);
imp2.show();
if (roi!=null && roi.isArea() && roi.getType()!=Roi.RECTANGLE && roi.getBounds().width==imp2.getWidth())
imp2.restoreRoi();
}
/** Returns a copy of the image, stack or hyperstack contained in the specified ImagePlus. */
public ImagePlus run(ImagePlus imp) {
if (Recorder.record) Recorder.recordCall("imp = new Duplicator().run(imp);");
if (imp.getStackSize()==1)
return duplicateImage(imp);
Rectangle rect = null;
Roi roi = imp.getRoi();
if (roi!=null && roi.isArea())
rect = roi.getBounds();
ImageStack stack = imp.getStack();
ImageStack stack2 = null;
for (int i=1; i<=stack.getSize(); i++) {
ImageProcessor ip2 = stack.getProcessor(i);
ip2.setRoi(rect);
ip2 = ip2.crop();
if (stack2==null)
stack2 = new ImageStack(ip2.getWidth(), ip2.getHeight(), imp.getProcessor().getColorModel());
stack2.addSlice(stack.getSliceLabel(i), ip2);
}
ImagePlus imp2 = imp.createImagePlus();
imp2.setStack("DUP_"+imp.getTitle(), stack2);
int[] dim = imp.getDimensions();
imp2.setDimensions(dim[2], dim[3], dim[4]);
if (imp.isComposite()) {
imp2 = new CompositeImage(imp2, 0);
((CompositeImage)imp2).copyLuts(imp);
}
if (imp.isHyperStack())
imp2.setOpenAsHyperStack(true);
Overlay overlay = imp.getOverlay();
if (overlay!=null && !imp.getHideOverlay()) {
overlay = overlay.duplicate();
if (rect!=null)
overlay.translate(-rect.x, -rect.y);
imp2.setOverlay(overlay);
}
return imp2;
}
ImagePlus duplicateImage(ImagePlus imp) {
ImageProcessor ip = imp.getProcessor();
ImageProcessor ip2 = ip.crop();
ImagePlus imp2 = imp.createImagePlus();
imp2.setProcessor("DUP_"+imp.getTitle(), ip2);
String info = (String)imp.getProperty("Info");
if (info!=null)
imp2.setProperty("Info", info);
if (imp.getStackSize()>1) {
ImageStack stack = imp.getStack();
String label = stack.getSliceLabel(imp.getCurrentSlice());
if (label!=null && label.indexOf('\n')>0)
imp2.setProperty("Info", label);
if (imp.isComposite()) {
LUT lut = ((CompositeImage)imp).getChannelLut();
imp2.getProcessor().setColorModel(lut);
}
}
Overlay overlay = imp.getOverlay();
if (overlay!=null && !imp.getHideOverlay()) {
overlay = overlay.duplicate();
Rectangle r = ip.getRoi();
if (r.x>0 || r.y>0)
overlay.translate(-r.x, -r.y);
imp2.setOverlay(overlay);
}
return imp2;
}
/** Returns a new stack containing a subrange of the specified stack. */
public ImagePlus run(ImagePlus imp, int firstSlice, int lastSlice) {
Rectangle rect = null;
Roi roi = imp.getRoi();
if (roi!=null && roi.isArea())
rect = roi.getBounds();
ImageStack stack = imp.getStack();
ImageStack stack2 = null;
for (int i=firstSlice; i<=lastSlice; i++) {
ImageProcessor ip2 = stack.getProcessor(i);
ip2.setRoi(rect);
ip2 = ip2.crop();
if (stack2==null)
stack2 = new ImageStack(ip2.getWidth(), ip2.getHeight(), imp.getProcessor().getColorModel());
stack2.addSlice(stack.getSliceLabel(i), ip2);
}
ImagePlus imp2 = imp.createImagePlus();
imp2.setStack("DUP_"+imp.getTitle(), stack2);
int size = stack2.getSize();
boolean tseries = imp.getNFrames()==imp.getStackSize();
if (tseries)
imp2.setDimensions(1, 1, size);
else
imp2.setDimensions(1, size, 1);
if (Recorder.record) Recorder.recordCall("imp = new Duplicator().run(imp, "+firstSlice+", "+lastSlice+");");
return imp2;
}
/** Returns a new hyperstack containing a possibly reduced version of the input image. */
public ImagePlus run(ImagePlus imp, int firstC, int lastC, int firstZ, int lastZ, int firstT, int lastT) {
Rectangle rect = null;
Roi roi = imp.getRoi();
if (roi!=null && roi.isArea())
rect = roi.getBounds();
ImageStack stack = imp.getStack();
ImageStack stack2 = null;
for (int t=firstT; t<=lastT; t++) {
for (int z=firstZ; z<=lastZ; z++) {
for (int c=firstC; c<=lastC; c++) {
int n1 = imp.getStackIndex(c, z, t);
ImageProcessor ip = stack.getProcessor(n1);
String label = stack.getSliceLabel(n1);
ip.setRoi(rect);
ip = ip.crop();
if (stack2==null)
stack2 = new ImageStack(ip.getWidth(), ip.getHeight(), null);
stack2.addSlice(label, ip);
}
}
}
ImagePlus imp2 = imp.createImagePlus();
imp2.setStack("DUP_"+imp.getTitle(), stack2);
imp2.setDimensions(lastC-firstC+1, lastZ-firstZ+1, lastT-firstT+1);
if (imp.isComposite()) {
int mode = ((CompositeImage)imp).getMode();
if (lastC>firstC) {
imp2 = new CompositeImage(imp2, mode);
int i2 = 1;
for (int i=firstC; i<=lastC; i++) {
LUT lut = ((CompositeImage)imp).getChannelLut(i);
((CompositeImage)imp2).setChannelLut(lut, i2++);
}
} else if (firstC==lastC) {
LUT lut = ((CompositeImage)imp).getChannelLut(firstC);
imp2.getProcessor().setColorModel(lut);
imp2.setDisplayRange(lut.min, lut.max);
}
}
imp2.setOpenAsHyperStack(true);
if (Recorder.record)
Recorder.recordCall("imp = new Duplicator().run(imp, "+firstC+", "+lastC+", "+firstZ+", "+lastZ+", "+firstT+", "+lastT+");");
return imp2;
}
String showDialog(ImagePlus imp, String title, String prompt, String defaultString) {
int stackSize = imp.getStackSize();
duplicateSubstack = stackSize>1 && (stackSize==imp.getNSlices()||stackSize==imp.getNFrames());
GenericDialog gd = new GenericDialog(title);
gd.addStringField(prompt, defaultString, duplicateSubstack?15:20);
if (stackSize>1) {
String msg = duplicateSubstack?"Duplicate stack":"Duplicate entire stack";
gd.addCheckbox(msg, duplicateStack||imp.isComposite());
if (duplicateSubstack) {
gd.setInsets(2, 30, 3);
gd.addStringField("Range:", "1-"+stackSize);
Vector v = gd.getStringFields();
rangeField = (TextField)v.elementAt(1);
rangeField.addTextListener(this);
checkbox = (Checkbox)(gd.getCheckboxes().elementAt(0));
}
} else
duplicateStack = false;
gd.showDialog();
if (gd.wasCanceled())
return null;
title = gd.getNextString();
if (stackSize>1) {
duplicateStack = gd.getNextBoolean();
if (duplicateStack && duplicateSubstack) {
String[] range = Tools.split(gd.getNextString(), " -");
double d1 = gd.parseDouble(range[0]);
double d2 = range.length==2?gd.parseDouble(range[1]):Double.NaN;
first = Double.isNaN(d1)?1:(int)d1;
last = Double.isNaN(d2)?stackSize:(int)d2;
if (first<1) first = 1;
if (last>stackSize) last = stackSize;
if (first>last) {first=1; last=stackSize;}
} else {
first = 1;
last = stackSize;
}
}
return title;
}
void duplicateHyperstack(ImagePlus imp, String newTitle) {
newTitle = showHSDialog(imp, newTitle);
if (newTitle==null)
return;
ImagePlus imp2 = null;
Roi roi = imp.getRoi();
if (!duplicateStack) {
int nChannels = imp.getNChannels();
boolean singleComposite = imp.isComposite() && nChannels==imp.getStackSize();
if (!singleComposite && nChannels>1 && imp.isComposite() && ((CompositeImage)imp).getMode()==CompositeImage.COMPOSITE) {
firstC = 1;
lastC = nChannels;
} else
firstC = lastC = imp.getChannel();
firstZ = lastZ = imp.getSlice();
firstT = lastT = imp.getFrame();
}
imp2 = run(imp, firstC, lastC, firstZ, lastZ, firstT, lastT);
if (imp2==null) return;
Calibration cal = imp2.getCalibration();
if (roi!=null && (cal.xOrigin!=0.0||cal.yOrigin!=0.0)) {
cal.xOrigin -= roi.getBounds().x;
cal.yOrigin -= roi.getBounds().y;
}
imp2.setTitle(newTitle);
imp2.show();
if (roi!=null && roi.isArea() && roi.getType()!=Roi.RECTANGLE && roi.getBounds().width==imp2.getWidth())
imp2.restoreRoi();
if (IJ.isMacro()&&imp2.getWindow()!=null)
IJ.wait(50);
}
String showHSDialog(ImagePlus imp, String newTitle) {
int nChannels = imp.getNChannels();
int nSlices = imp.getNSlices();
int nFrames = imp.getNFrames();
boolean composite = imp.isComposite() && nChannels==imp.getStackSize();
GenericDialog gd = new GenericDialog("Duplicate");
gd.addStringField("Title:", newTitle, 15);
gd.setInsets(12, 20, 8);
gd.addCheckbox("Duplicate hyperstack", duplicateStack||composite);
int nRangeFields = 0;
if (nChannels>1) {
gd.setInsets(2, 30, 3);
gd.addStringField("Channels (c):", "1-"+nChannels);
nRangeFields++;
}
if (nSlices>1) {
gd.setInsets(2, 30, 3);
gd.addStringField("Slices (z):", "1-"+nSlices);
nRangeFields++;
}
if (nFrames>1) {
gd.setInsets(2, 30, 3);
gd.addStringField("Frames (t):", "1-"+nFrames);
nRangeFields++;
}
Vector v = gd.getStringFields();
rangeFields = new TextField[3];
for (int i=0; i<nRangeFields; i++) {
rangeFields[i] = (TextField)v.elementAt(i+1);
rangeFields[i].addTextListener(this);
}
checkbox = (Checkbox)(gd.getCheckboxes().elementAt(0));
gd.showDialog();
if (gd.wasCanceled())
return null;
newTitle = gd.getNextString();
duplicateStack = gd.getNextBoolean();
if (nChannels>1) {
String[] range = Tools.split(gd.getNextString(), " -");
double c1 = gd.parseDouble(range[0]);
double c2 = range.length==2?gd.parseDouble(range[1]):Double.NaN;
firstC = Double.isNaN(c1)?1:(int)c1;
lastC = Double.isNaN(c2)?firstC:(int)c2;
if (firstC<1) firstC = 1;
if (lastC>nChannels) lastC = nChannels;
if (firstC>lastC) {firstC=1; lastC=nChannels;}
} else
firstC = lastC = 1;
if (nSlices>1) {
String[] range = Tools.split(gd.getNextString(), " -");
double z1 = gd.parseDouble(range[0]);
double z2 = range.length==2?gd.parseDouble(range[1]):Double.NaN;
firstZ = Double.isNaN(z1)?1:(int)z1;
lastZ = Double.isNaN(z2)?firstZ:(int)z2;
if (firstZ<1) firstZ = 1;
if (lastZ>nSlices) lastZ = nSlices;
if (firstZ>lastZ) {firstZ=1; lastZ=nSlices;}
} else
firstZ = lastZ = 1;
if (nFrames>1) {
String[] range = Tools.split(gd.getNextString(), " -");
double t1 = gd.parseDouble(range[0]);
double t2 = range.length==2?gd.parseDouble(range[1]):Double.NaN;
firstT= Double.isNaN(t1)?1:(int)t1;
lastT = Double.isNaN(t2)?firstT:(int)t2;
if (firstT<1) firstT = 1;
if (lastT>nFrames) lastT = nFrames;
if (firstT>lastT) {firstT=1; lastT=nFrames;}
} else
firstT = lastT = 1;
return newTitle;
}
public void textValueChanged(TextEvent e) {
checkbox.setState(true);
}
}