Exemple #1
0
  /** Generate output image whose type is same as input image. */
  private ImagePlus makeOutputImage(ImagePlus imp, FloatProcessor fp, int ptype) {
    int width = imp.getWidth();
    int height = imp.getHeight();
    float[] pixels = (float[]) fp.getPixels();
    ImageProcessor oip = null;

    // Create output image consistent w/ type of input image.
    int size = pixels.length;
    switch (ptype) {
      case BYTE_TYPE:
        oip = imp.getProcessor().createProcessor(width, height);
        byte[] pixels8 = (byte[]) oip.getPixels();
        for (int i = 0; i < size; i++) pixels8[i] = (byte) pixels[i];
        break;
      case SHORT_TYPE:
        oip = imp.getProcessor().createProcessor(width, height);
        short[] pixels16 = (short[]) oip.getPixels();
        for (int i = 0; i < size; i++) pixels16[i] = (short) pixels[i];
        break;
      case FLOAT_TYPE:
        oip = new FloatProcessor(width, height, pixels, null);
        break;
    }

    // Adjust for display.
    // Calling this on non-ByteProcessors ensures image
    // processor is set up to correctly display image.
    oip.resetMinAndMax();

    // Create new image plus object. Don't use
    // ImagePlus.createImagePlus here because there may be
    // attributes of input image that are not appropriate for
    // projection.
    return new ImagePlus(makeTitle(), oip);
  }
  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 Otsu(ImagePlus imp, int radius, double par1, double par2, boolean doIwhite) {
    // Otsu's threshold algorithm
    // C++ code by Jordan Bevik <*****@*****.**>
    // ported to ImageJ plugin by G.Landini. Same algorithm as in Auto_Threshold, this time on local
    // circular regions
    int[] data;
    int w = imp.getWidth();
    int h = imp.getHeight();
    int position;
    int radiusx2 = radius * 2;
    ImageProcessor ip = imp.getProcessor();
    byte[] pixels = (byte[]) ip.getPixels();
    byte[] pixelsOut =
        new byte
            [pixels.length]; // need this to avoid changing the image data (and further histograms)
    byte object;
    byte backg;

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

    int k, kStar; // k = the current threshold; kStar = optimal threshold
    int N1, N; // N1 = # points with intensity <=k; N = total number of points
    double BCV, BCVmax; // The current Between Class Variance and maximum BCV
    double num, denom; // temporary bookeeping
    int Sk; // The total intensity for all histogram points <=k
    int S,
        L =
            256; // The total intensity of the image. Need to hange here if modifying for >8 bits
                 // images
    int roiy;

    Roi roi = new OvalRoi(0, 0, radiusx2, radiusx2);
    // ip.setRoi(roi);
    for (int y = 0; y < h; y++) {
      IJ.showProgress(
          (double) (y) / (h - 1)); // this method is slow, so let's show the progress bar
      roiy = y - radius;
      for (int x = 0; x < w; x++) {
        roi.setLocation(x - radius, roiy);
        ip.setRoi(roi);
        // ip.setRoi(new OvalRoi(x-radius, roiy, radiusx2, radiusx2));
        position = x + y * w;
        data = ip.getHistogram();

        // Initialize values:
        S = N = 0;
        for (k = 0; k < L; k++) {
          S += k * data[k]; // Total histogram intensity
          N += data[k]; // Total number of data points
        }

        Sk = 0;
        N1 = data[0]; // The entry for zero intensity
        BCV = 0;
        BCVmax = 0;
        kStar = 0;

        // Look at each possible threshold value,
        // calculate the between-class variance, and decide if it's a max
        for (k = 1; k < L - 1; k++) { // No need to check endpoints k = 0 or k = L-1
          Sk += k * data[k];
          N1 += data[k];

          // The float casting here is to avoid compiler warning about loss of precision and
          // will prevent overflow in the case of large saturated images
          denom = (double) (N1) * (N - N1); // Maximum value of denom is (N^2)/4 =  approx. 3E10

          if (denom != 0) {
            // Float here is to avoid loss of precision when dividing
            num = ((double) N1 / N) * S - Sk; // Maximum value of num =  255*N = approx 8E7
            BCV = (num * num) / denom;
          } else BCV = 0;

          if (BCV >= BCVmax) { // Assign the best threshold found so far
            BCVmax = BCV;
            kStar = k;
          }
        }
        // kStar += 1;	// Use QTI convention that intensity -> 1 if intensity >= k
        // (the algorithm was developed for I-> 1 if I <= k.)
        // return kStar;
        pixelsOut[position] = ((int) (pixels[position] & 0xff) > kStar) ? object : backg;
      }
    }
    for (position = 0; position < w * h; position++)
      pixels[position] = pixelsOut[position]; // update with thresholded pixels
  }
  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;
  }