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