Displaying ARGBType images with SCIFIO

Hi all,

what could be done to display ARGBType images via SCIFIO?

ImageJ ij = new ImageJ();
Img<ARGBType> argb = ij.op().create().img(new FinalInterval(10, 10), new ARGBType());

Currently returns java.lang.ClassCastException: net.imglib2.type.numeric.ARGBType cannot be cast to net.imglib2.type.numeric.RealType.

This works:


AFAIK: ImageJ2 expects a channel img, not an image of type ARGBType. If that is true, the following should work: Try converting RandomAccessibleInterval<ARGBType> into individual RAI per channel with the ARGBChannelSamplerConverter and then Views.stack the indivdiual channels into a channel RAI.

Sorry for no code example, on the run.


Thanks for the pointers! :slightly_smiling_face: Let me rephrase my question: would it be desired to support this in scifio directly and if yes what needs to be done?

1 Like

I would think so but I am probably not the right person to answer that question. cc’ing @ctrueden and @imagejan to comment or ping other people as appropriate.

In particular, I am not familiar with the code base and ij.ui().show seems to be very generic and accepts Object as parameter.

I decided at the beginning of ImageJ2 not to support packed RGB/ARGB, in favor of treating channel as a proper dimension. But there has been a lot of pushback over this limitation over the years, and the fact is that ImgLib2 does have the ARGBType, so I think now we should support it.

I would rather not. It would complicate matters. It is very convenient if callers can assume they’ll always get channel as a dimension (and in the case of interleaved RGBRGBRGB… values, channel will simply be the first dimension before X and Y). We did not want callers to need to special-case handling of ARGBType, which is a very weird type breaking otherwise consistent typing rules (unlike every other bit depth, it does not implement RealType!).

If SCIFIO returned images using ARGBType, what about other orderings? What about RGBType? BGRType? RGBAType? I’d much rather not create these. Treating channel as a dimension, with some metadata somewhere about which channel index goes with each color, is more general.

This is true. We should support display of ARGBType images. Adapting the ARGBType image internally as @hanslovsky suggested using an ARGBChannelSamplerConverter plus Views.stack should certainly be doable.

@frauzufall If you need further guidance or have questions, I’d be willing to dig deeper and make more concrete suggestions about how to proceed. (Off the top of my head, without reading the code again, I am not sure.)

Edit: A related topic here which is its own can of worms is alpha. This channel should really be treated differently somehow, but I am not sure how. It is quite unfortunate that ImageJ (both 1 and 2) currently do not support alpha in a useful way. Future design work to be done there!

1 Like

Hey @ctrueden
The IO methods in SCIFIO also don’t support writing ARGB images it appears.
So what should a fella do if they have an ARGB image in Imglib2 and want to store it?

Not sure that this will work but I would try converting into channel images as discussed above.

Thanks for the thread,
In the end, what I opted for was to wrap my image into an ImagePlus (of ImageJ1) and then store it as an
RGB image.
I prefer this modest solution over creating a file for each channel, because after all we should know that people want to use color images in their publications and so on, and having only imagej figures is not sufficient.

Thanks again for the response,

I am glad you found a solution that works for me.

As far as I know, ScifIO should save a multi-channel image as a single file (instead of one file per channel), but ImageJ2/ScifIO experts may know better (cc @ctrueden @imagejan)

The IO class methods as well as the services return an exception saying that the format is not supported.

Wish it were possible!

You mean when attempting to save an image of type ARGBType directly, correct?

But you can convert it as discussed above. I took a stab at writing code that would work. Unfortunately, I was not quite successful. I would need to dig into the SCIFIO code more deeply to understand exactly what goes wrong. But for the sake of good communication, and because I don’t have time to dig any further right now, I post my almost-working script below.

Again, it is not quite working!

#@ Context ctx

import net.imglib2.img.ImgView
import net.imglib2.img.array.ArrayImgs
import net.imglib2.converter.Converters
import net.imglib2.view.Views
import io.scif.img.ImgSaver
import io.scif.config.SCIFIOConfig

// create an image
image = ArrayImgs.argbs(50, 60)
println("Created image: " + image)

// draw something
def fillRect(rai, x1, y1, x2, y2, color) {
	access = rai.randomAccess()
	for (j = y1; j <= y2; j++) {
		access.setPosition(j, 1)
		for (i = x1; i <= x2; i++) {
			access.setPosition(i, 0)
fillRect(image, 30, 21, 44, 36, (int) 0xffc09030) // A=255 R=192 G=144 B=48

// convert packed ARGBType to UnsignedByteType stack
a = Converters.argbChannel(image, 0)
r = Converters.argbChannel(image, 1)
g = Converters.argbChannel(image, 2)
b = Converters.argbChannel(image, 3)
xycStack = Views.stack(r, g, b)
xycImage = ImgView.wrap(xycStack, null)
// NB: I also played with permuting the axis orders; e.g.:
//yxcStack = Views.permute(xycStack, 0, 1)
//cxyStack = Views.permute(yxcStack, 0, 2)
//cxyImage = ImgView.wrap(cxyStack, null)

// save it
saver = new ImgSaver(ctx)
config = new SCIFIOConfig().imgSaverSetWriteRGB(true)
saver.saveImg("/Users/curtis/Desktop/argb.png", xycImage, config) // NB: Does not detect RGB :-(

If anyone has time to dig a little more to figure out what’s going on, the easiest way is probably to use the debugger of an IDE, put a breakpoint in ImgSaver and/or APNGFormat, to see what goes wrong. From my brief attempts, I suspect a blanket issue in ImgSaver. Somehow the imgSaverSetWriteRGB(true) is not sufficient to tip off the writer to save interleaved RGB data.


Hi everyone,
Just to give a proper conclusion to this thread, here’s a sample code that demonstrates how to save ARGB using ImageJ1 and and using ImgSaver.

	Img<ARGBType> img = new ArrayImgFactory<ARGBType>(new ARGBType()).create(50,50);

	Cursor<ARGBType> cursor = img.cursor();
	while (cursor.hasNext()) {
		ARGBType color = new ARGBType(ARGBType.rgba(255, 0, 255, 0));

	// As we know, ARGBType cannot be stored directly, because it's not derived
	// from RealType. Moreover, it was desired that a channel be stored as a proper 
	// dimenstion.
	// ij.io().save(img, "/home/masoud/Documents/SampleImages/a.tiff"); //This line throws exception.

	// However, ImageJ1 allows for direct storage.
	ImagePlus imagePlus = ImageJFunctions.wrapRGB(img, "ImageARGB");
	File imagej1 = new File("./ImageJ1RGB.tif");
    FileSaver fileSaver = new FileSaver(imagePlus);
	// The second way is to extract each channel, as a dimension in the image,
	// and then wrap them together to form a stack, and then store.
	WriteConvertedRandomAccessibleInterval<ARGBType, UnsignedByteType> r = Converters.argbChannel(img, 1);
	WriteConvertedRandomAccessibleInterval<ARGBType, UnsignedByteType> g = Converters.argbChannel(img, 2);
	WriteConvertedRandomAccessibleInterval<ARGBType, UnsignedByteType> b = Converters.argbChannel(img, 3);

	RandomAccessibleInterval<UnsignedByteType> ra = Views.stack(r,g,b);

	// Note: We can circumvent all of this, using argbChannels:
	// RandomAccessibleInterval<UnsignedByteType> ra1 = Converters.argbChannels(img);

	// We can also remerge these images and form an ARGB.
	RandomAccessibleInterval<ARGBType> raARGB = Converters.mergeARGB(ra, ColorChannelOrder.RGB);
	File imagej2 = new File("./ImageJ2RGB.tif");

	// Creating the corresponding image interface.
	Img<UnsignedByteType> imgRGBChannel = Util.getSuitableImgFactory(ra, new UnsignedByteType()).create(ra);
	LoopBuilder.setImages(imgRGBChannel, ra).forEachPixel(UnsignedByteType::set);
	ImgSaver imgSaver = new ImgSaver();
	imgSaver.saveImg(imagej2.getAbsolutePath(), imgRGBChannel);