-
Notifications
You must be signed in to change notification settings - Fork 3
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
LabeledImageServer coordinate problems #25
Comments
I see in the docs for # The image will only have one resolution level. This is also the case for QuPath’s Java I don’t know / remember the exact reason, but I suspect it is related to the fact that downsampling labeled images can have weird effects because of interpolation - and we’d likely get very different results if we downsample a rasterized version vs. downsampling the geometries, and a standard The thinking was that you’d create a labeled image at the exact downsample you’d want to export, then discard it afterwards. This means ‘later on’ is not expected to be relevant, and export at the downsample specified from the beginning should be free of any rasterization artifacts. Our use of arbitrary downsamples in requests later has posed problems in the past within QuPath. If (for example) we want to export 512x512 tiles with downsample 2.3235245 then this can be very awkward to do from external code because we can’t specify the output tile size. Rather, we have to figure out the coordinates to request in the full-resolution image and trust that QuPath will make the same internal resizing/rounding decisions that we expect. Furthermore, we can have trouble on tile boundaries, where some tiles will have coordinates rounded up and some rounded down. For most ‘natural’ images this isn’t a big deal, but could be more obvious for labeled or binary images if it can result in one-pixel gaps or line thickening. These problems are avoided/reduced if the size is fixed at the point the For a ‘natural’ image in QuPath, we can use Lastly, the QuPath version supports outlining regions - and then defining one downsample is crucial. Changing this may well may sense, but I think we need to proceed with caution and lots of example use-cases - and we should consider both the QuPath and QuBaLab behavior together. |
The problem I have with the current design is that we allow both. First we do this conversion of geometries to the downsampled scale, but then
Then we should at least disallow people from setting the downsample twice by overriding Anyway for the time being I've just added failing unit tests to demonstrate that it's currently broken. |
If that’s all #26 does, can you give it a more meaningful title and description please? Admittedly I haven’t tried to run any of this since the major refactoring, but based on the description here, I don’t understand why the current behavior is ‘broken’. And if I understand the alternatives correctly, I fear they might easily be broken in worse ways. If I remember correctly Therefore it’s perfectly valid to call I can think of two alternatives, but both are problematic:
Think how it might work in a viewer. There, pixels can be displayed at any downsample. If we always request at the display downsample, computing this every time would be extremely computationally intensive - we’d need to make an enormous number of requests when zooming in or out. What we probably want are that the labels are consistent and trustworthy at one resolution, but can be requested (with raster-based downsampling) at specific lower resolutions for a fast display. Also, the viewer should be able to rendered at any necessary resolution in between, again with raster-based scaling. |
At the moment it's just failing unit tests. If the implementation does not change, then I'll change the tests so they pass and document the current behaviour more clearly. I'll add a proper description either way, but there's not content to document yet.
Maybe I'm misunderstanding, but it's certainly not intuitive at the moment. That's why I started with failing tests. Hopefully the tests demonstrate what is currently unintuitive (it arose from a user testing the notebooks).
As far as I can tell the relevant calls are in the base labeled_server = LabeledImageServer(qupath_server.metadata, annotations, downsample=2)
labeled_server.read_region(downsample=1, x, y, width, height) should be substantially different to labeled_server = LabeledImageServer(qupath_server.metadata, annotations, downsample=1)
labeled_server.read_region(downsample=2, x, y, width, height) in terms of how we process annotations, or in terms of the expected output, or in terms of the x,y,width,height coordinates we supply. Currently, the two calls won't return the same region of the parent server, and one will use rasterised resizing. We should definitely be letting users know that one of these is "right" and one is "wrong", if we keep the current implementation.
Is there caching currently happening in qubalab? I can't identify any. If you mean on the qupath side, I understand the desire to match behaviour exactly, but I also fear writing a bad implementation here just to match behaviour in QuPath.
If it's something that will produce unintuitive results we should at least be warning users that they're probably not doing things the "right" way? |
There is also one very concrete way in which the current implementation is definitely broken. We try to draw on the pillow based on the coordinates of the entire LabeledImageServer, without considering that we may actually be in a region which is offset from the origin. |
No caching in QuBaLab currently I think, but there is in QuPath (and potentially could be in QuBaLab in the future). I think working off specific use cases would be useful. To me, the Potential use cases:
In all cases, we can use dask arrays. I think that, ideally, we’d hide as much
Because in the first you’re requesting that the labels are generated at a downsample of 2, then you are (weirdly) requesting them to be upsampled by a factor of 2. It’s like you’ve exported the image with downsample=2 and then zoomed in. The upsampling cannot create new information or detail that is not present in the original (downsample=2) version. In the second, you’re requesting the labels to be generated at the full image resolution, then you are requesting a quick downsampled version of them. This will lose information and have rasterization artifacts, as raster-based downsampling usually has. It’s like you’ve exported the image at full resolution and zoomed out. The general idea of an If a user simply wants a labeled image as a dask arrays, then they shouldn’t need to worry about With regard to coordinates that need to be passed, I don’t know… I haven’t used QuBaLab recently enough to know what it’s doing or to confirm it’s the same as QuPath. But as long as I can request my original image at one downsample and a labeled image at the same downsample, I’d rather get them both as dask arrays of the same size and just use indexing - not have to deal with lots of
Fair enough, that does indeed sound broken. |
Okay I've got you backed into a corner now Pete 😆 because that's not what the code will do. The labeled server has one downsample level, which is 1, so I'm actually retrieving the region at a downsample of 2 with respect to the original image. There'll be no transform happening in
|
Fair, that sounds like a bug :) I haven’t actually run any of the code, I’m really just describing what I think/hope it does. I maintain that the two different scenarios should do something different… even if it’s not the different thing they currently do. And that the original proposed fix could open up a whole new set of problems. I remember encountering this in QuPath with I think the approach I eventually took was to always assume Can’t 100% guarantee that’s what happens though, as I’m not currently using a proper computer and haven’t checked anything. As mentioned at #26 (comment) the entire |
Yes, the design sounds sensible; the idea behind adding the failing tests was to show that our design isn't really working as stated, or at least not in an intuitive/transparent way. Ideally we resolve any confusion like this behind the scenes, properly test it, and present a nice streamlined user interface. Also worth noting that qubalab will still be here on Monday regardless of whether or not you spend annual leave time responding to github threads :-) |
I think I see things differently. When you create So we have now a new image. When you call
In short, I'm not bothered by the current behaviour, but it seems I'm the only one. Or maybe I don't clearly see the problem. |
Hi @Rylern I think I used to see it the same way as you, and I expect that at some point that's also what QuPath did.... until it caused too much trouble. The problem is that the main use case for a
This gist shows how that works in QuPath. Basically, One place where this is alluded to in the QuPath docs is for This is the main reason I think we should make API changes here based on the limitations we find when we write sample notebooks illustrating specific use-cases. It's much easier to export images and labeled images that match if we can use the same |
OK. I always thought that the full resolution image had a downsample of 1, so it may be needed to update some things in qubalab (or at least document this). Should I do it or @alanocallaghan are you on it? |
I am writing a short notebook or two to document things |
There are some examples here: I think #27 seems to resolve any confusing cases here |
qubalab/qubalab/images/labeled_server.py
Line 57 in 2bc2027
qubalab/qubalab/images/labeled_server.py
Lines 117 to 124 in 2bc2027
and
qubalab/qubalab/images/labeled_server.py
Lines 96 to 112 in 2bc2027
if we fix the downsample on init, then we transform the geometries on request based on the downsample and region, which seems alright
We could allow both, which makes the code a bit more complex and arguably more confusing.
The text was updated successfully, but these errors were encountered: