From c09f1bfd326876e4f6523a8cee3468e18455bb09 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sat, 7 Dec 2013 00:01:32 -0600 Subject: [PATCH] Revert previous 3 commits to test Travis CI failures. --- skimage/io/_io.py | 10 +- skimage/io/inherited_config.py | 127 ++++++++++++++++++ skimage/io/manage_plugins.py | 25 ++-- skimage/io/tests/test_collection.py | 153 +++++++++++++++------- skimage/io/tests/test_inherited_config.py | 47 +++++++ skimage/io/tests/test_multi_image.py | 69 ---------- 6 files changed, 303 insertions(+), 128 deletions(-) create mode 100644 skimage/io/inherited_config.py create mode 100644 skimage/io/tests/test_inherited_config.py delete mode 100644 skimage/io/tests/test_multi_image.py diff --git a/skimage/io/_io.py b/skimage/io/_io.py index 97f8718e..d38e7ac0 100644 --- a/skimage/io/_io.py +++ b/skimage/io/_io.py @@ -1,3 +1,4 @@ +import os from io import BytesIO import numpy as np @@ -94,7 +95,14 @@ def imread(fname, as_grey=False, plugin=None, flatten=None, as_grey = flatten with file_or_url_context(fname) as fname: - img = call_plugin('imread', fname, plugin=plugin, **plugin_args) + function = 'imread' + # TODO: This should probably use imghdr to get the image format. + try: + _, extension = os.path.splitext(fname) + function = function + extension.lower() + except AttributeError: # Buffers don't work with splitext + pass + img = call_plugin(function, fname, plugin=plugin, **plugin_args) if as_grey and getattr(img, 'ndim', 0) >= 3: img = rgb2grey(img) diff --git a/skimage/io/inherited_config.py b/skimage/io/inherited_config.py new file mode 100644 index 00000000..9cbc7857 --- /dev/null +++ b/skimage/io/inherited_config.py @@ -0,0 +1,127 @@ +class InheritedConfig(dict): + """Configuration dictionary where non-existent keys can inherit values. + + This class allows you to define parameter names that can match exactly, but + if it doesn't, parameter names will be searched based on key inheritance. + For example, the key 'size.text' will default to 'size'. + + Note that indexing into the dictionary will raise an error if it doesn't + match exactly, while `InheritedConfig.get` will look up values based on + inheritance. + + Parameters + ---------- + config_values : dict or list of (key, value) pairs + Default values for a configuration, where keys are the parameter names + and values are the associated value. + cascade_map : dict + Dictionary defining cascading defaults. If a parameter name is not + found, indexing `cascade_map` with the parameter name will return + the parameter to look for. + kwargs : dict + Keyword arguments for initializing dict. + """ + + _separator = '.' + + def __init__(self, config_values=None, **kwargs): + assert 'config_values' not in kwargs + + if config_values is None: + config_values = {} + + super(InheritedConfig, self).__init__(config_values, **kwargs) + + def __getitem__(self, key): + return self.get(key, _raise=True) + + def get(self, key, default=None, _raise=False): + """Return best matching config value for `key`. + + Get value from configuration. The search for `key` is in the following + order: + + - `self` (Value in global configuration) + - `default` + - Alternate key specified by `self.cascade_map` + + This method supports the pattern commonly used for optional keyword + arguments to a function. For example:: + + >>> def print_value(key, **kwargs): + ... print kwargs.get(key, 0) + >>> print_value('size') + 0 + >>> print_value('size', size=1) + 1 + + Instead, you would create a config class and write:: + + >>> config = InheritedConfig(size=0) + >>> def print_value(key, **kwargs): + ... print kwargs.get(key, config.get(key)) + >>> print_value('size') + 0 + >>> print_value('size', size=1) + 1 + >>> print_value('non-existent') + None + >>> print_value('size.text') + 0 + + See examples below for a demonstration of the cascading of + configuration names. + + Parameters + ---------- + key : str + Name of config value you want. + default : object + Default value if `key` doesn't exist in instance. + + Examples + -------- + >>> config = InheritedConfig(size=0) + >>> config.get('size') + 0 + >>> top_choice={'size': 1} + >>> top_choice.get('size', config.get('size')) + 1 + >>> config.get('non-existent', 'unknown') + 'unknown' + >>> config.get('size.text') + 0 + >>> config.get('size.text', 2) + 2 + >>> top_choice.get('size', config.get('size.text')) + 1 + """ + if key in self.keys(): + return super(InheritedConfig, self).__getitem__(key) + elif default is not None: + return default + elif self._separator in key: + return self.get(self._parent(key)) + elif _raise: + raise KeyError('%r not in %s' % (key, self.__class__.__name__)) + else: + return None + + def _parent(self, key): + """Return parent key.""" + parts = key.split(self._separator) + return self._separator.join(parts[:-1]) + + def __contains__(self, key): + if key in self.keys(): + return True + elif self._separator in key: + return self.__contains__(self._parent(key)) + else: + return False + + +if __name__ == '__main__': + import doctest + + doctest.testmod() diff --git a/skimage/io/manage_plugins.py b/skimage/io/manage_plugins.py index ace57d8f..ebc55b6a 100644 --- a/skimage/io/manage_plugins.py +++ b/skimage/io/manage_plugins.py @@ -10,6 +10,8 @@ except ImportError: import os.path from glob import glob +from skimage.io.inherited_config import InheritedConfig + __all__ = ['use_plugin', 'call_plugin', 'plugin_info', 'plugin_order', 'reset_plugins', 'find_available_plugins', 'available_plugins'] @@ -29,6 +31,7 @@ preferred_plugins = { # Use PIL as the default imread plugin, since matplotlib (1.2.x) # is buggy (flips PNGs around, returns bytes as floats, etc.) 'imread': ['pil'], + 'imread.tiff': ['tifffile'], } @@ -37,11 +40,11 @@ def _clear_plugins(): """ global plugin_store - plugin_store = {'imread': [], - 'imsave': [], - 'imshow': [], - 'imread_collection': [], - '_app_show': []} + plugin_store = InheritedConfig({'imread': [], + 'imsave': [], + 'imshow': [], + 'imread_collection': [], + '_app_show': []}) _clear_plugins() @@ -197,19 +200,15 @@ def use_plugin(name, kind=None): See Also -------- - available_plugins : List of available plugins + plugins : List of available plugins Examples -------- - To use a plugin named 'null' as the default image reader, you would write: + Use the Python Imaging Library to read images: - >>> from skimage import io - >>> io.use_plugin('null', 'imread') - - To see a list of available plugins run ``io.available_plugins``. Note that - this lists plugins that are defined, but the full list may not be usable - if your system does not have the required libraries installed. + >>> from skimage.io import use_plugin + >>> use_plugin('pil', 'imread') """ if kind is None: diff --git a/skimage/io/tests/test_collection.py b/skimage/io/tests/test_collection.py index 1e9fea73..cf5ef820 100644 --- a/skimage/io/tests/test_collection.py +++ b/skimage/io/tests/test_collection.py @@ -1,72 +1,88 @@ +import sys import os.path import numpy as np -from numpy.testing import assert_raises, assert_equal, assert_allclose +from numpy.testing import (assert_raises, + assert_equal, + assert_array_almost_equal, + ) +from numpy.testing.decorators import skipif from skimage import data_dir -from skimage.io.collection import ImageCollection, alphanumeric_key +from skimage.io import ImageCollection, MultiImage +from skimage.io.collection import alphanumeric_key +from skimage.io import Image as ioImage + +import six -def test_string_split(): - test_string = 'z23a' - test_str_result = ['z', 23, 'a'] - assert_equal(alphanumeric_key(test_string), test_str_result) +try: + from PIL import Image +except ImportError: + PIL_available = False +else: + PIL_available = True -def test_string_sort(): - filenames = ['f9.10.png', 'f9.9.png', 'f10.10.png', 'f10.9.png', - 'e9.png', 'e10.png', 'em.png'] - sorted_filenames = ['e9.png', 'e10.png', 'em.png', 'f9.9.png', - 'f9.10.png', 'f10.9.png', 'f10.10.png'] - sorted_filenames = sorted(filenames, key=alphanumeric_key) - assert_equal(sorted_filenames, sorted_filenames) +class TestAlphanumericKey(): + def setUp(self): + self.test_string = 'z23a' + self.test_str_result = ['z', 23, 'a'] + self.filenames = ['f9.10.png', 'f9.9.png', 'f10.10.png', 'f10.9.png', + 'e9.png', 'e10.png', 'em.png'] + self.sorted_filenames = \ + ['e9.png', 'e10.png', 'em.png', 'f9.9.png', 'f9.10.png', + 'f10.9.png', 'f10.10.png'] + + def test_string_split(self): + assert_equal(alphanumeric_key(self.test_string), self.test_str_result) + + def test_string_sort(self): + sorted_filenames = sorted(self.filenames, key=alphanumeric_key) + assert_equal(sorted_filenames, self.sorted_filenames) class TestImageCollection(): - - pattern = [os.path.join(data_dir, pic) - for pic in ['camera.png', 'color.png']] - - pattern_matched = [os.path.join(data_dir, pic) - for pic in ['camera.png', 'moon.png']] + pattern = [os.path.join(data_dir, pic) for pic in ['camera.png', + 'color.png']] + pattern_matched = [os.path.join(data_dir, pic) for pic in + ['camera.png', 'moon.png']] def setUp(self): - # Generic image collection with images of different shapes. - self.images = ImageCollection(self.pattern) - # Image collection with images having shapes that match. - self.images_matched = ImageCollection(self.pattern_matched) + self.collection = ImageCollection(self.pattern) + self.collection_matched = ImageCollection(self.pattern_matched) def test_len(self): - assert len(self.images) == 2 + assert len(self.collection) == 2 def test_getitem(self): - num = len(self.images) + num = len(self.collection) for i in range(-num, num): - assert type(self.images[i]) is np.ndarray - assert_allclose(self.images[0], - self.images[-num]) + assert type(self.collection[i]) is np.ndarray + assert_array_almost_equal(self.collection[0], + self.collection[-num]) - # assert_raises expects a callable, hence this thin wrapper function. + #assert_raises expects a callable, hence this do-very-little func def return_img(n): - return self.images[n] + return self.collection[n] assert_raises(IndexError, return_img, num) assert_raises(IndexError, return_img, -num - 1) def test_slicing(self): - assert type(self.images[:]) is ImageCollection - assert len(self.images[:]) == 2 - assert len(self.images[:1]) == 1 - assert len(self.images[1:]) == 1 - assert_allclose(self.images[0], self.images[:1][0]) - assert_allclose(self.images[1], self.images[1:][0]) - assert_allclose(self.images[1], self.images[::-1][0]) - assert_allclose(self.images[0], self.images[::-1][1]) + assert type(self.collection[:]) is ImageCollection + assert len(self.collection[:]) == 2 + assert len(self.collection[:1]) == 1 + assert len(self.collection[1:]) == 1 + assert_array_almost_equal(self.collection[0], self.collection[:1][0]) + assert_array_almost_equal(self.collection[1], self.collection[1:][0]) + assert_array_almost_equal(self.collection[1], self.collection[::-1][0]) + assert_array_almost_equal(self.collection[0], self.collection[::-1][1]) def test_files_property(self): - assert isinstance(self.images.files, list) + assert isinstance(self.collection.files, list) def set_files(f): - self.images.files = f + self.collection.files = f assert_raises(AttributeError, set_files, 'newfiles') def test_custom_load(self): @@ -79,12 +95,59 @@ class TestImageCollection(): assert_equal(ic[1], (2, 'two')) def test_concatenate(self): - array = self.images_matched.concatenate() - expected_shape = (len(self.images_matched),) + self.images[0].shape - assert_equal(array.shape, expected_shape) + ar = self.collection_matched.concatenate() + assert_equal(ar.shape, (len(self.collection_matched),) + + self.collection[0].shape) + assert_raises(ValueError, self.collection.concatenate) - def test_concatentate_mismatched_image_shapes(self): - assert_raises(ValueError, self.images.concatenate) + +class TestMultiImage(): + + def setUp(self): + # This multipage TIF file was created with imagemagick: + # convert im1.tif im2.tif -adjoin multipage.tif + if PIL_available: + self.img = MultiImage(os.path.join(data_dir, 'multipage.tif')) + + @skipif(not PIL_available) + def test_len(self): + assert len(self.img) == 2 + + @skipif(not PIL_available) + def test_getitem(self): + num = len(self.img) + for i in range(-num, num): + assert type(self.img[i]) is np.ndarray + assert_array_almost_equal(self.img[0], + self.img[-num]) + + #assert_raises expects a callable, hence this do-very-little func + def return_img(n): + return self.img[n] + assert_raises(IndexError, return_img, num) + assert_raises(IndexError, return_img, -num - 1) + + @skipif(not PIL_available) + def test_files_property(self): + assert isinstance(self.img.filename, six.string_types) + + def set_filename(f): + self.img.filename = f + assert_raises(AttributeError, set_filename, 'newfile') + + @skipif(not PIL_available) + def test_conserve_memory_property(self): + assert isinstance(self.img.conserve_memory, bool) + + def set_mem(val): + self.img.conserve_memory = val + assert_raises(AttributeError, set_mem, True) + + @skipif(not PIL_available) + def test_concatenate(self): + ar = self.img.concatenate() + assert_equal(ar.shape, (len(self.img),) + + self.img[0].shape) if __name__ == "__main__": diff --git a/skimage/io/tests/test_inherited_config.py b/skimage/io/tests/test_inherited_config.py new file mode 100644 index 00000000..207d3cf1 --- /dev/null +++ b/skimage/io/tests/test_inherited_config.py @@ -0,0 +1,47 @@ +from skimage.io.inherited_config import InheritedConfig + + +def test_get_non_existent(): + config = InheritedConfig() + assert config.get('size') is None + + +def test_get_simple(): + config = InheritedConfig({'imread': 'matplotlib'}) + assert config.get('imread') == 'matplotlib' + + +def test_get_default(): + config = InheritedConfig() + assert config.get('size', 10) == 10 + + +def test_get_best(): + config = InheritedConfig({'size': 0, 'size.text': 1}) + assert config.get('size.text') == 1 + + +def test_get_multi_level(): + config = InheritedConfig({'size': 0}) + assert config.get('size.text.title') == 0 + config['size.text'] = 1 + assert config.get('size.text.title') == 1 + config['size.text.title'] = 2 + assert config.get('size.text.title') == 2 + + +def test_contains(): + config = InheritedConfig({'imread': 'matplotlib'}) + assert 'imread' in config + assert 'imread.jpg' in config + + +def test_getitem(): + config = InheritedConfig({'imread': 'a'}) + assert config['imread'] == 'a' + assert config['imread.png'] == 'a' + + +if __name__ == '__main__': + from numpy import testing + testing.run_module_suite() diff --git a/skimage/io/tests/test_multi_image.py b/skimage/io/tests/test_multi_image.py deleted file mode 100644 index ebaa71dc..00000000 --- a/skimage/io/tests/test_multi_image.py +++ /dev/null @@ -1,69 +0,0 @@ -import os - -import numpy as np -from numpy.testing.decorators import skipif -from numpy.testing import assert_raises, assert_equal, assert_allclose - -from skimage import data_dir -from skimage.io.collection import MultiImage - -try: - from PIL import Image -except ImportError: - PIL_available = False -else: - PIL_available = True - -import six - - -class TestMultiImage(): - - def setUp(self): - # This multipage TIF file was created with imagemagick: - # convert im1.tif im2.tif -adjoin multipage.tif - if PIL_available: - self.img = MultiImage(os.path.join(data_dir, 'multipage.tif')) - - @skipif(not PIL_available) - def test_len(self): - assert len(self.img) == 2 - - @skipif(not PIL_available) - def test_getitem(self): - num = len(self.img) - for i in range(-num, num): - assert type(self.img[i]) is np.ndarray - assert_allclose(self.img[0], self.img[-num]) - - # assert_raises expects a callable, hence this thin wrapper function. - def return_img(n): - return self.img[n] - assert_raises(IndexError, return_img, num) - assert_raises(IndexError, return_img, -num - 1) - - @skipif(not PIL_available) - def test_files_property(self): - assert isinstance(self.img.filename, six.string_types) - - def set_filename(f): - self.img.filename = f - assert_raises(AttributeError, set_filename, 'newfile') - - @skipif(not PIL_available) - def test_conserve_memory_property(self): - assert isinstance(self.img.conserve_memory, bool) - - def set_mem(val): - self.img.conserve_memory = val - assert_raises(AttributeError, set_mem, True) - - @skipif(not PIL_available) - def test_concatenate(self): - array = self.img.concatenate() - assert_equal(array.shape, (len(self.img),) + self.img[0].shape) - - -if __name__ == "__main__": - from numpy.testing import run_module_suite - run_module_suite()