| Thresholder |

    package ij.plugin;
    import ij.*;
    import ij.gui.*;
    import ij.process.*;
    import ij.measure.*;
    import ij.plugin.frame.Recorder;
    import ij.plugin.filter.PlugInFilter;
    import java.awt.*;

    /** This plugin implements the Process/Binary/Make Binary
        and Convert to Mask commands. */
    public class Thresholder implements PlugIn, Measurements {

        private double minThreshold;
        private double maxThreshold;
        boolean autoThreshold;
        boolean skipDialog;
        static boolean fill1 = true;
        static boolean fill2 = true;
        static boolean useBW = true;
        static boolean useLocal = true;
        boolean convertToMask;

        public void run(String arg) {
            convertToMask = arg.equals("mask");
            skipDialog = arg.equals("skip") || convertToMask;
            ImagePlus imp = WindowManager.getCurrentImage();
            if (imp==null)
                {IJ.noImage(); return;}
            if (imp.getStackSize()==1) {
                Undo.setup(Undo.COMPOUND_FILTER, imp);
                applyThreshold(imp);
                Undo.setup(Undo.COMPOUND_FILTER_DONE, imp);
            } else
                convertStack(imp);
        }

        void convertStack(ImagePlus imp) {
            if (!(imp.getProcessor().getMinThreshold()==ImageProcessor.NO_THRESHOLD))
                useLocal = false;
            GenericDialog gd = new GenericDialog("Convert to Mask");
            gd.addMessage("Convert all images in stack to binary?");
            gd.addCheckbox("Calculate Threshold for Each Image", useLocal);
            gd.addCheckbox("Black Background", Prefs.blackBackground);
            gd.showDialog();
            if (gd.wasCanceled()) return;
            useLocal = gd.getNextBoolean();
            Prefs.blackBackground = gd.getNextBoolean();
            Undo.reset();
            if (useLocal)
                convertStackToBinary(imp);
            else
                applyThreshold(imp);
        }

        void applyThreshold(ImagePlus imp) {
            imp.killRoi();
            ImageProcessor ip = imp.getProcessor();
            ip.resetBinaryThreshold();
            int type = imp.getType();
            if (type==ImagePlus.GRAY16 || type==ImagePlus.GRAY32) {
                applyShortOrFloatThreshold(imp);
                return;
            }
            if (!imp.lock()) return;
            double saveMinThreshold = ip.getMinThreshold();
            double saveMaxThreshold = ip.getMaxThreshold();
            autoThreshold = saveMinThreshold==ImageProcessor.NO_THRESHOLD;

            boolean useBlackAndWhite = true;
            if (skipDialog)
                fill1 = fill2 = useBlackAndWhite = true;
            else if (!autoThreshold) {
                GenericDialog gd = new GenericDialog("Make Binary");
                gd.addCheckbox("Thresholded pixels to foreground color", fill1);
                gd.addCheckbox("Remaining pixels to background color", fill2);
                gd.addMessage("");
                gd.addCheckbox("Black foreground, white background", useBW);
                gd.showDialog();
                if (gd.wasCanceled())
                    {imp.unlock(); return;}
                fill1 = gd.getNextBoolean();
                fill2 = gd.getNextBoolean();
                useBW = useBlackAndWhite = gd.getNextBoolean();
            } else {
                fill1 = fill2 = true;
                convertToMask = true;
            }

            if (type!=ImagePlus.GRAY8)
                convertToByte(imp);
            ip = imp.getProcessor();

            if (autoThreshold)
                autoThreshold(ip);
            else {
                if (Recorder.record && (!IJ.macroRunning()||Recorder.recordInMacros))
                    Recorder.record("setThreshold", (int)saveMinThreshold, (int)saveMaxThreshold);
                minThreshold = saveMinThreshold;
                maxThreshold = saveMaxThreshold;
            }

            if (convertToMask && ip.isColorLut())
                ip.setColorModel(ip.getDefaultColorModel());
            int fcolor, bcolor;
            ip.resetThreshold();
            int savePixel = ip.getPixel(0,0);
            if (useBlackAndWhite)
                ip.setColor(Color.black);
            else
                ip.setColor(Toolbar.getForegroundColor());
            ip.drawPixel(0,0);
            fcolor = ip.getPixel(0,0);
            if (useBlackAndWhite)
                ip.setColor(Color.white);
            else
                ip.setColor(Toolbar.getBackgroundColor());
            ip.drawPixel(0,0);
            bcolor = ip.getPixel(0,0);
            ip.setColor(Toolbar.getForegroundColor());
            ip.putPixel(0,0,savePixel);

            int[] lut = new int[256];
            for (int i=0; i<256; i++) {
                if (i>=minThreshold && i<=maxThreshold)
                    lut[i] = fill1?fcolor:(byte)i;
                else {
                    lut[i] = fill2?bcolor:(byte)i;
                }
            }
            if (imp.getStackSize()>1)
                new StackProcessor(imp.getStack(), ip).applyTable(lut);
            else
                ip.applyTable(lut);
            if (convertToMask) {
                if (!imp.isInvertedLut()) {
                    setInvertedLut(imp);
                    fcolor = 255 - fcolor;
                    bcolor = 255 - bcolor;
                }
                if (Prefs.blackBackground)
                    ip.invertLut();
            }
            if (fill1=true && fill2==true && ((fcolor==0&&bcolor==255)||(fcolor==255&&bcolor==0)))
                imp.getProcessor().setThreshold(fcolor, fcolor, ImageProcessor.NO_LUT_UPDATE);
            imp.updateAndRepaintWindow();
            imp.unlock();
        }

        void applyShortOrFloatThreshold(ImagePlus imp) {
            if (!imp.lock()) return;
            int width = imp.getWidth();
            int height = imp.getHeight();
            int size = width*height;
            boolean isFloat = imp.getType()==ImagePlus.GRAY32;
            int nSlices = imp.getStackSize();
            ImageStack stack1 = imp.getStack();
            ImageStack stack2 = new ImageStack(width, height);
            ImageProcessor ip = imp.getProcessor();
            float t1 = (float)ip.getMinThreshold();
            float t2 = (float)ip.getMaxThreshold();
            if (t1==ImageProcessor.NO_THRESHOLD) {
                //ip.resetMinAndMax();
                double min = ip.getMin();
                double max = ip.getMax();
                ip = ip.convertToByte(true);
                autoThreshold(ip);
                t1 = (float)(min + (max-min)*(minThreshold/255.0));
                t2 = (float)(min + (max-min)*(maxThreshold/255.0));
            }
            float value;
            ImageProcessor ip1, ip2;
            IJ.showStatus("Converting to mask");
            for(int i=1; i<=nSlices; i++) {
                IJ.showProgress(i, nSlices);
                String label = stack1.getSliceLabel(i);
                ip1 = stack1.getProcessor(i);
                ip2 = new ByteProcessor(width, height);
                for (int j=0; j<size; j++) {
                    value = ip1.getf(j);
                    if (value>=t1 && value<=t2)
                        ip2.set(j, 255);
                    else
                        ip2.set(j, 0);
                }
                stack2.addSlice(label, ip2);
            }
            imp.setStack(null, stack2);
            ImageStack stack = imp.getStack();
            stack.setColorModel(LookUpTable.createGrayscaleColorModel(!Prefs.blackBackground));
            imp.setStack(null, stack);
            imp.getProcessor().setThreshold(255, 255, ImageProcessor.NO_LUT_UPDATE);
            IJ.showStatus("");
            imp.unlock();
        }

        void convertStackToBinary(ImagePlus imp) {
            int nSlices = imp.getStackSize();
            if ((imp.getBitDepth()!=8)) {
                IJ.showStatus("Converting to byte");
                ImageStack stack1 = imp.getStack();
                ImageStack stack2 = new ImageStack(imp.getWidth(), imp.getHeight());
                for(int i=1; i<=nSlices; i++) {
                    IJ.showProgress(i, nSlices);
                    String label = stack1.getSliceLabel(i);
                    ImageProcessor ip = stack1.getProcessor(i);
                    ip.resetMinAndMax();
                    stack2.addSlice(label, ip.convertToByte(true));
                }
                imp.setStack(null, stack2);
            }
            ImageStack stack = imp.getStack();
            IJ.showStatus("Auto-thresholding");
            for(int i=1; i<=nSlices; i++) {
                IJ.showProgress(i, nSlices);
                ImageProcessor ip = stack.getProcessor(i);
                autoThreshold(ip);
                int[] lut = new int[256];
                for (int j=0; j<256; j++) {
                    if (j>=minThreshold && j<=maxThreshold)
                        lut[j] = (byte)255;
                    else
                        lut[j] = 0;
                }
                ip.applyTable(lut);
            }
            stack.setColorModel(LookUpTable.createGrayscaleColorModel(!Prefs.blackBackground));
            imp.setStack(null, stack);
            imp.getProcessor().setThreshold(255, 255, ImageProcessor.NO_LUT_UPDATE);
            IJ.showStatus("");
        }

        void convertToByte(ImagePlus imp) {
            ImageProcessor ip;
            int currentSlice =  imp.getCurrentSlice();
            ImageStack stack1 = imp.getStack();
            ImageStack stack2 = imp.createEmptyStack();
            int nSlices = imp.getStackSize();
            String label;
            for(int i=1; i<=nSlices; i++) {
                label = stack1.getSliceLabel(i);
                ip = stack1.getProcessor(i);
                ip.setMinAndMax(0, 255);
                stack2.addSlice(label, ip.convertToByte(true));
            }
            imp.setStack(null, stack2);
            imp.setSlice(currentSlice);
            imp.setCalibration(imp.getCalibration()); //update calibration
        }

        void setInvertedLut(ImagePlus imp) {
            ImageProcessor ip = imp.getProcessor();
            ip.invertLut();
            int nImages = imp.getStackSize();
            if (nImages==1)
                ip.invert();
            else {
                ImageStack stack = imp.getStack();
                for (int slice=1; slice<=nImages; slice++)
                    stack.getProcessor(slice).invert();
                stack.setColorModel(ip.getColorModel());
            }
        }

        void autoThreshold(ImageProcessor ip) {
            ImageStatistics stats = ImageStatistics.getStatistics(ip, MIN_MAX+MODE, null);
            int threshold = ((ByteProcessor)ip).getAutoThreshold();
            if ((stats.max-stats.mode)<(stats.mode-stats.min)) {
                minThreshold = 0.0;
                maxThreshold = threshold;
            } else {
                minThreshold = threshold;
                maxThreshold = 255.0;
            }
        }

    }

| Thresholder |