Migration guide: from skimage to skimage2#

Hint

This document is a work in progress and still subject to change.

Scikit-image is preparing to release version 2.0 as a new package: skimage2. Alongside skimage2, we will release version 1.0.0. Versions 1.x will be using the current API. Versions 1.1.x will throw a FutureWarning upon import, as a means to notify users that they should either upgrade to skimage2 or pin to version 1.0.x.

We have undertaken this to make some long-outstanding, backward-incomptible changes to the scikit-image API. Most changes were difficult or impossible to make using deprecations alone. To honor the Hinsen principle (that is, never change results silently unless to fix a bug), we introduce a new package, which gives users an explicit way of upgrading. Users also have the option to use the two versions side-by-side while they do so.

You can find a more detailed description of our motivation and discussion leading up to this in SKIP 4.

Updating existing code#

When switching to the new skimage2 namespace, some code will need to be updated to continue working the way it did before.

Note

For a while, you will be able to use skimage and skimage2 (the 2.0 API) side-by-side, to facilitate porting. The new API may, for the same function call, return different results—e.g., because of a change in a keyword argument default value. By importing functionality from skimage2, you explicitly opt in to the new behavior.

Grayscale morphological operators in skimage.morphology#

The following functions are deprecated in favor of counterparts in skimage2.morphology:

The new counterparts behave differently in the following ways:

  • All functions now default to mode='ignore' (was mode='reflect').

  • Additionally, skimage2.morphology.dilation, skimage2.morphology.closing, and skimage2.morphology.black_tophat now also mirror the footprint (invert its order in each dimension). Note this only impacts behavior for asymmetric footprints.

skimage.morphology.erosion#

skimage.morphology.erosion is deprecated in favor of skimage2.morphology.erosion, which changes the default value for parameter mode to 'ignore' (was 'reflect').

To keep the old (skimage, v1.x) behavior, set mode='reflect' explicitly. If you set it explicitly before, the behavior is unchanged.

>>> import numpy as np
>>>
>>> import skimage as ski1
>>> import skimage2 as ski2
>>>
>>> image = ski1.data.camera()
>>> res1 = ski1.morphology.erosion(image)  # skimage default is mode='reflect'
>>> res2 = ski2.morphology.erosion(image, mode='reflect')
>>> assert np.all(res1 == res2)

skimage.morphology.dilation#

skimage.morphology.dilation is deprecated in favor of skimage2.morphology.dilation which changes the default value for parameter mode to 'ignore' (was 'reflect'). It also mirrors the footprint (inverts its order in each dimension), which aligns its behavior with SciPy’s conventions.

To keep the old (skimage, v1.x) behavior:

  • Set mode='reflect' explicitly. If you set it explicitly before, the behavior is unchanged.

  • If you currently use an asymmetric footprint, modify it like this before passing it to skimage2.morphology.dilation:

    footprint = ski2.morphology.pad_footprint(footprint, pad_end=False)
    footprint = ski2.morphology.mirror_footprint(footprint)
    

For example:

>>> import numpy as np
>>>
>>> import skimage as ski1
>>> import skimage2 as ski2
>>>
>>> image = ski1.data.camera()
>>> asym_foot = np.zeros((4, 4))
>>> asym_foot[2:, 2:] = 1
>>> res1 = ski1.morphology.dilation(image, footprint=asym_foot)
>>> pad_asym = ski2.morphology.pad_footprint(asym_foot, pad_end=False)
>>> mirror_asym = ski2.morphology.mirror_footprint(pad_asym)
>>> res2 = ski2.morphology.dilation(image, footprint=mirror_asym, mode='reflect')
>>> assert np.all(res1 == res2)

skimage.morphology.opening#

skimage.morphology.opening is deprecated in favor of skimage2.morphology.opening, which changes the default value for parameter mode to 'ignore' (was 'reflect').

To keep the old (skimage, v1.x) behavior, set mode='reflect' explicitly. If you set it explicitly before, the behavior is unchanged.

>>> import numpy as np
>>>
>>> import skimage as ski1
>>> import skimage2 as ski2
>>>
>>> image = ski1.data.camera()
>>> res1 = ski1.morphology.opening(image)  # skimage default is mode='reflect'
>>> res2 = ski2.morphology.opening(image, mode='reflect')
>>> assert np.all(res1 == res2)

skimage.morphology.closing#

skimage.morphology.closing is deprecated in favor of skimage2.morphology.closing which changes the default value for parameter mode to 'ignore' (was 'reflect'). It also mirrors the footprint (inverts its order in each dimension), which aligns its behavior with SciPy’s conventions.

To keep the old (skimage, v1.x) behavior:

  • Set mode='reflect' explicitly. If you set it explicitly before, the behavior is unchanged.

  • If you currently use an asymmetric footprint, modify it like this before passing it to skimage2.morphology.closing:

    footprint = ski2.morphology.pad_footprint(footprint, pad_end=False)
    footprint = ski2.morphology.mirror_footprint(footprint)
    

For example:

>>> import numpy as np
>>>
>>> import skimage as ski1
>>> import skimage2 as ski2
>>>
>>> image = ski1.data.camera()
>>> asym_foot = np.zeros((4, 4))
>>> asym_foot[2:, 2:] = 1
>>> res1 = ski1.morphology.closing(image, footprint=asym_foot)
>>> pad_asym = ski2.morphology.pad_footprint(asym_foot, pad_end=False)
>>> mirror_asym = ski2.morphology.mirror_footprint(pad_asym)
>>> res2 = ski2.morphology.closing(image, footprint=mirror_asym, mode='reflect')
>>> assert np.all(res1 == res2)

skimage.morphology.white_tophat#

skimage.morphology.white_tophat is deprecated in favor of skimage2.morphology.white_tophat, which changes the default value for parameter mode to 'ignore' (was 'reflect').

To keep the old (skimage, v1.x) behavior, set mode='reflect' explicitly. If you set it explicitly before, the behavior is unchanged.

>>> import numpy as np
>>>
>>> import skimage as ski1
>>> import skimage2 as ski2
>>>
>>> image = ski1.data.camera()
>>> res1 = ski1.morphology.white_tophat(image)  # skimage default is mode='reflect'
>>> res2 = ski2.morphology.white_tophat(image, mode='reflect')
>>> assert np.all(res1 == res2)

skimage.morphology.black_tophat#

skimage.morphology.black_tophat is deprecated in favor of skimage2.morphology.black_tophat which changes the default value for parameter mode to 'ignore' (was 'reflect'). It also mirrors the footprint (inverts its order in each dimension), which aligns its behavior with SciPy’s conventions.

To keep the old (skimage, v1.x) behavior:

  • Set mode='reflect' explicitly. If you set it explicitly before, the behavior is unchanged.

  • If you currently use an asymmetric footprint, modify it like this before passing it to skimage2.morphology.black_tophat:

    footprint = ski2.morphology.pad_footprint(footprint, pad_end=False)
    footprint = ski2.morphology.mirror_footprint(footprint)
    

For example:

>>> import numpy as np
>>>
>>> import skimage as ski1
>>> import skimage2 as ski2
>>>
>>> image = ski1.data.camera()
>>> asym_foot = np.zeros((4, 4))
>>> asym_foot[2:, 2:] = 1
>>> res1 = ski1.morphology.black_tophat(image, footprint=asym_foot)
>>> pad_asym = ski2.morphology.pad_footprint(asym_foot, pad_end=False)
>>> mirror_asym = ski2.morphology.mirror_footprint(pad_asym)
>>> res2 = ski2.morphology.black_tophat(image, footprint=mirror_asym, mode='reflect')
>>> assert np.all(res1 == res2)
Removal of ``skimage.future``
-----------------------------

There will be no future submodule in skimage2. If you are using modules in current skimage.future, please either vendor the skimage.future code in your own code-base, or use other libraries. If you are making heavy use of skimage.future routines, please feel free to raise an issue at the scikit-image issues page to ask us to port the function you want to use.

skimage.future.TrainableSegmenter#

skimage.future.TrainableSegmenter is deprecated, and will be removed in skimage2. Please either vendor the skimage.future code in your own code-base, or use other libraries. If you are making heavy use of skimage.future routines, please feel free to raise a scikit-image issue to ask us to port the function you want to use.

skimage.future.fit_segmenter#

skimage.future.fit_segmenter is deprecated, and will be removed in skimage2. Please either vendor the skimage.future code in your own code-base, or use other libraries. If you are making heavy use of skimage.future routines, please feel free to raise a scikit-image issue to ask us to port the function you want to use.

skimage.future.manual_lasso_segmentation#

skimage.future.manual_lasso_segmentation is deprecated, and will be removed in skimage2. Please either vendor the skimage.future code in your own code-base, or use other libraries. If you are making heavy use of skimage.future routines, please feel free to raise a scikit-image issue to ask us to port the function you want to use.

skimage.future.manual_polygon_segmentation#

skimage.future.manual_polygon_segmentation is deprecated, and will be removed in skimage2. Please either vendor the skimage.future code in your own code-base, or use other libraries. If you are making heavy use of skimage.future routines, please feel free to raise a scikit-image issue to ask us to port the function you want to use.

skimage.future.predict_segmenter#

skimage.future.predict_segmenter is deprecated, and will be removed in skimage2. Please either vendor the skimage.future code in your own code-base, or use other libraries. If you are making heavy use of skimage.future routines, please feel free to raise a scikit-image issue to ask us to port the function you want to use.

skimage.data.binary_blobs#

skimage.data.binary_blobs is deprecated in favor of skimage2.data.binary_blobs, which has a new signature. Parameters length and n_dim have been replaced with a new parameter shape. Optional blob_size_fraction has been replaced with a required parameter blob_size, whose behavior is independent of the output image size. The default value of boundary_mode has been changed from 'nearest' to 'wrap'.

To keep the old (skimage, v1.x) behavior, use:

>>> import numpy as np
>>> from numpy.random import default_rng
>>>
>>> import skimage as ski1
>>> import skimage2 as ski2
>>>
>>> length, n_dim, blob_size_fraction = 512, 2, 0.1  # Default ski1 values.
>>> res1 = ski1.data.binary_blobs(rng=default_rng(1939))  # Make reproducible.
>>> res2 = ski2.data.binary_blobs(
...     shape=(length,) * n_dim,
...     blob_size=blob_size_fraction * length,
...     boundary_mode='nearest',
...     rng=default_rng(1939)
... )
>>> assert np.all(res1 == res2)

skimage.feature.canny#

skimage.feature.canny is deprecated in favor of skimage2.feature.canny with new behavior.

The default of the optional parameter mode has changed from ‘constant’ to ‘nearest’. If you did not set this parameter explicitly, and you want the old (skimage, v1.x) behavior, add an explicit mode='constant':

>>> import numpy as np
>>> import skimage as ski1
>>> import skimage2 as ski2
>>>
>>> img = ski1.data.checkerboard()
>>> res1 = ski1.feature.canny(img)  # ski1 default is mode='constant'
>>> res2 = ski2.feature.canny(img, mode='constant')
>>> assert np.all(res1 == res2)

skimage.feature.corner_peaks#

skimage.feature.corner_peaks will be removed in scikit-image 2. Please use skimage2.feature.peak_local_max instead.

skimage.feature.peak_local_max#

skimage.feature.peak_local_max is deprecated in favor of skimage2.feature.peak_local_max with new behavior:

  • Parameter p_norm defaults to 2 (Euclidean distance), was numpy.inf (Chebyshev distance)

  • Parameter exclude_border defaults to 1, was True

  • Parameter exclude_border no longer accepts False and True; pass 0 instead of False, or min_distance instead of True

  • Parameters after image are keyword-only

To keep the old behavior when switching to skimage2, update your call according to the following cases:

In skimage

In skimage2

exclude_border not passed (default)

Assign it the same value as min_distance which may be its default value 1.

exclude_border=True

Same as above in the default case.

exclude_border=False

Use min_distance=0.

exclude_border=<int>

No change necessary.

p_norm not passed (default)

Pass the Skimage 1 default explicitly with p_norm=numpy.inf.

p_norm=<float>

No change necessary.

Other keyword parameters can be left unchanged.

>>> import numpy as np
>>>
>>> import skimage as ski1
>>> import skimage2 as ski2
>>> image = ski1.data.camera()
>>> res1 = ski1.feature.peak_local_max(image)
>>> res2 = ski2.feature.peak_local_max(image, exclude_border=1, p_norm=np.inf)
>>> assert np.all(res1 == res2)
>>>
>>> res1 = ski1.feature.peak_local_max(image, min_distance=10)
>>> res2 = ski2.feature.peak_local_max(
...     image, min_distance=10, exclude_border=10, p_norm=np.inf
... )
>>> assert np.all(res1 == res2)

skimage.filters.gaussian#

skimage.filters.gaussian is deprecated in favor of skimage2.filters.gaussian with new behavior:

  • Parameter preserve_range was removed

  • The value range of image is now always preserved

To keep the old (skimage, v1.x) behavior of preserve_range=False after switching to skimage2, preprocess image:

skimage.filters.gaussian(image, ...)

Becomes

image = skimage2.util.rescale_legacy(image)
skimage2.filters.gaussian(image, ...)

Other keyword parameters can be left unchanged.

skimage.graph.pixel_graph#

skimage.graph.pixel_graph is deprecated in favor of skimage2.graph.pixel_graph with new behavior:

SciPy 2.0 deprecates the sparse matrix classes and will remove them no earlier than SciPy 2.2. The two types define * differently: matrix multiplication for scipy.sparse.csr_matrix, elementwise multiplication for scipy.sparse.csr_array. @ is matrix multiplication for both.

To keep the old (skimage, v1.x) behavior, convert the result:

graph, nodes = skimage2.graph.pixel_graph(image, ...)
graph = scipy.sparse.csr_matrix(graph)

skimage.metrics.contingency_table#

skimage.metrics.contingency_table is deprecated in favor of skimage2.metrics.contingency_table with new behavior:

SciPy 2.0 deprecates the sparse matrix classes and will remove them no earlier than SciPy 2.2. The two types define * differently: matrix multiplication for scipy.sparse.csr_matrix, elementwise multiplication for scipy.sparse.csr_array. @ is matrix multiplication for both.

To keep the old (skimage, v1.x) behavior, convert the result:

cont = skimage2.metrics.contingency_table(im_true, im_test, ...)
cont = scipy.sparse.csr_matrix(cont)

skimage.metrics.structural_similarity#

skimage.metrics.structural_similarity is deprecated in favor of skimage2.metrics.structural_similarity, which has a new signature. The parameter data_range is now a required parameter.

If you didn’t provide data_range explicitly before and relied on its default behavior, you can keep the old (skimage, v1.x) behavior by setting the parameter explicitly.

For example:

>>> import numpy as np
>>> import skimage as ski
>>> import skimage2 as ski2
...
>>> im1 = np.arange(20)
>>> im2 = im1 // 2
...
>>> result1 = ski.metrics.structural_similarity(im1, im2)
...
>>> data_range = np.iinfo(im1.dtype).max - np.iinfo(im1.dtype).min
>>> result2 = ski2.metrics.structural_similarity(im1, im2, data_range=data_range)
...
>>> np.testing.assert_equal(result1, result2)

For floating dtypes, setting data_range was already required in skimage/v1.x.

skimage.morphology.cube#

skimage.morphology.cube is deprecated in favor of skimage2.morphology.footprint_rectangle and skimage2.morphology.footprint_rectangle_decomposed.

To keep the old behavior when switching to skimage2, update your call according to the following cases:

Other keyword parameters can be left unchanged.

>>> import numpy as np
>>> import skimage as ski1
>>> import skimage2 as ski2
>>> fp1 = ski1.morphology.cube(3)
>>> fp2 = ski2.morphology.footprint_rectangle((3, 3, 3))
>>> np.testing.assert_equal(fp1, fp2)
>>> fp1 = ski1.morphology.cube(
...     3, decomposition="sequence"
... )
>>> fp2 = ski2.morphology.footprint_rectangle_decomposed(
...     (3, 3, 3), method="sequence"
... )
>>> np.testing.assert_equal(fp1, fp2)

skimage.morphology.footprint_rectangle#

skimage.morphology.footprint_rectangle is deprecated in favor of skimage2.morphology.footprint_rectangle and skimage2.morphology.footprint_rectangle_decomposed.

To keep the old behavior when switching to skimage2, update your call according to the following cases:

Other keyword parameters can be left unchanged.

>>> import numpy as np
>>> import skimage as ski1
>>> import skimage2 as ski2
>>> fp1 = ski1.morphology.footprint_rectangle((3, 3, 3))
>>> fp2 = ski2.morphology.footprint_rectangle((3, 3, 3))
>>> np.testing.assert_equal(fp1, fp2)
>>> fp1 = ski1.morphology.footprint_rectangle(
...     (3, 3, 3), decomposition="sequence"
... )
>>> fp2 = ski2.morphology.footprint_rectangle_decomposed(
...     (3, 3, 3), method="sequence"
... )
>>> np.testing.assert_equal(fp1, fp2)

skimage.morphology.rectangle#

skimage.morphology.rectangle is deprecated in favor of skimage2.morphology.footprint_rectangle and skimage2.morphology.footprint_rectangle_decomposed.

To keep the old behavior when switching to skimage2, update your call according to the following cases:

Other keyword parameters can be left unchanged.

>>> import numpy as np
>>> import skimage as ski1
>>> import skimage2 as ski2
>>> fp1 = ski1.morphology.rectangle(3, 3)
>>> fp2 = ski2.morphology.footprint_rectangle((3, 3))
>>> np.testing.assert_equal(fp1, fp2)
>>> fp1 = ski1.morphology.rectangle(
...     3, 3, decomposition="sequence"
... )
>>> fp2 = ski2.morphology.footprint_rectangle_decomposed(
...     (3, 3), method="sequence"
... )
>>> np.testing.assert_equal(fp1, fp2)

skimage.morphology.square#

skimage.morphology.square is deprecated in favor of skimage2.morphology.footprint_rectangle and skimage2.morphology.footprint_rectangle_decomposed.

To keep the old behavior when switching to skimage2, update your call according to the following cases:

Other keyword parameters can be left unchanged.

>>> import numpy as np
>>> import skimage as ski1
>>> import skimage2 as ski2
>>> fp1 = ski1.morphology.square(3)
>>> fp2 = ski2.morphology.footprint_rectangle((3, 3))
>>> np.testing.assert_equal(fp1, fp2)
>>> fp1 = ski1.morphology.square(
...     3, decomposition="sequence"
... )
>>> fp2 = ski2.morphology.footprint_rectangle_decomposed(
...     (3, 3), method="sequence"
... )
>>> np.testing.assert_equal(fp1, fp2)

Deprecations prior to skimage2#

We have already introduced a number of changes and deprecations to our API. These are part of the API cleanup for skimage2 but are not breaking. You will simply notice these as the classical deprecation warnings that you are already used to. We list them here, because updating your code to the new API will make it easier to transition to skimage2.

To be defined.