Randomly select and extract grid boxes

imagej
macro
roi

#1

I am looking for a way to do the following:

  1. generate a grid overlay (boxes)
  2. assign number to each box in the grid
  3. then randomly select a set number of boxes (e.g. select 10 out of 100 grid boxes) from the grid
  4. crop/extract each randomly selected grid box
  5. overlay random point on each randomly selected box from the grid.

Is this possible in ImageJ?


Selecting grid-created ROIs based on an Initially Defined ROI
#2

Hi @sjohnson,

Welcome to the forums!

Yes! I have made a script that I think works well. To run the script, copy and paste it in to the script editor (use the [ key to open the script editor) and select the language as “IJ1 Macro”. It is commented, but feel free to ask any questions.

    roiManager("reset");

    mainWindow = getTitle();
    close("\\Others"); // close all but selected window
    h = getHeight;
    w = getWidth;
    area = w*h;

    gridNum = getNumber("How many grid boxes in total?",100);
    boxArea = area/gridNum; // area of each grid box
    boxSide = sqrt(boxArea); // side length of each grid box
    numBoxY = floor(h/boxSide); // number of boxes that will fit along the width
    numBoxX = floor(w/boxSide); // "" height
    remainX = (w - (numBoxX*boxSide))/2; // remainder distance left when all boxes fit
    remainY = (h - (numBoxY*boxSide))/2;
    for (i=0; i<numBoxY; i++) { // draws rectangles in a grid, centred on the X and Y axes and adds to ROI manager
    	for (j=0; j<numBoxX; j++) {
    		makeRectangle((remainX + (j*boxSide)), (remainY+(i*boxSide)), boxSide, boxSide); 
    		roiManager("add");
    	}
    }

    grids = getNumber("How many grid boxes to select?", 10);
    boxID = newArray(grids); 
    gridTotal = roiManager("count"); // not neccessarily as many boxes in the grid as specified due to the constraint of # squares not fitting the width/height

    for (i=0; i<grids; i++) { // generate random number to select a square in the grid, makes sure the square has not already been chosen
    	randNum = round(random*gridTotal);
    		for (j=0; j<boxID.length; j++) {
    			if (randNum == boxID[j]) {
    				randNum = round(random*gridTotal);
    				j=0;
    			}
    		}
    boxID[i] = randNum; 
    selectWindow(mainWindow); 
    roiManager("select", boxID[i]);
    run("Duplicate...", "duplicate"); // duplicates the random square in the grid box
    rename("Grid " + i);
    }

    for (i=0; i<grids; i++) {
    	selectWindow("Grid " + i);
    	pointX = round(random*boxSide); // random X coord
    	pointY = round(random*boxSide); // random Y coord
    	makePoint(pointX, pointY);
    }

I don’t know if there is already a function to make a grid, or code online (I assume you searched), but this is the best I could come up with.

EDIT: turns out there is a grid plugin - https://imagej.nih.gov/ij/plugins/grid.html (other examples of grid plugins/macros are available from this page also).


#3

Thanks very much! Now I just need to figure out how to extract the random grids to a file. Much appreciated!


#4

Do you mean you need the cropped grids to be saved in an automated way? As separate files, or a stack?


#5

Yes I would like to save the cropped grids in a separate file.


#6

Hi Selena,

You could add a saveAs function to the ‘for’ loop, I’ve assumed you want to save them as .tiff files here:

    for (i=0; i<grids; i++) {
        	selectWindow("Grid " + i);
        	pointX = round(random*boxSide); // random X coord
        	pointY = round(random*boxSide); // random Y coord
        	makePoint(pointX, pointY);
            saveAs("Tiff", outputDir + mainWindow + "Grid" + i);
        }

You need to define outputDir, this can be defined at the start of your macro, by the user, or could be retrieved from the file you have opened most recently (assuming it is in the directory you want to save to).

For example:

outputDir = getDirectory("Choose folder to save grids to");
outputDir = File.directory;

If you want to repeat this macro for multiple files in a folder or subfolders, you may want to consider the template found in the script editor (Templates > IJ1 Macro > Process Folder). You will see there that you can insert your code to be run on all the files in a single folder + output the grids to another folder of your choice (outputDir).

Best,

Rob


#7

Hi Rob and/or sjohnson (or anyone) -

I am needing something very similar to this, but hopefully not as complicated. I am extremely new to ImageJ and am also not knowledgeable about macros, scripting, etc. I am really hoping this script can be easily modified to do what I need.

At step 3 is where the functionality deviates from sjohnson’s original question:

1.generate a grid overlay (boxes)
2.assign number to each box in the grid
3.measure the integrated density of each box in the grid (obviously with each box’s number corresponding to it’s measured density)
4.output this into an excel spreadsheet, or be able to copy and paste from the measurements window into an excel spreadsheet.

I would be SO grateful for help with this modification, or if you guys (or others) know of something that’s already been done that accomplishes this purpose and can direct me to where that is described, that would be fantastic.

Thank you!


#8

Good day,

I understand that you are not interested in random positions.

Have a look at this recent post and thread:
http://forum.image.sc/t/how-could-i-get-mean-grayvalues-of-each-grid/10589/9?u=herbie

Regards

Herbie


#9

Hi Herbie -

Thank you for your very fast reply!

Yes, your linked thread looks to be almost exactly what I need. The only difference is probably the size of the boxes (I can at least change that myself), and I need the integrated density of each box, rather than the average mean. So, I don’t need the 3 separate classifications, i just need straight integrated density values for each box.

How would your macro be modified to accomplish this?


#10

Good day,

it is not a good idea to use code that one doesn’t understand. So please learn to understand ImageJ-macro code which is really easy.

Then you will be able to perform the minor changes that are needed for your project.

Good luck

Herbie

ADDITION:
Replace these code lines

if ( mean > 0 ) {
  mnP = mean;
  mnN = NaN;
} else {
  mnP = NaN;
  mnN = mean;
}
setResult( "Pixel", nResults-1, N );
setResult( "Pos_Mean", nResults-1, mnP );
setResult( "Neg_Mean", nResults-1, mnN );

by this line

setResult( "Integral", nResults-1, N*mean );

and please report back why this works and if not, what you think the problem may be.