Пример #1
0
 public Point getPreferredLocation() {
   Rectangle maxBounds = GUI.getMaxWindowBounds();
   int ijX = Prefs.getInt(IJ_X, -99);
   int ijY = Prefs.getInt(IJ_Y, -99);
   if (ijX >= maxBounds.x && ijY >= maxBounds.y && ijX < (maxBounds.x + maxBounds.width - 75))
     return new Point(ijX, ijY);
   Dimension tbsize = toolbar.getPreferredSize();
   int ijWidth = tbsize.width + 10;
   double percent = maxBounds.width > 832 ? 0.8 : 0.9;
   ijX = (int) (percent * (maxBounds.width - ijWidth));
   if (ijX < 10) ijX = 10;
   return new Point(ijX, maxBounds.y);
 }
Пример #2
0
 public Point getPreferredLocation() {
   if (!IJ.isJava14()) return new Point(0, 0);
   GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
   Rectangle maxBounds = ge.getMaximumWindowBounds();
   int ijX = Prefs.getInt(IJ_X, -99);
   int ijY = Prefs.getInt(IJ_Y, -99);
   if (ijX >= 0 && ijY > 0 && ijX < (maxBounds.x + maxBounds.width - 75))
     return new Point(ijX, ijY);
   Dimension tbsize = toolbar.getPreferredSize();
   int ijWidth = tbsize.width + 10;
   double percent = maxBounds.width > 832 ? 0.8 : 0.9;
   ijX = (int) (percent * (maxBounds.width - ijWidth));
   if (ijX < 10) ijX = 10;
   return new Point(ijX, maxBounds.y);
 }
Пример #3
0
 void configureProxy() {
   if (Prefs.useSystemProxies) {
     try {
       System.setProperty("java.net.useSystemProxies", "true");
     } catch (Exception e) {
     }
   } else {
     String server = Prefs.get("proxy.server", null);
     if (server == null || server.equals("")) return;
     int port = (int) Prefs.get("proxy.port", 0);
     if (port == 0) return;
     Properties props = System.getProperties();
     props.put("proxySet", "true");
     props.put("http.proxyHost", server);
     props.put("http.proxyPort", "" + port);
   }
   // new ProxySettings().logProperties();
 }
Пример #4
0
  // Input/Output options
  void io() {
    GenericDialog gd = new GenericDialog("I/O Options");
    gd.addNumericField("JPEG quality (0-100):", FileSaver.getJpegQuality(), 0, 3, "");
    gd.addNumericField("GIF and PNG transparent index:", Prefs.getTransparentIndex(), 0, 3, "");
    gd.addStringField(
        "File extension for tables (.txt, .xls or .csv):", Prefs.get("options.ext", ".csv"), 4);
    gd.addCheckbox("Use JFileChooser to open/save", Prefs.useJFileChooser);
    if (!IJ.isMacOSX())
      gd.addCheckbox("Use_file chooser to import sequences", Prefs.useFileChooser);
    gd.addCheckbox("Save TIFF and raw in Intel byte order", Prefs.intelByteOrder);
    gd.addCheckbox("Skip dialog when opening .raw files", Prefs.skipRawDialog);

    gd.setInsets(15, 20, 0);
    gd.addMessage("Results Table Options");
    gd.setInsets(3, 40, 0);
    gd.addCheckbox("Copy_column headers", Prefs.copyColumnHeaders);
    gd.setInsets(0, 40, 0);
    gd.addCheckbox("Copy_row numbers", !Prefs.noRowNumbers);
    gd.setInsets(0, 40, 0);
    gd.addCheckbox("Save_column headers", !Prefs.dontSaveHeaders);
    gd.setInsets(0, 40, 0);
    gd.addCheckbox("Save_row numbers", !Prefs.dontSaveRowNumbers);

    gd.showDialog();
    if (gd.wasCanceled()) return;
    int quality = (int) gd.getNextNumber();
    if (quality < 0) quality = 0;
    if (quality > 100) quality = 100;
    FileSaver.setJpegQuality(quality);
    int transparentIndex = (int) gd.getNextNumber();
    Prefs.setTransparentIndex(transparentIndex);
    String extension = gd.getNextString();
    if (!extension.startsWith(".")) extension = "." + extension;
    Prefs.set("options.ext", extension);
    Prefs.useJFileChooser = gd.getNextBoolean();
    if (!IJ.isMacOSX()) Prefs.useFileChooser = gd.getNextBoolean();
    Prefs.intelByteOrder = gd.getNextBoolean();
    Prefs.skipRawDialog = gd.getNextBoolean();
    Prefs.copyColumnHeaders = gd.getNextBoolean();
    Prefs.noRowNumbers = !gd.getNextBoolean();
    Prefs.dontSaveHeaders = !gd.getNextBoolean();
    Prefs.dontSaveRowNumbers = !gd.getNextBoolean();
    return;
  }
Пример #5
0
  private boolean showDialog() {
    String[] types = {"RAW", "JPEG", "ZLIB"};
    GenericDialog gd = new GenericDialog("Generate Bricks");
    gd.addChoice("FileType", types, filetype);
    gd.addNumericField("JPEG quality", jpeg_quality, 0);
    gd.addNumericField("Max file size (MB)", bdsizelimit, 0);

    int[] wlist = WindowManager.getIDList();
    if (wlist == null) return false;

    String[] titles = new String[wlist.length];
    for (int i = 0; i < wlist.length; i++) titles[i] = "";

    int tnum = 0;
    for (int i = 0; i < wlist.length; i++) {
      ImagePlus imp = WindowManager.getImage(wlist[i]);
      if (imp != null) {
        titles[tnum] = imp.getTitle();
        tnum++;
      }
    }
    gd.addChoice("Source image: ", titles, titles[0]);

    gd.showDialog();
    if (gd.wasCanceled()) return false;

    filetype = types[gd.getNextChoiceIndex()];
    jpeg_quality = (int) gd.getNextNumber();
    if (jpeg_quality > 100) jpeg_quality = 100;
    if (jpeg_quality < 0) jpeg_quality = 0;
    bdsizelimit = (int) gd.getNextNumber();

    int id = gd.getNextChoiceIndex();
    lvImgTitle = new ArrayList<String>();
    lvImgTitle.add(titles[id]);

    Prefs.set("filetype.string", filetype);
    Prefs.set("jpeg_quality.int", jpeg_quality);
    Prefs.set("bdsizelimit.int", bdsizelimit);

    return true;
  }
Пример #6
0
 /** Quit using a separate thread, hopefully avoiding thread deadlocks. */
 public void run() {
   quitting = true;
   boolean changes = false;
   int[] wList = WindowManager.getIDList();
   if (wList != null) {
     for (int i = 0; i < wList.length; i++) {
       ImagePlus imp = WindowManager.getImage(wList[i]);
       if (imp != null && imp.changes == true) {
         changes = true;
         break;
       }
     }
   }
   Frame[] frames = WindowManager.getNonImageWindows();
   if (frames != null) {
     for (int i = 0; i < frames.length; i++) {
       if (frames[i] != null && (frames[i] instanceof Editor)) {
         if (((Editor) frames[i]).fileChanged()) {
           changes = true;
           break;
         }
       }
     }
   }
   if (windowClosed
       && !changes
       && Menus.window.getItemCount() > Menus.WINDOW_MENU_ITEMS
       && !(IJ.macroRunning() && WindowManager.getImageCount() == 0)) {
     GenericDialog gd = new GenericDialog("ImageJ", this);
     gd.addMessage("Are you sure you want to quit ImageJ?");
     gd.showDialog();
     quitting = !gd.wasCanceled();
     windowClosed = false;
   }
   if (!quitting) return;
   if (!WindowManager.closeAllWindows()) {
     quitting = false;
     return;
   }
   // IJ.log("savePreferences");
   if (applet == null) {
     saveWindowLocations();
     Prefs.savePreferences();
   }
   IJ.cleanup();
   // setVisible(false);
   // IJ.log("dispose");
   dispose();
   if (exitWhenQuitting) System.exit(0);
 }
Пример #7
0
 private void loadCursors() {
   Toolkit toolkit = Toolkit.getDefaultToolkit();
   String path = Prefs.getImageJDir() + "images/crosshair-cursor.gif";
   File f = new File(path);
   if (!f.exists()) return;
   // Image image = toolkit.getImage(path);
   ImageIcon icon = new ImageIcon(path);
   Image image = icon.getImage();
   if (image == null) return;
   int width = icon.getIconWidth();
   int height = icon.getIconHeight();
   Point hotSpot = new Point(width / 2, height / 2);
   Cursor crosshairCursor = toolkit.createCustomCursor(image, hotSpot, "crosshair-cursor.gif");
   ImageCanvas.setCursor(crosshairCursor, 0);
 }
Пример #8
0
 void saveWindowLocations() {
   Window win = WindowManager.getWindow("B&C");
   if (win != null) Prefs.saveLocation(ContrastAdjuster.LOC_KEY, win.getLocation());
   win = WindowManager.getWindow("Threshold");
   if (win != null) Prefs.saveLocation(ThresholdAdjuster.LOC_KEY, win.getLocation());
   win = WindowManager.getWindow("Results");
   if (win != null) {
     Prefs.saveLocation(TextWindow.LOC_KEY, win.getLocation());
     Dimension d = win.getSize();
     Prefs.set(TextWindow.WIDTH_KEY, d.width);
     Prefs.set(TextWindow.HEIGHT_KEY, d.height);
   }
   win = WindowManager.getWindow("Log");
   if (win != null) {
     Prefs.saveLocation(TextWindow.LOG_LOC_KEY, win.getLocation());
     Dimension d = win.getSize();
     Prefs.set(TextWindow.LOG_WIDTH_KEY, d.width);
     Prefs.set(TextWindow.LOG_HEIGHT_KEY, d.height);
   }
   win = WindowManager.getWindow("ROI Manager");
   if (win != null) Prefs.saveLocation(RoiManager.LOC_KEY, win.getLocation());
 }
Пример #9
0
 void saveWindowLocations() {
   Frame frame = WindowManager.getFrame("B&C");
   if (frame != null) Prefs.saveLocation(ContrastAdjuster.LOC_KEY, frame.getLocation());
   frame = WindowManager.getFrame("Threshold");
   if (frame != null) Prefs.saveLocation(ThresholdAdjuster.LOC_KEY, frame.getLocation());
   frame = WindowManager.getFrame("Results");
   if (frame != null) {
     Prefs.saveLocation(TextWindow.LOC_KEY, frame.getLocation());
     Dimension d = frame.getSize();
     Prefs.set(TextWindow.WIDTH_KEY, d.width);
     Prefs.set(TextWindow.HEIGHT_KEY, d.height);
   }
   frame = WindowManager.getFrame("Log");
   if (frame != null) {
     Prefs.saveLocation(TextWindow.LOG_LOC_KEY, frame.getLocation());
     Dimension d = frame.getSize();
     Prefs.set(TextWindow.LOG_WIDTH_KEY, d.width);
     Prefs.set(TextWindow.LOG_HEIGHT_KEY, d.height);
   }
 }
Пример #10
0
/**
 * This plugin performs a z-projection of the input stack. Type of output image is same as type of
 * input image.
 *
 * @author Patrick Kelly <*****@*****.**>
 */
public class ZProjector implements PlugIn {
  public static final int AVG_METHOD = 0;
  public static final int MAX_METHOD = 1;
  public static final int MIN_METHOD = 2;
  public static final int SUM_METHOD = 3;
  public static final int SD_METHOD = 4;
  public static final int MEDIAN_METHOD = 5;
  public static final String[] METHODS = {
    "Average Intensity",
    "Max Intensity",
    "Min Intensity",
    "Sum Slices",
    "Standard Deviation",
    "Median"
  };
  private static final String METHOD_KEY = "zproject.method";
  private int method = (int) Prefs.get(METHOD_KEY, AVG_METHOD);

  private static final int BYTE_TYPE = 0;
  private static final int SHORT_TYPE = 1;
  private static final int FLOAT_TYPE = 2;

  public static final String lutMessage =
      "Stacks with inverter LUTs may not project correctly.\n"
          + "To create a standard LUT, invert the stack (Edit/Invert)\n"
          + "and invert the LUT (Image/Lookup Tables/Invert LUT).";

  /** Image to hold z-projection. */
  private ImagePlus projImage = null;

  /** Image stack to project. */
  private ImagePlus imp = null;

  /** Projection starts from this slice. */
  private int startSlice = 1;
  /** Projection ends at this slice. */
  private int stopSlice = 1;
  /** Project all time points? */
  private boolean allTimeFrames = true;

  private String color = "";
  private boolean isHyperstack;
  private int increment = 1;
  private int sliceCount;

  public ZProjector() {}

  /** Construction of ZProjector with image to be projected. */
  public ZProjector(ImagePlus imp) {
    setImage(imp);
  }

  /**
   * Explicitly set image to be projected. This is useful if ZProjection_ object is to be used not
   * as a plugin but as a stand alone processing object.
   */
  public void setImage(ImagePlus imp) {
    this.imp = imp;
    startSlice = 1;
    stopSlice = imp.getStackSize();
  }

  public void setStartSlice(int slice) {
    if (imp == null || slice < 1 || slice > imp.getStackSize()) return;
    startSlice = slice;
  }

  public void setStopSlice(int slice) {
    if (imp == null || slice < 1 || slice > imp.getStackSize()) return;
    stopSlice = slice;
  }

  public void setMethod(int projMethod) {
    method = projMethod;
  }

  /** Retrieve results of most recent projection operation. */
  public ImagePlus getProjection() {
    return projImage;
  }

  public void run(String arg) {
    imp = IJ.getImage();
    int stackSize = imp.getStackSize();
    if (imp == null) {
      IJ.noImage();
      return;
    }

    //  Make sure input image is a stack.
    if (stackSize == 1) {
      IJ.error("Z Project", "Stack required");
      return;
    }

    //  Check for inverting LUT.
    if (imp.getProcessor().isInvertedLut()) {
      if (!IJ.showMessageWithCancel("ZProjection", lutMessage)) return;
    }

    // Set default bounds.
    int channels = imp.getNChannels();
    int frames = imp.getNFrames();
    int slices = imp.getNSlices();
    isHyperstack =
        imp.isHyperStack()
            || (ij.macro.Interpreter.isBatchMode()
                && ((frames > 1 && frames < stackSize) || (slices > 1 && slices < stackSize)));
    boolean simpleComposite = channels == stackSize;
    if (simpleComposite) isHyperstack = false;
    startSlice = 1;
    if (isHyperstack) {
      int nSlices = imp.getNSlices();
      if (nSlices > 1) stopSlice = nSlices;
      else stopSlice = imp.getNFrames();
    } else stopSlice = stackSize;

    // Build control dialog
    GenericDialog gd = buildControlDialog(startSlice, stopSlice);
    gd.showDialog();
    if (gd.wasCanceled()) return;

    if (!imp.lock()) return; // exit if in use
    long tstart = System.currentTimeMillis();
    setStartSlice((int) gd.getNextNumber());
    setStopSlice((int) gd.getNextNumber());
    method = gd.getNextChoiceIndex();
    Prefs.set(METHOD_KEY, method);
    if (isHyperstack) {
      allTimeFrames = imp.getNFrames() > 1 && imp.getNSlices() > 1 ? gd.getNextBoolean() : false;
      doHyperStackProjection(allTimeFrames);
    } else if (imp.getType() == ImagePlus.COLOR_RGB) doRGBProjection(true);
    else doProjection(true);

    if (arg.equals("") && projImage != null) {
      long tstop = System.currentTimeMillis();
      projImage.setCalibration(imp.getCalibration());
      if (simpleComposite) IJ.run(projImage, "Grays", "");
      projImage.show("ZProjector: " + IJ.d2s((tstop - tstart) / 1000.0, 2) + " seconds");
    }

    imp.unlock();
    IJ.register(ZProjector.class);
    return;
  }

  public void doRGBProjection() {
    doRGBProjection(imp.getStack());
  }

  // Added by Marcel Boeglin 2013.09.23
  public void doRGBProjection(boolean handleOverlay) {
    doRGBProjection(imp.getStack());
    Overlay overlay = imp.getOverlay();
    if (handleOverlay && overlay != null) projImage.setOverlay(projectRGBHyperStackRois(overlay));
  }

  private void doRGBProjection(ImageStack stack) {
    ImageStack[] channels = ChannelSplitter.splitRGB(stack, true);
    ImagePlus red = new ImagePlus("Red", channels[0]);
    ImagePlus green = new ImagePlus("Green", channels[1]);
    ImagePlus blue = new ImagePlus("Blue", channels[2]);
    imp.unlock();
    ImagePlus saveImp = imp;
    imp = red;
    color = "(red)";
    doProjection();
    ImagePlus red2 = projImage;
    imp = green;
    color = "(green)";
    doProjection();
    ImagePlus green2 = projImage;
    imp = blue;
    color = "(blue)";
    doProjection();
    ImagePlus blue2 = projImage;
    int w = red2.getWidth(), h = red2.getHeight(), d = red2.getStackSize();
    if (method == SD_METHOD) {
      ImageProcessor r = red2.getProcessor();
      ImageProcessor g = green2.getProcessor();
      ImageProcessor b = blue2.getProcessor();
      double max = 0;
      double rmax = r.getStatistics().max;
      if (rmax > max) max = rmax;
      double gmax = g.getStatistics().max;
      if (gmax > max) max = gmax;
      double bmax = b.getStatistics().max;
      if (bmax > max) max = bmax;
      double scale = 255 / max;
      r.multiply(scale);
      g.multiply(scale);
      b.multiply(scale);
      red2.setProcessor(r.convertToByte(false));
      green2.setProcessor(g.convertToByte(false));
      blue2.setProcessor(b.convertToByte(false));
    }
    RGBStackMerge merge = new RGBStackMerge();
    ImageStack stack2 =
        merge.mergeStacks(w, h, d, red2.getStack(), green2.getStack(), blue2.getStack(), true);
    imp = saveImp;
    projImage = new ImagePlus(makeTitle(), stack2);
  }

  /**
   * Builds dialog to query users for projection parameters.
   *
   * @param start starting slice to display
   * @param stop last slice
   */
  protected GenericDialog buildControlDialog(int start, int stop) {
    GenericDialog gd = new GenericDialog("ZProjection", IJ.getInstance());
    gd.addNumericField("Start slice:", startSlice, 0 /*digits*/);
    gd.addNumericField("Stop slice:", stopSlice, 0 /*digits*/);
    gd.addChoice("Projection type", METHODS, METHODS[method]);
    if (isHyperstack && imp.getNFrames() > 1 && imp.getNSlices() > 1)
      gd.addCheckbox("All time frames", allTimeFrames);
    return gd;
  }

  /** Performs actual projection using specified method. */
  public void doProjection() {
    if (imp == null) return;
    sliceCount = 0;
    if (method < AVG_METHOD || method > MEDIAN_METHOD) method = AVG_METHOD;
    for (int slice = startSlice; slice <= stopSlice; slice += increment) sliceCount++;
    if (method == MEDIAN_METHOD) {
      projImage = doMedianProjection();
      return;
    }

    // Create new float processor for projected pixels.
    FloatProcessor fp = new FloatProcessor(imp.getWidth(), imp.getHeight());
    ImageStack stack = imp.getStack();
    RayFunction rayFunc = getRayFunction(method, fp);
    if (IJ.debugMode == true) {
      IJ.log("\nProjecting stack from: " + startSlice + " to: " + stopSlice);
    }

    // Determine type of input image. Explicit determination of
    // processor type is required for subsequent pixel
    // manipulation.  This approach is more efficient than the
    // more general use of ImageProcessor's getPixelValue and
    // putPixel methods.
    int ptype;
    if (stack.getProcessor(1) instanceof ByteProcessor) ptype = BYTE_TYPE;
    else if (stack.getProcessor(1) instanceof ShortProcessor) ptype = SHORT_TYPE;
    else if (stack.getProcessor(1) instanceof FloatProcessor) ptype = FLOAT_TYPE;
    else {
      IJ.error("Z Project", "Non-RGB stack required");
      return;
    }

    // Do the projection.
    for (int n = startSlice; n <= stopSlice; n += increment) {
      IJ.showStatus("ZProjection " + color + ": " + n + "/" + stopSlice);
      IJ.showProgress(n - startSlice, stopSlice - startSlice);
      projectSlice(stack.getPixels(n), rayFunc, ptype);
    }

    // Finish up projection.
    if (method == SUM_METHOD) {
      fp.resetMinAndMax();
      projImage = new ImagePlus(makeTitle(), fp);
    } else if (method == SD_METHOD) {
      rayFunc.postProcess();
      fp.resetMinAndMax();
      projImage = new ImagePlus(makeTitle(), fp);
    } else {
      rayFunc.postProcess();
      projImage = makeOutputImage(imp, fp, ptype);
    }

    if (projImage == null) IJ.error("Z Project", "Error computing projection.");
  }

  // Added by Marcel Boeglin 2013.09.23
  /**
   * Performs actual projection using specified method. If handleOverlay, adds stack overlay
   * elements from startSlice to stopSlice to projection
   */
  public void doProjection(boolean handleOverlay) {
    doProjection();
    Overlay overlay = imp.getOverlay();
    if (handleOverlay && overlay != null) projImage.setOverlay(projectStackRois(overlay));
  }

  // Added by Marcel Boeglin 2013.09.23
  private Overlay projectStackRois(Overlay overlay) {
    if (overlay == null) return null;
    Overlay overlay2 = new Overlay();
    Roi roi;
    int s;
    for (Roi r : overlay.toArray()) {
      s = r.getPosition();
      roi = (Roi) r.clone();
      if (s >= startSlice && s <= stopSlice || s == 0) {
        roi.setPosition(s);
        overlay2.add(roi);
      }
    }
    return overlay2;
  }

  public void doHyperStackProjection(boolean allTimeFrames) {
    int start = startSlice;
    int stop = stopSlice;
    int firstFrame = 1;
    int lastFrame = imp.getNFrames();
    if (!allTimeFrames) firstFrame = lastFrame = imp.getFrame();
    ImageStack stack = new ImageStack(imp.getWidth(), imp.getHeight());
    int channels = imp.getNChannels();
    int slices = imp.getNSlices();
    if (slices == 1) {
      slices = imp.getNFrames();
      firstFrame = lastFrame = 1;
    }
    int frames = lastFrame - firstFrame + 1;
    increment = channels;
    boolean rgb = imp.getBitDepth() == 24;
    for (int frame = firstFrame; frame <= lastFrame; frame++) {
      for (int channel = 1; channel <= channels; channel++) {
        startSlice = (frame - 1) * channels * slices + (start - 1) * channels + channel;
        stopSlice = (frame - 1) * channels * slices + (stop - 1) * channels + channel;
        if (rgb) doHSRGBProjection(imp);
        else doProjection();
        stack.addSlice(null, projImage.getProcessor());
      }
    }
    projImage = new ImagePlus(makeTitle(), stack);
    projImage.setDimensions(channels, 1, frames);
    if (channels > 1) {
      projImage = new CompositeImage(projImage, 0);
      ((CompositeImage) projImage).copyLuts(imp);
      if (method == SUM_METHOD || method == SD_METHOD)
        ((CompositeImage) projImage).resetDisplayRanges();
    }
    if (frames > 1) projImage.setOpenAsHyperStack(true);
    Overlay overlay = imp.getOverlay();
    if (overlay != null) {
      startSlice = start;
      stopSlice = stop;
      if (imp.getType() == ImagePlus.COLOR_RGB)
        projImage.setOverlay(projectRGBHyperStackRois(overlay));
      else projImage.setOverlay(projectHyperStackRois(overlay));
    }
    IJ.showProgress(1, 1);
  }

  // Added by Marcel Boeglin 2013.09.22
  private Overlay projectRGBHyperStackRois(Overlay overlay) {
    if (overlay == null) return null;
    int frames = projImage.getNFrames();
    int t1 = imp.getFrame();
    Overlay overlay2 = new Overlay();
    Roi roi;
    int c, z, t;
    for (Roi r : overlay.toArray()) {
      c = r.getCPosition();
      z = r.getZPosition();
      t = r.getTPosition();
      roi = (Roi) r.clone();
      if (z >= startSlice && z <= stopSlice || z == 0 || c == 0 || t == 0) {
        if (frames == 1 && t != t1 && t != 0) // current time frame
        continue;
        roi.setPosition(t);
        overlay2.add(roi);
      }
    }
    return overlay2;
  }

  // Added by Marcel Boeglin 2013.09.22
  private Overlay projectHyperStackRois(Overlay overlay) {
    if (overlay == null) return null;
    int t1 = imp.getFrame();
    int channels = projImage.getNChannels();
    int slices = 1;
    int frames = projImage.getNFrames();
    Overlay overlay2 = new Overlay();
    Roi roi;
    int c, z, t;
    int size = channels * slices * frames;
    for (Roi r : overlay.toArray()) {
      c = r.getCPosition();
      z = r.getZPosition();
      t = r.getTPosition();
      roi = (Roi) r.clone();
      if (size == channels) { // current time frame
        if (z >= startSlice && z <= stopSlice && t == t1 || c == 0) {
          roi.setPosition(c);
          overlay2.add(roi);
        }
      } else if (size == frames * channels) { // all time frames
        if (z >= startSlice && z <= stopSlice) roi.setPosition(c, 1, t);
        else if (z == 0) roi.setPosition(c, 0, t);
        else continue;
        overlay2.add(roi);
      }
    }
    return overlay2;
  }

  private void doHSRGBProjection(ImagePlus rgbImp) {
    ImageStack stack = rgbImp.getStack();
    ImageStack stack2 = new ImageStack(stack.getWidth(), stack.getHeight());
    for (int i = startSlice; i <= stopSlice; i++) stack2.addSlice(null, stack.getProcessor(i));
    startSlice = 1;
    stopSlice = stack2.getSize();
    doRGBProjection(stack2);
  }

  private RayFunction getRayFunction(int method, FloatProcessor fp) {
    switch (method) {
      case AVG_METHOD:
      case SUM_METHOD:
        return new AverageIntensity(fp, sliceCount);
      case MAX_METHOD:
        return new MaxIntensity(fp);
      case MIN_METHOD:
        return new MinIntensity(fp);
      case SD_METHOD:
        return new StandardDeviation(fp, sliceCount);
      default:
        IJ.error("Z Project", "Unknown method.");
        return null;
    }
  }

  /** 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);
  }

  /**
   * Handles mechanics of projection by selecting appropriate pixel array type. We do this rather
   * than using more general ImageProcessor getPixelValue() and putPixel() methods because direct
   * manipulation of pixel arrays is much more efficient.
   */
  private void projectSlice(Object pixelArray, RayFunction rayFunc, int ptype) {
    switch (ptype) {
      case BYTE_TYPE:
        rayFunc.projectSlice((byte[]) pixelArray);
        break;
      case SHORT_TYPE:
        rayFunc.projectSlice((short[]) pixelArray);
        break;
      case FLOAT_TYPE:
        rayFunc.projectSlice((float[]) pixelArray);
        break;
    }
  }

  String makeTitle() {
    String prefix = "AVG_";
    switch (method) {
      case SUM_METHOD:
        prefix = "SUM_";
        break;
      case MAX_METHOD:
        prefix = "MAX_";
        break;
      case MIN_METHOD:
        prefix = "MIN_";
        break;
      case SD_METHOD:
        prefix = "STD_";
        break;
      case MEDIAN_METHOD:
        prefix = "MED_";
        break;
    }
    return WindowManager.makeUniqueName(prefix + imp.getTitle());
  }

  ImagePlus doMedianProjection() {
    IJ.showStatus("Calculating median...");
    ImageStack stack = imp.getStack();
    ImageProcessor[] slices = new ImageProcessor[sliceCount];
    int index = 0;
    for (int slice = startSlice; slice <= stopSlice; slice += increment)
      slices[index++] = stack.getProcessor(slice);
    ImageProcessor ip2 = slices[0].duplicate();
    ip2 = ip2.convertToFloat();
    float[] values = new float[sliceCount];
    int width = ip2.getWidth();
    int height = ip2.getHeight();
    int inc = Math.max(height / 30, 1);
    for (int y = 0; y < height; y++) {
      if (y % inc == 0) IJ.showProgress(y, height - 1);
      for (int x = 0; x < width; x++) {
        for (int i = 0; i < sliceCount; i++) values[i] = slices[i].getPixelValue(x, y);
        ip2.putPixelValue(x, y, median(values));
      }
    }
    if (imp.getBitDepth() == 8) ip2 = ip2.convertToByte(false);
    IJ.showProgress(1, 1);
    return new ImagePlus(makeTitle(), ip2);
  }

  float median(float[] a) {
    Arrays.sort(a);
    int middle = a.length / 2;
    if ((a.length & 1) == 0) // even
    return (a[middle - 1] + a[middle]) / 2f;
    else return a[middle];
  }

  /**
   * Abstract class that specifies structure of ray function. Preprocessing should be done in
   * derived class constructors.
   */
  abstract class RayFunction {
    /** Do actual slice projection for specific data types. */
    public abstract void projectSlice(byte[] pixels);

    public abstract void projectSlice(short[] pixels);

    public abstract void projectSlice(float[] pixels);

    /** Perform any necessary post processing operations, e.g. averging values. */
    public void postProcess() {}
  } // end RayFunction

  /** Compute average intensity projection. */
  class AverageIntensity extends RayFunction {
    private float[] fpixels;
    private int num, len;

    /**
     * Constructor requires number of slices to be projected. This is used to determine average at
     * each pixel.
     */
    public AverageIntensity(FloatProcessor fp, int num) {
      fpixels = (float[]) fp.getPixels();
      len = fpixels.length;
      this.num = num;
    }

    public void projectSlice(byte[] pixels) {
      for (int i = 0; i < len; i++) fpixels[i] += (pixels[i] & 0xff);
    }

    public void projectSlice(short[] pixels) {
      for (int i = 0; i < len; i++) fpixels[i] += pixels[i] & 0xffff;
    }

    public void projectSlice(float[] pixels) {
      for (int i = 0; i < len; i++) fpixels[i] += pixels[i];
    }

    public void postProcess() {
      float fnum = num;
      for (int i = 0; i < len; i++) fpixels[i] /= fnum;
    }
  } // end AverageIntensity

  /** Compute max intensity projection. */
  class MaxIntensity extends RayFunction {
    private float[] fpixels;
    private int len;

    /** Simple constructor since no preprocessing is necessary. */
    public MaxIntensity(FloatProcessor fp) {
      fpixels = (float[]) fp.getPixels();
      len = fpixels.length;
      for (int i = 0; i < len; i++) fpixels[i] = -Float.MAX_VALUE;
    }

    public void projectSlice(byte[] pixels) {
      for (int i = 0; i < len; i++) {
        if ((pixels[i] & 0xff) > fpixels[i]) fpixels[i] = (pixels[i] & 0xff);
      }
    }

    public void projectSlice(short[] pixels) {
      for (int i = 0; i < len; i++) {
        if ((pixels[i] & 0xffff) > fpixels[i]) fpixels[i] = pixels[i] & 0xffff;
      }
    }

    public void projectSlice(float[] pixels) {
      for (int i = 0; i < len; i++) {
        if (pixels[i] > fpixels[i]) fpixels[i] = pixels[i];
      }
    }
  } // end MaxIntensity

  /** Compute min intensity projection. */
  class MinIntensity extends RayFunction {
    private float[] fpixels;
    private int len;

    /** Simple constructor since no preprocessing is necessary. */
    public MinIntensity(FloatProcessor fp) {
      fpixels = (float[]) fp.getPixels();
      len = fpixels.length;
      for (int i = 0; i < len; i++) fpixels[i] = Float.MAX_VALUE;
    }

    public void projectSlice(byte[] pixels) {
      for (int i = 0; i < len; i++) {
        if ((pixels[i] & 0xff) < fpixels[i]) fpixels[i] = (pixels[i] & 0xff);
      }
    }

    public void projectSlice(short[] pixels) {
      for (int i = 0; i < len; i++) {
        if ((pixels[i] & 0xffff) < fpixels[i]) fpixels[i] = pixels[i] & 0xffff;
      }
    }

    public void projectSlice(float[] pixels) {
      for (int i = 0; i < len; i++) {
        if (pixels[i] < fpixels[i]) fpixels[i] = pixels[i];
      }
    }
  } // end MaxIntensity

  /** Compute standard deviation projection. */
  class StandardDeviation extends RayFunction {
    private float[] result;
    private double[] sum, sum2;
    private int num, len;

    public StandardDeviation(FloatProcessor fp, int num) {
      result = (float[]) fp.getPixels();
      len = result.length;
      this.num = num;
      sum = new double[len];
      sum2 = new double[len];
    }

    public void projectSlice(byte[] pixels) {
      int v;
      for (int i = 0; i < len; i++) {
        v = pixels[i] & 0xff;
        sum[i] += v;
        sum2[i] += v * v;
      }
    }

    public void projectSlice(short[] pixels) {
      double v;
      for (int i = 0; i < len; i++) {
        v = pixels[i] & 0xffff;
        sum[i] += v;
        sum2[i] += v * v;
      }
    }

    public void projectSlice(float[] pixels) {
      double v;
      for (int i = 0; i < len; i++) {
        v = pixels[i];
        sum[i] += v;
        sum2[i] += v * v;
      }
    }

    public void postProcess() {
      double stdDev;
      double n = num;
      for (int i = 0; i < len; i++) {
        if (num > 1) {
          stdDev = (n * sum2[i] - sum[i] * sum[i]) / n;
          if (stdDev > 0.0) result[i] = (float) Math.sqrt(stdDev / (n - 1.0));
          else result[i] = 0f;
        } else result[i] = 0f;
      }
    }
  } // end StandardDeviation
} // end ZProjection
Пример #11
0
/** This is a Canvas used to display images in a Window. */
public class ImageCanvas extends Canvas implements MouseListener, MouseMotionListener, Cloneable {

  protected static Cursor defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
  protected static Cursor handCursor = new Cursor(Cursor.HAND_CURSOR);
  protected static Cursor moveCursor = new Cursor(Cursor.MOVE_CURSOR);
  protected static Cursor crosshairCursor = new Cursor(Cursor.CROSSHAIR_CURSOR);

  public static boolean usePointer = Prefs.usePointerCursor;

  protected ImagePlus imp;
  protected boolean imageUpdated;
  protected Rectangle srcRect;
  protected int imageWidth, imageHeight;
  protected int xMouse; // current cursor offscreen x location
  protected int yMouse; // current cursor offscreen y location

  private boolean showCursorStatus = true;
  private int sx2, sy2;
  private boolean disablePopupMenu;
  private boolean showAllROIs;
  private static Color zoomIndicatorColor;
  private static Font smallFont, largeFont;
  private Font font;
  private Rectangle[] labelRects;
  private boolean maxBoundsReset;
  private Overlay overlay, showAllList;
  private static final int LIST_OFFSET = 100000;
  private static Color showAllColor = Prefs.getColor(Prefs.SHOW_ALL_COLOR, new Color(0, 255, 255));
  private Color defaultColor = showAllColor;
  private static Color labelColor, bgColor;
  private int resetMaxBoundsCount;
  private Roi currentRoi;

  protected ImageJ ij;
  protected double magnification;
  protected int dstWidth, dstHeight;

  protected int xMouseStart;
  protected int yMouseStart;
  protected int xSrcStart;
  protected int ySrcStart;
  protected int flags;

  private Image offScreenImage;
  private int offScreenWidth = 0;
  private int offScreenHeight = 0;
  private boolean mouseExited = true;
  private boolean customRoi;
  private boolean drawNames;

  public ImageCanvas(ImagePlus imp) {
    this.imp = imp;
    ij = IJ.getInstance();
    int width = imp.getWidth();
    int height = imp.getHeight();
    imageWidth = width;
    imageHeight = height;
    srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
    setDrawingSize(imageWidth, (int) (imageHeight));
    magnification = 1.0;
    addMouseListener(this);
    addMouseMotionListener(this);
    addKeyListener(ij); // ImageJ handles keyboard shortcuts
    setFocusTraversalKeysEnabled(false);
  }

  void updateImage(ImagePlus imp) {
    this.imp = imp;
    int width = imp.getWidth();
    int height = imp.getHeight();
    imageWidth = width;
    imageHeight = height;
    srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
    setDrawingSize(imageWidth, (int) imageHeight);
    magnification = 1.0;
  }

  /** Update this ImageCanvas to have the same zoom and scale settings as the one specified. */
  void update(ImageCanvas ic) {
    if (ic == null || ic == this || ic.imp == null) return;
    if (ic.imp.getWidth() != imageWidth || ic.imp.getHeight() != imageHeight) return;
    srcRect = new Rectangle(ic.srcRect.x, ic.srcRect.y, ic.srcRect.width, ic.srcRect.height);
    setMagnification(ic.magnification);
    setDrawingSize(ic.dstWidth, ic.dstHeight);
  }

  public void setSourceRect(Rectangle r) {
    srcRect = r;
  }

  void setSrcRect(Rectangle srcRect) {
    this.srcRect = srcRect;
  }

  public Rectangle getSrcRect() {
    return srcRect;
  }

  public void setDrawingSize(int width, int height) {
    dstWidth = width;
    dstHeight = height;
    setSize(dstWidth, dstHeight);
  }

  /**
   * ImagePlus.updateAndDraw calls this method to force the paint() method to update the image from
   * the ImageProcessor.
   */
  public void setImageUpdated() {
    imageUpdated = true;
  }

  public void update(Graphics g) {
    paint(g);
  }

  public void paint(Graphics g) {
    Roi roi = imp.getRoi();
    if (roi != null || showAllROIs || overlay != null) {
      if (roi != null) roi.updatePaste();
      if (!IJ.isMacOSX() && imageWidth != 0) {
        paintDoubleBuffered(g);
        return;
      }
    }
    try {
      if (imageUpdated) {
        imageUpdated = false;
        imp.updateImage();
      }
      Java2.setBilinearInterpolation(g, Prefs.interpolateScaledImages);
      Image img = imp.getImage();
      if (img != null)
        g.drawImage(
            img,
            0,
            0,
            (int) (srcRect.width * magnification),
            (int) (srcRect.height * magnification),
            srcRect.x,
            srcRect.y,
            srcRect.x + srcRect.width,
            srcRect.y + srcRect.height,
            null);
      if (overlay != null) drawOverlay(g);
      if (showAllROIs) drawAllROIs(g);
      if (roi != null) drawRoi(roi, g);
      if (srcRect.width < imageWidth || srcRect.height < imageHeight) drawZoomIndicator(g);
      if (IJ.debugMode) showFrameRate(g);
    } catch (OutOfMemoryError e) {
      IJ.outOfMemory("Paint");
    }
  }

  private void drawRoi(Roi roi, Graphics g) {
    if (roi == currentRoi) {
      Color lineColor = roi.getStrokeColor();
      Color fillColor = roi.getFillColor();
      float lineWidth = roi.getStrokeWidth();
      roi.setStrokeColor(null);
      roi.setFillColor(null);
      boolean strokeSet = roi.getStroke() != null;
      if (strokeSet) roi.setStrokeWidth(1);
      roi.draw(g);
      roi.setStrokeColor(lineColor);
      if (strokeSet) roi.setStrokeWidth(lineWidth);
      roi.setFillColor(fillColor);
      currentRoi = null;
    } else roi.draw(g);
  }

  void drawAllROIs(Graphics g) {
    RoiManager rm = RoiManager.getInstance();
    if (rm == null) {
      rm = Interpreter.getBatchModeRoiManager();
      if (rm != null && rm.getList().getItemCount() == 0) rm = null;
    }
    if (rm == null) {
      // if (showAllList!=null)
      //	overlay = showAllList;
      showAllROIs = false;
      repaint();
      return;
    }
    initGraphics(g, null, showAllColor);
    Hashtable rois = rm.getROIs();
    java.awt.List list = rm.getList();
    boolean drawLabels = rm.getDrawLabels();
    currentRoi = null;
    int n = list.getItemCount();
    if (IJ.debugMode) IJ.log("paint: drawing " + n + " \"Show All\" ROIs");
    if (labelRects == null || labelRects.length != n) labelRects = new Rectangle[n];
    if (!drawLabels) showAllList = new Overlay();
    else showAllList = null;
    if (imp == null) return;
    int currentImage = imp.getCurrentSlice();
    int channel = 0, slice = 0, frame = 0;
    boolean hyperstack = imp.isHyperStack();
    if (hyperstack) {
      channel = imp.getChannel();
      slice = imp.getSlice();
      frame = imp.getFrame();
    }
    drawNames = Prefs.useNamesAsLabels;
    for (int i = 0; i < n; i++) {
      String label = list.getItem(i);
      Roi roi = (Roi) rois.get(label);
      if (roi == null) continue;
      if (showAllList != null) showAllList.add(roi);
      if (i < 200 && drawLabels && roi == imp.getRoi()) currentRoi = roi;
      if (Prefs.showAllSliceOnly && imp.getStackSize() > 1) {
        if (hyperstack && roi.getPosition() == 0) {
          int c = roi.getCPosition();
          int z = roi.getZPosition();
          int t = roi.getTPosition();
          if ((c == 0 || c == channel) && (z == 0 || z == slice) && (t == 0 || t == frame))
            drawRoi(g, roi, drawLabels ? i : -1);
        } else {
          int position = roi.getPosition();
          if (position == 0) position = getSliceNumber(roi.getName());
          if (position == 0 || position == currentImage) drawRoi(g, roi, drawLabels ? i : -1);
        }
      } else drawRoi(g, roi, drawLabels ? i : -1);
    }
    ((Graphics2D) g).setStroke(Roi.onePixelWide);
    drawNames = false;
  }

  public int getSliceNumber(String label) {
    int slice = 0;
    if (label.length() >= 14 && label.charAt(4) == '-' && label.charAt(9) == '-')
      slice = (int) Tools.parseDouble(label.substring(0, 4), -1);
    else if (label.length() >= 17 && label.charAt(5) == '-' && label.charAt(11) == '-')
      slice = (int) Tools.parseDouble(label.substring(0, 5), -1);
    else if (label.length() >= 20 && label.charAt(6) == '-' && label.charAt(13) == '-')
      slice = (int) Tools.parseDouble(label.substring(0, 6), -1);
    return slice;
  }

  void drawOverlay(Graphics g) {
    if (imp != null && imp.getHideOverlay()) return;
    Color labelColor = overlay.getLabelColor();
    if (labelColor == null) labelColor = Color.white;
    initGraphics(g, labelColor, Roi.getColor());
    int n = overlay.size();
    if (IJ.debugMode) IJ.log("paint: drawing " + n + " ROI display list");
    int currentImage = imp != null ? imp.getCurrentSlice() : -1;
    if (imp.getStackSize() == 1) currentImage = -1;
    int channel = 0, slice = 0, frame = 0;
    boolean hyperstack = imp.isHyperStack();
    if (hyperstack) {
      channel = imp.getChannel();
      slice = imp.getSlice();
      frame = imp.getFrame();
    }
    drawNames = overlay.getDrawNames();
    boolean drawLabels = drawNames || overlay.getDrawLabels();
    for (int i = 0; i < n; i++) {
      if (overlay == null) break;
      Roi roi = overlay.get(i);
      if (hyperstack && roi.getPosition() == 0) {
        int c = roi.getCPosition();
        int z = roi.getZPosition();
        int t = roi.getTPosition();
        if ((c == 0 || c == channel) && (z == 0 || z == slice) && (t == 0 || t == frame))
          drawRoi(g, roi, drawLabels ? i + LIST_OFFSET : -1);
      } else {
        int position = roi.getPosition();
        if (position == 0 || position == currentImage)
          drawRoi(g, roi, drawLabels ? i + LIST_OFFSET : -1);
      }
    }
    ((Graphics2D) g).setStroke(Roi.onePixelWide);
    drawNames = false;
  }

  void initGraphics(Graphics g, Color textColor, Color defaultColor) {
    if (smallFont == null) {
      smallFont = new Font("SansSerif", Font.PLAIN, 9);
      largeFont = new Font("SansSerif", Font.PLAIN, 12);
    }
    if (textColor != null) {
      labelColor = textColor;
      if (overlay != null && overlay.getDrawBackgrounds())
        bgColor =
            new Color(
                255 - labelColor.getRed(), 255 - labelColor.getGreen(), 255 - labelColor.getBlue());
      else bgColor = null;
    } else {
      int red = defaultColor.getRed();
      int green = defaultColor.getGreen();
      int blue = defaultColor.getBlue();
      if ((red + green + blue) / 3 < 128) labelColor = Color.white;
      else labelColor = Color.black;
      bgColor = defaultColor;
    }
    this.defaultColor = defaultColor;
    g.setColor(defaultColor);
  }

  void drawRoi(Graphics g, Roi roi, int index) {
    int type = roi.getType();
    ImagePlus imp2 = roi.getImage();
    roi.setImage(imp);
    Color saveColor = roi.getStrokeColor();
    if (saveColor == null) roi.setStrokeColor(defaultColor);
    if (roi instanceof TextRoi) ((TextRoi) roi).drawText(g);
    else roi.drawOverlay(g);
    roi.setStrokeColor(saveColor);
    if (index >= 0) {
      if (roi == currentRoi) g.setColor(Roi.getColor());
      else g.setColor(defaultColor);
      drawRoiLabel(g, index, roi);
    }
    if (imp2 != null) roi.setImage(imp2);
    else roi.setImage(null);
  }

  void drawRoiLabel(Graphics g, int index, Roi roi) {
    Rectangle r = roi.getBounds();
    int x = screenX(r.x);
    int y = screenY(r.y);
    double mag = getMagnification();
    int width = (int) (r.width * mag);
    int height = (int) (r.height * mag);
    int size = width > 40 && height > 40 ? 12 : 9;
    if (font != null) {
      g.setFont(font);
      size = font.getSize();
    } else if (size == 12) g.setFont(largeFont);
    else g.setFont(smallFont);
    boolean drawingList = index >= LIST_OFFSET;
    if (drawingList) index -= LIST_OFFSET;
    String label = "" + (index + 1);
    if (drawNames && roi.getName() != null) label = roi.getName();
    FontMetrics metrics = g.getFontMetrics();
    int w = metrics.stringWidth(label);
    x = x + width / 2 - w / 2;
    y = y + height / 2 + Math.max(size / 2, 6);
    int h = metrics.getAscent() + metrics.getDescent();
    if (bgColor != null) {
      g.setColor(bgColor);
      g.fillRoundRect(x - 1, y - h + 2, w + 1, h - 3, 5, 5);
    }
    if (!drawingList && labelRects != null && index < labelRects.length)
      labelRects[index] = new Rectangle(x - 1, y - h + 2, w + 1, h);
    g.setColor(labelColor);
    g.drawString(label, x, y - 2);
    g.setColor(defaultColor);
  }

  void drawZoomIndicator(Graphics g) {
    int x1 = 10;
    int y1 = 10;
    double aspectRatio = (double) imageHeight / imageWidth;
    int w1 = 64;
    if (aspectRatio > 1.0) w1 = (int) (w1 / aspectRatio);
    int h1 = (int) (w1 * aspectRatio);
    if (w1 < 4) w1 = 4;
    if (h1 < 4) h1 = 4;
    int w2 = (int) (w1 * ((double) srcRect.width / imageWidth));
    int h2 = (int) (h1 * ((double) srcRect.height / imageHeight));
    if (w2 < 1) w2 = 1;
    if (h2 < 1) h2 = 1;
    int x2 = (int) (w1 * ((double) srcRect.x / imageWidth));
    int y2 = (int) (h1 * ((double) srcRect.y / imageHeight));
    if (zoomIndicatorColor == null) zoomIndicatorColor = new Color(128, 128, 255);
    g.setColor(zoomIndicatorColor);
    ((Graphics2D) g).setStroke(Roi.onePixelWide);
    g.drawRect(x1, y1, w1, h1);
    if (w2 * h2 <= 200 || w2 < 10 || h2 < 10) g.fillRect(x1 + x2, y1 + y2, w2, h2);
    else g.drawRect(x1 + x2, y1 + y2, w2, h2);
  }

  // Use double buffer to reduce flicker when drawing complex ROIs.
  // Author: Erik Meijering
  void paintDoubleBuffered(Graphics g) {
    final int srcRectWidthMag = (int) (srcRect.width * magnification);
    final int srcRectHeightMag = (int) (srcRect.height * magnification);
    if (offScreenImage == null
        || offScreenWidth != srcRectWidthMag
        || offScreenHeight != srcRectHeightMag) {
      offScreenImage = createImage(srcRectWidthMag, srcRectHeightMag);
      offScreenWidth = srcRectWidthMag;
      offScreenHeight = srcRectHeightMag;
    }
    Roi roi = imp.getRoi();
    try {
      if (imageUpdated) {
        imageUpdated = false;
        imp.updateImage();
      }
      Graphics offScreenGraphics = offScreenImage.getGraphics();
      Java2.setBilinearInterpolation(offScreenGraphics, Prefs.interpolateScaledImages);
      Image img = imp.getImage();
      if (img != null)
        offScreenGraphics.drawImage(
            img,
            0,
            0,
            srcRectWidthMag,
            srcRectHeightMag,
            srcRect.x,
            srcRect.y,
            srcRect.x + srcRect.width,
            srcRect.y + srcRect.height,
            null);
      if (overlay != null) drawOverlay(offScreenGraphics);
      if (showAllROIs) drawAllROIs(offScreenGraphics);
      if (roi != null) drawRoi(roi, offScreenGraphics);
      if (srcRect.width < imageWidth || srcRect.height < imageHeight)
        drawZoomIndicator(offScreenGraphics);
      if (IJ.debugMode) showFrameRate(offScreenGraphics);
      g.drawImage(offScreenImage, 0, 0, null);
    } catch (OutOfMemoryError e) {
      IJ.outOfMemory("Paint");
    }
  }

  public void resetDoubleBuffer() {
    offScreenImage = null;
  }

  long firstFrame;
  int frames, fps;

  void showFrameRate(Graphics g) {
    frames++;
    if (System.currentTimeMillis() > firstFrame + 1000) {
      firstFrame = System.currentTimeMillis();
      fps = frames;
      frames = 0;
    }
    g.setColor(Color.white);
    g.fillRect(10, 12, 50, 15);
    g.setColor(Color.black);
    g.drawString((int) (fps + 0.5) + " fps", 10, 25);
  }

  public Dimension getPreferredSize() {
    return new Dimension(dstWidth, dstHeight);
  }

  int count;

  /*
    public Graphics getGraphics() {
     	Graphics g = super.getGraphics();
  IJ.write("getGraphics: "+count++);
  if (IJ.altKeyDown())
  	throw new IllegalArgumentException("");
    	return g;
    }
    */

  /** Returns the current cursor location in image coordinates. */
  public Point getCursorLoc() {
    return new Point(xMouse, yMouse);
  }

  /** Returns 'true' if the cursor is over this image. */
  public boolean cursorOverImage() {
    return !mouseExited;
  }

  /** Returns the mouse event modifiers. */
  public int getModifiers() {
    return flags;
  }

  /** Returns the ImagePlus object that is associated with this ImageCanvas. */
  public ImagePlus getImage() {
    return imp;
  }

  /** Sets the cursor based on the current tool and cursor location. */
  public void setCursor(int sx, int sy, int ox, int oy) {
    xMouse = ox;
    yMouse = oy;
    mouseExited = false;
    Roi roi = imp.getRoi();
    ImageWindow win = imp.getWindow();
    if (win == null) return;
    if (IJ.spaceBarDown()) {
      setCursor(handCursor);
      return;
    }
    int id = Toolbar.getToolId();
    switch (Toolbar.getToolId()) {
      case Toolbar.MAGNIFIER:
        setCursor(moveCursor);
        break;
      case Toolbar.HAND:
        setCursor(handCursor);
        break;
      default: // selection tool
        if (id == Toolbar.SPARE1 || id >= Toolbar.SPARE2) {
          if (Prefs.usePointerCursor) setCursor(defaultCursor);
          else setCursor(crosshairCursor);
        } else if (roi != null && roi.getState() != roi.CONSTRUCTING && roi.isHandle(sx, sy) >= 0)
          setCursor(handCursor);
        else if (Prefs.usePointerCursor
            || (roi != null && roi.getState() != roi.CONSTRUCTING && roi.contains(ox, oy)))
          setCursor(defaultCursor);
        else setCursor(crosshairCursor);
    }
  }

  /** Converts a screen x-coordinate to an offscreen x-coordinate. */
  public int offScreenX(int sx) {
    return srcRect.x + (int) (sx / magnification);
  }

  /** Converts a screen y-coordinate to an offscreen y-coordinate. */
  public int offScreenY(int sy) {
    return srcRect.y + (int) (sy / magnification);
  }

  /** Converts a screen x-coordinate to a floating-point offscreen x-coordinate. */
  public double offScreenXD(int sx) {
    return srcRect.x + sx / magnification;
  }

  /** Converts a screen y-coordinate to a floating-point offscreen y-coordinate. */
  public double offScreenYD(int sy) {
    return srcRect.y + sy / magnification;
  }

  /** Converts an offscreen x-coordinate to a screen x-coordinate. */
  public int screenX(int ox) {
    return (int) ((ox - srcRect.x) * magnification);
  }

  /** Converts an offscreen y-coordinate to a screen y-coordinate. */
  public int screenY(int oy) {
    return (int) ((oy - srcRect.y) * magnification);
  }

  /** Converts a floating-point offscreen x-coordinate to a screen x-coordinate. */
  public int screenXD(double ox) {
    return (int) ((ox - srcRect.x) * magnification);
  }

  /** Converts a floating-point offscreen x-coordinate to a screen x-coordinate. */
  public int screenYD(double oy) {
    return (int) ((oy - srcRect.y) * magnification);
  }

  public double getMagnification() {
    return magnification;
  }

  public void setMagnification(double magnification) {
    setMagnification2(magnification);
  }

  void setMagnification2(double magnification) {
    if (magnification > 32.0) magnification = 32.0;
    if (magnification < 0.03125) magnification = 0.03125;
    this.magnification = magnification;
    imp.setTitle(imp.getTitle());
  }

  /** Enlarge the canvas if the user enlarges the window. */
  void resizeCanvas(int width, int height) {
    ImageWindow win = imp.getWindow();
    // IJ.log("resizeCanvas: "+srcRect+" "+imageWidth+"  "+imageHeight+" "+width+"  "+height+"
    // "+dstWidth+"  "+dstHeight+" "+win.maxBounds);
    if (!maxBoundsReset
        && (width > dstWidth || height > dstHeight)
        && win != null
        && win.maxBounds != null
        && width != win.maxBounds.width - 10) {
      if (resetMaxBoundsCount != 0)
        resetMaxBounds(); // Works around problem that prevented window from being larger than
                          // maximized size
      resetMaxBoundsCount++;
    }
    if (IJ.altKeyDown()) {
      fitToWindow();
      return;
    }
    if (srcRect.width < imageWidth || srcRect.height < imageHeight) {
      if (width > imageWidth * magnification) width = (int) (imageWidth * magnification);
      if (height > imageHeight * magnification) height = (int) (imageHeight * magnification);
      setDrawingSize(width, height);
      srcRect.width = (int) (dstWidth / magnification);
      srcRect.height = (int) (dstHeight / magnification);
      if ((srcRect.x + srcRect.width) > imageWidth) srcRect.x = imageWidth - srcRect.width;
      if ((srcRect.y + srcRect.height) > imageHeight) srcRect.y = imageHeight - srcRect.height;
      repaint();
    }
    // IJ.log("resizeCanvas2: "+srcRect+" "+dstWidth+"  "+dstHeight+" "+width+"  "+height);
  }

  public void fitToWindow() {
    ImageWindow win = imp.getWindow();
    if (win == null) return;
    Rectangle bounds = win.getBounds();
    Insets insets = win.getInsets();
    int sliderHeight = (win instanceof StackWindow) ? 20 : 0;
    double xmag = (double) (bounds.width - 10) / srcRect.width;
    double ymag = (double) (bounds.height - (10 + insets.top + sliderHeight)) / srcRect.height;
    setMagnification(Math.min(xmag, ymag));
    int width = (int) (imageWidth * magnification);
    int height = (int) (imageHeight * magnification);
    if (width == dstWidth && height == dstHeight) return;
    srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
    setDrawingSize(width, height);
    getParent().doLayout();
  }

  void setMaxBounds() {
    if (maxBoundsReset) {
      maxBoundsReset = false;
      ImageWindow win = imp.getWindow();
      if (win != null && !IJ.isLinux() && win.maxBounds != null) {
        win.setMaximizedBounds(win.maxBounds);
        win.setMaxBoundsTime = System.currentTimeMillis();
      }
    }
  }

  void resetMaxBounds() {
    ImageWindow win = imp.getWindow();
    if (win != null && (System.currentTimeMillis() - win.setMaxBoundsTime) > 500L) {
      win.setMaximizedBounds(win.maxWindowBounds);
      maxBoundsReset = true;
    }
  }

  private static final double[] zoomLevels = {
    1 / 72.0, 1 / 48.0, 1 / 32.0, 1 / 24.0, 1 / 16.0, 1 / 12.0, 1 / 8.0, 1 / 6.0, 1 / 4.0, 1 / 3.0,
    1 / 2.0, 0.75, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 12.0, 16.0, 24.0, 32.0
  };

  public static double getLowerZoomLevel(double currentMag) {
    double newMag = zoomLevels[0];
    for (int i = 0; i < zoomLevels.length; i++) {
      if (zoomLevels[i] < currentMag) newMag = zoomLevels[i];
      else break;
    }
    return newMag;
  }

  public static double getHigherZoomLevel(double currentMag) {
    double newMag = 32.0;
    for (int i = zoomLevels.length - 1; i >= 0; i--) {
      if (zoomLevels[i] > currentMag) newMag = zoomLevels[i];
      else break;
    }
    return newMag;
  }

  /**
   * Zooms in by making the window bigger. If it can't be made bigger, then make the source
   * rectangle (srcRect) smaller and center it at (sx,sy). Note that sx and sy are screen
   * coordinates.
   */
  public void zoomIn(int sx, int sy) {
    if (magnification >= 32) return;
    double newMag = getHigherZoomLevel(magnification);
    int newWidth = (int) (imageWidth * newMag);
    int newHeight = (int) (imageHeight * newMag);
    Dimension newSize = canEnlarge(newWidth, newHeight);
    if (newSize != null) {
      setDrawingSize(newSize.width, newSize.height);
      if (newSize.width != newWidth || newSize.height != newHeight)
        adjustSourceRect(newMag, sx, sy);
      else setMagnification(newMag);
      imp.getWindow().pack();
    } else adjustSourceRect(newMag, sx, sy);
    repaint();
    if (srcRect.width < imageWidth || srcRect.height < imageHeight) resetMaxBounds();
  }

  void adjustSourceRect(double newMag, int x, int y) {
    // IJ.log("adjustSourceRect1: "+newMag+" "+dstWidth+"  "+dstHeight);
    int w = (int) Math.round(dstWidth / newMag);
    if (w * newMag < dstWidth) w++;
    int h = (int) Math.round(dstHeight / newMag);
    if (h * newMag < dstHeight) h++;
    x = offScreenX(x);
    y = offScreenY(y);
    Rectangle r = new Rectangle(x - w / 2, y - h / 2, w, h);
    if (r.x < 0) r.x = 0;
    if (r.y < 0) r.y = 0;
    if (r.x + w > imageWidth) r.x = imageWidth - w;
    if (r.y + h > imageHeight) r.y = imageHeight - h;
    srcRect = r;
    setMagnification(newMag);
    // IJ.log("adjustSourceRect2: "+srcRect+" "+dstWidth+"  "+dstHeight);
  }

  protected Dimension canEnlarge(int newWidth, int newHeight) {
    // if ((flags&Event.CTRL_MASK)!=0 || IJ.controlKeyDown()) return null;
    ImageWindow win = imp.getWindow();
    if (win == null) return null;
    Rectangle r1 = win.getBounds();
    Insets insets = win.getInsets();
    Point loc = getLocation();
    if (loc.x > insets.left + 5 || loc.y > insets.top + 5) {
      r1.width = newWidth + insets.left + insets.right + 10;
      r1.height = newHeight + insets.top + insets.bottom + 10;
      if (win instanceof StackWindow) r1.height += 20;
    } else {
      r1.width = r1.width - dstWidth + newWidth + 10;
      r1.height = r1.height - dstHeight + newHeight + 10;
    }
    Rectangle max = win.getMaxWindow(r1.x, r1.y);
    boolean fitsHorizontally = r1.x + r1.width < max.x + max.width;
    boolean fitsVertically = r1.y + r1.height < max.y + max.height;
    if (fitsHorizontally && fitsVertically) return new Dimension(newWidth, newHeight);
    else if (fitsVertically && newHeight < dstWidth) return new Dimension(dstWidth, newHeight);
    else if (fitsHorizontally && newWidth < dstHeight) return new Dimension(newWidth, dstHeight);
    else return null;
  }

  /**
   * Zooms out by making the source rectangle (srcRect) larger and centering it on (x,y). If we
   * can't make it larger, then make the window smaller.
   */
  public void zoomOut(int x, int y) {
    if (magnification <= 0.03125) return;
    double oldMag = magnification;
    double newMag = getLowerZoomLevel(magnification);
    double srcRatio = (double) srcRect.width / srcRect.height;
    double imageRatio = (double) imageWidth / imageHeight;
    double initialMag = imp.getWindow().getInitialMagnification();
    if (Math.abs(srcRatio - imageRatio) > 0.05) {
      double scale = oldMag / newMag;
      int newSrcWidth = (int) Math.round(srcRect.width * scale);
      int newSrcHeight = (int) Math.round(srcRect.height * scale);
      if (newSrcWidth > imageWidth) newSrcWidth = imageWidth;
      if (newSrcHeight > imageHeight) newSrcHeight = imageHeight;
      int newSrcX = srcRect.x - (newSrcWidth - srcRect.width) / 2;
      int newSrcY = srcRect.y - (newSrcHeight - srcRect.height) / 2;
      if (newSrcX < 0) newSrcX = 0;
      if (newSrcY < 0) newSrcY = 0;
      srcRect = new Rectangle(newSrcX, newSrcY, newSrcWidth, newSrcHeight);
      // IJ.log(newMag+" "+srcRect+" "+dstWidth+" "+dstHeight);
      int newDstWidth = (int) (srcRect.width * newMag);
      int newDstHeight = (int) (srcRect.height * newMag);
      setMagnification(newMag);
      setMaxBounds();
      // IJ.log(newDstWidth+" "+dstWidth+" "+newDstHeight+" "+dstHeight);
      if (newDstWidth < dstWidth || newDstHeight < dstHeight) {
        // IJ.log("pack");
        setDrawingSize(newDstWidth, newDstHeight);
        imp.getWindow().pack();
      } else repaint();
      return;
    }
    if (imageWidth * newMag > dstWidth) {
      int w = (int) Math.round(dstWidth / newMag);
      if (w * newMag < dstWidth) w++;
      int h = (int) Math.round(dstHeight / newMag);
      if (h * newMag < dstHeight) h++;
      x = offScreenX(x);
      y = offScreenY(y);
      Rectangle r = new Rectangle(x - w / 2, y - h / 2, w, h);
      if (r.x < 0) r.x = 0;
      if (r.y < 0) r.y = 0;
      if (r.x + w > imageWidth) r.x = imageWidth - w;
      if (r.y + h > imageHeight) r.y = imageHeight - h;
      srcRect = r;
    } else {
      srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
      setDrawingSize((int) (imageWidth * newMag), (int) (imageHeight * newMag));
      // setDrawingSize(dstWidth/2, dstHeight/2);
      imp.getWindow().pack();
    }
    // IJ.write(newMag + " " + srcRect.x+" "+srcRect.y+" "+srcRect.width+" "+srcRect.height+"
    // "+dstWidth + " " + dstHeight);
    setMagnification(newMag);
    // IJ.write(srcRect.x + " " + srcRect.width + " " + dstWidth);
    setMaxBounds();
    repaint();
  }

  /** Implements the Image/Zoom/Original Scale command. */
  public void unzoom() {
    double imag = imp.getWindow().getInitialMagnification();
    if (magnification == imag) return;
    srcRect = new Rectangle(0, 0, imageWidth, imageHeight);
    ImageWindow win = imp.getWindow();
    setDrawingSize((int) (imageWidth * imag), (int) (imageHeight * imag));
    setMagnification(imag);
    setMaxBounds();
    win.pack();
    setMaxBounds();
    repaint();
  }

  /** Implements the Image/Zoom/View 100% command. */
  public void zoom100Percent() {
    if (magnification == 1.0) return;
    double imag = imp.getWindow().getInitialMagnification();
    if (magnification != imag) unzoom();
    if (magnification == 1.0) return;
    if (magnification < 1.0) {
      while (magnification < 1.0) zoomIn(imageWidth / 2, imageHeight / 2);
    } else if (magnification > 1.0) {
      while (magnification > 1.0) zoomOut(imageWidth / 2, imageHeight / 2);
    } else return;
    int x = xMouse, y = yMouse;
    if (mouseExited) {
      x = imageWidth / 2;
      y = imageHeight / 2;
    }
    int sx = screenX(x);
    int sy = screenY(y);
    adjustSourceRect(1.0, sx, sy);
    repaint();
  }

  protected void scroll(int sx, int sy) {
    int ox = xSrcStart + (int) (sx / magnification); // convert to offscreen coordinates
    int oy = ySrcStart + (int) (sy / magnification);
    // IJ.log("scroll: "+ox+" "+oy+" "+xMouseStart+" "+yMouseStart);
    int newx = xSrcStart + (xMouseStart - ox);
    int newy = ySrcStart + (yMouseStart - oy);
    if (newx < 0) newx = 0;
    if (newy < 0) newy = 0;
    if ((newx + srcRect.width) > imageWidth) newx = imageWidth - srcRect.width;
    if ((newy + srcRect.height) > imageHeight) newy = imageHeight - srcRect.height;
    srcRect.x = newx;
    srcRect.y = newy;
    // IJ.log(sx+"  "+sy+"  "+newx+"  "+newy+"  "+srcRect);
    imp.draw();
    Thread.yield();
  }

  Color getColor(int index) {
    IndexColorModel cm = (IndexColorModel) imp.getProcessor().getColorModel();
    // IJ.write(""+index+" "+(new Color(cm.getRGB(index))));
    return new Color(cm.getRGB(index));
  }

  protected void setDrawingColor(int ox, int oy, boolean setBackground) {
    // IJ.log("setDrawingColor: "+setBackground+this);
    int type = imp.getType();
    int[] v = imp.getPixel(ox, oy);
    switch (type) {
      case ImagePlus.GRAY8:
        {
          if (setBackground) setBackgroundColor(getColor(v[0]));
          else setForegroundColor(getColor(v[0]));
          break;
        }
      case ImagePlus.GRAY16:
      case ImagePlus.GRAY32:
        {
          double min = imp.getProcessor().getMin();
          double max = imp.getProcessor().getMax();
          double value = (type == ImagePlus.GRAY32) ? Float.intBitsToFloat(v[0]) : v[0];
          int index = (int) (255.0 * ((value - min) / (max - min)));
          if (index < 0) index = 0;
          if (index > 255) index = 255;
          if (setBackground) setBackgroundColor(getColor(index));
          else setForegroundColor(getColor(index));
          break;
        }
      case ImagePlus.COLOR_RGB:
      case ImagePlus.COLOR_256:
        {
          Color c = new Color(v[0], v[1], v[2]);
          if (setBackground) setBackgroundColor(c);
          else setForegroundColor(c);
          break;
        }
    }
    Color c;
    if (setBackground) c = Toolbar.getBackgroundColor();
    else {
      c = Toolbar.getForegroundColor();
      imp.setColor(c);
    }
    IJ.showStatus("(" + c.getRed() + ", " + c.getGreen() + ", " + c.getBlue() + ")");
  }

  private void setForegroundColor(Color c) {
    Toolbar.setForegroundColor(c);
    if (Recorder.record)
      Recorder.record("setForegroundColor", c.getRed(), c.getGreen(), c.getBlue());
  }

  private void setBackgroundColor(Color c) {
    Toolbar.setBackgroundColor(c);
    if (Recorder.record)
      Recorder.record("setBackgroundColor", c.getRed(), c.getGreen(), c.getBlue());
  }

  public void mousePressed(MouseEvent e) {
    // if (ij==null) return;
    showCursorStatus = true;
    int toolID = Toolbar.getToolId();
    ImageWindow win = imp.getWindow();
    if (win != null && win.running2 && toolID != Toolbar.MAGNIFIER) {
      if (win instanceof StackWindow) ((StackWindow) win).setAnimate(false);
      else win.running2 = false;
      return;
    }

    int x = e.getX();
    int y = e.getY();
    flags = e.getModifiers();
    // IJ.log("Mouse pressed: " + e.isPopupTrigger() + "  " + ij.modifiers(flags));
    // if (toolID!=Toolbar.MAGNIFIER && e.isPopupTrigger()) {
    if (toolID != Toolbar.MAGNIFIER
        && (e.isPopupTrigger() || (!IJ.isMacintosh() && (flags & Event.META_MASK) != 0))) {
      handlePopupMenu(e);
      return;
    }

    int ox = offScreenX(x);
    int oy = offScreenY(y);
    xMouse = ox;
    yMouse = oy;
    if (IJ.spaceBarDown()) {
      // temporarily switch to "hand" tool of space bar down
      setupScroll(ox, oy);
      return;
    }
    if (showAllROIs) {
      Roi roi = imp.getRoi();
      if (!(roi != null && (roi.contains(ox, oy) || roi.isHandle(x, y) >= 0))
          && roiManagerSelect(x, y)) return;
    }
    if (customRoi && overlay != null) return;

    switch (toolID) {
      case Toolbar.MAGNIFIER:
        if (IJ.shiftKeyDown()) zoomToSelection(ox, oy);
        else if ((flags & (Event.ALT_MASK | Event.META_MASK | Event.CTRL_MASK)) != 0) {
          // IJ.run("Out");
          zoomOut(x, y);
          if (getMagnification() < 1.0) imp.repaintWindow();
        } else {
          // IJ.run("In");
          zoomIn(x, y);
          if (getMagnification() <= 1.0) imp.repaintWindow();
        }
        break;
      case Toolbar.HAND:
        setupScroll(ox, oy);
        break;
      case Toolbar.DROPPER:
        setDrawingColor(ox, oy, IJ.altKeyDown());
        break;
      case Toolbar.WAND:
        Roi roi = imp.getRoi();
        if (roi != null && roi.contains(ox, oy)) {
          Rectangle r = roi.getBounds();
          if (r.width == imageWidth && r.height == imageHeight) imp.killRoi();
          else if (!e.isAltDown()) {
            handleRoiMouseDown(e);
            return;
          }
        }
        if (roi != null) {
          int handle = roi.isHandle(x, y);
          if (handle >= 0) {
            roi.mouseDownInHandle(handle, x, y);
            return;
          }
        }
        setRoiModState(e, roi, -1);
        String mode = WandToolOptions.getMode();
        double tolerance = WandToolOptions.getTolerance();
        int npoints = IJ.doWand(ox, oy, tolerance, mode);
        if (Recorder.record && npoints > 0) {
          if (tolerance == 0.0 && mode.equals("Legacy")) Recorder.record("doWand", ox, oy);
          else
            Recorder.recordString(
                "doWand(" + ox + ", " + oy + ", " + tolerance + ", \"" + mode + "\");\n");
        }
        break;
      case Toolbar.OVAL:
        if (Toolbar.getBrushSize() > 0) new RoiBrush();
        else handleRoiMouseDown(e);
        break;
      case Toolbar.SPARE1:
      case Toolbar.SPARE2:
      case Toolbar.SPARE3:
      case Toolbar.SPARE4:
      case Toolbar.SPARE5:
      case Toolbar.SPARE6:
      case Toolbar.SPARE7:
      case Toolbar.SPARE8:
      case Toolbar.SPARE9:
        Toolbar.getInstance().runMacroTool(toolID);
        break;
      default: // selection tool
        handleRoiMouseDown(e);
    }
  }

  boolean roiManagerSelect(int x, int y) {
    RoiManager rm = RoiManager.getInstance();
    if (rm == null) return false;
    Hashtable rois = rm.getROIs();
    java.awt.List list = rm.getList();
    int n = list.getItemCount();
    if (labelRects == null || labelRects.length != n) return false;
    boolean stackMode = imp != null && imp.getStackSize() > 1 && Prefs.showAllSliceOnly;
    for (int i = 0; i < n; i++) {
      if (labelRects[i] != null && labelRects[i].contains(x, y)) {
        if (stackMode) {
          int slice = getSliceNumber(list.getItem(i));
          if (slice != imp.getCurrentSlice()) continue;
        }
        // rm.select(i);
        // this needs to run on a separate thread, at least on OS X
        // "update2" does not clone the ROI so the "Show All"
        // outline moves as the user moves the RO.
        new ij.macro.MacroRunner("roiManager('select', " + i + "); roiManager('update2');");
        return true;
      }
    }
    return false;
  }

  void zoomToSelection(int x, int y) {
    IJ.setKeyUp(IJ.ALL_KEYS);
    String macro =
        "args = split(getArgument);\n"
            + "x1=parseInt(args[0]); y1=parseInt(args[1]); flags=20;\n"
            + "while (flags&20!=0) {\n"
            + "getCursorLoc(x2, y2, z, flags);\n"
            + "if (x2>=x1) x=x1; else x=x2;\n"
            + "if (y2>=y1) y=y1; else y=y2;\n"
            + "makeRectangle(x, y, abs(x2-x1), abs(y2-y1));\n"
            + "wait(10);\n"
            + "}\n"
            + "run('To Selection');\n";
    new MacroRunner(macro, x + " " + y);
  }

  protected void setupScroll(int ox, int oy) {
    xMouseStart = ox;
    yMouseStart = oy;
    xSrcStart = srcRect.x;
    ySrcStart = srcRect.y;
  }

  protected void handlePopupMenu(MouseEvent e) {
    if (disablePopupMenu) return;
    if (IJ.debugMode) IJ.log("show popup: " + (e.isPopupTrigger() ? "true" : "false"));
    int x = e.getX();
    int y = e.getY();
    Roi roi = imp.getRoi();
    if (roi != null
        && (roi.getType() == Roi.POLYGON
            || roi.getType() == Roi.POLYLINE
            || roi.getType() == Roi.ANGLE)
        && roi.getState() == roi.CONSTRUCTING) {
      roi.handleMouseUp(x, y); // simulate double-click to finalize
      roi.handleMouseUp(x, y); // polygon or polyline selection
      return;
    }
    PopupMenu popup = Menus.getPopupMenu();
    if (popup != null) {
      add(popup);
      if (IJ.isMacOSX()) IJ.wait(10);
      popup.show(this, x, y);
    }
  }

  public void mouseExited(MouseEvent e) {
    // autoScroll(e);
    ImageWindow win = imp.getWindow();
    if (win != null) setCursor(defaultCursor);
    IJ.showStatus("");
    mouseExited = true;
  }

  /*
  public void autoScroll(MouseEvent e) {
  	Roi roi = imp.getRoi();
  	if (roi==null || roi.getState()!=roi.CONSTRUCTING || srcRect.width>=imageWidth || srcRect.height>=imageHeight
  	|| !(roi.getType()==Roi.POLYGON || roi.getType()==Roi.POLYLINE || roi.getType()==Roi.ANGLE))
  		return;
  	int sx = e.getX();
  	int sy = e.getY();
  	xMouseStart = srcRect.x+srcRect.width/2;
  	yMouseStart = srcRect.y+srcRect.height/2;
  	Rectangle r = roi.getBounds();
  	Dimension size = getSize();
  	int deltax=0, deltay=0;
  	if (sx<0)
  		deltax = srcRect.width/4;
  	else if (sx>size.width)
  		deltax = -srcRect.width/4;
  	if (sy<0)
  		deltay = srcRect.height/4;
  	else if (sy>size.height)
  		deltay = -srcRect.height/4;
  	//IJ.log("autoscroll: "+sx+" "+sy+" "+deltax+" "+deltay+" "+r);
  	scroll(screenX(xMouseStart+deltax), screenY(yMouseStart+deltay));
  }
  */

  public void mouseDragged(MouseEvent e) {
    int x = e.getX();
    int y = e.getY();
    xMouse = offScreenX(x);
    yMouse = offScreenY(y);
    flags = e.getModifiers();
    // IJ.log("mouseDragged: "+flags);
    if (flags == 0) // workaround for Mac OS 9 bug
    flags = InputEvent.BUTTON1_MASK;
    if (Toolbar.getToolId() == Toolbar.HAND || IJ.spaceBarDown()) scroll(x, y);
    else {
      IJ.setInputEvent(e);
      Roi roi = imp.getRoi();
      if (roi != null) roi.handleMouseDrag(x, y, flags);
    }
  }

  protected void handleRoiMouseDown(MouseEvent e) {
    int sx = e.getX();
    int sy = e.getY();
    int ox = offScreenX(sx);
    int oy = offScreenY(sy);
    Roi roi = imp.getRoi();
    int handle = roi != null ? roi.isHandle(sx, sy) : -1;
    boolean multiPointMode =
        roi != null
            && (roi instanceof PointRoi)
            && handle == -1
            && Toolbar.getToolId() == Toolbar.POINT
            && Toolbar.getMultiPointMode();
    if (multiPointMode) {
      imp.setRoi(((PointRoi) roi).addPoint(ox, oy));
      return;
    }
    setRoiModState(e, roi, handle);
    if (roi != null) {
      if (handle >= 0) {
        roi.mouseDownInHandle(handle, sx, sy);
        return;
      }
      Rectangle r = roi.getBounds();
      int type = roi.getType();
      if (type == Roi.RECTANGLE
          && r.width == imp.getWidth()
          && r.height == imp.getHeight()
          && roi.getPasteMode() == Roi.NOT_PASTING
          && !(roi instanceof ImageRoi)) {
        imp.killRoi();
        return;
      }
      if (roi.contains(ox, oy)) {
        if (roi.modState == Roi.NO_MODS) roi.handleMouseDown(sx, sy);
        else {
          imp.killRoi();
          imp.createNewRoi(sx, sy);
        }
        return;
      }
      if ((type == Roi.POLYGON || type == Roi.POLYLINE || type == Roi.ANGLE)
          && roi.getState() == roi.CONSTRUCTING) return;
      int tool = Toolbar.getToolId();
      if ((tool == Toolbar.POLYGON || tool == Toolbar.POLYLINE || tool == Toolbar.ANGLE)
          && !(IJ.shiftKeyDown() || IJ.altKeyDown())) {
        imp.killRoi();
        return;
      }
    }
    imp.createNewRoi(sx, sy);
  }

  void setRoiModState(MouseEvent e, Roi roi, int handle) {
    if (roi == null || (handle >= 0 && roi.modState == Roi.NO_MODS)) return;
    if (roi.state == Roi.CONSTRUCTING) return;
    int tool = Toolbar.getToolId();
    if (tool > Toolbar.FREEROI && tool != Toolbar.WAND && tool != Toolbar.POINT) {
      roi.modState = Roi.NO_MODS;
      return;
    }
    if (e.isShiftDown()) roi.modState = Roi.ADD_TO_ROI;
    else if (e.isAltDown()) roi.modState = Roi.SUBTRACT_FROM_ROI;
    else roi.modState = Roi.NO_MODS;
    // IJ.log("setRoiModState: "+roi.modState+" "+ roi.state);
  }

  /** Disable/enable popup menu. */
  public void disablePopupMenu(boolean status) {
    disablePopupMenu = status;
  }

  /** Enables/disables the ROI Manager "Show All" mode. */
  public void setShowAllROIs(boolean showAllROIs) {
    this.showAllROIs = showAllROIs;
  }

  /** Returns the state of the ROI Manager "Show All" flag. */
  public boolean getShowAllROIs() {
    return showAllROIs;
  }

  /** Return the ROI Manager "Show All" list as an overlay. */
  public Overlay getShowAllList() {
    if (!showAllROIs) return null;
    if (showAllList != null) return showAllList;
    RoiManager rm = RoiManager.getInstance();
    if (rm == null) return null;
    Roi[] rois = rm.getRoisAsArray();
    if (rois.length == 0) return null;
    Overlay overlay = new Overlay();
    for (int i = 0; i < rois.length; i++) overlay.add((Roi) rois[i].clone());
    return overlay;
  }

  /** Returns the color used for "Show All" mode. */
  public static Color getShowAllColor() {
    if (showAllColor != null && showAllColor.getRGB() == 0xff80ffff) showAllColor = Color.cyan;
    return showAllColor;
  }

  /** Sets the color used used for the ROI Manager "Show All" mode. */
  public static void setShowAllColor(Color c) {
    if (c == null) return;
    showAllColor = c;
    labelColor = null;
    ImagePlus img = WindowManager.getCurrentImage();
    if (img != null) {
      ImageCanvas ic = img.getCanvas();
      if (ic != null && ic.getShowAllROIs()) img.draw();
    }
  }

  /** Use ImagePlus.setOverlay(ij.gui.Overlay). */
  public void setOverlay(Overlay overlay) {
    this.overlay = overlay;
    if (overlay != null) font = overlay.getLabelsFont();
    repaint();
  }

  /** Use ImagePlus.getOverlay(). */
  public Overlay getOverlay() {
    return overlay;
  }

  /** @deprecated replaced by ImagePlus.setOverlay(ij.gui.Overlay) */
  public void setDisplayList(Vector list) {
    if (list != null) {
      Overlay list2 = new Overlay();
      list2.setVector(list);
      setOverlay(list2);
    } else setOverlay(null);
    if (overlay != null)
      overlay.drawLabels(overlay.size() > 0 && overlay.get(0).getStrokeColor() == null);
    else customRoi = false;
    repaint();
  }

  /** @deprecated replaced by ImagePlus.setOverlay(Shape, Color, BasicStroke) */
  public void setDisplayList(Shape shape, Color color, BasicStroke stroke) {
    if (shape == null) {
      setOverlay(null);
      return;
    }
    Roi roi = new ShapeRoi(shape);
    roi.setStrokeColor(color);
    roi.setStroke(stroke);
    Overlay list = new Overlay();
    list.add(roi);
    setOverlay(list);
  }

  /** @deprecated replaced by ImagePlus.setOverlay(Roi, Color, int, Color) */
  public void setDisplayList(Roi roi, Color color) {
    roi.setStrokeColor(color);
    Overlay list = new Overlay();
    list.add(roi);
    setOverlay(list);
  }

  /** @deprecated replaced by ImagePlus.getOverlay() */
  public Vector getDisplayList() {
    if (overlay == null) return null;
    Vector displayList = new Vector();
    for (int i = 0; i < overlay.size(); i++) displayList.add(overlay.get(i));
    return displayList;
  }

  /** Allows plugins (e.g., Orthogonal_Views) to create a custom ROI using a display list. */
  public void setCustomRoi(boolean customRoi) {
    this.customRoi = customRoi;
  }

  public boolean getCustomRoi() {
    return customRoi;
  }

  /**
   * Called by IJ.showStatus() to prevent status bar text from being overwritten until the cursor
   * moves at least 12 pixels.
   */
  public void setShowCursorStatus(boolean status) {
    showCursorStatus = status;
    if (status == true) sx2 = sy2 = -1000;
    else {
      sx2 = screenX(xMouse);
      sy2 = screenY(yMouse);
    }
  }

  public void mouseReleased(MouseEvent e) {
    flags = e.getModifiers();
    flags &= ~InputEvent.BUTTON1_MASK; // make sure button 1 bit is not set
    flags &= ~InputEvent.BUTTON2_MASK; // make sure button 2 bit is not set
    flags &= ~InputEvent.BUTTON3_MASK; // make sure button 3 bit is not set
    Roi roi = imp.getRoi();
    if (roi != null) {
      Rectangle r = roi.getBounds();
      int type = roi.getType();
      if ((r.width == 0 || r.height == 0)
          && !(type == Roi.POLYGON || type == Roi.POLYLINE || type == Roi.ANGLE || type == Roi.LINE)
          && !(roi instanceof TextRoi)
          && roi.getState() == roi.CONSTRUCTING
          && type != roi.POINT) imp.killRoi();
      else {
        roi.handleMouseUp(e.getX(), e.getY());
        if (roi.getType() == Roi.LINE && roi.getLength() == 0.0) imp.killRoi();
      }
    }
  }

  public void mouseMoved(MouseEvent e) {
    // if (ij==null) return;
    int sx = e.getX();
    int sy = e.getY();
    int ox = offScreenX(sx);
    int oy = offScreenY(sy);
    flags = e.getModifiers();
    setCursor(sx, sy, ox, oy);
    IJ.setInputEvent(e);
    Roi roi = imp.getRoi();
    if (roi != null
        && (roi.getType() == Roi.POLYGON
            || roi.getType() == Roi.POLYLINE
            || roi.getType() == Roi.ANGLE)
        && roi.getState() == roi.CONSTRUCTING) {
      PolygonRoi pRoi = (PolygonRoi) roi;
      pRoi.handleMouseMove(ox, oy);
    } else {
      if (ox < imageWidth && oy < imageHeight) {
        ImageWindow win = imp.getWindow();
        // Cursor must move at least 12 pixels before text
        // displayed using IJ.showStatus() is overwritten.
        if ((sx - sx2) * (sx - sx2) + (sy - sy2) * (sy - sy2) > 144) showCursorStatus = true;
        if (win != null && showCursorStatus) win.mouseMoved(ox, oy);
      } else IJ.showStatus("");
    }
  }

  public void mouseClicked(MouseEvent e) {}

  public void mouseEntered(MouseEvent e) {}
}
Пример #12
0
 public static void main(String args[]) {
   if (System.getProperty("java.version").substring(0, 3).compareTo("1.5") < 0) {
     javax.swing.JOptionPane.showMessageDialog(
         null, "ImageJ " + VERSION + " requires Java 1.5 or later.");
     System.exit(0);
   }
   boolean noGUI = false;
   int mode = STANDALONE;
   arguments = args;
   // System.setProperty("file.encoding", "UTF-8");
   int nArgs = args != null ? args.length : 0;
   boolean commandLine = false;
   for (int i = 0; i < nArgs; i++) {
     String arg = args[i];
     if (arg == null) continue;
     if (args[i].startsWith("-")) {
       if (args[i].startsWith("-batch")) noGUI = true;
       else if (args[i].startsWith("-debug")) IJ.setDebugMode(true);
       else if (args[i].startsWith("-ijpath") && i + 1 < nArgs) {
         if (IJ.debugMode) IJ.log("-ijpath: " + args[i + 1]);
         Prefs.setHomeDir(args[i + 1]);
         commandLine = true;
         args[i + 1] = null;
       } else if (args[i].startsWith("-port")) {
         int delta = (int) Tools.parseDouble(args[i].substring(5, args[i].length()), 0.0);
         commandLine = true;
         if (delta == 0) mode = EMBEDDED;
         else if (delta > 0 && DEFAULT_PORT + delta < 65536) port = DEFAULT_PORT + delta;
       }
     }
   }
   // If existing ImageJ instance, pass arguments to it and quit.
   boolean passArgs = mode == STANDALONE && !noGUI;
   if (IJ.isMacOSX() && !commandLine) passArgs = false;
   if (passArgs && isRunning(args)) return;
   ImageJ ij = IJ.getInstance();
   if (!noGUI && (ij == null || (ij != null && !ij.isShowing()))) {
     ij = new ImageJ(null, mode);
     ij.exitWhenQuitting = true;
   }
   int macros = 0;
   for (int i = 0; i < nArgs; i++) {
     String arg = args[i];
     if (arg == null) continue;
     if (arg.startsWith("-")) {
       if ((arg.startsWith("-macro") || arg.startsWith("-batch")) && i + 1 < nArgs) {
         String arg2 = i + 2 < nArgs ? args[i + 2] : null;
         Prefs.commandLineMacro = true;
         if (noGUI && args[i + 1].endsWith(".js")) Interpreter.batchMode = true;
         IJ.runMacroFile(args[i + 1], arg2);
         break;
       } else if (arg.startsWith("-eval") && i + 1 < nArgs) {
         String rtn = IJ.runMacro(args[i + 1]);
         if (rtn != null) System.out.print(rtn);
         args[i + 1] = null;
       } else if (arg.startsWith("-run") && i + 1 < nArgs) {
         IJ.run(args[i + 1]);
         args[i + 1] = null;
       }
     } else if (macros == 0 && (arg.endsWith(".ijm") || arg.endsWith(".txt"))) {
       IJ.runMacroFile(arg);
       macros++;
     } else if (arg.length() > 0 && arg.indexOf("ij.ImageJ") == -1) {
       File file = new File(arg);
       IJ.open(file.getAbsolutePath());
     }
   }
   if (IJ.debugMode && IJ.getInstance() == null) new JavaProperties().run("");
   if (noGUI) System.exit(0);
 }
Пример #13
0
public class vvd_easy_export implements PlugIn {
  String basename;
  String directory;
  ArrayList<String> lvImgTitle;
  int bw = 128, bh = 128, bd = 64, lv = 1;
  String filetype = (String) Prefs.get("filetype.string", "RAW");
  int jpeg_quality = (int) Prefs.get("jpeg_quality.int", FileSaver.DEFAULT_JPEG_QUALITY);
  int bdsizelimit = (int) Prefs.get("bdsizelimit.int", 50);
  ArrayList<Integer> bwlist = new ArrayList<Integer>();
  ArrayList<Integer> bhlist = new ArrayList<Integer>();
  ArrayList<Integer> bdlist = new ArrayList<Integer>();

  class Brick {
    public int x_, y_, z_;
    public int w_, h_, d_;
    public long offset_, size_;
    public double tx0_, ty0_, tz0_, tx1_, ty1_, tz1_;
    public double bx0_, by0_, bz0_, bx1_, by1_, bz1_;

    Brick(
        int x,
        int y,
        int z,
        int w,
        int h,
        int d,
        long offset,
        long size,
        double tx0,
        double ty0,
        double tz0,
        double tx1,
        double ty1,
        double tz1,
        double bx0,
        double by0,
        double bz0,
        double bx1,
        double by1,
        double bz1) {
      x_ = x;
      y_ = y;
      z_ = z;
      w_ = w;
      h_ = h;
      d_ = d;
      offset_ = offset;
      size_ = size;
      tx0_ = tx0;
      ty0_ = ty0;
      tz0_ = tz0;
      tx1_ = tx1;
      ty1_ = ty1;
      tz1_ = tz1;
      bx0_ = bx0;
      by0_ = by0;
      bz0_ = bz0;
      bx1_ = bx1;
      by1_ = by1;
      bz1_ = bz1;
    }
  }

  public void run(String arg) {
    if (IJ.versionLessThan("1.49d")) return;

    if (!showDialog()) return;

    SaveDialog sd = new SaveDialog("Save as Bricks...", "", "");
    basename = sd.getFileName();
    directory = sd.getDirectory();
    if (basename == null || directory == null) return;

    build_bricks();
  }

  private boolean showDialog() {
    String[] types = {"RAW", "JPEG", "ZLIB"};
    GenericDialog gd = new GenericDialog("Generate Bricks");
    gd.addChoice("FileType", types, filetype);
    gd.addNumericField("JPEG quality", jpeg_quality, 0);
    gd.addNumericField("Max file size (MB)", bdsizelimit, 0);

    int[] wlist = WindowManager.getIDList();
    if (wlist == null) return false;

    String[] titles = new String[wlist.length];
    for (int i = 0; i < wlist.length; i++) titles[i] = "";

    int tnum = 0;
    for (int i = 0; i < wlist.length; i++) {
      ImagePlus imp = WindowManager.getImage(wlist[i]);
      if (imp != null) {
        titles[tnum] = imp.getTitle();
        tnum++;
      }
    }
    gd.addChoice("Source image: ", titles, titles[0]);

    gd.showDialog();
    if (gd.wasCanceled()) return false;

    filetype = types[gd.getNextChoiceIndex()];
    jpeg_quality = (int) gd.getNextNumber();
    if (jpeg_quality > 100) jpeg_quality = 100;
    if (jpeg_quality < 0) jpeg_quality = 0;
    bdsizelimit = (int) gd.getNextNumber();

    int id = gd.getNextChoiceIndex();
    lvImgTitle = new ArrayList<String>();
    lvImgTitle.add(titles[id]);

    Prefs.set("filetype.string", filetype);
    Prefs.set("jpeg_quality.int", jpeg_quality);
    Prefs.set("bdsizelimit.int", bdsizelimit);

    return true;
  }

  public static int log2(int n) {
    if (n <= 0) return 0;
    return 31 - Integer.numberOfLeadingZeros(n);
  }

  public void build_bricks() {

    ImagePlus imp;
    ImagePlus orgimp;
    ImageStack stack;
    FileInfo finfo;

    if (lvImgTitle.isEmpty()) return;
    orgimp = WindowManager.getImage(lvImgTitle.get(0));
    imp = orgimp;

    finfo = imp.getFileInfo();
    if (finfo == null) return;

    int[] dims = imp.getDimensions();
    int imageW = dims[0];
    int imageH = dims[1];
    int nCh = dims[2];
    int imageD = dims[3];
    int nFrame = dims[4];
    int bdepth = imp.getBitDepth();
    double xspc = finfo.pixelWidth;
    double yspc = finfo.pixelHeight;
    double zspc = finfo.pixelDepth;
    double z_aspect = Math.max(xspc, yspc) / zspc;

    int orgW = imageW;
    int orgH = imageH;
    int orgD = imageD;
    double orgxspc = xspc;
    double orgyspc = yspc;
    double orgzspc = zspc;

    lv = lvImgTitle.size();
    if (filetype == "JPEG") {
      for (int l = 0; l < lv; l++) {
        if (WindowManager.getImage(lvImgTitle.get(l)).getBitDepth() != 8) {
          IJ.error("A SOURCE IMAGE MUST BE 8BIT GLAYSCALE");
          return;
        }
      }
    }

    // calculate levels
    /*		int baseXY = 256;
    		int baseZ = 256;

    		if (z_aspect < 0.5) baseZ = 128;
    		if (z_aspect > 2.0) baseXY = 128;
    		if (z_aspect >= 0.5 && z_aspect < 1.0) baseZ = (int)(baseZ*z_aspect);
    		if (z_aspect > 1.0 && z_aspect <= 2.0) baseXY = (int)(baseXY/z_aspect);

    		IJ.log("Z_aspect: " + z_aspect);
    		IJ.log("BaseXY: " + baseXY);
    		IJ.log("BaseZ: " + baseZ);
    */

    int baseXY = 256;
    int baseZ = 128;
    int dbXY = Math.max(orgW, orgH) / baseXY;
    if (Math.max(orgW, orgH) % baseXY > 0) dbXY *= 2;
    int dbZ = orgD / baseZ;
    if (orgD % baseZ > 0) dbZ *= 2;
    lv = Math.max(log2(dbXY), log2(dbZ)) + 1;

    int ww = orgW;
    int hh = orgH;
    int dd = orgD;
    for (int l = 0; l < lv; l++) {
      int bwnum = ww / baseXY;
      if (ww % baseXY > 0) bwnum++;
      int bhnum = hh / baseXY;
      if (hh % baseXY > 0) bhnum++;
      int bdnum = dd / baseZ;
      if (dd % baseZ > 0) bdnum++;

      if (bwnum % 2 == 0) bwnum++;
      if (bhnum % 2 == 0) bhnum++;
      if (bdnum % 2 == 0) bdnum++;

      int bw = (bwnum <= 1) ? ww : ww / bwnum + 1 + (ww % bwnum > 0 ? 1 : 0);
      int bh = (bhnum <= 1) ? hh : hh / bhnum + 1 + (hh % bhnum > 0 ? 1 : 0);
      int bd = (bdnum <= 1) ? dd : dd / bdnum + 1 + (dd % bdnum > 0 ? 1 : 0);

      bwlist.add(bw);
      bhlist.add(bh);
      bdlist.add(bd);

      IJ.log("LEVEL: " + l);
      IJ.log("  width: " + ww);
      IJ.log("  hight: " + hh);
      IJ.log("  depth: " + dd);
      IJ.log("  bw: " + bw);
      IJ.log("  bh: " + bh);
      IJ.log("  bd: " + bd);

      int xyl2 = Math.max(ww, hh) / baseXY;
      if (Math.max(ww, hh) % baseXY > 0) xyl2 *= 2;
      if (lv - 1 - log2(xyl2) <= l) {
        ww /= 2;
        hh /= 2;
      }
      IJ.log("  xyl2: " + (lv - 1 - log2(xyl2)));

      int zl2 = dd / baseZ;
      if (dd % baseZ > 0) zl2 *= 2;
      if (lv - 1 - log2(zl2) <= l) dd /= 2;
      IJ.log("  zl2: " + (lv - 1 - log2(zl2)));

      if (l < lv - 1) {
        lvImgTitle.add(lvImgTitle.get(0) + "_level" + (l + 1));
        IJ.selectWindow(lvImgTitle.get(0));
        IJ.run(
            "Scale...",
            "x=- y=- z=- width="
                + ww
                + " height="
                + hh
                + " depth="
                + dd
                + " interpolation=Bicubic average process create title="
                + lvImgTitle.get(l + 1));
      }
    }

    for (int l = 0; l < lv; l++) {
      IJ.log(lvImgTitle.get(l));
    }

    Document doc = newXMLDocument();
    Element root = doc.createElement("BRK");
    root.setAttribute("version", "1.0");
    root.setAttribute("nLevel", String.valueOf(lv));
    root.setAttribute("nChannel", String.valueOf(nCh));
    root.setAttribute("nFrame", String.valueOf(nFrame));
    doc.appendChild(root);

    for (int l = 0; l < lv; l++) {
      IJ.showProgress(0.0);

      int[] dims2 = imp.getDimensions();
      IJ.log(
          "W: "
              + String.valueOf(dims2[0])
              + " H: "
              + String.valueOf(dims2[1])
              + " C: "
              + String.valueOf(dims2[2])
              + " D: "
              + String.valueOf(dims2[3])
              + " T: "
              + String.valueOf(dims2[4])
              + " b: "
              + String.valueOf(bdepth));

      bw = bwlist.get(l).intValue();
      bh = bhlist.get(l).intValue();
      bd = bdlist.get(l).intValue();

      boolean force_pow2 = false;
      /*			if(IsPowerOf2(bw) && IsPowerOf2(bh) && IsPowerOf2(bd)) force_pow2 = true;

      			if(force_pow2){
      				//force pow2
      				if(Pow2(bw) > bw) bw = Pow2(bw)/2;
      				if(Pow2(bh) > bh) bh = Pow2(bh)/2;
      				if(Pow2(bd) > bd) bd = Pow2(bd)/2;
      			}

      			if(bw > imageW) bw = (Pow2(imageW) == imageW) ? imageW : Pow2(imageW)/2;
      			if(bh > imageH) bh = (Pow2(imageH) == imageH) ? imageH : Pow2(imageH)/2;
      			if(bd > imageD) bd = (Pow2(imageD) == imageD) ? imageD : Pow2(imageD)/2;

      */
      if (bw > imageW) bw = imageW;
      if (bh > imageH) bh = imageH;
      if (bd > imageD) bd = imageD;

      if (bw <= 1 || bh <= 1 || bd <= 1) break;

      if (filetype == "JPEG" && (bw < 8 || bh < 8)) break;

      Element lvnode = doc.createElement("Level");
      lvnode.setAttribute("lv", String.valueOf(l));
      lvnode.setAttribute("imageW", String.valueOf(imageW));
      lvnode.setAttribute("imageH", String.valueOf(imageH));
      lvnode.setAttribute("imageD", String.valueOf(imageD));
      lvnode.setAttribute("xspc", String.valueOf(xspc));
      lvnode.setAttribute("yspc", String.valueOf(yspc));
      lvnode.setAttribute("zspc", String.valueOf(zspc));
      lvnode.setAttribute("bitDepth", String.valueOf(bdepth));
      root.appendChild(lvnode);

      Element brksnode = doc.createElement("Bricks");
      brksnode.setAttribute("brick_baseW", String.valueOf(bw));
      brksnode.setAttribute("brick_baseH", String.valueOf(bh));
      brksnode.setAttribute("brick_baseD", String.valueOf(bd));
      lvnode.appendChild(brksnode);

      ArrayList<Brick> bricks = new ArrayList<Brick>();
      int mw, mh, md, mw2, mh2, md2;
      double tx0, ty0, tz0, tx1, ty1, tz1;
      double bx0, by0, bz0, bx1, by1, bz1;
      for (int k = 0; k < imageD; k += bd) {
        if (k > 0) k--;
        for (int j = 0; j < imageH; j += bh) {
          if (j > 0) j--;
          for (int i = 0; i < imageW; i += bw) {
            if (i > 0) i--;
            mw = Math.min(bw, imageW - i);
            mh = Math.min(bh, imageH - j);
            md = Math.min(bd, imageD - k);

            if (force_pow2) {
              mw2 = Pow2(mw);
              mh2 = Pow2(mh);
              md2 = Pow2(md);
            } else {
              mw2 = mw;
              mh2 = mh;
              md2 = md;
            }

            if (filetype == "JPEG") {
              if (mw2 < 8) mw2 = 8;
              if (mh2 < 8) mh2 = 8;
            }

            tx0 = i == 0 ? 0.0d : ((mw2 - mw + 0.5d) / mw2);
            ty0 = j == 0 ? 0.0d : ((mh2 - mh + 0.5d) / mh2);
            tz0 = k == 0 ? 0.0d : ((md2 - md + 0.5d) / md2);

            tx1 = 1.0d - 0.5d / mw2;
            if (mw < bw) tx1 = 1.0d;
            if (imageW - i == bw) tx1 = 1.0d;

            ty1 = 1.0d - 0.5d / mh2;
            if (mh < bh) ty1 = 1.0d;
            if (imageH - j == bh) ty1 = 1.0d;

            tz1 = 1.0d - 0.5d / md2;
            if (md < bd) tz1 = 1.0d;
            if (imageD - k == bd) tz1 = 1.0d;

            bx0 = i == 0 ? 0.0d : (i + 0.5d) / (double) imageW;
            by0 = j == 0 ? 0.0d : (j + 0.5d) / (double) imageH;
            bz0 = k == 0 ? 0.0d : (k + 0.5d) / (double) imageD;

            bx1 = Math.min((i + bw - 0.5d) / (double) imageW, 1.0d);
            if (imageW - i == bw) bx1 = 1.0d;

            by1 = Math.min((j + bh - 0.5d) / (double) imageH, 1.0d);
            if (imageH - j == bh) by1 = 1.0d;

            bz1 = Math.min((k + bd - 0.5d) / (double) imageD, 1.0d);
            if (imageD - k == bd) bz1 = 1.0d;

            int x, y, z;
            x = i - (mw2 - mw);
            y = j - (mh2 - mh);
            z = k - (md2 - md);
            bricks.add(
                new Brick(
                    x, y, z, mw2, mh2, md2, 0, 0, tx0, ty0, tz0, tx1, ty1, tz1, bx0, by0, bz0, bx1,
                    by1, bz1));
          }
        }
      }

      Element fsnode = doc.createElement("Files");
      lvnode.appendChild(fsnode);

      stack = imp.getStack();

      int totalbricknum = nFrame * nCh * bricks.size();
      int curbricknum = 0;
      for (int f = 0; f < nFrame; f++) {
        for (int ch = 0; ch < nCh; ch++) {
          int sizelimit = bdsizelimit * 1024 * 1024;
          int bytecount = 0;
          int filecount = 0;
          int pd_bufsize = Math.max(sizelimit, bw * bh * bd * bdepth / 8);
          byte[] packed_data = new byte[pd_bufsize];
          String base_dataname =
              basename
                  + "_Lv"
                  + String.valueOf(l)
                  + "_Ch"
                  + String.valueOf(ch)
                  + "_Fr"
                  + String.valueOf(f);
          String current_dataname = base_dataname + "_data" + filecount;

          Brick b_first = bricks.get(0);
          if (b_first.z_ != 0) IJ.log("warning");
          int st_z = b_first.z_;
          int ed_z = b_first.z_ + b_first.d_;
          LinkedList<ImageProcessor> iplist = new LinkedList<ImageProcessor>();
          for (int s = st_z; s < ed_z; s++)
            iplist.add(stack.getProcessor(imp.getStackIndex(ch + 1, s + 1, f + 1)));

          //					ImagePlus test;
          //					ImageStack tsst;
          //					test = NewImage.createByteImage("test", imageW, imageH, imageD,
          // NewImage.FILL_BLACK);
          //					tsst = test.getStack();
          for (int i = 0; i < bricks.size(); i++) {
            Brick b = bricks.get(i);

            if (ed_z > b.z_ || st_z < b.z_ + b.d_) {
              if (b.z_ > st_z) {
                for (int s = 0; s < b.z_ - st_z; s++) iplist.pollFirst();
                st_z = b.z_;
              } else if (b.z_ < st_z) {
                IJ.log("warning");
                for (int s = st_z - 1; s > b.z_; s--)
                  iplist.addFirst(stack.getProcessor(imp.getStackIndex(ch + 1, s + 1, f + 1)));
                st_z = b.z_;
              }

              if (b.z_ + b.d_ > ed_z) {
                for (int s = ed_z; s < b.z_ + b.d_; s++)
                  iplist.add(stack.getProcessor(imp.getStackIndex(ch + 1, s + 1, f + 1)));
                ed_z = b.z_ + b.d_;
              } else if (b.z_ + b.d_ < ed_z) {
                IJ.log("warning");
                for (int s = 0; s < ed_z - (b.z_ + b.d_); s++) iplist.pollLast();
                ed_z = b.z_ + b.d_;
              }
            } else {
              IJ.log("warning");
              iplist.clear();
              st_z = b.z_;
              ed_z = b.z_ + b.d_;
              for (int s = st_z; s < ed_z; s++)
                iplist.add(stack.getProcessor(imp.getStackIndex(ch + 1, s + 1, f + 1)));
            }

            if (iplist.size() != b.d_) {
              IJ.log("Stack Error");
              return;
            }

            //						int zz = st_z;

            int bsize = 0;
            byte[] bdata = new byte[b.w_ * b.h_ * b.d_ * bdepth / 8];
            Iterator<ImageProcessor> ipite = iplist.iterator();
            while (ipite.hasNext()) {

              //							ImageProcessor tsip = tsst.getProcessor(zz+1);

              ImageProcessor ip = ipite.next();
              ip.setRoi(b.x_, b.y_, b.w_, b.h_);
              if (bdepth == 8) {
                byte[] data = (byte[]) ip.crop().getPixels();
                System.arraycopy(data, 0, bdata, bsize, data.length);
                bsize += data.length;
              } else if (bdepth == 16) {
                ByteBuffer buffer = ByteBuffer.allocate(b.w_ * b.h_ * bdepth / 8);
                buffer.order(ByteOrder.LITTLE_ENDIAN);
                short[] data = (short[]) ip.crop().getPixels();
                for (short e : data) buffer.putShort(e);
                System.arraycopy(buffer.array(), 0, bdata, bsize, buffer.array().length);
                bsize += buffer.array().length;
              } else if (bdepth == 32) {
                ByteBuffer buffer = ByteBuffer.allocate(b.w_ * b.h_ * bdepth / 8);
                buffer.order(ByteOrder.LITTLE_ENDIAN);
                float[] data = (float[]) ip.crop().getPixels();
                for (float e : data) buffer.putFloat(e);
                System.arraycopy(buffer.array(), 0, bdata, bsize, buffer.array().length);
                bsize += buffer.array().length;
              }
            }

            String filename =
                basename
                    + "_Lv"
                    + String.valueOf(l)
                    + "_Ch"
                    + String.valueOf(ch)
                    + "_Fr"
                    + String.valueOf(f)
                    + "_ID"
                    + String.valueOf(i);

            int offset = bytecount;
            int datasize = bdata.length;

            if (filetype == "RAW") {
              int dummy = -1;
              // do nothing
            }
            if (filetype == "JPEG" && bdepth == 8) {
              try {
                DataBufferByte db = new DataBufferByte(bdata, datasize);
                Raster raster = Raster.createPackedRaster(db, b.w_, b.h_ * b.d_, 8, null);
                BufferedImage img =
                    new BufferedImage(b.w_, b.h_ * b.d_, BufferedImage.TYPE_BYTE_GRAY);
                img.setData(raster);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
                String format = "jpg";
                Iterator<javax.imageio.ImageWriter> iter =
                    ImageIO.getImageWritersByFormatName("jpeg");
                javax.imageio.ImageWriter writer = iter.next();
                ImageWriteParam iwp = writer.getDefaultWriteParam();
                iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                iwp.setCompressionQuality((float) jpeg_quality * 0.01f);
                writer.setOutput(ios);
                writer.write(null, new IIOImage(img, null, null), iwp);
                // ImageIO.write(img, format, baos);
                bdata = baos.toByteArray();
                datasize = bdata.length;
              } catch (IOException e) {
                e.printStackTrace();
                return;
              }
            }
            if (filetype == "ZLIB") {
              byte[] tmpdata = new byte[b.w_ * b.h_ * b.d_ * bdepth / 8];
              Deflater compresser = new Deflater();
              compresser.setInput(bdata);
              compresser.setLevel(Deflater.DEFAULT_COMPRESSION);
              compresser.setStrategy(Deflater.DEFAULT_STRATEGY);
              compresser.finish();
              datasize = compresser.deflate(tmpdata);
              bdata = tmpdata;
              compresser.end();
            }

            if (bytecount + datasize > sizelimit && bytecount > 0) {
              BufferedOutputStream fis = null;
              try {
                File file = new File(directory + current_dataname);
                fis = new BufferedOutputStream(new FileOutputStream(file));
                fis.write(packed_data, 0, bytecount);
              } catch (IOException e) {
                e.printStackTrace();
                return;
              } finally {
                try {
                  if (fis != null) fis.close();
                } catch (IOException e) {
                  e.printStackTrace();
                  return;
                }
              }
              filecount++;
              current_dataname = base_dataname + "_data" + filecount;
              bytecount = 0;
              offset = 0;
              System.arraycopy(bdata, 0, packed_data, bytecount, datasize);
              bytecount += datasize;
            } else {
              System.arraycopy(bdata, 0, packed_data, bytecount, datasize);
              bytecount += datasize;
            }

            Element filenode = doc.createElement("File");
            filenode.setAttribute("filename", current_dataname);
            filenode.setAttribute("channel", String.valueOf(ch));
            filenode.setAttribute("frame", String.valueOf(f));
            filenode.setAttribute("brickID", String.valueOf(i));
            filenode.setAttribute("offset", String.valueOf(offset));
            filenode.setAttribute("datasize", String.valueOf(datasize));
            filenode.setAttribute("filetype", String.valueOf(filetype));

            fsnode.appendChild(filenode);

            curbricknum++;
            IJ.showProgress((double) (curbricknum) / (double) (totalbricknum));
          }
          if (bytecount > 0) {
            BufferedOutputStream fis = null;
            try {
              File file = new File(directory + current_dataname);
              fis = new BufferedOutputStream(new FileOutputStream(file));
              fis.write(packed_data, 0, bytecount);
            } catch (IOException e) {
              e.printStackTrace();
              return;
            } finally {
              try {
                if (fis != null) fis.close();
              } catch (IOException e) {
                e.printStackTrace();
                return;
              }
            }
          }
        }
      }

      for (int i = 0; i < bricks.size(); i++) {
        Brick b = bricks.get(i);
        Element bricknode = doc.createElement("Brick");
        bricknode.setAttribute("id", String.valueOf(i));
        bricknode.setAttribute("st_x", String.valueOf(b.x_));
        bricknode.setAttribute("st_y", String.valueOf(b.y_));
        bricknode.setAttribute("st_z", String.valueOf(b.z_));
        bricknode.setAttribute("width", String.valueOf(b.w_));
        bricknode.setAttribute("height", String.valueOf(b.h_));
        bricknode.setAttribute("depth", String.valueOf(b.d_));
        brksnode.appendChild(bricknode);

        Element tboxnode = doc.createElement("tbox");
        tboxnode.setAttribute("x0", String.valueOf(b.tx0_));
        tboxnode.setAttribute("y0", String.valueOf(b.ty0_));
        tboxnode.setAttribute("z0", String.valueOf(b.tz0_));
        tboxnode.setAttribute("x1", String.valueOf(b.tx1_));
        tboxnode.setAttribute("y1", String.valueOf(b.ty1_));
        tboxnode.setAttribute("z1", String.valueOf(b.tz1_));
        bricknode.appendChild(tboxnode);

        Element bboxnode = doc.createElement("bbox");
        bboxnode.setAttribute("x0", String.valueOf(b.bx0_));
        bboxnode.setAttribute("y0", String.valueOf(b.by0_));
        bboxnode.setAttribute("z0", String.valueOf(b.bz0_));
        bboxnode.setAttribute("x1", String.valueOf(b.bx1_));
        bboxnode.setAttribute("y1", String.valueOf(b.by1_));
        bboxnode.setAttribute("z1", String.valueOf(b.bz1_));
        bricknode.appendChild(bboxnode);
      }

      if (l < lv - 1) {
        imp = WindowManager.getImage(lvImgTitle.get(l + 1));
        int[] newdims = imp.getDimensions();
        imageW = newdims[0];
        imageH = newdims[1];
        imageD = newdims[3];
        xspc = orgxspc * ((double) orgW / (double) imageW);
        yspc = orgyspc * ((double) orgH / (double) imageH);
        zspc = orgzspc * ((double) orgD / (double) imageD);
        bdepth = imp.getBitDepth();
      }
    }

    File newXMLfile = new File(directory + basename + ".vvd");
    writeXML(newXMLfile, doc);

    for (int l = 1; l < lv; l++) {
      imp = WindowManager.getImage(lvImgTitle.get(l));
      imp.changes = false;
      imp.close();
    }
  }

  public static Document newXMLDocument() {
    DocumentBuilder dbuilder = null;
    try {
      dbuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
    } catch (ParserConfigurationException e) {
      e.printStackTrace();
      return null;
    }
    return dbuilder.newDocument();
  }

  public static boolean writeXML(File file, Document document) {

    javax.xml.transform.Transformer transformer = null;
    try {
      transformer = TransformerFactory.newInstance().newTransformer();
    } catch (TransformerConfigurationException e) {
      e.printStackTrace();
      return false;
    }

    transformer.setOutputProperty("indent", "yes");
    transformer.setOutputProperty(OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, "2");
    transformer.setOutputProperty("encoding", "UTF-8");

    try {
      transformer.transform(new DOMSource(document), new StreamResult(file));
    } catch (TransformerException e) {
      e.printStackTrace();
      return false;
    }

    return true;
  }

  // Fast way to check for power of two
  public static boolean IsPowerOf2(int n) {
    return (n & (n - 1)) == 0;
  }

  // Returns a number Greater Than or Equal to dim
  // that is an exact power of 2
  // Used for determining what size of texture to
  // allocate to store an image
  public static int Pow2(int dim) {
    if (IsPowerOf2(dim)) return dim;
    int val = 4;
    while (val < dim) val = val << 1;
    return val;
  }
}
/**
 * Implements ImageJ's Analyze Particles command.
 *
 * <p>
 *
 * <pre>
 * for each line do
 * for each pixel in this line do
 * if the pixel value is "inside" the threshold range then
 * trace the edge to mark the object
 * do the measurement
 * fill the object with a color outside the threshold range
 * else
 * continue the scan
 * </pre>
 */
public class ParticleAnalyzer implements PlugInFilter, Measurements {

  /** Display results in the ImageJ console. */
  public static final int SHOW_RESULTS = 1;

  /** Obsolete */
  public static final int SHOW_SUMMARY = 2;

  /** Display image containing outlines of measured particles. */
  public static final int SHOW_OUTLINES = 4;

  /** Do not measure particles touching edge of image. */
  public static final int EXCLUDE_EDGE_PARTICLES = 8;

  /** Display image containing grayscales masks that identify measured particles. */
  public static final int SHOW_ROI_MASKS = 16;

  /** Display a progress bar. */
  public static final int SHOW_PROGRESS = 32;

  /** Clear ImageJ console before starting. */
  public static final int CLEAR_WORKSHEET = 64;

  /** Record starting coordinates so outline can be recreated later using doWand(x,y). */
  public static final int RECORD_STARTS = 128;

  /** Display a summary. */
  public static final int DISPLAY_SUMMARY = 256;

  /** Do not display particle outline image. */
  public static final int SHOW_NONE = 512;

  /** Flood fill to ignore interior holes. */
  public static final int INCLUDE_HOLES = 1024;

  /** Add particles to ROI Manager. */
  public static final int ADD_TO_MANAGER = 2048;

  /** Display image containing binary masks of measured particles. */
  public static final int SHOW_MASKS = 4096;

  /** Use 4-connected particle tracing. */
  public static final int FOUR_CONNECTED = 8192;

  /** Replace original image with masks. */
  public static final int IN_SITU_SHOW = 16384;

  /** Display particle outlines as an overlay. */
  public static final int SHOW_OVERLAY_OUTLINES = 32768;

  /** Display filled particle as an overlay. */
  public static final int SHOW_OVERLAY_MASKS = 65536;

  static final String OPTIONS = "ap.options";

  static final int BYTE = 0, SHORT = 1, FLOAT = 2, RGB = 3;
  static final double DEFAULT_MIN_SIZE = 0.0;
  static final double DEFAULT_MAX_SIZE = Double.POSITIVE_INFINITY;

  private static double staticMinSize = 0.0;
  private static double staticMaxSize = DEFAULT_MAX_SIZE;
  private static boolean pixelUnits;
  private static int staticOptions = Prefs.getInt(OPTIONS, CLEAR_WORKSHEET);
  private static String[] showStrings = {
    "Nothing",
    "Outlines",
    "Bare Outlines",
    "Ellipses",
    "Masks",
    "Count Masks",
    "Overlay Outlines",
    "Overlay Masks"
  };
  private static double staticMinCircularity = 0.0, staticMaxCircularity = 1.0;
  private static String prevHdr;

  protected static final int NOTHING = 0,
      OUTLINES = 1,
      BARE_OUTLINES = 2,
      ELLIPSES = 3,
      MASKS = 4,
      ROI_MASKS = 5,
      OVERLAY_OUTLINES = 6,
      OVERLAY_MASKS = 7;
  protected static int staticShowChoice;
  protected ImagePlus imp;
  protected ResultsTable rt;
  protected Analyzer analyzer;
  protected int slice;
  protected boolean processStack;
  protected boolean showResults,
      excludeEdgeParticles,
      showSizeDistribution,
      resetCounter,
      showProgress,
      recordStarts,
      displaySummary,
      floodFill,
      addToManager,
      inSituShow;

  private String summaryHdr = "Slice\tCount\tTotal Area\tAverage Size\tArea Fraction";
  private double level1, level2;
  private double minSize, maxSize;
  private double minCircularity, maxCircularity;
  private int showChoice;
  private int options;
  private int measurements;
  private Calibration calibration;
  private String arg;
  private double fillColor;
  private boolean thresholdingLUT;
  private ImageProcessor drawIP;
  private int width, height;
  private boolean canceled;
  private ImageStack outlines;
  private IndexColorModel customLut;
  private int particleCount;
  private int maxParticleCount = 0;
  private int totalCount;
  private TextWindow tw;
  private Wand wand;
  private int imageType, imageType2;
  private boolean roiNeedsImage;
  private int minX, maxX, minY, maxY;
  private ImagePlus redirectImp;
  private ImageProcessor redirectIP;
  private PolygonFiller pf;
  private Roi saveRoi;
  private int beginningCount;
  private Rectangle r;
  private ImageProcessor mask;
  private double totalArea;
  private FloodFiller ff;
  private Polygon polygon;
  private RoiManager roiManager;
  private ImagePlus outputImage;
  private boolean hideOutputImage;
  private int roiType;
  private int wandMode = Wand.LEGACY_MODE;
  private Overlay overlay;
  boolean blackBackground;
  private static int defaultFontSize = 9;
  private static int nextFontSize = defaultFontSize;
  private static int nextLineWidth = 1;
  private int fontSize = nextFontSize;
  private int lineWidth = nextLineWidth;

  /**
   * Constructs a ParticleAnalyzer.
   *
   * @param options a flag word created by Oring SHOW_RESULTS, EXCLUDE_EDGE_PARTICLES, etc.
   * @param measurements a flag word created by ORing constants defined in the Measurements
   *     interface
   * @param rt a ResultsTable where the measurements will be stored
   * @param minSize the smallest particle size in pixels
   * @param maxSize the largest particle size in pixels
   * @param minCirc minimum circularity
   * @param maxCirc maximum circularity
   */
  public ParticleAnalyzer(
      int options,
      int measurements,
      ResultsTable rt,
      double minSize,
      double maxSize,
      double minCirc,
      double maxCirc) {
    this.options = options;
    this.measurements = measurements;
    this.rt = rt;
    if (this.rt == null) this.rt = new ResultsTable();
    this.minSize = minSize;
    this.maxSize = maxSize;
    this.minCircularity = minCirc;
    this.maxCircularity = maxCirc;
    slice = 1;
    if ((options & SHOW_ROI_MASKS) != 0) showChoice = ROI_MASKS;
    if ((options & SHOW_OVERLAY_OUTLINES) != 0) showChoice = OVERLAY_OUTLINES;
    if ((options & SHOW_OVERLAY_MASKS) != 0) showChoice = OVERLAY_MASKS;
    if ((options & SHOW_OUTLINES) != 0) showChoice = OUTLINES;
    if ((options & SHOW_MASKS) != 0) showChoice = MASKS;
    if ((options & SHOW_NONE) != 0) showChoice = NOTHING;
    if ((options & FOUR_CONNECTED) != 0) {
      wandMode = Wand.FOUR_CONNECTED;
      options |= INCLUDE_HOLES;
    }
    nextFontSize = defaultFontSize;
    nextLineWidth = 1;
  }

  /** Constructs a ParticleAnalyzer using the default min and max circularity values (0 and 1). */
  public ParticleAnalyzer(
      int options, int measurements, ResultsTable rt, double minSize, double maxSize) {
    this(options, measurements, rt, minSize, maxSize, 0.0, 1.0);
  }

  /** Default constructor */
  public ParticleAnalyzer() {
    slice = 1;
  }

  public int setup(String arg, ImagePlus imp) {
    this.arg = arg;
    this.imp = imp;
    IJ.register(ParticleAnalyzer.class);
    if (imp == null) {
      IJ.noImage();
      return DONE;
    }
    if (imp.getBitDepth() == 24 && !isThresholdedRGB(imp)) {
      IJ.error(
          "Particle Analyzer",
          "RGB images must be thresholded using\n" + "Image>Adjust>Color Threshold.");
      return DONE;
    }
    if (!showDialog()) return DONE;
    int baseFlags = DOES_ALL + NO_CHANGES + NO_UNDO;
    int flags = IJ.setupDialog(imp, baseFlags);
    processStack = (flags & DOES_STACKS) != 0;
    slice = 0;
    saveRoi = imp.getRoi();
    if (saveRoi != null && saveRoi.getType() != Roi.RECTANGLE && saveRoi.isArea())
      polygon = saveRoi.getPolygon();
    imp.startTiming();
    nextFontSize = defaultFontSize;
    nextLineWidth = 1;
    return flags;
  }

  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);
    }
  }

  /** Displays a modal options dialog. */
  public boolean showDialog() {
    Calibration cal = imp != null ? imp.getCalibration() : (new Calibration());
    double unitSquared = cal.pixelWidth * cal.pixelHeight;
    if (pixelUnits) unitSquared = 1.0;
    if (Macro.getOptions() != null) {
      boolean oldMacro = updateMacroOptions();
      if (oldMacro) unitSquared = 1.0;
      staticMinSize = 0.0;
      staticMaxSize = DEFAULT_MAX_SIZE;
      staticMinCircularity = 0.0;
      staticMaxCircularity = 1.0;
      staticShowChoice = NOTHING;
    }
    GenericDialog gd = new GenericDialog("Analyze Particles");
    minSize = staticMinSize;
    maxSize = staticMaxSize;
    minCircularity = staticMinCircularity;
    maxCircularity = staticMaxCircularity;
    showChoice = staticShowChoice;
    if (maxSize == 999999) maxSize = DEFAULT_MAX_SIZE;
    options = staticOptions;
    String unit = cal.getUnit();
    boolean scaled = cal.scaled();
    if (unit.equals("inch")) {
      unit = "pixel";
      unitSquared = 1.0;
      scaled = false;
      pixelUnits = true;
    }
    String units = unit + "^2";
    int places = 0;
    double cmin = minSize * unitSquared;
    if ((int) cmin != cmin) places = 2;
    double cmax = maxSize * unitSquared;
    if ((int) cmax != cmax && cmax != DEFAULT_MAX_SIZE) places = 2;
    String minStr = ResultsTable.d2s(cmin, places);
    if (minStr.indexOf("-") != -1) {
      for (int i = places; i <= 6; i++) {
        minStr = ResultsTable.d2s(cmin, i);
        if (minStr.indexOf("-") == -1) break;
      }
    }
    String maxStr = ResultsTable.d2s(cmax, places);
    if (maxStr.indexOf("-") != -1) {
      for (int i = places; i <= 6; i++) {
        maxStr = ResultsTable.d2s(cmax, i);
        if (maxStr.indexOf("-") == -1) break;
      }
    }
    if (scaled) gd.setInsets(5, 0, 0);
    gd.addStringField("Size (" + units + "):", minStr + "-" + maxStr, 12);
    if (scaled) {
      gd.setInsets(0, 40, 5);
      gd.addCheckbox("Pixel units", pixelUnits);
    }
    gd.addStringField("Circularity:", IJ.d2s(minCircularity) + "-" + IJ.d2s(maxCircularity), 12);
    gd.addChoice("Show:", showStrings, showStrings[showChoice]);
    String[] labels = new String[8];
    boolean[] states = new boolean[8];
    labels[0] = "Display results";
    states[0] = (options & SHOW_RESULTS) != 0;
    labels[1] = "Exclude on edges";
    states[1] = (options & EXCLUDE_EDGE_PARTICLES) != 0;
    labels[2] = "Clear results";
    states[2] = (options & CLEAR_WORKSHEET) != 0;
    labels[3] = "Include holes";
    states[3] = (options & INCLUDE_HOLES) != 0;
    labels[4] = "Summarize";
    states[4] = (options & DISPLAY_SUMMARY) != 0;
    labels[5] = "Record starts";
    states[5] = (options & RECORD_STARTS) != 0;
    labels[6] = "Add to Manager";
    states[6] = (options & ADD_TO_MANAGER) != 0;
    labels[7] = "In_situ Show";
    states[7] = (options & IN_SITU_SHOW) != 0;
    gd.addCheckboxGroup(4, 2, labels, states);
    gd.addHelp(IJ.URL + "/docs/menus/analyze.html#ap");
    gd.showDialog();
    if (gd.wasCanceled()) return false;

    String size = gd.getNextString(); // min-max size
    if (scaled) pixelUnits = gd.getNextBoolean();
    if (pixelUnits) unitSquared = 1.0;
    else unitSquared = cal.pixelWidth * cal.pixelHeight;
    String[] minAndMax = Tools.split(size, " -");
    double mins = gd.parseDouble(minAndMax[0]);
    double maxs = minAndMax.length == 2 ? gd.parseDouble(minAndMax[1]) : Double.NaN;
    minSize = Double.isNaN(mins) ? DEFAULT_MIN_SIZE : mins / unitSquared;
    maxSize = Double.isNaN(maxs) ? DEFAULT_MAX_SIZE : maxs / unitSquared;
    if (minSize < DEFAULT_MIN_SIZE) minSize = DEFAULT_MIN_SIZE;
    if (maxSize < minSize) maxSize = DEFAULT_MAX_SIZE;
    staticMinSize = minSize;
    staticMaxSize = maxSize;

    minAndMax = Tools.split(gd.getNextString(), " -"); // min-max circularity
    double minc = gd.parseDouble(minAndMax[0]);
    double maxc = minAndMax.length == 2 ? gd.parseDouble(minAndMax[1]) : Double.NaN;
    minCircularity = Double.isNaN(minc) ? 0.0 : minc;
    maxCircularity = Double.isNaN(maxc) ? 1.0 : maxc;
    if (minCircularity < 0.0 || minCircularity > 1.0) minCircularity = 0.0;
    if (maxCircularity < minCircularity || maxCircularity > 1.0) maxCircularity = 1.0;
    if (minCircularity == 1.0 && maxCircularity == 1.0) minCircularity = 0.0;
    staticMinCircularity = minCircularity;
    staticMaxCircularity = maxCircularity;

    if (gd.invalidNumber()) {
      IJ.error("Bins invalid.");
      canceled = true;
      return false;
    }
    showChoice = gd.getNextChoiceIndex();
    staticShowChoice = showChoice;
    if (gd.getNextBoolean()) options |= SHOW_RESULTS;
    else options &= ~SHOW_RESULTS;
    if (gd.getNextBoolean()) options |= EXCLUDE_EDGE_PARTICLES;
    else options &= ~EXCLUDE_EDGE_PARTICLES;
    if (gd.getNextBoolean()) options |= CLEAR_WORKSHEET;
    else options &= ~CLEAR_WORKSHEET;
    if (gd.getNextBoolean()) options |= INCLUDE_HOLES;
    else options &= ~INCLUDE_HOLES;
    if (gd.getNextBoolean()) options |= DISPLAY_SUMMARY;
    else options &= ~DISPLAY_SUMMARY;
    if (gd.getNextBoolean()) options |= RECORD_STARTS;
    else options &= ~RECORD_STARTS;
    if (gd.getNextBoolean()) options |= ADD_TO_MANAGER;
    else options &= ~ADD_TO_MANAGER;
    if (gd.getNextBoolean()) options |= IN_SITU_SHOW;
    else options &= ~IN_SITU_SHOW;
    staticOptions = options;
    options |= SHOW_PROGRESS;
    if ((options & DISPLAY_SUMMARY) != 0)
      Analyzer.setMeasurements(Analyzer.getMeasurements() | AREA);
    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();
  }

  boolean updateMacroOptions() {
    String options = Macro.getOptions();
    int index = options.indexOf("maximum=");
    if (index == -1) return false;
    index += 8;
    int len = options.length();
    while (index < len - 1 && options.charAt(index) != ' ') index++;
    if (index == len - 1) return false;
    int min = (int) Tools.parseDouble(Macro.getValue(options, "minimum", "1"));
    int max = (int) Tools.parseDouble(Macro.getValue(options, "maximum", "999999"));
    options = "size=" + min + "-" + max + options.substring(index, len);
    Macro.setOptions(options);
    return true;
  }

  /** Performs particle analysis on the specified image. Returns false if there is an error. */
  public boolean analyze(ImagePlus imp) {
    return analyze(imp, imp.getProcessor());
  }

  /**
   * 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;
  }

  void updateSliceSummary() {
    int slices = imp.getStackSize();
    float[] areas = rt.getColumn(ResultsTable.AREA);
    if (areas == null) areas = new float[0];
    String label = imp.getTitle();
    if (slices > 1) {
      label = imp.getStack().getShortSliceLabel(slice);
      label = label != null && !label.equals("") ? label : "" + slice;
    }
    String aLine = null;
    double sum = 0.0;
    int start = areas.length - particleCount;
    if (start < 0) return;
    for (int i = start; i < areas.length; i++) sum += areas[i];
    int places = Analyzer.getPrecision();
    Calibration cal = imp.getCalibration();
    String total = "\t" + ResultsTable.d2s(sum, places);
    String average = "\t" + ResultsTable.d2s(sum / particleCount, places);
    String fraction = "\t" + ResultsTable.d2s(sum * 100.0 / totalArea, 1);
    aLine = label + "\t" + particleCount + total + average + fraction;
    aLine = addMeans(aLine, areas.length > 0 ? start : -1);
    if (slices == 1) {
      Frame frame = WindowManager.getFrame("Summary");
      if (frame != null && (frame instanceof TextWindow) && summaryHdr.equals(prevHdr))
        tw = (TextWindow) frame;
    }
    if (tw == null) {
      String title = slices == 1 ? "Summary" : "Summary of " + imp.getTitle();
      tw = new TextWindow(title, summaryHdr, aLine, 450, 300);
      prevHdr = summaryHdr;
    } else tw.append(aLine);
  }

  String addMeans(String line, int start) {
    if ((measurements & MEAN) != 0) line = addMean(ResultsTable.MEAN, line, start);
    if ((measurements & MODE) != 0) line = addMean(ResultsTable.MODE, line, start);
    if ((measurements & PERIMETER) != 0) line = addMean(ResultsTable.PERIMETER, line, start);
    if ((measurements & ELLIPSE) != 0) {
      line = addMean(ResultsTable.MAJOR, line, start);
      line = addMean(ResultsTable.MINOR, line, start);
      line = addMean(ResultsTable.ANGLE, line, start);
    }
    if ((measurements & SHAPE_DESCRIPTORS) != 0) {
      line = addMean(ResultsTable.CIRCULARITY, line, start);
      line = addMean(ResultsTable.SOLIDITY, line, start);
    }
    if ((measurements & FERET) != 0) {
      line = addMean(ResultsTable.FERET, line, start);
      line = addMean(ResultsTable.FERET_X, line, start);
      line = addMean(ResultsTable.FERET_Y, line, start);
      line = addMean(ResultsTable.FERET_ANGLE, line, start);
      line = addMean(ResultsTable.MIN_FERET, line, start);
    }
    if ((measurements & INTEGRATED_DENSITY) != 0)
      line = addMean(ResultsTable.INTEGRATED_DENSITY, line, start);
    if ((measurements & MEDIAN) != 0) line = addMean(ResultsTable.MEDIAN, line, start);
    if ((measurements & SKEWNESS) != 0) line = addMean(ResultsTable.SKEWNESS, line, start);
    if ((measurements & KURTOSIS) != 0) line = addMean(ResultsTable.KURTOSIS, line, start);
    return line;
  }

  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;
  }

  String n(double n) {
    String s;
    if (Math.round(n) == n) s = ResultsTable.d2s(n, 0);
    else s = ResultsTable.d2s(n, Analyzer.getPrecision());
    return "\t" + s;
  }

  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;
  }

  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;
  }

  int counter = 0;

  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);
  }

  ImageStatistics getStatistics(ImageProcessor ip, int mOptions, Calibration cal) {
    switch (imageType2) {
      case BYTE:
        return new ByteStatistics(ip, mOptions, cal);
      case SHORT:
        return new ShortStatistics(ip, mOptions, cal);
      case FLOAT:
        return new FloatStatistics(ip, mOptions, cal);
      case RGB:
        return new ColorStatistics(ip, mOptions, cal);
      default:
        return null;
    }
  }

  /**
   * Saves statistics for one particle in a results table. This is a method subclasses may want to
   * override.
   */
  protected void saveResults(ImageStatistics stats, Roi roi) {
    analyzer.saveResults(stats, roi);
    if (recordStarts) {
      rt.addValue("XStart", stats.xstart);
      rt.addValue("YStart", stats.ystart);
    }
    if (addToManager) {
      if (roiManager == null) {
        if (Macro.getOptions() != null && Interpreter.isBatchMode())
          roiManager = Interpreter.getBatchModeRoiManager();
        if (roiManager == null) {
          Frame frame = WindowManager.getFrame("ROI Manager");
          if (frame == null) IJ.run("ROI Manager...");
          frame = WindowManager.getFrame("ROI Manager");
          if (frame == null || !(frame instanceof RoiManager)) {
            addToManager = false;
            return;
          }
          roiManager = (RoiManager) frame;
        }
        if (resetCounter) roiManager.runCommand("reset");
      }
      if (imp.getStackSize() > 1) roi.setPosition(imp.getCurrentSlice());
      if (lineWidth != 1) roi.setStrokeWidth(lineWidth);
      roiManager.add(imp, roi, rt.getCounter());
    }
    if (showResults) rt.addResults();
  }

  /**
   * Draws a selected particle in a separate image. This is another method subclasses may want to
   * override.
   */
  protected void drawParticle(
      ImageProcessor drawIP, Roi roi, ImageStatistics stats, ImageProcessor mask) {
    switch (showChoice) {
      case MASKS:
        drawFilledParticle(drawIP, roi, mask);
        break;
      case OUTLINES:
      case BARE_OUTLINES:
      case OVERLAY_OUTLINES:
      case OVERLAY_MASKS:
        drawOutline(drawIP, roi, rt.getCounter());
        break;
      case ELLIPSES:
        drawEllipse(drawIP, stats, rt.getCounter());
        break;
      case ROI_MASKS:
        drawRoiFilledParticle(drawIP, roi, mask, rt.getCounter());
        break;
      default:
    }
  }

  void drawFilledParticle(ImageProcessor ip, Roi roi, ImageProcessor mask) {
    // IJ.write(roi.getBounds()+" "+mask.length);
    ip.setRoi(roi.getBounds());
    ip.fill(mask);
  }

  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);
      }
    }
  }

  void drawEllipse(ImageProcessor ip, ImageStatistics stats, int count) {
    stats.drawEllipse(ip);
  }

  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 showResults() {
    int count = rt.getCounter();
    // if (count==0) return;
    boolean lastSlice = !processStack || slice == imp.getStackSize();
    if ((showChoice == OVERLAY_OUTLINES || showChoice == OVERLAY_MASKS) && slice == 1 && count > 0)
      imp.setOverlay(overlay);
    else if (outlines != null && lastSlice) {
      String title = imp != null ? imp.getTitle() : "Outlines";
      String prefix;
      if (showChoice == MASKS) prefix = "Mask of ";
      else if (showChoice == ROI_MASKS) prefix = "Count Masks of ";
      else prefix = "Drawing of ";
      outlines.update(drawIP);
      outputImage = new ImagePlus(prefix + title, outlines);
      if (inSituShow) {
        if (imp.getStackSize() == 1) Undo.setup(Undo.TRANSFORM, imp);
        imp.setStack(null, outputImage.getStack());
      } else if (!hideOutputImage) outputImage.show();
    }
    if (showResults && !processStack) {
      TextPanel tp = IJ.getTextPanel();
      if (beginningCount > 0 && tp != null && tp.getLineCount() != count) rt.show("Results");
      Analyzer.firstParticle = beginningCount;
      Analyzer.lastParticle = Analyzer.getCounter() - 1;
    } else Analyzer.firstParticle = Analyzer.lastParticle = 0;
  }

  /**
   * Returns the "Outlines", "Masks", "Elipses" or "Count Masks" image, or null if "Nothing" is
   * selected in the "Show:" menu.
   */
  public ImagePlus getOutputImage() {
    return outputImage;
  }

  /** Set 'hideOutputImage' true to not display the "Show:" image. */
  public void setHideOutputImage(boolean hideOutputImage) {
    this.hideOutputImage = hideOutputImage;
  }

  /** Sets the size of the font used to label outlines in the next particle analyzer instance. */
  public static void setFontSize(int size) {
    nextFontSize = size;
  }

  /** Sets the outline line width for the next particle analyzer instance. */
  public static void setLineWidth(int width) {
    nextLineWidth = width;
  }

  int getColumnID(String name) {
    int id = rt.getFreeColumn(name);
    if (id == ResultsTable.COLUMN_IN_USE) id = rt.getColumnIndex(name);
    return id;
  }

  void makeCustomLut() {
    IndexColorModel cm = (IndexColorModel) LookUpTable.createGrayscaleColorModel(false);
    byte[] reds = new byte[256];
    byte[] greens = new byte[256];
    byte[] blues = new byte[256];
    cm.getReds(reds);
    cm.getGreens(greens);
    cm.getBlues(blues);
    reds[1] = (byte) 255;
    greens[1] = (byte) 0;
    blues[1] = (byte) 0;
    customLut = new IndexColorModel(8, 256, reds, greens, blues);
  }

  /** Called once when ImageJ quits. */
  public static void savePreferences(Properties prefs) {
    prefs.put(OPTIONS, Integer.toString(staticOptions));
  }
}
Пример #15
0
  /**
   * If 'applet' is not null, creates a new ImageJ frame that runs as an applet. If 'mode' is
   * ImageJ.EMBEDDED and 'applet is null, creates an embedded (non-standalone) version of ImageJ.
   */
  public ImageJ(java.applet.Applet applet, int mode) {
    super("ImageJ");
    if ((mode & DEBUG) != 0) IJ.setDebugMode(true);
    mode = mode & 255;
    if (IJ.debugMode) IJ.log("ImageJ starting in debug mode: " + mode);
    embedded = applet == null && (mode == EMBEDDED || mode == NO_SHOW);
    this.applet = applet;
    String err1 = Prefs.load(this, applet);
    setBackground(backgroundColor);
    Menus m = new Menus(this, applet);
    String err2 = m.addMenuBar();
    m.installPopupMenu(this);
    setLayout(new BorderLayout());

    // Tool bar
    toolbar = new Toolbar();
    toolbar.addKeyListener(this);
    add("Center", toolbar);

    // Status bar
    statusBar = new Panel();
    statusBar.setLayout(new BorderLayout());
    statusBar.setForeground(Color.black);
    statusBar.setBackground(backgroundColor);
    statusLine = new JLabel();
    statusLine.setFont(new Font("SansSerif", Font.PLAIN, 13));
    statusLine.addKeyListener(this);
    statusLine.addMouseListener(this);
    statusBar.add("Center", statusLine);
    progressBar = new ProgressBar(120, 20);
    progressBar.addKeyListener(this);
    progressBar.addMouseListener(this);
    statusBar.add("East", progressBar);
    add("South", statusBar);

    IJ.init(this, applet);
    addKeyListener(this);
    addWindowListener(this);
    setFocusTraversalKeysEnabled(false);
    m.installStartupMacroSet(); // add custom tools
    runStartupMacro();

    Point loc = getPreferredLocation();
    Dimension tbSize = toolbar.getPreferredSize();
    setCursor(Cursor.getDefaultCursor()); // work-around for JDK 1.1.8 bug
    if (mode != NO_SHOW) {
      if (IJ.isWindows())
        try {
          setIcon();
        } catch (Exception e) {
        }
      setLocation(loc.x, loc.y);
      setResizable(!IJ.isMacOSX());
      pack();
      setVisible(true);
    }
    if (err1 != null) IJ.error(err1);
    if (err2 != null) {
      IJ.error(err2);
      IJ.runPlugIn("ij.plugin.ClassChecker", "");
    }
    if (IJ.isMacintosh() && applet == null) {
      Object qh = null;
      qh = IJ.runPlugIn("MacAdapter", "");
      if (qh == null) IJ.runPlugIn("QuitHandler", "");
    }
    if (applet == null) IJ.runPlugIn("ij.plugin.DragAndDrop", "");
    String str = m.getMacroCount() == 1 ? " macro" : " macros";
    IJ.showStatus(version() + m.getPluginCount() + " commands; " + m.getMacroCount() + str);
    configureProxy();
    if (applet == null) loadCursors();
  }
Пример #16
0
  /**
   * If 'applet' is not null, creates a new ImageJ frame that runs as an applet. If 'mode' is
   * ImageJ.EMBEDDED and 'applet is null, creates an embedded (non-standalone) version of ImageJ.
   */
  public ImageJ(java.applet.Applet applet, int mode) {
    super("ImageJ");
    embedded = applet == null && (mode == EMBEDDED || mode == NO_SHOW);
    this.applet = applet;
    String err1 = Prefs.load(this, applet);
    if (IJ.isLinux()) {
      backgroundColor = new Color(240, 240, 240);
      setBackground(backgroundColor);
    }
    Menus m = new Menus(this, applet);
    String err2 = m.addMenuBar();
    m.installPopupMenu(this);
    setLayout(new GridLayout(2, 1));

    // Tool bar
    toolbar = new Toolbar();
    toolbar.addKeyListener(this);
    add(toolbar);

    // Status bar
    statusBar = new Panel();
    statusBar.setLayout(new BorderLayout());
    statusBar.setForeground(Color.black);
    statusBar.setBackground(backgroundColor);
    statusLine = new Label();
    statusLine.setFont(SansSerif12);
    statusLine.addKeyListener(this);
    statusLine.addMouseListener(this);
    statusBar.add("Center", statusLine);
    progressBar = new ProgressBar(120, 20);
    progressBar.addKeyListener(this);
    progressBar.addMouseListener(this);
    statusBar.add("East", progressBar);
    statusBar.setSize(toolbar.getPreferredSize());
    add(statusBar);

    IJ.init(this, applet);
    addKeyListener(this);
    addWindowListener(this);
    setFocusTraversalKeysEnabled(false);

    Point loc = getPreferredLocation();
    Dimension tbSize = toolbar.getPreferredSize();
    int ijWidth = tbSize.width + 10;
    int ijHeight = 100;
    setCursor(Cursor.getDefaultCursor()); // work-around for JDK 1.1.8 bug
    if (mode != NO_SHOW) {
      if (IJ.isWindows())
        try {
          setIcon();
        } catch (Exception e) {
        }
      setBounds(loc.x, loc.y, ijWidth, ijHeight); // needed for pack to work
      setLocation(loc.x, loc.y);
      pack();
      setResizable(!(IJ.isMacintosh() || IJ.isWindows())); // make resizable on Linux
      show();
    }
    if (err1 != null) IJ.error(err1);
    if (err2 != null) {
      IJ.error(err2);
      IJ.runPlugIn("ij.plugin.ClassChecker", "");
    }
    m.installStartupMacroSet();
    if (IJ.isMacintosh() && applet == null) {
      Object qh = null;
      qh = IJ.runPlugIn("MacAdapter", "");
      if (qh == null) IJ.runPlugIn("QuitHandler", "");
    }
    if (applet == null) IJ.runPlugIn("ij.plugin.DragAndDrop", "");
    String str = m.getMacroCount() == 1 ? " macro" : " macros";
    IJ.showStatus(version() + m.getPluginCount() + " commands; " + m.getMacroCount() + str);
    // if (applet==null && !embedded && Prefs.runSocketListener)
    //	new SocketListener();
    configureProxy();
    if (applet == null) loadCursors();
  }
Пример #17
0
 // Delete preferences file when ImageJ quits
 private void reset() {
   if (IJ.showMessageWithCancel(
       "Reset Preferences", "Preferences will be reset when ImageJ restarts."))
     Prefs.resetPreferences();
 }
  public void run(ImageProcessor ip) {
    String[] imageNames = getOpenImageNames();
    if (imageNames[0] == "None") {
      IJ.error("need at least 2 binary open images");
      return;
    }
    double previousMinOverlap = Prefs.get("BVTB.BinaryFeatureExtractor.minOverlap", 0);
    boolean previousCombine = Prefs.get("BVTB.BinaryFeatureExtractor.combine", false);

    GenericDialog gd = new GenericDialog("Binary Feature Extractor");
    gd.addChoice("Objects image", imageNames, imageNames[0]);
    gd.addChoice("Selector image", imageNames, imageNames[1]);
    gd.addNumericField("Object_overlap in % (0=off)", previousMinOverlap, 0, 9, "");
    gd.addCheckbox("Combine objects and selectors", previousCombine);
    gd.addCheckbox("Count output", true);
    gd.addCheckbox("Analysis tables", false);
    gd.showDialog();
    if (gd.wasCanceled()) {
      return;
    }
    String objectsImgTitle = gd.getNextChoice();
    String selectorsImgTitle = gd.getNextChoice();
    double minOverlap = gd.getNextNumber();
    boolean combineImages = gd.getNextBoolean();
    boolean showCountOutput = gd.getNextBoolean();
    boolean showAnalysis = gd.getNextBoolean();
    if (gd.invalidNumber() || minOverlap < 0 || minOverlap > 100) {
      IJ.error("invalid number");
      return;
    }
    Prefs.set("BVTB.BinaryFeatureExtractor.minOverlap", minOverlap);
    Prefs.set("BVTB.BinaryFeatureExtractor.combine", combineImages);

    if (objectsImgTitle.equals(selectorsImgTitle)) {
      IJ.error("images need to be different");
      return;
    }

    ImagePlus objectsImp = WindowManager.getImage(objectsImgTitle);
    ImageProcessor objectsIP = objectsImp.getProcessor();
    ImagePlus selectorsImp = WindowManager.getImage(selectorsImgTitle);
    ImageProcessor selectorsIP = selectorsImp.getProcessor();

    if (!objectsIP.isBinary() || !selectorsIP.isBinary()) {
      IJ.error("works with 8-bit binary images only");
      return;
    }

    if ((objectsImp.getWidth() != selectorsImp.getWidth())
        || objectsImp.getHeight() != selectorsImp.getHeight()) {
      IJ.error("images need to be of the same size");
      return;
    }

    // close any existing RoiManager before instantiating a new one for this analysis
    RoiManager oldRM = RoiManager.getInstance2();
    if (oldRM != null) {
      oldRM.close();
    }

    RoiManager objectsRM = new RoiManager(true);
    ResultsTable objectsRT = new ResultsTable();
    ParticleAnalyzer analyzeObjects =
        new ParticleAnalyzer(analyzerOptions, measurementFlags, objectsRT, 0.0, 999999999.9);
    analyzeObjects.setRoiManager(objectsRM);

    analyzeObjects.analyze(objectsImp);
    objectsRM.runCommand("Show None");
    int objectNumber = objectsRT.getCounter();

    Roi[] objectRoi = objectsRM.getRoisAsArray();

    ResultsTable measureSelectorsRT = new ResultsTable();
    Analyzer overlapAnalyzer = new Analyzer(selectorsImp, measurementFlags, measureSelectorsRT);

    ImagePlus outputImp =
        IJ.createImage("output", "8-bit black", objectsImp.getWidth(), objectsImp.getHeight(), 1);
    ImageProcessor outputIP = outputImp.getProcessor();

    double[] measuredOverlap = new double[objectNumber];

    outputIP.setValue(255.0);
    for (int o = 0; o < objectNumber; o++) {
      selectorsImp.killRoi();
      selectorsImp.setRoi(objectRoi[o]);
      overlapAnalyzer.measure();
      measuredOverlap[o] = measureSelectorsRT.getValue("%Area", o);
      if (minOverlap != 0.0 && measuredOverlap[o] >= minOverlap) {
        outputIP.fill(objectRoi[o]);
        finalCount++;
      } else if (minOverlap == 0.0 && measuredOverlap[o] > 0.0) {
        outputIP.fill(objectRoi[o]);
        finalCount++;
      }
    }
    // measureSelectorsRT.show("Objects");

    selectorsImp.killRoi();
    RoiManager selectorRM = new RoiManager(true);
    ResultsTable selectorRT = new ResultsTable();
    ParticleAnalyzer.setRoiManager(selectorRM);
    ParticleAnalyzer analyzeSelectors =
        new ParticleAnalyzer(analyzerOptions, measurementFlags, selectorRT, 0.0, 999999999.9);
    analyzeSelectors.analyze(selectorsImp);
    selectorRM.runCommand("Show None");
    int selectorNumber = selectorRT.getCounter();

    if (combineImages) {
      outputImp.updateAndDraw();
      Roi[] selectorRoi = selectorRM.getRoisAsArray();

      ResultsTable measureObjectsRT = new ResultsTable();
      Analyzer selectorAnalyzer = new Analyzer(outputImp, measurementFlags, measureObjectsRT);

      double[] selectorOverlap = new double[selectorNumber];
      outputIP.setValue(255.0);
      for (int s = 0; s < selectorNumber; s++) {
        outputImp.killRoi();
        outputImp.setRoi(selectorRoi[s]);
        selectorAnalyzer.measure();
        selectorOverlap[s] = measureObjectsRT.getValue("%Area", s);
        if (selectorOverlap[s] > 0.0d) {
          outputIP.fill(selectorRoi[s]);
        }
      }
      selectorRoi = null;
      selectorAnalyzer = null;
      measureObjectsRT = null;
    }
    // selectorRT.show("Selectors");
    outputImp.killRoi();
    String outputImageTitle = WindowManager.getUniqueName("Extracted_" + objectsImgTitle);
    outputImp.setTitle(outputImageTitle);
    outputImp.show();
    outputImp.changes = true;

    if (showCountOutput) {
      String[] openTextWindows = WindowManager.getNonImageTitles();
      boolean makeNewTable = true;
      for (int w = 0; w < openTextWindows.length; w++) {
        if (openTextWindows[w].equals("BFE_Results")) {
          makeNewTable = false;
        }
      }

      TextWindow existingCountTable = ResultsTable.getResultsWindow();
      if (makeNewTable) {
        countTable = new ResultsTable();
        countTable.setPrecision(0);
        countTable.setValue("Image", 0, outputImageTitle);
        countTable.setValue("Objects", 0, objectNumber);
        countTable.setValue("Selectors", 0, selectorNumber);
        countTable.setValue("Extracted", 0, finalCount);
        countTable.show("BFE_Results");
      } else {
        IJ.renameResults("BFE_Results", "Results");
        countTable = ResultsTable.getResultsTable();
        countTable.setPrecision(0);
        countTable.incrementCounter();
        countTable.addValue("Image", outputImageTitle);
        countTable.addValue("Objects", objectNumber);
        countTable.addValue("Selectors", selectorNumber);
        countTable.addValue("Extracted", finalCount);
        IJ.renameResults("Results", "BFE_Results");
        countTable.show("BFE_Results");
      }
    }

    if (showAnalysis) {
      ResultsTable extractedRT = new ResultsTable();
      ParticleAnalyzer analyzeExtracted =
          new ParticleAnalyzer(
              ParticleAnalyzer.CLEAR_WORKSHEET | ParticleAnalyzer.RECORD_STARTS,
              measurementFlags,
              extractedRT,
              0.0,
              999999999.9);
      analyzeExtracted.analyze(outputImp);
      objectsRT.show("Objects");
      selectorRT.show("Selectors");
      extractedRT.show("Extracted");
    } else {
      objectsRT = null;
      selectorRT = null;
    }

    objectsRM = null;
    measureSelectorsRT = null;
    analyzeObjects = null;
    overlapAnalyzer = null;
    objectRoi = null;
    selectorRM = null;

    objectsImp.killRoi();
    objectsImp.changes = false;
    selectorsImp.changes = false;
  }
Пример #19
0
  public void run(String arg) {
    imp = IJ.getImage();
    int stackSize = imp.getStackSize();
    if (imp == null) {
      IJ.noImage();
      return;
    }

    //  Make sure input image is a stack.
    if (stackSize == 1) {
      IJ.error("Z Project", "Stack required");
      return;
    }

    //  Check for inverting LUT.
    if (imp.getProcessor().isInvertedLut()) {
      if (!IJ.showMessageWithCancel("ZProjection", lutMessage)) return;
    }

    // Set default bounds.
    int channels = imp.getNChannels();
    int frames = imp.getNFrames();
    int slices = imp.getNSlices();
    isHyperstack =
        imp.isHyperStack()
            || (ij.macro.Interpreter.isBatchMode()
                && ((frames > 1 && frames < stackSize) || (slices > 1 && slices < stackSize)));
    boolean simpleComposite = channels == stackSize;
    if (simpleComposite) isHyperstack = false;
    startSlice = 1;
    if (isHyperstack) {
      int nSlices = imp.getNSlices();
      if (nSlices > 1) stopSlice = nSlices;
      else stopSlice = imp.getNFrames();
    } else stopSlice = stackSize;

    // Build control dialog
    GenericDialog gd = buildControlDialog(startSlice, stopSlice);
    gd.showDialog();
    if (gd.wasCanceled()) return;

    if (!imp.lock()) return; // exit if in use
    long tstart = System.currentTimeMillis();
    setStartSlice((int) gd.getNextNumber());
    setStopSlice((int) gd.getNextNumber());
    method = gd.getNextChoiceIndex();
    Prefs.set(METHOD_KEY, method);
    if (isHyperstack) {
      allTimeFrames = imp.getNFrames() > 1 && imp.getNSlices() > 1 ? gd.getNextBoolean() : false;
      doHyperStackProjection(allTimeFrames);
    } else if (imp.getType() == ImagePlus.COLOR_RGB) doRGBProjection(true);
    else doProjection(true);

    if (arg.equals("") && projImage != null) {
      long tstop = System.currentTimeMillis();
      projImage.setCalibration(imp.getCalibration());
      if (simpleComposite) IJ.run(projImage, "Grays", "");
      projImage.show("ZProjector: " + IJ.d2s((tstop - tstart) / 1000.0, 2) + " seconds");
    }

    imp.unlock();
    IJ.register(ZProjector.class);
    return;
  }