Exemple #1
0
 void createEllipse(ImagePlus imp) {
   IJ.showStatus("Fitting ellipse");
   Roi roi = imp.getRoi();
   if (roi == null) {
     noRoi("Fit Ellipse");
     return;
   }
   if (roi.isLine()) {
     IJ.error("Fit Ellipse", "\"Fit Ellipse\" does not work with line selections");
     return;
   }
   ImageProcessor ip = imp.getProcessor();
   ip.setRoi(roi);
   int options = Measurements.CENTROID + Measurements.ELLIPSE;
   ImageStatistics stats = ImageStatistics.getStatistics(ip, options, null);
   double dx = stats.major * Math.cos(stats.angle / 180.0 * Math.PI) / 2.0;
   double dy = -stats.major * Math.sin(stats.angle / 180.0 * Math.PI) / 2.0;
   double x1 = stats.xCentroid - dx;
   double x2 = stats.xCentroid + dx;
   double y1 = stats.yCentroid - dy;
   double y2 = stats.yCentroid + dy;
   double aspectRatio = stats.minor / stats.major;
   imp.killRoi();
   imp.setRoi(new EllipseRoi(x1, y1, x2, y2, aspectRatio));
 }
    void setHistogram(ImagePlus imp, int j) {
      ImageProcessor ip = imp.getProcessor();
      ImageStatistics stats = ImageStatistics.getStatistics(ip, AREA + MODE, null);
      int maxCount2 = 0;
      histogram = stats.histogram;
      for (int i = 0; i < stats.nBins; i++)
        if ((histogram[i] > maxCount2) && (i != stats.mode)) maxCount2 = histogram[i];
      hmax = stats.maxCount;
      if ((hmax > (maxCount2 * 1.5)) && (maxCount2 != 0)) { // GL 1.5 was 2
        hmax = (int) (maxCount2 * 1.1); // GL 1.1 was 1.5
        histogram[stats.mode] = hmax;
      }
      os = null;
      ColorModel cm = ip.getColorModel();
      if (!(cm instanceof IndexColorModel)) return;
      IndexColorModel icm = (IndexColorModel) cm;
      int mapSize = icm.getMapSize();
      if (mapSize != 256) return;
      byte[] r = new byte[256];
      byte[] g = new byte[256];
      byte[] b = new byte[256];
      icm.getReds(r);
      icm.getGreens(g);
      icm.getBlues(b);
      hColors = new Color[256];

      if (isRGB) {
        if (j == 0) {
          for (int i = 0; i < 256; i++) hColors[i] = new Color(i & 255, 0 & 255, 0 & 255);
        } else if (j == 1) {
          for (int i = 0; i < 256; i++) hColors[i] = new Color(0 & 255, i & 255, 0 & 255);
        } else if (j == 2) {
          for (int i = 0; i < 256; i++) hColors[i] = new Color(0 & 255, 0 & 255, i & 255);
        }
      } else {
        if (j == 0) {
          for (int i = 0; i < 256; i++) hColors[i] = new Color(r[i] & 255, g[i] & 255, b[i] & 255);
        } else if (j == 1) {
          for (int i = 0; i < 256; i++)
            // hColors[i] = new Color(127-i/2&255, 127+i/2&255, 127-i/2&255);
            hColors[i] = new Color(192 - i / 4 & 255, 192 + i / 4 & 255, 192 - i / 4 & 255);
        } else if (j == 2) {
          for (int i = 0; i < 256; i++) hColors[i] = new Color(i & 255, i & 255, 0 & 255);
        }
      }
    }
 boolean eraseOutsideRoi(ImageProcessor ip, Rectangle r, ImageProcessor mask) {
   int width = ip.getWidth();
   int height = ip.getHeight();
   ip.setRoi(r);
   if (excludeEdgeParticles && polygon != null) {
     ImageStatistics stats = ImageStatistics.getStatistics(ip, MIN_MAX, null);
     if (fillColor >= stats.min && fillColor <= stats.max) {
       double replaceColor = level1 - 1.0;
       if (replaceColor < 0.0 || replaceColor == fillColor) {
         replaceColor = level2 + 1.0;
         int maxColor = imageType == BYTE ? 255 : 65535;
         if (replaceColor > maxColor || replaceColor == fillColor) {
           IJ.error("Particle Analyzer", "Unable to remove edge particles");
           return false;
         }
       }
       for (int y = minY; y < maxY; y++) {
         for (int x = minX; x < maxX; x++) {
           int v = ip.getPixel(x, y);
           if (v == fillColor) ip.putPixel(x, y, (int) replaceColor);
         }
       }
     }
   }
   ip.setValue(fillColor);
   if (mask != null) {
     mask = mask.duplicate();
     mask.invert();
     ip.fill(mask);
   }
   ip.setRoi(0, 0, r.x, height);
   ip.fill();
   ip.setRoi(r.x, 0, r.width, r.y);
   ip.fill();
   ip.setRoi(r.x, r.y + r.height, r.width, height - (r.y + r.height));
   ip.fill();
   ip.setRoi(r.x + r.width, 0, width - (r.x + r.width), height);
   ip.fill();
   ip.resetRoi();
   // IJ.log("erase: "+fillColor+"	"+level1+"	"+level2+"	"+excludeEdgeParticles);
   // (new ImagePlus("ip2", ip.duplicate())).show();
   return true;
 }
  /* Method takes roi and substract its mean value from each frame in the image*/
  public static ImagePlus DarkNoiseRemoval(ImagePlus imp_in, Roi noise_roi) {
    ImagePlus imp_out = imp_in.duplicate();
    int size = imp_in.getImageStackSize();
    ImageProcessor ip = imp_out.getProcessor();
    double minThreshold = ip.getMinThreshold();
    double maxThreshold = ip.getMaxThreshold();
    Calibration cal = imp_in.getCalibration();
    int measurements = Analyzer.getMeasurements();
    int current = imp_in.getCurrentSlice();
    ImageStack stack = imp_in.getStack();
    for (int i = 1; i <= size; i++) {
      ip = stack.getProcessor(i);
      if (minThreshold != ImageProcessor.NO_THRESHOLD)
        ip.setThreshold(minThreshold, maxThreshold, ImageProcessor.NO_LUT_UPDATE);
      ip.setRoi(noise_roi);
      ImageStatistics stats = ImageStatistics.getStatistics(ip, measurements, cal);
      ip.resetRoi();
      ip.subtract(stats.mean);
      stack.setProcessor(ip, i);
    }

    imp_out.setStack(stack);
    return imp_out;
  }
Exemple #5
0
  /*
  if selection is closed shape, create a circle with the same area and centroid, otherwise use<br>
  the Pratt method to fit a circle to the points that define the line or multi-point selection.<br>
  Reference: Pratt V., Direct least-squares fitting of algebraic surfaces", Computer Graphics, Vol. 21, pages 145-152 (1987).<br>
  Original code: Nikolai Chernov's MATLAB script for Newton-based Pratt fit.<br>
  (http://www.math.uab.edu/~chernov/cl/MATLABcircle.html)<br>
  Java version: https://github.com/mdoube/BoneJ/blob/master/src/org/doube/geometry/FitCircle.java<br>
  @authors Nikolai Chernov, Michael Doube, Ved Sharma
  */
  void fitCircle(ImagePlus imp) {
    Roi roi = imp.getRoi();
    if (roi == null) {
      noRoi("Fit Circle");
      return;
    }

    if (roi.isArea()) { // create circle with the same area and centroid
      ImageProcessor ip = imp.getProcessor();
      ip.setRoi(roi);
      ImageStatistics stats =
          ImageStatistics.getStatistics(ip, Measurements.AREA + Measurements.CENTROID, null);
      double r = Math.sqrt(stats.pixelCount / Math.PI);
      imp.killRoi();
      int d = (int) Math.round(2.0 * r);
      IJ.makeOval(
          (int) Math.round(stats.xCentroid - r), (int) Math.round(stats.yCentroid - r), d, d);
      return;
    }

    Polygon poly = roi.getPolygon();
    int n = poly.npoints;
    int[] x = poly.xpoints;
    int[] y = poly.ypoints;
    if (n < 3) {
      IJ.error("Fit Circle", "At least 3 points are required to fit a circle.");
      return;
    }

    // calculate point centroid
    double sumx = 0, sumy = 0;
    for (int i = 0; i < n; i++) {
      sumx = sumx + poly.xpoints[i];
      sumy = sumy + poly.ypoints[i];
    }
    double meanx = sumx / n;
    double meany = sumy / n;

    // calculate moments
    double[] X = new double[n], Y = new double[n];
    double Mxx = 0, Myy = 0, Mxy = 0, Mxz = 0, Myz = 0, Mzz = 0;
    for (int i = 0; i < n; i++) {
      X[i] = x[i] - meanx;
      Y[i] = y[i] - meany;
      double Zi = X[i] * X[i] + Y[i] * Y[i];
      Mxy = Mxy + X[i] * Y[i];
      Mxx = Mxx + X[i] * X[i];
      Myy = Myy + Y[i] * Y[i];
      Mxz = Mxz + X[i] * Zi;
      Myz = Myz + Y[i] * Zi;
      Mzz = Mzz + Zi * Zi;
    }
    Mxx = Mxx / n;
    Myy = Myy / n;
    Mxy = Mxy / n;
    Mxz = Mxz / n;
    Myz = Myz / n;
    Mzz = Mzz / n;

    // calculate the coefficients of the characteristic polynomial
    double Mz = Mxx + Myy;
    double Cov_xy = Mxx * Myy - Mxy * Mxy;
    double Mxz2 = Mxz * Mxz;
    double Myz2 = Myz * Myz;
    double A2 = 4 * Cov_xy - 3 * Mz * Mz - Mzz;
    double A1 = Mzz * Mz + 4 * Cov_xy * Mz - Mxz2 - Myz2 - Mz * Mz * Mz;
    double A0 = Mxz2 * Myy + Myz2 * Mxx - Mzz * Cov_xy - 2 * Mxz * Myz * Mxy + Mz * Mz * Cov_xy;
    double A22 = A2 + A2;
    double epsilon = 1e-12;
    double ynew = 1e+20;
    int IterMax = 20;
    double xnew = 0;
    int iterations = 0;

    // Newton's method starting at x=0
    for (int iter = 1; iter <= IterMax; iter++) {
      iterations = iter;
      double yold = ynew;
      ynew = A0 + xnew * (A1 + xnew * (A2 + 4. * xnew * xnew));
      if (Math.abs(ynew) > Math.abs(yold)) {
        if (IJ.debugMode) IJ.log("Fit Circle: wrong direction: |ynew| > |yold|");
        xnew = 0;
        break;
      }
      double Dy = A1 + xnew * (A22 + 16 * xnew * xnew);
      double xold = xnew;
      xnew = xold - ynew / Dy;
      if (Math.abs((xnew - xold) / xnew) < epsilon) break;
      if (iter >= IterMax) {
        if (IJ.debugMode) IJ.log("Fit Circle: will not converge");
        xnew = 0;
      }
      if (xnew < 0) {
        if (IJ.debugMode) IJ.log("Fit Circle: negative root:  x = " + xnew);
        xnew = 0;
      }
    }
    if (IJ.debugMode)
      IJ.log("Fit Circle: n=" + n + ", xnew=" + IJ.d2s(xnew, 2) + ", iterations=" + iterations);

    // calculate the circle parameters
    double DET = xnew * xnew - xnew * Mz + Cov_xy;
    double CenterX = (Mxz * (Myy - xnew) - Myz * Mxy) / (2 * DET);
    double CenterY = (Myz * (Mxx - xnew) - Mxz * Mxy) / (2 * DET);
    double radius = Math.sqrt(CenterX * CenterX + CenterY * CenterY + Mz + 2 * xnew);
    if (Double.isNaN(radius)) {
      IJ.error("Fit Circle", "Points are collinear.");
      return;
    }
    CenterX = CenterX + meanx;
    CenterY = CenterY + meany;
    imp.killRoi();
    IJ.makeOval(
        (int) Math.round(CenterX - radius),
        (int) Math.round(CenterY - radius),
        (int) Math.round(2 * radius),
        (int) Math.round(2 * radius));
  }
  // returns value of mean intensity+3*SD based on
  // fitting of image histogram to gaussian function
  int getThreshold(ImageProcessor thImage) {
    ImageStatistics imgstat;
    double[][] dHistogram;
    double[] dHistCum;
    double[][] dNoiseFit;
    int nHistSize;
    int nCount, nMaxCount;
    int nDownCount, nUpCount;
    int i, nPeakPos;
    double dRightWidth, dLeftWidth, dWidth;
    double dMean, dSD;
    double[] dFitErrors;
    double dErrCoeff;
    double dSum = 0.0;
    double dSumFit = 0.0;
    LMA fitlma;

    // mean, sd, min, max
    // imgstat = ImageStatistics.getStatistics(thImage, 38, null);
    imgstat =
        ImageStatistics.getStatistics(
            thImage, Measurements.MEAN + Measurements.STD_DEV + Measurements.MIN_MAX, null);
    dMean = imgstat.mean;
    nPeakPos = 0;

    nHistSize = imgstat.histogram.length;
    dHistogram = new double[2][nHistSize];
    nMaxCount = 0;

    // determine position and height of maximum count in histogram (mode)
    // and height at maximum
    for (i = 0; i < nHistSize; i++) {

      nCount = imgstat.histogram[i];
      dHistogram[0][i] = imgstat.min + i * imgstat.binSize;
      dHistogram[1][i] = (double) nCount;
      dSum += (double) nCount;
      if (nMaxCount < nCount) {
        nMaxCount = nCount;
        dMean = imgstat.min + i * imgstat.binSize;
        nPeakPos = i;
      }
    }
    // estimating width of a peak
    // going to the left
    i = nPeakPos;
    while (i > 0 && imgstat.histogram[i] > 0.5 * nMaxCount) {
      i--;
    }
    if (i < 0) i = 0;
    dLeftWidth = i;
    // going to the right
    i = nPeakPos;
    while (i < nHistSize && imgstat.histogram[i] > 0.5 * nMaxCount) {
      i++;
    }
    if (i == nHistSize) i = nHistSize - 1;
    dRightWidth = i;
    // FWHM in bins
    dWidth = (dRightWidth - dLeftWidth);
    dSD = dWidth * imgstat.binSize / 2.35;
    // fitting range +/- 3*SD
    dLeftWidth = nPeakPos - 3 * dWidth / 2.35;
    if (dLeftWidth < 0) dLeftWidth = 0;
    dRightWidth = nPeakPos + 3 * dWidth / 2.35;
    if (dRightWidth > nHistSize) dRightWidth = nHistSize;
    nUpCount = (int) dRightWidth;
    nDownCount = (int) dLeftWidth;
    // preparing histogram range for fitting
    dNoiseFit = new double[2][nUpCount - nDownCount + 1];
    for (i = nDownCount; i <= nUpCount; i++) {
      dNoiseFit[0][i - nDownCount] = dHistogram[0][i];
      dNoiseFit[1][i - nDownCount] = dHistogram[1][i];
      dSumFit += dHistogram[1][i];
    }
    // fitting range is too small, less than 80%
    if ((dSumFit / dSum) < 0.8) {
      // build cumulative distribution
      dHistCum = new double[nHistSize];
      dSumFit = 0;
      for (i = 0; i < nHistSize; i++) {
        dSumFit += dHistogram[1][i];
        dHistCum[i] = dSumFit / dSum;
      }
      nUpCount = 0;
      nDownCount = 0;
      for (i = 0; i < nHistSize; i++) {
        // 10% quantile
        if (dHistCum[i] < 0.11) nDownCount = i;
        // 90% quantile
        if (dHistCum[i] < 0.91) nUpCount = i;
      }
      // preparing histogram range for fitting
      dNoiseFit = new double[2][nUpCount - nDownCount + 1];
      for (i = nDownCount; i <= nUpCount; i++) {
        dNoiseFit[0][i - nDownCount] = dHistogram[0][i];
        dNoiseFit[1][i - nDownCount] = dHistogram[1][i];
      }
    }

    fitlma =
        new LMA(new SMLOneDGaussian(), new double[] {(double) nMaxCount, dMean, dSD}, dNoiseFit);
    fitlma.fit();

    dMean = fitlma.parameters[1];
    dSD = fitlma.parameters[2];

    dFitErrors = fitlma.getStandardErrorsOfParameters();
    // scaling coefficient for parameters errors estimation
    // (Standard deviation of residuals)
    dErrCoeff = Math.sqrt(fitlma.chi2 / (nUpCount - nDownCount + 1 - 3));
    for (i = 0; i < 3; i++) dFitErrors[i] *= dErrCoeff;
    for (i = 0; i < 3; i++) dFitErrors[i] *= 100 / fitlma.parameters[i];

    if (dFitErrors[1] > 20
        || dMean < imgstat.min
        || dMean > imgstat.max
        || dSD < imgstat.min
        || dSD > imgstat.max)
      // fit somehow failed
      return (int) (imgstat.mean + 3.0 * imgstat.stdDev);
    else return (int) (dMean + 3.0 * dSD);
  }
 void drawEllipse(ImageProcessor ip, ImageStatistics stats, int count) {
   stats.drawEllipse(ip);
 }
 void analyzeParticle(int x, int y, ImagePlus imp, ImageProcessor ip) {
   // Wand wand = new Wand(ip);
   ImageProcessor ip2 = redirectIP != null ? redirectIP : ip;
   wand.autoOutline(x, y, level1, level2, wandMode);
   if (wand.npoints == 0) {
     IJ.log("wand error: " + x + " " + y);
     return;
   }
   Roi roi = new PolygonRoi(wand.xpoints, wand.ypoints, wand.npoints, roiType);
   Rectangle r = roi.getBounds();
   if (r.width > 1 && r.height > 1) {
     PolygonRoi proi = (PolygonRoi) roi;
     pf.setPolygon(proi.getXCoordinates(), proi.getYCoordinates(), proi.getNCoordinates());
     ip2.setMask(pf.getMask(r.width, r.height));
     if (floodFill) ff.particleAnalyzerFill(x, y, level1, level2, ip2.getMask(), r);
   }
   ip2.setRoi(r);
   ip.setValue(fillColor);
   ImageStatistics stats = getStatistics(ip2, measurements, calibration);
   boolean include = true;
   if (excludeEdgeParticles) {
     if (r.x == minX || r.y == minY || r.x + r.width == maxX || r.y + r.height == maxY)
       include = false;
     if (polygon != null) {
       Rectangle bounds = roi.getBounds();
       int x1 = bounds.x + wand.xpoints[wand.npoints - 1];
       int y1 = bounds.y + wand.ypoints[wand.npoints - 1];
       int x2, y2;
       for (int i = 0; i < wand.npoints; i++) {
         x2 = bounds.x + wand.xpoints[i];
         y2 = bounds.y + wand.ypoints[i];
         if (!polygon.contains(x2, y2)) {
           include = false;
           break;
         }
         if ((x1 == x2 && ip.getPixel(x1, y1 - 1) == fillColor)
             || (y1 == y2 && ip.getPixel(x1 - 1, y1) == fillColor)) {
           include = false;
           break;
         }
         x1 = x2;
         y1 = y2;
       }
     }
   }
   ImageProcessor mask = ip2.getMask();
   if (minCircularity > 0.0 || maxCircularity < 1.0) {
     double perimeter = roi.getLength();
     double circularity =
         perimeter == 0.0 ? 0.0 : 4.0 * Math.PI * (stats.pixelCount / (perimeter * perimeter));
     if (circularity > 1.0) circularity = 1.0;
     // IJ.log(circularity+"	"+perimeter+"  "+stats.area);
     if (circularity < minCircularity || circularity > maxCircularity) include = false;
   }
   if (stats.pixelCount >= minSize && stats.pixelCount <= maxSize && include) {
     particleCount++;
     if (roiNeedsImage) roi.setImage(imp);
     stats.xstart = x;
     stats.ystart = y;
     saveResults(stats, roi);
     if (showChoice != NOTHING) drawParticle(drawIP, roi, stats, mask);
   }
   if (redirectIP != null) ip.setRoi(r);
   ip.fill(mask);
 }
  /**
   * Performs particle analysis on the specified ImagePlus and ImageProcessor. Returns false if
   * there is an error.
   */
  public boolean analyze(ImagePlus imp, ImageProcessor ip) {
    if (this.imp == null) this.imp = imp;
    showResults = (options & SHOW_RESULTS) != 0;
    excludeEdgeParticles = (options & EXCLUDE_EDGE_PARTICLES) != 0;
    resetCounter = (options & CLEAR_WORKSHEET) != 0;
    showProgress = (options & SHOW_PROGRESS) != 0;
    floodFill = (options & INCLUDE_HOLES) == 0;
    recordStarts = (options & RECORD_STARTS) != 0;
    addToManager = (options & ADD_TO_MANAGER) != 0;
    displaySummary = (options & DISPLAY_SUMMARY) != 0;
    inSituShow = (options & IN_SITU_SHOW) != 0;
    outputImage = null;
    ip.snapshot();
    ip.setProgressBar(null);
    if (Analyzer.isRedirectImage()) {
      redirectImp = Analyzer.getRedirectImage(imp);
      if (redirectImp == null) return false;
      int depth = redirectImp.getStackSize();
      if (depth > 1 && depth == imp.getStackSize()) {
        ImageStack redirectStack = redirectImp.getStack();
        redirectIP = redirectStack.getProcessor(imp.getCurrentSlice());
      } else redirectIP = redirectImp.getProcessor();
    } else if (imp.getType() == ImagePlus.COLOR_RGB) {
      ImagePlus original = (ImagePlus) imp.getProperty("OriginalImage");
      if (original != null
          && original.getWidth() == imp.getWidth()
          && original.getHeight() == imp.getHeight()) {
        redirectImp = original;
        redirectIP = original.getProcessor();
      }
    }
    if (!setThresholdLevels(imp, ip)) return false;
    width = ip.getWidth();
    height = ip.getHeight();
    if (!(showChoice == NOTHING || showChoice == OVERLAY_OUTLINES || showChoice == OVERLAY_MASKS)) {
      blackBackground = Prefs.blackBackground && inSituShow;
      if (slice == 1) outlines = new ImageStack(width, height);
      if (showChoice == ROI_MASKS) drawIP = new ShortProcessor(width, height);
      else drawIP = new ByteProcessor(width, height);
      drawIP.setLineWidth(lineWidth);
      if (showChoice == ROI_MASKS) {
      } // Place holder for now...
      else if (showChoice == MASKS && !blackBackground) drawIP.invertLut();
      else if (showChoice == OUTLINES) {
        if (!inSituShow) {
          if (customLut == null) makeCustomLut();
          drawIP.setColorModel(customLut);
        }
        drawIP.setFont(new Font("SansSerif", Font.PLAIN, fontSize));
        if (fontSize > 12 && inSituShow) drawIP.setAntialiasedText(true);
      }
      outlines.addSlice(null, drawIP);

      if (showChoice == ROI_MASKS || blackBackground) {
        drawIP.setColor(Color.black);
        drawIP.fill();
        drawIP.setColor(Color.white);
      } else {
        drawIP.setColor(Color.white);
        drawIP.fill();
        drawIP.setColor(Color.black);
      }
    }
    calibration = redirectImp != null ? redirectImp.getCalibration() : imp.getCalibration();

    if (rt == null) {
      rt = Analyzer.getResultsTable();
      analyzer = new Analyzer(imp);
    } else analyzer = new Analyzer(imp, measurements, rt);
    if (resetCounter && slice == 1) {
      if (!Analyzer.resetCounter()) return false;
    }
    beginningCount = Analyzer.getCounter();

    byte[] pixels = null;
    if (ip instanceof ByteProcessor) pixels = (byte[]) ip.getPixels();
    if (r == null) {
      r = ip.getRoi();
      mask = ip.getMask();
      if (displaySummary) {
        if (mask != null) totalArea = ImageStatistics.getStatistics(ip, AREA, calibration).area;
        else totalArea = r.width * calibration.pixelWidth * r.height * calibration.pixelHeight;
      }
    }
    minX = r.x;
    maxX = r.x + r.width;
    minY = r.y;
    maxY = r.y + r.height;
    if (r.width < width || r.height < height || mask != null) {
      if (!eraseOutsideRoi(ip, r, mask)) return false;
    }
    int offset;
    double value;
    int inc = Math.max(r.height / 25, 1);
    int mi = 0;
    ImageWindow win = imp.getWindow();
    if (win != null) win.running = true;
    if (measurements == 0) measurements = Analyzer.getMeasurements();
    if (showChoice == ELLIPSES) measurements |= ELLIPSE;
    measurements &= ~LIMIT; // ignore "Limit to Threshold"
    roiNeedsImage =
        (measurements & PERIMETER) != 0
            || (measurements & SHAPE_DESCRIPTORS) != 0
            || (measurements & FERET) != 0;
    particleCount = 0;
    wand = new Wand(ip);
    pf = new PolygonFiller();
    if (floodFill) {
      ImageProcessor ipf = ip.duplicate();
      ipf.setValue(fillColor);
      ff = new FloodFiller(ipf);
    }
    roiType = Wand.allPoints() ? Roi.FREEROI : Roi.TRACED_ROI;

    for (int y = r.y; y < (r.y + r.height); y++) {
      offset = y * width;
      for (int x = r.x; x < (r.x + r.width); x++) {
        if (pixels != null) value = pixels[offset + x] & 255;
        else if (imageType == SHORT) value = ip.getPixel(x, y);
        else value = ip.getPixelValue(x, y);
        if (value >= level1 && value <= level2) analyzeParticle(x, y, imp, ip);
      }
      if (showProgress && ((y % inc) == 0)) IJ.showProgress((double) (y - r.y) / r.height);
      if (win != null) canceled = !win.running;
      if (canceled) {
        Macro.abort();
        break;
      }
    }
    if (showProgress) IJ.showProgress(1.0);
    if (showResults) rt.updateResults();
    imp.killRoi();
    ip.resetRoi();
    ip.reset();
    if (displaySummary && IJ.getInstance() != null) updateSliceSummary();
    if (addToManager && roiManager != null) roiManager.setEditMode(imp, true);
    maxParticleCount = (particleCount > maxParticleCount) ? particleCount : maxParticleCount;
    totalCount += particleCount;
    if (!canceled) showResults();
    return true;
  }
  public void runStuff(
      DimensionMap map,
      TreeMap<DimensionMap, ROIPlus> maximaMap,
      TreeMap<DimensionMap, String> segMap,
      TreeMap<DimensionMap, String> maskMap,
      TreeMap<DimensionMap, String> imageMap,
      TreeMap<DimensionMap, Double> results,
      Canceler canceler) {
    // Get the Maxima
    ROIPlus maxima = maximaMap.get(map);

    // Make the mask image impMask
    // ByteProcessor impMask = (ByteProcessor) (new
    // ImagePlus(maskMap.get(map)).getProcessor().convertToByte(false));
    // ByteProcessor impSeg = (ByteProcessor) (new
    // ImagePlus(segMap.get(map)).getProcessor().convertToByte(false));
    ByteProcessor impSeg = (ByteProcessor) (new ImagePlus(segMap.get(map))).getProcessor();
    ByteProcessor impMask = (ByteProcessor) (new ImagePlus(maskMap.get(map))).getProcessor();
    ByteBlitter blit = new ByteBlitter(impSeg);
    blit.copyBits(impMask, 0, 0, Blitter.AND);
    FloatProcessor impImage =
        (FloatProcessor) (new ImagePlus(imageMap.get(map))).getProcessor().convertToFloat();
    Wand wand = new Wand(impSeg);
    Wand wand2 = new Wand(impMask);
    Vector<Double> measurements;
    for (IdPoint p : maxima.getPointList()) {
      if (canceler.isCanceled()) {
        return;
      }
      if (impSeg.getPixel(p.x, p.y)
          == 255) // if we land on a cell that made it through thresholding
      {
        wand.autoOutline(p.x, p.y); // outline it
        wand2.autoOutline(p.x, p.y);
        boolean partOfCellClump =
            !this.selectionsAreEqual(
                wand,
                wand2); // If the segmented and unsegmented masks do not agree on the roi, then this
        // cell is part of a clump.
        if (wand.npoints > 0) {
          Roi roi =
              new PolygonRoi(
                  wand.xpoints,
                  wand.ypoints,
                  wand.npoints,
                  Roi.POLYGON); // The roi helps for using getLength() (DON'T USE Roi.TRACED_ROI.,
          // IT SCREWS UP THE Polygon OBJECTS!!!! Bug emailed to ImageJ
          // folks)
          Polygon poly =
              new Polygon(
                  wand.xpoints,
                  wand.ypoints,
                  wand.npoints); // The polygon helps for using contains()
          Rectangle r = roi.getBounds();
          measurements = new Vector<Double>();
          for (int i = r.x; i < r.x + r.width; i++) {
            for (int j = r.y; j < r.y + r.height; j++) {
              // innerBoundary
              if (poly.contains(i, j) && impSeg.getPixelValue(i, j) == 255) {
                measurements.add((double) impImage.getPixelValue(i, j));
                // Logs.log("In - " + innerT, this);
              }
            }
          }

          impMask.setRoi(roi);
          ImageStatistics stats =
              ImageStatistics.getStatistics(
                  impMask,
                  ImageStatistics.AREA
                      & ImageStatistics.PERIMETER
                      & ImageStatistics.CIRCULARITY
                      & ImageStatistics.ELLIPSE,
                  null);
          if (measurements.size() > 0) {
            DimensionMap resultsMap = map.copy();
            resultsMap.put("Id", "" + p.id);

            resultsMap.put("Measurement", "X");
            results.put(resultsMap.copy(), (double) p.x);
            resultsMap.put("Measurement", "Y");
            results.put(resultsMap.copy(), (double) p.y);
            resultsMap.put("Measurement", "AREA");
            results.put(resultsMap.copy(), stats.area);
            resultsMap.put("Measurement", "PERIMETER");
            results.put(resultsMap.copy(), roi.getLength());
            resultsMap.put("Measurement", "CIRCULARITY");
            results.put(
                resultsMap.copy(), 4.0 * Math.PI * (stats.area / (Math.pow(roi.getLength(), 2))));
            resultsMap.put("Measurement", "ELLIPSE MAJOR");
            results.put(resultsMap.copy(), stats.major);
            resultsMap.put("Measurement", "ELLIPSE MINOR");
            results.put(resultsMap.copy(), stats.minor);
            resultsMap.put("Measurement", "MEAN");
            results.put(resultsMap.copy(), StatisticsUtility.mean(measurements));
            resultsMap.put("Measurement", "MEDIAN");
            results.put(resultsMap.copy(), StatisticsUtility.median(measurements));
            resultsMap.put("Measurement", "SUM");
            results.put(resultsMap.copy(), StatisticsUtility.sum(measurements));
            resultsMap.put("Measurement", "MIN");
            results.put(resultsMap.copy(), StatisticsUtility.min(measurements));
            resultsMap.put("Measurement", "MAX");
            results.put(resultsMap.copy(), StatisticsUtility.max(measurements));
            resultsMap.put("Measurement", "STDDEV");
            results.put(resultsMap.copy(), StatisticsUtility.stdDev(measurements));
            resultsMap.put("Measurement", "VARIANCE");
            results.put(resultsMap.copy(), StatisticsUtility.variance(measurements));
            resultsMap.put("Measurement", "CLUMP");
            results.put(resultsMap.copy(), (double) (partOfCellClump ? 1 : 0));
          }
        }
      }
    }
  }