|
/**
|
|
* root_tools
|
|
*
|
|
* The root tools help to efficiently measure the following characteristics of plant roots:
|
|
* - the angle of the opening of the whole root
|
|
* - the depth to which it goes down
|
|
* - the number of roots at 30 cm depth
|
|
* - the diameters of the roots at depth
|
|
*
|
|
* written 2013 by Volker Baecker (INSERM) at Montpellier RIO Imaging (www.mri.cnrs.fr)
|
|
*/
|
|
|
|
var helpURL = "http://dev.mri.cnrs.fr/wiki/imagej-macros/Root_Tools";
|
|
|
|
var KNOWN_DISTANCE = 4; // known distance for spatial calibration (there are 2cms between two nails)
|
|
var RADIUS=75; // radius of the circle drawn around the root point
|
|
var ROOT_POINT_LINE_WIDTH = 15; // line width of the circle drawn around the root point
|
|
var COLOR_ROOT_POINT = "red"; // color of the circle drawn around the root point
|
|
var COUNT_ROOT_DEPTH = 30; // depth from the root point at which the horizontal line is drawn. The number of roots will be counted at that depth
|
|
var HALF_LINE_WIDTH = 10; // half of the width of the line drawn at count root depth
|
|
var WIDTH_DEPTH = 8; // line width of the line drawn at count root depth
|
|
var COLOR_DEPTH = "cyan"; // color of the line drawn at count root depth
|
|
var DX_ANGLE = 300; // delta x for the angle tool that is added to the image
|
|
var DY_ANGLE = 600; // delta y for the angle tool that is added to the image
|
|
var ANGLE_COLOR = "red"; // the color for marking the measured angle
|
|
var ANGLE_LINE_WIDTH = 8; // the line width for marking the measured angle
|
|
var MEASURE_DEPTH_COLOR = "magenta"; // the color of the line indicating the max. depth to which the root goes down
|
|
var MEASURE_DEPTH_WIDTH = 8; // the line width of the line indicating the max. depth to which the root goes down
|
|
var ZOOM_RADIUS = 20; // radius of the region around the click that is copied and zoomed
|
|
var DIAMETER_COLOR = "yellow"; // color of the line indicating the measured root diameters
|
|
var DIAMETER_WIDTH = 2; // width of the line indicating the measured root diameters
|
|
var rootX;
|
|
var rootY;
|
|
var deepX;
|
|
var deepY;
|
|
var UNIT = "cm";
|
|
var X1;
|
|
var X2;
|
|
var Y;
|
|
var MAX_ROOTS = 20;
|
|
var DIAMETERS = newArray(MAX_ROOTS);
|
|
var numberOfRootsAtThirty = 0;
|
|
var DEPTH = 0;
|
|
var ID = 0;
|
|
var ANGLE = 0;
|
|
var TITLE;
|
|
var NUMBER = 0;
|
|
var FOLDER;
|
|
|
|
macro "zoomIn [f1]" {
|
|
run("In [+]");
|
|
}
|
|
|
|
macro "zoomOut [f2]" {
|
|
run("Out [-]");
|
|
}
|
|
|
|
macro "Root Tools Help Action Tool- C134D01D04D06D11D1fD20D2fD3fDcfDd4Dd7De0De2De3De4De5De6De7Df2Df3Df4Df5Df6Df7Df9DfdC345D08D09D18D25D27D32D34D3aD42D43D5fD6dD92Da3Da5Dc5Dc8DdbC334D0fD12D19D30D3bD59D5aD69D6aDa2Da4DadDaeDafDb2Db3DbdDbeDc6Dd8DdeC666D53D54D62D77D7bD7cD7dD7eD91D94D95D9aD9bD9cD9fDa9DbaDcaDcbC234D02D03D05D07D0aD0bD10D2eD4aDb4DbfDceDd0Dd1Dd5Dd6DdfDe8De9DeaDebDecDeeDf8DfbC555D0eD1bD35D36D45D50D51D52D56D6fD7aD90D93D9dDa1Da6Db1Db7DbcDddC345D0cD15D1aD37D3dD40D5cDb5Db6Dc9DfeC777D1cD57D66D71D72D73D74D76D80D82D83D88D89D8aD8eD8fD97D98C234D00Dc4Dd3De1Df0Df1DfaDfcC445D17D29D2bD38D41D46D55D58D65D6bD6eD78D79D9eDa0DacDcdC335D13D14D16D1eD23D24D28D33D3eD4cD5eD68D6cDc7Dd2DefC666D1dD48D61D63D67D70D7fD96D99Da7Da8Db9DbbDccC234D21D2dD3cD4bD4fDc0Dc3Dd9DdaDedC556D0dD26D2aD39D44D47D60D64D75DaaDabDb8DdcC345D22D2cD31D49D4dD4eD5bD5dDb0Dc1Dc2C777D81D84D85D86D87D8bD8cD8dDff" {
|
|
run('URL...', 'url='+helpURL);
|
|
}
|
|
|
|
macro "Set Scale [f5]" {
|
|
setScale();
|
|
}
|
|
|
|
macro "Define Root Point [f6]" {
|
|
setTool("Define Root Point Tool");
|
|
}
|
|
|
|
macro "Angle [f7]" {
|
|
measureAngle();
|
|
}
|
|
|
|
macro "Measure Depth [f8]" {
|
|
setTool("Measure Depth Tool");
|
|
}
|
|
|
|
macro "Zoom Region [f9]" {
|
|
setTool("Zoom region Tool");
|
|
}
|
|
|
|
macro "Measure Diameter [f10]" {
|
|
measureDiameter();
|
|
}
|
|
|
|
macro "Write Report [f11]" {
|
|
writeReport();
|
|
}
|
|
|
|
macro "Set Scale Action Tool- C000T4b12s"{
|
|
setScale();
|
|
}
|
|
|
|
macro "Set Scale Action Tool Options" {
|
|
Dialog.create("Root Tools - Set Scale - Options");
|
|
Dialog.addNumber("known distance [cm]: ", KNOWN_DISTANCE);
|
|
Dialog.show();
|
|
KNOWN_DISTANCE = Dialog.getNumber();
|
|
}
|
|
|
|
macro "Define Root Point Tool- C000T4b12r" {
|
|
getCursorLoc(x, y, z, flags);
|
|
ID = getImageID();
|
|
TITLE = getTitle();
|
|
FOLDER = getDirectory("image");
|
|
run("Remove Overlay");
|
|
rootX = x;
|
|
rootY = y;
|
|
setColor(COLOR_ROOT_POINT);
|
|
setLineWidth(ROOT_POINT_LINE_WIDTH);
|
|
Overlay.drawEllipse(rootX-RADIUS, rootY-RADIUS, 2*RADIUS+1, 2*RADIUS+1);
|
|
Overlay.show();
|
|
run("Select None");
|
|
lineAtThirtyCM();
|
|
angleSelection();
|
|
setTool("Angle tool");
|
|
}
|
|
|
|
macro "Define Root Point Tool Options" {
|
|
Dialog.create("Root Tools - Define Root Point - Options");
|
|
Dialog.addNumber("radius: ", RADIUS);
|
|
Dialog.addNumber("line width: ", ROOT_POINT_LINE_WIDTH);
|
|
Dialog.addString("root point color: ", COLOR_ROOT_POINT);
|
|
Dialog.addNumber("depth [cm]: ", COUNT_ROOT_DEPTH);
|
|
Dialog.addNumber("half of length [cm]: ", HALF_LINE_WIDTH);
|
|
Dialog.addNumber("line width for depth: ", WIDTH_DEPTH);
|
|
Dialog.addString("depth line color: ", COLOR_DEPTH);
|
|
Dialog.addNumber("dx angle: ", DX_ANGLE);
|
|
Dialog.addNumber("dy angle: ", DY_ANGLE);
|
|
Dialog.show();
|
|
RADIUS = Dialog.getNumber();
|
|
ROOT_POINT_LINE_WIDTH = Dialog.getNumber();
|
|
COLOR_ROOT_POINT = Dialog.getString();
|
|
COUNT_ROOT_DEPTH = Dialog.getNumber();
|
|
HALF_LINE_WIDTH = Dialog.getNumber();
|
|
WIDTH_DEPTH = Dialog.getNumber();
|
|
COLOR_DEPTH = Dialog.getString();
|
|
DX_ANGLE = Dialog.getNumber();
|
|
DY_ANGLE = Dialog.getNumber();
|
|
}
|
|
|
|
macro "Angle Action Tool- C000T4b12a" {
|
|
measureAngle();
|
|
}
|
|
|
|
macro "Angle Action Tool Options" {
|
|
Dialog.create("Root Tools - Angle - Options");
|
|
Dialog.addString("angle color: ", ANGLE_COLOR);
|
|
Dialog.addNumber("line width: ", ANGLE_LINE_WIDTH);
|
|
Dialog.show();
|
|
ANGLE_COLOR = Dialog.getString();
|
|
ANGLE_LINE_WIDTH = Dialog.getNumber();
|
|
}
|
|
|
|
macro "Measure Depth Tool- C000T4b12d" {
|
|
getCursorLoc(x, y, z, flags);
|
|
deepX = x;
|
|
deepY = y;
|
|
DEPTH = deepY - rootY;
|
|
toScaled(DEPTH);
|
|
setColor(MEASURE_DEPTH_COLOR);
|
|
setLineWidth(MEASURE_DEPTH_WIDTH);
|
|
Overlay.drawLine(rootX, rootY, rootX, deepY);
|
|
Overlay.drawLine(rootX, deepY, deepX, deepY);
|
|
Overlay.show();
|
|
run("Select None");
|
|
setTool("Zoom region Tool");
|
|
}
|
|
|
|
macro "Measure Depth Tool Options" {
|
|
Dialog.create("Root Tools - Measure Depth - Options");
|
|
Dialog.addString("line color: ", MEASURE_DEPTH_COLOR);
|
|
Dialog.addNumber("line width: ", MEASURE_DEPTH_WIDTH);
|
|
Dialog.show();
|
|
MEASURE_DEPTH_COLOR = Dialog.getString();
|
|
MEASURE_DEPTH_WIDTH = Dialog.getNumber();
|
|
}
|
|
|
|
macro "Zoom region Tool- C000T4b12z" {
|
|
title = getTitle();
|
|
getCursorLoc(x, y, z, flags);
|
|
makeRectangle(x-ZOOM_RADIUS, y-ZOOM_RADIUS, 2*ZOOM_RADIUS+1, 2*ZOOM_RADIUS+1);
|
|
run("Duplicate...", "title="+title+"box");
|
|
run("Remove Overlay");
|
|
run("In [+]");
|
|
run("In [+]");
|
|
run("In [+]");
|
|
run("In [+]");
|
|
run("In [+]");
|
|
run("In [+]");
|
|
setTool("line");
|
|
}
|
|
|
|
macro "Zoom region Tool Options" {
|
|
Dialog.create("Root Tools - Zoom region - Options");
|
|
Dialog.addNumber("radius ", ZOOM_RADIUS);
|
|
Dialog.show();
|
|
ZOOM_RADIUS = Dialog.getNumber();
|
|
}
|
|
|
|
macro "Measure diameter Action Tool- C000T4b12m" {
|
|
measureDiameter();
|
|
}
|
|
|
|
macro "Measure diameter Action Tool Options" {
|
|
Dialog.create("Root Tools - Measure diameter - Options");
|
|
Dialog.addString("line color: ", DIAMETER_COLOR);
|
|
Dialog.addNumber("line width", "DIAMETER_WIDTH);
|
|
Dialog.show();
|
|
DIAMETER_COLOR = Dialog.getString();
|
|
DIAMETER_WIDTH = Dialog.getNumber();
|
|
}
|
|
|
|
macro "Write report Action Tool- C000T4b12w" {
|
|
writeReport();
|
|
}
|
|
|
|
function lineAtThirtyCM() {
|
|
setColor(COLOR_DEPTH);
|
|
setLineWidth(WIDTH_DEPTH);
|
|
tenUnscaled = HALF_LINE_WIDTH;
|
|
toUnscaled(tenUnscaled);
|
|
thirtyUnscaled = COUNT_ROOT_DEPTH;
|
|
toUnscaled(thirtyUnscaled);
|
|
X1 = rootX - tenUnscaled;
|
|
X2 = rootX + tenUnscaled;
|
|
Y = rootY + thirtyUnscaled;
|
|
Overlay.drawLine(X1, Y, X2, Y);
|
|
Overlay.show();
|
|
}
|
|
|
|
function angleSelection() {
|
|
xCoords = newArray(3);
|
|
yCoords = newArray(3);
|
|
xCoords[0] = rootX - DX_ANGLE;
|
|
xCoords[1] = rootX;
|
|
xCoords[2] = rootX + DX_ANGLE;
|
|
yCoords[0] = rootY + DY_ANGLE;
|
|
yCoords[1] = rootY;
|
|
yCoords[2] = rootY + DY_ANGLE;
|
|
makeSelection("angle", xCoords, yCoords);
|
|
}
|
|
|
|
function setScale() {
|
|
getLine(x1, y1, x2, y2, lineWidth);
|
|
dx = x2-x1;
|
|
dy = y2-y1;
|
|
length = sqrt(dx*dx+dy*dy);
|
|
run("Set Scale...", "distance=" + length+" known="+KNOWN_DISTANCE+" pixel=1 unit="+UNIT);
|
|
run("Select None");
|
|
setTool("Define Root Point Tool");
|
|
}
|
|
|
|
function measureAngle() {
|
|
run("Measure");
|
|
ANGLE = getResult("Angle", nResults-1);
|
|
selectWindow("Results");
|
|
run("Close");
|
|
getSelectionCoordinates(xCoordinates, yCoordinates);
|
|
run("Select None");
|
|
setColor(ANGLE_COLOR);
|
|
setLineWidth(ANGLE_LINE_WIDTH);
|
|
Overlay.drawLine(xCoordinates[0], yCoordinates[0], xCoordinates[1], yCoordinates[1]);
|
|
Overlay.drawLine(xCoordinates[1], yCoordinates[1], xCoordinates[2], yCoordinates[2]);
|
|
Overlay.show();
|
|
setTool("Measure Depth Tool");
|
|
}
|
|
|
|
function measureDiameter() {
|
|
getLine(x1, y1, x2, y2, lineWidth);
|
|
dx = x2-x1;
|
|
dy = y2-y1;
|
|
length = sqrt(dx*dx+dy*dy);
|
|
toScaled(length);
|
|
DIAMETERS[numberOfRootsAtThirty++] = length;
|
|
close();
|
|
selectImage(ID);
|
|
getSelectionBounds(x, y, width, height);
|
|
run("Select None");
|
|
setColor(DIAMETER_COLOR);
|
|
setLineWidth(DIAMETER_WIDTH);
|
|
Overlay.drawLine(x+x1, y+y1, x+x2, y+y2);
|
|
Overlay.show();
|
|
setTool("Zoom region Tool");
|
|
}
|
|
|
|
function writeReport() {
|
|
NUMBER++;
|
|
getDateAndTime(year, month, dayOfWeek, dayOfMonth, hour, minute, second, msec);
|
|
title = "root measurements - " + year + "-" + month + "-" + dayOfMonth + ".txt";
|
|
ref = "[" + title + "]";
|
|
if (!isOpen(title)) {
|
|
run("Table...", "name="+ref+" width=250 height=600");
|
|
header = "\\Headings:" + "nr." + "\t" + "image" + "\t" + "root x" + "\t" + "root y" + "\t" + "depth" + "\t" + "nr at 30cm";
|
|
for (i=0; i<MAX_ROOTS; i++) {
|
|
header = header + "\t" + "d" + (i+1);
|
|
}
|
|
print(ref, header);
|
|
}
|
|
row = "" + NUMBER + "\t" + TITLE + "\t" + rootX + "\t" + rootY + "\t"+ DEPTH + "\t" + numberOfRootsAtThirty;
|
|
for (i=0; i<numberOfRootsAtThirty; i++) {
|
|
row = row + "\t" + DIAMETERS[i];
|
|
}
|
|
print(ref, row);
|
|
resetMeasurements();
|
|
if (!File.exists(FOLDER + "/" + "control/")) File.makeDirectory(FOLDER + "/" + "control/");
|
|
path = FOLDER + "/" + "control/" + TITLE;
|
|
saveAs("tiff", path);
|
|
Overlay.remove;
|
|
setTool("line");
|
|
}
|
|
|
|
function resetMeasurements() {
|
|
numberOfRootsAtThirty = 0;
|
|
DIAMETERS = newArray(MAX_ROOTS);
|
|
}
|