Fix intensity normalizations#981
Conversation
1. Make it actually channel-wise;
2. Before it should take `(old_min, old_max)` of the input image (otherwise it doesn't make sense), but now it takes `(new_min, new_max)` of the expected normalized output.
Before this fix, if `lowhigh` is provided, channel-wise linear normalization is performed by:
```python
for c in ...:
img_norm[...,c] = (img_norm[..., c] - lowhigh[0]) / (lowhigh[1] - lowhigh[0])
```
which has a redundant `[..., c]` and is not "channel-wise", i.e. it is equivalent to
```python
img_norm = (img_norm - lowhigh[0]) / (lowhigh[1] - lowhigh[0])
```
This parameter, `lowhigh`, is never used in Cellpose itself, however.
Previously, only the last channel was inverted. Now, channel 0 is inverted, which is typically cells used for segmentation. This update addresses the common use case where the biological structure of interest is in the first channel. Future improvements could allow for user-specified channels.
|
thanks @qin-yu ! the lowhigh is meant to represent the values to set to 0 and 1 (e.g. pixel value 100->0 and 1100->1 corresponds to lowhigh = [100,1100]. I think the fix here would be to check if lowhigh is a list of lists (per channel) or a single list (for all channels). It's a good point that this use-case isn't covered currently. for the inversion, thanks for catching that, I think it would make sense to apply for all channels? |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #981 +/- ##
==========================================
+ Coverage 52.76% 53.70% +0.93%
==========================================
Files 18 18
Lines 4126 4132 +6
==========================================
+ Hits 2177 2219 +42
+ Misses 1949 1913 -36 ☔ View full report in Codecov by Sentry. |
Thanks for explaining! That makes sense, working on it.
Yes you are right, inverting all channels by default if |
|
Thanks so much, regarding the invert function I think it's fine with all channels, I'm not sure how much it's being used with multichannel images |
* `lowhigh` could be either a 2-tuple or a `nchan`-tuple of 2-tuple * `invert` inverts all channels Note that `lowhigh` shouldn't be combined with pre-smoothing or -sharpening.
* Tested the changes made in transform.py * Removed unused import and formatted whitespace
You are right. Let's keep it simple I have finalized the PR, including two fixes along with the corresponding tests. Please take a look when you have a moment! @carsen-stringer |
|
The failed check seems to be incidental and maybe unrelated to the PR. |
|
All checks passed 🥳 |
Hi @carsen-stringer!
During the process of packaging the custom Cellpose model for bioimage.io, I identified two potential issues with the intensity normalization implementation in Cellpose. This PR proposes fixes for:
lowhighargument is not channel-wise. In this PR,lowhighnow either takes a 2-tuple(low, high)for all channels, or anchan-tuple of that for each channel.--chan); instead, it inverts the last channel. Depending on the--chan2value, this might not be the intended channel. In this PR, all channels are inverted ifinvert.Original PR message on 25 Jul 2024
Original PR
Hi @carsen-stringer!
During the process of packaging the custom Cellpose model for bioimage.io, I identified two potential issues with the intensity normalization implementation in Cellpose. This PR proposes fixes for:
lowhighargument is not channel-wise, and the argument itself might be misused.--chan); instead, it inverts the last channel. Depending on the--chan2value, this might not be the intended channel.Please review these changes as I might have misunderstood some parts.
Fix for Min-Max Normalization by 31bc5be
For
cellpose.transforms.normalize_img()(channel-wise normalization), thelowhighoption is provided. I assume this corresponds to min-max normalization wherelowhighis a tuple representing (new minimum, new maximum).cellpose/cellpose/transforms.py
Lines 600 to 601 in 84344b0
If
lowhighrepresents (old minimum, old maximum), then normalization is not channel-wise iflowhighhas a length of 2:cellpose/cellpose/transforms.py
Lines 624 to 626 in 84344b0
In either case, the implementation needs fixing:
cellpose/cellpose/transforms.py
Lines 640 to 643 in 84344b0
The current implementation is essentially:
img_norm = (img_norm - lowhigh[0]) / (lowhigh[1] - lowhigh[0])This has been fixed by commit 31bc5be.
Fix for Invert by 91469d1
For
cellpose.transforms.normalize_img()(channel-wise normalization), there is aninvertoption for cases where "cells are dark instead of bright":cellpose/cellpose/transforms.py
Lines 598 to 599 in 84344b0
In Cellpose, the invert function is performed after
reshape(), which moves the main channel to channel 0 and the auxiliary channel to channel 1. Therefore, the primary use case is to invert the first channel (channel 0) instead of the last one (current implementation):cellpose/cellpose/transforms.py
Lines 666 to 667 in 84344b0
The variable
cretains the value from a previous loop, being 1 ifnchanis 2 or 2 ifnchanis 3.This has been fixed by commit 91469d1, but further improvements can be made to allow for a user-specified list of channels to be inverted.