AllenCellModeling / napari-aicsimageio

Multiple file format reading directly into napari using pure Python
GNU General Public License v3.0
33 stars 9 forks source link

Force napari to always use aicsimageio #37

Closed MosGeo closed 2 years ago

MosGeo commented 2 years ago

Use Case

I have a couple of napari plugins that assumes that the image is an aicsimageio format (at least the dask array is in that format). Is there a way to force napari to open files using aicsimageio whenever that is possible (e.g., even for loading png and tif files).

Solution

When the user uses the open functionality in napari, it uses the aicsimageio readers by default (even for simple formats)

Alternatives

every plugin needs to check the image data dimensions and act accordingly.

psobolewskiPhD commented 2 years ago

Have you tried messing with these settings in the preferences?

image

Because when I uncheck everything but AICSImageIO:

image

...and drag-n-drop a a png or gif I get AICSImage messages in console, so I think it's loaded via AICSImageIO:

AICSImageIO: Reader will load image in-memory: False
MosGeo commented 2 years ago

Thanks @psobolewskiPhD That did the trick. Now I just have to figure ways to turn it off from code but that should be possible. I just need to monkey around the napari code.

Two notes on my standardization journey:

  1. The dask array returned by aicsimageio seems to be 5D when it is non-RGB and 6D when it is RGB. I would have expected it to be always 6D (with the last dimension being 1 when it is non-RGB).
  2. I am not sure if there is a way to return the AICSIMAGE from the layer object in napari, is there? I guess i can inject it in the layer object on loading.
evamaxfield commented 2 years ago

Now I just have to figure ways to turn it off from code but that should be possible.

A bit confused. If you are using Python to launch napari why not just provide the image to napari itself with napari.view_image(nd_array).

  1. The dask array returned by aicsimageio seems to be 5D when it is non-RGB and 6D when it is RGB. I would have expected it to be always 6D (with the last dimension being 1 when it is non-RGB).

Yes. We could probably better document this behavior in aicsimageio docs... The problem with Samples is it really shouldn't exist imo. The only reason it does is because CZI images can be both multi-channel and RGB. Other formats will take these and just expand the RGBness to more channels. We didn't like that behavior so instead we basically just had to do something which was make them optionally present. There is an argument to be made about the last dim always being 1 in the non RGB case but most images that we work with are non-RGB. Maybe we make it a param on the AICSImage object. fill_all_optional_default_dims=True. Idk, we just haven't seen a strong case for or against. And samples is just annoying.

  1. I am not sure if there is a way to return the AICSIMAGE from the layer object in napari, is there? I guess i can inject it in the layer object on loading.

nope, the layer object stores just the array + some metadata i believe.

MosGeo commented 2 years ago

Thanks for the great info.

To give you more context: my objective is to design a custom napari build (compiled) with a number of plugins that is specifically designed for rock thin section analysis. My users do not know any Python and so everything everything needs to be in the interface.

The approach: utilize napari as a package in a virtual environment and create a special launcher to apply any needed customization. This is easier to maintain compared to forking and customizing the napari source code. I just update napari as new versions are released and make sure nothing is broken in the application of any customization. It also means that I can contribute to the main napari branch if needed as I am always up to date.

For RGB, as long it is a predictible behaviour (which it is right now), it shouldn't be any problem. Personally, I like the the idea of fill_all_optional_default_dims=True solution in aicsimageio and aicsimageio-napari. This would mean a more predictible behaviour without resorting to checking the dimensions everytime.

evamaxfield commented 2 years ago

Can you open an issue for the fill_all_optional_default_dims param in aicsimageio. Will discuss with team.

tlambert03 commented 2 years ago

hey @MosGeo ... how's this working for you? Do you have a workflow you're happy with? Can we add any conveniences either here or on the napari side? or is this closeable?

MosGeo commented 2 years ago

@tlambert03 @evamaxfield

I am kind of confused about some of the most recent updates. Napari does not show napari-aicsimageio as a get-reader anymore. It shows at the bottom as an option though. How would I reproduce the old behavior: always use aicsimageio for loading any image?

image

evamaxfield commented 2 years ago

See most recent release for some details: https://github.com/AllenCellModeling/napari-aicsimageio/releases/tag/v0.7.0

If you specifically want old behavior... drop to last release? Hard to say, we moved to this single plugin / single entry point because npe2 but also because we thought our solution of "small image -> memory, large image -> delayed" would be good.

always use aicsimageio for loading any image?

I have no idea.

tlambert03 commented 2 years ago

Hey @MosGeo ... the cascading behavior in the upper tab only ever applied for npe1 plugins and is going away soon: https://github.com/napari/napari/issues/4000

It was considered too confusing. aicsimageio is probably the only plugin case that can handle so many extensions that it would make sense to have it always be used (which is great!)

How many different file types are you using? lots? As a slightly annoying short term solution, you can just select aicsimagio (And click the "save preference" checkbox) the first time you drop a new filetype onto napari...

however @DragaDoncila, do our preferences have a "*" option?

psobolewskiPhD commented 2 years ago

@MosGeo When you open a new image—at least by drag and drop—napari should ask you if you want to use a certain plugin for all future cases. You can also change that in that bottom area of your screenshot. So I don't think you can set all images, but you can set all .tiff, or all .lif, etc.

DragaDoncila commented 2 years ago

@tlambert03 you could add a * preference! If you're doing it from the GUI, your plugin would also have to declare it accepts all filename_patterns (otherwise the plugin wouldn't be a valid selection). Pretty sure we make no checks if you assign the setting directly via the API.

tlambert03 commented 2 years ago

if you're doing it from the GUI, your plugin would also have to declare it accepts all filename_patterns (otherwise the plugin wouldn't be a valid selection)

yeah ... this one is the tricky bit. since aicsimageio (properly?) exhaustively states all of the file extensions their support, and does not add *. so it kind of means that an end user would have to enter every one of the filename patterns?

MosGeo commented 2 years ago

@tlambert03 @psobolewskiPhD @DragaDoncila

Thanks for all the explanations. This clears up everything.

I don't have lots of file formats. Mainly the usual suspects (tiff, jpg, png, and czi in the near future). CZI is now handled externally and converted to tiff before handling in napari. That being said, even just these image types would be an issue. I would like to have the least friction to the user as possible. And allowing them to select things (even if it is once for every format) is a recipe for issues as some are bound to select the wrong things and all my plugins would break. My plugins are all assuming AICSIMAGE.

Now, I am noting a few things with napari-aicsimageio:

  1. Filenames are not assigned as image layers names. This is an easy fix.
  2. Recent changes made it to now return to pass a squeezed version of the dask array (#32) . This breaks the standardization of of aicsimage which is unfortunate. The good thing is that metadata now includes the aicsimage object.

Yesterday, I went a little bit crazy and just replaced all napari open actions with my own that always use aicsimageio now matter what. Now, they work wonderfully and I can control them exactly the way I want. I still have not replaced the drag-drop action to viewer but for now, I think users shouldn't have issues.

For now, this monkey patch solution should hold up for a while. I think the better option is to change the napari settings file before launching napari. Where is the settings file?

DragaDoncila commented 2 years ago

@MosGeo you can check the path to your settings file using

from napari.settings._napari_settings import _CFG_PATH

If all you're wanting to do is edit the reader preferences though, you could do it through the public interface e.g.

from napari.settings import get_settings
get_settings().plugins.extension2reader = {'*': 'napari-aicsimageio', **get_settings().plugins.extension2reader}
evamaxfield commented 2 years ago

I'm tempted to put that snippet into our README 🤔

tlambert03 commented 2 years ago

Yeah that would be good. But @DragaDoncila, does it matter that we don't have '*' as a pattern in the manifest here? Or was that limitation just for the GUI entry?

DragaDoncila commented 2 years ago

@tlambert03 just a gui thing. When the user types patterns we filter the readers they can choose to just those that accept that pattern. I thought about allowing the user to just yolo it but there didn't seem to be a compelling reason as it could be achieved via command line. But happy to revisit

MosGeo commented 2 years ago

@DragaDoncila the public api is working perfectly to force things the way I want it without user interactions. Very nice. I consider this issue resolved.

tlambert03 commented 2 years ago

Thanks for the feedback, and thanks a lot @DragaDoncila!

I agree with @evamaxfield, that would be good for the readme

evamaxfield commented 2 years ago

Added a note: https://github.com/AllenCellModeling/napari-aicsimageio/commit/94e5392269cb39709d76a86b5def9c338be14619