-
Notifications
You must be signed in to change notification settings - Fork 45
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
Add support for loading multiple single channel TIFFs #609
Comments
Is this a standardized format? Looking at the PR, there is a CSV but who makes that? I see you comment here that it "only supports flat tiffs" but who makes that decision? I don't think we want to be implicitly maintaining a custom format. @manzt any thoughts here? It seems like a use-case that has come up before (flat/individual non OME-TIFF) but we have never really addressed it since we have correctly assessed, given that @rj3d made a PR, that people can write custom loaders. |
Hey! I'm the maintainer of a tool called Mantis Viewer. I met with you and the rest of your group a few months ago and discussed using Vitessce to replace the Mantis Viewer UI. We talked about adding a flat tiff folder loader to Viv so that Vitessce could support loading a folder of flat tiffs as well. I think using a folder of flat tiffs is fairly common in the biological imaging analysis world. Mantis has ~50 users in at least five different scientific groups, and most of the users analyze their data as a folder of flat tiffs. Ideally I wouldn't have to maintain a forked Viv and a forked Vitessce, so I'm open to any changes that you want (that still support multiple flat tiffs) to get this accepted. The CSV is not a standardized format, and I'm open to changing or replacing that part. The loader has two ways to get files. For local files the user can just drag and drop a bunch of tiffs to the viewer. For remote files, ideally a user would be able to pass in a URL of a directory, but browser JS cant list all of the files in a web directory. I added the CSV file so that users could pass multiple URLs to be opened, but there are other ways this could be accomplished. Maybe a comma separated list of URLs? I'm very open to any suggestions you might have. |
Right yes! Ok! I thought the name looked familiar! @keller-mark and I are in the process of updating Vitessce right now to allow for registering custom loaders and much more (mostly Mark, I am just slowly reviewing the flood of PR's). @keller-mark are the loaders exposed yet as a custom format? |
So, I'd say, fear not, because your code will get used somewhere and you can stop maintaining two forks! |
My two cents would be that ideally:
since these are standard (or at least widely used) formats. Once those loaders are exposed by Viv, it should be very simple for external code to extend / compose the loader for individual TIFFs (for instance in order support a folder of TIFFs based on a custom CSV format). Any additional metadata required by Viv for rendering (i.e., any properties that are not in the TIFF standard) should be passed to Viv loaders using the standard OME-XML JSON format used by other loaders. As far as then using a custom Viv loader in Vitessce, yes a plugin file type could be used. The OmeZarrLoader class could be a starting point for a developer who wanted to implement a plugin file type for image files. |
@ilan-gold awesome, glad to hear! Based on @keller-mark's comment, where do you think this loader should live? I think my PR could be used as, or easily modified to be, an individual TIFF loader. For local files it already acts as one. A user can drag and drop one (or multiple) flat tiffs and they will load. Currently remote TIFFs have to be loaded from a CSV specifying the URLs, but I would be happy to replace that. I included the CSV format because I couldn't figure out how to load multiple TIFFs from a URL. It would be easy to add loading single remote TIFFs, but I could use suggestions on how a user could specify to load multiple remote TIFFs. We have some users who also use Mantis to visualize multi-page, non-OME tiffs. I was planning to write a loader for these as well if you both open to it. |
I think what @keller-mark is saying is that you get to keep the loader in your repo, unfork viv/vitessce, and then just "register" the loader as a plugin file type for Vitessce: http://vitessce.io/docs/dev-plugins/#plugin-file-types So you'd probably have some sort of TiffCSVSource that you implement that returns the Viv loaders, similar to what Mark linked to in the OmeZarrLoader, which returns a loader and metadata. |
Right, but I was also suggesting that a Viv loader for standard (non-OME) TIFFs could live in the Viv core. What are your thoughts on that @ilan-gold? Once that was in place, then a plugin file type for the CSV-based TIFFs would look like: import { loadTiff } from '@hms-dbmi/viv'; // Non-OME-TIFF loader in Viv core
function loadMultipleTiffsFromCSV(csvData) {
const myTiffsFromCSV = csvData.map(row => loadTiff(row.url));
// ...
}
class MultipleTiffsFromCSVLoader extends AbstractTwoStepLoader {
// ...
}
registerPluginFileType('raster.csv-tiff', 'raster', MultipleTiffsFromCSVLoader); |
Ah sorry yes @keller-mark you're right. I think we would be willing to accept a PR for just generic TIFFs but I am not sure what level of abstraction would fit into our repo here. Perhaps just the |
Yes I was suggesting the level of abstraction of a core loader that supports individual TIFF files, and then leave it up to external users to implement anything more custom (e.g., for custom folder/CSV structures) |
@manzt what do you think? If you're open to the idea of an individual TIFF loader than I will modify my PR to remove anything that provides metadata outside of the TIFF. |
@manzt do you want to weigh in here? I would be willing to accept a more generic tiff loader for loading from a set of files, but I want to get your opinion here. Seems like a generic enough use-case. Perhaps we could make the axis of selection arbitrary (like z-stack instead of |
Hi all - thanks for your patience. I am on vacation at the moment and was waiting to reply until I'd had a closer look at this thread. I'll be back soon (this weekend) but also don't want to block anything. |
Ok, just had a look at the PR and a read through of this thread. Thanks for your patience! I think it would be generally useful to have a reusable interface for TIFF-based images in Viv's core, which is why I tried to make the
|
indexer: (sel: PixelSourceSelection<S>) => Promise<GeoTIFFImage>, |
We call this special function an Indexer
and have two separate implementations depending on the (OME-)TIFF type.
viv/src/loaders/tiff/lib/indexers.ts
Lines 10 to 18 in 000b9b5
/* | |
* An "indexer" for a GeoTIFF-based source is a function that takes a | |
* "selection" (e.g. { z, t, c }) and returns a Promise for the GeoTIFFImage | |
* object corresponding to that selection. | |
* | |
* For OME-TIFF images, the "selection" object is the same regardless of | |
* the format version. However, modern version of Bioformats have a different | |
* memory layout for pyramidal resolutions. Thus, we have two different "indexers" | |
* depending on which format version is detected. |
Implementing a multi-file TIFF indexer
In a sense we already do support what @keller-mark suggested (since these are supported by GeoTIFF
).
the Viv core would contain loaders for:
individual (non-OME-) TIFFs
multi-page (non-OME-) TIFFs
BigTIFF
It's just an Indexer
is required to provide semantics over what "dimensions" are, and this requires parsing some additional metadata. So in your case, a load
function would initialize a custom multi-file Indexer
to accommodate this layout described in the CSV.
A benefit here is that GeoTIFF
objects can be instantiated on first access from within the async indexer.
Summary
My hope that we can figure out a solution that reuses the TiffPixelSource
to accommodate your use case, rather than creating a new PixelSource
for a variant of a format we already support. The primary issue, as identified by @keller-mark, is that some metadata is required to provide semantics to non-xy dimensions of image stacks, and this metadata can be formal (OME-XML) or informal (e.g., your CSV).
Perhaps (a more generally solution) we can think of exposing a custom loadTiffStack
API (or similar) that makes this notion of "stacking" tiffs and "indexing" using a selection explicit:
loadTiffStack([
{ source: "http://example.com/c1.tiff", selection: { c: 0 } },
{ source: "http://example.com/c2.tiff", selection: { c: 1 } },
{ source: "http://example.com/c2.tiff", selection: { c: 2 } },
]);
And that way users can parse their own metadata source and reuse the same indexer, etc.
let pixelSource = parseCsv(url)
.then(urls => urls.map((url, c) => ({ source, selection: { c } })))
.then(loadTiffStack);
@manzt, thanks very much for the detailed explanation! I think I understand, but I want to run what I'm thinking by you to make sure. I want to add the functionality in my TIFF folder loader to the TIFF loader and use the TiffPixelSource instead of my new TiffFolderPixelSource. To accomplish this, I'm thinking I would modify the TIFF folder indexer I wrote to work with the TiffPixelSource, create a new file in the tiff loader folder called tiff-stack.js (or similar) to handle indexer and metadata creation, and then exposing the new TIFF stack functionality through the tiff loader's index. Let me know if my understanding and proposed application is correct or if there's anything I should change. If it sounds good to you, I'll make the changes to my PR and report back when I'm done. |
@manzt @keller-mark @ilan-gold what do you all think of my understanding/proposal? |
@rj3d This makes sense to me. This feature would make the most sense to me under import { loadOmeTiff, loadTiffStack, loadOmeZarr } from '@hms-dbmi/viv'; Ideally we could generalize a way to "stack" single-channel tiffs together with an indexer. My hope is to decouple the metadata describing this "virtual" stack from Viv (e.g,. the CSV), and that we can work towards an |
@manzt great, thanks! I'll update the PR and ping you when it's done. |
@manzt @ilan-gold @keller-mark just updated PR #610 with the requested restructure. Let me know what you think and if there are any other changes I should make! |
Ok great! I'm at a conference this week but should be able to have a look when it's over. |
Sounds good, thanks! |
Hi there! I got this stacked flat TIFF solution running in my environment, so thank you! Is there a possibility of easily supporting multiple progressive OME-TIFFs? That's the target I have now. Even better would be to support progressive TIFFs (TIFFs with multiple subresolutions) as well as OME-TIFFs. Can you suggest how I could merge the logic between this and the default Viv behavior which does support a single OME-TIFF, so that we can have multiple OME-TIFFs with progressively-loaded or zoom-loaded subresolutions? |
@pace-ls From what I can tell, your main idea here would be to allow for folders of pyramidal tiffs? That seems reasonable. |
Not necessarily pyramidal, but just multiple single-channel OME-TIFFs that would display as if it were a single OME-TIFF. Like this file for example: https://avivator.gehlenborglab.org/?image_url=https://viv-demo.storage.googleapis.com/LuCa-7color_3x3component_data.ome.tif. It has 4 channels on page load, another 4 in the dropdown, a total of 8 channels in one file. What if instead they were 8 files, each with a single channel, imported like |
I don't think this is something we want to support in Avivator at the moment since the contribution in #610 was meant to be general i.e you provide the indexing so that |
Someone else asked about this as well in #580 so seems like it's something we'd be happy to support. |
User story
Most of my images take the form of folders containing multiple standard TIFFs that each contain one channel. It would be nice to be able to visualize these images with Viv and Avivator.
Preferred solution
Add support for loading multiple single channel TIFFs to Viv and Avivator.
The text was updated successfully, but these errors were encountered: