//************************************************************************************** // Copyright (C) 2022 Mary E. Cole / Pore Extractor 2D // First Release Date: //************************************************************************************** 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", "-", "About", "Cite")); macro "PoreExtractor 2D Menu Tool - C000D6cC000D5cD7cC000D5bC000D4aC000Da3DacC000D79C000D4bC000D7dC111D8dD93Db3C111D6bC111D6dC111Dd4C111DbcC111De5C111D8cC222D62C222D72C222D9cD9dC222D52C222D95C222D38C222C333D82C333D96C333D88C333D97C333Dc3C333D84C333D26D83C333C444D37C444D42C444D75C444Dc4De8C444D7aC444D94C444D49DdbC444D15D6aD8aD9aC444D4cDe9C555D5dC555D92C555D5aC555D39C555DdaC555D32C555D7bC555De6C555D89C555C666DcbC666DadDccC666De4C666D13C666D74D78C666De7C777D87C777D27C777D04C777D23C777C888D65C888D05C888Da2C888Df6C888D3aC888D22C888D16D99C999Dd3DeaC999D69Da6C999Db4C999D73Da5C999Da4C999Df7CaaaD63D85CaaaDaaCaaaDd5CaaaDabCaaaDf5CaaaD36CaaaD53CbbbD33D48CbbbDbbCbbbDd9CbbbDbdCbbbD3bCbbbCcccDb2CcccD43CcccD98CcccDa9CcccD7eDf8CcccD8bCcccD14CcccD6eCcccD8eCcccCdddD03CdddD4dCdddD25D66CdddD76D9bCdddDa7CdddD12D28D61CdddDdcCdddD64D9eCeeeD71CeeeD51CeeeDebCeeeD59D86CeeeDd8CeeeD81DcaCeeeD47Dc2CeeeD41CeeeD77Df4CfffDe3Df9CfffD3cD68CfffD06D5eDc5CfffDcdCfffDd6CfffD17D29D91CfffD31CfffDaeCfffD24D35Dd7CfffD02D21D46D55D56Da8Db5Dd2Dfa"{ PE2Dmacro = getArgument(); if (PE2Dmacro!="-") { if (PE2Dmacro=="Clip Trabeculae") { ClipROI(); } 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(); } } } //************************************************************************************** // Clip Trabeculae Function // Copyright (C) 2022 Mary E. Cole / Pore Extractor 2D // First Release Date: //************************************************************************************** function ClipROI() { setBatchMode(false); requires("1.53g"); Dialog.createNonBlocking("Welcome to ROI Extractor!"); Dialog.addMessage("Warning: Any current images or windows will be closed! \n \n Click OK to begin!"); 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{ Dialog.createNonBlocking("Trabecular Clipping"); Dialog.setInsets(0,0,0) Dialog.addMessage("Instructions",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("Seal cracks through cortex with black paintbrush at each end") Dialog.setInsets(0,0,0) Dialog.addMessage("Separate trabeculae from cortex with white paintbrush") Dialog.setInsets(0,0,0) Dialog.addMessage("Ctrl + Z to immediately delete drawn line") Dialog.setInsets(0,0,0) Dialog.addMessage("Ctrl + Shift + Drag Mouse to change paintbrush size") Dialog.setInsets(0,0,0) Dialog.addMessage("Keyboard Shortcuts",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("[4] Zoom Tool (Left-Click = Zoom In, Right-Click = Zoom Out)") Dialog.setInsets(0,0,0) Dialog.addMessage("[5] Scrolling Tool (Grab and Drag)") Dialog.setInsets(0,0,0) Dialog.addMessage("[7] Paintbrush Tool (Click and Drag)") Dialog.setInsets(0,0,0) Dialog.addMessage("[w] Make Paintbrush White") Dialog.setInsets(0,0,0) Dialog.addMessage("[b] Make Paintbrush Black") Dialog.setInsets(0,0,0) Dialog.addMessage("[F12] Auto-save Current Image") Dialog.setInsets(5,0,0) Dialog.addMessage("Exiting Macro",14,"#7f0000"); Dialog.setInsets(0,0,0) modifydialog = newArray("Keep clipping trabeculae","Save final image and exit"); Dialog.addRadioButtonGroup("", modifydialog, 2, 1, "Keep clipping trabeculae"); Dialog.show(); exitmodify = Dialog.getRadioButton(); } while (exitmodify=="Keep clipping trabeculae"); //Save final image and exit macro dirpath= call("ij.Prefs.get", "appendimage.dir", "nodir"); origname= call("ij.Prefs.get", "orig.string", ""); saveAs("Tiff", dirpath+origname+".tif"); //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{ Dialog.createNonBlocking("Wand ROI Selection"); Dialog.setInsets(0,0,0) Dialog.addMessage("Instructions",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("Select Wand tool [1], click outside border, and adjust wand tolerance [2]") Dialog.setInsets(0,0,0) Dialog.addMessage("Use [Backspace] to clear inside selection") Dialog.setInsets(0,0,0) Dialog.addMessage("Use [0] clear outside selection") Dialog.setInsets(0,0,0) Dialog.addMessage("Separate stray debris from cortex with paintbrush [7] or Freehand Selection tool [3]") Dialog.setInsets(0,0,0) Dialog.addMessage("Keyboard Shortcuts",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("[Ctrl + Z] Immediately Undo Paintbrush or Clear Selection") Dialog.setInsets(0,0,0) Dialog.addMessage("[Ctrl + Shift + Drag Mouse] Change Paintbrush Size") Dialog.setInsets(0,0,0) Dialog.addMessage("[Ctrl + Shift + A] Select None") Dialog.setInsets(0,0,0) Dialog.addMessage("[Backspace] Clear inside selection") Dialog.setInsets(0,0,0) Dialog.addMessage("[0] Clear outside selection") Dialog.setInsets(0,0,0) Dialog.addMessage("[1] Wand Tool") Dialog.setInsets(0,0,0) Dialog.addMessage("[2] Adjust Wand Tool Tolerance") Dialog.setInsets(0,0,0) Dialog.addMessage("[3] Freehand Selection Tool (Select with Mouse or Stylus)") Dialog.setInsets(0,0,0) Dialog.addMessage("[4] Zoom Tool (Left-Click = Zoom In, Right-Click = Zoom Out)") Dialog.setInsets(0,0,0) Dialog.addMessage("[5] Scrolling Tool (Grab and Drag)") Dialog.setInsets(0,0,0) Dialog.addMessage("[7] Paintbrush Tool (Click and Drag)") Dialog.setInsets(0,0,0) Dialog.addMessage("[w] Make Paintbrush White") Dialog.setInsets(0,0,0) Dialog.addMessage("[b] Make Paintbrush Black") Dialog.setInsets(0,0,0) Dialog.addMessage("[F4] Reset Wand Tool Tolerance to Zero") Dialog.setInsets(0,0,0) Dialog.addMessage("[F12] Auto-save Current Image") Dialog.setInsets(5,0,0) Dialog.addMessage("Exiting Macro",14,"#7f0000"); Dialog.setInsets(0,0,0) modifydialog = newArray("Keep clearing borders","Save final border ROIs"); Dialog.addRadioButtonGroup("", modifydialog, 2, 1, "Keep clearing borders"); Dialog.show(); exitmodify = Dialog.getRadioButton(); } while (exitmodify=="Keep clearing borders"); //Save final ROIs showStatus("!Saving Cleared Image..."); //Save final image origimg=getTitle(); 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 0){ roiManager("Select",0); run("Interpolate", "interval=" + smooth_size); roiManager("Update"); roiManager("Select",1); run("Interpolate", "interval=" + smooth_size); roiManager("Update"); } //Combine TA (ROI 0) and MA (ROI 1) as CA (ROI 2) roiManager("Deselect"); roiManager("Select", newArray(0,1)); roiManager("XOR"); roiManager("Add"); roiManager("Select", 2); roiManager("Rename", "CtAr"); //Start Log print("**ROI Smoothing Log for " + origname + "**" ); print(""); print("ROI Interpolation = " + smooth_size + " pixels" ); print(""); //Save log selectWindow("Log"); saveAs("Text", dir+origname+"_"+"Smoothing_Log.txt"); //Save as ROI set roiManager("Deselect"); roiManager("Save", dir+origname+"_"+"Borders_RoiSet_Touchup.zip"); //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 file roi if it exists temproidir = dir+origname+"_"+"Temp"+".roi"; if (File.exists(temproidir)) {File.delete(temproidir);} //Close existing images and windows while (nImages>0) { selectImage(nImages); close(); } openwindows = getList("window.titles"); for (i=0; i= 0){ //Save current ROI index call("ij.Prefs.set", "roi.number", currentroi); //Save current ROI name currentname = Roi.getName(); call("ij.Prefs.set", "roi.name", currentname); //Save as temp ROI dirpath= call("ij.Prefs.get", "appendimage.dir", "nodir"); origpath= call("ij.Prefs.get", "orig.string", ""); roiManager("Save", dirpath+origpath+"_"+"Temp"+".roi"); //Run the node interpolation node_size = call("ij.Prefs.get", "nodesize.number",""); run("Interpolate", "interval=1"); run("Fit Spline"); roiManager("Update"); run("Interpolate", "interval=" + node_size); roiManager("Update"); roiManager("Deselect"); roiManager("Select", currentroi); setTool("freehand"); } } macro "Convert ROI to Nodes [n9]" { //Check whether ROIs are selected currentroi = roiManager("index"); //If no ROIs are selected, switch to freehand selection tool if (currentroi < 0){showStatus("Select a border to modify");} //If ROI is selected, switch to selection brush if (currentroi >= 0){ //Save current ROI index call("ij.Prefs.set", "roi.number", currentroi); //Save current ROI name currentname = Roi.getName(); call("ij.Prefs.set", "roi.name", currentname); //Save as temp ROI dirpath= call("ij.Prefs.get", "appendimage.dir", "nodir"); origpath= call("ij.Prefs.get", "orig.string", ""); roiManager("Save", dirpath+origpath+"_"+"Temp"+".roi"); //Run the node interpolation run("Interpolate", "interval=1"); run("Fit Spline"); roiManager("Update"); run("Interpolate", "interval=" + node_size); roiManager("Update"); roiManager("Deselect"); roiManager("Select", currentroi); setTool("freehand"); } } macro "Revert ROI After Node Conversion [r]" { //Delete converted ROI based on index deleteroi = call("ij.Prefs.get", "roi.number", ""); //Get name of delete ROI renameroi = call("ij.Prefs.get", "roi.name", ""); //Delete converted ROI roiManager("Select", deleteroi); roiManager("Delete"); //Reopen saved ROI dirpath = call("ij.Prefs.get", "appendimage.dir", "nodir"); origpath = call("ij.Prefs.get", "orig.string", ""); temproidir = dirpath+origpath+"_"+"Temp"+".roi"; if (File.exists(temproidir)) {roiManager("Open", temproidir);} //Select new loaded ROI loadedroi = roiManager("count")-1; roiManager("Deselect"); roiManager("Select", loadedroi); }; macro "Revert ROI After Node Conversion [R]" { //Delete converted ROI based on index deleteroi = call("ij.Prefs.get", "roi.number", ""); //Get name of delete ROI renameroi = call("ij.Prefs.get", "roi.name", ""); //Delete converted ROI roiManager("Select", deleteroi); roiManager("Delete"); //Reopen saved ROI dirpath = call("ij.Prefs.get", "appendimage.dir", "nodir"); origpath = call("ij.Prefs.get", "orig.string", ""); temproidir = dirpath+origpath+"_"+"Temp"+".roi"; if (File.exists(temproidir)) {roiManager("Open", temproidir);} //Select new loaded ROI loadedroi = roiManager("count")-1; roiManager("Deselect"); roiManager("Select", loadedroi); }; macro "Save Current ROI Set [s]" { dirpath= call("ij.Prefs.get", "appendimage.dir", "nodir"); origname = call("ij.Prefs.get", "orig.string", ""); //Combine TA (ROI 0) and MA (ROI 1) as CA (ROI 2) 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", dirpath+origname+"_"+"Borders_RoiSet_Temp.zip"); //Delete cortical area roiManager("Select", 2); roiManager("Delete"); } macro "Save Current ROI Set [S]" { dirpath= call("ij.Prefs.get", "appendimage.dir", "nodir"); origname = call("ij.Prefs.get", "orig.string", ""); //Combine TA (ROI 0) and MA (ROI 1) as CA (ROI 2) 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", dirpath+origname+"_"+"Borders_RoiSet_Temp.zip"); //Delete cortical area roiManager("Select", 2); roiManager("Delete"); } //************************************************************************************** // Image Pre-Processing Function // Copyright (C) 2022 Mary E. Cole / Pore Extractor 2D // First Release Date: //************************************************************************************** function Preprocess() { setBatchMode(true); requires("1.53g"); //Close any open windows or images Dialog.createNonBlocking("Welcome to Pore Extractor 2D!"); Dialog.addMessage("Warning: Any current images or windows will be closed! \n \nNext you will open a cross-section that is: \n \nCleared (black) within the marrow cavity\nCleared (black) outside the periosteum\n \nClick OK to begin!"); 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"); } //Set scale Dialog.createNonBlocking("Set Image Scale"); Dialog.addNumber("Pixel Size:",0); Dialog.addToSameRow(); Dialog.addMessage("pixels / mm"); Dialog.show(); scale = Dialog.getNumber(); //Adjust colors run("Colors...", "foreground=white background=black selection=cyan"); //User choice of processing options var image_choice = ""; var origimg = ""; var origname = ""; var contrast_choice = ""; var equalize_choice = ""; var contrast_sat = ""; var bc_choice = ""; var elc_choice = ""; var elc_blocksize = ""; var elc_slope = ""; var highpass_choice = ""; var background_choice = ""; var rolling_ball = ""; var background_type = ""; var gaussian_choice = ""; var gaussian_level = ""; var combined_channel_choice = ""; var red_channel_choice = ""; var blue_channel_choice = ""; var green_channel_choice = ""; var dir = ""; //User choice of processing options Dialog.createNonBlocking("Image Preprocessing"); Dialog.setInsets(0,20,0) Dialog.addMessage("Process Image or Folder",12,"#7f0000"); image_label = newArray("Single Image","Batch Process Folder"); Dialog.setInsets(0,25,0) Dialog.addRadioButtonGroup("", image_label, 2, 1, "Single Image"); Dialog.setInsets(0,20,0) Dialog.addMessage("Contrast Enhancement",12,"#7f0000"); Dialog.setInsets(0,25,0) Dialog.addCheckbox("Auto Brightness-Contrast",false); Dialog.setInsets(0,25,0) Dialog.addCheckbox("Enhance Global Contrast",false); Dialog.setInsets(0,40,0) contrast_label = newArray("Normalize","Equalize"); Dialog.addToSameRow(); Dialog.addNumber("", 0.3, 1, 1, "%") Dialog.addRadioButtonGroup("", contrast_label, 2, 1, "Normalize"); Dialog.setInsets(0,25,0) Dialog.addCheckbox("Enhance Local Contrast",false); Dialog.setInsets(0,15,0) Dialog.addNumber("", 127, 0, 1, "Blocksize (px)") Dialog.setInsets(0,15,0) Dialog.addNumber("", 3.00, 2, 1, "Max Slope (> 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","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();;;;;;;;;; //Single Image---------------------------------------------------------- if(image_choice == "Single Image"){ //Prompt user to open the cleaned cross-sectional image file origpath = File.openDialog("Load the Cleared Image"); //Prompt user to select output directory dir=getDirectory("Select Output Location"); //Open the image and run the function open(origpath); origimg=getTitle(); origname=File.nameWithoutExtension; //Trim origname origname = replace(origname,"_Trimmed",""); origname = replace(origname,"_Cleared",""); origname = replace(origname,"_Touchup",""); origname = replace(origname,"_Temp",""); origname = replace(origname,"_Preprocessed_Red",""); origname = replace(origname,"_Preprocessed_Blue",""); origname = replace(origname,"_Preprocessed_Green",""); origname = replace(origname,"_Preprocessed",""); //Set scale for cross-sectional measurements according to user input run("Set Scale...", "distance=scale known=1 pixel=1 unit=mm global"); Preprocess_function(origimg, origname, bc_choice, contrast_choice, contrast_sat, equalize_choice, elc_choice, elc_blocksize, elc_slope, highpass_choice, background_choice, rolling_ball, background_type, gaussian_choice, 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"); //Close log window openwindows = getList("window.titles"); for (i=0; i=0; i--) { if (!endsWith(list[i], "tif") && !endsWith(list[i], "bmp")){ list = Array.deleteIndex(list, i); } } //If no tif or bmp files were detected, display warning if (list.length == 0){ exit("Macro Terminated: No .tif or .bmp images detected"); } //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"); //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 Preprocessing Log for " + origname + "**" ); print(""); //Auto BC if(bc_choice == 1){ print("Auto Brightness-Contrast"); showStatus("!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"); } //Enhance Global Contrast if(contrast_choice == 1){ showStatus("!Enhancing Global Contrast..."); if(equalize_choice == "Normalize"){ print("Enhance Global Contrast - Normalize Histogram 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"); } if(equalize_choice == "Equalize"){ print("Enhance Global Contrast - Equalize Histogram 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"); } } //Enhance local contrast if(elc_choice == 1){ print("Enhance Local Contrast - Blocksize " + elc_blocksize + " pixels - Maximum Slope " + elc_slope); showStatus("!Enhancing Local Contrast..."); 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"); } //Highpass filter if(highpass_choice == 1){ print("High Pass Filter"); showStatus("!High Pass Filter..."); 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"); } //Subtract background if(background_choice == 1){ showStatus("!Subtracting Background..."); if(background_type == "Rolling Ball"){ print("Subtract Background - Rolling Ball - " + 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"); } if(background_type == "Sliding Parabaloid"){ print("Subtract Background - 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"); } } //Gaussian if(gaussian_choice == 1){ showStatus("!Gaussian Blur..."); print("Gaussian Blur 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"); } //If user wants no channels split or if user wants a combined image, export image as-is if(combined_channel_choice == 0 && red_channel_choice == 0 && blue_channel_choice == 0 && green_channel_choice == 0){ showStatus("!Saving Images..."); selectImage(origimg); run("Select None"); run("Remove Overlay"); saveAs("TIFF", dir+origname+"_"+"Preprocessed.tif"); origimg=getTitle(); } if(combined_channel_choice == 1){ showStatus("!Saving Images..."); selectImage(origimg); run("Select None"); run("Remove Overlay"); saveAs("TIFF", dir+origname+"_"+"Preprocessed.tif"); origimg=getTitle(); } //Split channels and export selected ---------------------------------------------------------------------------------------------------------------------------------------------- if(red_channel_choice == 1 || blue_channel_choice == 1 || green_channel_choice == 1){ showStatus("!Saving Images..."); print("Split Color Channels"); //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+"_"+"Preprocessed_Red.tif"); } if(blue_channel_choice == 1){ selectImage(origimg_blue); saveAs("TIFF", dir+origname+"_"+"Preprocessed_Blue.tif"); } if(green_channel_choice == 1){ selectImage(origimg_green); saveAs("TIFF", dir+origname+"_"+"Preprocessed_Green.tif"); } //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 " + origname + "**" ); 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(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..."); //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.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(); //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();; //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 " + origname + "**" ); 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+"_"+"PoreExtractor_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=0){ roiManager("Deselect"); roiManager("Delete"); } //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"); } 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); //If user chose to split channels, split and display if (channelchoice == "Choose Color Channel"){ //Split channels selectImage(origimg); run("Split Channels"); //Reopen original image open(origpath); origimg=getTitle(); origimgext = File.name; //Clear overlays selectImage(origimg); run("Select None"); run("Remove Overlay"); //Remove scale run("Set Scale...", "distance=0 known=0 pixel=1 unit=pixel"); //Modify image names origimg_red = origimgext + " (red)"; origimg_blue = origimgext + " (blue)"; origimg_green = origimgext + " (green)"; //Display ROIs on all if ROIs loaded if(loadchoice == "Load Existing ROI Set"){ selectImage(origimg); roiManager("Show All without labels"); selectImage(origimg_red); roiManager("Show All without labels"); selectImage(origimg_blue); roiManager("Show All without labels"); selectImage(origimg_green); roiManager("Show All without labels"); } //Tile images run("Tile"); //Set tool to zoom setTool("zoom"); Dialog.createNonBlocking("Select Image Channel") Dialog.addMessage("Select image for pore modification"); Dialog.setInsets(0,0,0) Dialog.addImageChoice(""); Dialog.show(); origimg = Dialog.getImageChoice(); //Close all images except selected selectImage(origimg); close("\\Others"); //Reset origpores size selectImage(origimg); run("Original Scale"); //Display ROIs if ROIS loaded if(loadchoice == "Load Existing ROI Set"){ selectImage(origimg); roiManager("Show All without labels"); } } else{ selectImage(origimg); roiManager("Show All without labels"); } //Keyboard shortcut popup Table.create("Pore Modifier Keyboard Shortcuts"); shortcuts = newArray("[Backspace]","[t]","[1]","[2]","[3]","[4]","[5]","[6]","[F1]","[F2]","[F3]","[F4]","[F5]","[F6]","[F7]","[F8]","[F9]","[F10]","[F11]"); shortcutfunctions = newArray("Delete Selected ROI","Add Selection as ROI","Wand Tool","Adjust Wand Tool Tolerance","Freehand Selection Tool (Draw with Mouse/Stylus)","Zoom Tool (Left-Click = Zoom In, Right-Click = Zoom Out)","Scrolling Tool (Grab and Drag)","Selection Brush Tool","Toggle ROI Labels On","Toggle ROI Labels Off","Toggle ROIs Off","Reset Wand Tool Tolerance to Zero","Split ROI After Selection Brush Division","Fill ROI After Selection Brush Expansion","Merge Adjacent ROIs","Decrease Selection Brush Size by 5 pixels","Increase Selection Brush Size by 5 pixels","Revert ROI Immediately After Split or Fill","Save New Version of ROI Set"); Table.setColumn("Keyboard Shortcut", shortcuts); Table.setColumn("Function", shortcutfunctions); Table.setLocationAndSize(0, 0, 550, 500) //Do-While loop for manual modification do{ Dialog.createNonBlocking("Manual Modification of ROI Set"); Dialog.setInsets(0,0,0) Dialog.addMessage("Removing Incorrect ROIs",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("Toggle labels ON [F1]"); Dialog.setInsets(0,0,0) Dialog.addMessage("Select label of incorrect ROI with freehand selection tool [3], then use [Backspace] to delete") Dialog.setInsets(5,0,0) Dialog.addMessage("Adding Missed ROIs",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("Toggle labels OFF [F2] to view selection quality") Dialog.setInsets(0,0,0) Dialog.addMessage("Select Wand tool [1] and modulate tolerance [2] to auto-select ROIs") Dialog.setInsets(0,0,0) Dialog.addMessage("Or use Freehand Selection tool [3] to outline ROIs with mouse or touchscreen stylus") Dialog.setInsets(0,0,0) Dialog.addMessage("Use keyboard shortcut [t] to add selection to ROI manager") Dialog.setInsets(5,0,0) Dialog.addMessage("Modifying Existing ROIs with the Selection Brush",14,"#7f0000"); Dialog.setInsets(0,0,0) Dialog.addMessage("Selection brush diameter can be decreased [F8] or increased [F9]") Dialog.setInsets(0,0,0) Dialog.addMessage("Use keyboard shortcut [6] and click label of ROI to be modified") Dialog.setInsets(0,0,0) Dialog.addMessage("Use keyboard shortcut [6] again to switch to selection brush tool") Dialog.setInsets(0,0,0) Dialog.addMessage("Split ROI: ") Dialog.setInsets(0,0,0) Dialog.addMessage(" Hold down [Alt] and draw along split line, then use [F5] to split") Dialog.setInsets(0,0,0) Dialog.addMessage("Fill ROI: ") Dialog.setInsets(0,0,0) Dialog.addMessage(" Hold down [Shift] and draw along rim of ROI to be added, then use [F6] to fill hole") Dialog.setInsets(0,0,0) Dialog.addMessage("Merge Adjacent ROIs: ") Dialog.setInsets(0,0,0) Dialog.addMessage(" Follow prompts to click adjacent ROI labels and connect them by drawing while holding down [Shift]") Dialog.setInsets(0,0,0) Dialog.addMessage("Undo Selection Brush During Split or Fill: ") Dialog.setInsets(0,0,0) Dialog.addMessage(" [Ctrl] + [Shift] + [E] without clicking outside selection") Dialog.setInsets(0,0,0) Dialog.addMessage("Undo Selection Brush After Split or Fill:") Dialog.setInsets(0,0,0) Dialog.addMessage(" [F10] without clicking outside selection") Dialog.setInsets(5,0,0) Dialog.addMessage("Change Pore Colors",14,"#7f0000"); roi_color = newArray("red", "green", "blue","magenta", "cyan", "yellow", "orange", "black", "white"); Dialog.setInsets(0,0,0) Dialog.addChoice("Original Pore Color:", roi_color, roi_color_choice); Dialog.setInsets(0,0,0) Dialog.addChoice("Modified Pore Color:", roi_color, roi_color_save); Dialog.setInsets(0,0,0) Dialog.addMessage("ROI Color Code",14,"#7f0000"); //Dark Color Options 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 = "#0ac2a0";} 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 = "#0ac2a0";} 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";} Dialog.setInsets(0,5,0) Dialog.addMessage("Unmodified ROIs",12,roi_color_choice_display); Dialog.setInsets(0,5,0) Dialog.addMessage("Modified ROIs",12,roi_color_save_display); Dialog.setInsets(5,0,0) Dialog.addMessage("Exiting Macro",14,"#7f0000"); Dialog.setInsets(0,0,0) modifydialog = newArray("Click OK to update colors","Click OK to save final ROI set and exit"); Dialog.addRadioButtonGroup("", modifydialog, 2, 1, "Click OK to update colors"); Dialog.setLocation(550,0) Dialog.show(); exitmodify = Dialog.getRadioButton(); 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); //Change selection color to color choice run("Colors...", "foreground=white background=black selection="+ roi_color_choice); //Get array of updated ROIs //Check whether the updated ROIs include the name previous //Print index of ROIs that do not include the name previous //Modified from: https://forum.image.sc/t/selecting-roi-based-on-name/3809 nR = roiManager("Count"); roiIdx = newArray(nR); roiIdxorig = newArray(nR); k=0; p=0; clippedIdx = newArray(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; 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]" { if (roiManager("count")==0) showStatus("ROI Manager is empty"); else{ dirpath= call("ij.Prefs.get", "appendroi.dir", "nodir"); origpath= call("ij.Prefs.get", "orig.string", ""); //Save the current ROI roiManager("Save", dirpath+origpath+"_"+"Temp"+".roi"); //Save the current last ROI index roicount = roiManager("count") - 1; call("ij.Prefs.set", "roi.number", roicount); //Get current ROI index currentroi = roiManager("index"); roiManager("Update"); roiManager("Split"); //Delete original ROI roiManager("Select", currentroi); roiManager("Delete"); run("Select None"); //Show the revised image roiManager("Show All"); //Delete temp file roi from previous merge, if it exists temproizip = dirpath+origpath+"_"+"Temp"+".zip"; if (File.exists(temproizip)) {File.delete(temproizip); if (isOpen("Log")) { selectWindow("Log"); run("Close" ); } } }; } macro "Fill ROI [f6]" { if (roiManager("count")==0) showStatus("ROI Manager is empty"); else{ dirpath= call("ij.Prefs.get", "appendroi.dir", "nodir"); origpath= call("ij.Prefs.get", "orig.string", ""); //Save the current ROI roiManager("Save", dirpath+origpath+"_"+"Temp"+".roi"); //Save the current last ROI index roicount = roiManager("count") - 1; call("ij.Prefs.set", "roi.number", roicount); //Set binary options to black background setOption("BlackBackground", true); //Get current ROI index currentroi = roiManager("index"); roiManager("Update"); 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"); //Show the revised image roiManager("Show All"); //Delete temp file roi from previous merge, if it exists temproizip = dirpath+origpath+"_"+"Temp"+".zip"; if (File.exists(temproizip)) {File.delete(temproizip); if (isOpen("Log")) { selectWindow("Log"); run("Close" ); } } }; } macro "Merge ROIs [f7]" { if (roiManager("count")==0) showStatus("ROI Manager is empty"); else{ dirpath= call("ij.Prefs.get", "appendroi.dir", "nodir"); origpath= call("ij.Prefs.get", "orig.string", ""); //Save the current last ROI index roicount = roiManager("count") - 1; call("ij.Prefs.set", "roi.number", roicount); //Prompt user to select ROIs to merge run("Select None"); roiManager("Show All with labels") setTool("freehand"); waitForUser("Select First ROI \nThen click OK"); firstroi = roiManager("index"); setTool("freehand"); waitForUser("Select Second ROI\nThen click OK"); secondroi = roiManager("index"); //Save the two ROIs as a temp ROI zip file roiManager("Select", newArray(firstroi,secondroi)); roiManager("Save", dirpath+origpath+"_"+"Temp"+".zip"); //Combine ROIs roiManager("Select", newArray(firstroi,secondroi)); roiManager("XOR"); roiManager("Add"); //Delete original ROIs roiManager("Select", newArray(firstroi,secondroi)); roiManager("Delete"); //Set binary options to black background setOption("BlackBackground", true); //Switch to selection brush tool setTool("brush"); joined = roiManager("count") - 1; roiManager("Select", joined); waitForUser("Hold [Shift] and join ROIs \n \nUse [F8] to decrease selection brush size \n\nUse [F9] to increase selection brush size\n \nThen click OK"); roiManager("Update"); 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", joined); roiManager("Delete"); run("Select None"); //Show the revised image roiManager("Show All"); //Delete temp file roi from previous split or fill, if it exists temproidir = dirpath+origpath+"_"+"Temp"+".roi"; if (File.exists(temproidir)) {File.delete(temproidir); if (isOpen("Log")) { selectWindow("Log"); run("Close" ); } } }; } macro "Decrease Selection Brush Size [f8]" { setTool("brush"); a=call("ij.gui.Toolbar.getBrushSize"); step_in = Math.abs(a); step_out = step_in - 5; //Floor is 5 pixels step_out = Math.max(step_out, 5) b=call("ij.gui.Toolbar.setBrushSize",step_out); a=call("ij.gui.Toolbar.getBrushSize"); showStatus("Selection Brush Size: " + step_out); } macro "Increase Selection Brush Size [f9]" { setTool("brush"); a=call("ij.gui.Toolbar.getBrushSize"); step_in = Math.abs(a); step_out = step_in + 5; b=call("ij.gui.Toolbar.setBrushSize",step_out); a=call("ij.gui.Toolbar.getBrushSize"); showStatus("Selection Brush Size: " + step_out); } macro "Revert ROI [f10]" { if (roiManager("count")==0) showStatus("ROI Manager is empty"); else{ roiManager("Deselect"); roicurrent = call("ij.Prefs.get", "roi.number", ""); roiend = roiManager("count"); roidiff = Math.abs(roiend)-Math.abs(roicurrent); var roibound = newArray; 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; j--) { if (!matches(list1[j], ".*ModifiedRoiSet.*")){ 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+"_"+"ModifiedRoiSet"+"_"+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: //************************************************************************************** function Analyze() { requires("1.53g"); setBatchMode(true); setBatchMode("hide"); //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 Image (Preprocessing Optional)"); //Prompt user to load ROI set to modify roipath = File.openDialog("Open Final Pore ROIset"); //Prompt user to select output directory dir=getDirectory("Select Output Location"); //Set scale Dialog.createNonBlocking("Set Image Scale"); Dialog.addNumber("Pixel Size:",0); Dialog.addToSameRow(); Dialog.addMessage("pixels / mm"); Dialog.show(); scale = Dialog.getNumber(); scale_um = scale/1000; //Open the cleaned cross-sectional image open(origpath); origimg=getTitle(); origname=File.nameWithoutExtension; //Trim origname origname = replace(origname,"_Trimmed",""); origname = replace(origname,"_Cleared",""); origname = replace(origname,"_Touchup",""); origname = replace(origname,"_Temp",""); origname = replace(origname,"_Preprocessed_Red",""); origname = replace(origname,"_Preprocessed_Blue",""); origname = replace(origname,"_Preprocessed_Green",""); origname = replace(origname,"_Preprocessed",""); //Remove any overlays selectImage(origimg); run("Select None"); run("Remove Overlay"); //Ensure background set to black and foreground set to white run("Colors...", "foreground=white background=black selection=cyan"); //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 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 RCA and PI values rca = "Relative Cortical Area"; Table.create(rca); print("[" + rca + "]","\\Headings:Image\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) * 1.5 * rMax); Majory1 = floor(cY + sin(-th) * 1.5 * rMax); Majorx2 = floor(cX + cos(-th) * 1.5 * rMax); Majory2 = floor(cY - sin(-th) * 1.5 * 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("cyan") 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("cyan") 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 total image again selectImage(total); 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 selection=cyan"); 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"); 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 selection=cyan"); //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, "Lateral"); Dialog.addToSameRow(); Dialog.setInsets(5, 0, 5) Dialog.addChoice("", orientation,"Medial"); 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, "Medial"); Dialog.setInsets(5, 0, 5) Dialog.addChoice("Quadrant 3 :", orientation,"Posterior"); Dialog.setInsets(5, 0, 5) Dialog.addChoice("Quadrant 4: ", orientation,"Lateral"); 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("cyan") 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("cyan") 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 selection=cyan"); 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 selection=cyan"); //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 neutral color cyan and fill and save to region subfolders, but do not close roiManager("Select", 0); roiManager("Set Color", "cyan"); roiManager("Select", 1); roiManager("Set Color", "cyan"); 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 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", "cyan"); roiManager("Select", 1); roiManager("Rename", "EsAr"); roiManager("Set Color", "cyan"); roiManager("Select", 2); roiManager("Rename", "CtAr"); roiManager("Set Color", "cyan"); //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