package ij.plugin; import ij.*; import ij.gui.*; import ij.process.*; import ij.measure.Calibration; import ij.macro.Interpreter; import ij.io.FileInfo; import java.awt.*; /** Implements the AddSlice, DeleteSlice and "Stack to Images" commands. */ public class StackEditor implements PlugIn { ImagePlus imp; int nSlices, width, height; static boolean deleteFrames; public void run(String arg) { imp = IJ.getImage(); nSlices = imp.getStackSize(); width = imp.getWidth(); height = imp.getHeight(); if (arg.equals("add")) addSlice(); else if (arg.equals("delete")) deleteSlice(); else if (arg.equals("toimages")) convertStackToImages(imp); } void addSlice() { if (imp.isComposite() && nSlices==imp.getNChannels()) { addChannel(); return; } if (imp.isDisplayedHyperStack()) return; if (!imp.lock()) return; int id = 0; ImageStack stack = imp.getStack(); if (stack.getSize()==1) { String label = stack.getSliceLabel(1); if (label!=null && label.indexOf("\n")!=-1) stack.setSliceLabel(null, 1); Object obj = imp.getProperty("Label"); if (obj!=null && (obj instanceof String)) stack.setSliceLabel((String)obj, 1); id = imp.getID(); } ImageProcessor ip = imp.getProcessor(); int n = imp.getCurrentSlice(); if (IJ.altKeyDown()) n--; // insert in front of current slice stack.addSlice(null, ip.createProcessor(width, height), n); imp.setStack(null, stack); imp.setSlice(n+1); imp.unlock(); if (id!=0) IJ.selectWindow(id); // prevents macros from failing } void deleteSlice() { if (nSlices<2) {IJ.error("\"Delete Slice\" requires a stack"); return;} if (imp.isComposite() && nSlices==imp.getNChannels()) { deleteChannel(); return; } if (imp.isDisplayedHyperStack()) { deleteHyperstackSliceOrFrame(); return; } if (!imp.lock()) return; ImageStack stack = imp.getStack(); int n = imp.getCurrentSlice(); stack.deleteSlice(n); if (stack.getSize()==1) { String label = stack.getSliceLabel(1); if (label!=null) imp.setProperty("Label", label); } imp.setStack(null, stack); if (n--<1) n = 1; imp.setSlice(n); imp.unlock(); } void addChannel() { int c = imp.getChannel(); ImageStack stack = imp.getStack(); CompositeImage ci = (CompositeImage)imp; LUT[] luts = ci.getLuts(); ImageProcessor ip = stack.getProcessor(1); ImageProcessor ip2 = ip.createProcessor(ip.getWidth(), ip.getHeight()); stack.addSlice(null, ip2, c); ImagePlus imp2 = imp.createImagePlus(); imp2.setStack(stack); int n = imp2.getStackSize(); imp2 = new CompositeImage(imp, ci.getMode()); LUT lut = LUT.createLutFromColor(Color.white); int index = 0; for (int i=1; i<=n; i++) { if (c+1==index+1) { ((CompositeImage)imp2).setChannelLut(lut, i); c = -1; } else ((CompositeImage)imp2).setChannelLut(luts[index++], i); } imp.changes = false; imp.hide(); imp2.show(); } void deleteChannel() { int c = imp.getChannel(); ImageStack stack = imp.getStack(); CompositeImage ci = (CompositeImage)imp; LUT[] luts = ci.getLuts(); stack.deleteSlice(c); ImagePlus imp2 = imp.createImagePlus(); imp2.setStack(stack); int n = imp2.getStackSize(); int mode = ci.getMode(); if (mode==CompositeImage.COMPOSITE && n==1) mode = CompositeImage.COLOR; imp2 = new CompositeImage(imp, mode); int index = 0; for (int i=1; i<=n; i++) { if (c==index+1) index++; ((CompositeImage)imp2).setChannelLut(luts[index++], i); } imp.changes = false; imp.hide(); imp2.show(); } void deleteHyperstackSliceOrFrame() { int channels = imp.getNChannels(); int slices = imp.getNSlices(); int frames = imp.getNFrames(); int c1 = imp.getChannel(); int z1 = imp.getSlice(); int t1 = imp.getFrame(); if (frames>1 && slices==1) deleteFrames = true; else if (frames==1 && slices>1) deleteFrames = false; else if (slices>1 && frames>1) { GenericDialog gd = new GenericDialog("Delete Slice"); gd.addCheckbox("Delete time point "+t1, deleteFrames); gd.showDialog(); if (gd.wasCanceled()) return; deleteFrames = gd.getNextBoolean(); } else return; if (!imp.lock()) return; ImageStack stack = imp.getStack(); if (deleteFrames) { // delete time point for (int z=slices; z>=1; z--) { int index = imp.getStackIndex(channels, z, t1); for (int i=0; i<channels; i++) stack.deleteSlice(index-i); } frames--; } else { // delete slice z1 from all volumes for (int t=frames; t>=1; t--) { int index = imp.getStackIndex(channels, z1, t); for (int i=0; i<channels; i++) stack.deleteSlice(index-i); } slices--; } imp.setDimensions(channels, slices, frames); //for (int i=1; i<=stack.getSize(); i++) // IJ.log(i+" "+stack.getSliceLabel(i)+" "+stack.getProcessor(i).getPixel(0,0)); imp.unlock(); } public void convertImagesToStack() { (new ImagesToStack()).run(""); } public void convertStackToImages(ImagePlus imp) { if (nSlices<2) {IJ.error("\"Convert Stack to Images\" requires a stack"); return;} if (!imp.lock()) return; ImageStack stack = imp.getStack(); int size = stack.getSize(); if (size>30 && !IJ.isMacro()) { boolean ok = IJ.showMessageWithCancel("Convert to Images?", "Are you sure you want to convert this\nstack to " +size+" separate windows?"); if (!ok) {imp.unlock(); return;} } Calibration cal = imp.getCalibration(); CompositeImage cimg = imp.isComposite()?(CompositeImage)imp:null; if (imp.getNChannels()!=imp.getStackSize()) cimg = null; for (int i=1; i<=size; i++) { String label = stack.getShortSliceLabel(i); String title = label!=null&&!label.equals("")?label:getTitle(imp, i); ImageProcessor ip = stack.getProcessor(i); if (cimg!=null) { LUT lut = cimg.getChannelLut(i); if (lut!=null) { ip.setColorModel(lut); ip.setMinAndMax(lut.min, lut.max); } } ImagePlus imp2 = new ImagePlus(title, ip); imp2.setCalibration(cal); String info = stack.getSliceLabel(i); if (info!=null && !info.equals(label)) imp2.setProperty("Info", info); imp2.show(); } imp.changes = false; ImageWindow win = imp.getWindow(); if (win!=null) win.close(); else if (Interpreter.isBatchMode()) Interpreter.removeBatchModeImage(imp); imp.unlock(); } String getTitle(ImagePlus imp, int n) { String digits = "00000000"+n; return imp.getShortTitle()+"-"+digits.substring(digits.length()-4,digits.length()); } }