Ticket #7 (closed feature: fixed)

Opened 10 years ago

Last modified 7 years ago

Evaluate imglib for core ImageJ work

Reported by: curtis Owned by: aivar
Priority: blocker Milestone: progress-report
Component: Core Version:
Severity: non-issue Keywords:
Cc: wsr@… Blocked By:
Blocking:

Description (last modified by curtis) (diff)

We want to use the  Imglib library as the basis for the ImageJ 2.0 data model, since it makes writing data-type-agnostic image processing routines easier. We need to explore the library and evaluate it to verify that it is capable of meeting all of ImageJ's requirements:

As an initial test, we will try to adapt the ImageProcessor classes to use imglib.

For a history of the discussion around the idea to use imglib, see the " Image data types" thread on the ImageJX discussion group.

Change History

comment:1 Changed 10 years ago by curtis

  • Description modified (diff)

comment:2 Changed 10 years ago by curtis

One potential problem with imglib may be performance. The imglib authors have observed performance at 90% of ImageJ's, whereas Wayne Rasband reports a 4x slowdown with a simple imglib-driven plugin compared to ImageJ's Image>Math>Add command:

import ij.ImagePlus;
import ij.gui.GenericDialog;
import ij.plugin.filter.PlugInFilter;
import ij.process.ImageProcessor;
import mpicbg.imglib.cursor.Cursor;
import mpicbg.imglib.image.Image;
import mpicbg.imglib.image.ImagePlusAdapter;
import mpicbg.imglib.image.display.imagej.ImageJFunctions;
import mpicbg.imglib.type.NumericType;

public class Imglib_Plugin<T extends NumericType<T>> implements PlugInFilter {
	ImagePlus image;

	public int setup(String arg, ImagePlus imp) {
		image = imp;
		return DOES_ALL;
	}

	public void run(ImageProcessor ip) {
		run(image);
		image.updateAndDraw();
	}

	public void run(ImagePlus image) {
		Image<T> img = ImagePlusAdapter.wrap(image);
		add(img, 20);
	}

	public static<T extends NumericType<T>> void add(Image<T> img, float value) {
		final Cursor<T> cursor = img.createCursor();
		final T summand = cursor.getType().createVariable();

		summand.setReal(value);

		while (cursor.hasNext()) {
			cursor.fwd();
			cursor.getType().add(summand);
		}
	}
}

comment:3 Changed 10 years ago by curtis

Rick has pointed out  VSIPL as alternative image processing library. It has been in development for a long time, and is mature and robust; however, it is written in C++, and is not open source.

comment:4 Changed 10 years ago by curtis

  • Type changed from defect to task
  • Severity changed from serious to non-issue

comment:5 Changed 10 years ago by curtis

More detailed benchmarks from Wayne:

"Here are results in megapixels/sec using a 3000x3000 image:

typeImageJimglibfactor
8-bit473627.6
16-bit3333210.4
32-bit2722013.6
RGB158662.4

"You need to be using ImageJ 1.43p or later to duplicate these results. The ShortProcessor and FloatProcessor constructors no longer calculate the image min and max, which greatly speeds up simple operations like adding a constant to an image. I am using an older version of imglib; the current version may be faster. I should also note that the version of imglib I have does not handle overflows correctly but this is probably fixed in the current version."

comment:6 follow-up: ↓ 8 Changed 9 years ago by aivar

I looked at performance, starting with the claim that Imglib is faster, with the following sample code, from  http://pacific.mpi-cbg.de/wiki/index.php/Imglib#Example

ImageJ version:

final static public void multiply( final ImagePlus input, final double factor )
 {
         final int nPixels = input.getWidth() * input.getHeight();
 
         // iterate through all channels, frames and timepoints (might be just one)
         for ( int c = 1; c <= input.getNChannels(); ++c )
                 for ( int z = 1; z <= input.getNSlices(); ++z )
                         for ( int t = 1; t <= input.getNFrames(); ++t )
                         {
                                 input.setPosition( c, z, t );
                                 final ImageProcessor ip = input.getChannelProcessor();
 
                                 // RGB images are special
                                 if ( input.getType() == ImagePlus.COLOR_RGB )
                                 {
                                         for ( int i = 0; i < nPixels; ++i )
                                         {
                                                 final int rgb = ip.get( i );
                                                 final int r = doubleTo8Bit( Math.round( ( (
                                                         rgb >> 16 ) & 0xff ) * factor ) );
                                                 final int g = doubleTo8Bit( Math.round( ( (
                                                         rgb >> 8 ) & 0xff ) * factor ) );
                                                 final int b = doubleTo8Bit( Math.round( (
                                                         rgb & 0xff ) * factor ) );
                                                 ip.set( i, ( r << 16 ) | ( g << 8 ) | b );
                                         }
                                 }
                                 else
                                 {
                                         for ( int i = 0; i < nPixels; ++i )
                                         {
                                                 ip.setf( i, ip.getf( i ) * ( float )factor );
                                         }
                                 }
                         }
 }
 
 final static int doubleTo8Bit( final double value ) {
         return Math.max( 0, Math.min( 255, (int) value ) );
 }

Imglib version:

public <T extends NumericType<T>> void multiplyImageByValue( Image<T> input,  T factor )
 {
        final Cursor<T> cursor = input.createCursor();
 
         while ( cursor.hasNext() )
         {
                 cursor.fwd();
                 cursor.getType().mul( factor );
         }
 
        cursor.close();
}

This code loops through the image and multiplies by a factor. I tried changing how that multiplication is done and found the Imglib performance varied greatly depending on the type of the factor, the ImageJ not so much.

Here are some sample results. (These are milliseconds per 100 iterations using the 8-bit 439x167 image single-channel-ome.tif, on a 3GHz dual core iMac, with very rough estimates of the averages. Also, I had to add a new 'mul(int)' method to Imglib's ByteType.)

float T int
ImageJ 50 -- 40
Imglib 150 38 18

The premise of the sample code is that ImageJ has to use floats but Imglib can use T for the multiplying factor. So in that case Imglib comes out slightly ahead.

Actually if the multiplier has a fractional component (consider 0.5) it doesn't make any sense to convert it to a ByteType in this example. So it would be more practical for this example for both to use float multipliers, in which case ImageJ performs much better. (If both use integers Imglib comes out ahead.)

comment:7 Changed 9 years ago by curtis

  • Cc wsr@… added

comment:8 in reply to: ↑ 6 Changed 9 years ago by curtis

Replying to aivar:

Actually if the multiplier has a fractional component (consider 0.5) it doesn't make any sense to convert it to a ByteType in this example. So it would be more practical for this example for both to use float multipliers, in which case ImageJ performs much better. (If both use integers Imglib comes out ahead.)

My question is, why is Imglib so much slower with floats? If it were a general problem with method calls we shouldn't see such good performance with ints either. Is the speed issue with floats something we can improve?

comment:9 follow-up: ↓ 10 Changed 9 years ago by aivar

Wayne's benchmark is comparing the ImageJ built-in add function with one implemented as a plugin using Imglib.

I tried comparing ImageJ and Imglib both as plugins, with an ImageJ version similar to code above and got results like:

float T int
ImageJ 40 -- 43
Imglib 38 33 19

So both plugins are comparable except adding integers in the Imglib version is faster.

ImageJ's built-in add method in ImageProcessor gains its performance by precomputing a look-up table for all possible pixel values. Then the loop through the pixels just substitutes from the look-up table rather than doing arithmetic for each pixel.

The optimization is made for arithmetic operations on single pixels in 8-bit, 16-bit, and RGB images. 32-bit (float) images just do the arithmetic. This should just carry over in ImageProcessor if we do move to using Imglib. I can't think of any reason Imglib would prohibit its use.

comment:10 in reply to: ↑ 9 Changed 9 years ago by aivar

Replying to aivar:


This should just carry over in ImageProcessor if we do move to using Imglib. I can't think of any reason Imglib would prohibit its use.

Just to clarify. This won't happen for free but I believe it wouldn't be a big problem to implement.

comment:11 Changed 9 years ago by curtis

  • Status changed from new to closed
  • Resolution set to fixed

At the Madison hackathon, ImgLib underwent a major design iteration ("ImgLib2"), and should now capable of expressing everything needed for the ImageJ2 data model. We are working to fix remaining build issues with ImgLib2, then begin integrating with the ImageJDev codebase.

Combined with Barry DeZonia's  imglib-ops work, translation of existing ImageProcessor methods to ImgLib will be straightforward (and Barry has initial translations of many methods already).

comment:12 Changed 7 years ago by curtis

  • Blocking 6 added

comment:12 Changed 7 years ago by curtis

  • Type changed from task to story
  • Blocking 6 removed

comment:13 Changed 7 years ago by curtis

  • Blocking 6 added

comment:14 Changed 7 years ago by curtis

  • Blocking 6 removed
Note: See TracTickets for help on using tickets.