//************************************************************************************** //Load FIJI-Specific Brush Tool with modifiable diameter by Ctrl + Drag //Source: https://imagej.nih.gov/ij/developer/source/ij/plugin/tool/BrushTool.java.html //************************************************************************************** package ij.plugin.tool; import ij.*; import ij.process.*; import ij.gui.*; import ij.plugin.Colors; import java.awt.*; import java.awt.event.*; import java.util.Vector; // Versions // 2012-07-22 shift to confine horizontally or vertically, ctrl-shift to resize, ctrl to pick /** This class implements the Paintbrush Tool, which allows the user to draw on an image, or on an Overlay if "Paint on overlay" is enabled. */ public class BrushTool extends PlugInTool implements Runnable { private final static int UNCONSTRAINED=0, HORIZONTAL=1, VERTICAL=2, RESIZING=3, RESIZED=4, IDLE=5; //mode flags private static String BRUSH_WIDTH_KEY = "brush.width"; private static String PENCIL_WIDTH_KEY = "pencil.width"; private static String CIRCLE_NAME = "brush-tool-overlay"; private static final String LOC_KEY = "brush.loc"; private static final String OVERLAY_KEY = "brush.overlay"; private String widthKey; private int width; private ImageProcessor ip; private int mode; //resizing brush or motion constrained horizontally or vertically private int xStart, yStart; private int oldWidth; private boolean isPencil; private Overlay overlay; private Options options; private GenericDialog gd; private ImageRoi overlayImage; private boolean paintOnOverlay; private static BrushTool brushInstance; public void run(String arg) { isPencil = "pencil".equals(arg); widthKey = isPencil ? PENCIL_WIDTH_KEY : BRUSH_WIDTH_KEY; width = (int)Prefs.get(widthKey, isPencil ? 1 : 5); paintOnOverlay = Prefs.get(OVERLAY_KEY, false); Toolbar.addPlugInTool(this); if (!isPencil) brushInstance = this; } public void mousePressed(ImagePlus imp, MouseEvent e) { ImageCanvas ic = imp.getCanvas(); int x = ic.offScreenX(e.getX()); int y = ic.offScreenY(e.getY()); xStart = x; yStart = y; checkForOverlay(imp); if (overlayImage!=null) ip = overlayImage.getProcessor(); else ip = imp.getProcessor(); int ctrlMask = IJ.isMacintosh() ? InputEvent.META_MASK : InputEvent.CTRL_MASK; int resizeMask = InputEvent.SHIFT_MASK | ctrlMask; if ((e.getModifiers() & resizeMask) == resizeMask) { mode = RESIZING; oldWidth = width; return; } else if ((e.getModifiers() & ctrlMask) != 0) { boolean altKeyDown = (e.getModifiers() & InputEvent.ALT_MASK) != 0; ic.setDrawingColor(x, y, altKeyDown); //pick color from image (ignore overlay) if (!altKeyDown) setColor(Toolbar.getForegroundColor()); mode = IDLE; return; } mode = UNCONSTRAINED; ip.snapshot(); Undo.setup(Undo.FILTER, imp); ip.setLineWidth(width); if (e.isAltDown()) { if (overlayImage!=null) ip.setColor(0); //erase else ip.setColor(Toolbar.getBackgroundColor()); } else ip.setColor(Toolbar.getForegroundColor()); ip.moveTo(x, y); if (!e.isShiftDown()) { ip.lineTo(x, y); if (overlayImage!=null) { overlayImage.setProcessor(ip); imp.draw(); } else imp.updateAndDraw(); } } private void checkForOverlay(ImagePlus imp) { overlayImage = getOverlayImage(imp); if (overlayImage==null && paintOnOverlay) { ImageProcessor overlayIP = new ColorProcessor(imp.getWidth(), imp.getHeight()); ImageRoi imageRoi = new ImageRoi(0, 0, overlayIP); imageRoi.setZeroTransparent(true); imageRoi.setName("[Brush]"); Overlay overlay = imp.getOverlay(); if (overlay==null) overlay = new Overlay(); overlay.add(imageRoi); overlay.selectable(false); imp.setOverlay(overlay); overlayImage = imageRoi; } } private ImageRoi getOverlayImage(ImagePlus imp) { if (!paintOnOverlay) return null; Overlay overlay = imp.getOverlay(); if (overlay==null) return null; Roi roi = overlay.get("[Brush]"); if (roi==null||!(roi instanceof ImageRoi)) return null; Rectangle bounds = roi.getBounds(); if (bounds.x!=0||bounds.y!=0||bounds.width!=imp.getWidth()||bounds.height!=imp.getHeight()) return null; return (ImageRoi)roi; } public void mouseDragged(ImagePlus imp, MouseEvent e) { if (mode == IDLE) return; ImageCanvas ic = imp.getCanvas(); int x = ic.offScreenX(e.getX()); int y = ic.offScreenY(e.getY()); if (mode == RESIZING) { showToolSize(x-xStart, imp); return; } if ((e.getModifiers() & InputEvent.SHIFT_MASK) != 0) { //shift constrains if (mode == UNCONSTRAINED) { //first movement with shift down determines direction if (Math.abs(x-xStart) > Math.abs(y-yStart)) mode = HORIZONTAL; else if (Math.abs(x-xStart) < Math.abs(y-yStart)) mode = VERTICAL; else return; //constraint direction still unclear } if (mode == HORIZONTAL) y = yStart; else if (mode == VERTICAL) x = xStart; } else { xStart = x; yStart = y; mode = UNCONSTRAINED; } ip.setLineWidth(width); ip.lineTo(x, y); if (overlayImage!=null) { overlayImage.setProcessor(ip); imp.draw(); } else imp.updateAndDraw(); } public void mouseReleased(ImagePlus imp, MouseEvent e) { if (mode==RESIZING) { if (overlay!=null && overlay.size()>0 && CIRCLE_NAME.equals(overlay.get(overlay.size()-1).getName())) { overlay.remove(overlay.size()-1); imp.setOverlay(overlay); } overlay = null; if (e.isShiftDown()) { setWidth(width); Prefs.set(widthKey, width); } } } private void setWidth(int width) { if (gd==null) return; Vector numericFields = gd.getNumericFields(); TextField widthField = (TextField)numericFields.elementAt(0); widthField.setText(""+width); Vector sliders = gd.getSliders(); Scrollbar sb = (Scrollbar)sliders.elementAt(0); sb.setValue(width); } private void setColor(Color c) { if (gd==null) return; String name = Colors.colorToString2(c); if (name.length()>0) { Vector choices = gd.getChoices(); Choice ch = (Choice)choices.elementAt(0); ch.select(name); } } private void showToolSize(int deltaWidth, ImagePlus imp) { if (deltaWidth !=0) { width = oldWidth + deltaWidth; if (width<1) width=1; Roi circle = new OvalRoi(xStart-width/2, yStart-width/2, width, width); circle.setName(CIRCLE_NAME); circle.setStrokeColor(Color.red); overlay = imp.getOverlay(); if (overlay==null) overlay = new Overlay(); else if (overlay.size()>0 && CIRCLE_NAME.equals(overlay.get(overlay.size()-1).getName())) overlay.remove(overlay.size()-1); overlay.add(circle); imp.setOverlay(overlay); } IJ.showStatus((isPencil?"Pencil":"Brush")+" width: "+ width); } public void showOptionsDialog() { Thread thread = new Thread(this, "Brush Options"); thread.setPriority(Thread.NORM_PRIORITY); thread.start(); } public String getToolName() { if (isPencil) return "Pencil Tool"; else return "Paintbrush Tool"; } public String getToolIcon() { // C123 is the foreground color if (isPencil) return "C037L4990L90b0Lc1c3L82a4Lb58bL7c4fDb4L494fC123L5a5dL6b6cD7b"; else return "N02 C123H2i2g3e5c6b9b9e8g6h4i2i0 C037Lc07aLf09b P2i3e5c6b9b9e8g6h4i2i0"; } public void run() { new Options(); } class Options implements DialogListener { Options() { if (gd != null) { gd.toFront(); return; } options = this; showDialog(); } public void showDialog() { Color color = Toolbar.getForegroundColor(); String colorName = Colors.colorToString2(color); String name = isPencil?"Pencil":"Brush"; gd = GUI.newNonBlockingDialog(name+" Options"); gd.addSlider(name+" width:", 1, 50, width); //gd.addSlider("Transparency (%):", 0, 100, transparency); gd.addChoice("Color:", Colors.getColors(colorName), colorName); gd.addCheckbox("Paint on overlay", paintOnOverlay); gd.addDialogListener(this); gd.addHelp(getHelp()); Point loc = Prefs.getLocation(LOC_KEY); if (loc!=null) { gd.centerDialog(false); gd.setLocation (loc); } gd.showDialog(); Prefs.saveLocation(LOC_KEY, gd.getLocation()); gd = null; } public boolean dialogItemChanged(GenericDialog gd, AWTEvent e) { if (e!=null && e.toString().contains("Undo")) { ImagePlus imp = WindowManager.getCurrentImage(); if (imp!=null) IJ.run("Undo"); return true; } width = (int)gd.getNextNumber(); if (gd.invalidNumber() || width<0) width = (int)Prefs.get(widthKey, 1); //transparency = (int)gd.getNextNumber(); //if (gd.invalidNumber() || transparency<0 || transparency>100) // transparency = 100; String colorName = gd.getNextChoice(); paintOnOverlay = gd.getNextBoolean(); Color color = Colors.decode(colorName, null); Toolbar.setForegroundColor(color); Prefs.set(widthKey, width); Prefs.set(OVERLAY_KEY, paintOnOverlay); return true; } } public static void setBrushWidth(int width) { if (brushInstance!=null) { Color c = Toolbar.getForegroundColor(); brushInstance.setWidth(width); Toolbar.setForegroundColor(c); } } private String getHelp() { String ctrlString = IJ.isMacintosh()? "cmd":"ctrl"; return "" +"" +"Key modifiers" +"" +"Use Edit>Selection>Create Mask to create
a mask from the painted overlay. " +"Use
Image>Overlay>Remove Overlay to remove
the painted overlay.
" +"
" +"
"; } } //************************************************************************************** // Copyright (C) 2022 Mary E. Cole / Pore Extractor 2D // First Release Date: July 2022 //************************************************************************************** requires("1.53g"); var filemenu = newMenu("PoreExtractor 2D Menu Tool", newArray("Clip Trabeculae","Wand ROI Selection","ROI Touchup","-", "Image Pre-Processing", "Pore Extractor", "Pore Modifier", "Pore Analyzer", "-", "Set Global Preferences","About", "Cite")); macro "PoreExtractor 2D Menu Tool - C000D6cC000D5cD7cC000D5bC000D4aC000Da3DacC000D79C000D4bC000D7dC111D8dD93Db3C111D6bC111D6dC111Dd4C111DbcC111De5C111D8cC222D62C222D72C222D9cD9dC222D52C222D95C222D38C222C333D82C333D96C333D88C333D97C333Dc3C333D84C333D26D83C333C444D37C444D42C444D75C444Dc4De8C444D7aC444D94C444D49DdbC444D15D6aD8aD9aC444D4cDe9C555D5dC555D92C555D5aC555D39C555DdaC555D32C555D7bC555De6C555D89C555C666DcbC666DadDccC666De4C666D13C666D74D78C666De7C777D87C777D27C777D04C777D23C777C888D65C888D05C888Da2C888Df6C888D3aC888D22C888D16D99C999Dd3DeaC999D69Da6C999Db4C999D73Da5C999Da4C999Df7CaaaD63D85CaaaDaaCaaaDd5CaaaDabCaaaDf5CaaaD36CaaaD53CbbbD33D48CbbbDbbCbbbDd9CbbbDbdCbbbD3bCbbbCcccDb2CcccD43CcccD98CcccDa9CcccD7eDf8CcccD8bCcccD14CcccD6eCcccD8eCcccCdddD03CdddD4dCdddD25D66CdddD76D9bCdddDa7CdddD12D28D61CdddDdcCdddD64D9eCeeeD71CeeeD51CeeeDebCeeeD59D86CeeeDd8CeeeD81DcaCeeeD47Dc2CeeeD41CeeeD77Df4CfffDe3Df9CfffD3cD68CfffD06D5eDc5CfffDcdCfffDd6CfffD17D29D91CfffD31CfffDaeCfffD24D35Dd7CfffD02D21D46D55D56Da8Db5Dd2Dfa"{ //Turn hotkeys off hotkeys = "None"; call("ij.Prefs.set", "hotkeys.string", hotkeys); //Call user-selected function PE2Dmacro = getArgument(); if (PE2Dmacro!="-") { if (PE2Dmacro=="Set Global Preferences") { GlobalPreferences(); } else if (PE2Dmacro=="Clip Trabeculae") { ClipTrabeculae(); } else if (PE2Dmacro=="Wand ROI Selection") { WandROI(); } else if (PE2Dmacro=="ROI Touchup") { ROITouchup(); } else if (PE2Dmacro=="Image Pre-Processing") { Preprocess(); } else if (PE2Dmacro=="Pore Extractor") { Extract(); } else if (PE2Dmacro=="Pore Modifier") { Modify(); } else if (PE2Dmacro=="Pore Analyzer") { Analyze(); } else if (PE2Dmacro=="About") { About(); } else if (PE2Dmacro=="Cite") { Cite(); } } } //************************************************************************************** // Set Global Preferences Function // Copyright (C) 2022 Mary E. Cole / Pore Extractor 2D // First Release Date: July 2022 //************************************************************************************** function GlobalPreferences() { requires("1.53g"); //Get current presets scale = call("ij.Prefs.get", "scale.number", ""); if(scale == "") {scale = 0;} node_size = call("ij.Prefs.get", "nodesize.number", ""); if(node_size == "") {node_size = 1;} border_color_choice=call("ij.Prefs.get", "border_color_choice.string", ""); if(border_color_choice == "") {border_color_choice = "cyan";} roi_color_choice=call("ij.Prefs.get", "roi_color_choice.string", ""); if(roi_color_choice == "") {roi_color_choice = "cyan";} roi_color_save=call("ij.Prefs.get", "roi_color_save.string", ""); if(roi_color_save == "") {roi_color_save = "magenta";} roi_color_select=call("ij.Prefs.get", "roi_color_select.string", ""); if(roi_color_select == "") {roi_color_select = "green";} //Dark Color Options if (border_color_choice == "red"){border_color_choice_display = "#bf0000";} if (border_color_choice == "green"){border_color_choice_display = "#007f04";} if (border_color_choice == "blue"){border_color_choice_display = "#0a13c2";} if (border_color_choice == "magenta"){border_color_choice_display = "#c20aaf";} if (border_color_choice == "cyan"){border_color_choice_display = "#008B8B";} if (border_color_choice == "yellow"){border_color_choice_display = "#ab9422";} if (border_color_choice == "orange"){border_color_choice_display = "#b87d25";} if (border_color_choice == "black"){border_color_choice_display = "black";} if (border_color_choice == "white"){border_color_choice_display = "#a8a6a3";} if (roi_color_choice == "red"){roi_color_choice_display = "#bf0000";} if (roi_color_choice == "green"){roi_color_choice_display = "#007f04";} if (roi_color_choice == "blue"){roi_color_choice_display = "#0a13c2";} if (roi_color_choice == "magenta"){roi_color_choice_display = "#c20aaf";} if (roi_color_choice == "cyan"){roi_color_choice_display = "#008B8B";} if (roi_color_choice == "yellow"){roi_color_choice_display = "#ab9422";} if (roi_color_choice == "orange"){roi_color_choice_display = "#b87d25";} if (roi_color_choice == "black"){roi_color_choice_display = "black";} if (roi_color_choice == "white"){roi_color_choice_display = "#a8a6a3";} if (roi_color_save == "red"){roi_color_save_display = "#bf0000";} if (roi_color_save == "green"){roi_color_save_display = "#007f04";} if (roi_color_save == "blue"){roi_color_save_display = "#0a13c2";} if (roi_color_save == "magenta"){roi_color_save_display = "#c20aaf";} if (roi_color_save == "cyan"){roi_color_save_display = "#008B8B";} if (roi_color_save == "yellow"){roi_color_save_display = "#ab9422";} if (roi_color_save == "orange"){roi_color_save_display = "#b87d25";} if (roi_color_save == "black"){roi_color_save_display = "black";} if (roi_color_save == "white"){roi_color_save_display = "#a8a6a3";} if (roi_color_select == "red"){roi_color_select_display = "#bf0000";} if (roi_color_select == "green"){roi_color_select_display = "#007f04";} if (roi_color_select == "blue"){roi_color_select_display = "#0a13c2";} if (roi_color_select == "magenta"){roi_color_select_display = "#c20aaf";} if (roi_color_select == "cyan"){roi_color_select_display = "#008B8B";} if (roi_color_select == "yellow"){roi_color_select_display = "#ab9422";} if (roi_color_select == "orange"){roi_color_select_display = "#b87d25";} if (roi_color_select == "black"){roi_color_select_display = "black";} if (roi_color_select == "white"){roi_color_select_display = "#a8a6a3";} //Create array of color options roi_color = newArray("red", "green", "blue","magenta", "cyan", "yellow", "orange", "black", "white"); //Create Dialog Dialog.createNonBlocking("Global Preferences"); Dialog.setInsets(10,0,0) Dialog.addMessage("Default Spacing",14,"#7f0000"); //Image Scale Dialog.setInsets(0,0,0) Dialog.addNumber("Image Scale:",scale); Dialog.addToSameRow(); Dialog.addMessage("pixels / mm"); //Node Spacing Dialog.setInsets(0,0,0) Dialog.addNumber("Node Spacing:",node_size); Dialog.addToSameRow(); Dialog.addMessage("pixels"); //Default Colors Dialog.setInsets(10,0,0) Dialog.addMessage("Border ROI Outlines",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addChoice("Border ROI Color:", roi_color, border_color_choice); Dialog.addToSameRow(); Dialog.addMessage("Current Color",12,border_color_choice_display) Dialog.setInsets(10,0,0) Dialog.addMessage("Pore ROI Colors",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addChoice("Original ROI Color:", roi_color, roi_color_choice); Dialog.addToSameRow(); Dialog.addMessage("Current Color",12,roi_color_choice_display) Dialog.setInsets(0,0,0) Dialog.addChoice("Modified Pore Color:", roi_color, roi_color_save); Dialog.addToSameRow(); Dialog.addMessage("Current Color",12,roi_color_save_display) Dialog.setInsets(0,0,0) Dialog.addChoice("Wand + Freehand Color:", roi_color, roi_color_select); Dialog.addToSameRow(); Dialog.addMessage("Current Color",12,roi_color_select_display) Dialog.show(); scale = Dialog.getNumber(); call("ij.Prefs.set", "scale.number", scale); node_size = Dialog.getNumber();; call("ij.Prefs.set", "nodesize.number", node_size); border_color_choice = Dialog.getChoice(); call("ij.Prefs.set", "border_color_choice.string", border_color_choice ); roi_color_choice = Dialog.getChoice();; call("ij.Prefs.set", "roi_color_choice.string", roi_color_choice ); roi_color_save = Dialog.getChoice();;; call("ij.Prefs.set", "roi_color_save.string", roi_color_save); roi_color_select = Dialog.getChoice();;;; call("ij.Prefs.set", "roi_color_select.string", roi_color_select); exit(); } //************************************************************************************** // Clip Trabeculae Function // Copyright (C) 2022 Mary E. Cole / Pore Extractor 2D // First Release Date: July 2022 //************************************************************************************** function ClipTrabeculae() { setBatchMode(false); requires("1.53g"); //Welcome dialog Dialog.createNonBlocking("Welcome to Clip Trabeculae!"); Dialog.setInsets(0,0,0) Dialog.addMessage("Prepare to Load:",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("Unmodified brightfield cross-section"); Dialog.setInsets(5,0,0) Dialog.addMessage("Warning:",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("Any current images or windows will be closed!"); Dialog.setInsets(5,0,0) Dialog.addMessage("Click OK to begin!",14,"#306754"); Dialog.show(); //Close existing images and windows while (nImages>0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i=0){ roiManager("Deselect"); roiManager("Delete"); } selectWindow("ROI Manager"); run("Close"); // Message dialog selectImage(origimg); do{ //Keyboard shortcut popup Table.create("Clip Trabeculae Keyboard Shortcuts"); shortcuts = newArray( "[Ctrl + Z]", "[Ctrl + Shift + Drag Mouse]", "[4]", "[+/-]", "[5]", "[Space]", "[7]", "[w]", "[b]", "[F12]"); shortcutfunctions = newArray( "Delete last paintbrush stroke", "Increase or decrease paintbrush diameter", "Zoom Tool (Left-Click = Zoom In, Right-Click = Zoom Out)", "Zoom In (+) or Out (-) On Cursor Without Switching Tools", "Scrolling Tool (Grab and Drag)", "Scrolling Tool (Grab and Drag) Without Switching Tools", "Paintbrush Tool (Click and Drag)", "Make Paintbrush White", "Make Paintbrush Black", "Save Copy of Current Image Modification"); Table.setColumn("Keyboard Shortcut", shortcuts); Table.setColumn("Function", shortcutfunctions); Table.setLocationAndSize(0, 0, 600, 310) //Instructions popup Dialog.createNonBlocking("Clip Trabeculae Instructions"); Dialog.setInsets(0,0,0) Dialog.addMessage("Instructions",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("- Turn on the FIJI brush tool under >> Brush") Dialog.setInsets(0,0,0) Dialog.addMessage("- Select the paintbrush tool [7]") Dialog.setInsets(0,0,0) Dialog.addMessage("- Change paintbrush diameter with [Ctrl + Shift + Drag Mouse]") Dialog.setInsets(0,0,0) Dialog.addMessage(" or by double-clicking the paintbrush icon") Dialog.setInsets(0,0,0) Dialog.addMessage("- Use black paintbrush [b] to seal both ends of cracks through cortex") Dialog.setInsets(0,0,0) Dialog.addMessage("- Use white paintbrush [w] to separate trabeculae from endosteal border") Dialog.setInsets(5,0,0) Dialog.addMessage("Macro Options",14,"#7f0000"); Dialog.setInsets(0,0,0) modifydialog = newArray("Re-display keyboard shortcuts","Save modifications and exit macro"); Dialog.addRadioButtonGroup("", modifydialog, 2, 1, "Re-display keyboard shortcuts"); Dialog.setLocation(0, 315); Dialog.show(); exitmodify = Dialog.getRadioButton(); } while (exitmodify!="Save modifications and exit macro"); //Save final image and exit macro showStatus("!Saving Clipped Image..."); dirpath= call("ij.Prefs.get", "appendimage.dir", "nodir"); origname= call("ij.Prefs.get", "orig.string", ""); saveAs("Tiff", dirpath+origname+".tif"); //Reset colors to normal run("Colors...", "foreground=white background=black"); //Close existing images and windows while (nImages>0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i=0){ roiManager("Deselect"); roiManager("Delete"); } selectWindow("ROI Manager"); run("Close"); // Message dialog selectImage(origimg); do{ //Keyboard shortcut popup Table.create("Wand ROI Selection Keyboard Shortcuts"); shortcuts = newArray( "[Ctrl + Z]", "[Ctrl + Shift + Drag Mouse]", "[Backspace]", "[0]", "[1]", "[2]", "[3]", "[4]", "[+/-]", "[5]", "[Space]", "[7]", "[w]", "[b]", "[F4]", "[F12]"); shortcutfunctions = newArray( "Delete last paintbrush stroke or selection clearing", "Increase or decrease paintbrush diameter", "Clear Inside Selection", "Clear Outside Selection", "Wand Tool", "Adjust Wand Tool Tolerance", "Freehand Selection Tool (Draw with Mouse/Stylus)", "Zoom Tool (Left-Click = Zoom In, Right-Click = Zoom Out)", "Zoom In (+) or Out (-) On Cursor Without Switching Tools", "Scrolling Tool (Grab and Drag)", "Scrolling Tool (Grab and Drag) Without Switching Tools", "Paintbrush Tool (Click and Drag)", "Make Paintbrush White", "Make Paintbrush Black", "Reset Wand Tool Tolerance to Zero", "Save Copy of Current Image Modification"); Table.setColumn("Keyboard Shortcut", shortcuts); Table.setColumn("Function", shortcutfunctions); Table.setLocationAndSize(0, 0, 600, 435) //Instructions popup Dialog.createNonBlocking("Wand ROI Selection Instructions"); Dialog.setInsets(0,0,0) Dialog.addMessage("Instructions",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("Clear Outside Periosteal Border",14,"#0a13c2"); Dialog.setInsets(0,0,0) Dialog.addMessage("- Reset Wand tool tolerance to zero [F4]") Dialog.setInsets(0,0,0) Dialog.addMessage("- Select Wand tool [1]") Dialog.setInsets(0,0,0) Dialog.addMessage("- Click just outside bone section with Wand tool") Dialog.setInsets(0,0,0) Dialog.addMessage("- Increase Wand tool tolerance [2] until periosteal border is selected") Dialog.setInsets(0,0,0) Dialog.addMessage("- Use [0] to clear the area outside the section to black") Dialog.setInsets(0,0,0) Dialog.addMessage("Clear Inside Endosteal Border",14,"#0a13c2"); Dialog.setInsets(0,0,0) Dialog.addMessage("- Reset Wand tool tolerance to zero [F4]") Dialog.setInsets(0,0,0) Dialog.addMessage("- Select Wand tool [1]") Dialog.setInsets(0,0,0) Dialog.addMessage("- Click just inside marrow cavity with Wand tool") Dialog.setInsets(0,0,0) Dialog.addMessage("- Increase Wand tool tolerance [2] until ensosteal border is selected") Dialog.setInsets(0,0,0) Dialog.addMessage("- Use [Backspace] to clear the area inside the marrow cavity to black") Dialog.setInsets(0,0,0) Dialog.addMessage("Border Cleanup",14,"#0a13c2"); Dialog.setInsets(0,0,0) Dialog.addMessage("- Turn on the FIJI brush tool under >> Brush") Dialog.setInsets(0,0,0) Dialog.addMessage("- Select paintbrush [7], convert to black [b], and paint over debris") Dialog.setInsets(0,0,0) Dialog.addMessage("- Or use Freehand Selection tool [3] to circle debris and delete [Backspace]") Dialog.setInsets(0,0,5) Dialog.addMessage("Change Selection Color",14,"#7f0000"); roi_color = newArray("red", "green", "blue","magenta", "cyan", "yellow", "orange", "black", "white"); Dialog.setInsets(0,0,0) Dialog.addChoice("Color:", roi_color, border_color_choice); Dialog.setInsets(5,0,0) Dialog.addMessage("Macro Options",14,"#7f0000"); Dialog.setInsets(0,0,0) modifydialog = newArray("Re-display keyboard shortcuts","Change selection color","Save modifications and exit macro"); Dialog.addRadioButtonGroup("", modifydialog, 3, 1, "Re-display keyboard shortcuts"); Dialog.setLocation(0, 440); Dialog.show(); exitmodify = Dialog.getRadioButton(); border_color_choice = Dialog.getChoice(); if(exitmodify == "Change selection color"){ call("ij.Prefs.set", "border_color_choice.string", border_color_choice ); run("Colors...", "foreground=white background=black selection="+ border_color_choice); } } while (exitmodify!="Save modifications and exit macro"); //Save final ROIs showStatus("!Saving Cleared Image..."); //Hide image origimg=getTitle(); selectImage(origimg); setBatchMode("hide"); //Save final image selectImage(origimg); run("Select None"); run("Remove Overlay"); dirpath= call("ij.Prefs.get", "appendimage.dir", "nodir"); origname= call("ij.Prefs.get", "orig.string", ""); saveAs("Tiff", dirpath+origname+".tif"); //Save final ROIs showStatus("!Selecting Border ROIs..."); //Set measurements to area run("Set Measurements...", "area redirect=None decimal=3"); //Threshold Total Area (TA) origimg=getTitle(); selectImage(origimg); run("Select None"); run("Remove Overlay"); selectImage(origimg); run("Duplicate...", " "); cortex=getTitle(); setBatchMode("hide"); run("8-bit"); setAutoThreshold("Otsu dark"); setThreshold(1, 255); setOption("BlackBackground", true); run("Convert to Mask"); run("Clear Results"); selectImage(cortex); run("Analyze Particles...", "size=1-Infinity display exclude clear add"); //Get maximum value of TA, in case of absolute white artifacts roiManager("Measure"); selectWindow("Results"); setLocation(screenWidth, screenHeight); area=Table.getColumn("Area"); Array.getStatistics(area, min, max, mean, std); TA=(max); var Trow = ""; run("Clear Results"); roiManager("Measure"); area=Table.getColumn("Area"); ranks = Array.rankPositions(area); Array.reverse(ranks); Trow=ranks[0]; //Save TA roi roiManager("Select",Trow); roiManager("Save", dir+origname+"_"+"TtAr.roi"); roiManager("deselect"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Clear results run("Clear Results"); //Isolate Marrow Area (MA) //Invert cortex by thresholding selectImage(cortex); run("Select None"); run("Remove Overlay"); selectImage(cortex); run("Invert"); run("Analyze Particles...", "size=1-Infinity display exclude clear add"); //Get maximum value of MA, in case of absolute white artifacts roiManager("Measure"); area=Table.getColumn("Area"); Array.getStatistics(area, min, max, mean, std); MA=(max); var Mrow = ""; run("Clear Results"); roiManager("Measure"); area=Table.getColumn("Area"); ranks = Array.rankPositions(area); Array.reverse(ranks); Mrow=ranks[0]; //Save MA roi roiManager("Select",Mrow); roiManager("Save", dir+origname+"_"+"EsAr.roi"); roiManager("deselect"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Clear results run("Clear Results"); selectImage(cortex); close(); //Reopen TA and MA roiManager("Open", dir+origname+"_"+"TtAr.roi"); roiManager("Open", dir+origname+"_"+"EsAr.roi"); //Combine TA (ROI 0) and MA (ROI 1) as CA (ROI 2) roiManager("Select", newArray(0,1)); roiManager("XOR"); roiManager("Add"); roiManager("Select", 0); roiManager("Rename", "TtAr"); roiManager("Select", 1); roiManager("Rename", "EsAr"); roiManager("Select", 2); roiManager("Rename", "CtAr"); //Save as ROI set roiManager("Deselect"); roiManager("Save", dir+base+"_"+"Borders_RoiSet.zip"); //Delete temp ROIs TtArROI = dir+origname+"_"+"TtAr.roi"; if (File.exists(TtArROI)) {File.delete(TtArROI);} EsArROI = dir+origname+"_"+"EsAr.roi"; if (File.exists(EsArROI)) {File.delete(EsArROI);} //Close existing images and windows while (nImages>0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i=0){ roiManager("Deselect"); roiManager("Delete"); } //Open ROI set roiManager("open", roipath); //Find index of CtAr nR = roiManager("Count"); for (i=0; i [Shift + Drag]", "[6] -> [Alt + Drag]", "[8]", "[9]", "[r]", "[s]", "[F8]", "[F9]"); shortcutfunctions = newArray( "Clear Selection Brush Modifications", "Freehand Selection Tool (Click and Drag Node)", "Zoom Tool (Left-Click = Zoom In, Right-Click = Zoom Out)", "Zoom In (+) or Out (-) On Cursor Without Switching Tools", "Scrolling Tool (Grab and Drag)", "Scrolling Tool (Grab and Drag) Without Switching Tools", "Selection Brush Tool (Push Selection Out)", "Selection Brush Tool (Push Selection In)", "Update ROI After Selection Brush Modification", "Create or Change ROI Node Spacing", "Revert to Previous ROI After Modiciation", "Save Temporary Copy of Current ROI Set", "Decrease Selection Brush Size 5 px", "Increase Selection Brush Size 5 px"); Table.setColumn("Keyboard Shortcut", shortcuts); Table.setColumn("Function", shortcutfunctions); Table.setLocationAndSize(0, 0, 600, 395) Dialog.createNonBlocking("ROI Touchup"); Dialog.setInsets(5,0,0) Dialog.addMessage("Adjusting ROI with the Selection Brush",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("- Click desired border ROI in ROI manager"); Dialog.setInsets(0,0,0) Dialog.addMessage("- Switch to selection brush tool [6]"); Dialog.setInsets(0,0,0) Dialog.addMessage("- Selection brush size can be decreased [F8] or increased [F9] by 5 px"); Dialog.setInsets(0,0,0) Dialog.addMessage("- To expand ROI border outward, hold [Shift] and drag mouse against border from inside"); Dialog.setInsets(0,0,0) Dialog.addMessage("- To push ROI border inward, hold [Alt] and drag mouse against border from outside"); Dialog.setInsets(0,0,0) Dialog.addMessage("- Use [Ctrl + Shift + E] to clear selection brush modifications without updating") Dialog.setInsets(0,0,0) Dialog.addMessage("- Use [8] to update border ROI with current selection brush modifications") Dialog.setInsets(0,0,0) Dialog.addMessage("- Use [r] if you want to revert to the previous ROI after a selection brush update") Dialog.setInsets(5,0,0) Dialog.addMessage("Adjusting ROI with Conversion to Nodes",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("- Click desired border ROI in ROI manager"); Dialog.setInsets(0,0,0) Dialog.addMessage("- Use [9] to convert ROI to nodes or change node spacing") Dialog.setInsets(0,0,0) Dialog.addMessage("- Optionally click and drag nodes to desired location with Freehand Selection tool [3]") Dialog.setInsets(0,0,0) Dialog.addMessage("- Use [r] if you want to revert to the previous ROI after changing node spacing") Dialog.setInsets(0,0,5) Dialog.addMessage("Change Selection Color",14,"#7f0000"); roi_color = newArray("red", "green", "blue","magenta", "cyan", "yellow", "orange", "black", "white"); Dialog.setInsets(0,0,0) Dialog.addChoice("Color:", roi_color, border_color_choice); Dialog.setInsets(5,0,0) Dialog.addMessage("Macro Options",14,"#7f0000"); Dialog.setInsets(0,0,0) modifydialog = newArray("Re-display keyboard shortcuts","Change selection color","Save modifications and exit macro"); Dialog.addRadioButtonGroup("", modifydialog, 3, 1, "Re-display keyboard shortcuts"); Dialog.setLocation(0, 400); Dialog.show(); border_color_choice = Dialog.getChoice(); exitmodify = Dialog.getRadioButton(); if(exitmodify == "Change selection color"){ call("ij.Prefs.set", "border_color_choice.string", border_color_choice ); run("Colors...", "foreground=white background=black selection="+ border_color_choice); } }while (exitmodify!="Save modifications and exit macro"); //Combine TA (ROI 0) and MA (ROI 1) as CA (ROI 2) showStatus("!Selecting Border ROIs..."); selectImage(origimg); setBatchMode("hide"); roiManager("Deselect"); roiManager("Select", newArray(0,1)); roiManager("XOR"); roiManager("Add"); roiManager("Select", 2); roiManager("Rename", "CtAr"); //Save as ROI set roiManager("Deselect"); roiManager("Save", dir+origname+"_"+"Borders_RoiSet.zip"); showStatus("!Saving Cleared Image..."); //Make cleared image and save selectImage(origimg); setBatchMode("hide"); roiManager("Select", 2); setBackgroundColor(0, 0, 0); run("Clear Outside"); selectImage(origimg); run("Select None"); run("Remove Overlay"); selectImage(origimg); saveAs("Tiff", dir+origname+".tif"); //Delete temp roi folder and all contents if (File.exists(dirtemp)) { dirtemplist = getFileList(dirtemp); for (i=0; i0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i= 0){ //Get current ROI name currentname = Roi.getName(); checktype = Roi.getType; //NON-COMPOSITE WORKFLOW - ROITOUCHUP AND PORE MODIFIER if(checktype != "composite"){ dirpathtemp= call("ij.Prefs.get", "temproi.dir", "nodir"); temproi = dirpathtemp+currentname+".roi"; roiManager("Save", temproi); //Update selected ROI roiManager("Update"); //Reselect current ROI roiManager("Select",currentroi); //End for non-composite ROI } //COMPOSITE WORKFLOW - ROITOUCHUP ONLY //Add the updated ROI to the end - restore selection restores deleted versions if(checktype == "composite" && hotkeys_check == "ROITouchup"){ //Add revised ROI to end roiManager("add"); //Rename to original ROI name roiend = roiManager("count") - 1; roiManager("select",roiend); roiManager("rename",currentname); //Save the original ROI from its original index roiManager("select", currentroi); dirpathtemp= call("ij.Prefs.get", "temproi.dir", "nodir"); temproi = dirpathtemp+currentname+".roi"; roiManager("Save", temproi); //Delete the original ROI roiManager("select", currentroi); roiManager("delete"); //Resort in alphabetical order and select by name roiManager("sort"); //Select reverted ROI by name var currentindex; for (i=0; i= 0){ //Get current ROI name currentname = Roi.getName(); checktype = Roi.getType; //NON-COMPOSITE WORKFLOW - ROITOUCHUP AND PORE MODIFIER if(checktype != "composite"){ dirpathtemp= call("ij.Prefs.get", "temproi.dir", "nodir"); temproi = dirpathtemp+currentname+".roi"; roiManager("Save", temproi); //Update selected ROI roiManager("Update"); //Reselect current ROI roiManager("Select",currentroi); //End for non-composite ROI } //COMPOSITE WORKFLOW - ROITOUCHUP ONLY //Add the updated ROI to the end - restore selection restores deleted versions if(checktype == "composite" && hotkeys_check == "ROITouchup"){ //Add revised ROI to end roiManager("add"); //Rename to original ROI name roiend = roiManager("count") - 1; roiManager("select",roiend); roiManager("rename",currentname); //Save the original ROI from its original index roiManager("select", currentroi); dirpathtemp= call("ij.Prefs.get", "temproi.dir", "nodir"); temproi = dirpathtemp+currentname+".roi"; roiManager("Save", temproi); //Delete the original ROI roiManager("select", currentroi); roiManager("delete"); //Resort in alphabetical order and select by name roiManager("sort"); //Select reverted ROI by name var currentindex; for (i=0; i= 0){ //Get name of selected ROI currentname = Roi.getName(); //Check whether saved file exists before running macro dirpathtemp= call("ij.Prefs.get", "temproi.dir", "nodir"); temproi = dirpathtemp+currentname+".roi"; if (File.exists(temproi)) { //Delete current ROI roiManager("Select",currentroi); roiManager("Delete"); //Remove overlay from image run("Select None"); run("Remove Overlay"); //Get directory for saved ROI and open roiManager("Open", temproi); //Sort ROIs in alphabetical order roiManager("sort"); //Remove visible selections roiManager("Deselect"); roiManager("show none"); //Select reverted ROI var currentindex; for (i=0; i= 0){ //Get name of selected ROI currentname = Roi.getName(); //Check whether saved file exists before running macro dirpathtemp= call("ij.Prefs.get", "temproi.dir", "nodir"); temproi = dirpathtemp+currentname+".roi"; if (File.exists(temproi)) { //Delete current ROI roiManager("Select",currentroi); roiManager("Delete"); //Remove overlay from image run("Select None"); run("Remove Overlay"); //Get directory for saved ROI and open roiManager("Open", temproi); //Sort ROIs in alphabetical order roiManager("sort"); //Remove visible selections roiManager("Deselect"); roiManager("show none"); //Select reverted ROI var currentindex; for (i=0; i= 0){ //Retreive current node size start_node_size = call("ij.Prefs.get", "nodesize.number", ""); if(start_node_size == "") {start_node_size = 1;} //Popup and ask for node size, using previous selection Dialog.createNonBlocking("Set Node Spacing"); Dialog.addNumber("Node Spacing:",start_node_size); Dialog.addToSameRow(); Dialog.addMessage("pixels"); Dialog.show(); new_node_size = Dialog.getNumber(); //Save as new global node size call("ij.Prefs.set", "nodesize.number", new_node_size); //Show waiting status for node conversion showStatus("!Converting ROI to Nodes..."); //Get current ROI name currentname = Roi.getName(); //Save current ROI in Temp ROI subfolder dirpathtemp= call("ij.Prefs.get", "temproi.dir", "nodir"); temproi = dirpathtemp+currentname+".roi"; roiManager("Save", temproi); //Check whether selected ROI is polygonal (able to convert to nodes) //If it is not polygonal, re-extract the ROI from its binarized mask roiManager("Select", currentroi); currenttype = Roi.getType(); if(currenttype != "polygon"){ //Save the other (non-selected) ROI in Temp ROI subfolder var alternateindex; for (i=0; i=0){ roiManager("Deselect"); roiManager("Delete"); } //Remove image overlay selectImage(blank); run("Select None"); run("Remove Overlay"); //Threshold image selectImage(blank); run("8-bit"); setAutoThreshold("Otsu"); setThreshold(1, 255); setOption("BlackBackground", true); run("Convert to Mask"); //Clear results run("Clear Results"); //Analyze particles selectImage(blank); run("Analyze Particles...", "size=1-Infinity display exclude clear add"); //Get maximum value of filled ROI, in case of absolute white artifacts roiManager("Measure"); selectWindow("Results"); setLocation(screenWidth, screenHeight); area=Table.getColumn("Area"); var bigrow = ""; run("Clear Results"); roiManager("Measure"); area=Table.getColumn("Area"); ranks = Array.rankPositions(area); Array.reverse(ranks); bigrow=ranks[0]; run("Clear Results"); //Delete all ROIs except this row do{ for (i=roiManager("count")-1; i>=0; i--) { roiManager("Select",i); if(i != bigrow){roiManager("delete")}; }; }while(roiManager("count") > 1); //Close blank image selectImage(blank); close(); //Re-select the ROI roiManager("Select",0); //Rename the ROI as currentname roiManager("rename", currentname); //Run the node interpolation roiManager("Select",0); run("Interpolate", "interval=" + new_node_size); run("Fit Spline"); roiManager("Update"); //Reopen the alternate node roiManager("open", alternateroi); //Sort ROIs in alphabetical order roiManager("sort"); //Select node-modified ROI var currentroi; for (i=0; i0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i 1.00)") Dialog.setInsets(0,20,0) Dialog.addMessage("Reduce Image Noise",12,"#7f0000"); Dialog.setInsets(0,25,0) Dialog.addCheckbox("Highpass Filter (Default)",false); Dialog.setInsets(0,25,0) Dialog.addCheckbox("Subtract Background",false); Dialog.addToSameRow(); Dialog.addNumber("", 50.0, 2, 1, "px") Dialog.setInsets(0,40,0) background_label = newArray("Rolling Ball","Sliding Parabaloid"); Dialog.addRadioButtonGroup("", background_label, 2, 1, "Rolling Ball"); Dialog.setInsets(0,25,0) Dialog.addCheckbox("Gaussian Blur",false); Dialog.addToSameRow(); Dialog.addNumber("", 2.00, 2, 1, "px") Dialog.setInsets(0,20,0) Dialog.addMessage("Save Image Channels",12,"#7f0000"); channel_label = newArray("Combined (RGB)","Red","Blue","Green"); channel_label_defaults = newArray(true,false,false,false); Dialog.setInsets(0,25,0) Dialog.addCheckboxGroup(4,1, channel_label, channel_label_defaults) Dialog.show(); image_choice = Dialog.getRadioButton(); bc_choice = Dialog.getCheckbox(); contrast_choice = Dialog.getCheckbox();; equalize_choice = Dialog.getRadioButton();; contrast_sat = Dialog.getNumber(); elc_choice = Dialog.getCheckbox();;; elc_blocksize = Dialog.getNumber();; elc_slope = Dialog.getNumber();;; highpass_choice = Dialog.getCheckbox();;;; background_choice = Dialog.getCheckbox();;;;; rolling_ball = Dialog.getNumber();;;; background_type = Dialog.getRadioButton();;; gaussian_choice = Dialog.getCheckbox();;;;;; gaussian_level = Dialog.getNumber();;;;; combined_channel_choice = Dialog.getCheckbox();;;;;;; red_channel_choice = Dialog.getCheckbox();;;;;;;; blue_channel_choice = Dialog.getCheckbox();;;;;;;;; green_channel_choice = Dialog.getCheckbox();;;;;;;;;; //Identify user-selected functions modlist = newArray(bc_choice, contrast_choice, elc_choice, highpass_choice, background_choice, gaussian_choice); functionlist = newArray("Auto Brightness-Contrast","Enhance Global Contrast","Enhance Local Contrast","Highpass Filter","Subtract Background", "Gaussian Blur"); for (i=modlist.length-1; i>=0; i--) { if (modlist[i] == 0){ functionlist = Array.deleteIndex(functionlist, i); } } //If user selected more than one function, ask them to reorder if (functionlist.length > 1){ functionmult = "Y"; //Get array of numbers based on number of functions numbers = newArray(functionlist.length); for(i = 0; i < functionlist.length; i++){ append_i = "" + i + 1 + ""; numbers[i] = append_i; } var functionrepeat = "N"; var duplicatewarningtop = ""; var duplicatewarningbottom = ""; do{ //Dialog for ordering image preprocessing tasks Dialog.createNonBlocking("Set Task Order"); if (duplicatewarningtop == "Duplicates Detected"){ Dialog.setInsets(0,20,0) Dialog.addMessage(duplicatewarningtop,12,"#7f0000"); Dialog.setInsets(0,20,0) Dialog.addMessage(duplicatewarningbottom,12,"#7f0000"); } for(i = 0; i < functionlist.length; i++){ functionchoice = functionlist[i]; Dialog.setInsets(0,0,0); Dialog.addChoice(numbers[i], functionlist, functionchoice); } Dialog.show(); functionorder = newArray(functionlist.length); for(i = 0; i < functionlist.length; i++){ functionorder[i] = Dialog.getChoice(); } //Check for user duplicates functionsort = Array.copy(functionorder); Array.sort(functionsort); for(i = 0; i < functionsort.length-1; i++){ if (functionsort[i] == functionsort[i+1]){ functionrepeat = "Y"; duplicatewarningtop = "Duplicates Detected"; duplicatewarningbottom = "Please Reorder Tasks"; break; }else{var functionrepeat = "N"; var duplicatewarningtop = ""; var duplicatewarningbottom = "";} } }while(functionrepeat == "Y"); //End ordering for more than one function } //Option for single function selection if (functionlist.length == 1){ functionorder = functionlist[0]; functionmult = "N";} //Option for no processing functions if (functionlist.length == 0){ functionorder = "None"; functionmult = "N";} //Single Image---------------------------------------------------------- if(image_choice == "Single Image"){ //Prompt user to open the cleaned cross-sectional image file origpath = File.openDialog("Load the Cleared Brightfield Cross-Section"); //Prompt user to select output directory dir=getDirectory("Select Output Location"); //Open the image open(origpath); origimg=getTitle(); base=File.nameWithoutExtension; //Trim base base = replace(base,"_ClipTrabeculae",""); base = replace(base,"_Cleared",""); base = replace(base,"_Touchup",""); base = replace(base,"_Temp",""); base = replace(base,"_Preprocessed_Red",""); base = replace(base,"_Preprocessed_Blue",""); base = replace(base,"_Preprocessed_Green",""); base = replace(base,"_Preprocessed_RGB",""); base = replace(base,"_Preprocessed",""); origname = base+"_Preprocessed"; //Remove scale run("Set Scale...", "distance=0 known=0 pixel=1 unit=pixel"); //Preprocess subfunction Preprocess_function(functionmult, functionorder, origimg, origname, contrast_sat, equalize_choice, elc_blocksize, elc_slope, rolling_ball, background_type, gaussian_level, combined_channel_choice, red_channel_choice, blue_channel_choice, green_channel_choice, dir); //Delete temp ROIs TtArROI = dir+origname+"_"+"TtAr.roi"; if (File.exists(TtArROI)) {File.delete(TtArROI);} EsArROI = dir+origname+"_"+"EsAr.roi"; if (File.exists(EsArROI)) {File.delete(EsArROI);} //Clear log print("\\Clear"); //Hide Log selectWindow("Log"); setLocation(screenWidth, screenHeight); } //Batch Image---------------------------------------------------------- if(image_choice == "Batch Process Folder"){ dir_in = getDirectory("Select Input Folder"); list= getFileList(dir_in); Array.sort(list); //Delete all files that are not tif, bmp, png, or jpg from file list for (i=list.length-1; i>=0; i--) { if (!endsWith(list[i], "tif") && !endsWith(list[i], "bmp") && !endsWith(list[i], "jpg") && !endsWith(list[i], "png")){ list = Array.deleteIndex(list, i); } } //If no suitable image files were detected, display warning and terminate macro if (list.length == 0){ exit("Macro Terminated: Folder must contain images of file type .tif, .bmp, .png, or .jpg"); } //Prompt user to select output directory dir_top = getDirectory("Select Output Location"); //Get list length var listcount = lengthOf(list); //BEGIN IMAGE LOOP for (i=0; i0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i=0){ roiManager("Deselect"); roiManager("Delete"); } //Clear Results run("Clear Results"); //Ensure background set to black and foreground set to white run("Colors...", "foreground=white background=black"); //Clear any current selections on origimg selectImage(origimg); run("Select None"); run("Remove Overlay"); //**CROSS-SECTIONAL GEOMETRY** showStatus("!Extracting Borders..."); //Set measurements to area run("Set Measurements...", "area redirect=None decimal=3"); //Threshold Total Area (TA) selectImage(origimg); run("Duplicate...", " "); cortex=getTitle(); selectImage(cortex); run("8-bit"); setAutoThreshold("Otsu dark"); setThreshold(1, 255); setOption("BlackBackground", true); run("Convert to Mask"); run("Clear Results"); selectImage(cortex); run("Analyze Particles...", "size=1-Infinity display exclude clear add"); //Get maximum value of TA, in case of absolute white artifacts roiManager("Measure"); selectWindow("Results"); setLocation(screenWidth, screenHeight); area=Table.getColumn("Area"); Array.getStatistics(area, min, max, mean, std); TA=(max); var Trow = ""; run("Clear Results"); roiManager("Measure"); area=Table.getColumn("Area"); ranks = Array.rankPositions(area); Array.reverse(ranks); Trow=ranks[0]; //Save TA roi roiManager("Select",Trow); roiManager("Save", dir+origname+"_"+"TtAr.roi"); roiManager("deselect"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Clear results run("Clear Results"); //Isolate Marrow Area (MA) //Invert cortex by thresholding selectImage(cortex); run("Select None"); run("Remove Overlay"); selectImage(cortex); run("Invert"); run("Analyze Particles...", "size=1-Infinity display exclude clear add"); //Get maximum value of MA, in case of absolute white artifacts roiManager("Measure"); area=Table.getColumn("Area"); Array.getStatistics(area, min, max, mean, std); MA=(max); var Mrow = ""; run("Clear Results"); roiManager("Measure"); area=Table.getColumn("Area"); ranks = Array.rankPositions(area); Array.reverse(ranks); Mrow=ranks[0]; //Save MA roi roiManager("Select",Mrow); roiManager("Save", dir+origname+"_"+"EsAr.roi"); roiManager("deselect"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Clear results run("Clear Results"); selectImage(cortex); close(); //Reopen TA and MA roiManager("Open", dir+origname+"_"+"TtAr.roi"); roiManager("Open", dir+origname+"_"+"EsAr.roi"); //Combine TA (ROI 0) and MA (ROI 1) as CA (ROI 2) roiManager("Select", newArray(0,1)); roiManager("XOR"); roiManager("Add"); roiManager("Select", 0); roiManager("Rename", "TtAr"); roiManager("Select", 1); roiManager("Rename", "EsAr"); roiManager("Select", 2); roiManager("Rename", "CtAr"); //Save as ROI set roiManager("Deselect"); roiManager("Save", dir+origname+"_"+"Borders_RoiSet.zip"); //Remove TA and MA so that only CA remains roiManager("Select", newArray(0,1)); roiManager("Delete"); //Start Log print("**Pore Extractor 2D Image Preprocessing Log for " + base + "**" ); //Hide Log selectWindow("Log"); setLocation(screenWidth, screenHeight); //No image processing - set logstart number if (functionmult == "N"){if (functionorder == "None"){logstart = 0;}} //Image processing functions - SINGLE if (functionmult == "N"){ if (functionorder != "None"){ functionchoice = functionorder; logstart = 1; if(functionchoice == "Auto Brightness-Contrast"){AutoBC();} if(functionchoice == "Enhance Global Contrast"){EnhanceGlobalContrast();} if(functionchoice == "Enhance Local Contrast"){EnhanceLocalContrast();} if(functionchoice == "Highpass Filter"){Highpass();} if(functionchoice == "Subtract Background"){BackgroundSubtraction();} if(functionchoice == "Gaussian Blur"){GaussianBlur();} } } //Image processing functions - MULTIPLE if (functionmult == "Y"){ logstart = 0; //Begin loop through function order for(i = 0; i < functionorder.length; i++){ logstart = logstart + 1; functionchoice = functionorder[i]; if(functionchoice == "Auto Brightness-Contrast"){AutoBC();} if(functionchoice == "Enhance Global Contrast"){EnhanceGlobalContrast();} if(functionchoice == "Enhance Local Contrast"){EnhanceLocalContrast();} if(functionchoice == "Highpass Filter"){Highpass();} if(functionchoice == "Subtract Background"){BackgroundSubtraction();} if(functionchoice == "Gaussian Blur"){GaussianBlur();} //End multi-function forloop } //End multi-function } //Auto Brightness-Contrast Subfunction --------------------------------------------------- function AutoBC(){ showStatus("!Auto Brightness-Contrast..."); print(""); print(logstart + ". Auto Brightness-Contrast"); selectImage(origimg); roiManager("Select", 0); //Run Auto Threshold macro from http://imagej.1557.x6.nabble.com/Auto-Brightness-Contrast-and-setMinAndMax-td4968628.html nBins = 256; AUTO_THRESHOLD = 5000; getRawStatistics(pixcount); limit = pixcount/10; threshold = pixcount/AUTO_THRESHOLD; getHistogram(values, histA, nBins); i = -1; found = false; do { counts = histA[++i]; if (counts > limit) counts = 0; found = counts > threshold; } while ((!found) && (i < histA.length-1)) hmin = values[i]; i = histA.length; do { counts = histA[--i]; if (counts > limit) counts = 0; found = counts > threshold; } while ((!found) && (i > 0)) hmax = values[i]; setMinAndMax(hmin, hmax); run("Apply LUT"); selectImage(origimg); roiManager("Select", 0); setBackgroundColor(0, 0, 0); run("Clear Outside"); run("Select None"); run("Remove Overlay"); print(" Minimum Pixel Brightness = " + hmin); print(" Maximum Pixel Brightness = " + hmax); // End Auto Brightness-Contrast Subfunction --------------------------------------------------- } function EnhanceGlobalContrast(){ // Enhance Global Contrast Subfunction --------------------------------------------------- showStatus("!Enhancing Global Contrast..."); if(equalize_choice == "Normalize"){ print(""); print(logstart + ". Enhance Global Contrast"); print(" Normalize Histogram"); print(" Saturated Pixels = " + contrast_sat + "%"); selectImage(origimg); roiManager("Select", 0); run("Enhance Contrast...", "saturated=" + contrast_sat); selectImage(origimg); roiManager("Select", 0); setBackgroundColor(0, 0, 0); run("Clear Outside"); run("Select None"); run("Remove Overlay"); //End normalize } if(equalize_choice == "Equalize"){ print(""); print(logstart + ". Enhance Global Contrast"); print(" Equalize Histogram"); print(" Saturated Pixels = " + contrast_sat + "%"); selectImage(origimg); roiManager("Select", 0); run("Enhance Contrast...", "saturated=" + contrast_sat + " equalize"); selectImage(origimg); roiManager("Select", 0); setBackgroundColor(0, 0, 0); run("Clear Outside"); run("Select None"); run("Remove Overlay"); //End equalize } // End Enhance Global Contrast Subfunction --------------------------------------------------- } // Enhance Local Contrast Subfunction --------------------------------------------------- function EnhanceLocalContrast(){ showStatus("!Enhancing Local Contrast..."); print(""); print(logstart + ". Enhance Local Contrast"); print(" Blocksize = " + elc_blocksize + " pixels"); print(" Maximum Slope = " + elc_slope); selectImage(origimg); roiManager("Select", 0); run("Enhance Local Contrast (CLAHE)", "blocksize=" + elc_blocksize + " histogram=256 maximum=" + elc_slope + " mask=*None*"); selectImage(origimg); roiManager("Select", 0); setBackgroundColor(0, 0, 0); run("Clear Outside"); run("Select None"); run("Remove Overlay"); // End Enhance Local Contrast Subfunction --------------------------------------------------- } // Highpass Filter Subfunction --------------------------------------------------- function Highpass(){ showStatus("!High Pass Filter..."); print(""); print(logstart + ". High Pass Filter"); print(" Large Filter = 40 pixels"); print(" Small Filter = 3 pixels"); print(" Autoscale and Saturate"); selectImage(origimg); roiManager("Select", 0); run("Bandpass Filter...", "filter_large=40 filter_small=3 suppress=None tolerance=5 autoscale saturate"); selectImage(origimg); roiManager("Select", 0); setBackgroundColor(0, 0, 0); run("Clear Outside"); run("Select None"); run("Remove Overlay"); // End Highpass Filter Subfunction --------------------------------------------------- } // Background Subtraction Subfunction --------------------------------------------------- function BackgroundSubtraction(){ //Subtract background showStatus("!Subtracting Background..."); if(background_type == "Rolling Ball"){ print(""); print(logstart + ". Subtract Background"); print(" Rolling Ball Radius = " + rolling_ball + " pixels"); selectImage(origimg); roiManager("Select", 0); run("Subtract Background...", "rolling=" + rolling_ball + " light"); selectImage(origimg); roiManager("Select", 0); setBackgroundColor(0, 0, 0); run("Clear Outside"); run("Select None"); run("Remove Overlay"); //End rolling ball subfunction } if(background_type == "Sliding Parabaloid"){ print(""); print(logstart + ". Subtract Background"); print(" Sliding Parabaloid = " + rolling_ball + " pixels"); selectImage(origimg); roiManager("Select", 0); run("Subtract Background...", "rolling=" + rolling_ball + " light sliding"); selectImage(origimg); roiManager("Select", 0); setBackgroundColor(0, 0, 0); run("Clear Outside"); run("Select None"); run("Remove Overlay"); //End sliding parbaloid } // End Background Subtraction Subfunction --------------------------------------------------- } // Gaussian Blur Subfunction --------------------------------------------------- function GaussianBlur(){ showStatus("!Gaussian Blur..."); print(""); print(logstart + ". Gaussian Blur"); print(" Sigma Radius = " + gaussian_level + " pixels"); selectImage(origimg); roiManager("Select", 0); run("Gaussian Blur...", "sigma=" + gaussian_level); selectImage(origimg); roiManager("Select", 0); setBackgroundColor(0, 0, 0); run("Clear Outside"); run("Select None"); run("Remove Overlay"); // End Gaussian Blur Subfunction --------------------------------------------------- } //Export --------------------------------------------------------------------------- showStatus("!Saving Images..."); exportnum = logstart + 1; print(""); print(exportnum + ". Exported Images"); //Export options for combined channels if(combined_channel_choice == 0 && red_channel_choice == 0 && blue_channel_choice == 0 && green_channel_choice == 0){ selectImage(origimg); run("Select None"); run("Remove Overlay"); saveAs("TIFF", dir+origname+"_RGB.tif"); origimg=getTitle(); print(" RGB Channel"); } if(combined_channel_choice == 1){ selectImage(origimg); run("Select None"); run("Remove Overlay"); saveAs("TIFF", dir+origname+"_RGB.tif"); origimg=getTitle(); print(" RGB Channel"); } //Split channels and export selected ---------------------------------------------------------------------------------------------------------------------------------------------- if(red_channel_choice == 1 || blue_channel_choice == 1 || green_channel_choice == 1){ //Split channels selectImage(origimg); run("Select None"); run("Remove Overlay"); selectImage(origimg); run("Split Channels"); //Modify image names origimg_red = origimg + " (red)"; origimg_blue = origimg + " (blue)"; origimg_green = origimg + " (green)"; if(red_channel_choice == 1){ selectImage(origimg_red); saveAs("TIFF", dir+origname+"_Red.tif"); print(" Red Channel"); } if(blue_channel_choice == 1){ selectImage(origimg_blue); saveAs("TIFF", dir+origname+"_Blue.tif"); print(" Blue Channel"); } if(green_channel_choice == 1){ selectImage(origimg_green); saveAs("TIFF", dir+origname+"_Green.tif"); print(" Green Channel"); } //Close bracket for split channels } //Save log selectWindow("Log"); saveAs("Text", dir+origname+"_"+"Log.txt"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Clear results run("Clear Results"); //Close existing images and windows while (nImages>0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i=0){ roiManager("Deselect"); roiManager("Delete"); } //Clear results run("Clear Results"); //Isolate Marrow Area (MA) //Invert cortex by thresholding selectImage(cortex); run("Select None"); run("Remove Overlay"); selectImage(cortex); run("Invert"); run("Analyze Particles...", "size=1-Infinity display exclude clear add"); //Get maximum value of MA, in case of absolute white artifacts roiManager("Measure"); area=Table.getColumn("Area"); Array.getStatistics(area, min, max, mean, std); MA=(max); var Mrow = ""; run("Clear Results"); roiManager("Measure"); area=Table.getColumn("Area"); ranks = Array.rankPositions(area); Array.reverse(ranks); Mrow=ranks[0]; //Save MA roi roiManager("Select",Mrow); roiManager("Save", dir+origname+"_"+"EsAr.roi"); roiManager("deselect"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Clear results run("Clear Results"); selectImage(cortex); close(); //Reopen TA and MA roiManager("Open", dir+origname+"_"+"TtAr.roi"); roiManager("Open", dir+origname+"_"+"EsAr.roi"); //Combine TA (ROI 0) and MA (ROI 1) as CA (ROI 2) roiManager("Select", newArray(0,1)); roiManager("XOR"); roiManager("Add"); roiManager("Select", 0); roiManager("Rename", "TtAr"); roiManager("Select", 1); roiManager("Rename", "EsAr"); roiManager("Select", 2); roiManager("Rename", "CtAr"); //Save as ROI set roiManager("Deselect"); roiManager("Save", dir+origname+"_"+"Borders_RoiSet.zip"); //Remove TA and MA so that only CA remains roiManager("Select", newArray(0,1)); roiManager("Delete"); //Delete temp ROIs TtArROI = dir+origname+"_"+"TtAr.roi"; if (File.exists(TtArROI)) {File.delete(TtArROI);} EsArROI = dir+origname+"_"+"EsAr.roi"; if (File.exists(EsArROI)) {File.delete(EsArROI);} //Clear log print("\\Clear"); //Start Log print("**Pore Extractor 2D Thresholding Log for " + base + "**" ); print(""); print("Image Scale: " + scale + " pixels / mm"); selectWindow("Log"); setLocation(screenWidth, screenHeight); var threshchoice = ""; var phanchoice = ""; //***THRESHOLDING*** Dialog.createNonBlocking("Thresholding Method"); Dialog.setInsets(5,0,0) Dialog.addMessage("Thresholding Method",14,"#7f0000"); Dialog.setInsets(5,0,0) Dialog.addMessage("\"Try All\" Cycles Through All Options",12,"#7f0000"); Dialog.setInsets(0,0,0) thresh = newArray("Try All","Manual Pore Lumens (White)", "Manual Pore Borders (Black)", "Manual Pore Lumens + Borders", "Auto Local Phansalkar"); Dialog.addRadioButtonGroup("", thresh, 5, 1, "Try All"); Dialog.setInsets(0,13,0) phan = 15; Dialog.addNumber("Radius:", phan); Dialog.addToSameRow(); Dialog.addMessage("pixels"); Dialog.show(); //Get user choices for setup threshchoice = Dialog.getRadioButton(); phanchoice = Dialog.getNumber(); //Generate variables for images to be filled var whiteaccept = ""; var blackaccept = ""; var white_filled = ""; var black_filled = ""; var combined = ""; var phanimg = ""; var whiteautocombined = ""; var blackautocombined = ""; var autocombined = ""; //***MANUAL GLOBAL THRESHOLDING*** - Lumens if (threshchoice=="Manual Pore Lumens (White)" || threshchoice=="Manual Pore Lumens + Borders" || threshchoice=="Try All"){ showStatus("!Manual Pore Lumen Thresholding..."); //Duplicate image to extract white pore contents selectImage(origimg); run("Select None"); run("Remove Overlay"); selectImage(origimg); run("Duplicate...", "title = Manual_Pore_Lumens_(White)_Temp"); white=getTitle(); //Hide orig image selectImage(origimg); setBatchMode("hide"); //Convert to 8-bit selectImage(white); if (bitDepth!=8) {run("8-bit");} //Turn on CA roi selectImage(white); setBatchMode("show"); roiManager("Select", 0); //Run Auto Otsu threshold of white pore contents setAutoThreshold("Otsu dark"); call("ij.plugin.frame.ThresholdAdjuster.setMode", "Red"); call("ij.plugin.frame.ThresholdAdjuster.setMethod","Otsu") run("Threshold..."); //Center Threshold below dialog selectWindow("Threshold"); setLocation((screenWidth/2), (screenHeight/2)); //Set tool to zoom setTool("zoom"); //User-adjusted white pore contents thresholding Dialog.createNonBlocking("Pore Lumen Extraction ----------->"); Dialog.setInsets(5,0,0) Dialog.addMessage("Adjust Threshold TOP slider RIGHT") Dialog.setInsets(5,0,0) Dialog.addMessage("Zoom in (right-click) or out (left-click) to examine fit") Dialog.setInsets(5,0,0) Dialog.addMessage("White pore lumens should be red with minimal external noise") Dialog.setInsets(0,0,0) acceptthresh = newArray("Accept Pore Lumen Threshold","Do Not Use Pore Lumen Threshold"); Dialog.addRadioButtonGroup("", acceptthresh, 2, 1, "Accept Pore Lumen Threshold"); Dialog_x = screenWidth/2; Dialog_y = screenHeight/2 - 250; Dialog.setLocation(Dialog_x, Dialog_y); Dialog.show(); whiteaccept = Dialog.getRadioButton(); //Exit macro if lumens only selected and user chooses to not progress if (threshchoice=="Manual Pore Lumens (White)" && whiteaccept == "Do Not Use Pore Lumen Threshold"){ //Close all windows and images while (nImages>0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i=0){ roiManager("Deselect"); roiManager("Delete"); } //Tile images run("Tile"); setTool("zoom"); //Display Dialog Dialog.createNonBlocking("Morphological Modification"); Dialog.setInsets(0,0,0) Dialog.addMessage("Instructions",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("Check one or more options to preview"); Dialog.setInsets(0,0,0) Dialog.addMessage("You can return to this screen to select additional options"); Dialog.setInsets(0,0,0) Dialog.addMessage("Preview Options",14,"#7f0000"); morphaction = newArray("Revert to Original Threshold Image","Preview Selected Workflow Option(s)", "Preview Current Pore ROIs"); Dialog.setInsets(0,0,0) Dialog.addRadioButtonGroup("", morphaction, 3, 1, "Preview Current Pore ROIs"); Dialog.setInsets(0,0,0) Dialog.addNumber("Min. Pore Size", poresizechoice); Dialog.addToSameRow(); Dialog.addMessage(fromCharCode(956) + "m" + fromCharCode(0xb2)); Dialog.setInsets(0,0,0) Dialog.addNumber("Min. Pore Circularity", porecircchoice); Dialog.setInsets(0,0,0) Dialog.addMessage("Workflow Options",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addCheckbox("Despeckle", despecklechoice); Dialog.setInsets(0,0,0) Dialog.addToSameRow(); Dialog.addNumber("Number of Cycles", despecklecyclechoice); Dialog.setInsets(0,0,0) Dialog.addCheckbox("Remove Bright Outliers", outlierchoice); Dialog.addToSameRow(); Dialog.setInsets(0,0,0) Dialog.addNumber("Radius", outliersizechoice); Dialog.addToSameRow(); Dialog.addMessage("pixels"); Dialog.setInsets(0,0,0) Dialog.addCheckbox("Close and Fill Pores", closechoice); Dialog.addToSameRow(); Dialog.setInsets(0,0,0) Dialog.addNumber("Dilate - Erode", closecyclechoice); Dialog.addToSameRow(); Dialog.addMessage("pixels"); Dialog.setInsets(0,0,0) Dialog.addCheckbox("Smooth Pore Borders", openchoice); Dialog.addToSameRow(); Dialog.setInsets(0,0,0) Dialog.addNumber("Erode - Dilate", opencyclechoice); Dialog.addToSameRow(); Dialog.addMessage("pixels"); Dialog.show(); previewchoice = Dialog.getRadioButton(); poresizechoice= Dialog.getNumber(); porecircchoice= Dialog.getNumber();; despecklechoice = Dialog.getCheckbox(); despecklecyclechoice= Dialog.getNumber();;; outlierchoice = Dialog.getCheckbox();; outliersizechoice = Dialog.getNumber();;;; closechoice = Dialog.getCheckbox();;; closecyclechoice = Dialog.getNumber();;;;; openchoice = Dialog.getCheckbox();;;; opencyclechoice = Dialog.getNumber();;;;;; //Radio button choice: Preview Current Pore ROIs if (previewchoice=="Preview Current Pore ROIs"){ //Run superimpose function on unmodified current pores do { Dialog.createNonBlocking("Pore Filter Settings"); Dialog.setInsets(0,0,5) Dialog.addMessage("Pore Filter Settings",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addNumber("Min. Pore Size (" + fromCharCode(956) + "m)" + fromCharCode(0xb2), poresizechoice); //Dialog.addToSameRow(); //Dialog.addMessage(fromCharCode(956) + "m" + fromCharCode(0xb2)); Dialog.setInsets(0,0,0) Dialog.addNumber("Min. Pore Circularity", porecircchoice); roi_color = newArray("red", "green", "blue","magenta", "cyan", "yellow", "orange", "black", "white"); Dialog.setInsets(0,0,5) Dialog.addMessage("Change Pore Color",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addChoice("Color:", roi_color, roi_color_choice); Dialog.setInsets(0,75,0) Dialog.addRadioButtonGroup("", newArray("Outline","Filled"), 1, 2, roi_type_choice); Dialog.show(); poresizechoice= Dialog.getNumber(); porecircchoice= Dialog.getNumber();; roi_color_choice = Dialog.getChoice(); roi_type_choice = Dialog.getRadioButton(); //Update global ROI color call("ij.Prefs.set", "roi_color_choice.string", roi_color_choice); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Clear overlays selectImage(currentpores); setBatchMode("show"); run("Select None"); run("Remove Overlay"); selectImage(origimg); setBatchMode("show"); run("Select None"); run("Remove Overlay"); //Set scale selectImage(currentpores); run("Set Scale...", "distance=scale_um known=1 pixel=1 unit=um global"); //Analyze particles according to user values selectImage(currentpores); run("Analyze Particles...", "size=poresizechoice-Infinity circularity=porecircchoice-1.00 display exclude clear add"); //Toggle outline color if (roi_type_choice == "Outline"){ roiManager("Deselect"); RoiManager.setPosition(0); roiManager("Set Color", roi_color_choice); roiManager("Set Line Width", 0); } //Toggle fill color if (roi_type_choice == "Filled"){ roiManager("Deselect"); RoiManager.setPosition(0); roiManager("Set Fill Color", roi_color_choice); } //Superimpose these ROIs on original cleaned image roiManager("Deselect"); selectImage(currentpores); run("Scale to Fit"); selectImage(currentpores); roiManager("Show All without labels"); run("Original Scale"); selectImage(origimg); run("Scale to Fit"); selectImage(origimg); roiManager("Show All without labels"); run("Original Scale"); run("Tile"); setTool("zoom"); // Ask if user wants to change ROIs Dialog.createNonBlocking("Confirm Pore Selection"); Dialog.setInsets(0,0,0) Dialog.addRadioButtonGroup("Left-click to zoom in.\nRight-click to zoom out.\n \nAre you satisfied with final pore ROI selections?", newArray("Yes, Save ROIs and Exit Macro","No, Adjust Pore Filter or Color","No, Adjust Morphometry"), 3, 1, "No, Adjust Pore Filter or Color"); Dialog.show(); pore_repeat = Dialog.getRadioButton(); }while (pore_repeat == "No, Adjust Pore Filter or Color"); //Once pore_repeat is to exit or adjust morphometry, proceed if (pore_repeat == "Yes, Save ROIs and Exit Macro"){ G_repeat="Exit"; } if (pore_repeat == "No, Adjust Morphometry"){ G_repeat="Run"; } //End preview current pore ROIs } //Radio button choice: Preview Selected Workflow Option(s) if (previewchoice=="Preview Selected Workflow Option(s)"){ //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Clear overlays selectImage(currentpores); run("Select None"); run("Remove Overlay"); //Duplicate current pores for preview selectImage(currentpores); run("Duplicate...", "title=Preview_Pores"); previewpores=getTitle(); //Hide current pores selectImage(currentpores); setBatchMode("hide"); //Select preview pores selectImage(previewpores); setBatchMode("show"); //Despeckle option if (despecklechoice==true){ selectImage(previewpores); for(i=1; i<=despecklecyclechoice; i++){run("Despeckle");} } //Remove bright outliers option if (outlierchoice==true){ selectImage(previewpores); run("Remove Outliers...", "radius=outliersizechoice threshold=50 which=Bright"); } //Morphological closing choice if (closechoice==true){ selectImage(previewpores); run("EDM Binary Operations", "iterations=" + closecyclechoice + " operation=dilate"); run("Close-"); run("Fill Holes"); run("EDM Binary Operations", "iterations=" + closecyclechoice + " operation=erode"); } //Morphological opening choice if (openchoice==true){ selectImage(previewpores); run("EDM Binary Operations", "iterations=" + opencyclechoice + " operation=erode"); run("EDM Binary Operations", "iterations=" + opencyclechoice + " operation=dilate"); } //Run the ROI superimpose on the preview pores do { Dialog.createNonBlocking("Pore Filter Settings"); Dialog.setInsets(0,0,0) Dialog.addMessage("Display Options",14,"#7f0000"); Dialog.setInsets(0,0,5) Dialog.addRadioButtonGroup("", newArray("Display Pores on Image","Skip Pore Display"), 2, 1, "Display Pores on Image"); Dialog.setInsets(0,0,5) Dialog.addMessage("Pore Filter Settings",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addNumber("Min. Pore Size (" + fromCharCode(956) + "m)" + fromCharCode(0xb2), poresizechoice); //Dialog.addToSameRow(); //Dialog.addMessage(fromCharCode(956) + "m" + fromCharCode(0xb2)); Dialog.setInsets(0,0,0) Dialog.addNumber("Min. Pore Circularity", porecircchoice); roi_color = newArray("red", "green", "blue","magenta", "cyan", "yellow", "orange", "black", "white"); Dialog.setInsets(0,0,5) Dialog.addMessage("Change Pore Color",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addChoice("Color:", roi_color, roi_color_choice); Dialog.setInsets(0,75,0) Dialog.addRadioButtonGroup("", newArray("Outline","Filled"), 1, 2, roi_type_choice); Dialog.show(); poredisplaychoice = Dialog.getRadioButton(); poresizechoice= Dialog.getNumber(); porecircchoice= Dialog.getNumber();; roi_color_choice = Dialog.getChoice(); roi_type_choice = Dialog.getRadioButton();; //Update global ROI color call("ij.Prefs.set", "roi_color_choice.string", roi_color_choice); //Option to not display pores //Loop to display pores if chosen if (poredisplaychoice == "Display Pores on Image"){ //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Clear overlays selectImage(previewpores); run("Select None"); run("Remove Overlay"); selectImage(origimg); setBatchMode("show"); run("Select None"); run("Remove Overlay"); //Set scale selectImage(previewpores); run("Set Scale...", "distance=scale_um known=1 pixel=1 unit=um global"); //Analyze particles according to user values selectImage(previewpores); run("Analyze Particles...", "size=poresizechoice-Infinity circularity=porecircchoice-1.00 display exclude clear add"); //Toggle outline color if (roi_type_choice == "Outline"){ roiManager("Deselect"); RoiManager.setPosition(0); roiManager("Set Color", roi_color_choice); roiManager("Set Line Width", 0); } //Toggle fill color if (roi_type_choice == "Filled"){ roiManager("Deselect"); RoiManager.setPosition(0); roiManager("Set Fill Color", roi_color_choice); } //Superimpose these ROIs on original cleaned image roiManager("Deselect"); selectImage(previewpores); run("Scale to Fit"); selectImage(previewpores); roiManager("Show All without labels"); run("Original Scale"); selectImage(origimg); run("Scale to Fit"); selectImage(origimg); roiManager("Show All without labels"); run("Original Scale"); run("Tile"); setTool("zoom"); // Ask if user wants to change ROIs Dialog.createNonBlocking("Confirm Pore Selection"); Dialog.setInsets(0,0,0) Dialog.addRadioButtonGroup("Left-click to zoom in.\nRight-click to zoom out.\n \nAre you satisfied with final pore ROI selections?", newArray("Yes, Save ROIs and Exit Macro","No, Adjust Pore Filter or Color","No, Adjust Morphometry"), 3, 1, "No, Adjust Pore Filter or Color"); Dialog.show(); pore_repeat = Dialog.getRadioButton(); //End if statement for pore display } if (poredisplaychoice == "Skip Pore Display"){ pore_repeat = "No, Adjust Morphometry"; } //End do-while loop } while (pore_repeat == "No, Adjust Pore Filter or Color"); //If user wants to exit, close currentpores and replace it with previewpores if (pore_repeat=="Yes, Save ROIs and Exit Macro"){ selectImage(currentpores); close(); selectImage(previewpores); rename("Current_Pore_Modification"); currentpores = getTitle(); //Print changes from this workflow to log if (despecklechoice==true){print(" Despeckle: " + despecklecyclechoice + " cycle(s)");} if (outlierchoice==true){print(" Remove Bright Outliers: " + outliersizechoice + " pixel radius");} if (closechoice==true){print(" Close and Fill Pores: " + closecyclechoice + " Dilate - Erode pixel(s)");} if (openchoice==true){print(" Smooth Pore Borders: " + opencyclechoice + " Erode - Dilate pixel(s)");} G_repeat="Exit";} //If user wants to modify morphometry, ask if they want to save the change or discard it if (pore_repeat=="No, Adjust Morphometry"){ Dialog.createNonBlocking("Accept Morphological Change(s)?"); Dialog.setInsets(0,0,0) Dialog.addRadioButtonGroup("Accept Morphological Change(s)?", newArray("Accept Change","Discard Change"), 2, 1, "Accept Change"); Dialog.show(); morphchoice = Dialog.getRadioButton(); //If user accepts change, close currentpores and replace it with previewpores if (morphchoice=="Accept Change"){ selectImage(currentpores); close(); selectImage(previewpores); rename("Current_Pore_Modification"); currentpores = getTitle(); //Print changes from this workflow to log if (despecklechoice==true){print(" Despeckle: " + despecklecyclechoice + " cycle(s)");} if (outlierchoice==true){print(" Remove Bright Outliers: " + outliersizechoice + " pixel radius");} if (closechoice==true){print(" Close and Fill Pores: " + closecyclechoice + " Dilate - Erode pixel(s)");} if (openchoice==true){print(" Smooth Pore Borders: " + opencyclechoice + " Erode - Dilate pixel(s)");} } //If user discards change, close previewpores if (morphchoice=="Discard Change"){ selectImage(previewpores); close(); //Show current pores selectImage(currentpores); setBatchMode("show"); } G_repeat="Run"; //End No, Adjust Morphometry Loop } //End Preview Selected Workflow Option(s) } //Radio button choice: Revert to Original Threshold Image if (previewchoice=="Revert to Original Threshold Image"){ //Close current image and re-duplicate from original thresholded selectImage(currentpores); close(); selectImage(origpores); run("Duplicate...", "title=Current_Pore_Modification"); currentpores=getTitle(); //Restart log print("\\Clear"); print("**Pore Extractor 2D Morphological Modification Log for " + base + "**" ); print(""); print("Image Scale: " + scale + " pixels / mm"); print(""); print("**Morphological Modifications**"); //Trigger morphometry loop restart G_repeat="Run"; } //End of do-while loop for morphometric adjustment }while (G_repeat=="Run"); //Print Analyze Particle Parameters print(""); print("**Analyze Particle Parameters**"); print(" Minimum Pore Size: " + poresizechoice + " mm" + fromCharCode(0xb2)); print(" Minimum Pore Circularity: " + porecircchoice); //Save ROI set with user color choice roiManager("Deselect"); RoiManager.setPosition(0); roiManager("Set Color", roi_color_choice); roiManager("Set Line Width", 0); roiManager("Save", dir+origname+"_Pore_RoiSet.zip"); //Save copy of log selectWindow("Log"); saveAs("Text", dir+origname+"_Morphological_Log.txt"); //Close all windows and images while (nImages>0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i" +"Warning: " +"Loaded Image was Not RGB!

" +"Load an RGB Image to split color channels

" +" Or choose \"Load Image to Display\"
and load a single color channel image"); loadexit = "Rerun"; } else{ //Save to IJ Preferences base=File.nameWithoutExtension; //Trim base base = replace(base,"_ClipTrabeculae",""); base = replace(base,"_Cleared",""); base = replace(base,"_Touchup",""); base = replace(base,"_Temp",""); base = replace(base,"_Preprocessed_Red",""); base = replace(base,"_Preprocessed_Blue",""); base = replace(base,"_Preprocessed_Green",""); base = replace(base,"_Preprocessed_RGB",""); base = replace(base,"_Preprocessed",""); origname = base+"_PoreModifier"; call("ij.Prefs.set", "orig.string", origname); //Load dialog for split color channels //Clear overlays selectImage(origimg); run("Select None"); run("Remove Overlay"); //Remove scale run("Set Scale...", "distance=0 known=0 pixel=1 unit=pixel"); loadexit = "Exit"; } //End of Choose Color Channel } } while (loadexit=="Rerun"); //Get original colors from global preferences roi_color_choice=call("ij.Prefs.get", "roi_color_choice.string", ""); if(roi_color_choice == "") {roi_color_choice = "cyan";} roi_color_save=call("ij.Prefs.get", "roi_color_save.string", ""); if(roi_color_save == "") {roi_color_save = "magenta";} roi_color_select=call("ij.Prefs.get", "roi_color_select.string", ""); if(roi_color_select == "") {roi_color_select = "green";} //Change selection color to global preference run("Colors...", "foreground=white background=black selection="+ roi_color_select); if(loadchoice == "Load Existing ROI Set"){ //Prompt user to load ROI set to modify roipath = File.openDialog("Open the Pore ROI Set To Modify"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Set ROI options roiManager("Associate", "true"); roiManager("Centered", "false"); roiManager("UseNames", "false"); //Open ROI Path roiManager("open", roipath); //Check whether Previous labels already exist nR = roiManager("Count"); roiIdxorig = newArray(nR); p=0; for (i=0; i0) { prevIdx = Array.trim(roiIdxorig,p); clippedIdx = Array.trim(roiIdx,k); } //Make array of new ROIs if (k==0) { prevIdx = Array.trim(roiIdxorig,p); } //Change original and previous ROIs to user choice for (i = 0; i=0){ roiManager("Deselect"); roiManager("Delete"); } //Set ROI options roiManager("Associate", "true"); roiManager("Centered", "false"); roiManager("UseNames", "false"); run("ROI Manager..."); //roiManager("Show All without labels"); } //End if dialog for pre-existing ROIs //Prompt user to select output directory dir=getDirectory("Select Output Location"); //Save to IJ Preferences call("ij.Prefs.set", "appendroi.dir", dir); //Delete any old copies of temp ROI directory in this folder dirtemp=dir+"/Temp ROIs/"; if (File.exists(dirtemp)) { dirtemplist = getFileList(dirtemp); for (i=0; i0) { prevIdx = Array.trim(roiIdxorig,p); clippedIdx = Array.trim(roiIdx,k); } //Make array of new ROIs if (k==0) { prevIdx = Array.trim(roiIdxorig,p); } //Change original and previous ROIs to user choice for (i = 0; i0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i= 0){setTool("brush");} } macro "Selection Brush Tool [n6]" { //Check whether ROIs are selected currentroi = roiManager("index"); //If no ROIs are selected, switch to freehand selection tool if (currentroi < 0){setTool("freehand");} //If ROI is selected, switch to selection brush if (currentroi >= 0){setTool("brush");} } macro "ROI Labels On [f1]" { if (roiManager("count")==0) showStatus("ROI Manager is empty"); else (roiManager("Show All with labels")); } macro "ROI Labels Off [f2]" { if (roiManager("count")==0) showStatus("ROI Manager is empty"); else (roiManager("Show All without labels")); } macro "ROIs Off [f3]" { if (roiManager("count")==0) showStatus("ROI Manager is empty"); else (roiManager("Show None")); } macro "Reset Wand Tolerance [f4]" { setTool("wand"); run("Wand Tool...", "tolerance=0"); } macro "Split ROI [f5]" { //Check hotkeys hotkeys_check = call("ij.Prefs.get", "hotkeys.string", ""); if(hotkeys_check == "PoreModifier"){ //Check whether ROIs are selected currentroi = roiManager("index"); //If no ROIs are selected, display warning if (currentroi < 0){showStatus("Select an ROI to split");} //If ROI is selected, run macro if (currentroi >= 0){ //Save the current last ROI index roicount = roiManager("count") - 1; //Get current ROI name currentname = Roi.getName(); //Save current ROI in Temp ROI subfolder dirpathtemp= call("ij.Prefs.get", "temproi.dir", "nodir"); temproi = dirpathtemp+currentname+".roi"; roiManager("Save", temproi); //Split the ROI roiManager("Update"); roiManager("Split"); //Delete original ROI roiManager("Select", currentroi); roiManager("Delete"); run("Select None"); //Get the list of new pores created by the split roiend = roiManager("count"); var roinames = newArray; for (i = roicount; i 1){ //Create IJ prefs for each child ROI to revert to the parent ROI for (i = 0; i 1){ for (j = 1; j= 0){ //Save the current last ROI index roicount = roiManager("count") - 1; //Get current ROI name currentname = Roi.getName(); //Save current ROI in Temp ROI subfolder dirpathtemp= call("ij.Prefs.get", "temproi.dir", "nodir"); temproi = dirpathtemp+currentname+".roi"; roiManager("Save", temproi); //Set binary options to black background roiManager("Update"); setOption("BlackBackground", true); run("Create Mask"); setBatchMode("hide"); selectWindow("Mask"); run("Fill Holes"); selectWindow("Mask"); run("Analyze Particles...", "exclude include add"); selectWindow("Mask"); close(); setBatchMode("show"); //Delete original ROI roiManager("Select", currentroi); roiManager("Delete"); run("Select None"); //Get the list of new pores created by the fill roiend = roiManager("count"); //roidiff = Math.abs(roiend)-Math.abs(roicount); var roinames = newArray; for (i = roicount; i 1){ //Create IJ prefs for each child ROI to revert to the parent ROI for (i = 0; i 1){ for (j = 1; j 1){ //Create IJ prefs for each child ROI to revert to the parent ROI for (i = 0; i 1){ for (j = 1; j= 0){ //Get name of selected ROI currentname = Roi.getName(); //Call IJ pref linking selected child to corresponding parent and (if fill/split) other child ROIs childname = currentname + ".name"; parentname = call("ij.Prefs.get", childname, ""); //Split off the first string in parentname as the parent(s) ROI names to revert parentchildsplit = split(parentname,"child"); parentsplit = parentchildsplit[0]; parentsplitarray = split(parentsplit," "); //Get directory for saved ROI parent(s) dirpathtemp= call("ij.Prefs.get", "temproi.dir", "nodir"); //Check whether parent(s) exist in the Temp ROI folder var parentexists = "N"; for (i = 0; i < parentsplitarray.length; i++) { parentroi = dirpathtemp+parentsplitarray[i]+".roi"; if (File.exists(parentroi)){parentexists = "Y";} } //Run macro if at least one parent roi was saved if (parentexists == "Y"){ //Delete current child ROI roiManager("Select",currentroi); roiManager("Delete"); //Remove overlay from image run("Select None"); run("Remove Overlay"); //DELETE OTHER CHILDREN //Check whether any children exist if(parentchildsplit.length > 1){ childsplit = parentchildsplit[1]; childsplitarray = split(childsplit," "); //Loop through each child for (i = 0; i < childsplitarray.length; i++) { deletename = childsplitarray[i]; //Check the index for the name extracted from the array var deleteindex; for (j=0; j0) { prevIdx = Array.trim(roiIdxorig,p); clippedIdx = Array.trim(roiIdx,k); } //Make array of new ROIs if (k==0) { prevIdx = Array.trim(roiIdxorig,p); } //Change original and previous ROIs to user choice for (i = 0; i=0; j--) { if (!matches(list1[j], ".*PoreModifier_Pore_RoiSet.*")){ list1 = Array.deleteIndex(list1, j); } } //If there are no existing ModifiedRoiSet files, start with 001 if (lengthOf(list1) == 0){ roiappend = 1; roiappendpaste = leftPad(roiappend,3); } //If there are existing ModifiedRoiSet files, find the highest value if (lengthOf(list1) > 0){ Array.sort(list1); roilast = list1[lengthOf(list1)-1]; splitend = split(roilast, "_"); roiend = splitend[lengthOf(splitend)-1]; splitfile = split(roiend, "."); roinum = splitfile[0]; roinumint = parseInt(roinum); roiappend = roinumint+1; roiappendpaste = leftPad(roiappend,3); } //Save the roi set with the new append number roiManager("Deselect"); roiManager("Save", dir+origname+"_Pore_RoiSet_"+roiappendpaste+".zip"); //Re-display ROIs roiManager("Show All with labels") } //************************************************************************************** // Pore Analyzer Function // Copyright (C) 2022 Mary E. Cole / Pore Extractor 2D // First Release Date: July 2022 //************************************************************************************** function Analyze() { requires("1.53g"); setBatchMode(true); setBatchMode("hide"); //Welcome dialog Dialog.createNonBlocking("Welcome to Pore Analyzer!"); Dialog.setInsets(0,0,0) Dialog.addMessage("Prepare to Load:",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("1) Cleared brightfield cross-section as exported by:"); Dialog.setInsets(0,0,0) Dialog.addMessage(" " + fromCharCode(2022) + " Wand ROI Selection") Dialog.setInsets(0,0,0) Dialog.addMessage(" " + fromCharCode(2022) + " ROI Touchup") Dialog.setInsets(0,0,0) Dialog.addMessage(" " + fromCharCode(2022) + " Image Pre-Processing") Dialog.setInsets(5,0,0) Dialog.addMessage("2) Finalized Pore ROI Set as exported by Pore Modifier"); Dialog.setInsets(5,0,0) Dialog.addMessage("Warning:",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("Any current images or windows will be closed"); Dialog.setInsets(5,0,0) Dialog.addMessage("Click OK to begin!",14,"#306754"); Dialog.show(); //Close existing images and windows while (nImages>0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i Update --> Manage Update Sites --> BoneJ --> Close --> Apply Changes"); Dialog.addMessage("Then restart ImageJ and retry"); Dialog.show(); exit("BoneJ Installation Required"); } //Prompt user to open the cleaned cross-sectional image file origpath = File.openDialog("Load the Cleared Brightfield Cross-Section"); //Prompt user to load ROI set to modify roipath = File.openDialog("Open Final Pore ROI Set"); //Prompt user to select output directory dir=getDirectory("Select Output Location"); //Set scale scale = call("ij.Prefs.get", "scale.number", ""); if(scale == "") {scale = 0;} Dialog.createNonBlocking("Set Image Scale"); Dialog.addNumber("Pixel Size:",scale); Dialog.addToSameRow(); Dialog.addMessage("pixels / mm"); Dialog.show(); scale = Dialog.getNumber(); call("ij.Prefs.set", "scale.number", scale); scale_um = scale/1000; //Open the cleaned cross-sectional image open(origpath); origimg=getTitle(); base=File.nameWithoutExtension; //Trim base base = replace(base,"_ClipTrabeculae",""); base = replace(base,"_Cleared",""); base = replace(base,"_Touchup",""); base = replace(base,"_Temp",""); base = replace(base,"_Preprocessed_Red",""); base = replace(base,"_Preprocessed_Blue",""); base = replace(base,"_Preprocessed_Green",""); base = replace(base,"_Preprocessed_RGB",""); base = replace(base,"_Preprocessed",""); origname = base+"_PoreAnalyzer"; //Remove any overlays selectImage(origimg); run("Select None"); run("Remove Overlay"); //Reset colors to normal and add selection color choice border_color_choice=call("ij.Prefs.get", "border_color_choice.string", ""); if(border_color_choice == "") {border_color_choice = "cyan";} run("Colors...", "foreground=white background=black selection="+ border_color_choice); //Set scale for cross-sectional measurements according to user input run("Set Scale...", "distance=scale known=1 pixel=1 unit=mm global"); //***CROSS-SECTIONAL BOUNDING*********************************************************************************************************** //Modified from Pore Extractor Function //Will save over TA, MA, and CA images and ROIs if they exist in the output folder in case the user has modified the original image showStatus("!Cross-Sectional Geometry..."); //Create folder subdivision for summary statistics output sumdir=dir+"/Summary Statistics/"; File.makeDirectory(sumdir); //Create folder subdivision for cross-sectional output geomdir=dir+"/Cross-Sectional Geometry/"; File.makeDirectory(geomdir); //Set measurements to area run("Set Measurements...", "area redirect=None decimal=3"); //Threshold Total Area (TA) selectImage(origimg); run("Duplicate...", " "); cortex=getTitle(); selectImage(cortex); run("8-bit"); setAutoThreshold("Otsu dark"); setThreshold(1, 255); setOption("BlackBackground", true); run("Convert to Mask"); run("Clear Results"); selectImage(cortex); run("Analyze Particles...", "size=1-Infinity display exclude clear add"); //Get maximum value of TA, in case of absolute white artifacts roiManager("Measure"); selectWindow("Results"); setLocation(screenWidth, screenHeight); area=Table.getColumn("Area"); Array.getStatistics(area, min, max, mean, std); TA=(max); var Trow = ""; run("Clear Results"); roiManager("Measure"); area=Table.getColumn("Area"); ranks = Array.rankPositions(area); Array.reverse(ranks); Trow=ranks[0]; //Save TA roi roiManager("Select",Trow); roiManager("Save", geomdir+origname+"_"+"TtAr.roi"); roiManager("deselect"); //Make blank image of TA and save in output directory selectImage(origimg); run("Duplicate...", " "); total=getTitle(); //Clear total slice selectImage(total); run("Select All"); setBackgroundColor(0, 0, 0); run("Clear", "slice"); //Select and flatten TA image selectImage(total); roiManager("Select", Trow); run("Fill", "slice"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } selectImage(total); run("Select None"); run("Remove Overlay"); selectImage(cortex); run("Select None"); run("Remove Overlay"); //Threshold and save image selectImage(total); run("8-bit"); setAutoThreshold("Otsu"); setThreshold(1, 255); setOption("BlackBackground", true); run("Convert to Mask"); saveAs("Tiff", geomdir+origname+"_"+"TtAr.tif"); total=getTitle(); //Clear results run("Clear Results"); //Isolate Marrow Area (MA) //Invert cortex by thresholding selectImage(cortex); run("8-bit"); setAutoThreshold("Otsu"); //run("Threshold..."); setThreshold(1, 255); setOption("BlackBackground", true); run("Convert to Mask"); run("Invert"); run("Analyze Particles...", "size=1-Infinity display exclude clear add"); //Get maximum value of MA, in case of absolute white artifacts roiManager("Measure"); area=Table.getColumn("Area"); Array.getStatistics(area, min, max, mean, std); MA=(max); var Mrow = ""; run("Clear Results"); roiManager("Measure"); area=Table.getColumn("Area"); ranks = Array.rankPositions(area); Array.reverse(ranks); Mrow=ranks[0]; //Save MA roi roiManager("Select",Mrow); roiManager("Save", geomdir+origname+"_"+"EsAr.roi"); roiManager("deselect"); //Clear outside marrow selectImage(cortex); roiManager("Select", Mrow); run("Fill", "slice"); run("Clear Outside"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } selectImage(cortex); run("Select None"); run("Remove Overlay"); //Clear results run("Clear Results"); //Threshold and save image selectImage(cortex); run("8-bit"); setAutoThreshold("Otsu"); setThreshold(1, 255); setOption("BlackBackground", true); run("Convert to Mask"); selectImage(cortex); saveAs("Tiff",geomdir+origname+"_"+"EsAr.tif"); marrow=getTitle(); //Combine MA and TA to get CA image selectImage(marrow); imageCalculator("Subtract create",total,marrow); cortical=getTitle(); run("8-bit"); setAutoThreshold("Otsu"); setThreshold(1, 255); setOption("BlackBackground", true); run("Convert to Mask"); saveAs("Tiff", geomdir+origname+"_"+"CtAr.tif"); cortical=getTitle(); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Clear results run("Clear Results"); //Reopen TA and MA roiManager("Open", geomdir+origname+"_"+"TtAr.roi"); roiManager("Open", geomdir+origname+"_"+"EsAr.roi"); //Combine TA (ROI 0) and MA (ROI 1) as CA (ROI 2) roiManager("Select", newArray(0,1)); roiManager("XOR"); roiManager("Add"); roiManager("Select", 2); roiManager("Save", geomdir+origname+"_"+"CtAr.roi"); //BONEJ ANALYSIS ON CA //Clear results run("Clear Results"); //Set scale for cross-sectional measurements according to user input run("Set Scale...", "distance=scale known=1 pixel=1 unit=mm global"); //Run BoneJ Slice Geometry selectImage(cortical); run("Select None"); run("Remove Overlay"); selectImage(cortical); run("Slice Geometry", "bone=unknown bone_min=1 bone_max=255 slope=0.0000 y_intercept=0"); //Get row numbers of columns selectWindow("Results"); text = getInfo(); lines = split(text, "\n"); columns = split(lines[0], "\t"); if (columns[0]==" ") columns[0]= "Number"; //Pull variables from BoneJ results table Imin= getResult(columns[13],0); Imax = getResult(columns[14],0); Zpol = getResult(columns[18],0); //**CROSS-SECTIONAL MORPHOMETRY*********************************************************************************************************** //Make table for exporting cross-sectional geometry rca = "Relative Cortical Area"; Table.create(rca); print("[" + rca + "]","\\Headings:Image\tScale (pixels/mm)\tTotal Area (mm^2)\tEndosteal Area (mm^2)\tCortical Area (mm^2)\t% Cortical Area\t% Bone Area Total Porosity Corrected\t% Bone Area Cortical Porosity Corrected\t% Bone Area Trabecularized Porosity Corrected\t% Endosteal Area\tParabolic Index (Y)\tImin (mm^4)\tImax (mm^4)\tZpol (mm^3)\t"); selectWindow("Relative Cortical Area"); setLocation(screenWidth, screenHeight); //Set scale for cross-sectional measurements according to user input run("Set Scale...", "distance=scale known=1 pixel=1 unit=mm global"); //Set measurements to area only run("Set Measurements...", "area redirect=None decimal=3"); run("Clear Results"); //Measure TA, MA, and CA roiManager("Select", 0); roiManager("Measure"); TA=getResult("Area"); roiManager("Select", 1); roiManager("Measure"); MA=getResult("Area"); roiManager("Select", 2); roiManager("Measure"); CA=getResult("Area"); //Compute RCA and PI RCA=(CA/TA)*100; PerMA = (MA/TA)*100; Para=(CA*MA)/(TA*TA); //Clear results run("Clear Results"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //**Regional Subdivision**************************************************************************************************************** showStatus("!Regional Analysis..."); //User chooses rib or long bone Dialog.createNonBlocking("Cross-Section Type Selection"); Dialog.setInsets(5, 35, 0) Dialog.addMessage("Select Cross-Section Type:", 14,"#7f0000"); Dialog.addRadioButtonGroup("", newArray("Rib","Long Bone"), 2, 1, "Rib"); Dialog.setInsets(0, 50, 0) Dialog.addMessage("Draw Quadrants Using:"); Dialog.setInsets(0, 50, 0) Dialog.addRadioButtonGroup("", newArray("Section Alignment With Image Borders","Section Major Axis"), 2, 1, "Section Alignment With Image Borders"); Dialog.show(); bonetype = Dialog.getRadioButton(); tilt = Dialog.getRadioButton();; //Regional subdivision for long bones**************************************************************************************************** if (bonetype == "Long Bone"){ //Set scale for cross-sectional geometry according to user input in mm run("Set Scale...", "distance=scale known=1 pixel=1 unit=mm global"); //Run BoneJ Slice Geometry selectImage(cortical); run("Select None"); run("Remove Overlay"); selectImage(cortical); run("Slice Geometry", "bone=unknown bone_min=1 bone_max=255 slope=0.0000 y_intercept=0"); selectImage(cortical); getPixelSize(unit, pw, ph); //Get row numbers of columns selectWindow("Results"); text = getInfo(); lines = split(text, "\n"); columns = split(lines[0], "\t"); if (columns[0]==" ") columns[0]= "Number"; //Pull variables from BoneJ results table and divide by pixel size to obtain pixel coordinates cX= getResult(columns[5],0)/pw; cY = getResult(columns[6],0)/pw; th = abs(getResult(columns[10],0)); rMin = getResult(columns[11],0)/pw; rMax = getResult(columns[12],0)/pw; thPi = th + PI / 2; //Loop to define tilt value for major axis quadrant subdivision if (tilt == "Section Major Axis"){ //Define major axis - this will be vertical for a long image, and horizontal for a short image Majorx1 = floor(cX - cos(-th) * 2 * rMax); Majory1 = floor(cY + sin(-th) * 2 * rMax); Majorx2 = floor(cX + cos(-th) * 2 * rMax); Majory2 = floor(cY - sin(-th) * 2 * rMax); //Slope of line Major_m = (Majory1 - Majory2)/(Majorx1 - Majorx2); //Because the coordinates are inverted (increase from top to bottom of frame), a negative slope inclines to the right, and a positive slope inclines to the left //Angle of line compared to vertical axis is tan angle = 1/m for a positive slope and -1/m for a negative slope //Since a right incline is negative in slope and resulting angle, but rotation is clockwise, add a negative sign so that line rotates properly //Multiply by 180/PI to convert from radians to degrees Major_angle=-(atan(1/Major_m) * 180/PI); //Define minor axis Minorx1 = floor(cX - cos(thPi) * 2 * rMin); Minory1 = floor(cY - sin(thPi) * 2 * rMin); Minorx2 = floor(cX + cos(thPi) * 2 * rMin); Minory2 = floor(cY + sin(thPi) * 2 * rMin); //Slope of line Minor_m = (Minory1 - Minory2)/(Minorx1 - Minorx2); //Because the coordinates are inverted (increase from top to bottom of frame), a negative slope inclines to the right, and a positive slope inclines to the left //Angle of line compared to vertical axis is tan angle = 1/m for a positive slope and -1/m for a negative slope //Since a right incline is negative in slope and resulting angle, but rotation is clockwise, add a negative sign so that line rotates properly //Multiply by 180/PI to convert from radians to degrees Minor_angle=-(atan(1/Minor_m) * 180/PI); //Detrmine coordinates for quadrant lines //Get width and height of image w = getWidth(); h = getHeight(); //Length of the diagonal via pythagorean theorem d = sqrt((h*h) + (w*w)); //Difference between diagonal and height hdiff=((d-h)/2); //Extend 5000 pixels further beyond diagonal hboost=hdiff+5000; //Clear slice geometry results as all values have been extracted run("Clear Results"); var x1 = ""; var y1 = ""; var x2 = ""; var y2 = ""; var Final_angle = ""; var linestop = "lineredo"; selectImage(cortical); setBatchMode("show"); run("Select None"); run("Remove Overlay"); do { //Erase ROIs //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //For the adjusted centerline, make it the boosted diagonal length so it is sure to go out of frame by 1000 pixels even at the longest (diagonal) rotation selectImage(cortical); makeLine(cX, -hboost, cX, (h+hboost),30); //Rotate line to major axis tilt angle run("Rotate...", " angle=Major_angle"); Roi.setStrokeColor(border_color_choice) roiManager("Add"); roiManager("Show All without labels"); //Ask user to inspect regional subdivision Dialog.createNonBlocking("Confirm Longest Axis"); Dialog.setInsets(5, 0, 5) Dialog.addRadioButtonGroup("Are you satisfied with longest axis?", newArray("Yes, Proceed", "No, Use Minor Axis"), 2, 1, "Yes, Proceed"); Dialog.show(); lineswap= Dialog.getRadioButton(); //If user chooses to proceed with major axis, exit do while loop if (lineswap == "Yes, Proceed"){ Final_angle = Major_angle; linestop = "linestop"; } else{linestop = "lineredo";} //Swap names if chosen by user if (lineswap == "No, Use Minor Axis"){ //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //For the adjusted centerline, make it the boosted diagonal length so it is sure to go out of frame by 1000 pixels even at the longest (diagonal) rotation selectImage(cortical); makeLine(cX, -hboost, cX, (h+hboost),30); //Rotate line to minor axis tilt angle run("Rotate...", " angle=Minor_angle"); Roi.setStrokeColor(border_color_choice) roiManager("Add"); roiManager("Show All without labels"); //Ask user to inspect regional subdivision Dialog.createNonBlocking("Confirm Longest Axis"); Dialog.setInsets(5, 0, 5) Dialog.addRadioButtonGroup("Are you satisfied with longest axis?", newArray("Yes, Proceed", "No, Use Major Axis"), 2, 1, "Yes, Proceed"); Dialog.show(); lineswapredo= Dialog.getRadioButton(); //If user chooses to proceed, exit do while loop //If user chooses to revert to major axis, the loop will repeat if (lineswapredo == "Yes, Proceed"){ Final_angle = Minor_angle; linestop = "linestop"; } else{linestop = "lineredo";} } } while (linestop=="lineredo"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Hide cortical image again selectImage(cortical); setBatchMode("hide"); } else{ Final_angle = 0; run("Clear Results"); }; //Then proceed from final angle - re-run extraction in case no tilt was selected //Detrmine coordinates for quadrant lines //Get width and height of image w = getWidth(); h = getHeight(); //Length of the diagonal via pythagorean theorem d = sqrt((h*h) + (w*w)); //Difference between diagonal and height hdiff=((d-h)/2); //Extend 5000 pixels further beyond diagonal hboost=hdiff+5000; //For the adjusted centerline, make it the boosted diagonal length so it is sure to go out of frame by 1000 pixels even at the longest (diagonal) rotation makeLine(cX, -hboost, cX, (h+hboost)); //Rotate line to tilt angle run("Rotate...", " angle=Final_angle"); //Rotate major axis right 45 degrees to top of octant 2 run("Rotate...", " angle=45"); //Get coordinates for opposing lines 2 and 4 getSelectionCoordinates(x, y); o2x=x[0]; o2y=y[0]; o4x=x[1]; o4y=y[1]; //Rotate major axis right 90 degrees to top of octant 3 run("Rotate...", " angle=90"); //Get coordinates for opposing lines 3 and 7 getSelectionCoordinates(x, y); o3x=x[0]; o3y=y[0]; o1x=x[1]; o1y=y[1]; //Duplicate image for drawing all quadrants on the slice selectImage(cortical); run("Select None"); run("Remove Overlay"); run("Duplicate...", "title=[Drawn]"); drawquad=getImageID(); //Change foreground to black to draw in black run("Colors...", "foreground=black background=black"); run("Line Width...", "line=1"); //Drawn octants on duplicate selectImage(drawquad); run("Select None"); run("Remove Overlay"); makePolygon(o1x,o1y,o2x,o2y,cX,cY); run("Draw"); makePolygon(o3x,o3y,o4x,o4y,cX,cY); run("Draw"); run("Select None"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Add and combine in ROI manager makePolygon(o1x,o1y,o2x,o2y,cX,cY); roiManager("Add"); makePolygon(o3x,o3y,o4x,o4y,cX,cY); roiManager("Add"); roiManager("Select", newArray(0,1)); roiManager("XOR"); roiManager("Add"); roiManager("Delete"); roiManager("Deselect"); roiManager("show all without labels"); run("From ROI Manager"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Change foreground back to white run("Colors...", "foreground=white background=black"); //Create folder subdivision for regional output regiondir=dir+"/Regions/"; File.makeDirectory(regiondir); selectImage(drawquad); saveAs("TIFF", regiondir+origname+"_"+"DrawnQuadrants.tif"); drawquad=getImageID(); selectImage(drawquad); close(); //Close the total area image selectImage(total); close(); //Clear outside Octant 1 on CA selectImage(cortical); run("Select None"); run("Remove Overlay"); selectImage(cortical); run("Duplicate...", "title=[Quad1]"); quad1=getTitle(); selectImage(quad1); makePolygon(o1x,o1y,o2x,o2y,cX,cY); setBackgroundColor(0, 0, 0); run("Clear Outside"); setAutoThreshold("Default dark"); //run("Threshold..."); setThreshold(1,255); setOption("BlackBackground", true); run("Convert to Mask"); //Quadrant 1 Bounding //Counts particle(s), if multiple ROIs combines them in a single ROI and deletes individual ROIs run("Clear Results"); selectImage(quad1); run("Analyze Particles...", "display clear add"); var roicount = ""; roicount=roiManager("Count"); //Combine multiple ROIs for fragmented cortex region if (roicount>1){ roiManager("show all without labels"); roiManager("Combine"); roiManager("Add"); newcount=roiManager("Count")-1; deleteroi=Array.getSequence(newcount); roiManager("Select", deleteroi); roiManager("Delete"); roiManager("Select", 0); } //Or select single ROI for non-fragmented cortex region else {roiManager("Select", 0);} run("Clear Results"); //Temporarily save ROI roiManager("Select", 0); roiManager("Save", regiondir+"TempQuad1.roi"); //Clear results run("Clear Results"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Close quad 1 image selectImage(quad1); close(); //Clear outside Quadrant 2 on CA selectImage(cortical); run("Select None"); run("Remove Overlay"); selectImage(cortical); run("Duplicate...", "title=[Quad2]"); quad2=getTitle(); selectImage(quad2); makePolygon(o2x,o2y,o3x,o3y,cX,cY); setBackgroundColor(0, 0, 0); run("Clear Outside"); setAutoThreshold("Default dark"); //run("Threshold..."); setThreshold(1,255); setOption("BlackBackground", true); run("Convert to Mask"); //Quadrant 2 Bounding //Counts particle(s), if multiple ROIs combines them in a single ROI and deletes individual ROIs run("Clear Results"); selectImage(quad2); run("Analyze Particles...", "display clear add"); var roicount = ""; roicount=roiManager("Count"); //Combine multiple ROIs for fragmented cortex region if (roicount>1){ roiManager("show all without labels"); roiManager("Combine"); roiManager("Add"); newcount=roiManager("Count")-1; deleteroi=Array.getSequence(newcount); roiManager("Select", deleteroi); roiManager("Delete"); roiManager("Select", 0); } //Or select single ROI for non-fragmented cortex region else {roiManager("Select", 0);} run("Clear Results"); //Temporarily save ROI roiManager("Select", 0); roiManager("Save", regiondir+"TempQuad2.roi"); //Clear results run("Clear Results"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Close quad 2 image selectImage(quad2); close(); //Clear outside Quadrant 3 on CA selectImage(cortical); run("Select None"); run("Remove Overlay"); selectImage(cortical); run("Duplicate...", "title=[Quad3]"); quad3=getTitle(); selectImage(quad3); makePolygon(o3x,o3y,o4x,o4y,cX,cY); setBackgroundColor(0, 0, 0); run("Clear Outside"); setAutoThreshold("Default dark"); //run("Threshold..."); setThreshold(1,255); setOption("BlackBackground", true); run("Convert to Mask"); //Quadrant 3 Bounding //Counts particle(s), if multiple ROIs combines them in a single ROI and deletes individual ROIs run("Clear Results"); selectImage(quad3); run("Analyze Particles...", "display clear add"); var roicount = ""; roicount=roiManager("Count"); //Combine multiple ROIs for fragmented cortex region if (roicount>1){ roiManager("show all without labels"); roiManager("Combine"); roiManager("Add"); newcount=roiManager("Count")-1; deleteroi=Array.getSequence(newcount); roiManager("Select", deleteroi); roiManager("Delete"); roiManager("Select", 0); } //Or select single ROI for non-fragmented cortex region else {roiManager("Select", 0);} run("Clear Results"); //Temporarily save ROI roiManager("Select", 0); roiManager("Save", regiondir+"TempQuad3.roi"); //Clear results run("Clear Results"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Close quad 3 image selectImage(quad3); close(); //Clear outside Quadrant 4 on CA selectImage(cortical); run("Select None"); run("Remove Overlay"); selectImage(cortical); run("Duplicate...", "title=[Quad4]"); quad4=getTitle(); selectImage(quad4); makePolygon(o4x,o4y,o1x,o1y,cX,cY); setBackgroundColor(0, 0, 0); run("Clear Outside"); setAutoThreshold("Default dark"); //run("Threshold..."); setThreshold(1,255); setOption("BlackBackground", true); run("Convert to Mask"); //Quadrant 4 Bounding //Counts particle(s), if multiple ROIs combines them in a single ROI and deletes individual ROIs run("Clear Results"); selectImage(quad4); run("Analyze Particles...", "display clear add"); var roicount = ""; roicount=roiManager("Count"); //Combine multiple ROIs for fragmented cortex region if (roicount>1){ roiManager("show all without labels"); roiManager("Combine"); roiManager("Add"); newcount=roiManager("Count")-1; deleteroi=Array.getSequence(newcount); roiManager("Select", deleteroi); roiManager("Delete"); roiManager("Select", 0); } //Or select single ROI for non-fragmented cortex region else {roiManager("Select", 0);} run("Clear Results"); //Temporarily save ROI roiManager("Select", 0); roiManager("Save", regiondir+"TempQuad4.roi"); //Clear results run("Clear Results"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Close quad 4 image selectImage(quad4); close(); //Reopen ROIs selectImage(cortical); run("Select None"); run("Remove Overlay"); setBatchMode("show"); cortical = getTitle(); roiManager("Open", regiondir+"TempQuad1.roi"); roiManager("Open", regiondir+"TempQuad2.roi"); roiManager("Open", regiondir+"TempQuad3.roi"); roiManager("Open", regiondir+"TempQuad4.roi"); //Use labels as names roiManager("UseNames", "true"); roiManager("Select", 0); roiManager("Rename", "Quadrant_1"); roiManager("Set Fill Color", "red"); roiManager("Select", 1); roiManager("Rename", "Quadrant_2"); roiManager("Set Fill Color", "blue"); roiManager("Select", 2); roiManager("Rename", "Quadrant_3"); roiManager("Set Fill Color", "yellow"); roiManager("Select", 3); roiManager("Rename", "Quadrant_4"); roiManager("Set Fill Color", "green"); selectImage(cortical); roiManager("Show None"); roiManager("Show All with labels"); var regionstop = "regionredo"; //Do-while loop for user to rename quadrants do { //Dialog for quadrants set by anatomical orientation (directional display) if (tilt == "Section Alignment With Image Borders"){ orientation=newArray("Anterior", "Posterior", "Medial", "Lateral", "Superior", "Inferior", "Custom"); Dialog.createNonBlocking("Quadrant Naming"); Dialog.addMessage("Choose anatomical orientations as they appear on the image"); Dialog.addMessage(""); Dialog.setInsets(5, 85, 5) Dialog.addChoice("", orientation, "Anterior"); Dialog.setInsets(5, 0, 5) Dialog.addChoice("Orientation:", orientation, "Medial"); Dialog.addToSameRow(); Dialog.setInsets(5, 0, 5) Dialog.addChoice("", orientation,"Lateral"); Dialog.setInsets(5, 85, 5) Dialog.addChoice("", orientation,"Posterior"); Dialog.show(); //Get user choices regionname1 = Dialog.getChoice(); regionname4 = Dialog.getChoice(); regionname2 = Dialog.getChoice(); regionname3 = Dialog.getChoice(); } //Dialog for quadrants set by major axis (no directional display) else{ orientation=newArray("Anterior", "Posterior", "Medial", "Lateral", "Superior", "Inferior", "Custom"); Dialog.createNonBlocking("Quadrant Naming"); Dialog.addMessage("Choose anatomical orientations as they appear on the image"); Dialog.addMessage(""); Dialog.setInsets(5, 0, 5) Dialog.addChoice("Quadrant 1 :", orientation, "Anterior"); Dialog.setInsets(5, 0, 5) Dialog.addChoice("Quadrant 2 :", orientation, "Lateral"); Dialog.setInsets(5, 0, 5) Dialog.addChoice("Quadrant 3 :", orientation,"Posterior"); Dialog.setInsets(5, 0, 5) Dialog.addChoice("Quadrant 4: ", orientation,"Medial"); Dialog.show(); //Get user choices regionname1 = Dialog.getChoice(); regionname2 = Dialog.getChoice(); regionname3 = Dialog.getChoice(); regionname4 = Dialog.getChoice(); } //Loop for custom user-entered names if(regionname1 == "Custom" || regionname2 == "Custom" || regionname3 == "Custom" || regionname4 == "Custom"){ loopcount=0; Dialog.createNonBlocking("Custom Quadrant Name Entry"); Dialog.addMessage("Enter custom names for anatomical regions"); if(regionname1 == "Custom") {Dialog.setInsets(0, 0, 0); Dialog.addString("Quadrant 1 : ", ""); loopcount=loopcount+1;} else {Dialog.setInsets(0, 0, 0); Dialog.addMessage("Quadrant 1 : " + regionname1); } if(regionname2 == "Custom") {Dialog.setInsets(0, 0, 0); Dialog.addString("Quadrant 2 :", ""); loopcount=loopcount+1;} else {Dialog.setInsets(0, 0, 0); Dialog.addMessage("Quadrant 2 : " + regionname2);} if(regionname3 == "Custom") {Dialog.setInsets(0, 0, 0); Dialog.addString("Quadrant 3 :", ""); loopcount=loopcount+1;} else {Dialog.setInsets(0, 0, 0); Dialog.addMessage("Quadrant 3 : " +regionname3);} if(regionname4 == "Custom") {Dialog.setInsets(0, 0, 0); Dialog.addString("Quadrant 4 :", ""); loopcount=loopcount+1;} else {Dialog.setInsets(0, 0, 0); Dialog.addMessage("Quadrant 4 : " +regionname4);} Dialog.show(); //Fill an array with the entered strings loopout = newArray(loopcount); for(i = 0; i=0){ roiManager("Deselect"); roiManager("Delete"); } //Draw the major axis selectImage(cortical); makeLine(Majorx1,Majory1,Majorx2,Majory2,30); Roi.setStrokeColor(border_color_choice) roiManager("Add"); roiManager("Show All without labels"); //Ask user to inspect regional subdivision Dialog.createNonBlocking("Confirm Regional Subdivision"); Dialog.setInsets(5, 0, 5) Dialog.addRadioButtonGroup("Are you satisfied with cutaneous / pleural subdivision?", newArray("Yes, Proceed", "No, Use Minor Axis"), 2, 1, "Yes, Proceed"); Dialog.show(); lineswap= Dialog.getRadioButton(); //If user chooses to proceed with major axis, exit do while loop if (lineswap == "Yes, Proceed"){ x1 = Majorx1; y1= Majory1; x2 = Majorx2; y2 = Majory2; linestop = "linestop"; } else{linestop = "lineredo";} //Swap names if chosen by user if (lineswap == "No, Use Minor Axis"){ //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Draw the minor axis selectImage(cortical); makeLine(Minorx1,Minory1,Minorx2,Minory2,30); Roi.setStrokeColor(border_color_choice) roiManager("Add"); roiManager("Show All without labels"); //Ask user to inspect regional subdivision Dialog.createNonBlocking("Confirm Regional Subdivision"); Dialog.setInsets(5, 0, 5) Dialog.addRadioButtonGroup("Are you satisfied with cutaneous / pleural subdivision?", newArray("Yes, Proceed", "No, Use Major Axis"), 2, 1, "Yes, Proceed"); Dialog.show(); lineswapredo= Dialog.getRadioButton(); //If user chooses to proceed, exit do while loop //If user chooses to revert to major axis, the loop will repeat if (lineswapredo == "Yes, Proceed"){ x1 = Minorx1; y1= Minory1; x2 = Minorx2; y2 = Minory2; linestop = "linestop"; } else{linestop = "lineredo";} } } while (linestop=="lineredo"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Hide cortical image again selectImage(cortical); setBatchMode("hide"); //Duplicate image for drawing the major axis on the slice selectImage(cortical); run("Select None"); run("Remove Overlay"); run("Duplicate...", "title=[Drawn]"); drawhalf=getImageID(); //Change foreground to black run("Colors...", "foreground=black background=black"); run("Line Width...", "line=1"); //Drawn major axis on the duplicate selectImage(drawhalf); run("Select None"); run("Remove Overlay"); makeLine(x1, y1, x2, y2); run("Draw"); run("Add Selection..."); run("Select None"); //Change foreground back to white run("Colors...", "foreground=white background=black"); //Create folder subdivision for regional output regiondir=dir+"/Regions/"; File.makeDirectory(regiondir); selectImage(drawhalf); saveAs("TIFF", regiondir+origname+"_"+"DrawnHalves.tif"); drawhalf=getImageID(); selectImage(drawhalf); close(); //Close the total area image selectImage(total); close(); //Duplicate cortical image selectImage(cortical); run("Duplicate...", "title=[Region1]"); region1=getImageID(); selectImage(cortical); run("Duplicate...", "title=[Region2]"); region2=getImageID(); //Determine how the image is oriented with the long dimension of the rib //Vertically (height > width) or horizontally (width>height) //Note: The axis drawn by the BoneJ macro is incorrect and extends the major axis beyond image bounds //This corrected axis drawing will not match BoneJ macro axis output setBackgroundColor(0, 0, 0); setAutoThreshold("Default dark"); //run("Threshold..."); setThreshold(1,255); setOption("BlackBackground", true); run("Convert to Mask"); if (w>=h){ //Draw the top polygon on the horizontally oriented image //x1,0 is the top left corner //x1,0 is the top right corner selectImage(region1); makePolygon(x1,0,x2,0,x2,y2,x1,y1); run("Clear Outside"); setAutoThreshold("Default dark"); //run("Threshold..."); setThreshold(1,255); setOption("BlackBackground", true); run("Convert to Mask"); //Draw the bottom polygon on the horizontally oriented image //x1,h is the bottom left corner //x2,h is the bottom right corner selectImage(region2); makePolygon(x1,h,x2,h,x2,y2,x1,y1); run("Clear Outside"); setAutoThreshold("Default dark"); //run("Threshold..."); setThreshold(1,255); setOption("BlackBackground", true); run("Convert to Mask"); } else { //Draw the left polygon on the vertically oriented image //0,y1 is upper left corner //0,y2 is lower left corner selectImage(region1); makePolygon(0,y1,0,y2,x2,y2,x1,y1); run("Clear Outside"); setAutoThreshold("Default dark"); //run("Threshold..."); setThreshold(1,255); setOption("BlackBackground", true); run("Convert to Mask"); //Draw the right polygon on the vertically oriented image //w,y1 is upper right corner //w,y2 is lower right corner selectImage(region2); makePolygon(w,y1,w,y2,x2,y2,x1,y1); run("Clear Outside"); setAutoThreshold("Default dark"); //run("Threshold..."); setThreshold(1,255); setOption("BlackBackground", true); run("Convert to Mask"); } //Clear results run("Clear Results"); //Clear ROI finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Region 1 measurement //Analyze particles selectImage(region1); run("Remove Overlay"); run("Select None"); run("Analyze Particles...", "display clear add"); //Combine multiple ROIs for fragmented cortex region var roicount = ""; roicount=roiManager("Count"); if (roicount>1){ roiManager("show all without labels"); roiManager("Combine"); roiManager("Add"); newcount=roiManager("Count")-1; deleteroi=Array.getSequence(newcount); roiManager("Select", deleteroi); roiManager("Delete"); roiManager("Select", 0); } //Or select single ROI for non-fragmented cortex region else {roiManager("Select", 0);} //Temporarily save ROI roiManager("Select", 0); roiManager("Save", regiondir+"TempRegion1.roi"); //Clear results run("Clear Results"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Close region 1 image selectImage(region1); close(); //Region 2 measurement //Analyze particles selectImage(region2); run("Remove Overlay"); run("Select None"); run("Analyze Particles...", "display clear add"); //Combine multiple ROIs for fragmented cortex region var roicount = ""; roicount=roiManager("Count"); if (roicount>1){ roiManager("show all without labels"); roiManager("Combine"); roiManager("Add"); newcount=roiManager("Count")-1; deleteroi=Array.getSequence(newcount); roiManager("Select", deleteroi); roiManager("Delete"); roiManager("Select", 0); } //Or select single ROI for non-fragmented cortex region else {roiManager("Select", 0);} //Temporarily save ROI roiManager("Select", 0); roiManager("Save", regiondir+"TempRegion2.roi"); //Clear results run("Clear Results"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Close region 2 image selectImage(region2); close(); //Reopen ROIs selectImage(cortical); run("Select None"); run("Remove Overlay"); setBatchMode("show"); cortical = getTitle(); roiManager("Open", regiondir+"TempRegion1.roi"); roiManager("Open", regiondir+"TempRegion2.roi"); ////Guess cutaneous vs. pleural orientation //Set measurements to include area and shape descriptors run("Set Measurements...", "area shape redirect=None decimal=3"); //Measure area and shape descriptors for each region roiManager("Deselect"); roiManager("Measure"); var regionCA1 = ""; var regionCA2 = ""; var regionCA1 = getResult("Area",0); Circ1 = getResult("Circ.",0); var regionCA2 = getResult("Area",1); Circ2 = getResult("Circ.",1); //Test whether region 1 or region 2 has higher circularity (likely pleural) var region1guess = ""; var region1alt = ""; var region1fin = ""; var region2guess = ""; var region2alt = ""; var region2fin = ""; if (Circ1>Circ2){ region1guess = "Pleural"; region1alt = "Cutaneous"; region2guess = "Cutaneous"; region2alt = "Pleural"; } else{ region1guess = "Cutaneous"; region1alt = "Pleural"; region2guess = "Pleural"; region2alt = "Cutaneous"; } var regionstop = "regionredo"; do { //Use labels as names roiManager("UseNames", "true"); //Rename ROIs based on guess roiManager("Select", 0); roiManager("Rename", region1guess); roiManager("Set Fill Color", "red"); roiManager("Select", 1); roiManager("Rename", region2guess); roiManager("Set Fill Color", "blue"); selectImage(cortical); roiManager("Show None"); roiManager("Show All with labels"); //Ask user to inspect region names Dialog.createNonBlocking("Confirm Rib Region Naming"); Dialog.setInsets(5, 0, 5) Dialog.addRadioButtonGroup("Are you satisfied with rib region labels?", newArray("Yes, Proceed", "No, Swap Names"), 2, 1, "Yes, Proceed"); Dialog.show(); regionswap= Dialog.getRadioButton(); //If user chooses to proceed with guessed names, exit do while loop if (regionswap == "Yes, Proceed"){ region1fin = region1guess; region2fin = region2guess; regionstop = "regionstop"; } else{regionstop = "regionredo";} //Swap names if chosen by user if (regionswap == "No, Swap Names"){ roiManager("Select", 0); roiManager("Rename", region1alt); roiManager("Select", 1); roiManager("Rename", region2alt); selectImage(cortical); roiManager("Show None"); roiManager("Show All with labels"); //Ask user to inspect region names Dialog.createNonBlocking("Confirm Rib Region Naming"); Dialog.setInsets(5, 0, 5) Dialog.addRadioButtonGroup("Are you satisfied with rib region labels?", newArray("Yes, Proceed", "No, Swap Names"), 2, 1, "Yes, Proceed"); Dialog.show(); regionswapredo= Dialog.getRadioButton(); //If user chooses to proceed, exit do while loop //If user chooses to revert the labels to the original, the loop will repeat if (regionswapredo == "Yes, Proceed"){ region1fin = region1alt; region2fin = region2alt; regionstop = "regionstop";} else{regionstop = "regionredo";} } } while (regionstop=="regionredo"); //Silence batch mode setBatchMode("hide"); //Reset rib ROIs to border color roiManager("Select", 0); roiManager("Set Color", border_color_choice); roiManager("Select", 1); roiManager("Set Color", border_color_choice); selectImage(cortical); roiManager("Show None"); roiManager("Show All with labels"); //Save ROIs to output folders roiManager("Select",0); roiManager("Save", regiondir+origname+"_"+region1fin+".roi"); roiManager("deselect"); roiManager("Select",1); roiManager("Save", regiondir+origname+"_"+region2fin+".roi"); roiManager("deselect"); //Delete temp ROIs File.delete(regiondir+"TempRegion1.roi"); File.delete(regiondir+"TempRegion2.roi"); selectWindow("Log"); run("Close"); //Clear results run("Clear Results"); //Close the cortical area image selectImage(cortical); close(); } //Regional assignment****************************************************************************************** //This section opens each regional ROI and flattens it as a binary image //The mean gray value is measured for each ROI on each region //ROIs fully within a region will be 255, and fully outside a region will be 0 //ROIs on the border between two regions will have the higher mean gray value in the region containing more of their area //ROIs are labeled with their majority regional assignment and colorized in ROI Manager function RegionMeasure(origpath,regionroipath,roipath,regionname) { //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Clear any past results run("Clear Results"); //Open the preprocessed DIC image as a template for the region mask open(origpath); regionmask=getTitle(); //Clear origimg for region mask selectImage(regionmask); run("Select All"); setBackgroundColor(0, 0, 0); run("Clear", "slice"); //Select and flatten region ROI roiManager("open", regionroipath); selectImage(regionmask); roiManager("Select", 0); run("Fill", "slice"); selectImage(regionmask); run("8-bit"); setAutoThreshold("Otsu"); //run("Threshold..."); setThreshold(1, 255); setOption("BlackBackground", true); run("Convert to Mask"); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Open border ROIs roiManager("open", roipath); //Renumber for (i = 0; i 0){ //Make total pore output table tot="Individual Pore Measurements"; Table.create(tot); print("[" + tot + "]","\\Headings:Pore\tRegion\tArea (um^2)\tCentroid X (um)\tCentroid Y (um)\tPerimeter (um)\tEllipse Major Axis (um)\tEllipse Minor Axis (um)\tMajor Axis Angle (degree)\tCircularity\tMax Feret Diameter (um)\tMax Feret X (um)\tMax Feret Y (um)\tFeret Angle (degree)\tMin Feret Diameter (um)\tAspect Ratio\tRoundness\tSolidity\t"); selectWindow("Results"); //Save values to total pore table Group=Table.getColumn("Group"); Area=Table.getColumn("Area"); X=Table.getColumn("X"); Y=Table.getColumn("Y"); Perim=Table.getColumn("Perim."); Major=Table.getColumn("Major"); Minor=Table.getColumn("Minor"); Angle=Table.getColumn("Angle"); Circ=Table.getColumn("Circ."); Feret=Table.getColumn("Feret"); FeretX=Table.getColumn("FeretX"); FeretY=Table.getColumn("FeretY"); FeretAngle=Table.getColumn("FeretAngle"); MinFeret=Table.getColumn("MinFeret"); AR=Table.getColumn("AR"); Round=Table.getColumn("Round"); Solidity=Table.getColumn("Solidity"); //Change group designation poreregion = newArray(roicount); if (bonetype == "Long Bone"){ for (j=0; j 0){ //Calculate means Array.getStatistics(Mean_Area_array, min, max, mean, std); Mean_Area = (mean); Array.getStatistics(Mean_Perim_array, min, max, mean, std); Mean_Perim = (mean); Array.getStatistics(Mean_Major_array, min, max, mean, std); Mean_Major = (mean); Array.getStatistics(Mean_Minor_array, min, max, mean, std); Mean_Minor = (mean); Array.getStatistics(Mean_Angle_array, min, max, mean, std); Mean_Angle = (mean); Array.getStatistics(Mean_Circ_array, min, max, mean, std); Mean_Circ = (mean); Array.getStatistics(Mean_Feret_array, min, max, mean, std); Mean_Feret = (mean); Array.getStatistics(Mean_FeretX_array, min, max, mean, std); Mean_FeretX = (mean); Array.getStatistics(Mean_FeretY_array, min, max, mean, std); Mean_FeretY = (mean); Array.getStatistics(Mean_FeretAngle_array, min, max, mean, std); Mean_FeretAngle = (mean); Array.getStatistics(Mean_MinFeret_array, min, max, mean, std); Mean_MinFeret = (mean); Array.getStatistics(Mean_AR_array, min, max, mean, std); Mean_AR = (mean); Array.getStatistics(Mean_Round_array, min, max, mean, std); Mean_Round = (mean); Array.getStatistics(Mean_Solidity_array, min, max, mean, std); Mean_Solidity = (mean); //Calculations //Sum pore areas Total_Pore_Area = 0; for (k=0; k 0){ //Calculate means Array.getStatistics(Mean_Area_array, min, max, mean, std); Mean_Area = (mean); Array.getStatistics(Mean_Perim_array, min, max, mean, std); Mean_Perim = (mean); Array.getStatistics(Mean_Major_array, min, max, mean, std); Mean_Major = (mean); Array.getStatistics(Mean_Minor_array, min, max, mean, std); Mean_Minor = (mean); Array.getStatistics(Mean_Angle_array, min, max, mean, std); Mean_Angle = (mean); Array.getStatistics(Mean_Circ_array, min, max, mean, std); Mean_Circ = (mean); Array.getStatistics(Mean_Feret_array, min, max, mean, std); Mean_Feret = (mean); Array.getStatistics(Mean_FeretX_array, min, max, mean, std); Mean_FeretX = (mean); Array.getStatistics(Mean_FeretY_array, min, max, mean, std); Mean_FeretY = (mean); Array.getStatistics(Mean_FeretAngle_array, min, max, mean, std); Mean_FeretAngle = (mean); Array.getStatistics(Mean_MinFeret_array, min, max, mean, std); Mean_MinFeret = (mean); Array.getStatistics(Mean_AR_array, min, max, mean, std); Mean_AR = (mean); Array.getStatistics(Mean_Round_array, min, max, mean, std); Mean_Round = (mean); Array.getStatistics(Mean_Solidity_array, min, max, mean, std); Mean_Solidity = (mean); //Calculations //Sum pore areas Total_Pore_Area = 0; for (k=0; k 0){ //Calculate means Array.getStatistics(Mean_Area_array, min, max, mean, std); Mean_Area = (mean); Array.getStatistics(Mean_Perim_array, min, max, mean, std); Mean_Perim = (mean); Array.getStatistics(Mean_Major_array, min, max, mean, std); Mean_Major = (mean); Array.getStatistics(Mean_Minor_array, min, max, mean, std); Mean_Minor = (mean); Array.getStatistics(Mean_Angle_array, min, max, mean, std); Mean_Angle = (mean); Array.getStatistics(Mean_Circ_array, min, max, mean, std); Mean_Circ = (mean); Array.getStatistics(Mean_Feret_array, min, max, mean, std); Mean_Feret = (mean); Array.getStatistics(Mean_FeretX_array, min, max, mean, std); Mean_FeretX = (mean); Array.getStatistics(Mean_FeretY_array, min, max, mean, std); Mean_FeretY = (mean); Array.getStatistics(Mean_FeretAngle_array, min, max, mean, std); Mean_FeretAngle = (mean); Array.getStatistics(Mean_MinFeret_array, min, max, mean, std); Mean_MinFeret = (mean); Array.getStatistics(Mean_AR_array, min, max, mean, std); Mean_AR = (mean); Array.getStatistics(Mean_Round_array, min, max, mean, std); Mean_Round = (mean); Array.getStatistics(Mean_Solidity_array, min, max, mean, std); Mean_Solidity = (mean); //Calculations //Sum pore areas Total_Pore_Area = 0; for (k=0; k 0){ //Calculate means Array.getStatistics(Mean_Area_array, min, max, mean, std); Mean_Area = (mean); Array.getStatistics(Mean_Perim_array, min, max, mean, std); Mean_Perim = (mean); Array.getStatistics(Mean_Major_array, min, max, mean, std); Mean_Major = (mean); Array.getStatistics(Mean_Minor_array, min, max, mean, std); Mean_Minor = (mean); Array.getStatistics(Mean_Angle_array, min, max, mean, std); Mean_Angle = (mean); Array.getStatistics(Mean_Circ_array, min, max, mean, std); Mean_Circ = (mean); Array.getStatistics(Mean_Feret_array, min, max, mean, std); Mean_Feret = (mean); Array.getStatistics(Mean_FeretX_array, min, max, mean, std); Mean_FeretX = (mean); Array.getStatistics(Mean_FeretY_array, min, max, mean, std); Mean_FeretY = (mean); Array.getStatistics(Mean_FeretAngle_array, min, max, mean, std); Mean_FeretAngle = (mean); Array.getStatistics(Mean_MinFeret_array, min, max, mean, std); Mean_MinFeret = (mean); Array.getStatistics(Mean_AR_array, min, max, mean, std); Mean_AR = (mean); Array.getStatistics(Mean_Round_array, min, max, mean, std); Mean_Round = (mean); Array.getStatistics(Mean_Solidity_array, min, max, mean, std); Mean_Solidity = (mean); //Calculations //Sum pore areas Total_Pore_Area = 0; for (k=0; k 0){ //Calculate means Array.getStatistics(Mean_Area_array, min, max, mean, std); Mean_Area = (mean); Array.getStatistics(Mean_Perim_array, min, max, mean, std); Mean_Perim = (mean); Array.getStatistics(Mean_Major_array, min, max, mean, std); Mean_Major = (mean); Array.getStatistics(Mean_Minor_array, min, max, mean, std); Mean_Minor = (mean); Array.getStatistics(Mean_Angle_array, min, max, mean, std); Mean_Angle = (mean); Array.getStatistics(Mean_Circ_array, min, max, mean, std); Mean_Circ = (mean); Array.getStatistics(Mean_Feret_array, min, max, mean, std); Mean_Feret = (mean); Array.getStatistics(Mean_FeretX_array, min, max, mean, std); Mean_FeretX = (mean); Array.getStatistics(Mean_FeretY_array, min, max, mean, std); Mean_FeretY = (mean); Array.getStatistics(Mean_FeretAngle_array, min, max, mean, std); Mean_FeretAngle = (mean); Array.getStatistics(Mean_MinFeret_array, min, max, mean, std); Mean_MinFeret = (mean); Array.getStatistics(Mean_AR_array, min, max, mean, std); Mean_AR = (mean); Array.getStatistics(Mean_Round_array, min, max, mean, std); Mean_Round = (mean); Array.getStatistics(Mean_Solidity_array, min, max, mean, std); Mean_Solidity = (mean); //Calculations //Sum pore areas Total_Pore_Area = 0; for (k=0; k 0 && poretypeindextrab.length > 0){ //Save cortical pores roiManager("deselect"); roiManager("Select", poretypeindexcor); roiManager("Save Selected", poredir+origname+"_Cortical_Pores.zip"); //Save trabecularized pores roiManager("deselect"); roiManager("Select", poretypeindextrab); roiManager("Save Selected", poredir+origname+"_Trabecularized_Pores.zip"); } //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //SAVE TOTAL PORE IMAGE DECOY //Make blank duplicate to hold output total pore image selectImage(marrow); run("Select None"); run("Remove Overlay"); selectImage(marrow); run("Duplicate...", " "); totpores=getTitle(); //Clear blank duplicate selectImage(totpores); run("Select All"); setBackgroundColor(0, 0, 0); run("Clear", "slice"); run("Clear Outside", "slice"); run("Select None"); run("Remove Overlay"); //Open total pore ROI set via user-provided path totpath = poredir+origname+"_Total_Pores.zip"; roiManager("open", totpath); //Change ROI color, fill, and flatten roiManager("Deselect"); RoiManager.setPosition(0); roiManager("Set Fill Color", "white"); selectImage(totpores); roiManager("Show All without Labels"); run("Flatten"); totporesfin=getTitle(); //Close non-flattened image of pores selectImage(totpores); close(); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Save flattened image of total pores, then close selectImage(totporesfin); run("Select None"); run("Remove Overlay"); run("8-bit"); setAutoThreshold("Default dark"); setThreshold(1,255); setOption("BlackBackground", true); run("Convert to Mask"); selectImage(totporesfin); close(); // SAVE TOTAL PORE IMAGE //Make blank duplicate to hold output total pore image selectImage(marrow); run("Select None"); run("Remove Overlay"); selectImage(marrow); run("Duplicate...", " "); totpores=getTitle(); //Clear blank duplicate selectImage(totpores); run("Select All"); setBackgroundColor(0, 0, 0); run("Clear", "slice"); run("Clear Outside", "slice"); run("Select None"); run("Remove Overlay"); //Open total pore ROI set via user-provided path totpath = poredir+origname+"_Total_Pores.zip"; roiManager("open", totpath); //Change ROI color, fill, and flatten roiManager("Deselect"); RoiManager.setPosition(0); roiManager("Set Fill Color", "white"); selectImage(totpores); roiManager("Show All without Labels"); run("Flatten"); totporesfin=getTitle(); //Close non-flattened image of pores selectImage(totpores); close(); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Save flattened image of total pores, then close selectImage(totporesfin); run("Select None"); run("Remove Overlay"); run("8-bit"); setAutoThreshold("Default dark"); setThreshold(1,255); setOption("BlackBackground", true); run("Convert to Mask"); selectImage(totporesfin); saveAs("Tiff",poredir+origname+"_"+"Total_Pores.tif"); totporesfin=getTitle(); selectImage(totporesfin); close(); //If both cortical and trabecularized pores have pores, then save images individiually if (poretypeindexcor.length > 0 && poretypeindextrab.length > 0){ //Make blank duplicate to hold output cortical pore image selectImage(marrow); run("Select None"); run("Remove Overlay"); selectImage(marrow); run("Duplicate...", " "); corpores=getTitle(); //Clear blank duplicate selectImage(corpores); run("Select All"); setBackgroundColor(0, 0, 0); run("Clear", "slice"); run("Clear Outside", "slice"); run("Select None"); run("Remove Overlay"); //Open cortical pore ROI set via user-provided path corpath = poredir+origname+"_Cortical_Pores.zip"; roiManager("open", corpath); //Change ROI color, fill, and flatten roiManager("Deselect"); RoiManager.setPosition(0); roiManager("Set Fill Color", "white"); selectImage(corpores); roiManager("Show All without Labels"); run("Flatten"); corporesfin=getTitle(); //Close non-flattened image of pores selectImage(corpores); close(); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Save flattened image of cortical pores, then close selectImage(corporesfin); run("Select None"); run("Remove Overlay"); run("8-bit"); setAutoThreshold("Default dark"); setThreshold(1,255); setOption("BlackBackground", true); run("Convert to Mask"); selectImage(corporesfin); saveAs("Tiff",poredir+origname+"_"+"Cortical_Pores.tif"); corporesfin=getTitle(); selectImage(corporesfin); close(); //Make blank duplicate to hold output trabecularized pore image selectImage(marrow); run("Select None"); run("Remove Overlay"); selectImage(marrow); run("Duplicate...", " "); trabpores=getTitle(); //Clear blank duplicate selectImage(trabpores); run("Select All"); setBackgroundColor(0, 0, 0); run("Clear", "slice"); run("Clear Outside", "slice"); run("Select None"); run("Remove Overlay"); //Open trabecularized pore ROI set via user-provided path trabpath = poredir+origname+"_Trabecularized_Pores.zip"; roiManager("open", trabpath); //Change ROI color, fill, and flatten roiManager("Deselect"); RoiManager.setPosition(0); roiManager("Set Fill Color", "white"); selectImage(trabpores); roiManager("Show All without Labels"); run("Flatten"); trabporesfin=getTitle(); //Close non-flattened image of pores selectImage(trabpores); close(); //Clear ROI manager finalcount=roiManager("count")-1; if(finalcount>=0){ roiManager("Deselect"); roiManager("Delete"); } //Save flattened image of trabecularized pores, then close selectImage(trabporesfin); run("Select None"); run("Remove Overlay"); run("8-bit"); setAutoThreshold("Default dark"); setThreshold(1,255); setOption("BlackBackground", true); run("Convert to Mask"); selectImage(trabporesfin); saveAs("Tiff",poredir+origname+"_"+"Trabecularized_Pores.tif"); trabporesfin=getTitle(); selectImage(trabporesfin); close(); } //Do not save any images if there are no cortical pores or trabecularized pores individually //Resave cross-sectional geometry ROIs as ROI set //Reopen TA and MA and CA roiManager("Open", geomdir+origname+"_"+"TtAr.roi"); roiManager("Open", geomdir+origname+"_"+"EsAr.roi"); roiManager("Open", geomdir+origname+"_"+"CtAr.roi"); //Rename reopened ROIs roiManager("Select", 0); roiManager("Rename", "TtAr"); roiManager("Set Color", border_color_choice); roiManager("Select", 1); roiManager("Rename", "EsAr"); roiManager("Set Color", border_color_choice); roiManager("Select", 2); roiManager("Rename", "CtAr"); roiManager("Set Color", border_color_choice); //Save as ROI set roiManager("Deselect"); roiManager("Save", geomdir+origname+"_"+"Borders_RoiSet.zip"); //Delete temp ROIs TtArROI = geomdir+origname+"_"+"TtAr.roi"; if (File.exists(TtArROI)) {File.delete(TtArROI);} EsArROI = geomdir+origname+"_"+"EsAr.roi"; if (File.exists(EsArROI)) {File.delete(EsArROI);} CtArROI = geomdir+origname+"_"+"CtAr.roi"; if (File.exists(CtArROI)) {File.delete(CtArROI);} //Close any remaining images while (nImages>0) { selectImage(nImages); close(); } //Close any remaining windows list = getList("window.titles"); for (i=0; i