Issue with opening big planar 2D images with Bioformats

Hi all,

I’m trying to open a quite big czi 2D image file with bioformats. The series selection dialog can handle it:
image

But when opening the 44469x39042 pixels sized image, I get this error message:

(Fiji Is Just) ImageJ 2.0.0-rc-71/1.52p; Java 1.8.0_172 [64-bit]; Windows 10 10.0; 88MB of 12033MB (<1%)
 
java.lang.NegativeArraySizeException
	at loci.formats.MinMaxCalculator.openBytes(MinMaxCalculator.java:259)
	at loci.formats.ReaderWrapper.openBytes(ReaderWrapper.java:334)
	at loci.formats.ReaderWrapper.openBytes(ReaderWrapper.java:334)
	at loci.plugins.util.ImageProcessorReader.openProcessors(ImageProcessorReader.java:186)
	at loci.plugins.in.ImagePlusReader.readProcessors(ImagePlusReader.java:422)
	at loci.plugins.in.ImagePlusReader.readPlanes(ImagePlusReader.java:387)
	at loci.plugins.in.ImagePlusReader.readImage(ImagePlusReader.java:282)
	at loci.plugins.in.ImagePlusReader.readImages(ImagePlusReader.java:243)
	at loci.plugins.in.ImagePlusReader.readImages(ImagePlusReader.java:221)
	at loci.plugins.in.ImagePlusReader.openImagePlus(ImagePlusReader.java:116)
	at loci.plugins.in.Importer.readPixels(Importer.java:149)
	at loci.plugins.in.Importer.run(Importer.java:86)
	at loci.plugins.LociImporter.run(LociImporter.java:78)
	at ij.IJ.runUserPlugIn(IJ.java:230)
	at ij.IJ.runPlugIn(IJ.java:193)
	at ij.IJ.runPlugIn(IJ.java:182)
	at HandleExtraFileTypes.openImage(HandleExtraFileTypes.java:499)
	at HandleExtraFileTypes.run(HandleExtraFileTypes.java:72)
	at ij.IJ.runUserPlugIn(IJ.java:230)
	at ij.IJ.runPlugIn(IJ.java:193)
	at ij.IJ.runPlugIn(IJ.java:182)
	at ij.io.Opener.openWithHandleExtraFileTypes(Opener.java:516)
	at ij.io.Opener.openImage(Opener.java:370)
	at ij.io.Opener.openImage(Opener.java:242)
	at ij.io.Opener.open(Opener.java:109)
	at ij.io.Opener.openAndAddToRecent(Opener.java:291)
	at ij.plugin.DragAndDrop.openFile(DragAndDrop.java:192)
	at ij.plugin.DragAndDrop.run(DragAndDrop.java:159)
	at java.lang.Thread.run(Thread.java:748)

The file is just about 3.7 GB large and should fit in my memory. Does anyone know a workaround to open the image?

Thanks in advance!

Cheers,
Robert

Hi, did you try what was discussed here

1 Like

Hey @sebi06,

great hint! However, that script spits out an error message:

Traceback (most recent call last):
  File "New_.py", line 281, in <module>
  File "New_.py", line 215, in readczi
	at loci.common.DataTools.safeMultiply32(DataTools.java:1286)
	at loci.common.DataTools.allocate(DataTools.java:1259)
	at loci.formats.ChannelSeparator.openBytes(ChannelSeparator.java:160)
	at loci.formats.ReaderWrapper.openBytes(ReaderWrapper.java:334)
	at loci.formats.DimensionSwapper.openBytes(DimensionSwapper.java:233)
	at loci.formats.ReaderWrapper.openBytes(ReaderWrapper.java:334)
	at loci.formats.ReaderWrapper.openBytes(ReaderWrapper.java:334)
	at loci.plugins.util.ImageProcessorReader.openProcessors(ImageProcessorReader.java:186)
	at loci.plugins.in.ImagePlusReader.readProcessors(ImagePlusReader.java:422)
	at loci.plugins.in.ImagePlusReader.readPlanes(ImagePlusReader.java:387)
	at loci.plugins.in.ImagePlusReader.readImage(ImagePlusReader.java:282)
	at loci.plugins.in.ImagePlusReader.readImages(ImagePlusReader.java:243)
	at loci.plugins.in.ImagePlusReader.readImages(ImagePlusReader.java:221)
	at loci.plugins.in.ImagePlusReader.openImagePlus(ImagePlusReader.java:116)
	at loci.plugins.BF.openImagePlus(BF.java:98)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: Array size too large: 44469 x 39042 x 2

	at org.python.core.Py.JavaError(Py.java:552)
	at org.python.core.Py.JavaError(Py.java:543)
	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:190)
	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:206)
	at org.python.core.PyObject.__call__(PyObject.java:480)
	at org.python.core.PyObject.__call__(PyObject.java:484)
	at org.python.pycode._pyx2.readczi$6(New_.py:250)
	at org.python.pycode._pyx2.call_function(New_.py)
	at org.python.core.PyTableCode.call(PyTableCode.java:171)
	at org.python.core.PyBaseCode.call(PyBaseCode.java:308)
	at org.python.core.PyFunction.function___call__(PyFunction.java:471)
	at org.python.core.PyFunction.__call__(PyFunction.java:466)
	at org.python.pycode._pyx2.f$0(New_.py:295)
	at org.python.pycode._pyx2.call_function(New_.py)
	at org.python.core.PyTableCode.call(PyTableCode.java:171)
	at org.python.core.PyCode.call(PyCode.java:18)
	at org.python.core.Py.runCode(Py.java:1614)
	at org.python.core.__builtin__.eval(__builtin__.java:497)
	at org.python.core.__builtin__.eval(__builtin__.java:501)
	at org.python.util.PythonInterpreter.eval(PythonInterpreter.java:259)
	at org.python.jsr223.PyScriptEngine.eval(PyScriptEngine.java:57)
	at org.python.jsr223.PyScriptEngine.eval(PyScriptEngine.java:31)
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
	at org.scijava.script.ScriptModule.run(ScriptModule.java:160)
	at org.scijava.module.ModuleRunner.run(ModuleRunner.java:168)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:127)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:66)
	at org.scijava.thread.DefaultThreadService.lambda$wrap$2(DefaultThreadService.java:228)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalArgumentException: Array size too large: 44469 x 39042 x 2
	at loci.common.DataTools.safeMultiply32(DataTools.java:1286)
	at loci.common.DataTools.allocate(DataTools.java:1259)
	at loci.formats.ChannelSeparator.openBytes(ChannelSeparator.java:160)
	at loci.formats.ReaderWrapper.openBytes(ReaderWrapper.java:334)
	at loci.formats.DimensionSwapper.openBytes(DimensionSwapper.java:233)
	at loci.formats.ReaderWrapper.openBytes(ReaderWrapper.java:334)
	at loci.formats.ReaderWrapper.openBytes(ReaderWrapper.java:334)
	at loci.plugins.util.ImageProcessorReader.openProcessors(ImageProcessorReader.java:186)
	at loci.plugins.in.ImagePlusReader.readProcessors(ImagePlusReader.java:422)
	at loci.plugins.in.ImagePlusReader.readPlanes(ImagePlusReader.java:387)
	at loci.plugins.in.ImagePlusReader.readImage(ImagePlusReader.java:282)
	at loci.plugins.in.ImagePlusReader.readImages(ImagePlusReader.java:243)
	at loci.plugins.in.ImagePlusReader.readImages(ImagePlusReader.java:221)
	at loci.plugins.in.ImagePlusReader.openImagePlus(ImagePlusReader.java:116)
	at loci.plugins.BF.openImagePlus(BF.java:98)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:188)
	... 29 more

I can ask my collaborator if I’m allowed to share the file - if that would be of help. For now I can say it comes from a Axio Scan Z1 slide scanner.

Thanks for your support!

Cheers,
Robert

Sharing would help. And the used Zen version. Is it JPGXR compressed?

1 Like

java.lang.NegativeArraySizeException looks like the standard ‘too many bytes to fit in a Java array’ problem, not CZI-specific, e.g. see Can't open a tiff file

Note that the limit of roughly 2^31 doesn’t apply just to the number of pixels, but rather for the number of pixels multiplied by bytes per pixel multiplied by rgb channels… so can be exceeded even if the width x height would otherwise seem to fit.

Note that there are other open source software applications designed specifically to handle this kind of image that might be worth a try, and which can be used along with ImageJ/Fiji * cough * cough *…

PS. @dgault might it be worth calculating this value as a long in Bio-Formats, and throwing a more recognizable exception if it overflows to make this more explicit?

4 Likes

Hey @petebankhead,

great to hear from you.

Yes, but Imglib2 and ImageJ2 developers claim to have solved this problem. I’m just wondering how the end user can benefit from the advances :wink:

Thanks for the hint! I would love to use them but the analysis we programmed runs in ImageJ jython or groovy specifically. We just need to fix opening the image :wink: Last time I tried to run scripts processing images within QuPath (m3 I believe) using Imglib2 and clij, it was quite challenging to access pixels and to feedback image data and Rois to QuPath. Do you think it’s worth a try again in the current version? :upside_down_face:

Thanks for your support!

Cheers,
Robert

1 Like

By using anything else than the legacy user interface. ImageJ 1.x has the limitation of 2 Gigapixels per plane because it uses byte[] arrays for each image plane.

To display images larger than that, you have to go to #bigdataviewer (BDV) and other tools. I agree it would be awesome if this functionality could be more accessible to standard users of ImageJ. I remember discussions around making BDV the standard image viewer in ImageJ(2) at some point.

2 Likes

Yes, cool, but how can I open that file? :wink:

Thanks for any hint!

Cheers,
Robert

Can you try staying with ImgLib2/ImageJ2 API only (i.e. avoiding ImagePlus)? Here’s an example using SCIFIO:

#@ DatasetIOService io
dataset = io.open("path/to/your/image.czi")

IIRC, this opens the first series contained in the file.

For more control over options, have a look at this gist by @ctrueden:

1 Like

Hey @imagejan,

cool, this was promising:

However, it throws this error message:

Traceback (most recent call last):
  File "New_.py", line 2, in <module>
	at org.scijava.util.ArrayUtils.safeMultiply32(ArrayUtils.java:175)
	at org.scijava.util.ArrayUtils.allocate(ArrayUtils.java:148)
	at io.scif.ByteArrayPlane.blankPlane(ByteArrayPlane.java:81)
	at io.scif.ByteArrayPlane.blankPlane(ByteArrayPlane.java:47)
	at io.scif.AbstractPlane.populate(AbstractPlane.java:133)
	at io.scif.AbstractPlane.populate(AbstractPlane.java:122)
	at io.scif.AbstractPlane.<init>(AbstractPlane.java:77)
	at io.scif.ByteArrayPlane.<init>(ByteArrayPlane.java:58)
	at io.scif.ByteArrayReader.createPlane(ByteArrayReader.java:64)
	at io.scif.ByteArrayReader.createPlane(ByteArrayReader.java:43)
	at io.scif.filters.AbstractReaderFilter.createPlane(AbstractReaderFilter.java:406)
	at io.scif.filters.AbstractReaderFilter.createPlane(AbstractReaderFilter.java:406)
	at io.scif.filters.PlaneSeparator.openPlane(PlaneSeparator.java:208)
	at io.scif.filters.AbstractReaderFilter.openPlane(AbstractReaderFilter.java:225)
	at io.scif.filters.AbstractReaderFilter.openPlane(AbstractReaderFilter.java:193)
	at io.scif.img.ImgOpener.read(ImgOpener.java:691)
	at io.scif.img.ImgOpener.read(ImgOpener.java:656)
	at io.scif.img.ImgOpener.readPlanes(ImgOpener.java:643)
	at io.scif.img.ImgOpener.openImgs(ImgOpener.java:358)
	at io.scif.img.ImgOpener.openImgs(ImgOpener.java:245)
	at io.scif.img.ImgOpener.openImgs(ImgOpener.java:146)
	at io.scif.services.DefaultDatasetIOService.open(DefaultDatasetIOService.java:125)
	at io.scif.services.DefaultDatasetIOService.open(DefaultDatasetIOService.java:110)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: Array size too large: 44469 x 39042 x 2

	at org.python.core.Py.JavaError(Py.java:552)
	at org.python.core.Py.JavaError(Py.java:543)
	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:190)
	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:206)
	at org.python.core.PyObject.__call__(PyObject.java:497)
	at org.python.core.PyObject.__call__(PyObject.java:501)
	at org.python.core.PyMethod.__call__(PyMethod.java:141)
	at org.python.pycode._pyx3.f$0(New_.py:2)
	at org.python.pycode._pyx3.call_function(New_.py)
	at org.python.core.PyTableCode.call(PyTableCode.java:171)
	at org.python.core.PyCode.call(PyCode.java:18)
	at org.python.core.Py.runCode(Py.java:1614)
	at org.python.core.__builtin__.eval(__builtin__.java:497)
	at org.python.core.__builtin__.eval(__builtin__.java:501)
	at org.python.util.PythonInterpreter.eval(PythonInterpreter.java:259)
	at org.python.jsr223.PyScriptEngine.eval(PyScriptEngine.java:57)
	at org.python.jsr223.PyScriptEngine.eval(PyScriptEngine.java:31)
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
	at org.scijava.script.ScriptModule.run(ScriptModule.java:160)
	at org.scijava.module.ModuleRunner.run(ModuleRunner.java:168)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:127)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:66)
	at org.scijava.thread.DefaultThreadService.lambda$wrap$2(DefaultThreadService.java:228)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalArgumentException: Array size too large: 44469 x 39042 x 2
	at org.scijava.util.ArrayUtils.safeMultiply32(ArrayUtils.java:175)
	at org.scijava.util.ArrayUtils.allocate(ArrayUtils.java:148)
	at io.scif.ByteArrayPlane.blankPlane(ByteArrayPlane.java:81)
	at io.scif.ByteArrayPlane.blankPlane(ByteArrayPlane.java:47)
	at io.scif.AbstractPlane.populate(AbstractPlane.java:133)
	at io.scif.AbstractPlane.populate(AbstractPlane.java:122)
	at io.scif.AbstractPlane.<init>(AbstractPlane.java:77)
	at io.scif.ByteArrayPlane.<init>(ByteArrayPlane.java:58)
	at io.scif.ByteArrayReader.createPlane(ByteArrayReader.java:64)
	at io.scif.ByteArrayReader.createPlane(ByteArrayReader.java:43)
	at io.scif.filters.AbstractReaderFilter.createPlane(AbstractReaderFilter.java:406)
	at io.scif.filters.AbstractReaderFilter.createPlane(AbstractReaderFilter.java:406)
	at io.scif.filters.PlaneSeparator.openPlane(PlaneSeparator.java:208)
	at io.scif.filters.AbstractReaderFilter.openPlane(AbstractReaderFilter.java:225)
	at io.scif.filters.AbstractReaderFilter.openPlane(AbstractReaderFilter.java:193)
	at io.scif.img.ImgOpener.read(ImgOpener.java:691)
	at io.scif.img.ImgOpener.read(ImgOpener.java:656)
	at io.scif.img.ImgOpener.readPlanes(ImgOpener.java:643)
	at io.scif.img.ImgOpener.openImgs(ImgOpener.java:358)
	at io.scif.img.ImgOpener.openImgs(ImgOpener.java:245)
	at io.scif.img.ImgOpener.openImgs(ImgOpener.java:146)
	at io.scif.services.DefaultDatasetIOService.open(DefaultDatasetIOService.java:125)
	at io.scif.services.DefaultDatasetIOService.open(DefaultDatasetIOService.java:110)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:188)
	... 24 more

I’m about to organise sharing of such a file…

Thanks again for your support! I think talking about this and solving this issue is quite important.

Cheers,
Robert

Hey @petebankhead,

I can confirm this file opens in QuPath! Thaaaanks for reminding me of your amazing work!

Cheers,
Robert

4 Likes

Update: The code snippet from @ctrueden also looks very promising:

#@ ImageJ ij

import io.scif.config.SCIFIOConfig;
import io.scif.config.SCIFIOConfig.ImgMode;
import io.scif.ome.OMEMetadata

// Open and display a file as a cell image.
config = new SCIFIOConfig().imgOpenerSetImgModes(ImgMode.CELL)
dataset = ij.scifio().datasetIO().open("path/to/file.czi", config)

But crashes in the last line with this error:

Started New_.groovy at Thu Jan 30 11:25:01 CET 2020
java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: Expected bounds of dimensionality 3 but was 2
	at net.imglib2.cache.util.CacheAsUncheckedCacheAdapter.get(CacheAsUncheckedCacheAdapter.java:32)
	at net.imglib2.img.cell.LazyCellImg$LazyCells.get(LazyCellImg.java:104)
	at net.imglib2.img.list.AbstractLongListImg$LongListCursor.get(AbstractLongListImg.java:98)
	at net.imglib2.img.cell.CellCursor.getCell(CellCursor.java:94)
	at net.imglib2.img.cell.CellCursor.moveToNextCell(CellCursor.java:182)
	at net.imglib2.img.cell.CellCursor.reset(CellCursor.java:152)
	at net.imglib2.img.cell.CellCursor.<init>(CellCursor.java:88)
	at net.imglib2.img.cell.AbstractCellImg.cursor(AbstractCellImg.java:92)
	at net.imglib2.img.cell.AbstractCellImg.cursor(AbstractCellImg.java:51)
	at net.imglib2.img.AbstractImg.firstElement(AbstractImg.java:81)
	at net.imagej.ImgPlus.firstElement(ImgPlus.java:276)
	at net.imagej.DefaultDataset.getType(DefaultDataset.java:220)
	at net.imagej.DefaultDataset.isSigned(DefaultDataset.java:225)
	at net.imagej.DefaultDataset.mergedColorCompatible(DefaultDataset.java:704)
	at net.imagej.DefaultDataset.<init>(DefaultDataset.java:101)
	at net.imagej.DefaultDatasetService.create(DefaultDatasetService.java:200)
	at io.scif.services.DefaultDatasetIOService.open(DefaultDatasetIOService.java:128)
	at io.scif.services.DatasetIOService$open.call(Unknown Source)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:133)
	at Script2.run(Script2.groovy:9)
	at org.scijava.plugins.scripting.groovy.GroovyScriptEngine.eval(GroovyScriptEngine.java:303)
	at org.scijava.plugins.scripting.groovy.GroovyScriptEngine.eval(GroovyScriptEngine.java:122)
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
	at org.scijava.script.ScriptModule.run(ScriptModule.java:160)
	at org.scijava.module.ModuleRunner.run(ModuleRunner.java:168)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:127)
	at org.scijava.module.ModuleRunner.call(ModuleRunner.java:66)
	at org.scijava.thread.DefaultThreadService.lambda$wrap$2(DefaultThreadService.java:228)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.util.concurrent.ExecutionException: java.lang.IllegalArgumentException: Expected bounds of dimensionality 3 but was 2
	at net.imglib2.cache.ref.SoftRefLoaderRemoverCache.get(SoftRefLoaderRemoverCache.java:168)
	at net.imglib2.cache.util.LoaderRemoverCacheAsLoaderCacheAdapter.get(LoaderRemoverCacheAsLoaderCacheAdapter.java:37)
	at net.imglib2.cache.util.LoaderCacheAsCacheAdapter.get(LoaderCacheAsCacheAdapter.java:30)
	at net.imglib2.cache.util.CacheAsUncheckedCacheAdapter.get(CacheAsUncheckedCacheAdapter.java:28)
	... 33 more
Caused by: java.lang.IllegalArgumentException: Expected bounds of dimensionality 3 but was 2
	at io.scif.img.cell.loaders.AbstractArrayLoader.validateBounds(AbstractArrayLoader.java:305)
	at io.scif.img.cell.loaders.AbstractArrayLoader.read(AbstractArrayLoader.java:278)
	at io.scif.img.cell.loaders.AbstractArrayLoader.read(AbstractArrayLoader.java:251)
	at io.scif.img.cell.loaders.AbstractArrayLoader.loadArray(AbstractArrayLoader.java:231)
	at io.scif.img.cell.SCIFIOCellImgFactory$SCIFIOCellLoader.load(SCIFIOCellImgFactory.java:200)
	at net.imglib2.cache.img.LoadedCellCacheLoader.get(LoadedCellCacheLoader.java:91)
	at net.imglib2.cache.img.LoadedCellCacheLoader.get(LoadedCellCacheLoader.java:51)
	at net.imglib2.cache.img.DiskCellCache.get(DiskCellCache.java:104)
	at net.imglib2.cache.img.DiskCellCache.get(DiskCellCache.java:43)
	at net.imglib2.cache.IoSync.get(IoSync.java:174)
	at net.imglib2.cache.ref.SoftRefLoaderRemoverCache.get(SoftRefLoaderRemoverCache.java:158)
	... 36 more

Thanks again for everyones support!

Cheers,
Robert

1 Like

It seems that this was reported by @mstritt already:

1 Like

That depends… I’ve never managed to get QuPath and Fiji to work together very nicely, thanks partly to different JDKs (QuPath needs JDK 11+, which handles JavaFX very differently) and various incompatible dependency (e.g. QuPath uses a more recent JavaCPP). Perhaps surmountable… but a lot of work and I haven’t succeeded yet.

Using QuPath + ImageJ1 + Groovy is straightforward - I do it all the time. If you want to use QuPath + Fiji, I’d suggest ignoring the fact they are both written in Java and treat them as if they are unable to communicate directly.

With that in mind, you can use the following approach:

  • Use QuPath to open the image, interactively select a region of interest (optional) and export separate tiles of a manageable size at the resolution you need for analysis. The scaling and location information can be preserved through the image metadata (ImageJ TIFF) or filename (any other format).
  • Run your Jython script across the tiles in Fiji directly
  • (Optional) If the output includes ImageJ ROIs or binary images, these can be reimported into QuPath to visualize them in the context of the whole slide image (and further refined/classified/merged… whatever you need).

This can be accomplished with a few scripts, since all the required functionality is in QuPath already (including all the ROI conversion, scaling and translation). Although if we can define a clear workflow that would be widely useful for QuPath + Fiji exchange then it could probably be built in to QuPath pretty quickly to make it easier to apply without scripts.

I don’t know enough about the BigDataViewer to know what options a Fiji-only solution has in terms of analysis of whole slide images, I’d be interested to learn more.

Great!

2 Likes

Great! In fact, I work mostly with CLIJ and ImageJ1 ROIs. Is there a collection of some example scripts? I only found one on this page:

Was wondering if there are more somewhere…

Thanks again!

Cheers,
Robert

What exactly would you want to do?

For example, I haven’t checked it recently, but this script should export QuPath annotations to an ImageJ-friendly zip file:

import ij.plugin.frame.RoiManager

def path = buildFilePath(PROJECT_BASE_DIR, "rois.zip")

def annotations = getAnnotationObjects()
def roiMan = new RoiManager(false)
double x = 0
double y = 0
double downsample = 1 // Increase if you want to export to work at a lower resolution
annotations.each {
  def roi = IJTools.convertToIJRoi(it.getROI(), x, y, downsample)
  roiMan.addRoi(roi)
}
roiMan.runCommand("Save", path)

And this documentation is still mostly accurate for interactive stuff: https://github.com/qupath/qupath/wiki/Working-with-ImageJ

I’m currently rewriting the documentation and plan to add more example scripts and/or commands to the software. I’ve planned to have a streamlined QuPath + Fiji + QuPath again pipeline for a long time, but just haven’t needed it enough yet and no one has really asked for it - so I’m not entirely sure how it should look.

For example, can you say what do your Groovy/Jython scripts do? I’m thinking things like:

  • Do they operate on very large regions at high-resolution, or on ‘small’ (manageable) regions extracted from a whole slide image (either by cropping or downsampling)?
  • If they need large regions at high-resolutions, can they work on independent image tiles - and, if so, should these be overlapping or non-overlapping?
  • Do they make measurements + then they are done, or do they generation regions that should be reassembled back in QuPath?
  • If they generate regions, in what format do these exist and are they fully independent - or would they need to be merged across tiles?

So many questions :slight_smile: All scriptable with varying levels of complexity, but if there are some very common use cases it would be nice to figure out a standard pipeline that can be implemented without too much scripting.

2 Likes

@haesleinhuepf There’s a new TileExporter class in v0.2.0-m8 (or some recent milestone) to help with exporting overlapping/non-overlapping tiles.

Here’s an example of it in action, which assumes you’ve got your image in a QuPath project to figure out the export path automatically:

/**
 * Script to export labelled image tiles in various ways.
 */

import qupath.lib.common.GeneralTools
import qupath.lib.gui.ml.TileExporter

def imageData = getCurrentImageData()

// Define output path (relative to project)
def name = GeneralTools.getNameWithoutExtension(imageData.getServer().getMetadata().getName())
def pathOutput = buildFilePath(PROJECT_BASE_DIR, 'tiles', name)
mkdirs(pathOutput)

// Define output resolution
double requestedPixelSize = 5.0

// Convert to downsample
double downsample = requestedPixelSize / imageData.getServer().getPixelCalibration().getAveragedPixelSize()

// Create an exporter that requests corresponding tiles from the original & labelled image servers
new TileExporter(imageData)
    .downsample(downsample)   // Define export resolution
    .imageExtension('.tif')   // Define file extension for original pixels (often .tif, .jpg, '.png' or '.ome.tif')
    .tileSize(512)            // Define size of each tile, in pixels
    .annotatedTilesOnly(false) // If true, only export tiles if there is a (labelled) annotation present
    .overlap(64)              // Define overlap, in pixel units at the export resolution
    .writeTiles(pathOutput)   // Write tiles to the specified directory
    
print 'Done!'

It uses a builder so you can vary the parameters as needed.

If the extension is .tif then it will be a standard ImageJ TIFF, with ImagePlus.getCalibration() values set according to the location and scaling at which the tile was extracted. Now if you were to batch run a macro/script in Fiji - but retain the calibration values - it should be technically possible to relate everything back to the whole slide image later (including getting the ROIs back into QuPath if needed).

Less streamlined than direct QuPath-Fiji integration, but a lot easier to do and allows you to take full advantage of both without losing days wrestling with the classpath :slight_smile:

I’ll add these scripts to the new documentation.

(Please feel free to split the thread if this is going too far away…)

3 Likes

Hi @haesleinhuepf, as suggested by Pete earlier these exceptions you have been seeing are caused by opening a single image plane that is larger than the max array size. The solution would be to either crop the image on import (from the Bio-Formats Importer Options window) or you can use an application which has tiled reading (such as QuPath).

1 Like

Hey @dgault,

alright. However, imglib2s CellImg does support opening such images, right? Just curious.

Thanks!

Cheers,
Robert

This is particularly frustrating because we reported this both to BioFormats and to Zeiss over two years ago. People in our lab simply have stopped making large multichannel tiled regions.
We uploaded example images to BioFormats, but it must not have been a priority.
Even more surprised Zeiss hasn’t fixed this given the amount of noise we made. We bought extra RAM just for this, but even after upgarding to Zen Blue 2.6, there is no way to export a single big stitched tif tile.

1 Like