void Bernsen(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) {
    // Bernsen recommends WIN_SIZE = 31 and CONTRAST_THRESHOLD = 15.
    //  1) Bernsen J. (1986) "Dynamic Thresholding of Grey-Level Images"
    //    Proc. of the 8th Int. Conf. on Pattern Recognition, pp. 1251-1255
    //  2) Sezgin M. and Sankur B. (2004) "Survey over Image Thresholding
    //   Techniques and Quantitative Performance Evaluation" Journal of
    //   Electronic Imaging, 13(1): 146-165
    //  http://citeseer.ist.psu.edu/sezgin04survey.html
    // Ported to ImageJ plugin from E Celebi's fourier_0.8 routines
    // This version uses a circular local window, instead of a rectagular one
    ImagePlus Maximp, Minimp;
    ImageProcessor ip = imp.getProcessor(), ipMax, ipMin;
    int contrast_threshold = 15;
    int local_contrast;
    int mid_gray;
    byte object;
    byte backg;
    int temp;

    if (par1 != 0) {
      IJ.log("Bernsen: changed contrast_threshold from :" + contrast_threshold + "  to:" + par1);
      contrast_threshold = (int) par1;
    }

    if (doIwhite) {
      object = (byte) 0xff;
      backg = (byte) 0;
    } else {
      object = (byte) 0;
      backg = (byte) 0xff;
    }

    Maximp = duplicateImage(ip);
    ipMax = Maximp.getProcessor();
    RankFilters rf = new RankFilters();
    rf.rank(ipMax, radius, rf.MAX); // Maximum
    // Maximp.show();
    Minimp = duplicateImage(ip);
    ipMin = Minimp.getProcessor();
    rf.rank(ipMin, radius, rf.MIN); // Minimum
    // Minimp.show();
    byte[] pixels = (byte[]) ip.getPixels();
    byte[] max = (byte[]) ipMax.getPixels();
    byte[] min = (byte[]) ipMin.getPixels();

    for (int i = 0; i < pixels.length; i++) {
      local_contrast = (int) ((max[i] & 0xff) - (min[i] & 0xff));
      mid_gray = (int) ((min[i] & 0xff) + (max[i] & 0xff)) / 2;
      temp = (int) (pixels[i] & 0x0000ff);
      if (local_contrast < contrast_threshold)
        pixels[i] = (mid_gray >= 128) ? object : backg; // Low contrast region
      else pixels[i] = (temp >= mid_gray) ? object : backg;
    }
    // imp.updateAndDraw();
    return;
  }
  void Contrast(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) {
    // G. Landini, 2013
    // Based on a simple contrast toggle. This procedure does not have user-provided paramters other
    // than the kernel radius
    // Sets the pixel value to either white or black depending on whether its current value is
    // closest to the local Max or Min respectively
    // The procedure is similar to Toggle Contrast Enhancement (see Soille, Morphological Image
    // Analysis (2004), p. 259

    ImagePlus Maximp, Minimp;
    ImageProcessor ip = imp.getProcessor(), ipMax, ipMin;
    int c_value = 0;
    int mid_gray;
    byte object;
    byte backg;

    if (doIwhite) {
      object = (byte) 0xff;
      backg = (byte) 0;
    } else {
      object = (byte) 0;
      backg = (byte) 0xff;
    }

    Maximp = duplicateImage(ip);
    ipMax = Maximp.getProcessor();
    RankFilters rf = new RankFilters();
    rf.rank(ipMax, radius, rf.MAX); // Maximum
    // Maximp.show();
    Minimp = duplicateImage(ip);
    ipMin = Minimp.getProcessor();
    rf.rank(ipMin, radius, rf.MIN); // Minimum
    // Minimp.show();
    byte[] pixels = (byte[]) ip.getPixels();
    byte[] max = (byte[]) ipMax.getPixels();
    byte[] min = (byte[]) ipMin.getPixels();
    for (int i = 0; i < pixels.length; i++) {
      pixels[i] =
          ((Math.abs((int) (max[i] & 0xff - pixels[i] & 0xff))
                  <= Math.abs((int) (pixels[i] & 0xff - min[i] & 0xff))))
              ? object
              : backg;
    }
    // imp.updateAndDraw();
    return;
  }
  void MidGrey(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) {
    // See: Image Processing Learning Resourches HIPR2
    // http://homepages.inf.ed.ac.uk/rbf/HIPR2/adpthrsh.htm
    ImagePlus Maximp, Minimp;
    ImageProcessor ip = imp.getProcessor(), ipMax, ipMin;
    int c_value = 0;
    int mid_gray;
    byte object;
    byte backg;

    if (par1 != 0) {
      IJ.log("MidGrey: changed c_value from :" + c_value + "  to:" + par1);
      c_value = (int) par1;
    }

    if (doIwhite) {
      object = (byte) 0xff;
      backg = (byte) 0;
    } else {
      object = (byte) 0;
      backg = (byte) 0xff;
    }

    Maximp = duplicateImage(ip);
    ipMax = Maximp.getProcessor();
    RankFilters rf = new RankFilters();
    rf.rank(ipMax, radius, rf.MAX); // Maximum
    // Maximp.show();
    Minimp = duplicateImage(ip);
    ipMin = Minimp.getProcessor();
    rf.rank(ipMin, radius, rf.MIN); // Minimum
    // Minimp.show();
    byte[] pixels = (byte[]) ip.getPixels();
    byte[] max = (byte[]) ipMax.getPixels();
    byte[] min = (byte[]) ipMin.getPixels();

    for (int i = 0; i < pixels.length; i++) {
      pixels[i] =
          ((int) (pixels[i] & 0xff) > (int) (((max[i] & 0xff) + (min[i] & 0xff)) / 2) - c_value)
              ? object
              : backg;
    }
    // imp.updateAndDraw();
    return;
  }
  void Mean(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) {
    // See: Image Processing Learning Resourches HIPR2
    // http://homepages.inf.ed.ac.uk/rbf/HIPR2/adpthrsh.htm
    ImagePlus Meanimp;
    ImageProcessor ip = imp.getProcessor(), ipMean;
    int c_value = 0;
    byte object;
    byte backg;

    if (par1 != 0) {
      IJ.log("Mean: changed c_value from :" + c_value + "  to:" + par1);
      c_value = (int) par1;
    }

    if (doIwhite) {
      object = (byte) 0xff;
      backg = (byte) 0;
    } else {
      object = (byte) 0;
      backg = (byte) 0xff;
    }

    Meanimp = duplicateImage(ip);
    ImageConverter ic = new ImageConverter(Meanimp);
    ic.convertToGray32();

    ipMean = Meanimp.getProcessor();
    RankFilters rf = new RankFilters();
    rf.rank(ipMean, radius, rf.MEAN); // Mean
    // Meanimp.show();
    byte[] pixels = (byte[]) ip.getPixels();
    float[] mean = (float[]) ipMean.getPixels();

    for (int i = 0; i < pixels.length; i++)
      pixels[i] = ((int) (pixels[i] & 0xff) > (int) (mean[i] - c_value)) ? object : backg;
    // imp.updateAndDraw();
    return;
  }
  void Sauvola(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) {
    // Sauvola recommends K_VALUE = 0.5 and R_VALUE = 128.
    // This is a modification of Niblack's thresholding method.
    // Sauvola J. and Pietaksinen M. (2000) "Adaptive Document Image Binarization"
    // Pattern Recognition, 33(2): 225-236
    // http://www.ee.oulu.fi/mvg/publications/show_pdf.php?ID=24
    // Ported to ImageJ plugin from E Celebi's fourier_0.8 routines
    // This version uses a circular local window, instead of a rectagular one

    ImagePlus Meanimp, Varimp;
    ImageProcessor ip = imp.getProcessor(), ipMean, ipVar;
    double k_value = 0.5;
    double r_value = 128;
    byte object;
    byte backg;

    if (par1 != 0) {
      IJ.log("Sauvola: changed k_value from :" + k_value + "  to:" + par1);
      k_value = par1;
    }

    if (par2 != 0) {
      IJ.log("Sauvola: changed r_value from :" + r_value + "  to:" + par2);
      r_value = par2;
    }

    if (doIwhite) {
      object = (byte) 0xff;
      backg = (byte) 0;
    } else {
      object = (byte) 0;
      backg = (byte) 0xff;
    }

    Meanimp = duplicateImage(ip);
    ImageConverter ic = new ImageConverter(Meanimp);
    ic.convertToGray32();

    ipMean = Meanimp.getProcessor();
    RankFilters rf = new RankFilters();
    rf.rank(ipMean, radius, rf.MEAN); // Mean
    // Meanimp.show();
    Varimp = duplicateImage(ip);
    ic = new ImageConverter(Varimp);
    ic.convertToGray32();
    ipVar = Varimp.getProcessor();
    rf.rank(ipVar, radius, rf.VARIANCE); // Variance
    // Varimp.show();
    byte[] pixels = (byte[]) ip.getPixels();
    float[] mean = (float[]) ipMean.getPixels();
    float[] var = (float[]) ipVar.getPixels();

    for (int i = 0; i < pixels.length; i++)
      pixels[i] =
          ((int) (pixels[i] & 0xff)
                  > (int) (mean[i] * (1.0 + k_value * ((Math.sqrt(var[i]) / r_value) - 1.0))))
              ? object
              : backg;
    // imp.updateAndDraw();
    return;
  }
  void Phansalkar(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) {
    // This is a modification of Sauvola's thresholding method to deal with low contrast images.
    // Phansalskar N. et al. Adaptive local thresholding for detection of nuclei in diversity
    // stained
    // cytology images.International Conference on Communications and Signal Processing (ICCSP),
    // 2011,
    // 218 - 220.
    // In this method, the threshold t = mean*(1+p*exp(-q*mean)+k*((stdev/r)-1))
    // Phansalkar recommends k = 0.25, r = 0.5, p = 2 and q = 10. In this plugin, k and r are the
    // parameters 1 and 2 respectively, but the values of p and q are fixed.
    //
    // Implemented from Phansalkar's paper description by G. Landini
    // This version uses a circular local window, instead of a rectagular one

    ImagePlus Meanimp, Varimp, Orimp;
    ImageProcessor ip = imp.getProcessor(), ipMean, ipVar, ipOri;
    double k_value = 0.25;
    double r_value = 0.5;
    double p_value = 2.0;
    double q_value = 10.0;
    byte object;
    byte backg;

    if (par1 != 0) {
      IJ.log("Phansalkar: changed k_value from :" + k_value + "  to:" + par1);
      k_value = par1;
    }

    if (par2 != 0) {
      IJ.log("Phansalkar: changed r_value from :" + r_value + "  to:" + par2);
      r_value = par2;
    }

    if (doIwhite) {
      object = (byte) 0xff;
      backg = (byte) 0;
    } else {
      object = (byte) 0;
      backg = (byte) 0xff;
    }

    Meanimp = duplicateImage(ip);
    ContrastEnhancer ce = new ContrastEnhancer();
    ce.stretchHistogram(Meanimp, 0.0);
    ImageConverter ic = new ImageConverter(Meanimp);
    ic.convertToGray32();
    ipMean = Meanimp.getProcessor();
    ipMean.multiply(1.0 / 255);

    Orimp = duplicateImage(ip);
    ce.stretchHistogram(Orimp, 0.0);
    ic = new ImageConverter(Orimp);
    ic.convertToGray32();
    ipOri = Orimp.getProcessor();
    ipOri.multiply(1.0 / 255); // original to compare
    // Orimp.show();

    RankFilters rf = new RankFilters();
    rf.rank(ipMean, radius, rf.MEAN); // Mean

    // Meanimp.show();
    Varimp = duplicateImage(ip);
    ce.stretchHistogram(Varimp, 0.0);
    ic = new ImageConverter(Varimp);
    ic.convertToGray32();
    ipVar = Varimp.getProcessor();
    ipVar.multiply(1.0 / 255);

    rf.rank(ipVar, radius, rf.VARIANCE); // Variance
    ipVar.sqr(); // SD

    // Varimp.show();
    byte[] pixels = (byte[]) ip.getPixels();
    float[] ori = (float[]) ipOri.getPixels();
    float[] mean = (float[]) ipMean.getPixels();
    float[] sd = (float[]) ipVar.getPixels();

    for (int i = 0; i < pixels.length; i++)
      pixels[i] =
          ((ori[i])
                  > (mean[i]
                      * (1.0
                          + p_value * Math.exp(-q_value * mean[i])
                          + k_value * ((sd[i] / r_value) - 1.0))))
              ? object
              : backg;
    // imp.updateAndDraw();
    return;
  }
  void Niblack(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) {
    // Niblack recommends K_VALUE = -0.2 for images with black foreground
    // objects, and K_VALUE = +0.2 for images with white foreground objects.
    //  Niblack W. (1986) "An introduction to Digital Image Processing" Prentice-Hall.
    // Ported to ImageJ plugin from E Celebi's fourier_0.8 routines
    // This version uses a circular local window, instead of a rectagular one

    ImagePlus Meanimp, Varimp;
    ImageProcessor ip = imp.getProcessor(), ipMean, ipVar;
    double k_value;
    int c_value = 0;

    byte object;
    byte backg;

    if (doIwhite) {
      k_value = 0.2;
      object = (byte) 0xff;
      backg = (byte) 0;
    } else {
      k_value = -0.2;
      object = (byte) 0;
      backg = (byte) 0xff;
    }

    if (par1 != 0) {
      IJ.log("Niblack: changed k_value from :" + k_value + "  to:" + par1);
      k_value = par1;
    }

    if (par2 != 0) {
      IJ.log(
          "Niblack: changed c_value from :"
              + c_value
              + "  to:"
              + par2); // requested feature, not in original
      c_value = (int) par2;
    }

    Meanimp = duplicateImage(ip);
    ImageConverter ic = new ImageConverter(Meanimp);
    ic.convertToGray32();

    ipMean = Meanimp.getProcessor();
    RankFilters rf = new RankFilters();
    rf.rank(ipMean, radius, rf.MEAN); // Mean
    // Meanimp.show();
    Varimp = duplicateImage(ip);
    ic = new ImageConverter(Varimp);
    ic.convertToGray32();
    ipVar = Varimp.getProcessor();
    rf.rank(ipVar, radius, rf.VARIANCE); // Variance
    // Varimp.show();
    byte[] pixels = (byte[]) ip.getPixels();
    float[] mean = (float[]) ipMean.getPixels();
    float[] var = (float[]) ipVar.getPixels();

    for (int i = 0; i < pixels.length; i++)
      pixels[i] =
          ((int) (pixels[i] & 0xff) > (int) (mean[i] + k_value * Math.sqrt(var[i]) - c_value))
              ? object
              : backg;
    // imp.updateAndDraw();
    return;
  }