//**************************************************************************************
//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"
+""
+"- shift to draw horizontal or vertical lines
"
+" - alt to draw in background color (or
to erase if painting on overlay
"
+" - "+ ctrlString+"-shift-drag to change "+(isPencil ? "pencil" : "brush")+" width
"
+" - "+ ctrlString+"-click to change (\"pick up\") the
"
+"drawing color, or use the Color
Picker (shift-k)
"
+"
"
+"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