-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
WIP: Turn off relabelling in label2rgb #3015
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
base: main
Are you sure you want to change the base?
Changes from all commits
76a56e4
b23e179
0a784c0
8477573
9385aaa
436cb2f
6b6ad71
3070318
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
8000
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
class DefaultValue(object): | ||
"""Class to provide a dummy default value other than None. | ||
|
||
This is useful for cases where it makes sense to pass 'None' as an | ||
actual value. | ||
""" | ||
def __repr__(self): | ||
return 'None' | ||
|
||
def __str__(self): | ||
return 'None' | ||
|
||
|
||
DEFAULT = DefaultValue() |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -3,6 +3,7 @@ | |||||
import numpy as np | ||||||
|
||||||
from .._shared.utils import warn | ||||||
from .._shared._default_value import DEFAULT | ||||||
from ..util import img_as_float | ||||||
from . import rgb_colors | ||||||
from .colorconv import rgb2gray, gray2rgb | ||||||
|
@@ -39,7 +40,7 @@ def _rgb_vector(color): | |||||
return np.array(color[:3]) | ||||||
|
||||||
|
||||||
def _match_label_with_color(label, colors, bg_label, bg_color): | ||||||
def _match_labels_with_colors(label, colors, bg_label, bg_color, unique=False): | ||||||
"""Return `unique_labels` and `color_cycle` for label array and color list. | ||||||
|
||||||
Colors are cycled for normal labels, but the background color should only | ||||||
|
@@ -51,7 +52,10 @@ def _match_label_with_color(label, colors, bg_label, bg_color): | |||||
bg_color = _rgb_vector([bg_color]) | ||||||
|
||||||
# map labels to their ranks among all labels from small to large | ||||||
unique_labels, mapped_labels = np.unique(label, return_inverse=True) | ||||||
if unique: | ||||||
_, mapped_labels = np.unique(label, return_inverse=True) | ||||||
else: | ||||||
mapped_labels = label.ravel() | ||||||
|
||||||
# get rank of bg_label | ||||||
bg_label_rank_list = mapped_labels[label.flat == bg_label] | ||||||
|
@@ -75,7 +79,9 @@ def _match_label_with_color(label, colors, bg_label, bg_color): | |||||
|
||||||
|
||||||
def label2rgb(label, image=None, colors=None, alpha=0.3, | ||||||
bg_label=-1, bg_color=(0, 0, 0), image_alpha=1, kind='overlay'): | ||||||
background=DEFAULT, background_color=None, image_alpha=1, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I understood correctly, this DEFAULT thing is introduced only to help in handling such complex deprecation scenarios. I think using it for other purposes would reduce user experience and should not be recommended. |
||||||
kind='overlay', relabel=DEFAULT, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
bg_label=DEFAULT, bg_color=DEFAULT): | ||||||
"""Return an RGB image where color-coded labels are painted over the image. | ||||||
|
||||||
Parameters | ||||||
|
@@ -90,59 +96,124 @@ def label2rgb(label, image=None, colors=None, alpha=0.3, | |||||
then the colors are cycled. | ||||||
alpha : float [0, 1], optional | ||||||
Opacity of colorized labels. Ignored if image is `None`. | ||||||
bg_label : int, optional | ||||||
Label that's treated as the background. | ||||||
bg_color : str or array, optional | ||||||
Background color. Must be a name in `color_dict` or RGB float values | ||||||
between [0, 1]. | ||||||
background : int, optional | ||||||
Label to be treated as background. Currently defaults to -1, but | ||||||
will default to 0 starting in version 0.16. Use None to indicate that | ||||||
the image contains no background. | ||||||
background_color : str, tuple, array, or None, optional | ||||||
The color of the background. If None, background pixels will have | ||||||
no overlay at all and will show the image directly. | ||||||
image_alpha : float [0, 1], optional | ||||||
Opacity of the image. | ||||||
kind : string, one of {'overlay', 'avg'} | ||||||
kind : string, one of {'overlay', 'avg', 'average'} | ||||||
The kind of color image desired. 'overlay' cycles over defined colors | ||||||
and overlays the colored labels over the original image. 'avg' replaces | ||||||
each labeled segment with its average color, for a stained-class or | ||||||
pastel painting appearance. | ||||||
and overlays the colored labels over the original image. 'avg' or | ||||||
'average' replaces each labeled segment with its average color, for a | ||||||
stained-glass or pastel painting appearance. | ||||||
relabel : bool, optional | ||||||
Whether to remap input labels to be sequential. Defaults to True | ||||||
currently, but will change to False in 0.16. | ||||||
|
||||||
Other parameters | ||||||
---------------- | ||||||
bg_label : int, optional **DEPRECATED** | ||||||
Label that's treated as the background. This parameter has been | ||||||
deprecated in favor of 'background' and will be removed in version | ||||||
0.16. | ||||||
bg_color : str or array, optional **DEPRECATED** | ||||||
Background color. Must be a name in `color_dict` or RGB float values | ||||||
between [0, 1]. This parameter has been deprecated in favor of | ||||||
'background_color' and will be removed in version 0.16. | ||||||
|
||||||
Returns | ||||||
------- | ||||||
result : array of float, shape (M, N, 3) | ||||||
The result of blending a cycling colormap (`colors`) for each distinct | ||||||
value in `label` with the image, at a certain alpha value. | ||||||
""" | ||||||
if image is None and kind in ['avg' or 'average']: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
mesg = ('"image" *must* be provided when average color mode is used ' | ||||||
'in label2rgb. See\n\n' | ||||||
'* http://scikit-image.org/docs/dev/auto_examples/' | ||||||
'segmentation/plot_boundary_merge.html\n\n' | ||||||
'for example usage.') | ||||||
raise ValueError(mesg) | ||||||
if background is DEFAULT: | ||||||
if bg_label is DEFAULT: | ||||||
if np.any(label == -1) or np.any(label == 0): | ||||||
mesg = ('Starting in version 0.16, label2rgb will consider ' | ||||||
'label 0 to be background. Set background=None to ' | ||||||
'suppress this message without defining a background, ' | ||||||
'background=-1 to set -1 to be the background, or ' | ||||||
'background=0 to suppress this message and use 0 ' | ||||||
'as the background label.') | ||||||
warn(mesg) | ||||||
background = -1 | ||||||
else: | ||||||
mesg = ('The parameter "bg_label" was renamed "background" in ' | ||||||
'version 0.14 and will be removed in version 0.16.') | ||||||
warn(mesg) | ||||||
background = bg_label | ||||||
if background is None: | ||||||
background = int(np.max(labels)) + 1 | ||||||
if relabel is DEFAULT: | ||||||
mesg = ('The behavior of label2rgb for nonconsecutive labels will ' | ||||||
'change in version 0.16. Currently, nonconsecutive labels ' | ||||||
'are remapped to be consecutive before being mapped to ' | ||||||
'colors. Starting in 0.16, no remapping will occur, so that ' | ||||||
'coloring is reproducible between different images. To get ' | ||||||
'this behavior today, pass relabel=False to label2rgb. See ' | ||||||
'the discussion at ' | ||||||
'https://github.com/scikit-image/scikit-image/issues/2674 ' | ||||||
'for details.') | ||||||
relabel = True | ||||||
if kind == 'overlay': | ||||||
return _label2rgb_overlay(label, image, colors, alpha, bg_label, | ||||||
bg_color, image_alpha) | ||||||
return _label2rgb_overlay(label, image, colors, alpha, background, | ||||||
background_color, image_alpha, relabel) | ||||||
else: | ||||||
return _label2rgb_avg(label, image, bg_label, bg_color) | ||||||
return _label2rgb_avg(label, image, background, background_color) | ||||||
|
||||||
|
||||||
def _label2rgb_overlay(label, image=None, colors=None, alpha=0.3, | ||||||
bg_label=-1, bg_color=None, image_alpha=1): | ||||||
background=None, background_color=None, image_alpha=1, | ||||||
relabel=True, bg_label=None, bg_color=None): | ||||||
"""Return an RGB image where color-coded labels are painted over the image. | ||||||
|
||||||
Parameters | ||||||
---------- | ||||||
label : array, shape (M, N) | ||||||
Integer array of labels with the same shape as `image`. | ||||||
image : array, shape (M, N, 3), optional | ||||||
label : array, shape (M, N,[[ P,] ...]) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
Integer array of labels with the same shape as `image` (not including | ||||||
channels). | ||||||
image : array, shape (M, N,[[[ P,] ...,] 3]), optional | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
Image used as underlay for labels. If the input is an RGB image, it's | ||||||
converted to grayscale before coloring. | ||||||
colors : list, optional | ||||||
List of colors. If the number of labels exceeds the number of colors, | ||||||
then the colors are cycled. | ||||||
alpha : float [0, 1], optional | ||||||
Opacity of colorized labels. Ignored if image is `None`. | ||||||
bg_label : int, optional | ||||||
Label that's treated as the background. | ||||||
bg_color : str or array, optional | ||||||
Background color. Must be a name in `color_dict` or RGB float values | ||||||
between [0, 1]. | ||||||
background : int, optional | ||||||
Label to be treated as background. Starting in version 0.16, this | ||||||
default will change to 0. | ||||||
background_color : str, tuple, array, or None, optional | ||||||
The color of the background. If None, background pixels will have | ||||||
no overlay at all and will show the image directly. | ||||||
image_alpha : float [0, 1], optional | ||||||
Opacity of the image. | ||||||
|
||||||
Other parameters | ||||||
---------------- | ||||||
bg_label : int, optional **DEPRECATED** | ||||||
Label that's treated as the background. This parameter has been | ||||||
deprecated in favor of 'background'. | ||||||
bg_color : str or array, optional **DEPRECATED** | ||||||
Background color. Must be a name in `color_dict` or RGB float values | ||||||
between [0, 1]. This parameter has been deprecated in favor of | ||||||
'background_color'. | ||||||
|
||||||
Returns | ||||||
------- | ||||||
result : array of float, shape (M, N, 3) | ||||||
result : array of float, shape (M, N,[[ P,] ...,] 3) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
The result of blending a cycling colormap (`colors`) for each distinct | ||||||
value in `label` with the image, at a certain alpha value. | ||||||
""" | ||||||
|
@@ -152,32 +223,40 @@ def _label2rgb_overlay(label, image=None, colors=None, alpha=0.3, | |||||
|
||||||
if image is None: | ||||||
image = np.zeros(label.shape + (3,), dtype=np.float64) | ||||||
# Opacity doesn't make sense if no image exists. | ||||||
# Opacity doesn't make sense if no image is provided | ||||||
alpha = 1 | ||||||
else: | ||||||
if not image.shape[:2] == label.shape: | ||||||
raise ValueError("`image` and `label` must be the same shape") | ||||||
|
||||||
if image.min() < 0: | ||||||
warn("Negative intensities in `image` are not supported") | ||||||
|
||||||
image = img_as_float(rgb2gray(image)) | ||||||
image = gray2rgb(image) * image_alpha + (1 - image_alpha) | ||||||
if not image.shape[:label.ndim] == label.shape: | ||||||
msg = ('The (non-color) shape of `image` and `label` passed to\n' | ||||||
'skimage.color.label2rgb must exactly match. See the ' | ||||||
'function documentation at\n\n' | ||||||
'* http://scikit-image.org/docs/dev/api/' | ||||||
'skimage.color.html#skimage.color.label2rgb\n\n' | ||||||
'and example usage at\n\n' | ||||||
'* http://scikit-image.org/docs/dev/auto_examples/' | ||||||
'xx_applications/plot_coins_segmentation.html') | ||||||
raise ValueError(msg) | ||||||
|
||||||
# convert image to an gray-only RGB image to enable blending with | ||||||
# label colors. Note that rgb2gray is a no-op if the original image | ||||||
# is grayscale already. | ||||||
image = (image_alpha * gray2rgb(rgb2gray(image)) | ||||||
+ (1 - image_alpha)) | ||||||
|
||||||
# Ensure that all labels are non-negative so we can index into | ||||||
# `label_to_color` correctly. | ||||||
offset = min(label.min(), bg_label) | ||||||
offset = min(label.min(), background) | ||||||
if offset != 0: | ||||||
label = label - offset # Make sure you don't modify the input array. | ||||||
bg_label -= offset | ||||||
background -= offset | ||||||
|
||||||
new_type = np.min_scalar_type(int(label.max())) | ||||||
if new_type == np.bool: | ||||||
new_type = np.uint8 | ||||||
label = label.astype(new_type) | ||||||
|
||||||
mapped_labels_flat, color_cycle = _match_label_with_color(label, colors, | ||||||
bg_label, bg_color) | ||||||
mapped_labels_flat, color_cycle = _match_labels_with_colors(label, colors, | ||||||
background, background_color, relabel) | ||||||
|
||||||
if len(mapped_labels_flat) == 0: | ||||||
return image | ||||||
|
@@ -191,14 +270,15 @@ def _label2rgb_overlay(label, image=None, colors=None, alpha=0.3, | |||||
result = label_to_color[mapped_labels] * alpha + image * (1 - alpha) | ||||||
|
||||||
# Remove background label if its color was not specified. | ||||||
remove_background = 0 in mapped_labels_flat and bg_color is None | ||||||
remove_background = 0 in mapped_labels_flat and background_color is None | ||||||
if remove_background: | ||||||
result[label == bg_label] = image[label == bg_label] | ||||||
result[label == background] = image[label == background] | ||||||
|
||||||
return result | ||||||
|
||||||
|
||||||
def _label2rgb_avg(label_field, image, bg_label=0, bg_color=(0, 0, 0)): | ||||||
def _label2rgb_avg(label_field, image, background=0, | ||||||
background_color=(0, 0, 0)): | ||||||
"""Visualise each segment in `label_field` with its mean color in `image`. | ||||||
|
||||||
Parameters | ||||||
|
@@ -207,9 +287,9 @@ def _label2rgb_avg(label_field, image, bg_label=0, bg_color=(0, 0, 0)): | |||||
A segmentation of an image. | ||||||
image : array, shape ``label_field.shape + (3,)`` | ||||||
A color image of the same spatial shape as `label_field`. | ||||||
bg_label : int, optional | ||||||
background : int, optional | ||||||
A value in `label_field` to be treated as background. | ||||||
bg_color : 3-tuple of int, optional | ||||||
background_color : 3-tuple of int, optional | ||||||
The color for the background label | ||||||
|
||||||
Returns | ||||||
|
@@ -219,10 +299,10 @@ def _label2rgb_avg(label_field, image, bg_label=0, bg_color=(0, 0, 0)): | |||||
""" | ||||||
out = np.zeros_like(image) | ||||||
labels = np.unique(label_field) | ||||||
bg = (labels == bg_label) | ||||||
if bg.any(): | ||||||
labels = labels[labels != bg_label] | ||||||
out[bg] = bg_color | ||||||
is_background = (labels == background) | ||||||
if is_background.any(): | ||||||
labels = labels[labels != background] | ||||||
out[is_background] = background_color | ||||||
for label in labels: | ||||||
mask = (label_field == label).nonzero() | ||||||
color = image[mask].mean(axis=0) | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use Ellipsis instead of None?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good suggestion, 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stefanv @soupault to clarify, you mean, get rid of this object, and just use Ellipsis directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I see it, use Ellipsis instead of 'None' in this class. Your option would work equally well, though