diff --git a/README.md b/README.md index 727dd42..f3f1d84 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,6 @@ Formats readable by pims include: PIMS is based on readers by: * [scikit-image](http://scikit-image.org/) -* [matplotlib](http://matplotlib.org/) * [ffmpeg](https://www.ffmpeg.org/) and [PyAV](http://mikeboers.github.io/PyAV/) (video formats such as AVI, MOV) * [jpype](http://jpype.readthedocs.org/en/latest/) (interface with Bio-formats) * [Pillow](http://pillow.readthedocs.org/en/latest/) (improved TIFF support) diff --git a/doc/source/image_sequence.rst b/doc/source/image_sequence.rst index 866026c..4bf71d5 100644 --- a/doc/source/image_sequence.rst +++ b/doc/source/image_sequence.rst @@ -41,6 +41,6 @@ popular formats like PNG, JPG, TIFF, and others. PIMS requires **one of the following** packages, in order of decreasing preference. * `scikit-image `_ -* `matplotlib `_ +* `imageio `_ Scikit-image is installed with the PIMS conda package. diff --git a/pims/image_reader.py b/pims/image_reader.py index 4b55718..0d6de62 100644 --- a/pims/image_reader.py +++ b/pims/image_reader.py @@ -6,26 +6,13 @@ from pims.base_frames import FramesSequence, FramesSequenceND from pims.frame import Frame -# If scikit-image is not available, use matplotlib (with a warning) instead. -import warnings try: from skimage.io import imread except ImportError: - try: - from matplotlib.pyplot import imread - # imread() works differently between scikit-image and matplotlib. - # We don't require users to have scikit-image, - # but if we fall back to matplotlib, make sure the user - # is aware of the consequences. - ski_preferred = ("PIMS image_reader.py could not find scikit-image. " - "Falling back to matplotlib's imread(), which uses floats " - "instead of integers. This may break your scripts. \n" - "(To ignore this warning, include the line " - '"warnings.simplefilter("ignore", RuntimeWarning)" ' - "in your script.)") - warnings.warn(RuntimeWarning(ski_preferred)) - except ImportError: - imread = None + import imageio + + def imread(*args, **kwargs): # Strip metadata for consistency. + return np.asarray(imageio.imread(*args, **kwargs)) class ImageReader(FramesSequence): @@ -40,11 +27,6 @@ def class_exts(cls): class_priority = 12 def __init__(self, filename, **kwargs): - if imread is None: - raise ImportError("One of the following packages are required for " - "using the ImageReader: " - "matplotlib or scikit-image.") - self._data = imread(filename, **kwargs) def get_frame(self, i): @@ -74,10 +56,6 @@ def class_exts(cls): class_priority = 11 def __init__(self, filename, **kwargs): - if imread is None: - raise ImportError("One of the following packages are required for " - "using the ImageReaderND: " - "matplotlib or scikit-image.") super(ImageReaderND, self).__init__() self._data = Frame(imread(filename, **kwargs), frame_no=0) diff --git a/pims/image_sequence.py b/pims/image_sequence.py index 5bcc9d4..e6b7c26 100644 --- a/pims/image_sequence.py +++ b/pims/image_sequence.py @@ -17,20 +17,9 @@ import pims from pims.base_frames import FramesSequence, FramesSequenceND from pims.frame import Frame +from pims.image_reader import imread from pims.utils.sort import natural_keys -# skimage.io.plugin_order() gives a nice hierarchy of implementations of imread. -# If skimage is not available, go down our own hard-coded hierarchy. -has_skimage = False -try: - from skimage.io import imread - has_skimage = True -except ImportError: - try: - from matplotlib.pyplot import imread - except ImportError: - imread = None - class ImageSequence(FramesSequence): """Read a directory of sequentially numbered image files into an @@ -68,11 +57,11 @@ class ImageSequence(FramesSequence): >>> frame_shape = video.frame_shape # Pixel dimensions of video """ def __init__(self, path_spec, plugin=None): - if not has_skimage: + if not imread.__module__.startswith("skimage"): if plugin is not None: warn("A plugin was specified but ignored. Plugins can only " "be specified if scikit-image is available. Instead, " - "ImageSequence will try using matplotlib") + "ImageSequence will use imageio") self.kwargs = dict() else: self.kwargs = dict(plugin=plugin) @@ -94,10 +83,6 @@ def __del__(self): self.close() def imread(self, filename, **kwargs): - if imread is None: - raise ImportError("One of the following packages are required for " - "using the ImageSequence reader: " - "scikit-image or matplotlib.") if self._is_zipfile: file_handle = BytesIO(self._zipfile.read(filename)) return imread(file_handle, **kwargs) diff --git a/pims/tests/test_common.py b/pims/tests/test_common.py index bd35ae8..91d048e 100644 --- a/pims/tests/test_common.py +++ b/pims/tests/test_common.py @@ -48,12 +48,6 @@ def _skip_if_no_tifffile(): raise unittest.SkipTest('tifffile not installed. Skipping.') -def _skip_if_no_imread(): - if pims.image_sequence.imread is None: - raise unittest.SkipTest('ImageSequence requires either matplotlib or' - ' scikit-image. Skipping.') - - def _skip_if_no_skimage(): try: import skimage @@ -61,12 +55,6 @@ def _skip_if_no_skimage(): raise unittest.SkipTest('skimage not installed. Skipping.') -def _skip_if_no_PIL(): - import pims.tiff_stack - if not pims.tiff_stack.PIL_available(): - raise unittest.SkipTest('PIL/Pillow not installed. Skipping.') - - def assert_image_equal(actual, expected): if np.issubdtype(actual.dtype, np.integer): assert_equal(actual, expected) @@ -183,7 +171,6 @@ def compare_slice_to_list(actual, expected): class TestRecursiveSlicing(unittest.TestCase): def setUp(self): - _skip_if_no_imread() class DemoReader(pims.ImageSequence): def imread(self, filename, **kwargs): return np.array([[filename]]) @@ -382,7 +369,6 @@ def test_simple_negative_index(self): class TestImageReaderTIFF(_image_single, unittest.TestCase): def setUp(self): - _skip_if_no_imread() self.filename = os.path.join(path, 'stuck.tif') self.frame0 = np.load(os.path.join(path, 'stuck_frame0.npy')) self.frame1 = np.load(os.path.join(path, 'stuck_frame1.npy')) @@ -395,7 +381,6 @@ def setUp(self): class TestImageReaderPNG(_image_single, unittest.TestCase): def setUp(self): - _skip_if_no_imread() self.klass = pims.ImageReader self.kwargs = dict() self.expected_shape = (10, 11) @@ -408,7 +393,6 @@ def setUp(self): class TestImageReaderND(_image_single, unittest.TestCase): def setUp(self): - _skip_if_no_imread() self.klass = pims.ImageReaderND self.kwargs = dict() self.expected_shape = (10, 11, 3) @@ -516,7 +500,6 @@ def check_skip(self): pass def setUp(self): - _skip_if_no_PIL() self.filename = os.path.join(path, 'stuck.tif') self.frame0 = np.load(os.path.join(path, 'stuck_frame0.npy')) self.frame1 = np.load(os.path.join(path, 'stuck_frame1.npy')) @@ -572,9 +555,6 @@ def test_metadata(self): class TestOpenFiles(unittest.TestCase): - def setUp(self): - _skip_if_no_PIL() - def test_open_png(self): self.filenames = ['dummy_png.png'] shape = (10, 11) @@ -583,7 +563,6 @@ def test_open_png(self): clean_dummy_png(path, self.filenames) def test_open_pngs(self): - _skip_if_no_imread() self.filepath = os.path.join(path, 'image_sequence') self.filenames = ['T76S3F00001.png', 'T76S3F00002.png', 'T76S3F00003.png', 'T76S3F00004.png', diff --git a/pims/tests/test_frame.py b/pims/tests/test_frame.py index 7d55da5..9d64318 100644 --- a/pims/tests/test_frame.py +++ b/pims/tests/test_frame.py @@ -7,13 +7,6 @@ from pims.frame import Frame -def _skip_if_no_PIL(): - try: - from PIL import Image - except ImportError: - raise unittest.SkipTest('PIL/Pillow not installed. Skipping.') - - def _skip_if_no_jinja2(): try: import jinja2 @@ -39,7 +32,6 @@ def test_creation_md(): def test_repr_html_(): - _skip_if_no_PIL() _skip_if_no_jinja2() # This confims a bugfix, where 16-bit images would raise # an error. diff --git a/pims/tests/test_imseq.py b/pims/tests/test_imseq.py index 380cc39..06a7b0d 100644 --- a/pims/tests/test_imseq.py +++ b/pims/tests/test_imseq.py @@ -13,7 +13,7 @@ from pims.tests.test_common import (_image_series, clean_dummy_png, save_dummy_png, - _skip_if_no_skimage, _skip_if_no_imread) + _skip_if_no_skimage) path, _ = os.path.split(os.path.abspath(__file__)) path = os.path.join(path, 'data') @@ -83,7 +83,6 @@ def tearDown(self): class TestImageSequenceAcceptsList(_image_series, unittest.TestCase): def setUp(self): - _skip_if_no_imread() self.filepath = os.path.join(path, 'image_sequence') self.filenames = ['T76S3F00001.png', 'T76S3F00002.png', 'T76S3F00003.png', 'T76S3F00004.png', @@ -107,7 +106,6 @@ def tearDown(self): class TestImageSequenceNaturalSorting(_image_series, unittest.TestCase): def setUp(self): - _skip_if_no_imread() self.filepath = os.path.join(path, 'image_sequence') self.filenames = ['T76S3F1.png', 'T76S3F20.png', 'T76S3F3.png', 'T76S3F4.png', @@ -150,7 +148,6 @@ def tearDown(self): class ImageSequenceND(_image_series, unittest.TestCase): def setUp(self): - _skip_if_no_imread() self.filepath = os.path.join(path, 'image_sequence3d') self.filenames = ['file_t001_z001_c1.png', 'file_t001_z001_c2.png', @@ -212,7 +209,6 @@ def test_sizeC(self): class ImageSequenceND_RGB(_image_series, unittest.TestCase): def setUp(self): - _skip_if_no_imread() self.filepath = os.path.join(path, 'image_sequence3d') self.filenames = ['file_t001_z001_c1.png', 'file_t001_z002_c1.png', @@ -249,7 +245,6 @@ def tearDown(self): class ReaderSequence(_image_series, unittest.TestCase): def setUp(self): - _skip_if_no_imread() self.filepath = os.path.join(path, 'image_sequence3d') self.filenames = ['file001.png', 'file002.png', diff --git a/setup.py b/setup.py index 1e81781..2539ad3 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,12 @@ cmdclass=versioneer.get_cmdclass(), description="Python Image Sequence", author="PIMS Contributors", - install_requires=['slicerator>=0.9.8', 'six>=1.8', 'numpy>=1.19'], + install_requires=[ + 'imageio', + 'numpy>=1.19', + 'six>=1.8', + 'slicerator>=0.9.8', + ], author_email="dallan@pha.jhu.edu", url="https://github.com/soft-matter/pims", packages=["pims", "pims.utils", "pims.tests"],