//////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// ///////// ///////// ///////// 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. Last modified: 14/10/2019 */ //Check if there is a file containing the OptScan settings settings_path = getDirectory("home") + "/.optscan_settings.txt"; 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!\n"); //Ask the user for LowRes and HighRes acquisition parameters Dialog.create("Magnification and Camera Settings"); Dialog.addNumber("LowRes imagePixelSize:", LowResimagePixelSize); Dialog.addNumber("LowRes XX center:", XXcenterLowRes); Dialog.addNumber("LowRes ZZ center:", ZZcenterLowRes); Dialog.addNumber("LowRes Y position (B1):", YposLowRes); //Y of highest volume Dialog.addNumber("LowRes FOV Y:", LowResFOVY); Dialog.addNumber("HighRes FOV X:", cameraFOVX); Dialog.addNumber("HighRes FOV Y:", cameraFOVY); Dialog.addNumber("HighRes imagePixelSize:", HighResimagePixelSize); Dialog.addNumber("Camera movement X:", CameraMoveX); Dialog.addNumber("Camera movement Y:", CameraMoveY); 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.show(); LowResimagePixelSize = Dialog.getNumber(); XXcenterLowRes = Dialog.getNumber(); ZZcenterLowRes = Dialog.getNumber(); YposLowRes = Dialog.getNumber(); LowResFOVY = Dialog.getNumber(); cameraFOVX = Dialog.getNumber(); cameraFOVY = Dialog.getNumber(); HighResimagePixelSize = Dialog.getNumber(); CameraMoveX = Dialog.getNumber(); CameraMoveY = Dialog.getNumber(); overlapXZ = Dialog.getNumber(); overlapY = Dialog.getNumber(); projMethod = Dialog.getChoice(); // Real magnification real_magnification = LowResimagePixelSize/HighResimagePixelSize; // Set the starting slice waitForUser("Need for action", "Scroll to the desired starting slice and click OK when you are done!\n"); StartSlice = getSliceNumber(); //Set the ending slice waitForUser("Need for action", "Scroll to the desired ending slice and click OK when you are done!\n"); EndSlice = getSliceNumber(); //Compute number of acquisition layers in Y and layer size blockSizeY = cameraFOVY-overlapY; 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("Volume Size in XZ is " + (cameraFOVX/real_magnification) + " pixels"); print("Block Size in XZ is " + (blockSizeXZ/real_magnification) + " pixels"); //Check if image is binary binaryFlag = checkBinaryImg(); getDimensions(imagewidth, imageheight, stackchannels, stackslices, stackframes); auxInit = StartSlice; xVals = newArray(nLayersY); yVals = newArray(nLayersY); xEndVals = newArray(nLayersY); yEndVals = newArray(nLayersY); imgList = getList("image.titles"); //Get name of main stack imgName = imgList[0]; if (binaryFlag) { //For binary images //Generate maximum/minimum intensity projection images for (i = 0; i < nLayersY; i++) { auxEnd = auxInit+(cameraFOVY-overlapY)/real_magnification; if (auxEnd > EndSlice) { auxEnd = EndSlice; } selectWindow(imgName); if (projMethod == "MaxIP") { run("Z Project...", "start=" + auxInit + " stop=" + auxEnd + " projection=[Max Intensity]"); } else { run("Z Project...", "start=" + auxInit + " stop=" + auxEnd + " projection=[Min Intensity]"); } auxInit = auxEnd + 1; if (i > 0) { //Show previous rectangle makeRectangle(xVals[i-1], yVals[i-1], xEndVals[i-1]-xVals[i-1], yEndVals[i-1]-yVals[i-1]); } waitForUser("Need for action", "Draw a rectangle around the object and click OK when you are done!\n"); getSelectionBounds(x, y, width, height); //Save definition of all rectangles xVals[i] = x; yVals[i] = y; xEndVals[i] = x + width -1; yEndVals[i] = y + height -1; } //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 } else { //For non binary images //Generate maximum/minimum intensity projection images and surround object with bounding box for (i = 0; i < nLayersY; i++) { auxEnd = auxInit+(cameraFOVY-overlapY)/real_magnification; if (auxEnd > EndSlice) { auxEnd = EndSlice; } selectWindow(imgName); if (projMethod == "MaxIP") { run("Z Project...", "start=" + auxInit + " stop=" + auxEnd + " projection=[Max Intensity]"); } else { run("Z Project...", "start=" + auxInit + " stop=" + auxEnd + " projection=[Min Intensity]"); } auxInit = auxEnd + 1; if (i > 0) { //Show previous rectangle makeRectangle(xVals[i-1], yVals[i-1], xEndVals[i-1]-xVals[i-1], yEndVals[i-1]-yVals[i-1]); } waitForUser("Need for action", "Draw a rectangle around the object and click OK when you are done!\n"); getSelectionBounds(x, y, width, height); //Save definition of all rectangles xVals[i] = x; yVals[i] = y; xEndVals[i] = x + width -1; yEndVals[i] = y + height -1; } //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); } //Print .txt file with the generated coordinates writeCoordsFile(centresXXZZY); //Last dialog and restart flag repeatFlag = finalDialog(centresXXZZY, nLayersY); //Close all windows except stack 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 //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////// FUNCTIONS ///////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// function checkBinaryImg() { //Function that checks if the current image is binary //Use bitdepth() to adapt to 16bit getHistogram(values, counts, 256); gvs = 0; binary_flag = true; for (i=0;i<=255;i++){ if (counts[i]>0) { gvs++; } if (gvs>2) { binary_flag = false; break; } } return binary_flag; } //////////////////////////////////////// function computeNumberBlocks(axis,blockSize,cameraFOV,StartPoint,EndPoint,real_magnification) { //Function that computes the number of blocks in the given axis direction. delta = (EndPoint-StartPoint)*real_magnification; //Include the fact that the first Y layer is complete if (delta <= cameraFOV) { nBlocks = 1; //Number of heights in the new scan }else{ ratio = (delta - cameraFOV)/blockSize; nBlocks = floor(ratio) + 2; //Number of heights in the new scan } print("Number of " + axis + " blocks: " + nBlocks); return nBlocks; } //////////////////////////////////////// function computeYCoords(nLayersY,YposLowRes,LowResFOVY,StartSlice,LowResimagePixelSize,CameraMoveY,cameraFOVY, HighResimagePixelSize,blockSizeY) { //Function that computes the Y positions in stage coordinates //Y position of center of first volume in stage coords centresYreal = newArray(nLayersY); centresYreal[0] = YposLowRes - (LowResFOVY/2 - StartSlice) * LowResimagePixelSize + CameraMoveY + (cameraFOVY*HighResimagePixelSize)/2; //Y position of each of the layers in stage coords for (l = 1; l < nLayersY; l++) { centresYreal[l] = centresYreal[l-1] + (blockSizeY * HighResimagePixelSize); } return centresYreal; } //////////////////////////////////////// function lowResPx2StageCoords (numBlocksXX,numBlocksZZ,totalBlocks,nLayersY,yVals,xVals,cameraFOVX, real_magnification,imagewidth,imageheight,XXcenterLowRes,ZZcenterLowRes,LowResimagePixelSize, HighResimagePixelSize,CameraMoveX,blockSizeXZ,centresYreal) { //Function that transforms low resolution pixel coordinates to stage coordinates. //Grid in stage coords giving (XX,ZZ,Y) for each volume, from top to bottom, left to right and up to down. centresXXZZY = newArray(totalBlocks*3); //Counter for filling up the last array cnt = 0; for (ll = 0; ll