Thresholding as a percentage of intensity histogram in a macro

I’m new to writing macros and attempting to process a whole batch of images. After several processing steps I would like to apply a threshold to each image and have it be set such that the same percent of pixel intensities are thresholded every time. For reference, if you go to Image>Adjust>Threshold the box that comes up has a percent on the left side below the space with the slider graphic and histogram and you can see what percent of the pixels you are including/excluding.

I’ve found some different algorithms that could be used here: and one might pass these along but as far as I can tell none are quite what I’m looking for in terms of approach. I’d like to either have the threshold dialog box just open for every image so I can set each image manually to the same percent thresholded, or ideally specify in the macro what threshold percentage should be used and just have it run that way. Any advice would be much appreciated!


I found an older thread which might be helpful:

However you might be able to calculate the percentage from the histogram values

and then set the threshold values according to the results for each loaded images.

To set the theshold in a marco:

Custom histogram example:

It also might be helpful to record useful commands you need for your macro in the macro recorder of ImageJ:


Thanks a lot, that first link was very helpful. I did hit a bit of a snag though and I’m wondering if you might be able to spot my mistake, I can’t seem to figure out why it wont work.

Firstly, I copied over that code from the first link, pasted below, and it seems to be a good solution to finding the cutoff value above which only the desired percent of pixels remain. What I attempted to do was to use this value in the setThreshold function by saying setThreshold(0,i).

I confirmed firstly that the output from the script, i, is correct by using the output value in the Image>Adjust>Threshold sliders to check if the surviving percent is indeed what I had asked for in the macro. This works great and when I move the sliders to i, the desired percent of the image is thresholded. However, when I added the line setThreshold(0,i) the result is vastly different- basically the entire image is thresholded, nothing survives the cutoff- and I’m at a loss why would be.

run("Duplicate...", "title=duplicate");
// Input a tissue threshold percentage 0-100
tissueThreshPerc = 98;
nBins = 256;
getHistogram(values, count, nBins);
size = count.length;
// find culmulative sum
cumSum = 0;
for (i = 0; i<count.length; i++)
  cumSum += count[i];
//totalNumberOfPixels = getWidth() * getHeight()
tissueValue = cumSum * tissueThreshPerc / 100;
// cumulative sum of before
cumSumValues = count;
for (i = 1; i<count.length; i++)
  cumSumValues[i] += cumSumValues[i-1];
// find tissueValue
for (i = 1; i<cumSumValues.length; i++)
  if (cumSumValues[i-1] <= tissueValue && tissueValue <= cumSumValues[i])
    // output tissue threshold:

Can please post that script again because it is incomplete. Use the preformatted text

action to wrap it.

Are you working only with greyscale images (8-bit) only?

Sometimes (float images) the display range is not the same as the pixel range, see:


Looks like ctrueden reformatted the script for me already, let me know if can see it.

So the script works with 8-bit as is, it runs into problems with 16-bit and changing the nbins to 65536 doesn’t help so in testing this out I’ve converted the sample images to 8-bit which seems to solve the issue. For the purposes of this analysis I’m not concerned with using 8-bit images so I’ll probably just stick with that moving forward.

What confuses me is that as written the script will identify whatever value the cutoff is correctly. For instance, it will say 98% of the pixels are below i=180 and I can input 180 into the manual threshold dialog box and it shows that I’m thresholding the expected percent. It seems to me that when I try to pass this into the setThreshold function there is something else going on because the resultant thresholded image is different than the one I did manually. I think that floating point is a concern for 32 bit only, and by adjusting the window/level by resetting the min/max I’ll still have the same issue where even if the correct threshold is identified, passing it to the threshold function will cause some error.

Without having read this thread carefully, I have a quick question: did you intend for the setThreshold(0, i); at the end of your for loop to happen inside the if statement? Or outside of it? As written, it happens outside, which might explain the behavior you’re seeing. I recommend always adding curly braces around your if statements to avoid these sorts of errors.

I’ve not written any macros before, mostly I work with R, but from what it looks like, the setThreshold(0,i) is inside the curly braces and is inside the if statement. Is there some problem with passing the variable i as an argument to setThreshold?

Also, just to try it out, I moved the setThreshold(0,i) inside and outside of curly braces just to see if it made a difference and in both cases the threshold did not work. However, when printed i, as it is written above, then ran the script again and input the integer value of i into the code in place of i, then the threshold was set properly.

Well there is a difference you set curly braces. At least for me it was like Curtis suggested in the if condition. If I do not set the braces the result is as your description. Here the corrected lines:

if (cumSumValues[i-1] <= tissueValue && tissueValue <= cumSumValues[i])
  // output tissue threshold:

Apropos you don’t need to calculate the cumSum (first loop) because that is equal to the amount of pixels (totalNumberOfPixels).

But is the cutoff of a pixel distribution really what you want to do leading to different percentage cutoffs?

1 Like