package ij.plugin;
import java.awt.*;
import java.io.*;
import java.text.DecimalFormat;
import java.util.*;
import ij.*;
import ij.io.*;
import ij.gui.*;
import ij.measure.Calibration;
import ij.process.*;
import ij.plugin.frame.Recorder;
import ij.macro.Interpreter;
import ij.util.Tools;
public class StackWriter implements PlugIn {
private static final String DIR_KEY = "save.sequence.dir";
private static String[] choices = {"BMP", "FITS", "GIF", "JPEG", "PGM", "PNG", "Raw", "Text", "TIFF", "ZIP"};
private static String staticFileType = "TIFF";
private String fileType = "TIFF";
private int ndigits = 4;
private boolean useLabels;
private boolean firstTime = true;
private int startAt;
private boolean hyperstack;
private int[] dim;
private ImagePlus imp;
private String directory;
private String format = "tiff";
private String name;
public static void save(ImagePlus imp, String directoryPath, String options) {
StackWriter sw = new StackWriter();
sw.imp = imp;
sw.format = Tools.getStringFromList(options, "format=", sw.format);
sw.name = Tools.getStringFromList(options, "name=");
sw.ndigits = (int)Tools.getNumberFromList(options, "digits=", sw.ndigits);
sw.useLabels = options.contains(" use");
sw.run(directoryPath);
}
public void run(String arg) {
if (imp==null)
imp = WindowManager.getCurrentImage();
if (imp==null || (imp!=null && imp.getStackSize()<2&&!IJ.isMacro())) {
IJ.error("Stack Writer", "This command requires a stack.");
return;
}
int stackSize = imp.getStackSize();
if (name==null) {
name = imp.getTitle();
int dotIndex = name.lastIndexOf(".");
if (dotIndex>=0)
name = name.substring(0, dotIndex);
}
hyperstack = imp.isHyperStack();
LUT[] luts = null;
int lutIndex = 0;
int nChannels = imp.getNChannels();
if (hyperstack) {
dim = imp.getDimensions();
if (imp.isComposite())
luts = ((CompositeImage)imp).getLuts();
if (firstTime && ndigits==4) {
ndigits = 3;
firstTime = false;
}
}
if (arg!=null && arg.length()>0)
directory = arg;
else {
if (!showDialog(imp))
return;
}
File d = new File(directory);
if (d==null || !d.isDirectory()) {
IJ.error("File>Save As>Image Sequence", "Directory not found: "+directory);
return;
}
int number = 0;
if (ndigits<1) ndigits = 1;
if (ndigits>8) ndigits = 8;
int maxImages = (int)Math.pow(10,ndigits);
if (stackSize>maxImages && !useLabels && !hyperstack) {
IJ.error("Stack Writer", "More than " + ndigits
+" digits are required to generate \nunique file names for "+stackSize+" images.");
return;
}
if (format.equals("fits") && !FileSaver.okForFits(imp))
return;
if (format.equals("text"))
format = "text image";
String extension = "." + format;
if (format.equals("tiff"))
extension = ".tif";
else if (format.equals("text image"))
extension = ".txt";
Overlay overlay = imp.getOverlay();
boolean isOverlay = overlay!=null && !imp.getHideOverlay();
if (!(format.equals("jpeg")||format.equals("png")))
isOverlay = false;
ImageStack stack = imp.getStack();
ImagePlus imp2 = new ImagePlus();
imp2.setTitle(imp.getTitle());
Calibration cal = imp.getCalibration();
int nSlices = stack.size();
String path,label=null;
imp.lock();
for (int i=1; i<=nSlices; i++) {
IJ.showStatus("writing: "+i+"/"+nSlices);
IJ.showProgress(i, nSlices);
ImageProcessor ip = stack.getProcessor(i);
if (isOverlay) {
imp.setSliceWithoutUpdate(i);
ip = imp.flatten().getProcessor();
} else if (luts!=null && nChannels>1 && hyperstack) {
ip.setColorModel(luts[lutIndex++]);
if (lutIndex>=luts.length) lutIndex = 0;
}
imp2.setProcessor(null, ip);
String label2 = stack.getSliceLabel(i);
imp2.setProp("Slice_Label", null);
if (label2!=null) {
if (label2.contains("\n"))
imp2.setProperty("Info", label2);
else
imp2.setProp("Slice_Label", label2);;
} else {
Properties props = imp2.getProperties();
if (props!=null) props.remove("Info");
}
imp2.setCalibration(cal);
String digits = getDigits(number++);
if (useLabels) {
label = stack.getShortSliceLabel(i, 111);
if (label!=null && label.equals("")) label = null;
if (label!=null) label = label.replaceAll("/","-");
}
if (label==null)
path = directory+name+digits+extension;
else
path = directory+label+extension;
if (i==1) {
File f = new File(path);
if (f.exists()) {
if (!IJ.isMacro() && !IJ.showMessageWithCancel("Overwrite files?",
"One or more files will be overwritten if you click \"OK\".\n \n"+path)) {
imp.unlock();
IJ.showStatus("");
IJ.showProgress(1.0);
return;
}
}
}
if (Recorder.record)
Recorder.disablePathRecording();
imp2.setOverlay(null);
if (overlay!=null && format.equals("tiff")) {
Overlay overlay2 = overlay.duplicate();
overlay2.crop(i, i);
if (overlay2.size()>0) {
for (int j=0; j<overlay2.size(); j++) {
Roi roi = overlay2.get(j);
int pos = roi.getPosition();
if (pos==1)
roi.setPosition(i);
}
imp2.setOverlay(overlay2);
}
}
IJ.saveAs(imp2, format, path);
}
imp.unlock();
if (isOverlay) imp.setSlice(1);
IJ.showStatus("");
}
private boolean showDialog(ImagePlus imp) {
String options = Macro.getOptions();
if (options!=null && options.contains("save=")) Macro.setOptions(options.replaceAll("save=", "dir="));
directory = Prefs.get(DIR_KEY, IJ.getDir("downloads")+"stack2/");
GenericDialog gd = new GenericDialog("Save Image Sequence");
if (!IJ.isMacro())
fileType = staticFileType;
gd.setInsets(5, 0, 0);
gd.addDirectoryField("Dir:", directory);
gd.setInsets(2, 110, 5);
gd.addMessage("drag and drop target", IJ.font10, Color.darkGray);
gd.addChoice("Format:", choices, fileType);
gd.addStringField("Name:", name, 12);
if (!hyperstack)
gd.addNumericField("Start At:", startAt, 0);
gd.addNumericField("Digits (1-8):", ndigits, 0);
if (!hyperstack)
gd.addCheckbox("Use slice labels as file names", useLabels);
gd.showDialog();
if (gd.wasCanceled())
return false;
directory = gd.getNextString();
directory = IJ.addSeparator(directory);
Prefs.set(DIR_KEY, directory);
gd.setSmartRecording(true);
fileType = gd.getNextChoice();
format = fileType.toLowerCase(Locale.US);
if (!IJ.isMacro())
staticFileType = fileType;
String name2 = gd.getNextString();
boolean nameChanged = !name2.equals(name);
name = name2;
if (!hyperstack)
startAt = (int)gd.getNextNumber();
if (startAt<0) startAt = 0;
int ndigits2 = (int)gd.getNextNumber();
boolean ndigitsChanged = ndigits2!=ndigits;
ndigits = ndigits2;
if (!hyperstack)
useLabels = gd.getNextBoolean();
else
useLabels = false;
if (Recorder.record) {
String options2 = "format="+format;
if (nameChanged)
options2 += " name="+name;
if (ndigitsChanged)
options2 += " digits="+ndigits;
if (useLabels)
options2 += " use";
String dir = Recorder.fixPath(directory);
Recorder.recordCall("StackWriter.save(imp, \""+dir+"\", \""+options2+"\");");
}
return true;
}
String getDigits(int n) {
if (hyperstack) {
int c = (n%dim[2])+1;
int z = ((n/dim[2])%dim[3])+1;
int t = ((n/(dim[2]*dim[3]))%dim[4])+1;
String cs="", zs="", ts="";
if (dim[2]>1) {
cs = "00000000"+c;
cs = "_c"+cs.substring(cs.length()-ndigits);
}
if (dim[3]>1) {
zs = "00000000"+z;
zs = "_z"+zs.substring(zs.length()-ndigits);
}
if (dim[4]>1) {
ts = "00000000"+t;
ts = "_t"+ts.substring(ts.length()-ndigits);
}
return ts+zs+cs;
} else {
String digits = "00000000"+(startAt+n);
return digits.substring(digits.length()-ndigits);
}
}
}