Skip to content
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

SLIC segmentation results differ depending on input image datatype #5512

Closed
loicdtx opened this issue Aug 16, 2021 · 1 comment · Fixed by #5518
Closed

SLIC segmentation results differ depending on input image datatype #5512

loicdtx opened this issue Aug 16, 2021 · 1 comment · Fixed by #5518

Comments

@loicdtx
Copy link

loicdtx commented Aug 16, 2021

I just realized that segmentation output can vary a lot depending on the datatype of the input array.

See reproducible example below

from skimage import segmentation, data
import numpy as np
import matplotlib.pyplot as plt


img = data.cat()
labels_0 = segmentation.slic(img)
labels_1 = segmentation.slic(img.astype(np.float32))
fig, [ax0, ax1] = plt.subplots(1,2)
ax0.imshow(segmentation.mark_boundaries(img, labels_0))
ax1.imshow(segmentation.mark_boundaries(img, labels_1))
plt.show()

Version information

3.8.10 (default, Jun  2 2021, 10:49:15) 
[GCC 10.3.0]
Linux-5.8.0-63-generic-x86_64-with-glibc2.32
scikit-image version: 0.18.2
numpy version: 1.21.1
@grlee77
Copy link
Contributor

grlee77 commented Aug 17, 2021

I think it is not so much the datatype, but the scaling that matters here. Internally skimage.img_as_float(img) results in rescaling the uint8 image to floats in the range [0.0, 1.0]. The .astype call here converts to floats with values in the original integer range of img. This difference in absolute scale is the source of the difference.

Using the following gives labels_1 that looks the same as labels_0

img_f32 = img.astype(np.float32)
img_f32 /= img_f32.max()
labels_1 = segmentation.slic(img_f32)

For this algorithm I think we should always just rescale internally to [0.0, 1.0] range so the default compactness value is a reasonable starting point. A few releases back img_as_float always rescaled to [0, 1.0] but in more recent releases it preserves the original floating point range. We do in most cases want to move toward preserving range, but this function only outputs labels anyway, so I think it is best to just the input to [0.0, 1.0] so that the default settings are reasonable regardless of the input data scale.

@grlee77 grlee77 added this to the 0.19 milestone Aug 17, 2021
grlee77 added a commit to grlee77/scikit-image that referenced this issue Aug 18, 2021
closes scikit-imagegh-5512
Currently img_as_float rescales the range of integer inputs, but leaves floating point range unnormalized.
The compactness parameter needs to be tuned based on image scale, so it is based to just always rescale
to range [0, 1] internally so the default is a reasonable starting point regardless of the input image
scale.
@grlee77 grlee77 removed this from the 0.19 milestone Aug 19, 2021
grlee77 added a commit that referenced this issue Aug 31, 2021
* Make SLIC superpixel output robust to image scale

closes gh-5512
Currently img_as_float rescales the range of integer inputs, but leaves floating point range unnormalized.
The compactness parameter needs to be tuned based on image scale, so it is based to just always rescale
to range [0, 1] internally so the default is a reasonable starting point regardless of the input image
scale.

* TST: add slic test case veryify same segmentation across input amplitudes/dtypes

* Use copy=True so internal rescaling cannot modify the input
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants