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;
/** This plugin, which saves the images in a stack as separate files,
implements the File/Save As/Image Sequence command. */
public class StackWriter implements PlugIn {
//private static String defaultDirectory = null;
private static String[] choices = {"BMP", "FITS", "GIF", "JPEG", "PGM", "PNG", "Raw", "Text", "TIFF", "ZIP"};
private static String fileType = "TIFF";
private static int ndigits = 4;
private static boolean useLabels;
private static boolean firstTime = true;
private int startAt;
private boolean hyperstack;
private int[] dim;
//private static boolean startAtZero;
public void run(String arg) {
ImagePlus imp = WindowManager.getCurrentImage();
if (imp==null || (imp!=null && imp.getStackSize()<2)) {
IJ.error("Stack Writer", "This command requires a stack.");
return;
}
int stackSize = imp.getStackSize();
String 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;
}
}
GenericDialog gd = new GenericDialog("Save Image Sequence");
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;
fileType = gd.getNextChoice();
name = gd.getNextString();
if (!hyperstack)
startAt = (int)gd.getNextNumber();
if (startAt<0) startAt = 0;
ndigits = (int)gd.getNextNumber();
if (!hyperstack)
useLabels = gd.getNextBoolean();
else
useLabels = false;
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;
}
String format = fileType.toLowerCase(Locale.US);
if (format.equals("gif") && !FileSaver.okForGif(imp))
return;
else 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";
String title = "Save Image Sequence";
String macroOptions = Macro.getOptions();
String directory = null;
if (macroOptions!=null) {
directory = Macro.getValue(macroOptions, title, null);
if (directory!=null) {
File f = new File(directory);
if (!f.isDirectory() && (f.exists()||directory.lastIndexOf(".")>directory.length()-5))
directory = f.getParent();
if (!directory.endsWith(File.separator))
directory+= File.separator;
}
}
if (directory==null) {
if (Prefs.useFileChooser && !IJ.isMacOSX()) {
String digits = getDigits(number);
SaveDialog sd = new SaveDialog(title, name+digits+extension, extension);
String name2 = sd.getFileName();
if (name2==null)
return;
directory = sd.getDirectory();
} else
directory = IJ.getDirectory(title);
}
if (directory==null)
return;
boolean isOverlay = imp.getOverlay()!=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.getSize();
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);
if (label2!=null && label2.indexOf("\n")!=-1)
imp2.setProperty("Info", 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);
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 (Recorder.record)
Recorder.disablePathRecording();
IJ.saveAs(imp2, format, path);
}
imp.unlock();
if (isOverlay) imp.setSlice(1);
IJ.showStatus("");
}
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);
}
}
}