*
//
// WELCOME TO IJ_RHIZO
//
// (April 2013)
//
// An Image macro that measures a folder of root images
// obtained via scanning of washed root samples
//
// Alain Pierret - Institut de Recherche pour le Developpement (IRD)
// c/o National Agriculture and Forestry Research Institute (NAFRI)
// P.O. Box 4199, Ban Nongviengkham, Xaythany District, Vientiane, Lao P.D.R.
//
// alain.pierret@ird.fr
// apierret2@gmail.com
//
//
// IJ_Rhizo will normally run under ImageJ 1.421 and later
//
// !!! WARNING: input file names should not contain any space characters !!!
// !!! WARNING: Morphological Operators for ImageJ must be installed !!!
//
// download as a single zip file from:
//
// http://www.dentistry.bham.ac.uk/landinig/
//
// and copy to ImageJ's "plugins" directory
//
// Tested succesfully with:
//
// IJ 1.34s on MacOSX 10.4.9 with Java 1.5.0_07
// IJ 1.44o on MacOSX 10.6 with Java 1.6.0_31
// IJ 1.36b with WinXP v2002 SP2 with Java 1.5.0_06
// IJ 1.42l on Linux 2.6.24-16-generic
// IJ 1.46r on MacOSX 10.7.5 with Java 1.6.0_43
//
// DISCLAIMER:
// IJ_Rhizo is provided on an “as is” basis with ABSOLUTELY NO WARRANTY from the
// author or the author's employer. The author or the author's employer has no
// obligation to provide technical support nor to fix bugs upon users' requests.
// In no event shall the author or the author's employer be liable for any direct,
// incidental, special, exemplary, or consequential damages (including, but not
// limited to loss of use, data, or profits; or business interruption) however
// caused and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of the use
// of this code, even if advised of the possibility of such damage.
//
//*********************************************************************************
*
LowT= 255;
HighT = 0;
start = getTime();
setBatchMode(true); // runs up to 6 times faster
PartClean = LengthCorr = LKimura = ExcelOut = 1;
path3=dir2+"ResultAll.txt";
//f=File.open(path3);
Dialog.create("Input Data");
Dialog.addNumber("Image Resolution", 600, 0, 4,"dpi");
Dialog.addNumber("Width of excluded border", 50, 0, 4,"pixels");
Dialog.addCheckbox("Perform particle cleaning", PartClean);
Dialog.addNumber("Size of smallest measured particle", 1, 0, 4,"mm^2");
Dialog.addNumber("Circularity of measured particles [0-1]", 0.75, 2, 4, "");
Dialog.addCheckbox("Perform root length correction", LengthCorr);
Dialog.addCheckbox("Perform Kimura root length correction", LKimura);
Dialog.addChoice("Type of threshold:", newArray("Automatic", "User
defined"));
Dialog.addCheckbox("Output file extension (will be '.xls' if checked, '.txt'
if unchecked)", ExcelOut);
Dialog.show;
ImRes = Dialog.getNumber();
ImMar = Dialog.getNumber();
PartClean = Dialog.getCheckbox();
PartSize = Dialog.getNumber();
PartCirc = Dialog.getNumber();
LengthCorr = Dialog.getCheckbox();
LKimura = Dialog.getCheckbox();
ThresTyp = Dialog.getChoice();
ExcelOut = Dialog.getCheckbox();
if(ThresTyp=="User defined"){
Dialog.create("Thresholding values");
Dialog.addNumber("Lower threshold value", 0, 0, 3,"");
Dialog.addNumber("Upper threshold value", 255, 0, 3,"");
Dialog.show;
LowT = Dialog.getNumber;
HighT = Dialog.getNumber;
}
MinSize = PartSize/pow((25.4/ImRes),2);
// Initialization of variables
RLc = RL = XtraPix = MeanDia = EqVol = EqSurf = AvGL_01 = AvGL_02 =
AvGL_Skel = ActArea = NPart = 0;
path = dir1+list[k];
showProgress(k, list.length);
s=list[k];
NameLength=lengthOf(s)-4;
FName=substring(s,0,NameLength);
extension=substring(s,NameLength+1,lengthOf(s));
print("Extension: "+extension);
open(path);
if(extension!="jpg"){
path4=dir1+FName+".jpg";
saveAs("Jpeg", path4);
close();
open (path4);
}
print("Processing File:"+FName+"...");
width=getWidth;
height=getHeight;
area=width*height;
if(ThresTyp=="Automatic"){
MyThres = FindThres();
setThreshold(0,MyThres);
run("Threshold", "thresholded remaining black");
}
if(ThresTyp=="User defined"){
setAutoThreshold("Default");
setThreshold(LowT, HighT);
run("Convert to Mask");
}
if(PartClean>0) {
ImName=FName+".jpg";
selectWindow(ImName);
CleanParticles();
}
if (AvgGL==255) {
if(LKimura==1)
{File.append("NA"+"\t"+"NA"+"\t"+"NA"+"\t"+"NA"+"\t"+"NA"+"\t"+"NA"+"\t"+"NA"+"\t"+
"NA"+"\t"+ "NA"+"\t"+"NA"+"\t"+"NA"+"\t"+"NA", path3);}
if(LKimura!=1)
{File.append("NA"+"\t"+"NA"+"\t"+"NA"+"\t"+"NA"+"\t"+"NA"+"\t"+"NA"+"\t"+
"NA"+"\t"+"NA"+"\t"+"NA"+"\t"+"NA", path3);}
}
path1=dir2+FName+"THRES.jpg";
saveAs("Jpeg", path1);
close();
//********************************************************//
// Skeletonization and length measurement
//********************************************************//
open(path1);
setThreshold(0,128);
run("Threshold", "thresholded remaining black");
run("Invert LUT");
run("Invert");
selectWindow("Results");
run("Clear Results");
close();
//*********************************************************************//
open(path1);
setThreshold(0,128);
run("Threshold", "thresholded remaining black");
run("Invert LUT");
run("Invert");
run("Measure");
selectWindow("Results");
AvGL_01 = getResult('Mean', 0);
ActArea = (AvGL_01/255)*(getHeight()*getWidth())*pow((25.4/ImRes),2);
run("Clear Results");
close();
//*********************************************************************//
// Call MyHisto
open(path1);
run("Duplicate...", "title=copy01.jpg");
ImName = FName + "THRES.jpg";
selectWindow(ImName);
setThreshold(0,192);
run("Threshold", "thresholded remaining black");
run("Distance Map");
run("Invert");
selectWindow("copy01.jpg");
setThreshold(0,192);
run("Threshold", "thresholded remaining black");
run("Skeletonize");
run("Invert");
run("Divide...", "value=255.000");
imageCalculator("Multiply create", ImName,"copy01.jpg");
selectWindow("Result of "+FName+"THRES"+".jpg");
run("Invert");
getMinAndMax(min, max);
print("Maximum diameter is "+((2*max)-1)*(25.4/ImRes));
DiaC = MyHisto();
//*********************************************************************//
open(path1);
run("Duplicate...", "title=DistanceMap");
ImName=FName+"THRES.jpg";
selectWindow(ImName);
close();
selectWindow("DistanceMap");
setThreshold(0,128);
run("Threshold", "thresholded remaining black");
run("Distance Map");
run("Invert");
run("Subtract...", "value=1.000");
run("Measure");
MinV = getResult('Min', 0) + 1;
MaxV = getResult('Max', 0);
print("Min is "+MinV+" Max is "+MaxV);
print("Maximum diameter is "+((2*MaxV)-1)*(25.4/ImRes));
run("Clear Results");
//*********************************************************************//
//SkelSz = 0; //!!!!! Initialization of skeleton size
if(LKimura==1){
L1=L2=L3=LK=0;
open(path1);
selectWindow(ImName);
setThreshold(0,128);
run("Convert to Mask");
run("Skeletonize");
L1 = PixCount();
print("L1="+L1);
selectWindow(ImName);
setThreshold(182, 255);
run("Convert to Mask");
L2 = PixCount();
close;
print("L2="+L2);
selectWindow("Dupli01");
setThreshold(128, 172);
run("Convert to Mask");
L3 = 1.414214*PixCount();
close;
print("L3="+L3);
LK=L2+L3;
if(LengthCorr==1){
open(path1);
selectWindow(ImName);
setThreshold(0,128);
run("Threshold", "thresholded remaining black");
run("BinaryLabel8 ", " ");
selectWindow(ImName);
close();
SkelSz=0;
selectWindow("Labelled");
run("Duplicate...", "title=Labelled_copy");
selectWindow("Labelled_copy");
setThreshold(obj,obj);
run("Threshold", "thresholded remaining black");
run("Skeletonize");
run("Set Measurements...", " mean redirect=None decimal=3");
run("Measure");
selectWindow("Results");
AvGL_02 = getResult('Mean', 0);
SkelSz += (AvGL_02/255)*(getHeight()*getWidth()); // raw size of
skeleton //!!! Caution here skeleton size incremented iteratively
run("Clear Results");
selectWindow("Labelled_copy");
run("Divide...", "value=255.000");
imageCalculator("Multiply create", "DistanceMap","Labelled_copy");
run("Invert");
run("Subtract...", "value=1.000");
run("Set Measurements...", " mean min redirect=None decimal=3");
run("Measure");
selectWindow("Results");
AvGL_Skel = getResult('Min', 0); //average grey level of labelled
skeleton image
XtraPix += 2* AvGL_Skel;
close();
close();
}
}
if(LengthCorr==0){
open(path1);
SkelSz = 0;
selectWindow(ImName);
setThreshold(0,128);
run("Threshold", "thresholded remaining black");
run("Invert LUT");
run("Invert");
run("Skeletonize");
run("Set Measurements...", " mean redirect=None decimal=3");
run("Measure");
selectWindow("Results");
AvGL_02 = getResult('Mean', 0);
SkelSz = (AvGL_02/255)*(getHeight()*getWidth()); // raw size of skeleton
print("SkelSz: "+SkelSz);
run("Clear Results");
}
RL = SkelSz*(25.4/ImRes);
RLc = RL + ( XtraPix*(25.4/ImRes) );
DiaC = 2 * DiaC;
if(LKimura!=1)
{File.append(FName+"\t"+SkelSz+"\t"+SkelSz+XtraPix+"\t"+RL+"\t"+RLc+"\t"+
MeanDia+"\t"+ DiaC+"\t"+ EqSurf+"\t"+ EqVol+"\t"+ActArea, path3);}
} // end if(Npart>0);
else if(NPart==0){
close();
}
if (isOpen("Results")) {
selectWindow("Results");
run("Close" );
}
if (isOpen("Log")) {
selectWindow("Log");
run("Close" );
}
close();
close();
close();
close();
close();
} //print((getTime()-start)/1000);
//****************************************************//
// Pixel counting
//****************************************************//
function PixCount(){
searchValue = 255;
countValue = 0;
//****************************************************//
// Particle cleaning
//****************************************************//
function CleanParticles() {
run("Set Measurements...", "area perimeter fit circularity decimal=3");
run("Analyze Particles...", "minimum=1 maximum=999999 bins=20 show=Nothing
clear record");
//****************************************************//
// Function which finds the threshold value
//****************************************************//
function FindThres() {
nBins = 256;
row=0;
ThInf=0;
ThSup=0;
Thres=0;
for (j=1;j<nBins-1;j++) {
ind1=j-1; ind2=j+1;
val1 = getResult('Count',ind1);
val2 = getResult('Count',ind2);
slope = val2-val1;
}
Thres = floor((95*ThSup + 5*ThInf)/100);
return Thres;
print("Thres "+Thres);
}
//****************************************************//
// Function that creates histogram file
//****************************************************//
function MyHisto() {
maxHis=0;
nBins = 256;
getHistogram(values, counts, nBins);
run("Clear Results");
row=0;
max =max+1;
print("\\Clear");
print("\"Root Radius (mm)\""+" "+"\"Root Length (mm)\"");
path2=dir2+FName+"_Histo.txt";
selectWindow("Log");
saveAs("Text", path2);
sum1 = 0;
sum2 = 0;
close();
close();
close();
close();
}