Anda di halaman 1dari 11

//*********************************************************************************

*
//
// 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.
//
//*********************************************************************************
*

dir1 = getDirectory("Choose Source Directory ");


dir2 = getDirectory("Choose Destination Directory ");
list = getFileList(dir1);

print("Input Directory is "+dir1);


print("Output Directory is "+dir2);

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;
}

print("Image Resolution = "+ImRes+" dpi");


print("Width of excluded border = "+ImMar+" pixels");
print("Size of smallest measured particle = "+PartSize+" mm^2");
print("Circularity of measured particles = "+PartCirc);
print("Length correction: "+LengthCorr);
print("Length correction: "+LKimura);
print("Particle cleaning: "+PartClean);
if(ThresTyp=="User defined") print("User defined thresholding with lower
value = "+LowT+" and higher value ="+HighT); else print("Automatic threshold");
print("Output file format = "+ExcelOut);
if(ExcelOut==1) path3=dir2+"ResultAll.xls";

MinSize = PartSize/pow((25.4/ImRes),2);

setBackgroundColor(255,255,255); // Initialization of background colour


if(LKimura==1)
{File.append("File_Name\t"+"Raw_Skel\t"+"Skel_Corr\t"+"Kimura\t"+"Raw_RL\t"+"RLc\t"
+"Kimura_length\t"+"Mean_Dia\t"+"Mean_Dia_C\t"+"Eq_Surf\t"+"Eq_Vol\t"+"Surf_Area\t"
, path3);}

if(LKimura!=1) {File.append("File Name"+"\t"+"Raw Skel"+"\t"+"Skel Corr"+"\t"+"Raw


RL"+"\t"+"RLc"+"\t"+"Mean_Dia"+"\t"+"Mean_Dia_C"+"\t"+ "Eq Surf"+"\t"+"Eq.
Vol"+"\t"+"Surf. Area", path3);}

for (k=0; k<list.length; k++) {

// 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;

NHeight = height -(2*ImMar); // This can be adjusted to


images characteristics
NWidth = width - (2*ImMar); // the purpose being to
remove pixels at the image
// periphery because these pixels
are known to include
setTool(0); // values which could mistakenly be
considered as "objects"
makeRectangle(ImMar, ImMar, NWidth, NHeight);
run("Clear Outside");
run("Select None");
run("8-bit");

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();
}

run("Set Measurements...", " mean redirect=None decimal=3");


run("Select All");
run("Measure");
selectWindow("Results");
AvgGL = getResult('Mean', 0);
run("Clear Results");

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");

run("Analyze Particles...", "size=0-Infinity circularity=0.00-1.00


show=Nothing display clear");
NPart = nResults;
print("!!!!! Number of particles ="+NPart);

if(NPart>0){ // *** This test ends Line 394

selectWindow("Results");
run("Clear Results");
close();

run("Set Measurements...", " mean min redirect=None decimal=3");

//*********************************************************************//
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);

run("Convolve...", "text1=[ 1 5 1 \n5 25 5\n1 5 1\n] normalize");


run("Duplicate...", "title=Dupli01");

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;

for (obj=1; obj<NPart+1; obj++) {

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;

print("!!!!! Grey Level ="+AvGL_02);


print("!!!!! Raw Skeleton size ="+SkelSz);
print("!!!!! Extra Pix ="+XtraPix);
run("Clear Results");

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;

MeanDia = ActArea/RLc; //******* Calc. of Mean Dia.=Proj. Surf.


Area/RL
EqVol = pow((MeanDia/2),2) * RLc * PI; //******* Calc. of Equiv. root
volume
EqSurf = MeanDia * RLc * PI; //******* Calc. of Equiv. root surface
area
if(LKimura==1)
{File.append(FName+"\t"+SkelSz+"\t"+SkelSz+XtraPix+"\t"+LK+"\t"+RL+"\t"+RLc+"\t"+
LK*(25.4/ImRes)+"\t"+ MeanDia+"\t"+ DiaC+"\t"+ EqSurf+"\t"+ EqVol+"\t"+ActArea,
path3);}

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;

for( x = 0; x < getWidth(); x++ ) {


for( y = 0; y < getHeight(); y++ ) {
if( getPixel(x,y) == searchValue ) {
countValue += 1;
}
}
}
return countValue;

//****************************************************//
// 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");

print("Size of smallest measured particle = "+MinSize+" Pixels");


print("Circularity of measured particles = "+PartCirc);

for (i=0; i<nResults; i++) {


x = getResult('XStart', i);
y = getResult('YStart', i);
MinAx = getResult('Minor', i);
MaxAx = getResult('Major', i);
RatAx = MaxAx/MinAx;
circularity = getResult('Circ.', i);
area = getResult('Area', i);

if (getPixel(x, y) == 0 && circularity>PartCirc || area<MinSize) {


doWand(x,y);
run("Clear");
}
if(getPixel(x, y) == 0 && RatAx>30 && area>20000)
{ doWand(x,y);run("Clear");}
}
}

//****************************************************//
// Function which finds the threshold value
//****************************************************//

function FindThres() {

nBins = 256;
row=0;

ThInf=0;
ThSup=0;
Thres=0;

getHistogram(values, counts, nBins);


for (i=0; i<nBins; i++) {

setResult("Value", row, values[i]);


setResult("Count", row, counts[i]);
row++;
}

for (j=1;j<nBins-1;j++) {

ind1=j-1; ind2=j+1;
val1 = getResult('Count',ind1);
val2 = getResult('Count',ind2);
slope = val2-val1;

if(slope>0 && ThInf==0){ThInf=j;}


if(slope>1000 && ThSup==0 && j>ThInf+100){ThSup=j;}

}
Thres = floor((95*ThSup + 5*ThInf)/100);
return Thres;
print("Thres "+Thres);
}

//****************************************************//
// Function that creates histogram file
//****************************************************//

function MyHisto() {

getStatistics(area, mean, min, max, std, histogram);


print(mean);
print(min);
print(max);
print( ((2*max)-1)*(25.4/ImRes));

maxHis=0;
nBins = 256;
getHistogram(values, counts, nBins);

for (i=1; i<nBins; i++) {


maxHis = maxOf(maxHis,counts[i]);
}
print(maxHis);

// Plot.create("Histogram", "Value", "Count", histogram);


// Plot.setLimits(1, max, 0, maxHis);

run("Clear Results");
row=0;
max =max+1;

for (j=1; j<max; j++) {


calVal = values[j] * (25.4/ImRes);
setResult("Value", row, calVal);
setResult("Count", row, counts[j]);
row++;
}
updateResults();

print("\\Clear");
print("\"Root Radius (mm)\""+" "+"\"Root Length (mm)\"");

for (j=1; j<max; j++) {


calVal = values[j] * (25.4/ImRes);
print(calVal+" "+counts[j] * (25.4/ImRes) );
}

path2=dir2+FName+"_Histo.txt";
selectWindow("Log");
saveAs("Text", path2);

sum1 = 0;
sum2 = 0;

for (j=1; j<max-1; j++) {


sum1 = sum1 + (counts[j] * (25.4/ImRes));
sum2 = sum2 + ( (counts[j] * values[j]) * ((25.4/ImRes) * (25.4/ImRes)) ) ;
}
sum3 = sum2 / sum1;
return sum3;

close();
close();
close();
close();
}

// END of macro IJ_RHIZO

Anda mungkin juga menyukai