Search code examples
image-processingjythonimagej

Mathematical Operations on an Image Stack in ImageJ (Fiji)


I am writing an imageJ/Fiji plugin in Jython using the pydev plugin in eclipse.The plugin will be the ImageJ version of an already existing denoising software called CANDLE written as a matlab program. Changing the value of every pixel(voxel) of an image in matlab is trivial:

InputImage = 2 * sqrt(InputImage + (3/8));
Median3DFilteredImage = 2 * sqrt(Median3DFiltered + (3/8));

Here "InputImage" and "Median3DFilteredImage" are 3D Matrices, with the last dimension being time (slices). To reproduced the following operation on an ImageJ image, I had to employ two for loops, one to iterate through the image slices (3rd dimension) and the other loop to iterate over all the pixels in a particular slice:

medFiltStack = medianFilteredImage.getStack()   
newMedFiltStack = ImageStack(medianFilteredImage.width, medianFilteredImage.height)

InputStack = InputImage.getStack() 
newInputStack = ImageStack(InputImage.width, InputImage.height)

for i in xrange(1 , medianFilteredImage.getNSlices() + 1):

 ip = medFiltStack.getProcessor(i).convertToFloat()
 ip2 = InputStack.getProcessor(i).convertToFloat()

 pixels = ip.getPixels()
 pixels2 = ip2.getPixels()

 for j in xrange (len(pixels)):

     pixels[j] = 2 * javaMath.sqrt(pixels[j] + (3.0/8.0)  )
     pixels2[j] = 2 * javaMath.sqrt(pixels2[j] + (3.0/8.0)  )

 newMedFiltStack.addSlice(ip)
 newInputStack.addSlice(ip2)    

medianFilteredImage = ImagePlus("MedianFiltered-Image", newMedFiltStack)
InputImage = ImagePlus("Input-Image", newInputStack)

My question is as follows: Is there a way to perform mathematical operations on an image Stack, i.e. on every pixel (voxel) in the image stack, without having to write code that explicitly visits every pixel in every slice of the image, i.e. for loops. It just seems to be a very primitive way of going about it and I am wondering if there isn't an optimal way of doing this operation. I also had to work with copies and then gave the new images the same names as before as opposed to working with the original images and editing them directly. So is there a way to edit the pixel values of the original images rather than copies of the images? Any help would be appreciated as there are plenty of more math operations that I have to perform. It would be super useful to find a way to do mathematical operations on images in an optimal way both in terms of the amount of code and if possible, in terms of speed.


Solution

  • In pure ImageJ 1.x, the answer is: no, there's no other way than to visit every slice and get its ImageProcessor. That's the way how ImageJ1 deals with its limited number of dimensions (z, time, channel), you always have a (Hyper-)Stack of 2D planes.

    There is however a more powerful way of dealing with n-dimensional images called ImgLib, which is included into Fiji together with ImageJ2.

    To avoid re-inventing the wheel, you should have a look a Jean-Yves Tinevez's great plugin Image Expression Parser. Use it headlessly with Fiji, or just have look at its source code (it uses a previous version though, ImgLib1, but the idea is the same: you avoid hard-coding the dimensions by using Java generics), see e.g. for the sqrt function:

    public final <R extends RealType<R>> float evaluate(final R alpha) {
        return (float) Math.sqrt(alpha.getRealDouble());
    }