-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Deprecate skimage.io
plugin infrastructure
#7353
base: main
Are you sure you want to change the base?
Changes from all commits
d4eaa76
8dd01a5
1367f41
749611d
0509863
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,65 +1,89 @@ | ||
"""Utilities to read and write images in various formats. | ||
|
||
The following plug-ins are available: | ||
|
||
""" | ||
|
||
from .manage_plugins import * | ||
from .sift import * | ||
from .collection import * | ||
|
||
from ._io import * | ||
from ._image_stack import * | ||
|
||
|
||
reset_plugins() | ||
import warnings | ||
import functools | ||
from contextlib import contextmanager | ||
|
||
WRAP_LEN = 73 | ||
from .._shared.utils import deprecate_func | ||
|
||
|
||
def _separator(char, lengths): | ||
return [char * separator_length for separator_length in lengths] | ||
@contextmanager | ||
def _hide_repeated_plugin_deprecation_warnings(): | ||
"""Ignore warnings related to plugin infrastructure deprecation.""" | ||
with warnings.catch_warnings(): | ||
warnings.filterwarnings( | ||
action="ignore", | ||
message=".*Use imageio or a similar package instead.*", | ||
category=FutureWarning, | ||
module="skimage", | ||
) | ||
yield | ||
|
||
|
||
def _format_plugin_info_table(info_table, column_lengths): | ||
"""Add separators and column titles to plugin info table.""" | ||
info_table.insert(0, _separator('=', column_lengths)) | ||
info_table.insert(1, ('Plugin', 'Description')) | ||
info_table.insert(2, _separator('-', column_lengths)) | ||
info_table.append(_separator('=', column_lengths)) | ||
def _deprecate_plugin_function(func): | ||
"""Mark a function of the plugin infrastructure as deprecated. | ||
|
||
|
||
def _update_doc(doc): | ||
"""Add a list of plugins to the module docstring, formatted as | ||
a ReStructuredText table. | ||
In addition to emitting the appropriate FutureWarning, this also supresses | ||
identical warnings that might be caused when this function calls other | ||
functions from the deprecated plugin infrastructure. | ||
""" | ||
from textwrap import wrap | ||
|
||
info_table = [ | ||
(p, plugin_info(p).get('description', 'no description')) | ||
for p in available_plugins | ||
if not p == 'test' | ||
] | ||
@deprecate_func( | ||
deprecated_version="0.23", | ||
removed_version="0.25", | ||
hint="Use imageio or a similar package instead.", | ||
) | ||
@functools.wraps(func) | ||
def wrapper(*args, **kwargs): | ||
with _hide_repeated_plugin_deprecation_warnings(): | ||
return func(*args, **kwargs) | ||
|
||
if len(info_table) > 0: | ||
name_length = max([len(n) for (n, _) in info_table]) | ||
else: | ||
name_length = 0 | ||
return wrapper | ||
|
||
description_length = WRAP_LEN - 1 - name_length | ||
column_lengths = [name_length, description_length] | ||
_format_plugin_info_table(info_table, column_lengths) | ||
|
||
for name, plugin_description in info_table: | ||
description_lines = wrap(plugin_description, description_length) | ||
name_column = [name] | ||
name_column.extend(['' for _ in range(len(description_lines) - 1)]) | ||
for name, description in zip(name_column, description_lines): | ||
doc += f"{name.ljust(name_length)} {description}\n" | ||
doc = doc.strip() | ||
from .manage_plugins import * | ||
from .sift import * | ||
from .collection import * | ||
|
||
return doc | ||
from ._io import * | ||
from ._image_stack import * | ||
|
||
|
||
if __doc__ is not None: | ||
__doc__ = _update_doc(__doc__) | ||
__all__ = [ | ||
'use_plugin', | ||
'call_plugin', | ||
'plugin_info', | ||
'plugin_order', | ||
'reset_plugins', | ||
'find_available_plugins', | ||
'available_plugins', | ||
'load_sift', | ||
'load_surf', | ||
'MultiImage', | ||
'ImageCollection', | ||
'concatenate_images', | ||
'imread_collection_wrapper', | ||
'imread', | ||
'imsave', | ||
'imshow', | ||
'show', | ||
'imread_collection', | ||
'imshow_collection', | ||
'image_stack', | ||
'push', | ||
'pop', | ||
] | ||
|
||
|
||
def __getattr__(name): | ||
if name == "available_plugins": | ||
warnings.warn( | ||
"`available_plugins` is deprecated since version 0.23 and will " | ||
"be removed in version 0.25. Use imageio or a similar package " | ||
"instead.", | ||
category=FutureWarning, | ||
stacklevel=2, | ||
) | ||
return globals()["_available_plugins"] | ||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,17 @@ | ||
import pathlib | ||
import warnings | ||
|
||
import numpy as np | ||
import imageio.v3 as iio | ||
|
||
from .._shared.utils import warn | ||
from .._shared.utils import warn, deprecate_parameter, DEPRECATED | ||
from .._shared.version_requirements import require | ||
from ..exposure import is_low_contrast | ||
from ..color.colorconv import rgb2gray, rgba2rgb | ||
from ..io.manage_plugins import call_plugin | ||
from ..io.manage_plugins import call_plugin, plugin_order | ||
from .util import file_or_url_context | ||
|
||
from . import _deprecate_plugin_function | ||
|
||
__all__ = [ | ||
'imread', | ||
|
@@ -20,7 +23,17 @@ | |
] | ||
|
||
|
||
def imread(fname, as_gray=False, plugin=None, **plugin_args): | ||
@deprecate_parameter( | ||
deprecated_name="plugin", | ||
start_version="0.23", | ||
stop_version="0.25", | ||
template="Parameter `{deprecated_name}` is deprecated since version " | ||
"{deprecated_version} and will be removed in {changed_version} (or " | ||
"later). To avoid this warning, please do not use the parameter " | ||
"`{deprecated_name}`. Use imageio or other 3rd party libraries directly " | ||
"for more advanced IO features.", | ||
) | ||
def imread(fname, as_gray=False, plugin=DEPRECATED, **plugin_args): | ||
"""Load an image from file. | ||
|
||
Parameters | ||
|
@@ -30,16 +43,13 @@ def imread(fname, as_gray=False, plugin=None, **plugin_args): | |
as_gray : bool, optional | ||
If True, convert color images to gray-scale (64-bit floats). | ||
Images that are already in gray-scale format are not converted. | ||
plugin : str, optional | ||
Name of plugin to use. By default, the different plugins are | ||
tried (starting with imageio) until a suitable | ||
candidate is found. If not given and fname is a tiff file, the | ||
tifffile plugin will be used. | ||
|
||
Other Parameters | ||
---------------- | ||
plugin_args : keywords | ||
Passed to the given plugin. | ||
**plugin_args : DEPRECATED | ||
`plugin_args` is deprecated. | ||
|
||
.. deprecated:: 0.23 | ||
|
||
Returns | ||
------- | ||
|
@@ -49,15 +59,42 @@ def imread(fname, as_gray=False, plugin=None, **plugin_args): | |
RGB-image MxNx3 and an RGBA-image MxNx4. | ||
|
||
""" | ||
if isinstance(fname, pathlib.Path): | ||
fname = str(fname.resolve()) | ||
if plugin_args: | ||
warnings.warn( | ||
"Additional keyword arguments are deprecated since version " | ||
"0.23 and will be removed in 0.25 (or later). To avoid this " | ||
"warning, please do not use additional keyword arguments. " | ||
"Use imageio or a similar package instead.", | ||
category=FutureWarning, | ||
stacklevel=2, | ||
) | ||
|
||
if plugin is None and hasattr(fname, 'lower'): | ||
if fname.lower().endswith(('.tiff', '.tif')): | ||
plugin = 'tifffile' | ||
with warnings.catch_warnings(): | ||
warnings.filterwarnings( | ||
action="ignore", | ||
message=".*Use imageio or a similar package instead.*", | ||
category=FutureWarning, | ||
module="skimage.io", | ||
) | ||
use_plugin = ( | ||
plugin is not DEPRECATED | ||
or plugin_args | ||
or plugin_order()["imread"][0] != "imageio" | ||
) | ||
if use_plugin: | ||
Comment on lines
+79
to
+84
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. It's been proven quite tricky to preserve
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. Why not just pop the 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. Because we want a deprecation period during which we support both APIs (our old one and imageio), right? I'm probably overthinking this. Let me look into this again and try for a simpler approach. |
||
plugin = None if plugin is DEPRECATED else plugin | ||
if isinstance(fname, pathlib.Path): | ||
fname = str(fname.resolve()) | ||
|
||
if plugin is None and hasattr(fname, 'lower'): | ||
if fname.lower().endswith(('.tiff', '.tif')): | ||
plugin = 'tifffile' | ||
|
||
with file_or_url_context(fname) as fname: | ||
img = call_plugin('imread', fname, plugin=plugin, **plugin_args) | ||
with file_or_url_context(fname) as fname: | ||
img = call_plugin('imread', fname, plugin=plugin, **plugin_args) | ||
else: | ||
with file_or_url_context(fname) as fname: | ||
img = iio.imread(fname) | ||
|
||
if not hasattr(img, 'ndim'): | ||
return img | ||
|
@@ -75,6 +112,7 @@ def imread(fname, as_gray=False, plugin=None, **plugin_args): | |
return img | ||
|
||
|
||
@_deprecate_plugin_function | ||
def imread_collection(load_pattern, conserve_memory=True, plugin=None, **plugin_args): | ||
""" | ||
Load a collection of images. | ||
|
@@ -105,7 +143,17 @@ def imread_collection(load_pattern, conserve_memory=True, plugin=None, **plugin_ | |
) | ||
|
||
|
||
def imsave(fname, arr, plugin=None, check_contrast=True, **plugin_args): | ||
@deprecate_parameter( | ||
deprecated_name="plugin", | ||
start_version="0.23", | ||
stop_version="0.25", | ||
template="Parameter `{deprecated_name}` is deprecated since version " | ||
"{deprecated_version} and will be removed in {changed_version} (or " | ||
"later). To avoid this warning, please do not use the parameter " | ||
"`{deprecated_name}`. Use imageio or other 3rd party libraries directly " | ||
"for more advanced IO features.", | ||
) | ||
def imsave(fname, arr, plugin=DEPRECATED, check_contrast=True, **plugin_args): | ||
"""Save an image to file. | ||
|
||
Parameters | ||
|
@@ -135,24 +183,52 @@ def imsave(fname, arr, plugin=None, check_contrast=True, **plugin_args): | |
and largest file size (default 75). This is only available when using | ||
the PIL and imageio plugins. | ||
""" | ||
if isinstance(fname, pathlib.Path): | ||
fname = str(fname.resolve()) | ||
if plugin is None and hasattr(fname, 'lower'): | ||
if fname.lower().endswith(('.tiff', '.tif')): | ||
plugin = 'tifffile' | ||
if plugin_args: | ||
warnings.warn( | ||
"Additional keyword arguments are deprecated since version " | ||
"0.23 and will be removed in 0.25 (or later). To avoid this " | ||
"warning, please do not use additional keyword arguments. " | ||
"Use imageio or a similar package instead.", | ||
category=FutureWarning, | ||
stacklevel=2, | ||
) | ||
|
||
if arr.dtype == bool: | ||
warn( | ||
f'{fname} is a boolean image: setting True to 255 and False to 0. ' | ||
'To silence this warning, please convert the image using ' | ||
'img_as_ubyte.', | ||
stacklevel=2, | ||
stacklevel=3, | ||
) | ||
arr = arr.astype('uint8') * 255 | ||
if check_contrast and is_low_contrast(arr): | ||
warn(f'{fname} is a low contrast image') | ||
return call_plugin('imsave', fname, arr, plugin=plugin, **plugin_args) | ||
warn(f'{fname} is a low contrast image', stacklevel=3) | ||
|
||
with warnings.catch_warnings(): | ||
warnings.filterwarnings( | ||
action="ignore", | ||
message=".*Use imageio or a similar package instead.*", | ||
category=FutureWarning, | ||
module="skimage.io", | ||
) | ||
use_plugin = ( | ||
plugin is not DEPRECATED | ||
or plugin_args | ||
or plugin_order()["imsave"][0] != "imageio" | ||
) | ||
if use_plugin: | ||
plugin = None if plugin is DEPRECATED else plugin | ||
if isinstance(fname, pathlib.Path): | ||
fname = str(fname.resolve()) | ||
if plugin is None and hasattr(fname, 'lower'): | ||
if fname.lower().endswith(('.tiff', '.tif')): | ||
plugin = 'tifffile' | ||
return call_plugin('imsave', fname, arr, plugin=plugin, **plugin_args) | ||
|
||
return iio.imwrite(fname, arr) | ||
|
||
|
||
@_deprecate_plugin_function | ||
def imshow(arr, plugin=None, **plugin_args): | ||
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.
|
||
"""Display an image. | ||
|
||
|
@@ -170,11 +246,14 @@ def imshow(arr, plugin=None, **plugin_args): | |
Passed to the given plugin. | ||
|
||
""" | ||
if isinstance(arr, str): | ||
arr = call_plugin('imread', arr, plugin=plugin) | ||
return call_plugin('imshow', arr, plugin=plugin, **plugin_args) | ||
with warnings.catch_warnings(): | ||
warnings.simplefilter("ignore") | ||
if isinstance(arr, str): | ||
arr = call_plugin('imread', arr, plugin=plugin) | ||
return call_plugin('imshow', arr, plugin=plugin, **plugin_args) | ||
|
||
|
||
@_deprecate_plugin_function | ||
def imshow_collection(ic, plugin=None, **plugin_args): | ||
"""Display a collection of images. | ||
|
||
|
@@ -192,9 +271,12 @@ def imshow_collection(ic, plugin=None, **plugin_args): | |
Passed to the given plugin. | ||
|
||
""" | ||
from ..io.manage_plugins import call_plugin | ||
|
||
return call_plugin('imshow_collection', ic, plugin=plugin, **plugin_args) | ||
|
||
|
||
@_deprecate_plugin_function | ||
@require("matplotlib", ">=3.3") | ||
def show(): | ||
"""Display pending images. | ||
|
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.
Probably want to hide items from
__all__
that are deprecated.