package ij.plugin; import ij.*; import ij.process.*; import ij.gui.*; /** This plugin, which concatenates two images or stacks, implements the Image/Stacks/Tools/Concatenate command. The images or stacks must have same width, height and data type. */ public class Concatenator implements PlugIn { ImagePlus imp1, imp2; static boolean keep; static String title = "Concatenated Stacks"; public void run(String arg) { if (!showDialog()) return; ImagePlus imp3 = null; if (imp1.isComposite() || imp1.isHyperStack()) { ImagePlus[] images = new ImagePlus[2]; images[0] = imp1; images[1] = imp2; imp3 = concatenate(images, keep); if (imp3==null) error(); else imp3.setTitle(title); } else imp3 = concatenate(imp1, imp2, keep); if (imp3!=null) imp3.show(); } public ImagePlus concatenate(ImagePlus imp1, ImagePlus imp2, boolean keep) { if (imp1.getType()!=imp2.getType() || imp1.isHyperStack() || imp2.isHyperStack()) {error(); return null;} int width = imp1.getWidth(); int height = imp1.getHeight(); if (width!=imp2.getWidth() || height!=imp2.getHeight()) {error(); return null;} ImageStack stack1 = imp1.getStack(); ImageStack stack2 = imp2.getStack(); int size1 = stack1.getSize(); int size2 = stack2.getSize(); ImageStack stack3 = imp1.createEmptyStack(); int slice = 1; for (int i=1; i<=size1; i++) { ImageProcessor ip = stack1.getProcessor(slice); String label = stack1.getSliceLabel(slice); if (keep || imp1==imp2) { ip = ip.duplicate(); slice++; } else stack1.deleteSlice(slice); stack3.addSlice(label, ip); } slice = 1; for (int i=1; i<=size2; i++) { ImageProcessor ip = stack2.getProcessor(slice); String label = stack2.getSliceLabel(slice); if (keep || imp1==imp2) { ip = ip.duplicate(); slice++; } else stack2.deleteSlice(slice); stack3.addSlice(label, ip); } ImagePlus imp3 = new ImagePlus(title, stack3); imp3.setCalibration(imp1.getCalibration()); if (!keep) { imp1.changes = false; imp1.close(); if (imp1!=imp2) { imp2.changes = false; imp2.close(); } } return imp3; } public ImagePlus concatenate(ImagePlus[] images, boolean keepSourceImages) { int n = images.length; int width = images[0].getWidth(); int height = images[0].getHeight(); int bitDepth = images[0].getBitDepth(); int channels = images[0].getNChannels(); int slices = images[0].getNSlices(); int frames = images[0].getNFrames(); boolean concatSlices = slices>1 && frames==1; for (int i=1; i<n; i++) { if (images[i].getNFrames()>1) concatSlices = false; if (images[i].getWidth()!=width || images[i].getHeight()!=height || images[i].getBitDepth()!=bitDepth || images[i].getNChannels()!=channels || (!concatSlices && images[i].getNSlices()!=slices)) return null; } ImageStack stack2 = new ImageStack(width, height); int slices2=0, frames2=0; for (int i=0;i<n;i++) { ImageStack stack = images[i].getStack(); slices = images[i].getNSlices(); if (concatSlices) { slices = images[i].getNSlices(); slices2 += slices; frames2 = frames; } else { frames = images[i].getNFrames(); frames2 += frames; slices2 = slices; } for (int f=1; f<=frames; f++) { for (int s=1; s<=slices; s++) { for (int c=1; c<=channels; c++) { int index = (f-1)*channels*s + (s-1)*channels + c; ImageProcessor ip = stack.getProcessor(index); if (keepSourceImages) ip = ip.duplicate(); String label = stack.getSliceLabel(index); stack2.addSlice(label, ip); } } } } ImagePlus imp2 = new ImagePlus("Concatenated Images", stack2); imp2.setDimensions(channels, slices2, frames2); if (channels>1) { int mode = 0; if (images[0].isComposite()) mode = ((CompositeImage)images[0]).getMode(); imp2 = new CompositeImage(imp2, mode); ((CompositeImage)imp2).copyLuts(images[0]); } if (channels>1 && frames2>1) imp2.setOpenAsHyperStack(true); if (!keepSourceImages) { for (int i=0; i<n; i++) { images[i].changes = false; images[i].close(); } } return imp2; } int getStackIndex(int channel, int slice, int frame, int nChannels, int nSlices, int nFrames) { return (frame-1)*nChannels*nSlices + (slice-1)*nChannels + channel; } boolean showDialog() { int[] wList = WindowManager.getIDList(); if (wList==null) { IJ.noImage(); return false; } String[] titles = new String[wList.length]; for (int i=0; i<wList.length; i++) { ImagePlus imp = WindowManager.getImage(wList[i]); titles[i] = imp!=null?imp.getTitle():""; } GenericDialog gd = new GenericDialog("Concatenator"); gd.addChoice("Stack1:", titles, titles[0]); gd.addChoice("Stack2:", titles, wList.length>1?titles[1]:titles[0]); gd.addStringField("Title:", title, 16); gd.addCheckbox("Keep Source Stacks", keep); gd.showDialog(); if (gd.wasCanceled()) return false; int[] index = new int[3]; int index1 = gd.getNextChoiceIndex(); int index2 = gd.getNextChoiceIndex(); title = gd.getNextString(); keep = gd.getNextBoolean(); imp1 = WindowManager.getImage(wList[index1]); imp2 = WindowManager.getImage(wList[index2]); return true; } void error() { IJ.showMessage("Concatenator", "This command requires two images with\n"+ "the same dimesions and data type."); } }