New upstream version 1.53j
Nilesh Patra
2 years ago
186 | 186 | } |
187 | 187 | |
188 | 188 | public void updateAndDraw() { |
189 | if (win==null) { | |
190 | img = null; | |
191 | return; | |
192 | } | |
189 | 193 | updateImage(); |
190 | 194 | if (win!=null) |
191 | 195 | notifyListeners(UPDATED); |
2461 | 2461 | return new String[0]; |
2462 | 2462 | for (int i=0; i<lutsMenu.getItemCount(); i++) { |
2463 | 2463 | MenuItem menuItem = lutsMenu.getItem(i); |
2464 | if (menuItem.getActionListeners().length == 0) // separator? | |
2465 | continue; | |
2466 | 2464 | String label = menuItem.getLabel(); |
2467 | if (label.equals("Invert LUT") || label.equals("Apply LUT")) | |
2465 | if (label.equals("-") || label.equals("Invert LUT") || label.equals("Apply LUT")) | |
2468 | 2466 | continue; |
2469 | 2467 | String command = (String)commands.get(label); |
2470 | 2468 | if (command==null || command.startsWith("ij.plugin.LutLoader")) |
76 | 76 | MouseListener, KeyListener, WindowListener, ItemListener, Runnable { |
77 | 77 | |
78 | 78 | /** Plugins should call IJ.getVersion() or IJ.getFullVersion() to get the version string. */ |
79 | public static final String VERSION = "1.53i"; | |
80 | public static final String BUILD = ""; //44 | |
79 | public static final String VERSION = "1.53j"; | |
80 | public static final String BUILD = ""; //46 | |
81 | 81 | public static Color backgroundColor = new Color(237,237,237); |
82 | 82 | /** SansSerif, 12-point, plain font. */ |
83 | 83 | public static final Font SansSerif12 = new Font("SansSerif", Font.PLAIN, 12); |
733 | 733 | if (!noGUI && (ij==null || (ij!=null && !ij.isShowing()))) { |
734 | 734 | ij = new ImageJ(null, mode); |
735 | 735 | ij.exitWhenQuitting = true; |
736 | } | |
736 | } else if (batchMode && noGUI) | |
737 | Prefs.load(null, null); | |
737 | 738 | int macros = 0; |
738 | 739 | for (int i=0; i<nArgs; i++) { |
739 | 740 | String arg = args[i]; |
297 | 297 | nothing if there is no window associated with |
298 | 298 | this image (i.e. show() has not been called).*/ |
299 | 299 | public synchronized void updateAndDraw() { |
300 | if (win==null) { | |
301 | img = null; | |
302 | return; | |
303 | } | |
300 | 304 | if (stack!=null && !stack.isVirtual() && currentSlice>=1 && currentSlice<=stack.size()) { |
301 | 305 | if (stack.size()>1 && win!=null && !(win instanceof StackWindow)) { |
302 | 306 | setStack(stack); //adds scroll bar if stack size has changed to >1 |
421 | 425 | /** ImageCanvas.paint() calls this method when the |
422 | 426 | ImageProcessor has generated a new image. */ |
423 | 427 | public void updateImage() { |
428 | if (win==null) { | |
429 | img = null; | |
430 | return; | |
431 | } | |
424 | 432 | if (ip!=null) |
425 | 433 | img = ip.createImage(); |
426 | 434 | } |
428 | 436 | /** Closes the window, if any, that is displaying this image. */ |
429 | 437 | public void hide() { |
430 | 438 | if (win==null) { |
439 | img = null; | |
431 | 440 | Interpreter.removeBatchModeImage(this); |
432 | 441 | return; |
433 | 442 | } |
542 | 551 | img = ip.createImage(); |
543 | 552 | return img; |
544 | 553 | } |
545 | ||
554 | ||
546 | 555 | /** Returns a copy of this image as an 8-bit or RGB BufferedImage. |
547 | 556 | * @see ij.process.ShortProcessor#get16BitBufferedImage |
548 | 557 | */ |
3346 | 3355 | public void setBorderColor(Color borderColor) { |
3347 | 3356 | this.borderColor = borderColor; |
3348 | 3357 | } |
3358 | ||
3359 | public boolean windowActivated() { | |
3360 | return this.activated; | |
3361 | } | |
3349 | 3362 | |
3350 | 3363 | } |
825 | 825 | /** Returns the specified ImageJ menu (e.g., "File>New") or null if it is not found. */ |
826 | 826 | public static Menu getImageJMenu(String menuPath) { |
827 | 827 | if (menus==null) |
828 | IJ.init(); | |
829 | if (menus==null) | |
828 | 830 | return null; |
829 | 831 | if (menus.get(menuPath)!=null) |
830 | 832 | return getMenu(menuPath, false); |
1332 | 1334 | |
1333 | 1335 | /** Returns the hashtable that associates commands with plugins. */ |
1334 | 1336 | public static Hashtable getCommands() { |
1337 | if (pluginsTable==null) | |
1338 | IJ.init(); | |
1335 | 1339 | return pluginsTable; |
1336 | 1340 | } |
1337 | 1341 | |
1660 | 1664 | |
1661 | 1665 | /** Called once when ImageJ quits. */ |
1662 | 1666 | public static void savePreferences(Properties prefs) { |
1667 | if (pluginsPrefs==null) | |
1668 | return; | |
1663 | 1669 | int index = 0; |
1664 | 1670 | for (Enumeration en=pluginsPrefs.elements(); en.hasMoreElements();) { |
1665 | 1671 | String key = "plugin" + (index/10)%10 + index%10; |
287 | 287 | public static String load(Object ij, Applet applet) { |
288 | 288 | if (ImageJDir==null) |
289 | 289 | ImageJDir = System.getProperty("user.dir"); |
290 | InputStream f = null; | |
291 | try { // Look for IJ_Props.txt in ImageJ folder | |
292 | f = new FileInputStream(ImageJDir+"/"+PROPS_NAME); | |
293 | propertiesPath = ImageJDir+"/"+PROPS_NAME; | |
294 | } catch (FileNotFoundException e) { | |
295 | f = null; | |
296 | } | |
297 | if (f==null) { | |
298 | // Look in ij.jar if not found in ImageJ folder | |
299 | f = ij.getClass().getResourceAsStream("/"+PROPS_NAME); | |
300 | } | |
301 | if (applet!=null) | |
302 | return loadAppletProps(f, applet); | |
303 | if (f==null) | |
304 | return PROPS_NAME+" not found in ij.jar or in "+ImageJDir; | |
305 | f = new BufferedInputStream(f); | |
306 | try { | |
307 | props.load(f); | |
308 | f.close(); | |
309 | } catch (IOException e) { | |
310 | return("Error loading "+PROPS_NAME); | |
311 | } | |
312 | imagesURL = props.getProperty(IJ.isJava18()?"images.location":"images.location2"); | |
290 | if (ij!=null) { | |
291 | InputStream f = null; | |
292 | try { // Look for IJ_Props.txt in ImageJ folder | |
293 | f = new FileInputStream(ImageJDir+"/"+PROPS_NAME); | |
294 | propertiesPath = ImageJDir+"/"+PROPS_NAME; | |
295 | } catch (FileNotFoundException e) { | |
296 | f = null; | |
297 | } | |
298 | if (f==null) { | |
299 | // Look in ij.jar if not found in ImageJ folder | |
300 | f = ij.getClass().getResourceAsStream("/"+PROPS_NAME); | |
301 | } | |
302 | if (applet!=null) | |
303 | return loadAppletProps(f, applet); | |
304 | if (f==null) | |
305 | return PROPS_NAME+" not found in ij.jar or in "+ImageJDir; | |
306 | f = new BufferedInputStream(f); | |
307 | try { | |
308 | props.load(f); | |
309 | f.close(); | |
310 | } catch (IOException e) { | |
311 | return("Error loading "+PROPS_NAME); | |
312 | } | |
313 | imagesURL = props.getProperty(IJ.isJava18()?"images.location":"images.location2"); | |
314 | } | |
313 | 315 | loadPreferences(); |
314 | 316 | loadOptions(); |
315 | 317 | guiScale = get(GUI_SCALE, 1.0); |
461 | 463 | if (!IJ.isLinux()) dialogCancelButtonOnRight = false; |
462 | 464 | saveOptions(prefs); |
463 | 465 | savePluginPrefs(prefs); |
464 | IJ.getInstance().savePreferences(prefs); | |
466 | ImageJ ij = IJ.getInstance(); | |
467 | if (ij!=null) | |
468 | ij.savePreferences(prefs); | |
465 | 469 | Menus.savePreferences(prefs); |
466 | 470 | ParticleAnalyzer.savePreferences(prefs); |
467 | 471 | Analyzer.savePreferences(prefs); |
721 | 721 | } |
722 | 722 | |
723 | 723 | /** Adds one or two (side by side) text areas. |
724 | * @param text1 initial contents of the first text area | |
725 | * @param text2 initial contents of the second text area or null | |
726 | * @param rows the number of rows | |
727 | * @param columns the number of columns | |
724 | * Append "SCROLLBARS_VERTICAL_ONLY" to the text of | |
725 | * the first text area to get vertical scrollbars | |
726 | * and "SCROLLBARS_BOTH" to get both vertical and | |
727 | * horizontal scrollbars. | |
728 | * @param text1 initial contents of the first text area | |
729 | * @param text2 initial contents of the second text area or null | |
730 | * @param rows the number of rows | |
731 | * @param columns the number of columns | |
728 | 732 | */ |
729 | 733 | public void addTextAreas(String text1, String text2, int rows, int columns) { |
730 | 734 | if (textArea1!=null) return; |
731 | 735 | Panel panel = new Panel(); |
736 | int scrollbars = TextArea.SCROLLBARS_NONE; | |
737 | if (text1.endsWith("SCROLLBARS_BOTH")) { | |
738 | scrollbars = TextArea.SCROLLBARS_BOTH; | |
739 | text1 = text1.substring(0, text1.length()-15); | |
740 | } | |
741 | if (text1.endsWith("SCROLLBARS_VERTICAL_ONLY")) { | |
742 | scrollbars = TextArea.SCROLLBARS_VERTICAL_ONLY; | |
743 | text1 = text1.substring(0, text1.length()-24); | |
744 | } | |
732 | 745 | Font font = new Font("SansSerif", Font.PLAIN, (int)(14*Prefs.getGuiScale())); |
733 | textArea1 = new TextArea(text1,rows,columns,TextArea.SCROLLBARS_NONE); | |
746 | textArea1 = new TextArea(text1,rows,columns,scrollbars); | |
734 | 747 | if (IJ.isLinux()) textArea1.setBackground(Color.white); |
735 | 748 | textArea1.setFont(font); |
736 | 749 | textArea1.addTextListener(this); |
737 | 750 | panel.add(textArea1); |
738 | 751 | if (text2!=null) { |
739 | textArea2 = new TextArea(text2,rows,columns,TextArea.SCROLLBARS_NONE); | |
752 | textArea2 = new TextArea(text2,rows,columns,scrollbars); | |
740 | 753 | if (IJ.isLinux()) textArea2.setBackground(Color.white); |
741 | 754 | textArea2.setFont(font); |
742 | 755 | panel.add(textArea2); |
72 | 72 | if (dialogD.height > 0.80*screenD.height && screenD.height>400) //max 80% of screen height |
73 | 73 | dialogD.height = (int)(0.80*screenD.height); |
74 | 74 | setSize(dialogD); |
75 | GUI.centerOnImageJScreen(this); | |
76 | if (!modal) WindowManager.addWindow(this); | |
77 | show(); | |
75 | GUI.centerOnImageJScreen(this); | |
76 | if (!modal) { | |
77 | WindowManager.addWindow(this); | |
78 | show(); | |
79 | } | |
80 | final JScrollBar verticalScrollBar = scrollPane.getVerticalScrollBar(); | |
81 | if (verticalScrollBar!=null) { | |
82 | EventQueue.invokeLater(new Runnable() { | |
83 | public void run() { | |
84 | verticalScrollBar.setValue(verticalScrollBar.getMinimum()); //start scrollbar at top | |
85 | } | |
86 | }); | |
87 | } | |
88 | if (modal) show(); | |
78 | 89 | } |
79 | 90 | |
80 | 91 | public void actionPerformed(ActionEvent e) { |
993 | 993 | public void setLabel(int index, String label) { |
994 | 994 | if (index < 0) index = allPlotObjects.size() + index; |
995 | 995 | allPlotObjects.get(index).label = label; |
996 | } | |
997 | ||
998 | /** Removes NaNs from the xValues and yValues arrays of all plot objects. */ | |
999 | public void removeNaNs() { | |
1000 | for (PlotObject plotObj : allPlotObjects){ | |
1001 | if(plotObj != null && plotObj.xValues!= null && plotObj.yValues != null ){ | |
1002 | int oldSize = plotObj.xValues.length; | |
1003 | float[] xVals = new float[oldSize]; | |
1004 | float[] yVals = new float[oldSize]; | |
1005 | int newSize = 0; | |
1006 | for (int kk = 0; kk < oldSize; kk++) { | |
1007 | if (!Float.isNaN(plotObj.xValues[kk] + plotObj.yValues[kk])) { | |
1008 | xVals[newSize] = plotObj.xValues[kk]; | |
1009 | yVals[newSize] = plotObj.yValues[kk]; | |
1010 | newSize++; | |
1011 | } | |
1012 | } | |
1013 | if (newSize < oldSize) { | |
1014 | plotObj.xValues = new float[newSize]; | |
1015 | plotObj.yValues = new float[newSize]; | |
1016 | System.arraycopy(xVals, 0, plotObj.xValues, 0, newSize); | |
1017 | System.arraycopy(yVals, 0, plotObj.yValues, 0, newSize); | |
1018 | } | |
1019 | } | |
1020 | } | |
996 | 1021 | } |
997 | 1022 | |
998 | 1023 | /** Returns an array of the available curve types ("Line", "Bar", "Circle", etc). */ |
365 | 365 | return this; |
366 | 366 | } |
367 | 367 | |
368 | /** Adds a point at the specified stack position. */ | |
369 | public void addPoint(double x, double y, int position) { | |
370 | if (counters==null) { | |
371 | counters = new short[100]; | |
372 | positions = new int[100]; | |
373 | } | |
374 | addPoint(null, x, y); | |
375 | positions[nPoints-1] = position; | |
376 | } | |
377 | ||
368 | 378 | protected void deletePoint(int index) { |
369 | 379 | super.deletePoint(index); |
370 | 380 | if (index>=0 && index<=nPoints && counters!=null) { |
404 | 414 | if (rt!=null && WindowManager.getFrame(getCountsTitle())!=null) |
405 | 415 | displayCounts(); |
406 | 416 | } |
407 | ||
417 | ||
408 | 418 | /** Returns the index of the current counter. */ |
409 | 419 | public int getCounter() { |
410 | 420 | return counter; |
95 | 95 | |
96 | 96 | private static Color foregroundColor = Prefs.getColor(Prefs.FCOLOR,Color.white); |
97 | 97 | private static Color backgroundColor = Prefs.getColor(Prefs.BCOLOR,Color.black); |
98 | private static double foregroundValue = Double.NaN; | |
99 | private static double backgroundValue = Double.NaN; | |
98 | 100 | private static int ovalType = OVAL_ROI; |
99 | 101 | private static int rectType = RECT_ROI; |
100 | 102 | private static boolean multiPointMode = Prefs.multiPointMode; |
935 | 937 | if (c==null) |
936 | 938 | return; |
937 | 939 | foregroundColor = c; |
940 | foregroundValue = Double.NaN; | |
938 | 941 | IJ.notifyEventListeners(IJEventListener.FOREGROUND_COLOR_CHANGED); |
939 | 942 | if (instance==null) |
940 | 943 | return; |
953 | 956 | public static void setBackgroundColor(Color c) { |
954 | 957 | if (c!=null) { |
955 | 958 | backgroundColor = c; |
959 | backgroundValue = Double.NaN; | |
956 | 960 | repaintTool(DROPPER); |
957 | 961 | IJ.notifyEventListeners(IJEventListener.BACKGROUND_COLOR_CHANGED); |
958 | 962 | } |
963 | } | |
964 | ||
965 | public static double getForegroundValue() { | |
966 | return foregroundValue; | |
967 | } | |
968 | ||
969 | public static void setForegroundValue(double value) { | |
970 | if (value>=0) { | |
971 | int v = (int)value; | |
972 | if (v>255) v=255; | |
973 | setForegroundColor(new Color(v,v,v)); | |
974 | } | |
975 | foregroundValue = value; | |
976 | } | |
977 | ||
978 | public static double getBackgroundValue() { | |
979 | return backgroundValue; | |
980 | } | |
981 | ||
982 | public static void setBackgroundValue(double value) { | |
983 | if (value>=0) { | |
984 | int v = (int)value; | |
985 | if (v>255) v=255; | |
986 | setBackgroundColor(new Color(v,v,v)); | |
987 | } | |
988 | backgroundValue = value; | |
959 | 989 | } |
960 | 990 | |
961 | 991 | private static void setRoiColor(Color c) { |
278 | 278 | case EXEC: str = exec(); break; |
279 | 279 | case LIST: str = doList(); break; |
280 | 280 | case DEBUG: str = debug(); break; |
281 | case IJ_CALL: str = ijCall(); break; | |
281 | case IJ_CALL: str = doIJ(); break; | |
282 | 282 | case GET_RESULT_STRING: str = getResultString(null); break; |
283 | 283 | case TRIM: str = trim(); break; |
284 | 284 | default: |
693 | 693 | IJ.setKeyUp(IJ.ALL_KEYS); |
694 | 694 | shiftKeyDown = altKeyDown = false; |
695 | 695 | } |
696 | ||
696 | ||
697 | 697 | private void selectWindow() { |
698 | 698 | String title = getStringArg(); |
699 | 699 | if (resultsPending && "Results".equals(title)) { |
2218 | 2218 | return getPlotLimits(currentPlot); |
2219 | 2219 | } else if (name.equals("freeze")) { |
2220 | 2220 | currentPlot.setFrozen(getBooleanArg()); |
2221 | return Double.NaN; | |
2222 | } else if (name.equals("removeNaNs")) { | |
2223 | currentPlot.removeNaNs(); | |
2221 | 2224 | return Double.NaN; |
2222 | 2225 | } else if (name.equals("addLegend") || name.equals("setLegend")) { |
2223 | 2226 | return addPlotLegend(currentPlot); |
3886 | 3889 | } |
3887 | 3890 | } |
3888 | 3891 | } |
3889 | ||
3892 | ||
3890 | 3893 | String trim() { |
3891 | 3894 | if (interp.nextToken()=='=') |
3892 | 3895 | interp.error("'trim' is a reserved word"); |
5111 | 5114 | return Toolbar.getForegroundColor().getRGB()&0xffffff; |
5112 | 5115 | else if (key.equals("rgb.background")) |
5113 | 5116 | return Toolbar.getBackgroundColor().getRGB()&0xffffff; |
5114 | else if (key.contains("foreground")) | |
5115 | return getColorValue(Toolbar.getForegroundColor()); | |
5116 | else if (key.contains("background")) | |
5117 | return getColorValue(Toolbar.getBackgroundColor()); | |
5118 | else if (key.equals("font.size")) { | |
5117 | else if (key.contains("foreground")) { | |
5118 | double value = Toolbar.getForegroundValue(); | |
5119 | if (Double.isNaN(value)) | |
5120 | return getColorValue(Toolbar.getForegroundColor()); | |
5121 | else | |
5122 | return value; | |
5123 | } else if (key.contains("background")) { | |
5124 | double value = Toolbar.getBackgroundValue(); | |
5125 | if (Double.isNaN(value)) | |
5126 | return getColorValue(Toolbar.getBackgroundColor()); | |
5127 | else | |
5128 | return value; | |
5129 | } else if (key.equals("font.size")) { | |
5119 | 5130 | resetImage(); |
5120 | 5131 | ImageProcessor ip = getProcessor(); |
5121 | 5132 | setFont(ip); |
5740 | 5751 | props = new Properties(); |
5741 | 5752 | String name = interp.tokenString; |
5742 | 5753 | if (name.equals("doFit")) |
5743 | return fitCurve(); | |
5754 | return fitCurve(false); | |
5755 | if (name.equals("doWeightedFit")) | |
5756 | return fitCurve(true); | |
5744 | 5757 | else if (name.equals("getEquation")) |
5745 | 5758 | return getEquation(); |
5746 | 5759 | else if (name.equals("nEquations")) { |
5776 | 5789 | return Double.NaN; |
5777 | 5790 | } |
5778 | 5791 | |
5779 | double fitCurve() { | |
5792 | double fitCurve(boolean withWeights) { | |
5780 | 5793 | interp.getLeftParen(); |
5781 | 5794 | int fit = -1; |
5782 | 5795 | String name = null; |
5800 | 5813 | double[] x = getNextArray(); |
5801 | 5814 | interp.getComma(); |
5802 | 5815 | double[] y = getNumericArray(); |
5816 | double[] weights = null; | |
5817 | if (withWeights) { | |
5818 | interp.getComma(); | |
5819 | weights = getNumericArray(); | |
5820 | } | |
5803 | 5821 | if (interp.nextToken()==',') { |
5804 | 5822 | interp.getComma(); |
5805 | 5823 | initialValues = getNumericArray(); |
5810 | 5828 | if (x.length==0) |
5811 | 5829 | interp.error("Zero length array"); |
5812 | 5830 | fitter = new CurveFitter(x, y); |
5831 | if (withWeights) | |
5832 | fitter.setWeights(weights); | |
5813 | 5833 | fitter.setStatusAndEsc(null, true); |
5814 | 5834 | if (fit==-1 && name!=null) { |
5815 | 5835 | Interpreter instance = Interpreter.getInstance(); |
5969 | 5989 | contains = str.contains(filter); |
5970 | 5990 | if (contains) |
5971 | 5991 | list.add(a1[i]); |
5972 | } | |
5992 | } | |
5973 | 5993 | } |
5974 | 5994 | return (Variable[])list.toArray(new Variable[list.size()]); |
5975 | 5995 | } |
5976 | ||
5996 | ||
5977 | 5997 | Variable[] deleteArrayIndex() { |
5978 | 5998 | interp.getLeftParen(); |
5979 | 5999 | Variable[] arr1 = getArray(); |
6486 | 6506 | resetImage(); |
6487 | 6507 | } |
6488 | 6508 | |
6489 | private String ijCall() { | |
6509 | private String doIJ() { | |
6490 | 6510 | interp.getToken(); |
6491 | 6511 | if (interp.token!='.') |
6492 | 6512 | interp.error("'.' expected"); |
6514 | 6534 | renameResults(); |
6515 | 6535 | else if (name.equals("getFullVersion")) |
6516 | 6536 | {interp.getParens(); return ""+IJ.getFullVersion();} |
6537 | else if (name.equals("checksum")) | |
6538 | return checksum(); | |
6517 | 6539 | else |
6518 | 6540 | interp.error("Unrecognized IJ function name"); |
6519 | 6541 | return null; |
6542 | } | |
6543 | ||
6544 | private String checksum(){ | |
6545 | String method = getFirstString(); | |
6546 | String src = getLastString(); | |
6547 | method = method.toUpperCase(); | |
6548 | if (method.contains("FILE") && method.contains("MD5")) | |
6549 | return Tools.getHash("MD5", true, src); | |
6550 | if (method.contains("STRING") && method.contains("MD5")) | |
6551 | return Tools.getHash("MD5", false, src); | |
6552 | if (method.contains("FILE") && method.contains("SHA-256")) | |
6553 | return Tools.getHash("SHA-256", true, src); | |
6554 | if (method.contains("STRING") && method.contains("SHA-256")) | |
6555 | return Tools.getHash("SHA-256", false, src); | |
6556 | interp.error("must contain 'file' or 'string' and 'MD5' or 'SHA-256'"); | |
6557 | return "0"; | |
6520 | 6558 | } |
6521 | 6559 | |
6522 | 6560 | private String pad() { |
6735 | 6773 | interp.error("Unrecognized function name"); |
6736 | 6774 | return Double.NaN; |
6737 | 6775 | } |
6738 | ||
6776 | ||
6739 | 6777 | private double activateSelection(ImagePlus imp, Overlay overlay, boolean wait) { |
6740 | 6778 | int index = (int)getArg(); |
6741 | 6779 | int size = overlay.size(); |
6764 | 6802 | IJ.wait(5); |
6765 | 6803 | } while (ic!=null && ic.getPaintPending() && System.currentTimeMillis()-t0<50); |
6766 | 6804 | } else |
6767 | imp.setRoi(roi, !Interpreter.isBatchMode()); | |
6805 | imp.setRoi(roi, !Interpreter.isBatchMode()); | |
6768 | 6806 | if (Analyzer.addToOverlay()) |
6769 | 6807 | ResultsTable.selectRow(roi); |
6770 | 6808 | return Double.NaN; |
6771 | } | |
6772 | ||
6809 | } | |
6810 | ||
6773 | 6811 | private double getOverlayElementBounds(Overlay overlay) { |
6774 | 6812 | int index = (int)getFirstArg(); |
6775 | 6813 | Variable x = getNextVariable(); |
7186 | 7224 | resultsPending = false; |
7187 | 7225 | return null; |
7188 | 7226 | } |
7189 | ||
7227 | ||
7190 | 7228 | private Variable resetTable() { |
7191 | 7229 | String title = getTitleArg(); |
7192 | 7230 | ResultsTable rt = null; |
7929 | 7967 | interp.error("Unrecognized RoiManager function"); |
7930 | 7968 | return null; |
7931 | 7969 | } |
7932 | ||
7970 | ||
7933 | 7971 | private Variable doProperty() { |
7934 | 7972 | interp.getToken(); |
7935 | 7973 | if (interp.token!='.') |
7989 | 8027 | interp.error("Unrecognized Property function"); |
7990 | 8028 | return null; |
7991 | 8029 | } |
7992 | ||
8030 | ||
7993 | 8031 | private void setPropertiesFromString(Properties props) { |
7994 | 8032 | String list = getStringArg(); |
7995 | 8033 | props.clear(); |
8019 | 8057 | } |
8020 | 8058 | return sb.toString(); |
8021 | 8059 | } |
8022 | ||
8060 | ||
8023 | 8061 | static boolean isStringFunction(String name, int type) { |
8024 | 8062 | boolean isString = false; |
8025 | 8063 | switch (type) { |
8058 | 8096 | } |
8059 | 8097 | return isString; |
8060 | 8098 | } |
8061 | ||
8099 | ||
8062 | 8100 | private Variable doImage() { |
8063 | 8101 | interp.getToken(); |
8064 | 8102 | if (interp.token!='.') |
8078 | 8116 | interp.getParens(); |
8079 | 8117 | imp.copy(); |
8080 | 8118 | return null; |
8081 | } else if (name.equals("paste")) { | |
8119 | } else if (name.equals("paste")) { | |
8082 | 8120 | int x = (int)getFirstArg(); |
8083 | 8121 | int y = (int)getNextArg(); |
8084 | 8122 | String mode = null; |
8119 | 8157 | return setForegroundOrBackground(true); |
8120 | 8158 | } else if (name.equals("setBackground")) { |
8121 | 8159 | return setForegroundOrBackground(false); |
8160 | } else if (name.equals("setForegroundValue")) { | |
8161 | Toolbar.setForegroundValue(getArg()); | |
8162 | return null; | |
8163 | } else if (name.equals("setBackgroundValue")) { | |
8164 | Toolbar.setBackgroundValue(getArg()); | |
8165 | return null; | |
8122 | 8166 | } else if (name.equals("toString")) { |
8123 | 8167 | int red = (int)getFirstArg(); |
8124 | 8168 | int green = (int)getNextArg(); |
8143 | 8187 | interp.error("Unrecognized Color function"); |
8144 | 8188 | return null; |
8145 | 8189 | } |
8146 | ||
8190 | ||
8147 | 8191 | private Variable setForegroundOrBackground(boolean foreground) { |
8148 | 8192 | interp.getLeftParen(); |
8149 | 8193 | Color color = null; |
8157 | 8201 | int blue = (int)getLastArg(); |
8158 | 8202 | color = Colors.toColor(red, green, blue); |
8159 | 8203 | } |
8160 | if (foreground) | |
8204 | if (foreground) | |
8161 | 8205 | Toolbar.setForegroundColor(color); |
8162 | 8206 | else |
8163 | 8207 | Toolbar.setBackgroundColor(color); |
8165 | 8209 | } |
8166 | 8210 | |
8167 | 8211 | } // class Functions |
8168 |
830 | 830 | case Variable.STRING: doStringAssignment(); break; |
831 | 831 | case Variable.ARRAY: doArrayAssignment(); break; |
832 | 832 | case USER_FUNCTION: doUserFunctionAssignment(); break; |
833 | case STRING_FUNCTION: doNumericStringAssignment(); break; | |
833 | 834 | default: |
834 | 835 | putTokenBack(); |
835 | 836 | double value = getAssignmentExpression(); |
42 | 42 | * 2016-11-28: added static getNumParams methods |
43 | 43 | * 2018-03-23: fixes NullPointerException for custom fit without initialParamVariations |
44 | 44 | * 2018-07-19: added error function erf (=integral over Gaussian) |
45 | * 2021-04-30: data points can have weights | |
45 | 46 | */ |
46 | 47 | |
47 | 48 | public class CurveFitter implements UserFunction{ |
93 | 94 | "y = a*(1-exp(-b*x))^c", //CHAPMAN |
94 | 95 | "y = a+b*erf((x-c)/d)" //ERF; note that the c parameter is sqrt2 times the Gaussian |
95 | 96 | }; |
96 | ||
97 | ||
97 | 98 | /** ImageJ Macro language code for the built-in functions */ |
98 | 99 | public static final String[] fMacro = { |
99 | 100 | "y = a+x*b","y = a+x*(b+x*c)", //STRAIGHT_LINE,POLY2 |
122 | 123 | |
123 | 124 | private int fitType = -1; // Number of curve type to fit |
124 | 125 | private double[] xData, yData; // x,y data to fit |
126 | private double[] weights; // weights for the data to fit | |
125 | 127 | private double[] xDataSave, yDataSave; //saved original data after fitting modified data |
126 | 128 | private int numPoints; // number of data points in actual fit |
127 | 129 | private double ySign = 0; // remember sign of y data for power-law fit via regression |
128 | private double sumY = Double.NaN, sumY2 = Double.NaN; // sum(y), sum(y^2) of the data used for fitting | |
130 | private double sumY = Double.NaN, sumY2 = Double.NaN; // sum(y*w), sum(y^2*w) of the data used for fitting (w=weight, default 1) | |
131 | private double sumWeights = Double.NaN; //sum of weights (or numPoints, if no weigths are given) | |
129 | 132 | private int numParams; // number of parameters |
130 | 133 | private double[] initialParams; // user specified or estimated initial parameters |
131 | 134 | private double[] initialParamVariations; // estimate of range of parameters |
258 | 261 | } |
259 | 262 | |
260 | 263 | /** Fit a function defined as a macro String like "y = a + b*x + c*x*x". |
264 | * When showSettings is true, pops up a dialog allowing the user to set the initial | |
265 | * fit parameters and various numbers controlling the Minimizer | |
261 | 266 | * Returns the number of parameters, or 0 in case of a macro syntax error. |
262 | 267 | * |
263 | * For good performance, it is advisable to set also the typical variation range | |
264 | * of the initial parameters by the | |
265 | * getMinimizer().setInitialParamVariations(double[]) method (especially if one or | |
266 | * more of the initialParams are zero). | |
267 | 268 | * Use getStatus() and/or getStatusString() to see whether fitting was (probably) successful and |
268 | 269 | * getParams() to access the result. |
270 | * | |
271 | * For complicated fits and good performance, it is advisable to use the doCustomFit method with | |
272 | * a (java) UserFunction, which also has more options. | |
273 | * | |
269 | 274 | */ |
270 | 275 | public int doCustomFit(String equation, double[] initialParams, boolean showSettings) { |
271 | 276 | customFormula = null; |
297 | 302 | * Use getStatus() and/or getStatusString() to see whether fitting was (probably) successful and |
298 | 303 | * getParams() to access the result. |
299 | 304 | * |
300 | * @param userFunction A plugin where the fit function is defined by the | |
301 | * userFunction(params, x) method. | |
302 | * This function must allow simultaneous calls in multiple threads. | |
305 | * For getter performance, if possible it is advisable to first call setOffsetMultiplySlopeParams, | |
306 | * to avoid searching for one or two parameters that can be calculated directly by linear regression. | |
307 | * | |
308 | * @param userFunction A class instance implementing the userFunction interface. There, the | |
309 | * fit function hould be defined by the method userFunction(params, x). | |
310 | * This function must allow simultaneous calls in multiple threads. | |
303 | 311 | * @param numParams Number of parameters of the fit function. |
304 | 312 | * @param formula A String describing the fit formula, may be null. |
305 | * @param initialParams Starting point for the parameters; may be null (than values | |
306 | * of 0 are used). The fit function with these parameters must | |
307 | * not return NaN for any of the data points given in the | |
308 | * constructor (xData). | |
313 | * @param initialParams Starting point for the parameters; the fit function with these parameters | |
314 | * must not return NaN for any of the data points given in the constructor (xData). | |
315 | * initialParams may be null, then random values are used, | |
316 | * with repeated tries if the userFunction returns NaN. | |
309 | 317 | * @param initialParamVariations Each parameter is initially varied by up to +/- this value. |
310 | * If not given (null), initial variations are taken as | |
311 | * 10% of initial parameter value or 0.01 for parameters that are zero. | |
312 | * When this array is given, all elements must be positive (nonzero). | |
313 | * See Minimizer.minimize for details. | |
314 | * @param showSettings Displays a popup dialog for modifying the initial parameters and | |
315 | * a few numbers controlling the minimizer. | |
318 | * If not given (null), initial variations are taken as | |
319 | * 10% of initial parameter value or 0.01 for parameters that are zero. | |
320 | * When this array is given, all elements must be positive (nonzero). | |
321 | * See Minimizer.minimize for details. Providing this array is | |
322 | * especially valuable if one or more initial parameters have a value of 0. | |
323 | * @param showSettings Displays a popup dialog for modifying the initial parameters and | |
324 | * a few numbers controlling the minimizer. | |
316 | 325 | */ |
317 | 326 | public void doCustomFit(UserFunction userFunction, int numParams, String formula, |
318 | 327 | double[] initialParams, double[] initialParamVariations, boolean showSettings) { |
327 | 336 | /** Sets the initial parameters, which override the default initial parameters. */ |
328 | 337 | public void setInitialParameters(double[] initialParams) { |
329 | 338 | this.initialParams = initialParams; |
339 | } | |
340 | ||
341 | /** Sets weights of the data points. The 'weights' array must have the same length as the data arrays | |
342 | * passed with the constructor. If the error bars of the data points are known, the weights | |
343 | * should be proportional to 1/error^2. | |
344 | * When weights are specified, note that 'getSumResidualsSqr' will return the weighted sum. */ | |
345 | public void setWeights(double[] weights) { | |
346 | this.weights = weights; | |
330 | 347 | } |
331 | 348 | |
332 | 349 | /** Returns a reference to the Minimizer used, for accessing Minimizer methods directly. |
556 | 573 | return residuals; |
557 | 574 | } |
558 | 575 | |
559 | /* Get the sum of the residuals (may be NaN if the minimizer could not start properly | |
576 | /** Returns the sum of the residuals (may be NaN if the minimizer could not start properly | |
560 | 577 | * i.e., if getStatus() returns Minimizer.INITILIZATION_FAILURE). |
578 | * If weights have been specified, each of the residuals is multiplied by the corresponding | |
579 | * weight before summing. | |
561 | 580 | */ |
562 | 581 | public double getSumResidualsSqr() { |
563 | 582 | return getParams()[numParams]; // value is stored as last element by the minimizer |
566 | 585 | /** Returns the standard deviation of the residuals. |
567 | 586 | * Here, the standard deviation is defined here as the root-mean-square of the residuals |
568 | 587 | * times sqrt(n/(n-1)); where n is the number of points. |
588 | * If weights are provided, the standard deviation does not take the weights into account. | |
589 | * With weights, the standard deviation and getSumResidualsSqr (which uses weights) | |
590 | * are not related the usual way. | |
569 | 591 | */ |
570 | 592 | public double getSD() { |
571 | 593 | double[] residuals = getResiduals(); |
579 | 601 | return Math.sqrt(stdDev/(n-1.0)); |
580 | 602 | } |
581 | 603 | |
582 | /** Returns R^2, where 1.0 is best. | |
604 | /** Returns R^2, where 1.0 is best. For unweighted data, | |
583 | 605 | <pre> |
584 | 606 | r^2 = 1 - SSE/SSD |
585 | 607 | |
586 | where: SSE = sum of the squared errors | |
587 | SSD = sum of the squared deviations about the mean. | |
608 | where: SSE = sum of the squared errors | |
609 | SSD = sum of the squared deviations about the mean. | |
588 | 610 | </pre> |
589 | 611 | * For power, exp by linear regression and 'Rodbard NIH Image', this is calculated for the |
590 | 612 | * fit actually done, not for the residuals of the original data. |
591 | */ | |
613 | */ | |
592 | 614 | public double getRSquared() { |
593 | 615 | if (Double.isNaN(sumY)) calculateSumYandY2(); |
594 | double sumMeanDiffSqr = sumY2 - sumY*sumY/numPoints; | |
616 | double sumMeanDiffSqr = sumY2 - sumY*sumY/sumWeights; | |
595 | 617 | double rSquared = 0.0; |
596 | 618 | if (sumMeanDiffSqr > 0.0) |
597 | 619 | rSquared = 1.0 - getSumResidualsSqr()/sumMeanDiffSqr; |
600 | 622 | |
601 | 623 | /** Get a measure of "goodness of fit" where 1.0 is best. |
602 | 624 | * Approaches R^2 if the number of points is much larger than the number of fit parameters. |
625 | * Assumes that the data points are independent (i.e., each point having a different x value). | |
603 | 626 | * For power, exp by linear regression and 'Rodbard NIH Image', this is calculated for the |
604 | 627 | * fit actually done, not for the residuals of the original data. |
605 | 628 | */ |
606 | 629 | public double getFitGoodness() { |
607 | 630 | if (Double.isNaN(sumY)) calculateSumYandY2(); |
608 | double sumMeanDiffSqr = sumY2 - sumY*sumY/numPoints; | |
631 | double sumMeanDiffSqr = sumY2 - sumY*sumY/sumWeights; | |
609 | 632 | double fitGoodness = 0.0; |
610 | 633 | int degreesOfFreedom = numPoints - getNumParams(); |
611 | 634 | if (sumMeanDiffSqr > 0.0 && degreesOfFreedom > 0) |
739 | 762 | fitType = RODBARD; |
740 | 763 | return fList[fitType]; |
741 | 764 | } |
742 | ||
765 | ||
743 | 766 | /** Returns macro code of the form "y = ...x" for the fit function used. |
744 | 767 | * Note that this is not neccessarily the equation acutally used for the fit |
745 | 768 | * (for the various "linear regression" types and RODBARD2, the fit is done |
790 | 813 | if (numRegressionParams == 0) { // simply calculate sum of residuals |
791 | 814 | for (int i=0; i<numPoints; i++) { |
792 | 815 | double fValue = f(params,xData[i]); |
793 | sumResidualsSqr += sqr(fValue-yData[i]); | |
816 | double resSqr = sqr(fValue-yData[i]); | |
817 | if (weights != null) resSqr *= weights[i]; | |
818 | sumResidualsSqr += resSqr; | |
794 | 819 | } |
795 | 820 | //IJ.log(IJ.d2s(params[0],3,5)+","+IJ.d2s(params[1],3,5)+": r="+IJ.d2s(sumResidualsSqr,3,5)+Thread.currentThread().getName() ); |
796 | 821 | } else { // handle simple linear dependencies by linear regression: |
845 | 870 | private void doRegression(double[] params) { |
846 | 871 | double sumX=0, sumX2=0, sumXY=0; //sums for regression; here 'x' are function values |
847 | 872 | double sumY=0, sumY2=0; //only calculated for 'slope', otherwise we use the values calculated already |
873 | double sumWeights=0; | |
848 | 874 | for (int i=0; i<numPoints; i++) { |
849 | 875 | double fValue = fitType == STRAIGHT_LINE ? 0 : f(params, xData[i]); // function value |
850 | 876 | if (Double.isNaN(fValue)) { //check for NaN now; later we need NaN checking for division-by-zero check. |
851 | 877 | params[numParams] = Double.NaN; |
852 | 878 | return; //sum of squared residuals is NaN if any value is NaN |
853 | 879 | } |
880 | double w = weights==null ? 1 : weights[i]; | |
881 | sumWeights += w; | |
854 | 882 | //if(getIterations()==0)IJ.log(xData[i]+"\t"+yData[i]+"\t"+fValue); //x,y,function |
855 | 883 | if (hasSlopeParam) { // fit y = offset + slope*x + function(of other params) |
856 | 884 | double x = xData[i]; |
857 | 885 | double y = yData[i] - fValue; |
858 | sumX += x; | |
859 | sumX2 += x*x; | |
860 | sumXY += x*y; | |
861 | sumY2 += y*y; | |
862 | sumY += y; | |
886 | sumX += x*w; | |
887 | sumX2 += x*x*w; | |
888 | sumXY += x*y*w; | |
889 | sumY2 += y*y*w; | |
890 | sumY += y*w; | |
863 | 891 | } else { // fit y = offset + factor * function(of other params) |
864 | 892 | double x = fValue; |
865 | 893 | double y = yData[i]; |
866 | sumX += fValue; | |
867 | sumX2 += fValue*fValue; | |
868 | sumXY += fValue*yData[i]; | |
894 | sumX += fValue*w; | |
895 | sumX2 += fValue*fValue*w; | |
896 | sumXY += fValue*yData[i]*w; | |
869 | 897 | } |
870 | 898 | } |
871 | 899 | if (!hasSlopeParam) { |
883 | 911 | sumResidualsSqr = 2e-15*sumY2; |
884 | 912 | } else { // full linear regression or offset only. Slope is named 'factor' here |
885 | 913 | if (factorParam >= 0) { |
886 | factor = (sumXY-sumX*sumY/numPoints)/(sumX2-sumX*sumX/numPoints); | |
914 | factor = (sumXY-sumX*sumY/sumWeights)/(sumX2-sumX*sumX/sumWeights); | |
887 | 915 | if (restrictPower & factor<=0) // power-law fit with (0,0) point: power must be >0 |
888 | 916 | factor = 1e-100; |
889 | 917 | else if (Double.isNaN(factor) || Double.isInfinite(factor)) |
890 | 918 | factor = 0; // all 'x' values are equal, any factor (slope) will fit |
891 | 919 | } |
892 | double offset = (sumY-factor*sumX)/numPoints; | |
920 | double offset = (sumY-factor*sumX)/sumWeights; | |
893 | 921 | params[offsetParam] = offset; |
894 | sumResidualsSqr = sqr(factor)*sumX2 + numPoints*sqr(offset) + sumY2 + | |
922 | sumResidualsSqr = sqr(factor)*sumX2 + sumWeights*sqr(offset) + sumY2 + | |
895 | 923 | 2*factor*offset*sumX - 2*factor*sumXY - 2*offset*sumY; |
896 | 924 | // check for accuracy problem: large difference of small numbers? |
897 | 925 | // Don't report unrealistic or even negative values, otherwise minimization could lead |
898 | 926 | // into parameters where we have a numeric problem |
899 | if (sumResidualsSqr < 2e-15*(sqr(factor)*sumX2 + numPoints*sqr(offset) + sumY2)) | |
900 | sumResidualsSqr = 2e-15*(sqr(factor)*sumX2 + numPoints*sqr(offset) + sumY2); | |
927 | if (sumResidualsSqr < 2e-15*(sqr(factor)*sumX2 + sumWeights*sqr(offset) + sumY2)) | |
928 | sumResidualsSqr = 2e-15*(sqr(factor)*sumX2 + sumWeights*sqr(offset) + sumY2); | |
901 | 929 | //if(){IJ.log("sumX="+sumX+" sumX2="+sumX2+" sumXY="+sumXY+" factor="+factor+" offset=="+offset);} |
902 | 930 | } |
903 | 931 | params[numParams] = sumResidualsSqr; |
1215 | 1243 | } |
1216 | 1244 | |
1217 | 1245 | |
1218 | /** calculates the sum of y and y^2 */ | |
1246 | /** calculates the sum of y and y^2 (weighted sum if we have weights) */ | |
1219 | 1247 | private void calculateSumYandY2() { |
1220 | sumY = 0.0; sumY2 = 0.0; | |
1248 | sumY = 0.0; sumY2 = 0.0; sumWeights = 0.0; | |
1249 | double w = 1.0; | |
1221 | 1250 | for (int i=0; i<numPoints; i++) { |
1222 | 1251 | double y = yData[i]; |
1223 | sumY += y; | |
1224 | sumY2 += y*y; | |
1252 | if (weights != null) w = weights[i]; | |
1253 | sumY += y*w; | |
1254 | sumY2 += y*y*w; | |
1255 | sumWeights += w; | |
1225 | 1256 | } |
1226 | 1257 | } |
1227 | 1258 |
1 | 1 | import ij.*; |
2 | 2 | import ij.process.*; |
3 | 3 | import ij.gui.*; |
4 | import ij.measure.ResultsTable; | |
5 | import ij.util.Tools; | |
4 | 6 | |
5 | 7 | /** Implements the Plugins/Utilities/Run Benchmark command. |
6 | 8 | * Suppresses subordinate status bar messages by using |
9 | 11 | * IJ.showProgress(-currentIndex,finalIndex). |
10 | 12 | */ |
11 | 13 | public class Benchmark implements PlugIn { |
12 | int size = 5000; | |
13 | int ops = 62; | |
14 | int counter; | |
14 | private String[] results = { | |
15 | "10.9|MacBook Air (M1, 2020, Native)", | |
16 | "17.2|iMac Pro (2017)", | |
17 | "18.1|MacBook Air (M1, 2020, Rosetta)", | |
18 | "22.8|Dell T7920 (Dual Xeon, 282GB RAM, 2018)", | |
19 | "24.7|27\" iMac (Early 2015)", | |
20 | "29.7|13\" MacBook Pro (Late 2015)", | |
21 | "29.7|15\" MacBook Pro (Early 2013)", | |
22 | "62.3|Acer Aspire laptop (Core i5, 2014)" | |
23 | }; | |
24 | private int size = 5000; | |
25 | private int ops = 62; | |
26 | private int counter; | |
15 | 27 | |
16 | 28 | public void run(String arg) { |
17 | 29 | ImagePlus cImp = WindowManager.getCurrentImage(); |
55 | 67 | scale = scale*1.2; |
56 | 68 | } |
57 | 69 | double time = (System.currentTimeMillis()-t0)/1000.0; |
70 | ResultsTable rt = new ResultsTable(); | |
71 | rt.showRowNumbers(true); | |
72 | for (int i=0; i<results.length; i++) { | |
73 | String[] columns = Tools.split(results[i],"|"); | |
74 | rt.addRow(); | |
75 | rt.addValue("Time", columns[0]); | |
76 | rt.addValue("Computer", columns[1]); | |
77 | } | |
78 | rt.addRow(); | |
79 | String t = IJ.d2s(time,1); | |
80 | if (t.length()<4) t=" "+t; | |
81 | rt.addValue("Time", t); | |
82 | rt.addValue("Computer", "<<THIS MACHINE ("+Prefs.getThreads()+" THREADS)>>"); | |
83 | rt.sort("Time"); | |
84 | rt.show("Benchmark Results"); | |
58 | 85 | IJ.showStatus("!"+IJ.d2s(time,1)+" seconds to perform "+counter+" operations on a "+size+"x"+size+" 16-bit image"); |
59 | 86 | } |
60 | 87 |
97 | 97 | |
98 | 98 | public ImageStack expandStack(ImageStack stackOld, int wNew, int hNew, int xOff, int yOff) { |
99 | 99 | int nFrames = stackOld.getSize(); |
100 | ImageProcessor ipOld = stackOld.getProcessor(1); | |
101 | java.awt.Color colorBack = Toolbar.getBackgroundColor(); | |
102 | ||
100 | ImageProcessor ipOld = stackOld.getProcessor(1); | |
103 | 101 | ImageStack stackNew = new ImageStack(wNew, hNew, stackOld.getColorModel()); |
104 | 102 | ImageProcessor ipNew; |
105 | 103 | |
109 | 107 | if (zeroFill) |
110 | 108 | ipNew.setValue(0.0); |
111 | 109 | else |
112 | ipNew.setColor(colorBack); | |
110 | ipNew.setGlobalBackgroundColor(); | |
113 | 111 | ipNew.fill(); |
114 | 112 | ipNew.insert(stackOld.getProcessor(i), xOff, yOff); |
115 | 113 | stackNew.addSlice(stackOld.getSliceLabel(i), ipNew); |
122 | 120 | if (zeroFill) |
123 | 121 | ipNew.setValue(0.0); |
124 | 122 | else |
125 | ipNew.setColor(Toolbar.getBackgroundColor()); | |
123 | ipNew.setGlobalBackgroundColor(); | |
126 | 124 | ipNew.fill(); |
127 | 125 | ipNew.insert(ipOld, xOff, yOff); |
128 | 126 | return ipNew; |
41 | 41 | ImagePlus rImp = new ImagePlus(title+" (red)", channels[0]); |
42 | 42 | rImp.setCalibration(cal); |
43 | 43 | rImp.setIJMenuBar(false); |
44 | rImp.setBorderColor(new Color(255,180,180)); | |
45 | 44 | rImp.show(); |
46 | 45 | rImp.setSlice(pos); |
47 | 46 | if (IJ.isMacOSX()) IJ.wait(500); |
48 | 47 | ImagePlus gImp = new ImagePlus(title+" (green)", channels[1]); |
49 | 48 | gImp.setCalibration(cal); |
50 | 49 | gImp.setIJMenuBar(false); |
51 | gImp.setBorderColor(new Color(180,255,180)); | |
52 | 50 | gImp.show(); |
53 | 51 | gImp.setSlice(pos); |
54 | 52 | if (IJ.isMacOSX()) IJ.wait(500); |
55 | 53 | ImagePlus bImp = new ImagePlus(title+" (blue)", channels[2]); |
56 | 54 | bImp.setCalibration(cal); |
57 | bImp.setBorderColor(new Color(180,180,255)); | |
58 | 55 | bImp.show(); |
59 | 56 | bImp.setSlice(pos); |
60 | 57 | } |
257 | 257 | private static final int AE=0x4145, AS=0x4153, AT=0x4154, CS=0x4353, DA=0x4441, DS=0x4453, DT=0x4454, |
258 | 258 | FD=0x4644, FL=0x464C, IS=0x4953, LO=0x4C4F, LT=0x4C54, PN=0x504E, SH=0x5348, SL=0x534C, |
259 | 259 | SS=0x5353, ST=0x5354, TM=0x544D, UI=0x5549, UL=0x554C, US=0x5553, UT=0x5554, |
260 | OB=0x4F42, OW=0x4F57, SQ=0x5351, UN=0x554E, QQ=0x3F3F; | |
260 | OB=0x4F42, OW=0x4F57, SQ=0x5351, UN=0x554E, QQ=0x3F3F, | |
261 | OF=0x4F46, OL=0x4F4C, OD=0x4F44, UC=0x5543, UR=0x5552, OV=0x4F56, SV=0x5356, UV=0x5556; | |
262 | ||
261 | 263 | |
262 | 264 | private static Properties dictionary; |
263 | 265 | |
460 | 462 | |
461 | 463 | switch (vr) { |
462 | 464 | case OB: case OW: case SQ: case UN: case UT: |
465 | case OF: case OL: case OD: case UC: case UR: | |
466 | case OV: case SV: case UV: | |
463 | 467 | // Explicit VR with 32-bit length if other two bytes are zero |
464 | 468 | if ( (b2 == 0) || (b3 == 0) ) return getInt(); |
465 | 469 | // Implicit VR with 32-bit length |
467 | 471 | if (littleEndian) |
468 | 472 | return ((b3<<24) + (b2<<16) + (b1<<8) + b0); |
469 | 473 | else |
470 | return ((b0<<24) + (b1<<16) + (b2<<8) + b3); | |
474 | return ((b0<<24) + (b1<<16) + (b2<<8) + b3); | |
471 | 475 | case AE: case AS: case AT: case CS: case DA: case DS: case DT: case FD: |
472 | 476 | case FL: case IS: case LO: case LT: case PN: case SH: case SL: case SS: |
473 | case ST: case TM:case UI: case UL: case US: case QQ: | |
477 | case ST: case TM: case UI: case UL: case US: case QQ: | |
474 | 478 | // Explicit vr with 16-bit length |
475 | 479 | if (littleEndian) |
476 | 480 | return ((b3<<8) + b2); |
1716 | 1720 | }; |
1717 | 1721 | |
1718 | 1722 | } |
1719 |
280 | 280 | * @see ij.ImagePlus#crop(String) |
281 | 281 | */ |
282 | 282 | public ImagePlus crop(ImagePlus imp) { |
283 | //if (imp!=null) throw new IllegalArgumentException(); | |
284 | 283 | if (imp.getNChannels()>1 && imp.getCompositeMode()==IJ.COMPOSITE) { |
285 | 284 | int z = imp.getSlice(); |
286 | 285 | int t = imp.getFrame(); |
17 | 17 | private static final int MAX_SEPARATE = 40; |
18 | 18 | private static final String DIR_KEY = "import.sequence.dir"; |
19 | 19 | private static final String[] types = {"default", "16-bit", "32-bit", "RGB"}; |
20 | private static String[] excludedTypes = {".txt", ".lut", ".roi", ".pty", ".hdr", ".java", ".ijm", ".py", ".js", ".bsh", ".xml"}; | |
20 | private static String[] excludedTypes = {".txt",".lut",".roi",".pty",".hdr",".java",".ijm",".py",".js",".bsh",".xml",".rar",".h5",".doc",".xls"}; | |
21 | 21 | private static boolean staticSortFileNames = true; |
22 | 22 | private static boolean staticOpenAsVirtualStack; |
23 | 23 | private boolean convertToRGB; |
421 | 421 | } |
422 | 422 | } |
423 | 423 | if (imp2.getStackSize()==1) { |
424 | imp2.setProperty("Label", list[0]); | |
424 | int idx = this.start-1; | |
425 | if (idx<0 || idx>=list.length) | |
426 | idx = 0; | |
427 | imp2.setProperty("Label", list[idx]); | |
425 | 428 | if (info1!=null) |
426 | 429 | imp2.setProperty("Info", info1); |
427 | 430 | } |
91 | 91 | gd.addChoice("Method:", methods, methods[staticMethod]); |
92 | 92 | } |
93 | 93 | gd.addStringField("Name:", name, 12); |
94 | gd.addStringField("Title Contains:", "", 12); | |
94 | gd.addStringField("Title contains:", "", 12); | |
95 | 95 | if (sizesDiffer) |
96 | gd.addCheckbox("Bicubic Interpolation", staticBicubic); | |
97 | gd.addCheckbox("Use Titles as Labels", staticTitlesAsLabels); | |
98 | gd.addCheckbox("Keep Source Images", staticKeep); | |
96 | gd.addCheckbox("Bicubic interpolation", staticBicubic); | |
97 | gd.addCheckbox("Use titles as labels", staticTitlesAsLabels); | |
98 | gd.addCheckbox("Keep source images", staticKeep); | |
99 | 99 | gd.showDialog(); |
100 | 100 | if (gd.wasCanceled()) return; |
101 | 101 | if (sizesDiffer) |
149 | 149 | if (ip.getMin()<min) min = ip.getMin(); |
150 | 150 | if (ip.getMax()>max) max = ip.getMax(); |
151 | 151 | String label = titlesAsLabels?images[i].getTitle():null; |
152 | if (label==null) | |
153 | label = (String)images[i].getProperty("Label"); | |
152 | 154 | if (label!=null) { |
153 | 155 | String info = (String)images[i].getProperty("Info"); |
154 | 156 | if (info!=null) label += "\n" + info; |
181 | 181 | bgColor = Color.white; |
182 | 182 | } |
183 | 183 | } |
184 | montage.setColor(bgColor); | |
184 | if (Double.isNaN(Toolbar.getBackgroundValue())) | |
185 | montage.setColor(bgColor); | |
186 | else | |
187 | montage.setGlobalBackgroundColor(); | |
185 | 188 | montage.fill(); |
186 | montage.setColor(fgColor); | |
189 | if (Double.isNaN(Toolbar.getForegroundValue())) | |
190 | montage.setColor(fgColor); | |
191 | else | |
192 | montage.setGlobalForegroundColor(); | |
187 | 193 | montage.setFont(new Font("SansSerif", Font.PLAIN, fontSize)); |
188 | 194 | montage.setAntialiasedText(true); |
189 | 195 | ImageStack stack = imp.getStack(); |
409 | 409 | public static void listRois(Roi[] rois) { |
410 | 410 | ImagePlus imp = WindowManager.getCurrentImage(); |
411 | 411 | ResultsTable rt = new ResultsTable(); |
412 | rt.showRowNumbers(true); | |
412 | 413 | for (int i=0; i<rois.length; i++) { |
413 | 414 | if (rois[i]==null) |
414 | 415 | continue; |
437 | 438 | Rectangle2D.Double bounds = rois[i].getFloatBounds(); |
438 | 439 | rt.setValue("X", i, (int)Math.round(bounds.x)); |
439 | 440 | rt.setValue("Y", i, (int)Math.round(bounds.y)); |
441 | } else if (rois[i] instanceof Arrow) { | |
442 | Polygon p = ((Arrow)rois[i]).getPoints(); | |
443 | rt.setValue("X", i, p.xpoints[1]); | |
444 | rt.setValue("Y", i, p.ypoints[1]); | |
440 | 445 | } else { |
441 | 446 | rt.setValue("X", i, r.x); |
442 | 447 | rt.setValue("Y", i, r.y); |
36 | 36 | msg = ""+e; |
37 | 37 | msg = "An error occured writing the file.\n \n" + msg; |
38 | 38 | if (msg.contains("NullPointerException")) |
39 | msg = "Incorrect file path: \""+path+"\""; | |
39 | msg = "Incorrect file path:"; | |
40 | msg += "\n \n"+path; | |
40 | 41 | IJ.error("PNG Writer", msg); |
41 | 42 | } |
42 | 43 | IJ.showStatus(""); |
4 | 4 | import ij.measure.Calibration; |
5 | 5 | import ij.plugin.filter.EDM; |
6 | 6 | import ij.plugin.filter.ThresholdToSelection; |
7 | import ij.plugin.frame.Recorder; | |
7 | 8 | import java.awt.*; |
9 | import java.util.Vector; | |
8 | 10 | |
9 | 11 | /** This plugin, which enlarges or shrinks selections, implements the Edit/Selection/Enlarge command. */ |
10 | public class RoiEnlarger implements PlugIn { | |
11 | private static double defaultDistance = 15; // pixels | |
12 | public class RoiEnlarger implements PlugIn, DialogListener { | |
13 | private static final String DISTANCE_KEY = "enlarger.distance"; | |
14 | private static final String USE_PIXELS_KEY = "enlarger.pixels"; | |
15 | private double defaultDistance = Prefs.get(DISTANCE_KEY, 15); // pixels | |
16 | private boolean defaultUsePixels = Prefs.get(USE_PIXELS_KEY, false); | |
17 | private Calibration cal; | |
18 | private Label unitsLabel; | |
12 | 19 | |
13 | 20 | public void run(String arg) { |
14 | 21 | ImagePlus imp = IJ.getImage(); |
20 | 27 | if (!imp.okToDeleteRoi()) |
21 | 28 | return; |
22 | 29 | double n = showDialog(imp, defaultDistance); |
23 | if (n==Double.NaN) | |
24 | return; | |
30 | if (Double.isNaN(n)) | |
31 | return; | |
32 | Prefs.set(DISTANCE_KEY, defaultDistance); | |
33 | Prefs.set(USE_PIXELS_KEY, defaultUsePixels); | |
25 | 34 | Roi roi2 = Math.abs(n)<256?enlarge255(roi,n):enlarge(roi,n); |
26 | 35 | if (roi2!=null) { |
27 | 36 | imp.setRoi(roi2); |
28 | 37 | Roi.setPreviousRoi(roi); |
29 | 38 | defaultDistance = n; |
30 | 39 | } |
31 | } | |
32 | ||
40 | int pixels = (int)Math.round(n); | |
41 | Recorder.recordCall("RoiEnlarger.enlarge(imp, "+pixels+");"); | |
42 | } | |
43 | ||
44 | public static void enlarge(ImagePlus imp, int pixels) { | |
45 | Roi roi = imp.getRoi(); | |
46 | if (roi==null || roi.isLine() || (roi instanceof PointRoi)) | |
47 | return; | |
48 | Roi roi2 = Math.abs(pixels)<256?enlarge255(roi,pixels):enlarge(roi,pixels); | |
49 | if (roi2!=null) | |
50 | imp.setRoi(roi2); | |
51 | } | |
52 | ||
33 | 53 | public double showDialog(ImagePlus imp, double pixels) { |
34 | Calibration cal = imp.getCalibration(); | |
54 | cal = imp.getCalibration(); | |
35 | 55 | boolean scaled = cal.scaled(); |
36 | boolean usePixels = false; | |
37 | double n = pixels*cal.pixelWidth; | |
56 | double pixelWidth = cal.pixelWidth; | |
57 | boolean xyScaleDifferent = scaled && cal.pixelWidth != cal.pixelHeight; | |
58 | boolean usePixels = defaultUsePixels; | |
59 | double n = pixels; | |
38 | 60 | int decimalPlaces = 0; |
39 | if (Math.floor(n)!=n) | |
40 | decimalPlaces = 2; | |
61 | if (scaled && !usePixels) { | |
62 | n *= pixelWidth; | |
63 | decimalPlaces = getDecimalPlaces(pixelWidth, n); | |
64 | } | |
41 | 65 | GenericDialog gd = new GenericDialog("Enlarge Selection"); |
42 | gd.addNumericField("Enlarge by", n, decimalPlaces, 4, cal.getUnits()); | |
66 | gd.addNumericField("Enlarge by", n, decimalPlaces); | |
67 | String units = scaled && !usePixels ? cal.getUnits()+" " : "pixels "; | |
68 | gd.addToSameRow(); | |
69 | gd.addMessage(units.replace('\n', ' ')); //just in case of a newline character, which would make it a MultiLineLabel | |
70 | unitsLabel = (Label)gd.getMessage(); | |
43 | 71 | if (scaled) { |
44 | gd.setInsets(0, 20, 0); | |
72 | gd.setInsets(0, 20, 0); //top left bottom | |
45 | 73 | gd.addCheckbox("Pixel units", usePixels); |
46 | 74 | } |
47 | 75 | gd.setInsets(10, 0, 0); |
48 | 76 | gd.addMessage("Enter negative number to shrink", null, Color.darkGray); |
77 | if (xyScaleDifferent) { | |
78 | gd.setInsets(5, 0, 0); | |
79 | gd.addMessage(" \n ", null, Color.RED); | |
80 | } | |
81 | gd.addDialogListener(this); | |
82 | if (xyScaleDifferent && Macro.getOptions()==null) | |
83 | updateWarning(gd); //in interactive mode only | |
49 | 84 | gd.showDialog(); |
50 | 85 | if (gd.wasCanceled()) |
51 | 86 | return Double.NaN; |
52 | 87 | n = gd.getNextNumber(); |
53 | 88 | if (scaled) |
54 | 89 | usePixels = gd.getNextBoolean(); |
55 | pixels = usePixels?n:n/cal.pixelWidth; | |
90 | pixels = usePixels ? n : n/pixelWidth; | |
91 | defaultDistance = pixels; | |
92 | defaultUsePixels = usePixels; | |
56 | 93 | return pixels; |
57 | 94 | } |
58 | ||
95 | ||
96 | public boolean dialogItemChanged(GenericDialog gd, AWTEvent e) { | |
97 | Vector checkboxes = gd.getCheckboxes(); | |
98 | Checkbox usePixelsCbx = checkboxes == null ? null : (Checkbox)checkboxes.get(0); | |
99 | double n = gd.getNextNumber(); | |
100 | boolean usePixels = cal.scaled() ? gd.getNextBoolean() : true; //getNextBoolean also needed for macro recorded | |
101 | if (e != null && e.getSource() == usePixelsCbx) { | |
102 | double pixelWidth = cal.pixelWidth; | |
103 | int decimalPlaces = 0; | |
104 | if (usePixels) { | |
105 | n /= pixelWidth; //scaled to pixels | |
106 | } else { | |
107 | n *= pixelWidth; //pixels to scaled | |
108 | decimalPlaces = getDecimalPlaces(pixelWidth, n); | |
109 | } | |
110 | TextField numberField = (TextField)gd.getNumericFields().get(0); | |
111 | numberField.setText(IJ.d2s(n, decimalPlaces)); | |
112 | if (unitsLabel != null) unitsLabel.setText(usePixels ? "pixels" : cal.getUnits()); | |
113 | boolean xyScaleDifferent = cal.scaled() && cal.pixelWidth != cal.pixelHeight; | |
114 | if (xyScaleDifferent && usePixelsCbx != null) updateWarning(gd); | |
115 | } | |
116 | return !gd.invalidNumber(); | |
117 | } | |
118 | ||
119 | private void updateWarning(GenericDialog gd) { | |
120 | Checkbox usePixelsCbx = (Checkbox)gd.getCheckboxes().get(0); | |
121 | MultiLineLabel warningLabel = (MultiLineLabel)gd.getMessage(); | |
122 | boolean showWarning = !usePixelsCbx.getState(); //warn if not pixels units | |
123 | warningLabel.setText(showWarning ? "WARNING: x & y scales differ\nConversion to pixels uses x scale" : " \n "); | |
124 | } | |
125 | ||
126 | //decimal places for displaying the scaled enlarge/shrink value | |
127 | private static int getDecimalPlaces(double pixelWidth, double number) { | |
128 | if (number == (int)number || pixelWidth == 1) return 0; | |
129 | int decimalPlaces = (int)(-Math.log10(pixelWidth)+1.9); | |
130 | if (decimalPlaces < 0) decimalPlaces = 0; | |
131 | if (decimalPlaces >9) decimalPlaces = 9; | |
132 | return decimalPlaces; | |
133 | } | |
134 | ||
59 | 135 | public static Roi enlarge(Roi roi, double pixels) { |
60 | 136 | if (pixels==0) |
61 | 137 | return roi; |
85 | 161 | edm.setThreshold(0, n, ImageProcessor.NO_LUT_UPDATE); |
86 | 162 | roi2 = (new ThresholdToSelection()).convert(edm); |
87 | 163 | if (roi2==null) |
88 | return roi; | |
164 | return roi; | |
89 | 165 | roi2.copyAttributes(roi); |
90 | 166 | roi2.setLocation(bounds.x-n+xoffset, bounds.y-n+yoffset); |
91 | 167 | if (roi.getStroke()!=null) |
92 | 168 | roi2.setStroke(roi.getStroke()); |
93 | 169 | return roi2; |
94 | 170 | } |
95 | ||
171 | ||
96 | 172 | private static Roi enlargeRectOrOval(Roi roi, int n) { |
97 | 173 | Rectangle bounds = roi.getBounds(); |
98 | 174 | bounds.x -= n; |
109 | 185 | roi2.copyAttributes(roi); |
110 | 186 | return roi2; |
111 | 187 | } |
112 | ||
188 | ||
113 | 189 | private static Roi shrink(Roi roi, int n) { |
114 | 190 | Rectangle bounds = roi.getBounds(); |
115 | 191 | int width = bounds.width + 2; |
119 | 195 | ip.setColor(255); |
120 | 196 | ip.fill(roi); |
121 | 197 | roi.setLocation(bounds.x, bounds.y); |
122 | FloatProcessor edm = new EDM().makeFloatEDM (ip, 0, false); | |
198 | FloatProcessor edm = new EDM().makeFloatEDM (ip, 0, false); | |
123 | 199 | edm.setThreshold(n+1, Float.MAX_VALUE, ImageProcessor.NO_LUT_UPDATE); |
124 | 200 | Roi roi2 = (new ThresholdToSelection()).convert(edm); |
125 | 201 | if (roi2==null) |
131 | 207 | roi2.setLocation(bounds.x+bounds2.x-1, bounds.y+bounds2.y-1); |
132 | 208 | return roi2; |
133 | 209 | } |
134 | ||
210 | ||
135 | 211 | public static Roi enlarge255(Roi roi, double pixels) { |
136 | 212 | if (pixels==0) |
137 | 213 | return roi; |
164 | 240 | ip.setThreshold(0, n, ImageProcessor.NO_LUT_UPDATE); |
165 | 241 | roi2 = (new ThresholdToSelection()).convert(ip); |
166 | 242 | if (roi2==null) |
167 | return roi; | |
243 | return roi; | |
168 | 244 | roi2.copyAttributes(roi); |
169 | 245 | roi2.setLocation(bounds.x-n+xoffset, bounds.y-n+yoffset); |
170 | 246 | if (roi.getStroke()!=null) |
171 | 247 | roi2.setStroke(roi.getStroke()); |
172 | 248 | return roi2; |
173 | 249 | } |
174 | ||
250 | ||
175 | 251 | private static Roi shrink255(Roi roi, int n) { |
176 | 252 | Rectangle bounds = roi.getBounds(); |
177 | 253 | int width = bounds.width + 2; |
24 | 24 | Zoom zoom = new Zoom(); |
25 | 25 | zoom.setZoom(imp, magnification, x, y); |
26 | 26 | } |
27 | ||
28 | public static void in(ImagePlus imp) { | |
29 | ImageCanvas ic = imp.getCanvas(); | |
30 | if (ic==null) return; | |
31 | waitUntilActivated(imp); | |
32 | int x = ic.screenX(imp.getWidth()/2); | |
33 | int y = ic.screenY(imp.getHeight()/2); | |
34 | ic.zoomIn(x, y); | |
35 | if (ic.getMagnification()<=1.0) | |
36 | imp.repaintWindow(); | |
37 | } | |
38 | ||
39 | public static void out(ImagePlus imp) { | |
40 | ImageCanvas ic = imp.getCanvas(); | |
41 | if (ic==null) return; | |
42 | waitUntilActivated(imp); | |
43 | int x = ic.screenX(imp.getWidth()/2); | |
44 | int y = ic.screenY(imp.getHeight()/2); | |
45 | ic.zoomOut(x, y); | |
46 | if (ic.getMagnification()<=1.0) | |
47 | imp.repaintWindow(); | |
48 | } | |
49 | ||
50 | public static void unzoom(ImagePlus imp) { | |
51 | ImageCanvas ic = imp.getCanvas(); | |
52 | if (ic!=null) { | |
53 | waitUntilActivated(imp); | |
54 | ic.unzoom(); | |
55 | } | |
56 | } | |
57 | ||
58 | public static void maximize(ImagePlus imp) { | |
59 | ImageWindow win = imp.getWindow(); | |
60 | if (win!=null) { | |
61 | waitUntilActivated(imp); | |
62 | win.maximize(); | |
63 | IJ.wait(100); | |
64 | } | |
65 | } | |
66 | ||
67 | private static void waitUntilActivated(ImagePlus imp) { | |
68 | int count = 0; | |
69 | boolean isCanvas = imp.getCanvas()!=null; | |
70 | if (isCanvas) { | |
71 | long t0 = System.currentTimeMillis(); | |
72 | while (!imp.windowActivated() && (System.currentTimeMillis()-t0)<=1000) { | |
73 | IJ.wait(10); | |
74 | count++; | |
75 | } | |
76 | } | |
77 | if (IJ.debugMode) | |
78 | IJ.log("Zoom: "+ count+" "+imp.windowActivated()+" "+isCanvas+" "+imp); | |
79 | } | |
27 | 80 | |
28 | 81 | public void run(String arg) { |
29 | 82 | ImagePlus imp = WindowManager.getCurrentImage(); |
30 | if (imp==null) | |
31 | {IJ.noImage(); return;} | |
83 | if (imp==null) { | |
84 | IJ.noImage(); | |
85 | return; | |
86 | } | |
32 | 87 | ImageCanvas ic = imp.getCanvas(); |
33 | 88 | if (ic==null) return; |
34 | 89 | if (ic instanceof PlotCanvas && !((PlotCanvas)ic).isFrozen()) { |
44 | 99 | int x = ic.screenX(loc.x); |
45 | 100 | int y = ic.screenY(loc.y); |
46 | 101 | if (arg.equals("in")) { |
47 | ic.zoomIn(x, y); | |
48 | if (ic.getMagnification()<=1.0) imp.repaintWindow(); | |
102 | waitUntilActivated(imp); | |
103 | ic.zoomIn(x, y); | |
104 | if (ic.getMagnification()<=1.0) | |
105 | imp.repaintWindow(); | |
106 | Recorder.recordCall("Zoom.in(imp);"); | |
49 | 107 | } else if (arg.equals("out")) { |
108 | waitUntilActivated(imp); | |
50 | 109 | ic.zoomOut(x, y); |
51 | if (ic.getMagnification()<1.0) imp.repaintWindow(); | |
52 | } else if (arg.equals("orig")) | |
53 | ic.unzoom(); | |
54 | else if (arg.equals("100%")) | |
110 | if (ic.getMagnification()<1.0) | |
111 | imp.repaintWindow(); | |
112 | Recorder.recordCall("Zoom.out(imp);"); | |
113 | } else if (arg.equals("orig")) { | |
114 | unzoom(imp); | |
115 | Recorder.recordCall("Zoom.unzoom(imp);"); | |
116 | } else if (arg.equals("100%")) { | |
117 | waitUntilActivated(imp); | |
55 | 118 | ic.zoom100Percent(); |
56 | else if (arg.equals("to")) { | |
119 | } else if (arg.equals("to")) { | |
57 | 120 | zoomToSelection(imp, ic); |
58 | 121 | Recorder.recordCall("Zoom.toSelection(imp);"); |
59 | } else if (arg.equals("set")) | |
122 | } else if (arg.equals("set")) { | |
60 | 123 | setZoom(imp, -1, -1, -1); |
61 | else if (arg.equals("max")) { | |
62 | ImageWindow win = imp.getWindow(); | |
63 | if (win!=null) { | |
64 | win.maximize(); | |
65 | IJ.wait(100); | |
66 | } | |
124 | } else if (arg.equals("max")) { | |
125 | maximize(imp); | |
126 | Recorder.recordCall("Zoom.maximize(imp);"); | |
67 | 127 | } else if (arg.equals("scale")) |
68 | 128 | scaleToFit(imp); |
69 | 129 | } |
70 | 130 | |
71 | 131 | void zoomToSelection(ImagePlus imp, ImageCanvas ic) { |
132 | waitUntilActivated(imp); | |
72 | 133 | Roi roi = imp.getRoi(); |
73 | 134 | ic.unzoom(); |
74 | 135 | if (roi==null) return; |
80 | 141 | int x = r.x+r.width/2; |
81 | 142 | int y = r.y+r.height/2; |
82 | 143 | mag = ic.getHigherZoomLevel(mag); |
83 | while(r.width*mag<w.width - marginw && r.height*mag<w.height - marginh) { | |
144 | while (r.width*mag<w.width-marginw && r.height*mag<w.height-marginh) { | |
84 | 145 | ic.zoomIn(ic.screenX(x), ic.screenY(y)); |
85 | 146 | double cmag = ic.getMagnification(); |
86 | 147 | if (cmag==32.0) break; |
92 | 153 | /** Based on Albert Cardona's ZoomExact plugin: |
93 | 154 | http://albert.rierol.net/software.html */ |
94 | 155 | void setZoom(ImagePlus imp, double mag, int x, int y) { |
156 | waitUntilActivated(imp); | |
95 | 157 | ImageCanvas ic = imp.getCanvas(); |
96 | 158 | if (ic==null) |
97 | 159 | return; |
155 | 217 | int canvasHeight = (int)(srcHeight*mag+insets.top+insets.bottom+ImageWindow.VGAP*2+win.getSliderHeight()); |
156 | 218 | ic.setSourceRect(new Rectangle(x-srcWidth/2,y-srcHeight/2,srcWidth,srcHeight)); |
157 | 219 | ic.setMagnification(mag); |
220 | ic.setSize(new Dimension((int)(srcWidth*mag), (int)(srcHeight*mag))); | |
158 | 221 | win.setSize(canvasWidth, canvasHeight); |
159 | 222 | return; |
160 | 223 | } |
193 | 256 | } |
194 | 257 | |
195 | 258 | private void scaleToFit(ImagePlus imp) { |
259 | waitUntilActivated(imp); | |
196 | 260 | ImageCanvas ic = imp.getCanvas(); |
197 | 261 | if (ic==null) |
198 | 262 | return; |
76 | 76 | } |
77 | 77 | |
78 | 78 | public void clear(ImageProcessor ip) { |
79 | ip.setColor(Toolbar.getBackgroundColor()); | |
79 | ip.setGlobalBackgroundColor(); | |
80 | 80 | if (isLineSelection()) { |
81 | 81 | if (isStraightLine() && roi.getStrokeWidth()>1) |
82 | 82 | ip.fillPolygon(roi.getPolygon()); |
86 | 86 | ((TextRoi)roi).clear(ip); |
87 | 87 | else |
88 | 88 | ip.fill(); // fill with background color |
89 | ip.setColor(Toolbar.getForegroundColor()); | |
89 | ip.setGlobalForegroundColor(); | |
90 | 90 | } |
91 | 91 | |
92 | 92 | /** |
95 | 95 | */ |
96 | 96 | public void fill(ImageProcessor ip) { |
97 | 97 | if (!IJ.isMacro() || !ip.fillValueSet()) |
98 | ip.setColor(Toolbar.getForegroundColor()); | |
98 | ip.setGlobalForegroundColor(); | |
99 | 99 | if (isLineSelection()) { |
100 | 100 | if (isStraightLine() && roi.getStrokeWidth()>1 && !(roi instanceof Arrow)) { |
101 | 101 | Roi roi2=Roi.convertLineToArea(roi); |
113 | 113 | * replaced by ImageProcessor.draw(Roi) |
114 | 114 | */ |
115 | 115 | public void draw(ImageProcessor ip) { |
116 | ip.setColor(Toolbar.getForegroundColor()); | |
116 | ip.setGlobalForegroundColor(); | |
117 | 117 | roi.drawPixels(ip); |
118 | 118 | if (IJ.altKeyDown()) |
119 | 119 | drawLabel(ip); |
215 | 215 | Rectangle r = ip.getRoi(); |
216 | 216 | if (mask==null) |
217 | 217 | makeMask(ip, r); |
218 | ip.setColor(Toolbar.getBackgroundColor()); | |
218 | ip.setGlobalBackgroundColor(); | |
219 | 219 | int stackSize = imp.getStackSize(); |
220 | 220 | if (stackSize>1) |
221 | 221 | ip.snapshot(); |
233 | 233 | ip.fill(); |
234 | 234 | ip.setRoi(r); // restore original ROI |
235 | 235 | if (sliceCount==stackSize) { |
236 | ip.setColor(Toolbar.getForegroundColor()); | |
236 | ip.setGlobalForegroundColor(); | |
237 | 237 | Roi roi = imp.getRoi(); |
238 | 238 | imp.deleteRoi(); |
239 | 239 | imp.updateAndDraw(); |
1032 | 1032 | overlay.add(roi2); |
1033 | 1033 | } else { |
1034 | 1034 | Rectangle r = roi.getBounds(); |
1035 | int nPoints = ((PolygonRoi)roi).getNCoordinates(); | |
1036 | int[] xp = ((PolygonRoi)roi).getXCoordinates(); | |
1037 | int[] yp = ((PolygonRoi)roi).getYCoordinates(); | |
1038 | int x=r.x, y=r.y; | |
1039 | 1035 | if (!inSituShow) |
1040 | 1036 | ip.setValue(0.0); |
1041 | ip.moveTo(x+xp[0], y+yp[0]); | |
1042 | for (int i=1; i<nPoints; i++) | |
1043 | ip.lineTo(x+xp[i], y+yp[i]); | |
1044 | ip.lineTo(x+xp[0], y+yp[0]); | |
1037 | if (roi instanceof PolygonRoi) { | |
1038 | int nPoints = ((PolygonRoi)roi).getNCoordinates(); | |
1039 | int[] xp = ((PolygonRoi)roi).getXCoordinates(); | |
1040 | int[] yp = ((PolygonRoi)roi).getYCoordinates(); | |
1041 | int x=r.x, y=r.y; | |
1042 | ip.moveTo(x+xp[0], y+yp[0]); | |
1043 | for (int i=1; i<nPoints; i++) | |
1044 | ip.lineTo(x+xp[i], y+yp[i]); | |
1045 | ip.lineTo(x+xp[0], y+yp[0]); | |
1046 | } else | |
1047 | roi.drawPixels(ip); | |
1045 | 1048 | if (showChoice!=BARE_OUTLINES) { |
1046 | 1049 | String s = ResultsTable.d2s(count,0); |
1047 | 1050 | ip.moveTo(r.x+r.width/2-ip.getStringWidth(s)/2, r.y+r.height/2+fontSize/2); |
105 | 105 | ((ExtendedPlugInFilter)theFilter).setNPasses(nPasses); |
106 | 106 | if ((flags&PlugInFilter.NO_CHANGES)==0) { // for filters modifying the image |
107 | 107 | boolean disableUndo = Prefs.disableUndo || (flags&PlugInFilter.NO_UNDO)!=0; |
108 | if (!disableUndo) { | |
108 | if (!disableUndo || ((ip instanceof ColorProcessor)&&WindowManager.getWindow("B&C")!=null)) { | |
109 | 109 | ip.snapshot(); |
110 | 110 | snapshotPixels = ip.getSnapshotPixels(); |
111 | 111 | } |
22 | 22 | private String[] methods = ImageProcessor.getInterpolationMethods(); |
23 | 23 | private static int interpolationMethod = ImageProcessor.BILINEAR; |
24 | 24 | private Overlay overlay; |
25 | private boolean done; | |
25 | 26 | |
26 | 27 | public int setup(String arg, ImagePlus imp) { |
27 | 28 | this.imp = imp; |
80 | 81 | imp.updateAndDraw(); |
81 | 82 | Undo.setup(Undo.COMPOUND_FILTER_DONE, imp); |
82 | 83 | } |
84 | if (done) { // remove grid | |
85 | Overlay ovly = imp.getOverlay(); | |
86 | if (ovly!=null) { | |
87 | ovly.remove(GRID); | |
88 | if (ovly.size()==0) imp.setOverlay(null); | |
89 | } | |
90 | } | |
83 | 91 | } |
84 | 92 | |
85 | 93 | void enlargeCanvas() { |
86 | 94 | imp.unlock(); |
87 | IJ.run("Select All"); | |
88 | IJ.run("Rotate...", "angle="+angle); | |
95 | IJ.run(imp, "Select All", ""); | |
96 | IJ.run(imp, "Rotate...", "angle="+angle); | |
89 | 97 | Roi roi = imp.getRoi(); |
90 | 98 | Rectangle r = roi.getBounds(); |
91 | 99 | if (r.width<imp.getWidth()) r.width = imp.getWidth(); |
93 | 101 | IJ.showStatus("Rotate: Enlarging..."); |
94 | 102 | if (imp.getStackSize()==1) |
95 | 103 | Undo.setup(Undo.COMPOUND_FILTER, imp); |
96 | IJ.run("Canvas Size...", "width="+r.width+" height="+r.height+" position=Center "+(fillWithBackground?"":"zero")); | |
104 | IJ.run(imp, "Canvas Size...", "width="+r.width+" height="+r.height+" position=Center "+(fillWithBackground?"":"zero")); | |
97 | 105 | IJ.showStatus("Rotating..."); |
98 | 106 | } |
99 | 107 | |
100 | 108 | void drawGridLines(int lines) { |
101 | //if (overlay.size()>0 && GRID.equals(overlay.get(0).getName())) | |
102 | // overlay.remove(0); | |
109 | if (overlay==null) | |
110 | return; | |
103 | 111 | overlay.remove(GRID); |
104 | 112 | if (lines==0) |
105 | 113 | return; |
153 | 161 | return DONE; |
154 | 162 | } |
155 | 163 | Overlay ovly = imp.getOverlay(); |
156 | if (ovly!=null) ovly.remove(GRID); | |
157 | if (!enlarge) | |
164 | if (ovly!=null) { | |
165 | ovly.remove(GRID); | |
166 | if (ovly.size()==0) imp.setOverlay(null); | |
167 | } | |
168 | if (enlarge) | |
169 | flags |= NO_CHANGES; // undoable as a "compound filter" | |
170 | else if (imp.getStackSize()==1) | |
158 | 171 | flags |= KEEP_PREVIEW; // standard filter without enlarge |
159 | else if (imp.getStackSize()==1) | |
160 | flags |= NO_CHANGES; // undoable as a "compound filter" | |
172 | done = true; | |
161 | 173 | return IJ.setupDialog(imp, flags); |
162 | 174 | } |
163 | 175 |
704 | 704 | //IJ.log("restore: "+roi.getPosition()+" "+roi.getZPosition()+" "+imp.getNSlices()+" "+imp.getStackSize()); |
705 | 705 | if (setSlice) { |
706 | 706 | boolean hyperstack = imp.isHyperStack(); |
707 | int position = roi.getPosition(); | |
707 | 708 | if (hyperstack && roi.hasHyperStackPosition()) |
708 | 709 | imp.setPosition(roi.getCPosition(), roi.getZPosition(), roi.getTPosition()); |
710 | else if (hyperstack && imp.getNSlices()==1) | |
711 | imp.setPosition(imp.getChannel(), 1, position); | |
712 | else if (hyperstack) | |
713 | imp.setPosition(imp.getChannel(), position, imp.getChannel()); | |
709 | 714 | else if (roi.getZPosition()>0 && imp.getNSlices()==imp.getStackSize()) |
710 | imp.setSlice(roi.getZPosition()); | |
711 | else if (roi.getPosition()>0 && roi.getPosition()<=imp.getStackSize()) | |
712 | imp.setSlice(roi.getPosition()); | |
715 | imp.setSlice(roi.getZPosition()); | |
716 | else if (position>0 && position<=imp.getStackSize()) | |
717 | imp.setSlice(position); | |
713 | 718 | else { |
714 | 719 | String label = (String)listModel.getElementAt(index); |
715 | 720 | int n = getSliceNumber(roi, label); |
429 | 429 | |
430 | 430 | /** Returns the background fill value. */ |
431 | 431 | public abstract double getBackgroundValue(); |
432 | ||
433 | /** Sets the global (Color Picker) foreground color | |
434 | * as the fill/draw color. | |
435 | * @see ij.gui.Toolbar#setForegroundColor(Color) | |
436 | * @see ij.gui.Toolbar#setForegroundValue(double) | |
437 | */ | |
438 | public void setGlobalForegroundColor() { | |
439 | double value = Toolbar.getForegroundValue(); | |
440 | if (Double.isNaN(value)) | |
441 | setColor(Toolbar.getForegroundColor()); | |
442 | else | |
443 | setValue(value); | |
444 | } | |
445 | ||
446 | /** Sets the global (Color Picker) background color | |
447 | * as the fill/draw color. | |
448 | * @see ij.gui.Toolbar#setBackgroundColor(Color) | |
449 | * @see ij.gui.Toolbar#setBackgroundValue(double) | |
450 | */ | |
451 | public void setGlobalBackgroundColor() { | |
452 | double value = Toolbar.getBackgroundValue(); | |
453 | if (Double.isNaN(value)) | |
454 | setColor(Toolbar.getBackgroundColor()); | |
455 | else | |
456 | setValue(value); | |
457 | } | |
432 | 458 | |
433 | 459 | /** Returns the smallest displayed pixel value. */ |
434 | 460 | public abstract double getMin(); |
0 | package ij.text; | |
1 | ||
2 | /** Plugins that implement this interface are notified when | |
3 | a table is opened, closed or updated. The | |
4 | Plugins/Utilities/Monitor Events command uses this interface. | |
5 | */ | |
6 | public interface TableListener { | |
7 | ||
8 | public void TableUpdated(TextPanel table, String event, int row); | |
9 | ||
10 | } |
4 | 4 | import java.io.*; |
5 | 5 | import java.util.Comparator; |
6 | 6 | import java.nio.channels.FileChannel; |
7 | import java.nio.file.*; | |
8 | import java.security.MessageDigest; | |
9 | ||
7 | 10 | |
8 | 11 | /** This class contains static utility methods. */ |
9 | 12 | public class Tools { |
417 | 420 | default: return c; |
418 | 421 | } |
419 | 422 | } |
423 | ||
424 | /** Returns the checksum of a string or file, or "0" if no success. | |
425 | The 'method' argument must be "MD5" or "SHA-256". | |
426 | */ | |
427 | public static String getHash(String method, boolean fromFile, String pathOrString) { | |
428 | method = method.toUpperCase(); | |
429 | boolean md5 = method.contains("MD5"); | |
430 | boolean sha_256 = method.contains("SHA-256"); | |
431 | try { | |
432 | MessageDigest digest = null; | |
433 | if (md5) | |
434 | digest = MessageDigest.getInstance("MD5"); | |
435 | else if(sha_256) | |
436 | digest = MessageDigest.getInstance("SHA-256"); | |
437 | else | |
438 | return "0"; | |
439 | Path path = Paths.get(pathOrString); | |
440 | byte[] encodedhash; | |
441 | if (fromFile) | |
442 | encodedhash = digest.digest(Files.readAllBytes(path)); | |
443 | else | |
444 | encodedhash = digest.digest(pathOrString.getBytes()); | |
445 | ||
446 | return bytesToHex(encodedhash); | |
447 | ||
448 | } catch (Exception e) {} | |
449 | return "0"; | |
450 | } | |
451 | ||
452 | private static String bytesToHex(byte[] hash) { | |
453 | StringBuilder hexString = new StringBuilder(2 * hash.length); | |
454 | for (int i = 0; i < hash.length; i++) { | |
455 | String hex = Integer.toHexString(0xff & hash[i]); | |
456 | if (hex.length() == 1) { | |
457 | hexString.append('0'); | |
458 | } | |
459 | hexString.append(hex); | |
460 | } | |
461 | return hexString.toString(); | |
462 | } | |
420 | 463 | |
421 | 464 | } |
3 | 3 | <title>Release Notes</title> |
4 | 4 | </head> |
5 | 5 | <body> |
6 | ||
7 | <li> <u>1.53j 13 May 2021</u> | |
8 | <ul> | |
9 | <li> Thanks to 'VolkerH', the table created by | |
10 | <i>Image>Overlay>List Elements</i> now | |
11 | displays row numbers and arrow selection 'X' and 'Y' | |
12 | values are the coordinates of the arrow tip. | |
13 | <li> Thanks to Stein Rorvik, added the Color.setForegroundValue() | |
14 | and Color.setBackgroundValue() macro functions and the | |
15 | Toolbar.setForegroundValue(), Toolbar.setBackgroundValue(), | |
16 | ImageProcessor.setGlobalForegroundColor() and | |
17 | ImageProcessor.setGlobalBackgroundColor() methods. | |
18 | <li> Thanks to Norbert Vischer, added the IJ.checksum() macro | |
19 | function, which returns the MD5 (or SHA-256) checksum from | |
20 | a string or file | |
21 | (<a href="http://wsr.imagej.net/macros/ChecksumTest.txt">example</a>). | |
22 | <li> Thanks to Michael Schmid, added the | |
23 | Fit.doWeightedFit(equation,x,y,weights[,initialGuesses]) | |
24 | macro function. | |
25 | <li> Thanks to Norbert Vischer, added the Plot.removeNaNs macro function. | |
26 | <li> Thanks to Laurent Thomas, added support for scrollbars in | |
27 | GenericDialog TextAreas. | |
28 | <li> Thanks to Fred Damen, added the Zoom.in(imp), Zoom.out(imp), | |
29 | Zoom.unzoom(imp) and Zoom.maximize(imp) methods. | |
30 | <li> Thanks to John Dunsmuir, added the PointRoi.addPoint(x,y,position) method. | |
31 | <li> Thanks to Fred Damen, fixed bugs that caused programmatic image | |
32 | window zooming to not work reliably | |
33 | (<a href="http://wsr.imagej.net/macros/js/ZoomTest.js">example</a>). | |
34 | <li> Thanks to Michael Schmid, fixed a bug with HTMLDialogs that | |
35 | caused the initial scroll position to be at the end when displaying | |
36 | more text than what fits in the window. | |
37 | <li> Thanks to 'Sethur', fixed bugs that caused DICOM | |
38 | files using newer VRs (from 2014 and later), such as | |
39 | UC and UR, to fail. | |
40 | <li> Thanks to Bruno Vellutini, fixed a bug that caused images rotated | |
41 | interactively using the "Enlarge image" and "Preview" options to not | |
42 | have the grid lines removed after the rotation. | |
43 | <li> Thanks to Jerome Mutterer, fixed a bug that caused the | |
44 | Prefs.savePreferences() method to throw a null pointer | |
45 | exception if the "ImageJ" window was not open. | |
46 | <li> Thanks to Stein Rorvik, fixed a bug that caused the slice | |
47 | label to be incorrect after importing an image sequence and | |
48 | "Start" was greater than 1 and "Count" was 1. | |
49 | <li> Thanks to Stein Rorvik, fixed a bug that caused | |
50 | the <i>Images to Stack</i> command to ignore slice labels | |
51 | when the "Use titles as labels" option was not enabled. | |
52 | <li> Thanks to 'ghf', worked around a Fiji bug that caused the | |
53 | <i>File>Import>Image Sequence</i> command to fail | |
54 | if the folder contained a ".h5" file. | |
55 | <li> Thanks to Jerome Mutterer, fixed bugs that caused the | |
56 | Stack.setDisplayMode(), Stack.setSlice(), Stack.setFrame() | |
57 | and Stack.setPosition() macro functions to not work as expected | |
58 | in batch mode, and the ImagePlus.setDisplayMode(), | |
59 | ImagePlus.setZ(), ImagePlus.setSetT() and | |
60 | ImagePlus.setPosition() methods to not work as expected when | |
61 | the image was not displayed. | |
62 | <li> Thanks to Nicolas Montes, fixed a bug that caused the | |
63 | particle analyzer to throw an exception when both the | |
64 | "Composite ROIs" and "Show: Outlines" options where used. | |
65 | <li> Thanks to Christian Kremser, fixed a 1.53i regression | |
66 | that caused macro statements like "n=Dialog.getNumber()+1" | |
67 | to generate and error. | |
68 | <li> Thanks to Christian Tischer, fixed a regression that | |
69 | caused the ROI Manager to not set the hyperstack position | |
70 | of ROIs without a c,z,t position. | |
71 | ||
72 | </ul> | |
6 | 73 | |
7 | 74 | <li> <u>1.53i 24 March 2021</u> |
8 | 75 | <ul> |
14 | 81 | is displayed in the status bar. The total number of lines is |
15 | 82 | displayed after <i>Select All</i>. |
16 | 83 | <li> Thanks to Norbert Vischer, ImageJ now corrects the orientation of |
17 | phone camera photos. This does not work on Fiji because it is | |
84 | phone camera photos. Does not work on Fiji because it is | |
18 | 85 | missing Exif_Reader.jar. |
19 | 86 | <li> Improved the Plugins>Utilities>Benchmark command. It now |
20 | 87 | suppresses subordinate status bar messages and displays |
24 | 91 | in the Log window if it was not called from a macro and a |
25 | 92 | threshold was not set. |
26 | 93 | <li> Thanks to Laurent Thomas, <i>File>Save As>Image Sequence</i>, |
27 | with "Use slice labels as names" enabled, now supports file names as | |
94 | when "Use slice labels as names" is enabled, now supports file names as | |
28 | 95 | long as 111 characters, excluding the extension. |
29 | <li> Thanks to Michael Schmid, ImageJ displays “horizontal” in the | |
30 | image info line of column average plots and “vertical” for | |
96 | <li> Thanks to Michael Schmid, ImageJ displays "horizontal" in the | |
97 | image info line of column average plots and "vertical" with | |
31 | 98 | row average plots. |
32 | 99 | <li> Thanks to Jerome Mutterer, added the showStatus(message,options) |
33 | 100 | macro function and the IJ.showStatus(message,options) method |
34 | 101 | (<a href="http://wsr.imagej.net/macros/FlashingStatusMessages.txt">example</a>). |
35 | <li> Added the Overlay.update(index) macro function. DOCUMENT | |
102 | <li> Added the Overlay.update(index) macro function. | |
36 | 103 | <li> Added the ImagePlus.setBorderColor() method, |
37 | 104 | used by <i>Image>Color>Split Channels</i> to |
38 | 105 | colorize image borders. |
41 | 108 | methods. |
42 | 109 | <li> Thanks to Herbie Gluender, fixed a bug that caused the status bar |
43 | 110 | to stop being updated after activating an overlay selection. |
44 | <li> Thanks to Fred Damen, fixed a bug with one slice stacks where the slice label | |
45 | is not displayed on the image window info line or in the <i>Image>Show Info</i> | |
46 | window. | |
111 | <li> Thanks to Fred Damen, fixed a bug with one slice stacks that caused the | |
112 | slice label to not be displayed on the image info line or in the | |
113 | <i>Image>Show Info</i> window. | |
47 | 114 | <li> Thanks to Antoneta, fixed a bug with the <i>Flatten</i> command that caused |
48 | 115 | multi-point selections on stacks to lose counter information. |
49 | 116 | <li> Thanks to Herbie Gluender, fixed a bug that caused unexpected overlay |
63 | 130 | WaitForUserDialogs to not be scaled on high-resolution |
64 | 131 | screens. |
65 | 132 | <li> Thanks to Stein Rorvik, fixed a macro interpreter bug that caused |
66 | if statements using built in string functions to fail | |
133 | if statements using string functions to sometimes fail | |
67 | 134 | (e.g., "if (getTitle.length>10) ..."). |
68 | 135 | <li> Thanks to Norbert Vischer, fixed a bug that caused the |
69 | 136 | first column of tables copied to the clipboard to be missing |
74 | 141 | the "Properties..." command in the ROI Manager to not work as |
75 | 142 | expected when changing the stroke color. |
76 | 143 | <li> Thanks to Stein Rorvik, fixed a 1.53i regression that caused |
77 | the str.trim() macro function to fail. | |
144 | the str.trim() macro function to sometimes fail. | |
78 | 145 | </ul> |
79 | 146 | |
80 | 147 | <li> <u>1.53h 04 February 2021</u> |