package ij.plugin;
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.util.Tools;
import ij.io.*;
import ij.macro.Interpreter;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.Vector;
/** This plugin implements the File/Batch/Macro and File/Batch/Virtual Stack commands. */
public class BatchProcessor implements PlugIn, ActionListener, ItemListener, Runnable {
private static final String MACRO_FILE_NAME = "BatchMacro.ijm";
private static final String[] formats = {"TIFF", "8-bit TIFF", "JPEG", "GIF", "PNG", "PGM", "BMP", "FITS", "Text Image", "ZIP", "Raw"};
private static String format = Prefs.get("batch.format", formats[0]);
private static final String[] code = {
"[Select from list]",
"Add Border",
"Convert to RGB",
"Crop",
"Gaussian Blur",
"Invert",
"Label",
"Timestamp",
"Max Dimension",
"Measure",
"Print Index and Title",
"Resize",
"Scale",
"Show File Info",
"Unsharp Mask",
};
private static final String help = ""
+"
Process>Batch>Virtual Stack
"
+""
+"This command runs macro code on each image in a virtual stack.
"
+"The processed images are saved in the Output folder,
"
+"in the specified Format, allowing them to be opened as a
"
+"virtual stack. Make sure the Output folder is empty
"
+"before clicking on Process.
"
+"
"
+"In the macro code, the 'i' (slice index) and 'n' (stack size) variables
"
+"are predefined. Call setOption('SaveBatchOutput',false) to
"
+"prevent the image currently being processed from being saved,
"
+"effectively removing it from the output virtual stack.
"
+"";
private String macro = "";
private int testImage;
private Button input, output, open, save, test;
private TextField inputDir, outputDir;
private GenericDialog gd;
private Thread thread;
private ImagePlus virtualStack;
private ImagePlus outputImage;
private boolean errorDisplayed;
private String filter;
private static boolean saveOutput = true;
public void run(String arg) {
if (arg.equals("stack")) {
virtualStack = IJ.getImage();
if (virtualStack.getStackSize()==1) {
error("This command requires a stack or virtual stack.");
return;
}
}
String macroPath = IJ.getDirectory("macros")+MACRO_FILE_NAME;
macro = IJ.openAsString(macroPath);
if (macro==null || macro.startsWith("Error: ")) {
IJ.showStatus(macro.substring(7) + ": "+macroPath);
macro = "";
}
if (!showDialog()) return;
String inputPath = null;
if (virtualStack==null) {
inputPath = inputDir.getText();
if (inputPath.equals("")) {
error("Please choose an input folder");
return;
}
inputPath = addSeparator(inputPath);
File f1 = new File(inputPath);
if (!f1.exists() || !f1.isDirectory()) {
error("Input does not exist or is not a folder\n \n"+inputPath);
return;
}
}
String outputPath = outputDir.getText();
outputPath = addSeparator(outputPath);
File f2 = new File(outputPath);
if (!outputPath.equals("") && (!f2.exists() || !f2.isDirectory())) {
error("Output does not exist or is not a folder\n \n"+outputPath);
return;
}
if (macro.equals("")) {
error("There is no macro code in the text area");
return;
}
ImageJ ij = IJ.getInstance();
if (ij!=null) ij.getProgressBar().setBatchMode(true);
IJ.resetEscape();
if (virtualStack!=null)
processVirtualStack(outputPath);
else
processFolder(inputPath, outputPath);
IJ.showProgress(1,1);
if (virtualStack==null)
Prefs.set("batch.input", inputDir.getText());
Prefs.set("batch.output", outputDir.getText());
Prefs.set("batch.format", format);
macro = gd.getTextArea1().getText();
if (!macro.equals(""))
IJ.saveString(macro, IJ.getDirectory("macros")+MACRO_FILE_NAME);
}
boolean showDialog() {
validateFormat();
gd = new NonBlockingGenericDialog("Batch Process");
addPanels(gd);
gd.setInsets(15, 0, 5);
gd.addChoice("Output_format:", formats, format);
gd.setInsets(0, 0, 5);
gd.addChoice("Add macro code:", code, code[0]);
if (virtualStack==null)
gd.addStringField("File name contains:", "", 10);
gd.setInsets(15, 10, 0);
Dimension screen = IJ.getScreenSize();
gd.addTextAreas(macro, null, screen.width<=600?10:15, 60);
addButtons(gd);
gd.setOKLabel("Process");
Vector choices = gd.getChoices();
Choice choice = (Choice)choices.elementAt(1);
if (virtualStack!=null)
gd.addHelp(help);
choice.addItemListener(this);
gd.showDialog();
format = gd.getNextChoice();
if (virtualStack==null)
filter = gd.getNextString();
macro = gd.getNextText();
return !gd.wasCanceled();
}
void processVirtualStack(String outputPath) {
ImageStack stack = virtualStack.getStack();
int n = stack.getSize();
int index = 0;
for (int i=1; i<=n; i++) {
if (IJ.escapePressed()) break;
IJ.showProgress(i, n);
ImageProcessor ip = stack.getProcessor(i);
if (ip==null) return;
ImagePlus imp = new ImagePlus(i+"/"+stack.getSize(), ip);
if (!macro.equals("")) {
if (!runMacro("i="+(index++)+";"+"n="+stack.getSize()+";"+macro, imp))
break;
}
if (saveOutput && !outputPath.equals("")) {
if (format.equals("8-bit TIFF") || format.equals("GIF")) {
if (imp.getBitDepth()==24)
IJ.run(imp, "8-bit Color", "number=256");
else
IJ.run(imp, "8-bit", "");
}
IJ.saveAs(imp, format, outputPath+pad(i));
}
saveOutput = true;
imp.close();
}
if (outputPath!=null && !outputPath.equals(""))
IJ.run("Image Sequence...", "open=[" + outputPath + "]"+" use");
}
String pad(int n) {
String str = ""+n;
while (str.length()<5)
str = "0" + str;
return str;
}
void processFolder(String inputPath, String outputPath) {
String[] list = (new File(inputPath)).list();
list = FolderOpener.getFilteredList(list, filter, "Batch Processor");
if (list==null)
return;
int index = 0;
int startingCount = WindowManager.getImageCount();
for (int i=0; istartingCount)
imp = WindowManager.getCurrentImage();
if (imp==null)
imp = Opener.openUsingBioFormats(path);
if (imp==null) {
IJ.log("openImage() and openUsingBioFormats() returned null: "+path);
continue;
}
if (!macro.equals("")) {
outputImage = null;
if (!runMacro("i="+(index++)+";"+macro, imp))
break;
}
if (saveOutput && !outputPath.equals("")) {
if (format.equals("8-bit TIFF") || format.equals("GIF")) {
if (imp.getBitDepth()==24)
IJ.run(imp, "8-bit Color", "number=256");
else
IJ.run(imp, "8-bit", "");
}
if (outputImage!=null && outputImage!=imp)
IJ.saveAs(outputImage, format, outputPath+list[i]);
else
IJ.saveAs(imp, format, outputPath+list[i]);
}
saveOutput = true;
imp.close();
}
}
private boolean runMacro(String macro, ImagePlus imp) {
WindowManager.setTempCurrentImage(imp);
Interpreter interp = new Interpreter();
try {
outputImage = interp.runBatchMacro(macro, imp);
} catch(Throwable e) {
interp.abortMacro();
String msg = e.getMessage();
if (!(e instanceof RuntimeException && msg!=null && e.getMessage().equals(Macro.MACRO_CANCELED)))
IJ.handleException(e);
return false;
} finally {
WindowManager.setTempCurrentImage(null);
}
return true;
}
String addSeparator(String path) {
if (path.equals("")) return path;
if (!(path.endsWith("/")||path.endsWith("\\")))
path = path + File.separator;
return path;
}
void validateFormat() {
boolean validFormat = false;
for (int i=0; i 0)
sb.append(b,0, n);
macro = sb.toString();
}
catch (IOException e) {
return null;
}
return macro;
}
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source==input) {
String path = IJ.getDirectory("Input Folder");
if (path==null) return;
inputDir.setText(path);
} else if (source==output) {
String path = IJ.getDirectory("Output Folder");
if (path==null) return;
outputDir.setText(path);
} else if (source==test) {
thread = new Thread(this, "BatchTest");
thread.setPriority(Math.max(thread.getPriority()-2, Thread.MIN_PRIORITY));
thread.start();
} else if (source==open)
open();
else if (source==save)
save();
}
void open() {
String text = IJ.openAsString("");
if (text==null) return;
if (text.startsWith("Error: "))
error(text.substring(7));
else {
if (text.length()>30000)
error("File is too large");
else
gd.getTextArea1().setText(text);
}
}
void save() {
macro = gd.getTextArea1().getText();
if (!macro.equals(""))
IJ.saveString(macro, "");
}
void error(String msg) {
IJ.error("Batch Processor", msg);
}
public void run() {
TextArea ta = gd.getTextArea1();
//ta.selectAll();
String macro = ta.getText();
if (macro.equals("")) {
error("There is no macro code in the text area");
return;
}
ImagePlus imp = null;
IJ.redirectErrorMessages(true);
if (virtualStack!=null)
imp = getVirtualStackImage();
else
imp = getFolderImage();
IJ.redirectErrorMessages(false);
if (imp==null) {
if (!errorDisplayed)
IJ.log("IJ.openImage() returned null");
return;
}
runMacro("i=0;"+macro, imp);
Point loc = new Point(10, 30);
if (testImage!=0) {
ImagePlus imp2 = WindowManager.getImage(testImage);
if (imp2!=null) {
ImageWindow win = imp2.getWindow();
if (win!=null) loc = win.getLocation();
imp2.changes=false;
imp2.close();
}
}
imp.show();
ImageWindow iw = imp.getWindow();
if (iw!=null) iw.setLocation(loc);
testImage = imp.getID();
}
ImagePlus getVirtualStackImage() {
ImagePlus imp = virtualStack.createImagePlus();
imp.setProcessor("", virtualStack.getProcessor().duplicate());
return imp;
}
ImagePlus getFolderImage() {
String inputPath = inputDir.getText();
inputPath = addSeparator(inputPath);
File f1 = new File(inputPath);
if (!f1.exists() || !f1.isDirectory()) {
error("Input does not exist or is not a folder\n \n"+inputPath);
errorDisplayed = true;
return null;
}
String[] list = (new File(inputPath)).list();
String name = list[0];
if (name.startsWith(".")&&list.length>1) name = list[1];
String path = inputPath + name;
setDirAndName(path);
return IJ.openImage(path);
}
void setDirAndName(String path) {
File f = new File(path);
OpenDialog.setLastDirectory(f.getParent()+File.separator);
OpenDialog.setLastName(f.getName());
}
public static void saveOutput(boolean b) {
saveOutput = b;
}
}