boolean setThresholdLevels(ImagePlus imp, ImageProcessor ip) {
   double t1 = ip.getMinThreshold();
   double t2 = ip.getMaxThreshold();
   boolean invertedLut = imp.isInvertedLut();
   boolean byteImage = ip instanceof ByteProcessor;
   if (ip instanceof ShortProcessor) imageType = SHORT;
   else if (ip instanceof FloatProcessor) imageType = FLOAT;
   else imageType = BYTE;
   if (t1 == ImageProcessor.NO_THRESHOLD) {
     ImageStatistics stats = imp.getStatistics();
     if (imageType != BYTE || (stats.histogram[0] + stats.histogram[255] != stats.pixelCount)) {
       IJ.error(
           "Particle Analyzer",
           "A thresholded image or 8-bit binary image is\n"
               + "required. Threshold levels can be set using\n"
               + "the Image->Adjust->Threshold tool.");
       canceled = true;
       return false;
     }
     boolean threshold255 = invertedLut;
     if (Prefs.blackBackground) threshold255 = !threshold255;
     if (threshold255) {
       level1 = 255;
       level2 = 255;
       fillColor = 64;
     } else {
       level1 = 0;
       level2 = 0;
       fillColor = 192;
     }
   } else {
     level1 = t1;
     level2 = t2;
     if (imageType == BYTE) {
       if (level1 > 0) fillColor = 0;
       else if (level2 < 255) fillColor = 255;
     } else if (imageType == SHORT) {
       if (level1 > 0) fillColor = 0;
       else if (level2 < 65535) fillColor = 65535;
     } else if (imageType == FLOAT) fillColor = -Float.MAX_VALUE;
     else return false;
   }
   imageType2 = imageType;
   if (redirectIP != null) {
     if (redirectIP instanceof ShortProcessor) imageType2 = SHORT;
     else if (redirectIP instanceof FloatProcessor) imageType2 = FLOAT;
     else if (redirectIP instanceof ColorProcessor) imageType2 = RGB;
     else imageType2 = BYTE;
   }
   return true;
 }
 void drawOutline(ImageProcessor ip, Roi roi, int count) {
   if (showChoice == OVERLAY_OUTLINES || showChoice == OVERLAY_MASKS) {
     if (overlay == null) {
       overlay = new Overlay();
       overlay.drawLabels(true);
       overlay.setLabelFont(new Font("SansSerif", Font.PLAIN, fontSize));
     }
     Roi roi2 = (Roi) roi.clone();
     roi2.setStrokeColor(Color.cyan);
     if (lineWidth != 1) roi2.setStrokeWidth(lineWidth);
     if (showChoice == OVERLAY_MASKS) roi2.setFillColor(Color.cyan);
     overlay.add(roi2);
   } else {
     Rectangle r = roi.getBounds();
     int nPoints = ((PolygonRoi) roi).getNCoordinates();
     int[] xp = ((PolygonRoi) roi).getXCoordinates();
     int[] yp = ((PolygonRoi) roi).getYCoordinates();
     int x = r.x, y = r.y;
     if (!inSituShow) ip.setValue(0.0);
     ip.moveTo(x + xp[0], y + yp[0]);
     for (int i = 1; i < nPoints; i++) ip.lineTo(x + xp[i], y + yp[i]);
     ip.lineTo(x + xp[0], y + yp[0]);
     if (showChoice != BARE_OUTLINES) {
       String s = ResultsTable.d2s(count, 0);
       ip.moveTo(r.x + r.width / 2 - ip.getStringWidth(s) / 2, r.y + r.height / 2 + fontSize / 2);
       if (!inSituShow) ip.setValue(1.0);
       ip.drawString(s);
     }
   }
 }
 private String addMean(int column, String line, int start) {
   if (start == -1) {
     line += "\tNaN";
     summaryHdr += "\t" + ResultsTable.getDefaultHeading(column);
   } else {
     float[] c = column >= 0 ? rt.getColumn(column) : null;
     if (c != null) {
       ImageProcessor ip = new FloatProcessor(c.length, 1, c, null);
       if (ip == null) return line;
       ip.setRoi(start, 0, ip.getWidth() - start, 1);
       ip = ip.crop();
       ImageStatistics stats = new FloatStatistics(ip);
       if (stats == null) return line;
       line += n(stats.mean);
     } else line += "\tNaN";
     summaryHdr += "\t" + rt.getColumnHeading(column);
   }
   return line;
 }
 public void run(ImageProcessor ip) {
   if (canceled) return;
   slice++;
   if (imp.getStackSize() > 1 && processStack) imp.setSlice(slice);
   if (imp.getType() == ImagePlus.COLOR_RGB) {
     ip = (ImageProcessor) imp.getProperty("Mask");
     ip.setThreshold(255, 255, ImageProcessor.NO_LUT_UPDATE);
   }
   if (!analyze(imp, ip)) canceled = true;
   if (slice == imp.getStackSize()) {
     imp.updateAndDraw();
     if (saveRoi != null) imp.setRoi(saveRoi);
   }
 }
 void drawRoiFilledParticle(ImageProcessor ip, Roi roi, ImageProcessor mask, int count) {
   int grayLevel = (count < 65535) ? count : 65535;
   ip.setValue((double) grayLevel);
   ip.setRoi(roi.getBounds());
   ip.fill(mask);
 }
 void drawFilledParticle(ImageProcessor ip, Roi roi, ImageProcessor mask) {
   // IJ.write(roi.getBounds()+" "+mask.length);
   ip.setRoi(roi.getBounds());
   ip.fill(mask);
 }
 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);
 }
 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;
 }
  /**
   * 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;
  }
 private boolean isThresholdedRGB(ImagePlus imp) {
   Object obj = imp.getProperty("Mask");
   if (obj == null || !(obj instanceof ImageProcessor)) return false;
   ImageProcessor mask = (ImageProcessor) obj;
   return mask.getWidth() == imp.getWidth() && mask.getHeight() == imp.getHeight();
 }