| FolderOpener |

    package ij.plugin;
    import java.awt.*;
    import java.io.*;
    import java.awt.event.*;
    import java.awt.image.ColorModel;
    import ij.*;
    import ij.io.*;
    import ij.gui.*;
    import ij.process.*;
    import ij.measure.Calibration;

    /** Implements the File/Import/Image Sequence command, which
    opens a folder of images as a stack. */
    public class FolderOpener implements PlugIn {

        private static boolean convertToGrayscale, convertToRGB;
        private static boolean sortFileNames = true;
        private static double scale = 100.0;
        private static boolean virtualStack;
        private int n, start, increment;
        private String filter;
        private FileInfo fi;
        private String info1;

        public void run(String arg) {
            OpenDialog od = new OpenDialog("Open Image Sequence...", arg);
            String directory = od.getDirectory();
            String name = od.getFileName();
            if (name==null)
                return;
            String[] list = (new File(directory)).list();
            if (list==null)
                return;
            String title = directory;
            if (title.endsWith(File.separator))
                title = title.substring(0, title.length()-1);
            int index = title.lastIndexOf(File.separatorChar);
            if (index!=-1) title = title.substring(index + 1);
            if (title.endsWith(":"))
                title = title.substring(0, title.length()-1);

            IJ.register(FolderOpener.class);
            list = trimFileList(list);
            if (list==null) return;
            if (IJ.debugMode) IJ.log("FolderOpener: "+directory+" ("+list.length+" files)");
            int width=0,height=0,depth=0,bitDepth=0;
            ImageStack stack = null;
            double min = Double.MAX_VALUE;
            double max = -Double.MAX_VALUE;
            Calibration cal = null;
            boolean allSameCalibration = true;
            IJ.resetEscape();
            try {
                for (int i=0; i<list.length; i++) {
                    IJ.redirectErrorMessages();
                    ImagePlus imp = (new Opener()).openImage(directory, list[i]);
                    if (imp!=null) {
                        width = imp.getWidth();
                        height = imp.getHeight();
                        bitDepth = imp.getBitDepth();
                        fi = imp.getOriginalFileInfo();
                        if (!showDialog(imp, list))
                            return;
                        break;
                    }
                }
                if (width==0) {
                    IJ.error("Import Sequence", "This folder does not appear to contain any TIFF,\n"
                    + "JPEG, BMP, DICOM, GIF, FITS or PGM files.");
                    return;
                }

                if (filter!=null && (filter.equals("") || filter.equals("*")))
                    filter = null;
                if (filter!=null) {
                    int filteredImages = 0;
                    for (int i=0; i<list.length; i++) {
                        if (list[i].indexOf(filter)>=0)
                            filteredImages++;
                        else
                            list[i] = null;
                    }
                    if (filteredImages==0) {
                        IJ.error("None of the "+list.length+" files contain\n the string '"+filter+"' in their name.");
                        return;
                    }
                    String[] list2 = new String[filteredImages];
                    int j = 0;
                    for (int i=0; i<list.length; i++) {
                        if (list[i]!=null)
                            list2[j++] = list[i];
                    }
                    list = list2;
                }
                if (sortFileNames)
                    list = sortFileList(list);

                if (n<1)
                    n = list.length;
                if (start<1 || start>list.length)
                    start = 1;
                if (start+n-1>list.length)
                    n = list.length-start+1;
                int count = 0;
                int counter = 0;
                ImagePlus imp = null;
                for (int i=start-1; i<list.length; i++) {
                    if ((counter++%increment)!=0)
                        continue;
                    Opener opener = new Opener();
                    opener.setSilentMode(true);
                    IJ.redirectErrorMessages();
                    if (!virtualStack||stack==null)
                        imp = opener.openImage(directory, list[i]);
                    if (imp!=null && stack==null) {
                        width = imp.getWidth();
                        height = imp.getHeight();
                        depth = imp.getStackSize();
                        bitDepth = imp.getBitDepth();
                        cal = imp.getCalibration();
                        if (convertToRGB) bitDepth = 24;
                        if (convertToGrayscale) bitDepth = 8;
                        ColorModel cm = imp.getProcessor().getColorModel();
                        if (virtualStack)
                            stack = new VirtualStack(width, height, cm, directory);
                        else if (scale<100.0)
                            stack = new ImageStack((int)(width*scale/100.0), (int)(height*scale/100.0), cm);
                        else
                            stack = new ImageStack(width, height, cm);
                        info1 = (String)imp.getProperty("Info");
                    }
                    if (imp==null)
                        continue;
                    if (imp.getWidth()!=width || imp.getHeight()!=height) {
                        IJ.log(list[i] + ": wrong size; "+width+"x"+height+" expected, "+imp.getWidth()+"x"+imp.getHeight()+" found");
                        continue;
                    }
                    String label = imp.getTitle();
                    if (depth==1) {
                        String info = (String)imp.getProperty("Info");
                        if (info!=null)
                            label += "\n" + info;
                    }
                    if (imp.getCalibration().pixelWidth!=cal.pixelWidth)
                        allSameCalibration = false;
                    ImageStack inputStack = imp.getStack();
                    for (int slice=1; slice<=inputStack.getSize(); slice++) {
                        ImageProcessor ip = inputStack.getProcessor(slice);
                        int bitDepth2 = imp.getBitDepth();
                        if (convertToRGB) {
                            ip = ip.convertToRGB();
                            bitDepth2 = 24;
                        } else if(convertToGrayscale) {
                            ip = ip.convertToByte(true);
                            bitDepth2 = 8;
                        }
                        if (bitDepth2!=bitDepth) {
                            if (bitDepth==8) {
                                ip = ip.convertToByte(true);
                                bitDepth2 = 8;
                            } else if (bitDepth==24) {
                                ip = ip.convertToRGB();
                                bitDepth2 = 24;
                            }
                        }
                        if (bitDepth2!=bitDepth) {
                            IJ.log(list[i] + ": wrong bit depth; "+bitDepth+" expected, "+bitDepth2+" found");
                            break;
                        }
                        if (slice==1) count++;
                        IJ.showStatus(count+"/"+n);
                        IJ.showProgress(count, n);
                        if (scale<100.0)
                            ip = ip.resize((int)(width*scale/100.0), (int)(height*scale/100.0));
                        if (ip.getMin()<min) min = ip.getMin();
                        if (ip.getMax()>max) max = ip.getMax();
                        String label2 = label;
                        if (depth>1) label2 = ""+slice;
                        if (virtualStack) {
                            if (slice==1) ((VirtualStack)stack).addSlice(list[i]);
                        } else
                            stack.addSlice(label2, ip);
                    }
                    if (count>=n)
                        break;
                    if (IJ.escapePressed())
                        {IJ.beep(); break;}
                    //System.gc();
                }
            } catch(OutOfMemoryError e) {
                IJ.outOfMemory("FolderOpener");
                if (stack!=null) stack.trim();
            }
            if (stack!=null && stack.getSize()>0) {
                if (info1!=null && info1.lastIndexOf("7FE0,0010")>0)
                    stack = (new DICOM_Sorter()).sort(stack);
                ImagePlus imp2 = new ImagePlus(title, stack);
                if (imp2.getType()==ImagePlus.GRAY16 || imp2.getType()==ImagePlus.GRAY32)
                    imp2.getProcessor().setMinAndMax(min, max);
                if (fi==null)
                    fi = new FileInfo();
                fi.fileFormat = FileInfo.UNKNOWN;
                fi.fileName = "";
                fi.directory = directory;
                imp2.setFileInfo(fi); // saves FileInfo of the first image
                if (allSameCalibration)
                    imp2.setCalibration(cal); // use calibration from first image
                if (imp2.getStackSize()==1 && info1!=null)
                    imp2.setProperty("Info", info1);
                imp2.show();
            }
            IJ.showProgress(1.0);
        }

        boolean showDialog(ImagePlus imp, String[] list) {
            int fileCount = list.length;
            FolderOpenerDialog gd = new FolderOpenerDialog("Sequence Options", imp, list);
            gd.addNumericField("Number of Images:", fileCount, 0);
            gd.addNumericField("Starting Image:", 1, 0);
            gd.addNumericField("Increment:", 1, 0);
            //gd.addMessage("");
            gd.addStringField("File Name Contains:", "");
            gd.addNumericField("Scale Images:", scale, 0, 4, "%");
            gd.addCheckbox("Convert to 8-bit Grayscale", convertToGrayscale);
            gd.addCheckbox("Convert_to_RGB", convertToRGB);
            gd.addCheckbox("Sort Names Numerically", sortFileNames);
            gd.addCheckbox("Use Virtual Stack", virtualStack);
            gd.addMessage("10000 x 10000 x 1000 (100.3MB)");
            gd.showDialog();
            if (gd.wasCanceled())
                return false;
            n = (int)gd.getNextNumber();
            start = (int)gd.getNextNumber();
            increment = (int)gd.getNextNumber();
            if (increment<1)
                increment = 1;
            scale = gd.getNextNumber();
            if (scale<5.0) scale = 5.0;
            if (scale>100.0) scale = 100.0;
            filter = gd.getNextString();
            convertToGrayscale = gd.getNextBoolean();
            convertToRGB = gd.getNextBoolean();
            sortFileNames = gd.getNextBoolean();
            virtualStack = gd.getNextBoolean();
            if (virtualStack) {
                convertToGrayscale = false;
                convertToRGB = false;
                scale = 100.0;
            }
            return true;
        }

        /** Removes names that start with "." or end with ".db". ".txt", ".lut", "roi" or ".pty". */
        public String[] trimFileList(String[] rawlist) {
            int count = 0;
            for (int i=0; i< rawlist.length; i++) {
                String name = rawlist[i];
                if (name.startsWith(".")||name.equals("Thumbs.db")||name.endsWith(".txt")||name.endsWith(".lut")||name.endsWith(".roi")||name.endsWith(".pty"))
                    rawlist[i] = null;
                else
                    count++;
            }
            if (count==0) return null;
            String[] list = rawlist;
            if (count<rawlist.length) {
                list = new String[count];
                int index = 0;
                for (int i=0; i< rawlist.length; i++) {
                    if (rawlist[i]!=null)
                        list[index++] = rawlist[i];
                }
            }
            return list;
        }

        /** Sorts the file names into numeric order. */
        public String[] sortFileList(String[] list) {
            int listLength = list.length;
            boolean allSameLength = true;
            int len0 = list[0].length();
            for (int i=0; i<listLength; i++) {
                if (list[i].length()!=len0) {
                    allSameLength = false;
                    break;
                }
            }
            if (allSameLength)
                {ij.util.StringSorter.sort(list); return list;}
            int maxDigits = 15;
            String[] list2 = null;
            char ch;
            for (int i=0; i<listLength; i++) {
                int len = list[i].length();
                String num = "";
                for (int j=0; j<len; j++) {
                    ch = list[i].charAt(j);
                    if (ch>=48&&ch<=57) num += ch;
                }
                if (list2==null) list2 = new String[listLength];
                if (num.length()==0) num = "aaaaaa";
                num = "000000000000000" + num; // prepend maxDigits leading zeroes
                num = num.substring(num.length()-maxDigits);
                list2[i] = num + list[i];
            }
            if (list2!=null) {
                ij.util.StringSorter.sort(list2);
                for (int i=0; i<listLength; i++)
                    list2[i] = list2[i].substring(maxDigits);
                return list2;
            } else {
                ij.util.StringSorter.sort(list);
                return list;
            }
        }

    } // FolderOpener

    class FolderOpenerDialog extends GenericDialog {
        ImagePlus imp;
        int fileCount;
        boolean eightBits, rgb;
        String[] list;

        public FolderOpenerDialog(String title, ImagePlus imp, String[] list) {
            super(title);
            this.imp = imp;
            this.list = list;
            this.fileCount = list.length;
        }

        protected void setup() {
            eightBits = ((Checkbox)checkbox.elementAt(0)).getState();
            rgb = ((Checkbox)checkbox.elementAt(1)).getState();
            setStackInfo();
        }

        public void itemStateChanged(ItemEvent e) {
            Checkbox item = (Checkbox)e.getSource();
            Checkbox grayscaleCB = (Checkbox)checkbox.elementAt(0);
            Checkbox rgbCB = (Checkbox)checkbox.elementAt(1);
            if (item==grayscaleCB) {
                eightBits = item.getState();
                if (eightBits) {
                 rgbCB.setState(false);
                 rgb = false;
                }
            }
            if (item==rgbCB) {
                rgb = item.getState();
                if (rgb) {
                    grayscaleCB.setState(false);
                    eightBits = false;
                }
            }
            setStackInfo();
        }

        public void textValueChanged(TextEvent e) {
            setStackInfo();
        }

        void setStackInfo() {
            int width = imp.getWidth();
            int height = imp.getHeight();
            int depth = imp.getStackSize();
            int bytesPerPixel = 1;
            int n = getNumber(numberField.elementAt(0));
            int start = getNumber(numberField.elementAt(1));
            int inc = getNumber(numberField.elementAt(2));
            double scale = getNumber(numberField.elementAt(3));
            if (scale<5.0) scale = 5.0;
            if (scale>100.0) scale = 100.0;

            if (n<1) n = fileCount;
            if (start<1 || start>fileCount) start = 1;
            if (start+n-1>fileCount)
                n = fileCount-start+1;
            if (inc<1) inc = 1;
            TextField tf = (TextField)stringField.elementAt(0);
            String filter = tf.getText();
            if (!filter.equals("") && !filter.equals("*")) {
                int n2 = 0;
                for (int i=0; i<list.length; i++) {
                    if (list[i].indexOf(filter)>=0)
                        n2++;
                }
                if (n2<n) n = n2;
            }
            switch (imp.getType()) {
                case ImagePlus.GRAY16:
                    bytesPerPixel=2;break;
                case ImagePlus.COLOR_RGB:
                case ImagePlus.GRAY32:
                    bytesPerPixel=4; break;
            }
            if (eightBits)
                bytesPerPixel = 1;
            if (rgb)
                bytesPerPixel = 4;
            width = (int)(width*scale/100.0);
            height = (int)(height*scale/100.0);
            int n2 = ((fileCount-start+1)*depth)/inc;
            if (n2<0) n2 = 0;
            if (n2>n) n2 = n;
            double size = ((double)width*height*n2*bytesPerPixel)/(1024*1024);
            ((Label)theLabel).setText(width+" x "+height+" x "+n2+" ("+IJ.d2s(size,1)+"MB)");
        }

        public int getNumber(Object field) {
            TextField tf = (TextField)field;
            String theText = tf.getText();
            double value;
            Double d;
            try {d = new Double(theText);}
            catch (NumberFormatException e){
                d = null;
            }
            if (d!=null)
                return (int)d.doubleValue();
            else
                return 0;
          }

    } // FolderOpenerDialog

| FolderOpener |