Open christofferaberg opened 9 years ago
I think the problem is that bioformats is using the wrong reader. It should be using loci.formats.in.OMETiffReader and instead is using loci.formats.in.TiffDelegateReader. This is because bioformats, by default, prevents the ImageReader class from examining the file contents if it can find a reader by looking at the file's extension. The OMETiffReader insists on reading the file before accepting that it is the file's reader, but the TiffDelegateReader does not, so the TiffDelegateReader is chosen as the reader.
We do this to limit the time it takes to choose a reader and to limit the impact of (potentially) 100+ readers each reading the file from disk. This can have a heavy impact for our parent project, CellProfiler, if run on a cluster (We've brought down an Isolon file server before).
You can use the loci.formats.ImageReader class to pick the image reader and override this behavior:
import javabridge
import bioformats.formatreader
javabridge.start_vm(class_path=bioformats.JARS)
from pylab import *
rcParams['figure.figsize'] = (63, 27)
path = r"c:\Temp\output\bfissue23\multi-channel-time-series.ome.tif"
image_reader = bioformats.formatreader.make_image_reader_class()()
# This sets up the readers to read the contents of the file if necessary
image_reader.allowOpenToCheckType(True)
image_reader.setId(path)
n_c = image_reader.getSizeC()
n_t = image_reader.getSizeT()
# Make a fake ImageReader and install the one above inside it
wrapper = bioformats.formatreader.ImageReader(path=path, perform_init=False)
wrapper.rdr = image_reader
print "This file has %d channels and %d timepoints" % (n_c, n_t)
for c in range(n_c):
for t in range(n_t):
subplot(n_c, n_t, c*n_t + t + 1).imshow(wrapper.read(c=c, t=t), cmap="gray")
savefig(r"c:\Temp\output\bfissue23\figure.png")
We've improved the Javabridge considerably since writing python-bioformats. You might consider using the Java directly to access the metadata:
rdr = javabridge.JClassWrapper('loci.formats.in.OMETiffReader')()
rdr.setOriginalMetadataPopulated(True)
clsOMEXMLService = javabridge.JClassWrapper('loci.formats.services.OMEXMLService')
serviceFactory = javabridge.JClassWrapper('loci.common.services.ServiceFactory')()
service = serviceFactory.getInstance(clsOMEXMLService.klass)
metadata = service.createOMEXMLMetadata()
rdr.setMetadataStore(metadata)
rdr.setId(path)
root = metadata.getRoot()
first_image = root.getImage(0)
pixels = first_image.getPixels()
# The plane data isn't in the planes, it's in the tiff data
for idx in range(pixels.sizeOfTiffDataList()):
tiffData = pixels.getTiffData(idx)
c = tiffData.getFirstC().getValue().intValue()
t = tiffData.getFirstT().getValue().intValue()
print "TiffData: c=%d, t=%d" % (c, t)
Thanks for your prompt reply!
However, I am not sure of how that solves the problem. Perhaps I need to re-emphasize that what I am interested in is reading metadata from microscopy image files using the bioformats library, using python. The problem with SizeT was an example, as was the fact that I used a sample file from BioFormats.
So, to put my question in a wider perspective, is python-bioformats the right tool to use for reading metadata from microscopy images files in a bioformats-type-of-way?
With regards to the specific solutions you suggest (thanks again!), the first one (using python-bioformats) works in the sense that the number of channels and time frames are correct. However, I am unsure of how to get at all the other metadata, in particular the ones associated with the individual planes.
The second one (using javabridge) fails at line 949 in jutil.pyc with the error message: AttributeError: 'NoneType' object has no attribute 'find_class'
"Is python-bioformats the right tool to use for reading metadata from microscopy images files in a bioformats-type-of-way?" In some ways, python-bioformats is a work in progress and in others, it's tailored to the CellProfiler use case without too many hooks to access the configuration options that would give you richer metadata but at a computational expense. I'd welcome a commit that added that flexibility. The code you use might also be dependent on the file type and its internal structure. Do you have an example file you could send to me? What microscope are you using?
Perhaps, though, you could get the farthest by accessing the Bio-formats jar (loci_tools or bioformats_package) directly through the javabridge. I think the script is failing because I neglected to add code to start the VM (see corrected below). You should be able to use the javabridge to do anything you might do in Java using the Java Bio-formats classes directly.
import javabridge
import bioformats
path = r"c:\Temp\output\bfissue23\multi-channel-time-series.ome.tif"
javabridge.start_vm(class_path=bioformats.JARS)
rdr = javabridge.JClassWrapper('loci.formats.in.OMETiffReader')()
rdr.setOriginalMetadataPopulated(True)
clsOMEXMLService = javabridge.JClassWrapper('loci.formats.services.OMEXMLService')
serviceFactory = javabridge.JClassWrapper('loci.common.services.ServiceFactory')()
service = serviceFactory.getInstance(clsOMEXMLService.klass)
metadata = service.createOMEXMLMetadata()
rdr.setMetadataStore(metadata)
rdr.setId(path)
root = metadata.getRoot()
first_image = root.getImage(0)
pixels = first_image.getPixels()
# The plane data isn't in the planes, it's in the tiff data
for idx in range(pixels.sizeOfTiffDataList()):
tiffData = pixels.getTiffData(idx)
c = tiffData.getFirstC().getValue().intValue()
t = tiffData.getFirstT().getValue().intValue()
print "TiffData: c=%d, t=%d" % (c, t)
I had the same issue with ome tiffs, and the solution works awesomely for me. However, I do not only want to read metadata but also the actual image data. Here, obviously, I get the same issue when using the 'standard' reader via bioformats, and image dimensions do not match. Therefore I would like to go the javabridge-only-route [should this post be transferred to javabridge, then?]. Unfortunately I am not quite successful here, though. I have been trying to use a concoction of the above solution and the example in https://github.com/CellProfiler/python-bioformats/blob/master/bioformats/formatreader.py
My code then looks like this:
path = 'path/to/image/file.ome.tif'
sizeSTZCYX = np.array([1,2,3,4,5,6])
rdr = javabridge.JClassWrapper('loci.formats.in.OMETiffReader')()
rdr.setOriginalMetadataPopulated(True)
clsOMEXMLService = javabridge.JClassWrapper(
'loci.formats.services.OMEXMLService')
serviceFactory = javabridge.JClassWrapper(
'loci.common.services.ServiceFactory')()
service = serviceFactory.getInstance(clsOMEXMLService.klass)
metadata = service.createOMEXMLMetadata()
rdr.setMetadataStore(metadata)
rdr.setId(path)
ChannelSeparator = javabridge.JClassWrapper(
'loci.formats.ChannelSeparator')()
cs = ChannelSeparator(rdr)
img = np.zeros(sizeSTZCYX, dtype=np.float64)
try:
for sidx in range(sizeSTZCYX[0]):
rdr.setSeries(sidx)
for tidx in range(sizeSTZCYX[1]):
for zidx in range(sizeSTZCYX[2]):
for cidx in range(sizeSTZCYX[3]):
img[sidx, tidx, zidx, cidx, :, :] = cs.openBytes(
cs.getIndex(zidx, cidx, tidx))
This throws an Error: TypeError: 'JWrapper' object is not callable
Which makes total sense, but I have too little insight into the inner workings of the javabridge to get any further. Any help would be highly appreciated.
Hi Heerpa,
Are you getting the exception in service = serviceFactory.getInstance(clsOMEXMLService.klass)
? Offhand, you might have better luck with this:
clsOMEXMLService = javabridge.JClassWrapper('java.lang.Class') \
.forName('loci.formats.services.OMEXMLService')
serviceFactory.getInstance(clsOMEXMLService)
I didn't check it, should work.
Hi Lee,
oh sorry, no I get the exception in cs = ChannelSeparator(rdr)
. Apparently I can't call the javabridge wrapper with the reader.
The previous part about clsOMEXMLService
works fine. Replacing it with your suggestion throws
javabridge.jutil.JavaException: loci/formats/services/OMEXMLService
in line 918 of javabridge/jutil.py
Oh piece of cake:
cs = javabridge.JClassWrapper('loci.formats.ChannelSeparator')(rdr)
Does that work for you?
That works perfectly. Now I have an issue with broadcasting the byte array into a 2D uint16 array, but that should be googlable. Thanks very much for the immediate rescue!
First I should commend the creation of this package. I had just about given up (at least temporarily) on using bioformats due to the lack of python support, when I realised this package had been released. Thanks for that!
I do, however, have some issues with actually using the package. I have tried files from two instruments, and in all cases I am unable to read metadata from individual planes. Note that this metadata is readable by the BioFormats library itself, because I can see it using the BioFormats Import plugin in ImageJ.
To take an easy example, consider one of the bioformats sample files, say:
http://www.openmicroscopy.org/Schemas/Samples/2015-01/bioformats-artificial/multi-channel-time-series.ome.tif.zip
This file contains the following metadata
Based on the documentation, I expected that using the code below, I would be able to traverse this tree of information:
As you can see, the code does not give the correct number of channels, nor the correct number of planes or SizeT.
Now, this example is perhaps somewhat artificial (the XML string that is extracted is correct, so I could in principle traverse its tree manually). However, for all other cases I have tried, I have been unable to read any of the information of the individual planes, and
get_plane_count()
always returns 0. Am I, somehow, using the package incorrectly?