How to wrap any kind of ImagePlus to an imglib2 Img<FloatType>

Hi all,

I am looking for a generalised way of wrapping any kind of ImagePlus (32bit, 16bit, 8bit) to a datastructure of kind Img{FloatType>… There are some wrap functions in the net.imglib2.img.display.imagej.ImageJFunctions class, but they appear to cause crashes depending on what ImagePlus I handover. The following code crashes in two (marked) lines, when wrapping the test image.

import ij.IJ;
import ij.ImagePlus;
import ij.process.ImageConverter;
import ij.process.ImageProcessor;
import net.imglib2.RandomAccess;
import net.imglib2.img.Img;
import net.imglib2.img.display.imagej.ImageJFunctions;
import net.imglib2.type.numeric.real.FloatType;


public class WrappingTest {
    
    public static void testWrappers()
    {
        for (int i = 2; i < 3; i++)
        {
            ImagePlus imp = IJ.openImage("http://imagej.nih.gov/ij/images/leaf.jpg");
            switch (i)
            {
            case 0:
                System.out.println("32 Bit");
                new ImageConverter(imp).convertToGray32();    
                break;

            case 1:
                System.out.println("16 Bit");
                new ImageConverter(imp).convertToGray16();    
                break;

            case 2:
                System.out.println("8 Bit");
                new ImageConverter(imp).convertToGray8();    
                break;
                
            }
            
            ImageProcessor ip = imp.getProcessor();
            Img<FloatType> convertFloatImg = ImageJFunctions.convertFloat(imp);
            Img<FloatType> wrapFloatImg = ImageJFunctions.wrapFloat(imp); 
            Img<FloatType> wrapImg = ImageJFunctions.wrap(imp);
            
            long[] position = {333,127};
            
    
            RandomAccess<FloatType> convertFloatRA     = convertFloatImg.randomAccess();
            RandomAccess<FloatType> wrapFloatRA     = wrapFloatImg.randomAccess(); //crash here (NullPointerException) when processing a 16 or 8 bit image
            RandomAccess<FloatType> wrapRA             = wrapImg.randomAccess();
    
            convertFloatRA.setPosition(position);
            wrapFloatRA.setPosition(position);
            wrapRA.setPosition(position);
            
            System.out.println("Imp value at defined position: " + ip.getPixelValue((int)position[0], (int)position[1]));
            System.out.println("convertFloatRA value at defined position: " + convertFloatRA.get().get());
            System.out.println("wrapFloatRA value at defined position: " + wrapFloatRA.get().get());
            System.out.println("wrapRA value at defined position: " + wrapRA.get().get()); //crash here (ClassCastException) when processing an 8 bit image
        }
        System.out.println("Done. Bye.");
    }
    
    public static void main(String[] args) {
        testWrappers();
    }
}

Is there a way to get a (hopefully simple) function which does any kind of ImagePlus -> Img wrapping? Or do I have to implement a function with a block of IFs and ELSEs? I do not want to apply convert, because I assume it slows down my application…

Thanks!
Robert

Hi, yes, you can use:

Img< FloatType > img = ImageJFunctions.convertFloat( imp );

If it is a float ImagePlus it will wrap, if not it will copy the data.

Hope this helps,
Stephan

If you want to avoid convert calls, then you must use either:

You can, however, do better than completely raw Types by explicitly wrapping your ImagePlus as an ImgPls<RealType>; you can then ask any RealType for its float value:

  Img<RealType> wrapImg = ImageJFunctions.wrapReal(imp);
  RandomAccess<RealType> wrapRA             = wrapImg.randomAccess();
  System.out.println("wrapRA value at defined position: " + wrapRA.get().getRealFloat()); 
1 Like

Thanks for the hints!

It runs fine using wrapReal (even though a type onversion warning is blinking…) aaaaaand it takes almost one order of magnitude less time compared to convertFloat!

Great, thanks!
Robert

Output:

...
wrapping took in average   1752156 ns
converting took in average 12057775 ns
Done. Bye.

Code:

import java.util.Random;

import ij.IJ;
import ij.ImagePlus;
import ij.plugin.Duplicator;
import ij.process.ImageConverter;
import ij.process.ImageProcessor;
import net.imglib2.RandomAccess;
import net.imglib2.img.Img;
import net.imglib2.img.display.imagej.ImageJFunctions;
import net.imglib2.type.numeric.RealType;
import net.imglib2.type.numeric.real.FloatType;


public class WrappingTest {
	
	public static void testWrapperandConverters()
	{

		ImagePlus imp = IJ.openImage("http://imagej.nih.gov/ij/images/leaf.jpg");
		
		
		long sumWrappingTime = 0;
		long sumConvertingTime = 0;
		long countWrapping = 0;
		long countConverting = 0;
		
		int testCount = 100;
		Random rnd = new Random(System.nanoTime());
		
		// run a number of wrapping/converting tests in random order 
		for (int i = 0; i < testCount; i++)
		{
			//create three test images in memory
			ImagePlus imp32 = new Duplicator().run(imp);
			ImagePlus imp16 = new Duplicator().run(imp);
			ImagePlus imp8 = new Duplicator().run(imp);
			
			new ImageConverter(imp32).convertToGray32();
			new ImageConverter(imp16).convertToGray16();
			new ImageConverter(imp8).convertToGray8();
			
			ImagePlus[] images = {imp32,imp16,imp8};
			
			long timestamp = System.nanoTime();
			if (rnd.nextBoolean())
			{
				testWrapping(images);
				sumWrappingTime += System.nanoTime() - timestamp;
				countWrapping++;
			}
			else
			{
				testConverting(images);
				sumConvertingTime += System.nanoTime() - timestamp;
				countConverting++;
			}
		}
		System.out.println("wrapping took in average   " + (sumWrappingTime / countWrapping) + " ns");
		System.out.println("converting took in average " + (sumConvertingTime / countConverting) + " ns");
		System.out.println("Done. Bye.");
	}
	
	private static void testWrapping(ImagePlus[] givenImp)
	{
		for (int i = 0; i < 3; i++)
		{
			ImagePlus imp = givenImp[i];
			
			ImageProcessor ip = imp.getProcessor();
			Img<RealType> wrapRealImg = ImageJFunctions.wrapReal(imp);
			
			long[] position = {333,127};
	
			RandomAccess<RealType> wrapRealRA = wrapRealImg.randomAccess();
	
			wrapRealRA.setPosition(position);
			
			System.out.println("Imp value at defined position: " + ip.getPixelValue((int)position[0], (int)position[1]));
			System.out.println("wrapRealRA value at defined position: " + wrapRealRA.get().getRealFloat());
		}
	}
	
	private static void testConverting(ImagePlus[] givenImp)
	{
		for (int i = 0; i < 3; i++)
		{
			ImagePlus imp = givenImp[i];
			
			ImageProcessor ip = imp.getProcessor();
			Img<FloatType> convertFloatImg = ImageJFunctions.convertFloat(imp);
			
			long[] position = {333,127};
			
	
			RandomAccess<FloatType> convertFloatRA 	= convertFloatImg.randomAccess();
	
			convertFloatRA.setPosition(position);
			
			System.out.println("Imp value at defined position: " + ip.getPixelValue((int)position[0], (int)position[1]));
			System.out.println("convertFloatRA value at defined position: " + convertFloatRA.get().get());
		}
	}
	
	public static void main(String[] args) {
		testWrapperandConverters();
	}
}
1 Like

Yeah this is a limitation of the recursive generics use in ImgLib2.

When we don’t know exactly which RealType we’re going to need, we type our Img on the RealType interface. But RealType itself needs a generic parameter, and that parameter must also be a RealType.

Since we don’t know which type we’ll want, the generics convention would be to use wildcards.

But Img<RealType<?>> doesn’t work because the default wildcard implies a bound of Object, and RealType needs another RealType. So the generics convention would be to specify an upper bound.

But Img<RealType<? extends RealType>> doesn’t work because now we have another unbound RealType! So we concede the recursive generic fight, go back to Img<RealType>, and add a @SuppressWarnings("rawtypes") so our compile stops complaining.

If anyone knows a better way, please share. :dizzy_face:

1 Like

Use a dedicated generically typed method:

import ij.IJ;
import ij.ImagePlus;

import net.imglib2.img.Img;
import net.imglib2.img.display.imagej.ImageJFunctions;
import net.imglib2.type.numeric.RealType;

public class ConvertImagePlusToImg {

	public static void main(final String... args) throws Exception {
		final ImagePlus imp =
			IJ.openImage("/Users/curtis/data/samples/ramp-diagonal.png");
		process(imp);
	}
	
	public static <T extends RealType<T>> void process(final ImagePlus imp) {
		final Img<T> wrapImg = ImageJFunctions.wrapReal(imp);
		System.out.println("ImgLib2 image type is " +
			wrapImg.firstElement().getClass().getName());
	}

}
1 Like

Hey all,

just for completeness. At some point we added the *s convenience classes following the Views/ RealViews etc. scheme. For ImagePlusImg, this is ImagePlusImgs

which has the method ImagePlusImgs.from(ImagePlus) that, internally, falls back to ImageJFunctions.wrap(ImagePlus). There is, however, no conversion method and that’s what you were after—right?

1 Like