NOTICE! This is a static HTML version of a legacy ImageJ Trac ticket.

The ImageJ project now uses GitHub Issues for issue tracking.

Please file all new issues there.

Ticket #7 (closed feature: fixed)

Opened 2010-02-15T13:51:24-06:00

Last modified 2012-02-23T11:19:37-06:00

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)

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 2010-02-15T18:12:24-06:00 by curtis

  • Description modified

comment:2 Changed 2010-02-17T00:14:11-06:00 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 2010-02-18T16:40:52-06:00 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 2010-02-18T16:41:12-06:00 by curtis

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

comment:5 Changed 2010-02-19T16:15:28-06:00 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 2010-02-25T16:04:32-06:00 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 2010-02-25T16:14:11-06:00 by curtis

  • Cc wsr@… added

comment:8 in reply to: ↑ 6 Changed 2010-02-25T16:20:22-06:00 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 2010-02-25T16:55:32-06:00 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 2010-02-25T17:03:02-06:00 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 2011-02-16T13:07:01-06:00 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 2012-02-23T11:18:53-06:00 by curtis

  • Blocking 6 added

comment:12 Changed 2012-02-23T11:19:18-06:00 by curtis

  • Type changed from task to story
  • Blocking 6 removed

comment:13 Changed 2012-02-23T11:19:37-06:00 by curtis

  • Blocking 6 added

comment:14 Changed 2012-03-05T14:33:36-06:00 by curtis

  • Blocking 6 removed