|
|
|
|
@@ -1,30 +1,67 @@
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
///////// /////////
|
|
|
|
|
///////// OPTIMISED SCAN PLUGIN /////////
|
|
|
|
|
///////// /////////
|
|
|
|
|
///////// Created by Hector Dejea, hector.dejea@psi.ch /////////
|
|
|
|
|
///////// February, 2018 /////////
|
|
|
|
|
///////// /////////
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
/* Optimised Scan
|
|
|
|
|
|
|
|
|
|
Opt_Scan.ijm is an ImageJ plugin to find the optimal scanning grid for irregular objects. Binary and
|
|
|
|
|
non binary images are accepted. The option to have a constant grid on the whole object or
|
|
|
|
|
independent grids for each of the different Y layers is given. The plugin generates a .txt file that
|
|
|
|
|
can be used as input for the beamline macro.
|
|
|
|
|
Opt_Scan.ijm is an ImageJ plugin to find the optimal scanning grid for
|
|
|
|
|
irregular objects. Binary and non binary images are accepted. The option
|
|
|
|
|
to have a constant grid on the whole object or independent grids for
|
|
|
|
|
each of the different Y layers is given. The plugin generates a .txt file
|
|
|
|
|
that can be used as input for the beamline macro.
|
|
|
|
|
|
|
|
|
|
*Created by hector.dejea@psi.ch in February 2018* */
|
|
|
|
|
Last modified: 14/10/2019 */
|
|
|
|
|
|
|
|
|
|
//Set default values
|
|
|
|
|
LowResimagePixelSize = 6.5;
|
|
|
|
|
ZZcenterLowRes = 0;
|
|
|
|
|
XXcenterLowRes = 0;
|
|
|
|
|
YposLowRes = 0;
|
|
|
|
|
LowResFOVY = 590;
|
|
|
|
|
cameraFOVX = 2560;
|
|
|
|
|
cameraFOVY = 2160;
|
|
|
|
|
HighResimagePixelSize = 0.65;
|
|
|
|
|
CameraMoveX = 0;
|
|
|
|
|
CameraMoveY = 0;
|
|
|
|
|
//Check if there is a file containing the OptScan settings
|
|
|
|
|
settings_path = getDirectory("home") + "/.optscan_settings.txt";
|
|
|
|
|
|
|
|
|
|
overlapXZ = cameraFOVX*0.3;
|
|
|
|
|
overlapY = 50;
|
|
|
|
|
if (File.exists(settings_path)){
|
|
|
|
|
|
|
|
|
|
//Set default parameters specified in the file
|
|
|
|
|
parameters_file = File.openAsString(settings_path);
|
|
|
|
|
lines=split(parameters_file,"\n");
|
|
|
|
|
|
|
|
|
|
LowResimagePixelSize = lines[3];
|
|
|
|
|
ZZcenterLowRes = lines[5];
|
|
|
|
|
XXcenterLowRes = lines[7];
|
|
|
|
|
YposLowRes = lines[9];
|
|
|
|
|
LowResFOVY = lines[11];
|
|
|
|
|
cameraFOVX = lines[13];
|
|
|
|
|
cameraFOVY = lines[15];
|
|
|
|
|
HighResimagePixelSize = lines[17];
|
|
|
|
|
CameraMoveX = lines[19];
|
|
|
|
|
CameraMoveY = lines[21];
|
|
|
|
|
overlapXZ = lines[23];
|
|
|
|
|
overlapY = lines[25];
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
//Otherwise set these default values
|
|
|
|
|
LowResimagePixelSize = 6.5;
|
|
|
|
|
ZZcenterLowRes = 0;
|
|
|
|
|
XXcenterLowRes = 0;
|
|
|
|
|
YposLowRes = 0;
|
|
|
|
|
LowResFOVY = 590;
|
|
|
|
|
cameraFOVX = 2560;
|
|
|
|
|
cameraFOVY = 2160;
|
|
|
|
|
HighResimagePixelSize = 0.65;
|
|
|
|
|
CameraMoveX = 0;
|
|
|
|
|
CameraMoveY = 0;
|
|
|
|
|
overlapXZ = cameraFOVX*0.3;
|
|
|
|
|
overlapY = 50;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
repeatFlag = 1;
|
|
|
|
|
while (repeatFlag == 1){
|
|
|
|
|
print("Welcome to the Scan Optimisation plugin!");
|
|
|
|
|
print("Welcome to the Scan Optimisation plugin!\n");
|
|
|
|
|
|
|
|
|
|
//Ask the user for LowRes and HighRes acquisition parameters
|
|
|
|
|
Dialog.create("Magnification and Camera Settings");
|
|
|
|
|
@@ -41,12 +78,10 @@ while (repeatFlag == 1){
|
|
|
|
|
Dialog.addNumber("Overlap in X and Z (pixels):", overlapXZ);
|
|
|
|
|
Dialog.addNumber("Overlap in Y (pixels):", overlapY);
|
|
|
|
|
Dialog.addChoice("Projection Method:", newArray("MaxIP" ,"MinIP"), "MaxIP");
|
|
|
|
|
Dialog.addChoice("Grid Mode:", newArray("Constant !!NOT READY!!" ,"Independent"), "Independent");
|
|
|
|
|
|
|
|
|
|
Dialog.show();
|
|
|
|
|
|
|
|
|
|
LowResimagePixelSize = Dialog.getNumber();
|
|
|
|
|
//angleLowRes = Dialog.getChoice();
|
|
|
|
|
XXcenterLowRes = Dialog.getNumber();
|
|
|
|
|
ZZcenterLowRes = Dialog.getNumber();
|
|
|
|
|
YposLowRes = Dialog.getNumber();
|
|
|
|
|
@@ -59,7 +94,6 @@ while (repeatFlag == 1){
|
|
|
|
|
overlapXZ = Dialog.getNumber();
|
|
|
|
|
overlapY = Dialog.getNumber();
|
|
|
|
|
projMethod = Dialog.getChoice();
|
|
|
|
|
gridMode = Dialog.getChoice();
|
|
|
|
|
|
|
|
|
|
// Real magnification
|
|
|
|
|
real_magnification = LowResimagePixelSize/HighResimagePixelSize;
|
|
|
|
|
@@ -74,13 +108,13 @@ while (repeatFlag == 1){
|
|
|
|
|
|
|
|
|
|
//Compute number of acquisition layers in Y and layer size
|
|
|
|
|
blockSizeY = cameraFOVY-overlapY;
|
|
|
|
|
print("Complete block Size in Y is " + (cameraFOVY/real_magnification) + " pixels");
|
|
|
|
|
print("Volume Size in Y is " + (cameraFOVY/real_magnification) + " pixels");
|
|
|
|
|
print("Block Size in Y is " + (blockSizeY/real_magnification) + " pixels");
|
|
|
|
|
nLayersY = computeNumberBlocks("Y",blockSizeY,cameraFOVY,StartSlice,EndSlice,real_magnification);
|
|
|
|
|
|
|
|
|
|
//Compute blocksize XZ
|
|
|
|
|
blockSizeXZ = cameraFOVX-overlapXZ;
|
|
|
|
|
print("Complete block Size in XZ is " + (cameraFOVX/real_magnification) + " pixels");
|
|
|
|
|
print("Volume Size in XZ is " + (cameraFOVX/real_magnification) + " pixels");
|
|
|
|
|
print("Block Size in XZ is " + (blockSizeXZ/real_magnification) + " pixels");
|
|
|
|
|
|
|
|
|
|
//Check if image is binary
|
|
|
|
|
@@ -127,20 +161,10 @@ while (repeatFlag == 1){
|
|
|
|
|
yEndVals[i] = y + height -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//GENERATE Y CONSTANT GRID
|
|
|
|
|
if (gridMode == "Constant") {
|
|
|
|
|
centresXXZZY = generateYConstantGrid(blockSizeXZ,blockSizeY,cameraFOVX,cameraFOVY,nLayersY,xVals,
|
|
|
|
|
yVals,xEndVals,yEndVals,real_magnification,XXcenterLowRes,ZZcenterLowRes,LowResimagePixelSize,
|
|
|
|
|
HighResimagePixelSize,LowResFOVY,CameraMoveX,CameraMoveY,imagewidth,imageheight,StartSlice);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//GENERATE Y INDEPENDENT GRID
|
|
|
|
|
if (gridMode == "Independent") {
|
|
|
|
|
centresXXZZY = generateYIndependentGrid(blockSizeXZ,blockSizeY,cameraFOVX,cameraFOVY,nLayersY,xVals,
|
|
|
|
|
yVals,xEndVals,yEndVals,real_magnification,XXcenterLowRes,ZZcenterLowRes,LowResimagePixelSize,
|
|
|
|
|
HighResimagePixelSize,LowResFOVY,CameraMoveX,CameraMoveY,imagewidth,imageheight,StartSlice);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//GENERATE GRID
|
|
|
|
|
centresXXZZY = generateYIndependentGrid(blockSizeXZ,blockSizeY,cameraFOVX,cameraFOVY,nLayersY,xVals,
|
|
|
|
|
yVals,xEndVals,yEndVals,real_magnification,XXcenterLowRes,ZZcenterLowRes,LowResimagePixelSize,
|
|
|
|
|
HighResimagePixelSize,LowResFOVY,CameraMoveX,CameraMoveY,imagewidth,imageheight,StartSlice);
|
|
|
|
|
|
|
|
|
|
//COMPLETE FOR BINARY IMAGES -> ELIMINATE EMPTY VOLUMES
|
|
|
|
|
|
|
|
|
|
@@ -176,19 +200,11 @@ while (repeatFlag == 1){
|
|
|
|
|
yEndVals[i] = y + height -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//GENERATE CONSTANT GRID
|
|
|
|
|
if (gridMode == "Constant") {
|
|
|
|
|
centresXXZZY = generateYConstantGrid(blockSizeXZ,blockSizeY,cameraFOVX,cameraFOVY,nLayersY,xVals,
|
|
|
|
|
yVals,xEndVals,yEndVals,real_magnification,XXcenterLowRes,ZZcenterLowRes,LowResimagePixelSize,
|
|
|
|
|
HighResimagePixelSize,LowResFOVY,CameraMoveX,CameraMoveY,imagewidth,imageheight,StartSlice);
|
|
|
|
|
}
|
|
|
|
|
//GENERATE GRID
|
|
|
|
|
centresXXZZY = generateYIndependentGrid(blockSizeXZ,blockSizeY,cameraFOVX,cameraFOVY,nLayersY,xVals,
|
|
|
|
|
yVals,xEndVals,yEndVals,real_magnification,XXcenterLowRes,ZZcenterLowRes,LowResimagePixelSize,
|
|
|
|
|
HighResimagePixelSize,LowResFOVY,CameraMoveX,CameraMoveY,imagewidth,imageheight,StartSlice);
|
|
|
|
|
|
|
|
|
|
//GENERATE INDEPENDENT GRID
|
|
|
|
|
if (gridMode == "Independent") {
|
|
|
|
|
centresXXZZY = generateYIndependentGrid(blockSizeXZ,blockSizeY,cameraFOVX,cameraFOVY,nLayersY,xVals,
|
|
|
|
|
yVals,xEndVals,yEndVals,real_magnification,XXcenterLowRes,ZZcenterLowRes,LowResimagePixelSize,
|
|
|
|
|
HighResimagePixelSize,LowResFOVY,CameraMoveX,CameraMoveY,imagewidth,imageheight,StartSlice);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Print .txt file with the generated coordinates
|
|
|
|
|
@@ -200,8 +216,26 @@ while (repeatFlag == 1){
|
|
|
|
|
if (repeatFlag) {
|
|
|
|
|
closeGeneratedImgs();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Save txt file with the parameters used, so that they are default for next use.
|
|
|
|
|
settings_file = File.open(settings_path);
|
|
|
|
|
settings_array = "File containing the last settings used in Opt_Scan.ijm\n" +
|
|
|
|
|
"\nLowResimagePixelSize\n" + LowResimagePixelSize +
|
|
|
|
|
"\nZZcenterLowRes\n" + ZZcenterLowRes +
|
|
|
|
|
"\nXXcenterLowRes\n" + XXcenterLowRes +
|
|
|
|
|
"\nYposLowRes\n" + YposLowRes +
|
|
|
|
|
"\nLowResFOVY\n" + LowResFOVY +
|
|
|
|
|
"\ncameraFOVX\n" + cameraFOVX +
|
|
|
|
|
"\ncameraFOVY\n" + cameraFOVY +
|
|
|
|
|
"\nHighResimagePixelSize\n" + HighResimagePixelSize +
|
|
|
|
|
"\nCameraMoveX\n" + CameraMoveX +
|
|
|
|
|
"\nCameraMoveY\n" + CameraMoveY +
|
|
|
|
|
"\noverlapXZ\n" + overlapXZ +
|
|
|
|
|
"\noverlapY\n" + overlapY;
|
|
|
|
|
print(settings_file, settings_array);
|
|
|
|
|
File.close(settings_file);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
} //END
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -332,8 +366,10 @@ while (repeatFlag == 1){
|
|
|
|
|
totalBlocksLayer = 0;
|
|
|
|
|
totalBlocks = 0;
|
|
|
|
|
for (i = 0; i < nLayersY; i++ ) {
|
|
|
|
|
print("\nY layer" + (i+1) + ":");
|
|
|
|
|
numBlocksXX[i] = computeNumberBlocks("XX",blockSizeXZ,cameraFOVX,yVals[i],yEndVals[i],real_magnification);
|
|
|
|
|
numBlocksZZ[i] = computeNumberBlocks("ZZ",blockSizeXZ,cameraFOVX,xVals[i],xEndVals[i],real_magnification);
|
|
|
|
|
|
|
|
|
|
//Total number of XX and ZZ blocks
|
|
|
|
|
totalBlocksLayer = numBlocksXX[i]*numBlocksZZ[i];
|
|
|
|
|
totalBlocks = totalBlocks + totalBlocksLayer;
|
|
|
|
|
@@ -346,111 +382,6 @@ while (repeatFlag == 1){
|
|
|
|
|
return centresXXZZY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
function generateYConstantGrid(blockSizeXZ,blockSizeY,cameraFOVX,cameraFOVY,nLayersY,xVals,yVals,xEndVals,
|
|
|
|
|
yEndVals,real_magnification,XXcenterLowRes,ZZcenterLowRes,LowResimagePixelSize,HighResimagePixelSize,
|
|
|
|
|
LowResFOVY,CameraMoveX,CameraMoveY,imagewidth,imageheight,StartSlice) {
|
|
|
|
|
//Function that generates a constant grid for all Y layers.
|
|
|
|
|
|
|
|
|
|
Array.getStatistics(xVals, minx);
|
|
|
|
|
Array.getStatistics(yVals, miny);
|
|
|
|
|
Array.getStatistics(xEndVals, minw, maxw);
|
|
|
|
|
Array.getStatistics(yEndVals, minh, maxh);
|
|
|
|
|
|
|
|
|
|
//Compute number of blocks for most extense rectangle
|
|
|
|
|
numBlocksXXmax = computeNumberBlocks("XX",blockSizeXZ,cameraFOVX,miny,maxh,real_magnification);
|
|
|
|
|
numBlocksZZmax = computeNumberBlocks("ZZ",blockSizeXZ,cameraFOVX,minx,maxw,real_magnification);
|
|
|
|
|
|
|
|
|
|
//Generate the FIXED GRID (in Low Resolution pixels)
|
|
|
|
|
gridXX = newArray(numBlocksXXmax+1);
|
|
|
|
|
gridZZ = newArray(numBlocksZZmax+1);
|
|
|
|
|
gridXXcentres = newArray(numBlocksXXmax);
|
|
|
|
|
gridZZcentres = newArray(numBlocksZZmax);
|
|
|
|
|
|
|
|
|
|
gridXX[0] = miny;
|
|
|
|
|
gridZZ[0] = minx;
|
|
|
|
|
|
|
|
|
|
//First block is complete, so the center is computed without overlap taken into account
|
|
|
|
|
gridXXcentres[0] = gridXX[0] + cameraFOVX/2/real_magnification;
|
|
|
|
|
|
|
|
|
|
//Compute XX center and XX position for each volume of the grid
|
|
|
|
|
for (i=1; i < numBlocksXXmax; i++) {
|
|
|
|
|
gridXXcentres[i] = gridXXcentres[i-1] + blockSizeXZ/real_magnification;
|
|
|
|
|
gridXX[i] = gridXX[i-1] + blockSizeXZ/real_magnification;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//First block is complete, so the center is computed without overlap taken into account
|
|
|
|
|
gridZZcentres[0] = gridZZ[0] + cameraFOVX/2/real_magnification;
|
|
|
|
|
|
|
|
|
|
//Compute ZZ center and ZZ grid position for each volume of the grid
|
|
|
|
|
for (i=1; i < numBlocksZZmax; i++) {
|
|
|
|
|
gridZZcentres[i] = gridZZcentres[i-1] + blockSizeXZ/real_magnification;
|
|
|
|
|
gridZZ[i] = gridZZ[i-1] + blockSizeXZ/real_magnification;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Modify rectangle coordinates to fit the generated grid
|
|
|
|
|
//Central position of the corresponding FOV is given (in pixels)
|
|
|
|
|
posInZZ = newArray(nLayersY);
|
|
|
|
|
posFinZZ = newArray(nLayersY);
|
|
|
|
|
posInXX = newArray(nLayersY);
|
|
|
|
|
posFinXX = newArray(nLayersY);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < nLayersY; i++) {
|
|
|
|
|
|
|
|
|
|
for (j = 1; j < numBlocksZZmax+1; j++) {
|
|
|
|
|
if (xVals[i] < gridZZ[j]){
|
|
|
|
|
posInZZ[i] = gridZZcentres[j-1];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (k = j; k < numBlocksZZmax+1; k++) {
|
|
|
|
|
if (xEndVals[i] < gridZZ[k]){
|
|
|
|
|
posFinZZ[i] = gridZZcentres[k-1];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (jj = 1; jj < numBlocksXXmax+1; jj++) {
|
|
|
|
|
if (yVals[i] < gridXX[jj]){
|
|
|
|
|
posInXX[i] = gridXXcentres[jj-1];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (kk = jj; kk < numBlocksXXmax+1; kk++) {
|
|
|
|
|
if (yEndVals[i] < gridXX[kk]){
|
|
|
|
|
posFinXX[i] = gridXXcentres[kk-1];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Compute Y positions in stage coords
|
|
|
|
|
centresYreal = computeYCoords(nLayersY,YposLowRes,LowResFOVY,StartSlice,LowResimagePixelSize,CameraMoveY,
|
|
|
|
|
cameraFOVY,HighResimagePixelSize,blockSizeY);
|
|
|
|
|
|
|
|
|
|
//Compute number of blocks per layer and total number
|
|
|
|
|
numBlocksXX = newArray(nLayersY);
|
|
|
|
|
numBlocksZZ = newArray(nLayersY);
|
|
|
|
|
totalBlocksLayer = 0;
|
|
|
|
|
totalBlocks = 0;
|
|
|
|
|
for (i = 0; i < nLayersY; i++ ) {
|
|
|
|
|
numBlocksXX[i] = computeNumberBlocks("XX",blockSizeXZ,cameraFOVX,posInXX[i],posFinXX[i],real_magnification);
|
|
|
|
|
numBlocksZZ[i] = computeNumberBlocks("ZZ",blockSizeXZ,cameraFOVX,posInZZ[i],posFinZZ[i],real_magnification);
|
|
|
|
|
//Total number of XX and ZZ blocks
|
|
|
|
|
totalBlocksLayer = numBlocksXX[i]*numBlocksZZ[i];
|
|
|
|
|
totalBlocks = totalBlocks + totalBlocksLayer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Grid in stage coords giving (XX,ZZ,Y) for each volume, from top to bottom, left to right and up to down.
|
|
|
|
|
centresXXZZY = lowResPx2StageCoords(numBlocksXX,numBlocksZZ,totalBlocks,nLayersY,posInXX,posInZZ,
|
|
|
|
|
cameraFOVX,real_magnification,imagewidth,imageheight,XXcenterLowRes,ZZcenterLowRes,LowResimagePixelSize,
|
|
|
|
|
HighResimagePixelSize,CameraMoveX,blockSizeXZ,centresYreal);
|
|
|
|
|
return centresXXZZY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
function writeCoordsFile(centresXXZZY) {
|
|
|
|
|
|