-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
additional dimensions (excitation and emission wavelength) #14
Comments
An example with additional dimensions would be great! I'll see if I can find information on what 6-8 are used for. I've been unsuccessful finding much documentation about the format, so I welcome help. In terms of implementation - where do you think these units should go, and what should they be called? At the moment, I'm in favor of leaving the attributes relating to the image data block in |
Thanks for the very quick response! I recorded some example_data today: one file where I imaged in the x-z plane rather than x-y, and another one where I used the 'spectroscopy' option of imaging as a function of excitation or emission wavelength.
I fully agree with this, but to clarify: in the case of the excitation/emission wavelength I referred to, an image series is recorded as a function of wavelength, in exactly the same way that an xyz dataset is xy images as a function of z position. These are treated in identical manner in the metadata as x, y, time, etc., which I why I think it makes most sense it would go in As for the name: the Leica software we use lists these options with the small and capital Greek letter lambda for the detection and illumination wavelengths respectively. In the xml data they are written out as |
Thanks for the clarification. It makes sense that the data in the file might be organized in the same order of the dimensions listed above. The confusing thing is where 'channel' fits in to the organization system. From the previous images I've built this from, it's right after X and Y. I think it will be fairly straightforward to figure out the offsets for these dimensions. but I'm not sure exactly how to adapt the API to handle all of the dimensions. I don't know if having all of the options in Would it be necessary to make the respective iterators for each dimension? |
I assume that at least dimID 6-8 also exist as valid dimensions, and there may be more from 11 onwards we don't know about, so this would potentially give a very long list of optional arguments to What I personally did like about that, is that it automatically checked which dimensions were present and all functions worked with an arbitrary number and combination of the possible dimensions (but always in a fixed order, following the table above). Instead of optional arguments for each possible dimension (like
Basically what we can do is take a series of images where e.g. the detection wavelength is systematically varied in steps, like in a spectroscope. You then get something like a video where wavelength is varied rather than time, so this is really a different kind of 'experiment' than just setting up the detection wavelength and then recording a time series. When taking a spectroscopy series, it is treated as a dimension in the same way that the others (time, z-position, etc) are, and is listed in the part of the xml data with dimensions where your code finds
I haven't really looked at the iterators yet, but depending on what you think about the stuff above there could be a single iterator function where you can specify which dimension to iterate over. I don't know how doable that would be to implement, but API wise it could be nice. Thanks again for your time and for putting these codes up, it's very useful for me and some of my colleagues to be able to work with lif files in python directly. If you want, I'd also be happy to get involved and help out where I can with some of these things. |
Thanks for taking the time to provide the detailed feedback and information. I've thought about this for a bit and I think I know how to begin to approach this problem in a few parts.
Without the benefit of consistently located sliders in a UI, I'm having a hard time coming up with a consistent API to access (up-to) 10 dimensions + channels. I think I'll leave the current API alone because it covers the most common scenarios: imaging XY planes in some number of Z-dimensions and M-tiles. This also preserves capabilities for people / packages using the current setup. The less (but perhaps increasingly) common scenarios include your example of an XZ image, or images spectroscopic images. For these scenarios, I think creating a generalize-able function makes sense. A design choice/limitation is that the function will always need to return a 2-dimension 'image' (even if that is a n x 1 dimension image) in order to use PIL. This is similar to how ImageJ/Bioformats seems to work. This function may be something like
This might also need a new numbered tuple for the dimensions, because the order of these might change. Perhaps An example usage might be: from readlif.reader import LifFile
new = LifFile('./path/to/file.lif')
img_0 = new.get_image(0)
for i in range(img_0.dims_n[3]):
img_0.get_arbitrary_frame(display_dims=(0,1), c = 0, fetch_dims={3: i}).show() For most experiments, I can't imagine that all of the dimensions would be changing / iterated - but the code above isn't super readable either. Perhaps this strategy is getting too complicated, but I'll see if I can make a prototype in the dev branch. These ideas are still a work in progress, and I'd be happy to get feedback. Another (possible) option is to just make a new LifImage-like class that just returns everything in a n-dimension numpy array, but there is some other tricky decisions about how to keep the memory footprint low. Time-lapse imaging can easily end up being hundreds of GB, which is why I made this whole thing in the first place! |
I was thinking about this after my last comment as well, and I think the easiest thing to do is always return a 2D image but take whatever the first two dimensions are in dimension metadata or byte order for these images (kind of what you suggested for the default of I guess that would also cover the majority of use cases, and I like the idea of a function for arbitrarily oriented images that covers the rest. The specific form of
For what it is worth, the lif metadata treats channels as a separate thing to the dimensions, so it would be more consistent to keep it as a separate parameter. Not sure if a function which returns a multidimensional numpy array will really add much to these. While that is what I often use for further processing, it is easy to construct by iterating over a |
I haven't forgotten about this! I have a working example up in the dev branch. This branch isn't current with main, it's based on 0.4.1. Instead of calling the function A couple of new attributes were added to LifImage:
To use this, Right now, it doesn't support channels - do you happen to be able to make a multi 'channel' (detector?) image similar to the one you made before? I can probably make some inferences from the other images I have. This still needs code cleanup, documentation, and speed optimizations. Things will change, but if you have a chance to check this on your usecase, I would appreciate it! |
Hi Nick, This looks great already. I'll have a look and see if I can test this on some of my data tomorrow morning.
If you end up changing it, maybe something like
I can record some multichannel test data tomorrow, but was that for the wavelength sweep measurements, or for the xz plane imaging? |
Excellent idea! Perhaps
The type of image doesn't really matter. I'm mostly interested in testing that the data is organized in the same way, where 'channel' acts like dimension 11. I think the X-Z-Y image would be sufficient. |
Good point that a slice isn't necessarily 2D, get_plane sounds pretty clear to me. Here's some 2-channel test data, with an xz image, xzt series and xzy series. |
Thanks for the test data! I'll see if I can get channels working this weekend. Before I forget to ask, would I be able to include all of these test files in the github repo / project as part of the unit tests? The other challenge here will be solving the IO problem. Right now, the code sets the offset for each pixel, reads that pixel, and moves to the next pixel. This is simply a bad approach for efficiency. This is relatively simple to speed up when loading the first two dimensions by just doing a long sequential read. This gets harder when arbitrary dimensions are called - there is no way to make this sequential (as far as I can tell?). This starting to get outside of my current python knowledge, so there are probably approaches I'm overlooking. The common theme from what I'm finding, is that every approach will trade memory for speed. Perhaps using To accommodate large data, I think I would be able to add an option to |
Yes, absolutely.
When one of the |
In the latest commit to the dev branch, I've handled the situation where the image is ultimately read in one big block - that really helped with the speed. Try as I might, I can't figure out a good way to get the 'arbitraty access' faster without involving numpy. It might be worthwhile to do.. given that most of the downstream processing that would happen with this data would involve numpy, but I'll need to think about that. Reading in different channels also works in this update, thanks for the example files! I've also renamed the new function to This is probably close to done feature-wise. After some code cleanup (this is all pretty ugly), updates to the documentation, and tests, this will probably make it up on to the main branch. |
Version 0.6.1 is up with |
In addition to the currently supported dimensions, data may be recorded with varying detection and excitation wavelengths. These are denoted by DimID 5 and 9 in the xml metadata. It would be nice and I think fairly straightforward to implement these. I am not aware what DimID's 6-8 are used for, but it may be documented somewhere.
DimID
I can provide example files for wavelength sweep data, if this would be helpful.
The text was updated successfully, but these errors were encountered: