Last active
May 14, 2020 10:24
-
-
Save github-martin/e699d18ae6cfa5b1a6aace15d3c3544c to your computer and use it in GitHub Desktop.
macro collection Butt et al., Nature metabolism 2020
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This GitHub Gist contains 3 Fiji/ImageJ macros that were used for data analysis in the Butt et al. paper "A molecular mechanism explaining albuminuria in kidney disease" published in Nature metabolism 2020. | |
https://www.nature.com/articles/s42255-020-0204-y | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Macro written for the Butt et al., 2020 paper to get the slit diaphragm length per area | |
* from nephrin-stained kidney STED images. | |
* The macro uses the Ridge Detection Plugin from the BioMedGroup Fiji update site (Version 1.4.0 at the time of analysing the images for the paper). | |
* https://imagej.net/Ridge_Detection | |
* | |
* Fiji/ImageJ version used to analyse the data from the paper: 2.0.0-rc-67/1.52c | |
* | |
* Usage: | |
* - The maccro asks for a STED image | |
* - The user has to select one or more appropriate areas | |
* - The macro does the rest without further user interference | |
* - Results are saved automatically | |
* | |
* Martin Höhne, Nephrolab Cologne, 2018 | |
* | |
*/ | |
//instructions(); //own function. Currently inactive - could be used to display some instructions | |
//Variables | |
//preprocessing | |
bkgrd=50; | |
mean_rad=3; | |
enh_contr=0.5; | |
//ridge detection plugin variables | |
rlw = 10; //Ridge line_width=9 | |
rhc = 255; //Ridge high_contrast=255 | |
rlc = 0; //Ridge low_contrast=0 | |
rminll = 2*rlw; //Ridge minimum_line_length=18 | |
rmaxll = 0; //Rigde maximum_line_length=0.00 | |
//other | |
v = "slit_measurement_v01e-final.ijm"; | |
textsizeoverlay = 48; | |
run("Close All"); | |
roiManager("Reset"); | |
//open image and create resultsdirectory | |
img = File.openDialog("Select the STED image file"); | |
imgpath = File.directory; | |
imgcore = File.nameWithoutExtension; | |
open(img); | |
originalID = getImageID; | |
//deduce name for the results directory and image | |
newImageAndResultsName = deduceResultDirandImagename(imgpath, imgcore); // own Function | |
// the string array "newImageAndResultsName" with the following contents is returned: | |
// 0 = animal | |
// 1 = age | |
// 2 = genotype | |
// 3 = image number | |
// 4 = new name for resultsdir and results files | |
//Array.show(newImageAndResultsName); | |
//print(imgpath); | |
//print(imgcore); | |
newDir = imgpath+File.separator+newImageAndResultsName[4]; | |
File.makeDirectory(newDir); | |
//prepare log window. The log will be saved in the end | |
print ("\\Clear"); | |
print ("Macro version: \t"+v); | |
print ("Fiji/ImageJ version: \t"+getVersion); | |
print ("Original image name: \t", img); | |
//preprocessing of the image | |
selectImage(originalID); | |
run("Subtract Background...", "rolling=&bkgrd sliding"); | |
run("Mean...", "radius=&mean_rad"); | |
run("Enhance Contrast...", "saturated=&enh_contr normalize"); | |
run("Magenta Hot"); | |
originalnormalisiert=getTitle; | |
// manually select the areas of interest. More than one area with slit diaphragm pattern can be selected | |
// Each area is treated as a separate ROI (and the results for each ROI will be saved). | |
// In addition, the results for the different ROIs are also summed up. | |
setTool("freehand"); | |
roiManager("Show All without labels"); | |
waitForUser("selectROIs -- add with T");//add ROIs to the ROImanager | |
run("From ROI Manager"); //=add to overlay | |
roicount=roiManager("count"); | |
setBatchMode(true); | |
setBackgroundColor(0, 0, 0); | |
//Create a table for the results | |
Table.create("roiResults"); | |
//initialize variables for the summed area and sd-length over all ROIs | |
ROIsumArea = 0; | |
ROIsumLength = 0; | |
ROIsumLongestBranch = newArray; | |
ROIsumSecondLongestBranch = newArray; | |
// analyse each of the manually selected ROIs separately | |
for (i=0; i<roicount ;i++) { | |
selectImage(originalnormalisiert); | |
duplikattitel="ROI_"+i; | |
run("Duplicate...", "title=&duplikattitel"); | |
Overlay.remove; //duplicate image without overlay | |
// in duplicate image delete the area outside the ROI | |
roiManager("Select", i);//Roi Manager is still open. Select ROI number i | |
run("Clear Outside"); | |
// measure capillaryarea (= area of the currently selected ROI) | |
List.setMeasurements; | |
capillaryarea = List.getValue("Area"); | |
//print("capillaryarea: "+capillaryarea); | |
// Ridge detection: | |
// there are two modes of Ridge detection: with or without the "extend line" feature. In some areas one, in other areas the other mode | |
// produces the better output --> therefore both are used and merged (added) the the resulting binary images. | |
// Ridge detection outputs also length and so on, but because the images (with and w/o "extend line" feature) are added, none of the | |
// Ridge detection outputs is the correct one --> do a skeleton analysis on the final (added-up) binary file to get these values. | |
run("Ridge Detection", "line_width=&rlw high_contrast=&rhc low_contrast=&rlc minimum_line_length=&rminll maximum_line_length=&rmaxll correct_position make_binary method_for_overlap_resolution=NONE"); | |
rename("ohne");//=binary output image | |
selectWindow(duplikattitel); | |
run("Ridge Detection", "line_width=&rlw high_contrast=&rhc low_contrast=&rlc minimum_line_length=&rminll maximum_line_length=&rmaxll correct_position extend_line make_binary method_for_overlap_resolution=NONE"); | |
rename("mit"); | |
//merge(add) binary images from with and w/o "extend line" feature | |
imageCalculator("Add create", "ohne","mit");//create (new window) shouldn´t be necessary in theory, but somehow the behaviour was weired without - | |
//sometimes (not reproducibly?) images were missing. | |
rename("merge"); | |
selectWindow("mit"); | |
run("Close"); | |
selectWindow("ohne"); | |
run("Close"); | |
// doing dilate once on the binary closes some tiny gaps | |
selectWindow("merge"); | |
setOption("BlackBackground", false);//process>binary>options | |
run("Dilate"); | |
// skeletonize (the binary image) | |
run("Skeletonize (2D/3D)");//no new image | |
run("Create Selection"); | |
roiManager("Add"); // to be used later in the overlay. This is ROI number roicount | |
//run("Analyze Skeleton (2D/3D)", "prune=none calculate display"); | |
run("Analyze Skeleton (2D/3D)", "prune=none display"); //creates a Results table. And a new image "merge-labeled-skeletons" | |
// figure out which skeleton branch is the longest one (and second longest one) | |
// Macro provides this information, but this info was actually not used in the Butt et al. manuscript | |
// read all maximum branch lenghts from the Results table into an array | |
a=newArray(nResults); | |
for (j=0; j<nResults; j++){ | |
a[j]=getResult("Maximum Branch Length", j); | |
} | |
//Array.print(a); | |
ranks=Array.rankPositions(a);//the last element in the ranks array is the index of the longest branch in the array | |
maxBranchSkeleton = ranks[nResults-1]+1;// | |
secondmaxBranchSkeleton = ranks[nResults-2]+1;// | |
//print("max length has the skeleton branch number "+maxBranchSkeleton); | |
// add longest branch to ROI Manager | |
selectWindow("merge-labeled-skeletons"); | |
setThreshold(maxBranchSkeleton, maxBranchSkeleton); | |
run("Create Selection"); | |
roiManager("Add");//=roicount +1 | |
run("Select None"); | |
// add second longest branch to ROI Manager | |
selectWindow("merge-labeled-skeletons"); | |
setThreshold(secondmaxBranchSkeleton, secondmaxBranchSkeleton); | |
run("Create Selection"); | |
roiManager("Add");//=roicount+2 | |
run("Select None"); | |
//close no longer needed images | |
selectWindow("merge"); | |
run("Close"); | |
selectWindow("merge-labeled-skeletons"); | |
run("Close"); | |
// add skeleton, longest branches and area to overlay of contrast enhanced original image | |
selectWindow(originalnormalisiert); | |
//selectImage(normalizedID); | |
//Overlay.remove; | |
roiManager("Select", i);//area | |
roiManager("Set Color", "cyan"); | |
roiManager("Set Line Width", 2); | |
roiManager("Select", roicount);//skeleton | |
roiManager("Set Color", "yellow"); | |
roiManager("Set Line Width", 2); | |
roiManager("Select", roicount+1);//longest branch | |
roiManager("Set Color", "green"); | |
roiManager("Set Line Width", 2); | |
roiManager("Select", roicount+2);//second longest branch | |
roiManager("Set Color", "red"); | |
roiManager("Set Line Width", 2); | |
run("From ROI Manager");// to overlay | |
// delete the Skeleton ROIs from the ROIManager | |
// in case two ROIs were selected initally, the sceletons in the next ROIs will have the same indices in the ROI manager | |
roiManager("Select", newArray(roicount,roicount+1,roicount+2));//= complete skeleton, longest branch, second longest branch | |
roiManager("Delete"); | |
run("Select None"); | |
// write sum of all branch lengths into the results | |
// each slit diaphragm segment is a row in the skeleton results table | |
// the lenght of each slit diaphragm segment is calculated by multiplying the number of branches with the average branch lenght (both given in the table) | |
total=0; // variable for the sum of all lengths | |
for (k=0; k<nResults; k++) { | |
branches=getResult("# Branches", k); | |
avg=getResult("Average Branch Length", k); | |
total=total+branches*avg; | |
setResult("total branch length", k, branches*avg); | |
} //k | |
// add at the end of the results table | |
setResult("sum branch length", k, total); | |
setResult("ROI area", k, capillaryarea); | |
setResult("slit length/area", k+1, total/capillaryarea); | |
// write to log | |
print("\n---ROI_"+i+" -------------------------------------------------------------------------------------------"); | |
print("ROI_"+i+": \tCapillary area: \t"+ capillaryarea +" µm²"); | |
print("ROI_"+i+": Slit length: "+ total +" µm"); | |
print("ROI_"+i+": Slit length/area: "+ total/capillaryarea); | |
print("\nROI_"+i+": Number of slit fragments: "+ nResults-2); | |
print("ROI_"+i+": Slit fragments/area: "+ (nResults-2)/capillaryarea); | |
print("\nROI_"+i+": max length has skeleton branch "+maxBranchSkeleton+": "+getResult("Maximum Branch Length", maxBranchSkeleton-1)+" µm");//-1 weil die Branchnummerierung mit 1 anfängt aber die Row-Zählung mit 0 | |
print("ROI_"+i+": second max length has skeleton branch "+secondmaxBranchSkeleton+": "+getResult("Maximum Branch Length", secondmaxBranchSkeleton-1)+" µm"); | |
//write results for the current ROI into the table "roiResults" that was generated (empty) above | |
//its kind of a duplication of the values written to the log window, but a table is easier to | |
//merge (in the end, all tables for all images analysed will be merged [outside Fiji/imageJ]) | |
//contents of the newImageAndResultsName sting array: | |
// 0 = animal | |
// 1 = age | |
// 2 = genotype | |
// 3 = image number | |
// 4 = new name for resultsdir and results files | |
Table.update("roiResults"); // define roiResults table as the "foremost" table | |
Table.set("Original image name and path", i, img); // - Assigns a numeric or string value to the cell at the specified column and row. | |
Table.set("modified image name", i, newImageAndResultsName[4]); | |
Table.set("animal", i, newImageAndResultsName[0]); | |
Table.set("genotype", i, newImageAndResultsName[2]); | |
Table.set("age", i, newImageAndResultsName[1]); | |
Table.set("ROI", i, "ROI_"+i); | |
Table.set("ROI area[µm²]", i, capillaryarea); | |
Table.set("Length in this area", i, total); | |
Table.set("slit length/area", i, total/capillaryarea); | |
Table.set("Number of slit fragments",i , nResults-2); | |
Table.set("Slit fragments/area",i , (nResults-2)/capillaryarea); | |
Table.set("longest branch", i, getResult("Maximum Branch Length", maxBranchSkeleton-1)); | |
Table.set("second longest branch", i, getResult("Maximum Branch Length", secondmaxBranchSkeleton-1)); | |
//add up the values for the different ROIs | |
ROIsumArea = ROIsumArea + capillaryarea; | |
ROIsumLength = ROIsumLength + total; | |
ROIsumSlitfragments = ROIsumSlitfragments + nResults-2; | |
ROIsumLongestBranch = Array.concat(ROIsumLongestBranch, getResult("Maximum Branch Length", maxBranchSkeleton-1)); | |
ROIsumSecondLongestBranch = Array.concat(ROIsumSecondLongestBranch, getResult("Maximum Branch Length", secondmaxBranchSkeleton-1)); | |
//save (skeleton analysis) results table for this ROI | |
selectWindow("Results"); | |
saveAs("results", newDir+File.separator+newImageAndResultsName[4]+"_Res_ROI_"+i+".xls"); | |
selectWindow("Results"); | |
run("Close"); | |
} //next i=next ROI | |
// add the results for the added ROIs to the roiResults table | |
Table.update("roiResults"); // define roiResults table as the "foremost" table | |
Table.set("Original image name and path", i, img); // - Assigns a numeric or string value to the cell at the specified column and row. | |
Table.set("modified image name", i, newImageAndResultsName[4]); | |
Table.set("animal", i, newImageAndResultsName[0]); | |
Table.set("genotype", i, newImageAndResultsName[2]); | |
Table.set("age", i, newImageAndResultsName[1]); | |
Table.set("ROI", i, "ROI_total"); | |
Table.set("ROI area[µm²]", i, ROIsumArea); | |
Table.set("Length in this area", i, ROIsumLength); | |
Table.set("slit length/area", i, ROIsumLength/ROIsumArea); | |
Table.set("Number of slit fragments",i , ROIsumSlitfragments); | |
Table.set("Slit fragments/area",i , ROIsumSlitfragments/ROIsumArea); | |
ROIsumLongestBranch = Array.sort(ROIsumLongestBranch); | |
Table.set("longest branch", i, ROIsumLongestBranch[ROIsumLongestBranch.length-1]); | |
ROIsumSecondLongestBranch = Array.sort(ROIsumSecondLongestBranch); | |
Table.set("second longest branch", i, ROIsumSecondLongestBranch[ROIsumSecondLongestBranch.length-1]); | |
Table.update("roiResults"); | |
Table.save(newDir+File.separator+newImageAndResultsName[4]+".tsv"); | |
run("Close"); | |
// write settings to log and save log | |
print ("\n\n=========================================================================================================\nPreprocessing settings:\n" | |
+"\tsubtract background rolling ball (sliding): "+bkgrd+"\n" | |
+"\tmean radius: "+mean_rad+"\n" | |
+"\tenhance contrast (normalize) saturated: "+enh_contr+"\n"); | |
print ("\n\nRidge detection plugin settings:\n" | |
+"\t rlw //Ridge line_width: "+rlw+"\n" | |
+"\t rhc //Ridge high_contrast: "+rhc+"\n" | |
+"\t rlc //Ridge low_contrast: "+rlc+"\n" | |
+"\t rminll //Ridge minimum_line_length: "+rminll+"\n" | |
+"\t rmaxll //Rigde maximum_line_length: "+rmaxll+"\n"); | |
selectWindow("Log"); | |
saveAs("txt", newDir+File.separator+newImageAndResultsName[4]+"_summary"); | |
run("Close"); | |
//add ROI names to overlay | |
selectImage(originalID); | |
setFont("SanSerif", textsizeoverlay, "antialiased"); | |
setColor("white"); | |
for (m=0; m<roicount; m++) { | |
roiManager("select", m); | |
Roi.getBounds(x, y, width, height); | |
Overlay.drawString("ROI_"+m, x+10,y+textsizeoverlay+10); | |
} | |
//save image with overlay | |
saveAs("TIFF", newDir+File.separator+newImageAndResultsName[4]+"_overview");// | |
run("Flatten"); | |
saveAs("PNG", newDir+File.separator+newImageAndResultsName[4]+"_overview_flat");// | |
setBatchMode(false); | |
run("Close All"); | |
exit("results have been saved to this folder: \n"+newImageAndResultsName[4]); | |
//################################################################################################################# | |
// FUNCTIONS | |
//################################################################################################################# | |
//----------------------------------------------------------------------------------------------------------------- | |
function instructions() { | |
/* nur ein Platzhalter | |
showMessage("How to ....", "<html>" | |
+"<h3>Instructions</h3>" | |
+"<ol>" | |
+"<li>choose the directory with the image files (macro will ask for this)" | |
+"<li>the first image will open automatically" | |
+"</ol>" | |
+"The resulting files are saved to a directory on the same level as the source directory.<br>" | |
+"Name of this folder: ´source<b>_cleaned</b>´"); | |
*/ | |
} | |
//----------------------------------------------------------------------------------------------------------------- | |
function deduceResultDirandImagename (imagepath, imagename) { | |
// collect some information about the image | |
// for the analysis done for the paper there were animals of 5 age groups and 3 possible genotypes --> chose from dialog | |
// for each age/genotype combination several animals were analysed. The animal identifier "prefix_eartag" has to be entered manually | |
// for each animal up to seven images were analysed. | |
ages = newArray("00w", "02w", "04w", "08w", "20w"); | |
genotypes = newArray("mut", "ctrl", "special"); | |
imagenumbers = newArray("first time", "_img1", "_img2", "_img3", "_img4", "_img5", "_img6", "_img7"); | |
Dialog.create("Animal info"); | |
Dialog.addMessage("image: \n"+imagepath+File.separator+imagename+"\n"); | |
Dialog.addString("enter prefix_eartag: "," "); | |
Dialog.addChoice("age", ages); | |
Dialog.addChoice("genotype", genotypes); | |
Dialog.addMessage("\nFrom this animal there can be several images (up to 7).\n" | |
+"In case this is the first analysis (default value: _first time_),\n" | |
+"the macro realises which image this is and gives the appropriate\n" | |
+"name to the results files.\n" | |
+"However, if this is a repetition of an analysis and old results should\n" | |
+"be overwritten, you have to tell the macro which images this is, so that\n" | |
+"the macro knows which results files to overwrite"); | |
Dialog.addChoice("image number", imagenumbers, "first time"); | |
Dialog.show; | |
animal = Dialog.getString; | |
age = Dialog.getChoice; | |
genotype = Dialog.getChoice; | |
imagenumber = Dialog.getChoice; | |
// all strings are also needed outside the function --> write strings into an array and return the array | |
stringarray = newArray(animal, age, genotype, "platzhalter imagenummer", "platzhalter resultdir"); | |
// create folders for the overview and snapshots | |
// imageparent = File.getParent(imagename); | |
// imgname = File.getName(img); | |
// there can be up to 7 images of the same animal ... this can not be deduced from the filename. | |
if (imagenumber == "first time") { | |
resultdir = imagepath+File.separator+age+"_"+genotype+"_"+animal+"_img1"; | |
if (File.exists(resultdir)==false) { | |
stringarray[3] = "_img1"; | |
stringarray[4] = age+"_"+genotype+"_"+animal+"_img1"; | |
return stringarray; | |
} | |
else { | |
resultdir = imagepath+File.separator+age+"_"+genotype+"_"+animal+"_img2"; | |
if (File.exists(resultdir)==false) { | |
stringarray[3] = "_img2"; | |
stringarray[4] = age+"_"+genotype+"_"+animal+"_img2"; | |
return stringarray; | |
} | |
else { | |
resultdir = imagepath+File.separator+age+"_"+genotype+"_"+animal+"_img3"; | |
if (File.exists(resultdir)==false) { | |
stringarray[3] = "_img3"; | |
stringarray[4] = age+"_"+genotype+"_"+animal+"_img3"; | |
return stringarray; | |
} | |
else { | |
resultdir = imagepath+File.separator+age+"_"+genotype+"_"+animal+"_img4"; | |
if (File.exists(resultdir)==false) { | |
stringarray[3] = "_img4"; | |
stringarray[4] = age+"_"+genotype+"_"+animal+"_img4"; | |
return stringarray; | |
} | |
else { | |
resultdir = imagepath+File.separator+age+"_"+genotype+"_"+animal+"_img5"; | |
if (File.exists(resultdir)==false) { | |
stringarray[3] = "_img5"; | |
stringarray[4] = age+"_"+genotype+"_"+animal+"_img5"; | |
return stringarray; | |
} | |
else { | |
resultdir = imagepath+File.separator+age+"_"+genotype+"_"+animal+"_img6"; | |
if (File.exists(resultdir)==false) { | |
stringarray[3] = "_img6"; | |
stringarray[4] = age+"_"+genotype+"_"+animal+"_img6"; | |
return stringarray; | |
} | |
else { | |
resultdir = imagepath+File.separator+age+"_"+genotype+"_"+animal+"_img7"; | |
if (File.exists(resultdir)==false) { | |
stringarray[3] = "_img7"; | |
stringarray[4] = age+"_"+genotype+"_"+animal+"_img7"; | |
return stringarray; | |
} | |
else exit("7 image resultsdirectories for this animal already exist. \nBut you did not enter an image number.\n Therefore I don´t know which results to overwrite.\n\nI´ll quit"); | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
else { | |
resultdir = imagepath+File.separator+age+"_"+genotype+"_"+animal+imagenumber; | |
stringarray[3] = imagenumber; | |
stringarray[4] = age+"_"+genotype+"_"+animal+imagenumber; | |
return stringarray; | |
} | |
} //function |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
maschenweite = 0.75;//uncomment the next line and add comment to this line to activate the dialog to change maschenweite (=grid with) | |
/* | |
# @BigDecimal(label="Maschenweite [µm]",value=0.75) maschenweite | |
*/ | |
/* | |
* Macro written for the Butt et al., 2020 paper to get the slit diaphragm length per area | |
* from nephrin-stained kidney STED images. | |
* | |
* Fiji/ImageJ version used to analyse the data from the paper: 2.0.0-rc-67/1.52c | |
* | |
* This macros provides an unbiased approach for the evaluation of slit diagphragm coverage. | |
* The slit diaphragm pattern (slit lines) are overlayed with a grid pattern, and the distances between | |
* crossing points line/pattern are measured. | |
* The grid is only overlayed in regions where there is slit pattern, i.e. | |
* the macro can deal with concave slit-pattern areas. | |
* The distance between the grid lines can be adjusted ("Maschenweite"). | |
* | |
* The input images can contain one or several ROIs with slit pattern, the | |
* analysis is done for all ROIs in one image (automatically). | |
* | |
* The macro asks for an input image. This is one of the images generated by the | |
* slit_measurement_v0X.ijm macro. | |
* This tif file has the slit pattern in the overlay. | |
* | |
* Inital processing of the input image (binarization of the pattern and extracting the different | |
* regions (if there are different regions) is partially copied from fp_morphology_vX.ijm macro. | |
* | |
* Input: | |
* - a folder with tif images that have the (skeletonized) slit diaphragm pattern in the overlay | |
* | |
* Output (for each image): | |
* - an image of the slit pattern and the grid in the image overlay | |
* - xls file with the distances between the crossing points listed for all ROIs in the image + area of the slit-pattern ROI(s) and crossingpoints/µm². | |
* - summary file (xls) with some statistics. | |
* | |
* | |
* Martin Höhne, Nephrolab Cologne, 2018 | |
* | |
* | |
*/ | |
run("Close All"); | |
run("Input/Output...", "jpeg=80 gif=-1 file=.txt use_file copy_column save_column");//these settings make sure that the Row_number from results tables are not saved (setting is reverted at the end of the macro) | |
// select folder containing the tifs with slit pattern in the overlay | |
dir1 = getDirectory("Choose Source Directory "); | |
imglist = getFileList(dir1); | |
dir1parent = File.getParent(dir1); | |
dir1name = File.getName(dir1); | |
//create resultsfolders | |
resultdir = dir1parent+File.separator+dir1name+"_gridresults"; | |
if (File.exists(resultdir)==false) { | |
File.makeDirectory(resultdir); // new directory for results | |
} | |
else { | |
yesno= getBoolean("resultdirectory already exists. \nExisting results will be overwritten.\n\nContinue?"); | |
if (yesno==false) { | |
run("Close All"); | |
exit("Na dann halt nicht"); | |
} | |
} | |
setBatchMode(true); | |
//open image and create resultdirectory | |
for (n=0; n<imglist.length; n++) { | |
showProgress(n+1, imglist.length); | |
img=dir1+imglist[n]; | |
open(img); | |
originalID = getImageID; | |
// needed below to save the image | |
imgcore = File.nameWithoutExtension; | |
imgparent = File.getParent(img); | |
imgname = File.getName(img); | |
// Make sure the ROI Manager is closed. It runs faster | |
// and more reliably in batch mode if it is closed. | |
if (isOpen("ROI Manager")) { | |
selectWindow("ROI Manager"); | |
run("Close"); | |
} | |
run("To ROI Manager");//transfer slit diaphragm pattern from overlay | |
run("Remove Overlay"); | |
getVoxelSize(width, height, depth, unit); | |
//grid with in pixel units | |
maschenweitepx = round(maschenweite / width); | |
img_w = getWidth; //Returns the width in pixels of the current image. | |
img_h = getHeight; | |
// There can be several areas with slit diaphragm pattern in the image (However, it is unkown how many). | |
// The name of each of these areas was an overlay element and is now a ROI itself. | |
// The name ROIs are at the end of the ROI List | |
// Theses "name" ROIs have to be deleted. They can be recognized by having only ONE "-" in the ROI name | |
killArray1 = newArray; | |
for (i=0; i<roiManager("count"); i++) { | |
roiName = call("ij.plugin.frame.RoiManager.getName", i); | |
first = indexOf(roiName, "-");// first occurence of - | |
last = lastIndexOf(roiName, "-"); // last occurence of - | |
if (first == last) { | |
killArray1 = Array.concat(killArray1, i); | |
} | |
} | |
roiManager("select", killArray1); | |
roiManager("delete"); | |
// Each area (there are n possible areas) has an outline ROI that has to be deleted. | |
// In theory, these ROIs should be the first n ROIs in the list. But for reasons unclear to me, these outline ROIs occur more than once in the ROI list | |
// i.e. in addition to the fist n ROIs | |
// The easiest way to identify these outline ROIs is by size (area > 20) | |
killArray2 = newArray; | |
for (i=0; i<roiManager("count"); i++) { | |
roiManager("select", i); | |
List.setMeasurements; | |
area = List.getValue("Area"); | |
if (area>20) { | |
killArray2 = Array.concat(killArray2, i); | |
} | |
} | |
roiindex = Array.getSequence(killArray1.length);// Returns an array containing the numeric sequence 0,1,2...n-1. Requires 1.49u.; | |
// killArray1.lenght is the number of areas (that were initially outlined manually (=n)) | |
// The outline-ROIs of the selected areas (i.e. the first n ROIs) should be selected and saved | |
// save the outline ROIs as a ROIset (they will be reloaded later in the macro) | |
roiManager("select", roiindex); | |
roiFile = resultdir+File.separator+imgcore+"_rois.zip"; | |
roiManager("save selected", roiFile); | |
// delete the outline ROIs | |
roiManager("select", killArray2); | |
roiManager("delete"); | |
// the ROI Manager only contains the slit diaphragm pattern --> binarize | |
newImage("temp", "8-bit black", img_w, img_h, 1); | |
roibildID = getImageID; | |
roiManager("Show All without labels"); | |
roiManager("deselect");// = select all | |
run("Flatten"); | |
roibildflatID = getImageID; | |
selectImage(roibildID); | |
run("Close"); | |
selectImage(roibildflatID); | |
run("8-bit"); | |
setThreshold(2, 255); | |
run("Convert to Mask"); | |
setVoxelSize(width, height, depth, unit); | |
// Overlay with the gris and calculate the distances between crossing points of slit pattern and grid pattern | |
// for each of the grid lines | |
List.clear; | |
roiManager("Show All without labels"); | |
alledeltas = newArray();// this array will contain the distances | |
crossingpoints = 0; | |
roiManager("reset"); | |
// reload the outline ROIs (they had to be removed in order to flatten the slit pattern without the outline | |
roiManager("Open", roiFile);// there were originally n areas outlined --> ROI Manager contains n ROIs | |
// if n > 1 then merge the ROIs into 1 ROI | |
if (roiManager("count")>1) { | |
a = Array.getSequence(roiManager("count"));// - Returns an array containing the numeric sequence 0,1,2...n-1. Requires 1.49u. | |
roiManager("select", a); | |
roiManager("combine"); | |
roiManager("add"); | |
roiManager("select", a); | |
roiManager("delete"); | |
} | |
roiManager("select", 0); | |
List.setMeasurements; | |
ROIarea = List.getValue("Area"); | |
// add the grid and determine the distance between crossing points of grid and slit pattern | |
alledeltas = drawgridandreturndeltas(roibildflatID, maschenweitepx);//own function drawgridandreturndeltas. Returns an array containing all deltas | |
// the array returned (alledeltas) contains as the last element the total number | |
// of crossing points. Extract this number and shorten the array by 1 element | |
crossingpoints = crossingpoints + alledeltas[alledeltas.length-1];//letzes Element aus returnname zu crossingpoints addieren | |
alledeltas = Array.trim(alledeltas, alledeltas.length-1); | |
// add the grid to the overlay and save the image | |
selectImage(roibildflatID); | |
run("From ROI Manager");//add grid und ROIareas to overlay | |
saveAs("TIFF", resultdir+File.separator+imgcore+"_grid");// | |
// write results table and save | |
for (j=0; j<alledeltas.length; j++) { | |
setResult("Image", j, imgname); | |
alledeltas[j]=alledeltas[j]*width; //convert from pixel to micron. | |
setResult("all deltas [µm]", j, alledeltas[j]); | |
} | |
setResult("Area all ROIs [µm²]", 0, ROIarea); | |
setResult("crossingpoints/µm²", 0, crossingpoints/ROIarea); | |
setResult("Maschenweite [µm]",0 , maschenweite); | |
updateResults; | |
selectWindow("Results"); | |
saveAs("results", resultdir+File.separator+imgcore+"_res.xls"); | |
run("Close"); | |
// write summary to log and save | |
Array.getStatistics(alledeltas, min, max, mean, stdDev); | |
print("\\Clear"); | |
print(imgname); | |
print("min\t"+min); | |
print("max\t"+max); | |
print("mean\t"+mean); | |
print("stdDev\t"+stdDev); | |
print("\nCrossingpoints/[µm²]\t"+crossingpoints/ROIarea); | |
print("Number of distances measured\t"+alledeltas.length);//neu in 2f | |
print("Number of crossing points\t "+crossingpoints); | |
print("Maschenweite [µm]\t "+maschenweite); | |
selectWindow("Log"); | |
saveAs("text", resultdir+File.separator+imgcore+"_stats.xls"); | |
run("Close All");//closes all open images | |
run("Clear Results"); | |
run("Close");//Closes log | |
} //n (image list) | |
setBatchMode(false); | |
run("Input/Output...", "jpeg=80 gif=-1 file=.txt use_file copy_column copy_row save_column save_row");//revert the "Do not save row number from tables" setting | |
exit("Done. Results have been written to "+resultdir); | |
//+++++++++++++++++++++++++++++++++++++++ F U N C T I O N S +++++++++++++++++++++++++++++++++ | |
function drawgridandreturndeltas(currentimgID, gridweite) { | |
/* | |
* this function adds a grid on top of the slit pattern and | |
* determines the distances of the crossing points of the slit pattern | |
* with the individul grid lines | |
* | |
*/ | |
tolerance = 150; //for max detectcion in the profile array | |
delta = newArray(); | |
maxcount = 0; | |
selectImage(currentimgID); | |
w = getWidth;//aktive image in px | |
h = getHeight; | |
roiManager("Show All"); | |
// "draw" grid lines | |
// the grid lines are added with the own function linienziehen. | |
// This function adds a ROI to the ROI Manager for each grid segment that lies within the slit diaphragm area. | |
linienziehen(w, h , gridweite, "vertical");// w, h = image width and heigth (in px), gridweite = gridwidth (in px), direction | |
linienziehen(w, h , gridweite, "horizontal"); | |
// select each grid segment separately and detemine the distance of crossing points of the slit pattern with this grid segment (using getProfile) | |
for (j=1; j<roiManager("count"); j++) { | |
roiManager("select", j); // select a grid segment | |
profile = getProfile; | |
// since the slit pattern is binary it is well possible to look at the maxima of the profile to determine the crossing points | |
maxLocs= Array.findMaxima(profile, tolerance);//maxima of the profile in decreasing strenght | |
Array.sort(maxLocs);// Maxima sorted along the profile line | |
// determine the distance between the maximas and write the distances into the delta array | |
for (k=1; k<maxLocs.length; k++) { | |
delta = Array.concat(delta, (maxLocs[k]-maxLocs[k-1])); | |
} | |
maxcount = maxcount + maxLocs.length; // count the maxima=crossings of the pattern with the grid line | |
} | |
// add number of crossings as the last value to the delta array and return this array to the macro | |
delta = Array.concat(delta, maxcount); | |
return delta; | |
} | |
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |
function linienziehen(w, h , gridweite, direction) { | |
/* | |
* -This function draws the grid pattern in areas that are WITHIN the originally selected areas | |
* -each line segment of the grid is saved as an individual ROI to the ROI Manager. Since this | |
* ROI can also be accessed outside this function there is no "return" line in this function. | |
* -input to the function is: | |
* - w: image width (in px) | |
* - h: image heigth (in px) | |
* - gridwidth (=gridweite) (in px) | |
* - direction of the line | |
*/ | |
// determine direction of the line to be generated | |
if (direction=="vertical") { | |
wh = w; | |
} | |
if (direction=="horizontal") { | |
wh = h; | |
} | |
for (xy=20+gridweite; xy<wh; xy=xy+gridweite) { | |
// "draw line" | |
// Actually it is not a line but a very narrow rectangle (1px width) because only for areas (e.g. rectangles) | |
// the overlap with a given ROI can be determined. For lines this is not possible (roiManager("and") command). | |
if (direction=="vertical") makeRectangle(xy, 0, 1, wh); | |
if (direction=="horizontal") makeRectangle(0, xy, wh, 1); | |
roiManager("Add"); | |
// determine if the line has an overlap with the slit diapgragm area | |
currentline = roiManager("count")-1;//index of the last added ROI | |
roiManager("select",newArray(0,currentline)); //ROI 0 is the area, ROI currentline is the current line (rectangle) | |
roiManager("and"); | |
if (selectionType==-1) { | |
//("No selection"); No overlap between line and area --> delete the line | |
roiManager("deselect"); // deselect all | |
roiManager("select", currentline); | |
roiManager("delete"); | |
} | |
else { | |
// line has an overlap with the area | |
roiManager("Add");// index=currentline+1 = composite ROI = overlap line and area | |
roiManager("select", currentline+1); | |
roiManager("Split"); // if the area is concave or consists of separate parts, it might be that | |
// there are two or more regions of the line that overlap with the area | |
// the split command separates these different parts and adds each part | |
// as a new ROI to the ROI Manager. | |
// If there is only 1 part overlapping a new ROI (identical to the combined | |
// ROI is added anyways. | |
// delete the original line (rectangle) and the combined ROI | |
// only the area (Roi 0) and the splitted lines (i.e. rectangles) are left in the ROI Manager | |
roiManager("select", newArray(currentline, currentline+1)); | |
roiManager("delete"); | |
// determine the number of newly added ROIs | |
AnzahlNeueROIs = roiManager("count") - currentline; | |
// the newly added ROIs are still very narrow rectangles (more exactely: polyline areas). They have to be converted into real lines | |
// in order to generate profile plots. | |
// Conversion is done by determining the corners of the polyline areas and drawing lines using these coordinates. | |
killArray = newArray(AnzahlNeueROIs); // preparea an array for the indices of the polyline areas to delete them in the next step | |
for (i=0; i<AnzahlNeueROIs; i++) { | |
killArray[i] = currentline + i; | |
roiManager("Select", currentline + i); | |
getSelectionCoordinates(xcoord, ycoord);// Returns two arrays containing the X and Y coordinates, in pixels, | |
// of the points that define the current selection. | |
// draw a line at one of the borders of the selection | |
Array.getStatistics(xcoord, xmin, xmax, mean, stdDev); | |
Array.getStatistics(ycoord, ymin, ymax, mean, stdDev); | |
if (direction=="vertical") makeLine(xmin, ymin, xmin, ymax);// vertical lines, therefore xmin at both ends | |
if (direction=="horizontal") makeLine(xmin, ymin, xmax, ymin);// horizontal lines, therefore ymin at both ends | |
roiManager("Add"); // add the (real) line to the ROI manager | |
} | |
//delete the PolyLineArea-ROIs | |
roiManager("select", killArray); | |
roiManager("delete"); | |
} | |
run("Select None"); | |
} // for xy | |
} // function |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Macro written for the Butt et al., 2020 paper to get various morphological parameters | |
* for foot processes from nephrin-stained kidney STED images. | |
* | |
* The input for this macro is a tiff file with the slit diaphragm pattern in the overlay. | |
* This tiff if the output of another macro (slit_measurement_v01xxx.ijm). | |
* | |
* Fiji/ImageJ version used to analyse the data from the paper: 2.0.0-rc-67/1.52c | |
* | |
* The analysis is semi-automatic (at most). | |
* - The user has to manually close the foot processes | |
* - the macro detects all closed areas | |
* - the user has to exclude auto-detected closed areas that are NOT foot processes | |
* - after exclusion, for the remaining foot processes various morphological parameters are measured | |
* - results are saved automatically | |
* | |
* | |
* Martin Höhne, Nephrolab Cologne, April 2018 | |
* | |
*/ | |
run("Close All"); | |
//open image and create resultsdirectory | |
img = File.openDialog("Select the input file"); | |
open(img); | |
originalID = getImageID; | |
imgcore = File.nameWithoutExtension; | |
// create folders for the overview and snapshots | |
imgparent = File.getParent(img); | |
imgname = File.getName(img); | |
resultdir = imgparent+File.separator+imgname+"-res"; | |
if (File.exists(resultdir)==false) { | |
File.makeDirectory(resultdir); // new directory for results | |
} | |
else { | |
yesno= getBoolean("Resultsdirectory already exists. \nExisting results will be overwritten.\n\nContinue?"); | |
if (yesno==false) { | |
run("Close All"); | |
exit("Na dann halt nicht"); | |
} | |
} | |
run("To ROI Manager");// transfer the overlay to the ROI Manager | |
run("Remove Overlay"); | |
getVoxelSize(width, height, depth, unit); | |
// There can be several areas with slit diaphragm pattern in the image (However, it is unkown how many). | |
// The name of each of these areas was an overlay element and is now a ROI itself. | |
// The name ROIs are at the end of the ROI List | |
// Theses "name" ROIs have to be deleted. They can be recognized by having only ONE "-" in the ROI name | |
killArray1 = newArray; | |
keepArray = newArray; | |
for (i=0; i<roiManager("count"); i++) { | |
roiName = call("ij.plugin.frame.RoiManager.getName", i); | |
first = indexOf(roiName, "-");// first occurence of - | |
last = lastIndexOf(roiName, "-"); // last occurence of - | |
if (first == last) { | |
killArray1 = Array.concat(killArray1, i); | |
} | |
else keepArray = Array.concat(keepArray, i); //ROIs to keep | |
} | |
roiManager("select", killArray1); | |
roiManager("delete"); | |
roiManager("select", keepArray); | |
roiManager("Set Color", "yellow"); | |
// Each area (there are n possible areas) has an outline ROI that has to be deleted. | |
// In theory, these ROIs should be the first n ROIs in the list. But for reasons unclear to me, these outline ROIs occur more than once in the ROI list | |
// i.e. in addition to the fist n ROIs | |
// The easiest way to identify these outline ROIs is by size (area > 10) | |
killArray2 = newArray; | |
for (i=0; i<roiManager("count"); i++) { | |
roiManager("select", i); | |
List.setMeasurements; | |
area = List.getValue("Area"); | |
if (area>10) killArray2 = Array.concat(killArray2, i); | |
} | |
roiManager("select", killArray2); | |
roiManager("delete"); | |
// User has to close the foot processes manually | |
selectImage(originalID); | |
roiManager("Show All without labels"); | |
run("Colors...", "foreground=yellow background=white selection=green"); | |
//run("Set... ", "zoom=100"); does not work. Zooming with complex ROIs is very SLOOOOOWWWWWWWWW | |
run("Magenta Hot"); | |
setTool("freeline"); | |
waitForUser("draw lines to close foot processes, add with T to ROI manager"); | |
// the slit diaphragm pattern has to be binarized in order to detect closed foot processes with the Analyse particels function. | |
// create a new black image, flatten the ROIs into this image --> binarize | |
newImage("Binary", "8-bit black", 2048, 2048, 1); | |
setVoxelSize(width, height, depth, unit); | |
roiManager("Show All"); | |
run("Flatten"); | |
rename(imgcore+"_binary"); | |
buntroi = getImageID; // ROIs will be dispayed in color in this image | |
run("8-bit"); | |
setThreshold(2, 255); | |
run("Convert to Mask"); | |
run("Dilate"); | |
run("Invert"); | |
roiManager("reset"); | |
run("Analyze Particles...", "size=0.01-1.50 exclude add"); | |
// assign various (random) colors to the ROIs (simply to be able to differentiate them more easily from each other) | |
for ( i=0; i<roiManager("count"); i++ ) { | |
roiManager("Select", i); | |
Roi.setFillColor(255*random, 255*random, 255*random);//Sets the selection fill color, where 'red', 'green' and 'blue' are integers | |
} | |
// Allow the user to manually exclude certain areas that were detected by the Analyze particles algorithm. | |
// This part is taken from another macro that was dealing whit cilia. Therefore the variables have often | |
// "clia" in the name. Read "foot process area" instead of "cilia" and everything fits | |
// show image with foot process area ROIs and select the ones that should be deleted | |
run("Select None"); | |
//run("Set... ", "zoom=200"); | |
roiManager("Show All with labels"); | |
//run("ROI Manager..."); | |
//waitForUser("Use -Show All- in the ROI Manager to toggle ROI display\nAfter clicking OK you can select the ones that should be deleted. "); | |
do { | |
// setBatchMode("Show"); | |
exclude = getBoolean("Exclude one (more) ROIs from the analysis?"); | |
if (exclude) { | |
cilianumber=roiManager("Count"); | |
//show panel with checkboxes. One checkbox for each cilium. | |
columns = 3;//checkbox panel with 3 columns | |
rows = -floor(-cilianumber/columns);//-floor(-x) = ceiling(x) | |
n = rows*columns; | |
labels = newArray(cilianumber);//label for checkbox | |
defaults = newArray(cilianumber);//default for checkbox | |
for (i=0; i<cilianumber; i++) { | |
labels[i] = d2s(i+1,0);//d2s Converts the number n into a string using the specified number of decimal places. | |
//simply cosmetic reason: 1 looks better that 1.0 | |
defaults[i] = false;// 0 = not checked as default for all checkboxes | |
} | |
Dialog.create("Checkbox Group"); | |
//Dialog.addMessage("If no ROIs are dispayed unselect and\n reselect -Show All- in the ROI Manager\ncheck the ones to be deleted"); | |
Dialog.addMessage("Check the ones that should be deleted"); | |
Dialog.addCheckboxGroup(rows,columns,labels,defaults); | |
Dialog.show(); | |
//fill array with the ROIs that should be deleted | |
loesch=newArray(); | |
for (i=0; i<cilianumber; i++){ ////////////////////////////////////////// | |
if (Dialog.getCheckbox()==1){ | |
loesch = Array.concat(loesch, i); | |
} | |
} | |
//delete (start at the end of the list) | |
if (loesch.length!=0) { | |
for (i=loesch.length-1; i>=0; i--){ | |
roiManager("Select", loesch[i]); | |
roiManager("Delete"); | |
} | |
} | |
// rename remaining ROIs from 1 to ... | |
if (roiManager("Count")>0) { | |
for (i=0; i<roiManager("Count"); i++) { | |
roiManager("Select",i); | |
roiManager("Rename",i+1); | |
} | |
roiManager("Update"); | |
} | |
run("Select None"); | |
//if (roiManager("Count")>0) waitForUser("Use -Show All- in the ROI Manager to toggle ROI display\nAfter clicking OK you can select the ones that should be deleted. "); | |
} | |
if (roiManager("Count")==0) { | |
exclude = false; | |
run("Clear Results"); | |
} | |
} while (exclude) // loop until exclude is false | |
if (roiManager("Count")>0) roiManager("Show All with labels");//in case all ROIs are deleted because they are not cilia | |
// end of the cilia macro part ######################################################################################################################### | |
// only ROIs that represent real foot processes are left. | |
// Measure foot processes | |
run("Set Measurements...", "area perimeter fit shape feret's display redirect=None decimal=3"); | |
roiManager("Deselect"); | |
roiManager("Measure"); | |
// Save results | |
selectWindow("Results"); | |
saveAs("results", resultdir+File.separator+imgcore+"_Results_FP_analysis.xls"); | |
run("Close"); | |
selectImage(buntroi); | |
run("From ROI Manager");// to overlay | |
saveAs("TIFF", resultdir+File.separator+imgcore+"_bin");// | |
run("Flatten"); | |
saveAs("PNG", resultdir+File.separator+imgcore+"_bin_flat");// | |
selectImage(originalID); | |
run("From ROI Manager");// to overlay | |
saveAs("TIFF", resultdir+File.separator+imgcore+"_fp_overlay");// | |
run("Flatten"); | |
saveAs("PNG", resultdir+File.separator+imgcore+"_fp_overlay");// | |
run("Close All"); | |
exit ("Ende"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment