Unable to use CellImg as input for some algorithms

imglib2
scifio
imagej

#1

Hi,

I am trying to write some simple code to try out imglib2, basically opening an image, process it using one of the algorithms and save it.
When I want to open the image as a CellImg, I then get the following error when I call one of the Gauss or Thresholder for example:

Exception in thread "main" java.lang.IllegalStateException: Tried to create a new SCIFIOCellImg without a Reader to use for opening planes.
Call setReader(Reader) before invoking create()
	at io.scif.img.cell.SCIFIOCellImgFactory.create(SCIFIOCellImgFactory.java:159)
	at io.scif.img.cell.SCIFIOCellImgFactory.create(SCIFIOCellImgFactory.java:71)
	at net.imglib2.img.ImgFactory.create(ImgFactory.java:74)
	at net.imglib2.algorithm.gauss.AbstractGauss.<init>(AbstractGauss.java:110)
	at net.imglib2.algorithm.gauss.GaussFloat.<init>(GaussFloat.java:110)
	at net.imglib2.algorithm.gauss.Gauss.inFloatInPlace(Gauss.java:313)
	at net.imglib2.algorithm.gauss.Gauss.inFloatInPlace(Gauss.java:271)
	at net.imglib2.algorithm.gauss.Gauss.inFloatInPlace(Gauss.java:256)

Here is the code producing the error:

File file = new File( "/path/to/input/DrosophilaWing.tif" );  
String path = file.getAbsolutePath();

File fileO = new File( "/path/to/output/DrosophilaWing.tif" );
			
SCIFIOConfig config = new SCIFIOConfig();
config.imgOpenerSetImgModes( ImgMode.CELL );
			
ImgOpener imgOpener = new ImgOpener();

System.out.println("Opening input image");
final Img< T > image = (Img<T>) imgOpener.openImg( path, config );
			
System.out.println(image.factory().getClass());

System.out.println("Convolve image");
Gauss.inFloatInPlace( 2, image);
			
System.out.println("Save image");
ImgSaver imgSaver = new ImgSaver();
imgSaver.saveImg(fileO.getAbsolutePath(), image);

Am I missing something? If I don’t put the SCIFIOConfig when opening the image, it is opened as an ArrayImg and I don’t get any error.

Thanks!


#2

Based on the stacktrace that you shared, Gauss.java:313 acquires an ImgFactory (more specifically a SCIFIOCellImgFactory in the failing case) from your input and passes it to the constructor of GaussFloat which in turn calls the super constructor AbstractGauss. The exception message states that setReader needs to be called before invoking create(). This means that you cannot call Gauss.inFloatInPlace with an Img that produces a SCIFIOCellImgFactory.

Instead, I suggest you use Gauss3.gauss( double sigma, RandomAccessible< S > source, RandomAccessibleInterval< T > target ). Here, you will need to

  1. extend the input via any of Views.extend... to make sure that the source is defined where necessary, and
  2. create the target img, e.g. via ArrayImgs.doubles.

example usage (based off your code/variables):

final Img< T > image = (Img<T>) imgOpener.openImg( path, config );
RandomAccessible< T > source = Views.extendMirrorSingle( image );
RandomAccessibleInterval< DoubleType > target = ArrayImgs.doubles( Intervals.dimensionsAsLongArray( image ) );
Gauss3.gauss( sigma, source, target );

Update: The @Deprecated tag in the Gauss class also supports my suggestion to use Gauss3 instead.


#3

I just noticed that there is github issue associated with this (imglib/imglib2-algorithm#59).


#4

Thanks @hanslovsky for the example code, I don’t get the error using Gauss3 like you suggested.
But is there a way to have the target as a CellImg as well? I was particularly interested in the usage of CellImg hoping it will improve the memory footprint for big images.


#5

Being able to read from a CellImg, perform the processing (e.g. thresholing), and write to a CellImg would drastically reduce the memory footprint required when processing large images.

The algorithm might require more runtime, but it enables processing of images larger than system Ram.


#6

Typically, when you would like to process data that does not fit into memory (both source and target), I would suggest your process the data in blocks and write out the blocks as you process them.

RandomAccessibleInterval< T > source = // Should be a CachedCellImg if data is too big for memory;
List< Interval > blocks = // generate blocks
for ( Interval block : blocks )
{
	processAndWriteBlock( source, block );
}

Local or distributed paraellization can be easily implemented following this scheme.

I not familiar with scifio and the SCIFIOCellImg and I do not know if it supports lazy/cached reading. If you have a specific task and data format in mind, please specify those.


#7

This looks like what we are trying to do.
We are currently working with OME TIFF images, so scifio looked like the way to go if we wanted to manipulate the metadata.
We have a bunch of algorithms that we would like to improve (memory-wise) such as segmentation, stitching, filtering or tracking for example. Right now we are trying to implement a few of them (specifically: Gaussian filtering, thresholding and object labeling) using a couple of libraries to test them out.


#8

@ctrueden Do you know if SCIFIO can read/write blocks from tiff images? If not, please ping someone who does.
Thanks

@MyleneSimon We started using n5 for storing multi-dimensional blocked image data almost exclusively. There are utility methods for easy integration into imglib2 and hdf5 bindings for the N5 interface. If blocked IO through SCIFIO does not work and switching the data format is a realistic option for you, I will gladly provide an example of use.


#9

In theory, yes. It used to work. The exception @MyleneSimon is seeing looks like a straight-up bug to me. But I am sorry to say I do not have time to investigate now. @gab1one Would you have time to look into this at all?