Writing a macro for automatic processing with ImageProcessor thresholding

Hi, I’ve been working on this problem for a little while (see here and here) and now I think my problem lies in (my use of) ImageJ code.

I wish to acquire ~1600 images per second, process them to find the centre coordinates of a bead, and save these along with the time for the frame. I have a script in micro-manager which almost does this. It acquires the image, puts it into an ImageJ ImageProcessor object, and then uses a ShortStatistics object to calculate a centre of mass. Problem is, this centre of mass is skewed by the bright background, and always very close to the centre of the image (within 40nm of the centre for a 4um wide field of view).

I have attempted to alleviate this using thresholding - the bead is very bright, and Otsu thresholding does a good job of removing the background. However the ImageProcessor does not create a mask, meaning the ShortStatistics cannot use this. I have included some code from my micro-manager script below to illustrate this:

pixels = mmc.popNextImage(); // Micro-manager's way of giving me the pixel values as a short[]
ip.setPixels(pixels);
ip.setAutoThreshold("Otsu dark");
ss = new ShortStatistics(ip, ij.process.ImageStatistics.CENTER_OF_MASS, null);

a = ip.getMaskArray();
print(a); // null
print("IP thresh " + ip.minThreshold + " " ip.maxThreshold); // Prints limits with sensible values (~1100 and 65000 normally)
print("SS thresh " + ss.lowerThreshold + " " + ss.upperThreshold); // Prints NaN for both limits

The last 4 lines above are my troubleshooting - I cannot get a mask array from the ImageProcessor, but it does have thresholds set. The ShortStatistics does not have thresholds set.

How can I either: Make the ImageProcessor return the mask array correctly (needed for Shortstatistics to use the thresholding)? Or set thresholds to the ShortStatistics?

You need to use the createMask() method on the imageProcessor. Which returns a ByteProcessor.

ip.setAutoThreshold("Otsu dark");
ByteProcessor mask = ip.createMask();
1 Like

I see, thanks for your answer. And then how do I use this ByteProcessor mask to apply in the ShortStatistics?

From what I can see this will give me a separate object that is not accessible to the ShortStatistics, and my goal is to have this mask apply within ShortStatistics so background pixels are not used in the centre of mass calculation.

There is the ByteStatistics subclass of the ImageStatistics.

I understand that. The ByteProcessor created by ip.createMask() only contains the binary mask (either 0 or 255), right? Meaning any information in the pixel values within the ShortProcessor is not present within the ByteProcessor (mask)?

Yes that is the idea of thresholding, to generate a binary mask. But you can use the binary mask then for all the operations you might want.

Create ROIs to do measurements on a different image.
Multiply them to another image to mask out unwanted signal (https://imagej.nih.gov/ij/developer/api/ij/plugin/ImageCalculator.html).
Measure the center of mass of an object based on the binary image.

Thanks for your patience, image processing in ImageJ is new to me. Is this code below correct?

imp1 = ImagePlus(); 
imp2 = ImagePlus(); // These will be outside of a loop to reduce overhead from initialising

pixels = mmc.popNextImage(); // Micro-manager's way of giving me the pixel values as a short[]
ip.setPixels(pixels);
ip.setAutoThreshold("Otsu dark");
imp1.setProcessor(ip);
imp2.setProcessor(ip.createMask());
imp1 = ImageCalculator.run(imp1,imp2,"multiply");

ss = new ShortStatistics(imp1.getProcessor(), ij.process.ImageStatistics.CENTER_OF_MASS, null);

I believe that this initialises two empty ImagePlus objects, then once I’ve got the ImageProcessors for the pixels and the mask, I assign these to the ImagePlus, use ImageCalculator as you told me, and then get the result and use it in ShortStatistics?

You are doing this in beanshell? I came up with a working version here:

import ij.*;
import ij.process.*;
import ij.gui.*;
import java.awt.*;
import ij.plugin.*;
import ij.ImagePlus;
import ij.plugin.ImageCalculator;
import ij.process.ImageProcessor;
import ij.process.ImageStatistics;
import ij.process.ShortStatistics;
import ij.io.Opener;

ImagePlus testImage = IJ.openImage("http://imagej.nih.gov/ij/images/boats.gif");
ImagePlus imp1 = new ImagePlus();
ImagePlus imp2 = new ImagePlus();// These will be outside of a loop to reduce overhead from initialising

ImageProcessor ip = testImage.getProcessor();
ip.setAutoThreshold("Otsu dark");
imp1.setProcessor(ip);
imp2.setProcessor(ip.createMask());
ImageCalculator calculator = new ImageCalculator();
ImagePlus impResult = calculator.run( "multiply create 32-bit", imp1, imp2);
impResult.show();

ShortStatistics ss = new ShortStatistics(impResult.getProcessor().convertToShort(true), ImageStatistics.CENTER_OF_MASS, null);

The thing is a bit more complicated than that. If you want to multiply two images ideally you would like to create a 32-bit result. This you can then cast into Short with scaling enabled. For whatever reason I was not able to get the static run method of the ImageCalculator running.

Yes, I am using beanshell. That’s fantastic, thanks for all your help! Why does putting the result into 32-bit matter? If it’s the risk of Short overflow, would it not make sense to scale the ByteProcessor pixels to [0 1] instead of [0 255]?

Sorry forgot to explain. Yes when you do this here then the result would be otherwise clipped.

I guess you can try to do that do that then in your program. That is just how a mask is defined in ImageJ.