Skip to content

Instantly share code, notes, and snippets.

@github-martin
Last active May 14, 2020 10:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save github-martin/e699d18ae6cfa5b1a6aace15d3c3544c to your computer and use it in GitHub Desktop.
Save github-martin/e699d18ae6cfa5b1a6aace15d3c3544c to your computer and use it in GitHub Desktop.
macro collection Butt et al., Nature metabolism 2020
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
/*
* 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
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
/*
* 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