Looping through RGB pixels in ImageJ2 in an ImageStack

We have created a bit of ImageJ2 code that loops through all pixels of an Image and gets the Red, Green and Blue value from those. It assumes an uncompressed image with 3 dimensions XYC. In order to be able to work with Image Stacks, we would also like to be able to loop through XYCZ images. The method we are using (Converters.mergeARGB ) however assumes the last dimension is the Color Channel.

We have been trying to find something that allows us to loop through the Z dimension as well, so we can process Image stacks, but are failing to find something useful.

Can anyone suggest how to build this loop? Perhaps @imagejan?

The code we have now is:

        RandomAccessibleInterval<ARGBType> mergeARGB = Converters.mergeARGB(img, ColorChannelOrder.RGB);

        Img<UnsignedByteType> outputImg1 = img.factory().create(mergeARGB);
        Img<UnsignedByteType> outputImg2 = img.factory().create(mergeARGB);
        Img<UnsignedByteType> outputImg3 = img.factory().create(mergeARGB);

        LoopBuilder.setImages(mergeARGB, outputImg1, outputImg2, outputImg3).forEachPixel(
                (input, out1, out2, out3) -> {
                    int rgba = input.get();

This code is used in the Colour Deconvolution for ImageJ2 plugin that we are trying to get to a 1.0 version:

More discussion on this plugin in this forum thread:

Thanks in advance!

Martin & Boudewijn

1 Like

May I ask why you use mergeARGB at all when:

  1. you always have 8 bit per channel (UnsignedByteType) in your input image anyway, and
  2. you split the red, green and blue values again inside your loop (e.g. ARGBType.red(rgba)) ?

You could instead use three inputs in the LoopBuilder, one for each channel, that you can easily get using Views.hyperSlice, independent on which axis index corresponds to your CHANNEL axis.

In addition, I’d suggest using Dataset instead of ImgPlus.

Here’s a Groovy script to illustrate what I mean (doing the trivial operation of copying each of the three channels to the respective outputs):

#@ Dataset input
#@output out1
#@output out2
#@output out3

import net.imagej.axis.Axes
import net.imglib2.view.Views
import net.imglib2.loops.LoopBuilder
import net.imglib2.type.numeric.integer.UnsignedByteType

// Get the index of the CHANNEL axis
channelIndex = input.dimensionIndex(Axes.CHANNEL)

// Get the three channel hyperslices
channelR = Views.hyperSlice(input, channelIndex, 0)
channelG = Views.hyperSlice(input, channelIndex, 1)
channelB = Views.hyperSlice(input, channelIndex, 2)

out1 = input.factory().create(channelR)
out2 = input.factory().create(channelR)
out3 = input.factory().create(channelR)

println input.numDimensions()
LoopBuilder.setImages(channelR, channelG, channelB, out1, out2, out3).forEachPixel( {
	r, g, b, o1, o2, o3 -> {
		o1.set(r.get())
		o2.set(g.get())
		o3.set(b.get())
	}} as LoopBuilder.SixConsumer
)
2 Likes

Love it, @imagejan, will definitely have a look at that.

Sorry for the long wait, I was working on understanding the code better together with a mathematician. Making some headway at that in this branch:

Will definitely also start working this concept in there, no need at all to do a merge indeed!