Skip to content

Commit 3c33a0f

Browse files
committed
Add dependency on imageio; remove matplotlib-based reader.
matplotlib's imread is now anyways a thin shim around pillow (a dependency of matplotlib), so if you have matplotlib installed, you may as well use imageio's imread: imageio is a smaller dependency, and has more consistent behavior (it doesn't return pngs as floats). imageio is also a dependency of scikit-image (since 0.15), so it seems reasonable to directly depend on it instead. For now, the scikit-image reader was kept, as removing it (possibly in the future) would entail removing support for the `plugin` kwarg, and thus an API break.
1 parent ab2c12d commit 3c33a0f

File tree

8 files changed

+15
-82
lines changed

8 files changed

+15
-82
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ Formats readable by pims include:
2323

2424
PIMS is based on readers by:
2525
* [scikit-image](http://scikit-image.org/)
26-
* [matplotlib](http://matplotlib.org/)
2726
* [ffmpeg](https://www.ffmpeg.org/) and [PyAV](http://mikeboers.github.io/PyAV/) (video formats such as AVI, MOV)
2827
* [jpype](http://jpype.readthedocs.org/en/latest/) (interface with Bio-formats)
2928
* [Pillow](http://pillow.readthedocs.org/en/latest/) (improved TIFF support)

doc/source/image_sequence.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,6 @@ popular formats like PNG, JPG, TIFF, and others. PIMS requires **one of
4141
the following** packages, in order of decreasing preference.
4242

4343
* `scikit-image <http://scikit-image.org/>`_
44-
* `matplotlib <http://matplotlib.org/>`_
44+
* `imageio <https://imageio.github.io/>`_
4545

4646
Scikit-image is installed with the PIMS conda package.

pims/image_reader.py

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,13 @@
66
from pims.base_frames import FramesSequence, FramesSequenceND
77
from pims.frame import Frame
88

9-
# If scikit-image is not available, use matplotlib (with a warning) instead.
10-
import warnings
119
try:
1210
from skimage.io import imread
1311
except ImportError:
14-
try:
15-
from matplotlib.pyplot import imread
16-
# imread() works differently between scikit-image and matplotlib.
17-
# We don't require users to have scikit-image,
18-
# but if we fall back to matplotlib, make sure the user
19-
# is aware of the consequences.
20-
ski_preferred = ("PIMS image_reader.py could not find scikit-image. "
21-
"Falling back to matplotlib's imread(), which uses floats "
22-
"instead of integers. This may break your scripts. \n"
23-
"(To ignore this warning, include the line "
24-
'"warnings.simplefilter("ignore", RuntimeWarning)" '
25-
"in your script.)")
26-
warnings.warn(RuntimeWarning(ski_preferred))
27-
except ImportError:
28-
imread = None
12+
import imageio
13+
14+
def imread(*args, **kwargs): # Strip metadata for consistency.
15+
return np.asarray(imageio.imread(*args, **kwargs))
2916

3017

3118
class ImageReader(FramesSequence):
@@ -40,11 +27,6 @@ def class_exts(cls):
4027
class_priority = 12
4128

4229
def __init__(self, filename, **kwargs):
43-
if imread is None:
44-
raise ImportError("One of the following packages are required for "
45-
"using the ImageReader: "
46-
"matplotlib or scikit-image.")
47-
4830
self._data = imread(filename, **kwargs)
4931

5032
def get_frame(self, i):
@@ -74,10 +56,6 @@ def class_exts(cls):
7456
class_priority = 11
7557

7658
def __init__(self, filename, **kwargs):
77-
if imread is None:
78-
raise ImportError("One of the following packages are required for "
79-
"using the ImageReaderND: "
80-
"matplotlib or scikit-image.")
8159
super(ImageReaderND, self).__init__()
8260

8361
self._data = Frame(imread(filename, **kwargs), frame_no=0)

pims/image_sequence.py

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,9 @@
1717
import pims
1818
from pims.base_frames import FramesSequence, FramesSequenceND
1919
from pims.frame import Frame
20+
from pims.image_reader import imread
2021
from pims.utils.sort import natural_keys
2122

22-
# skimage.io.plugin_order() gives a nice hierarchy of implementations of imread.
23-
# If skimage is not available, go down our own hard-coded hierarchy.
24-
has_skimage = False
25-
try:
26-
from skimage.io import imread
27-
has_skimage = True
28-
except ImportError:
29-
try:
30-
from matplotlib.pyplot import imread
31-
except ImportError:
32-
imread = None
33-
3423

3524
class ImageSequence(FramesSequence):
3625
"""Read a directory of sequentially numbered image files into an
@@ -68,11 +57,11 @@ class ImageSequence(FramesSequence):
6857
>>> frame_shape = video.frame_shape # Pixel dimensions of video
6958
"""
7059
def __init__(self, path_spec, plugin=None):
71-
if not has_skimage:
60+
if not imread.__module__.startswith("skimage"):
7261
if plugin is not None:
7362
warn("A plugin was specified but ignored. Plugins can only "
7463
"be specified if scikit-image is available. Instead, "
75-
"ImageSequence will try using matplotlib")
64+
"ImageSequence will use imageio")
7665
self.kwargs = dict()
7766
else:
7867
self.kwargs = dict(plugin=plugin)
@@ -94,10 +83,6 @@ def __del__(self):
9483
self.close()
9584

9685
def imread(self, filename, **kwargs):
97-
if imread is None:
98-
raise ImportError("One of the following packages are required for "
99-
"using the ImageSequence reader: "
100-
"scikit-image or matplotlib.")
10186
if self._is_zipfile:
10287
file_handle = BytesIO(self._zipfile.read(filename))
10388
return imread(file_handle, **kwargs)

pims/tests/test_common.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -48,25 +48,13 @@ def _skip_if_no_tifffile():
4848
raise unittest.SkipTest('tifffile not installed. Skipping.')
4949

5050

51-
def _skip_if_no_imread():
52-
if pims.image_sequence.imread is None:
53-
raise unittest.SkipTest('ImageSequence requires either matplotlib or'
54-
' scikit-image. Skipping.')
55-
56-
5751
def _skip_if_no_skimage():
5852
try:
5953
import skimage
6054
except ImportError:
6155
raise unittest.SkipTest('skimage not installed. Skipping.')
6256

6357

64-
def _skip_if_no_PIL():
65-
import pims.tiff_stack
66-
if not pims.tiff_stack.PIL_available():
67-
raise unittest.SkipTest('PIL/Pillow not installed. Skipping.')
68-
69-
7058
def assert_image_equal(actual, expected):
7159
if np.issubdtype(actual.dtype, np.integer):
7260
assert_equal(actual, expected)
@@ -183,7 +171,6 @@ def compare_slice_to_list(actual, expected):
183171
class TestRecursiveSlicing(unittest.TestCase):
184172

185173
def setUp(self):
186-
_skip_if_no_imread()
187174
class DemoReader(pims.ImageSequence):
188175
def imread(self, filename, **kwargs):
189176
return np.array([[filename]])
@@ -382,7 +369,6 @@ def test_simple_negative_index(self):
382369

383370
class TestImageReaderTIFF(_image_single, unittest.TestCase):
384371
def setUp(self):
385-
_skip_if_no_imread()
386372
self.filename = os.path.join(path, 'stuck.tif')
387373
self.frame0 = np.load(os.path.join(path, 'stuck_frame0.npy'))
388374
self.frame1 = np.load(os.path.join(path, 'stuck_frame1.npy'))
@@ -395,7 +381,6 @@ def setUp(self):
395381

396382
class TestImageReaderPNG(_image_single, unittest.TestCase):
397383
def setUp(self):
398-
_skip_if_no_imread()
399384
self.klass = pims.ImageReader
400385
self.kwargs = dict()
401386
self.expected_shape = (10, 11)
@@ -408,7 +393,6 @@ def setUp(self):
408393

409394
class TestImageReaderND(_image_single, unittest.TestCase):
410395
def setUp(self):
411-
_skip_if_no_imread()
412396
self.klass = pims.ImageReaderND
413397
self.kwargs = dict()
414398
self.expected_shape = (10, 11, 3)
@@ -516,7 +500,6 @@ def check_skip(self):
516500
pass
517501

518502
def setUp(self):
519-
_skip_if_no_PIL()
520503
self.filename = os.path.join(path, 'stuck.tif')
521504
self.frame0 = np.load(os.path.join(path, 'stuck_frame0.npy'))
522505
self.frame1 = np.load(os.path.join(path, 'stuck_frame1.npy'))
@@ -572,9 +555,6 @@ def test_metadata(self):
572555

573556

574557
class TestOpenFiles(unittest.TestCase):
575-
def setUp(self):
576-
_skip_if_no_PIL()
577-
578558
def test_open_png(self):
579559
self.filenames = ['dummy_png.png']
580560
shape = (10, 11)
@@ -583,7 +563,6 @@ def test_open_png(self):
583563
clean_dummy_png(path, self.filenames)
584564

585565
def test_open_pngs(self):
586-
_skip_if_no_imread()
587566
self.filepath = os.path.join(path, 'image_sequence')
588567
self.filenames = ['T76S3F00001.png', 'T76S3F00002.png',
589568
'T76S3F00003.png', 'T76S3F00004.png',

pims/tests/test_frame.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,6 @@
77
from pims.frame import Frame
88

99

10-
def _skip_if_no_PIL():
11-
try:
12-
from PIL import Image
13-
except ImportError:
14-
raise unittest.SkipTest('PIL/Pillow not installed. Skipping.')
15-
16-
1710
def _skip_if_no_jinja2():
1811
try:
1912
import jinja2
@@ -39,7 +32,6 @@ def test_creation_md():
3932

4033

4134
def test_repr_html_():
42-
_skip_if_no_PIL()
4335
_skip_if_no_jinja2()
4436
# This confims a bugfix, where 16-bit images would raise
4537
# an error.

pims/tests/test_imseq.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from pims.tests.test_common import (_image_series,
1515
clean_dummy_png, save_dummy_png,
16-
_skip_if_no_skimage, _skip_if_no_imread)
16+
_skip_if_no_skimage)
1717

1818
path, _ = os.path.split(os.path.abspath(__file__))
1919
path = os.path.join(path, 'data')
@@ -83,7 +83,6 @@ def tearDown(self):
8383

8484
class TestImageSequenceAcceptsList(_image_series, unittest.TestCase):
8585
def setUp(self):
86-
_skip_if_no_imread()
8786
self.filepath = os.path.join(path, 'image_sequence')
8887
self.filenames = ['T76S3F00001.png', 'T76S3F00002.png',
8988
'T76S3F00003.png', 'T76S3F00004.png',
@@ -107,7 +106,6 @@ def tearDown(self):
107106

108107
class TestImageSequenceNaturalSorting(_image_series, unittest.TestCase):
109108
def setUp(self):
110-
_skip_if_no_imread()
111109
self.filepath = os.path.join(path, 'image_sequence')
112110
self.filenames = ['T76S3F1.png', 'T76S3F20.png',
113111
'T76S3F3.png', 'T76S3F4.png',
@@ -150,7 +148,6 @@ def tearDown(self):
150148

151149
class ImageSequenceND(_image_series, unittest.TestCase):
152150
def setUp(self):
153-
_skip_if_no_imread()
154151
self.filepath = os.path.join(path, 'image_sequence3d')
155152
self.filenames = ['file_t001_z001_c1.png',
156153
'file_t001_z001_c2.png',
@@ -212,7 +209,6 @@ def test_sizeC(self):
212209

213210
class ImageSequenceND_RGB(_image_series, unittest.TestCase):
214211
def setUp(self):
215-
_skip_if_no_imread()
216212
self.filepath = os.path.join(path, 'image_sequence3d')
217213
self.filenames = ['file_t001_z001_c1.png',
218214
'file_t001_z002_c1.png',
@@ -249,7 +245,6 @@ def tearDown(self):
249245

250246
class ReaderSequence(_image_series, unittest.TestCase):
251247
def setUp(self):
252-
_skip_if_no_imread()
253248
self.filepath = os.path.join(path, 'image_sequence3d')
254249
self.filenames = ['file001.png',
255250
'file002.png',

setup.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616
cmdclass=versioneer.get_cmdclass(),
1717
description="Python Image Sequence",
1818
author="PIMS Contributors",
19-
install_requires=['slicerator>=0.9.8', 'six>=1.8', 'numpy>=1.19'],
19+
install_requires=[
20+
'imageio',
21+
'numpy>=1.19',
22+
'six>=1.8',
23+
'slicerator>=0.9.8',
24+
],
2025
author_email="[email protected]",
2126
url="https://github.com/soft-matter/pims",
2227
packages=["pims", "pims.utils", "pims.tests"],

0 commit comments

Comments
 (0)