Last active
August 8, 2021 21:35
-
-
Save DSGlab/1b3a226a7af884efd9356ea2d6a02bd4 to your computer and use it in GitHub Desktop.
This script automates the Plant Immunity and Disease Image-based Quantification (PIDIQ) process.
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
////////////////////////////////////////////////////////// | |
// Peggy Muddles | |
// | |
// March 15, 2016 | |
// Revised March 28, 2017 with thanks to Brandon Hurr | |
// | |
// Fully automatic macro that assesses yellowing in arabidopsis | |
// Input | |
// - Folder of plant tray images | |
// - background is soil | |
// Output | |
// - False color image showing isolated plants and yellowed area | |
// - Results table of plant and yellowed area measurements | |
// | |
// Release v0.0.1 | |
// | |
// v0.0.1 Initial Recorded Release (borrowed codebase from Brandon Hurr cucumber macro) | |
// | |
// Needs | |
// - ? | |
////////////////////////////////////////////////////////// | |
//////////////////////////////// | |
// global variables | |
var orig = 0; //stores name of picture | |
// for getTimeString() function | |
var TimeString = 0; //Date and Time for macro run | |
// for createTable() function | |
var title1 = 0; //Name of Results Table | |
var f=0; //Result Table | |
// for getDateTime() function | |
var DateTime = 0; //Date and Time from Exif info | |
//for folderChoice() function | |
var savedir = 0; // Directory where files will be saved | |
var falsecolordir = ""; // Directory to store main image with fruit highlighted | |
var midlinedir = ""; // Directory to store midline/skeleton images of isoloated fruit | |
var filecount = 0; // how many files are there? | |
var jpgcount = 0; // how many jpgs in the folder? | |
var folderstoprocess = newArray(1000000); //folder names to process | |
var filestoprocess = newArray(1000000); //file names to process | |
// begin macro code | |
resetImageJ(); // clean up in case there are open windows or ROIs in manager | |
createTable(); // creates the results table | |
getTimeString(); // makes the timestring | |
// control structure of macro to allow in-determinate number of images to be processed. | |
folderChoice(); // select folder(s) for processing | |
do { | |
choice = getBoolean("Do you want to process another folder?"); | |
if (choice==0) { | |
start = getTime(); | |
folderstoprocess = Array.trim(folderstoprocess, filecount); | |
filestoprocess = Array.trim(filestoprocess, filecount); | |
setBatchMode(true); | |
for (z=0; z<filestoprocess.length; z++) { | |
roiManager("reset"); | |
run("Clear Results"); | |
showProgress(z/filestoprocess.length); | |
open(folderstoprocess[z]+filestoprocess[z]); | |
clearScale(); // clear any scale that is present all results will be in pixels | |
orig = getTitle(); | |
selectWindow(orig); | |
run("Duplicate...", "title=Painted"); | |
selectWindow(orig); | |
// find green area | |
run("Duplicate...", "title=Green"); | |
min=newArray(3); | |
max=newArray(3); | |
filter=newArray(3); | |
a=getTitle(); | |
run("HSB Stack"); | |
run("Convert Stack to Images"); | |
selectWindow("Hue"); | |
rename("0"); | |
selectWindow("Saturation"); | |
rename("1"); | |
selectWindow("Brightness"); | |
rename("2"); | |
min[0]=50; | |
max[0]=104; | |
filter[0]="pass"; | |
min[1]=0; | |
max[1]=255; | |
filter[1]="pass"; | |
min[2]=43; | |
max[2]=255; | |
filter[2]="pass"; | |
for (i=0;i<3;i++){ | |
selectWindow(""+i); | |
setThreshold(min[i], max[i]); | |
run("Convert to Mask"); | |
if (filter[i]=="stop") run("Invert"); | |
} | |
imageCalculator("AND create", "0","1"); | |
imageCalculator("AND create", "Result of 0","2"); | |
for (i=0;i<3;i++){ | |
selectWindow(""+i); | |
close(); | |
} | |
selectWindow("Result of 0"); | |
close(); | |
selectWindow("Result of Result of 0"); | |
rename("Green"); | |
// correct the f'n LUT | |
invertedLUT = is("Inverting LUT"); | |
if (invertedLUT == 1) { | |
run("Invert LUT"); | |
run("Invert"); | |
} | |
run("Clear Results"); | |
run("Measure"); | |
how_white = getResult("Mean", 0); | |
if (how_white == 255) { // get mean brightness from results table | |
greenArea = 0; // nothing is green, the end is nigh | |
} else { | |
// something's green! | |
run("Create Selection"); | |
run("Clear Results"); | |
run("Measure"); | |
greenArea = getResult("Area", 0); // get area from results table | |
// paint it | |
selectWindow("Painted"); | |
run("Restore Selection"); | |
setForegroundColor(0, 255, 0); // Outline with Red | |
run("Line Width...", "line=5"); | |
run("Fill", "slice"); | |
} | |
selectWindow("Green"); | |
run("Close"); | |
selectWindow(orig); | |
// find yellow area | |
run("Duplicate...", "title=Yellow"); | |
min=newArray(3); | |
max=newArray(3); | |
filter=newArray(3); | |
a=getTitle(); | |
run("HSB Stack"); | |
run("Convert Stack to Images"); | |
selectWindow("Hue"); | |
rename("0"); | |
selectWindow("Saturation"); | |
rename("1"); | |
selectWindow("Brightness"); | |
rename("2"); | |
min[0]=33; | |
max[0]=50; | |
filter[0]="pass"; | |
min[1]=0; | |
max[1]=255; | |
filter[1]="pass"; | |
min[2]=134; | |
max[2]=255; | |
filter[2]="pass"; | |
for (i=0;i<3;i++){ | |
selectWindow(""+i); | |
setThreshold(min[i], max[i]); | |
run("Convert to Mask"); | |
if (filter[i]=="stop") run("Invert"); | |
} | |
imageCalculator("AND create", "0","1"); | |
imageCalculator("AND create", "Result of 0","2"); | |
for (i=0;i<3;i++){ | |
selectWindow(""+i); | |
close(); | |
} | |
selectWindow("Result of 0"); | |
close(); | |
selectWindow("Result of Result of 0"); | |
rename("Yellow"); | |
// correct the f'n LUT | |
invertedLUT = is("Inverting LUT"); | |
if (invertedLUT == 1) { | |
run("Invert LUT"); | |
run("Invert"); | |
} | |
run("Clear Results"); | |
run("Measure"); | |
how_white = getResult("Mean", 0); | |
if (how_white == 255) { // get mean brightness from results table | |
yellowArea = 0; // nothing is yellow, the world is great | |
} else { | |
// something's yellow! | |
run("Create Selection"); | |
run("Clear Results"); | |
run("Measure"); | |
yellowArea = getResult("Area", 0); // get area from results table | |
// paint it | |
selectWindow("Painted"); | |
run("Restore Selection"); | |
setForegroundColor(255, 0, 0); | |
run("Fill", "slice"); | |
} | |
selectWindow("Yellow"); | |
run("Close"); | |
selectWindow("Painted"); | |
//save with false color | |
saveAs("jpg", savedir + orig + "_Painted"); | |
close(); | |
//Print results for each leaf | |
print(f, orig + "\t" + greenArea + "\t" + yellowArea); | |
//reset ROIs for netting/stripes/shell diagnostics | |
roiManager("reset"); | |
// close unnecessary image windows | |
run("Close All"); | |
selectWindow("Results Table (results in px)"); | |
saveAs("Text", savedir + "Results.temp"); | |
} // end of image loop | |
} // end of if statement when choice ==0 | |
if (choice==1) { | |
folderChoice(); | |
} | |
} while (choice==1); | |
exit("Finished!"); | |
/////////////////////////////////////////////////////////////////////////////////////////////////// | |
//These are the functions called by the macro | |
/////////////////////////////////////////////////////////////////////////////////////////////////// | |
/////////////////////////////////////// | |
function resetImageJ() { | |
requires("1.48d"); | |
// Only if needed uncomment these lines | |
//run("Proxy Settings...", "proxy=webproxy-chbs.eame.syngenta.org port=8080"); | |
//run("Memory & Threads...", "maximum=1500 parallel=4 run"); | |
run("Options...", "iterations=1 count=1 edm=Overwrite"); | |
run("Line Width...", "line=1"); | |
run("Colors...", "foreground=black background=white selection=yellow"); | |
run("Clear Results"); | |
run("Close All"); | |
print("\\Clear"); | |
run("ROI Manager..."); | |
run("Input/Output...", "jpeg=75 gif=-1 file=.csv use_file copy_row save_column save_row"); | |
run("Set Measurements...", "area mean standard modal min centroid center perimeter bounding fit shape feret's integrated median skewness kurtosis area_fraction stack redirect=None decimal=3"); | |
} | |
/////////////////////////////////////// | |
/////////////////////////////////////////////////////// | |
function folderChoice() { | |
path = getDirectory("Choose a folder of images to analyze"); | |
// saves a folder for the processed images at your path with the following name | |
savedir = path+TimeString+File.separator; | |
File.makeDirectory(savedir); | |
filelist = getFileList(path); | |
for (z=0; z<filelist.length; z++) { | |
if (endsWith(filelist[z],"JPG")) { | |
folderstoprocess[filecount] = path; | |
filestoprocess[filecount] = filelist[z]; | |
jpgcount++; | |
filecount++; | |
} | |
if (endsWith(filelist[z],"jpg")) { | |
folderstoprocess[filecount] = path; | |
filestoprocess[filecount] = filelist[z]; | |
jpgcount++; | |
filecount++; | |
} | |
if (endsWith(filelist[z],"tif")) { | |
folderstoprocess[filecount] = path; | |
filestoprocess[filecount] = filelist[z]; | |
jpgcount++; | |
filecount++; | |
} | |
if (endsWith(filelist[z],"tiff")) { | |
folderstoprocess[filecount] = path; | |
filestoprocess[filecount] = filelist[z]; | |
jpgcount++; | |
filecount++; | |
} | |
} | |
var count = (count+1); | |
}// end of Folderchoice Function | |
/////////////////////////////////////////////////////// | |
//////////////////////////////////////// | |
function createTable() { | |
// creates a custom results table or clears the current open if open | |
title1 = "Results Table (results in px)"; | |
title2 = "["+title1+"]"; | |
f = title2; | |
if (isOpen(title1)) | |
print(f, "\\Clear"); | |
else | |
run("Table...", "name="+title2+" width=800 height=200"); | |
print(f, "\\Headings:File\tGreenArea\tYellowedArea"); | |
} // end of function Createtable() | |
////////////////////////////////////////// | |
////////////////////////////////////////// | |
function getTimeString() { | |
// time string for folder name | |
MonthNames = newArray("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"); | |
DayNames = newArray("Sun", "Mon","Tue","Wed","Thu","Fri","Sat"); | |
getDateAndTime(year, month, dayOfWeek, dayOfMonth, hour, minute, second, msec); | |
TimeString = DayNames[dayOfWeek]+"_"; | |
if (dayOfMonth<10) {TimeString = TimeString+"0";} | |
TimeString = TimeString+dayOfMonth+"_"+MonthNames[month]+"_"+year+"_"; | |
if (hour<10) {TimeString = TimeString+"0";} | |
TimeString = TimeString+hour+""; | |
if (minute<10) {TimeString = TimeString+"0";} | |
TimeString = TimeString+minute+"_"; | |
if (second<10) {TimeString = TimeString+"0";} | |
TimeString = TimeString+second; | |
} // end of getTimeString() | |
////////////////////////////////////////// | |
/////////////////////////////////////// | |
function clearScale() { | |
run("Set Scale...", "distance=0 known=0 pixel=1 unit=pixel global"); | |
} // end of ClearScale () function { | |
/////////////////////////////////////// | |
///////////////////////////////////////////////////////////////////////// | |
function correctLUT() { | |
invertedLUT = is("Inverting LUT"); | |
if (invertedLUT == 1) { | |
run("Invert LUT"); | |
run("Invert"); | |
} | |
} | |
///////////////////////////////////////////////////////////////////////// |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment