diff --git a/.travis.yml b/.travis.yml index 1f292dfa..3a061e13 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,28 +3,19 @@ # After changing this file, check it on: # http://lint.travis-ci.org/ - language: python -python: - - 2.6 -matrix: - include: - - python: 2.7 - env: - - PYTHON=python - - PYTHONWARNINGS=all - - PYTHONX=python - - PYVER=2.x - - python: 3.2 - env: - - PYTHON=python3 - - PYTHONWARNINGS=all - - PYTHONX=python3 - - PYVER=3.x - exclude: - - python: 2.6 +# Tthe Travis python is set to 3.2 for all builds, since we use the default python for the 3.2 build and the anaconda python otherwise +python: + - 3.2 + +env: + - ENV="python=2.6 numpy=1.6 cython=0.19.2" + - ENV="python=2.7 numpy cython" + - ENV="python=3.2" + - ENV="python=3.3 numpy cython" + - ENV="python=3.4 numpy cython" virtualenv: system_site_packages: true @@ -32,64 +23,90 @@ virtualenv: before_install: - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start - - sudo apt-get update - - sudo apt-get install $PYTHON-numpy - - wget https://raw.githubusercontent.com/numpy/numpy/master/numpy/_import_tools.py -O /home/travis/virtualenv/python3.2_with_system_site_packages/lib/python3.2/site-packages/numpy/_import_tools.py - - - sudo apt-get install $PYTHON-scipy - - sudo apt-get install libfreeimage3 - - - if [[ $PYVER == '2.x' ]]; then - - sudo apt-get install $PYTHON-qt4; - - sudo apt-get install $PYTHON-matplotlib; - - fi - - if [[ $PYVER == '3.x' ]]; then - - sudo apt-get install $PYTHON-pyqt4; - - pip install --use-mirrors matplotlib; - - fi - - - pip install pillow - - pip install cython - - pip install flake8 - - pip install six - - pip install networkx - - - pip install nose-cov - - pip install coveralls - + # Python 3.2 is not supported by Miniconda, so we use the package manager for that run. + # NumPy has a bug in python 3 that is only fixed in the latest version, + # hence the below wget of numpy/_import_tools.py from github. + - if [[ $ENV == python=3.2 ]]; then + sudo apt-get install python3-numpy; + wget https://raw.githubusercontent.com/numpy/numpy/master/numpy/_import_tools.py -O /home/travis/virtualenv/python3.2_with_system_site_packages/lib/python3.2/site-packages/numpy/_import_tools.py; + sudo apt-get install python3-scipy; + pip install cython flake8 six; + else + wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh; + bash miniconda.sh -b -p $HOME/miniconda; + export PATH="$HOME/miniconda/bin:$PATH"; + hash -r; + conda config --set always_yes yes; + conda update conda; + conda info -a; + travis_retry conda create -n test $ENV six scipy pip flake8 nose; + source activate test; + fi + - pip install coveralls pillow - python check_bento_build.py install: - tools/header.py "Dependency versions" - tools/build_versions.py - + - export PYTHONWARNINGS=all - python setup.py build_ext --inplace script: - # Matplotlib settings - - mkdir -p $HOME/.matplotlib - - touch $HOME/.matplotlib/matplotlibrc - - "echo 'backend : Agg' > $HOME/.matplotlib/matplotlibrc" - - "echo 'backend.qt4 : PyQt4' >> $HOME/.matplotlib/matplotlibrc" - - # Run all tests - - if [[ $PYVER == '3.x' ]]; then - - nosetests --exe -v --with-doctest --with-cov --cov skimage --cov-config=.coveragerc skimage - - fi - - if [[ $PYVER == '2.x' ]]; then - - nosetests --exe -v --with-doctest skimage - - fi - # Run all doc examples - - export PYTHONPATH=$(pwd):$PYTHONPATH - - for f in doc/examples/*.py; do $PYTHONX "$f"; if [ $? -ne 0 ]; then exit 1; fi done - - for f in doc/examples/applications/*.py; do $PYTHONX "$f"; if [ $? -ne 0 ]; then exit 1; fi done + # Run all tests with minimum dependencies + - nosetests --exe -v skimage # Run pep8 and flake tests - flake8 --exit-zero --exclude=test_*,six.py skimage doc/examples viewer_examples + # Install optional dependencies to get full test coverage + # Notes: + # - pyfits and imread do NOT support py3.2 + # - Use the png headers included in anaconda (from matplotlib install) + # TODO: Remove the libm removal when anaconda fixes their libraries + # The solution was suggested here + # https://groups.google.com/a/continuum.io/forum/#!topic/anaconda/-DLG2ZdTkw0 + - if [[ $ENV == python=3.2 ]]; then + sudo apt-get install -qq python3-pyqt4; + pip install --use-mirrors -q matplotlib; + pip install -q networkx; + else + travis_retry conda install -q matplotlib pyqt networkx; + sudo cp ~/miniconda/envs/test/include/png* /usr/include; + rm ~/miniconda/envs/test/lib/libm-2.5.so; + rm ~/miniconda/envs/test/lib/libm.*; + sudo apt-get install -qq libtiff4-dev libwebp-dev xcftools; + pip install -q imread; + pip install -q pyfits; + fi + - sudo apt-get install -qq libfreeimage3 + # TODO: update when SimpleITK become available on py34 or hopefully pip + - if [[ $ENV != python=3.4* ]]; then + travis_retry easy_install -q SimpleITK; + fi + + # Matplotlib settings + - export MPL_DIR=$HOME/.config/matplotlib + - mkdir -p $MPL_DIR + - touch $MPL_DIR/matplotlibrc + - "echo 'backend : Agg' > $MPL_DIR/matplotlibrc" + - "echo 'backend.qt4 : PyQt4' >> $MPL_DIR/matplotlibrc" + + # Run all doc examples + - export PYTHONPATH=$(pwd):$PYTHONPATH + - for f in doc/examples/*.py; do python "$f"; if [ $? -ne 0 ]; then exit 1; fi done + - for f in doc/examples/applications/*.py; do python "$f"; if [ $? -ne 0 ]; then exit 1; fi done + + # run tests again with optional dependencies to get more coverage + # measure coverage on py3.3 + - if [[ $ENV == python=3.3* ]]; then + nosetests --exe -v --with-doctest --with-cov --cover-package skimage; + else + nosetests --exe -v --with-doctest skimage; + fi + after_success: - - if [[ $PYVER == '3.x' ]]; then - - coveralls - - fi + - if [[ $ENV == python=3.3* ]]; then + coveralls; + fi diff --git a/skimage/_shared/tests/test_version_requirements.py b/skimage/_shared/tests/test_version_requirements.py new file mode 100644 index 00000000..59cf5814 --- /dev/null +++ b/skimage/_shared/tests/test_version_requirements.py @@ -0,0 +1,42 @@ +"""Tests for the version requirement functions. + +""" +import numpy as np +from numpy.testing import assert_raises, assert_equal +import nose +from skimage._shared import version_requirements as version_req + + +def test_get_module_version(): + assert version_req.get_module_version('numpy') + assert version_req.get_module_version('scipy') + assert_raises(ImportError, + lambda: version_req.get_module_version('fakenumpy')) + + +def test_is_installed(): + assert version_req.is_installed('python', '>=2.6') + assert not version_req.is_installed('numpy', '<1.0') + + +def test_require(): + + # A function that only runs on Python >2.6 and numpy > 1.5 (should pass) + @version_req.require('python', '>2.6') + @version_req.require('numpy', '>1.5') + def foo(): + return 1 + + assert_equal(foo(), 1) + + # function that requires scipy < 0.1 (should fail) + @version_req.require('scipy', '<0.1') + def bar(): + return 0 + + assert_raises(ImportError, lambda: bar()) + + +def test_get_module(): + assert_equal(version_req.get_module('numpy'), np) + assert_equal(version_req.get_module('nose'), nose) diff --git a/skimage/_shared/version_requirements.py b/skimage/_shared/version_requirements.py new file mode 100644 index 00000000..4ebf8ee9 --- /dev/null +++ b/skimage/_shared/version_requirements.py @@ -0,0 +1,142 @@ +from distutils.version import LooseVersion +import functools +import re +import sys + + +def _check_version(actver, version, cmp_op): + """ + Check version string of an active module against a required version. + + If dev/prerelease tags result in TypeError for string-number comparison, + it is assumed that the dependency is satisfied. + Users on dev branches are responsible for keeping their own packages up to + date. + + Copyright (C) 2013 The IPython Development Team + + Distributed under the terms of the BSD License. + """ + try: + if cmp_op == '>': + return LooseVersion(actver) > LooseVersion(version) + elif cmp_op == '>=': + return LooseVersion(actver) >= LooseVersion(version) + elif cmp_op == '=': + return LooseVersion(actver) == LooseVersion(version) + elif cmp_op == '<': + return LooseVersion(actver) < LooseVersion(version) + else: + return False + except TypeError: + return True + + +def get_module_version(module_name): + """Return module version or None if version can't be retrieved.""" + mod = __import__(module_name, + fromlist=[module_name.rpartition('.')[-1]]) + return getattr(mod, '__version__', getattr(mod, 'VERSION', None)) + + +def is_installed(name, version=None): + """Test if *name* is installed. + + Parameters + ---------- + name : str + Name of module or "python" + version : str, optional + Version string to test against. + If version is not None, checking version + (must have an attribute named '__version__' or 'VERSION') + Version may start with =, >=, > or < to specify the exact requirement + + Returns + ------- + out : bool + True if *name* is installed matching the optional version. + + Note + ---- + Original Copyright (C) 2009-2011 Pierre Raybaut + Licensed under the terms of the MIT License. + """ + if name.lower() == 'python': + actver = sys.version[:6] + else: + try: + actver = get_module_version(name) + except ImportError: + return False + if version is None: + return True + else: + match = re.search('[0-9]', version) + assert match is not None, "Invalid version number" + symb = version[:match.start()] + if not symb: + symb = '=' + assert symb in ('>=', '>', '=', '<'),\ + "Invalid version condition '%s'" % symb + version = version[match.start():] + return _check_version(actver, version, symb) + + +def require(name, version=None): + """Return decorator that forces a requirement for a function or class. + + Parameters + ---------- + name : str + Name of module or "python". + version : str, optional + Version string to test against. + If version is not None, checking version + (must have an attribute named '__version__' or 'VERSION') + Version may start with =, >=, > or < to specify the exact requirement + + Returns + ------- + func : function + A decorator that raises an ImportError if a function is run + in the absence of the input dependency. + """ + def decorator(obj): + @functools.wraps(obj) + def func_wrapped(*args, **kwargs): + if is_installed(name, version): + return obj(*args, **kwargs) + else: + msg = '"%s" in "%s" requires "%s' + msg = msg % (obj, obj.__module__, name) + if not version is None: + msg += " %s" % version + raise ImportError(msg + '"') + return func_wrapped + return decorator + + +def get_module(module_name, version=None): + """Return a module object of name *module_name* if installed. + + Parameters + ---------- + module_name : str + Name of module. + version : str, optional + Version string to test against. + If version is not None, checking version + (must have an attribute named '__version__' or 'VERSION') + Version may start with =, >=, > or < to specify the exact requirement + + Returns + ------- + mod : module or None + Module if *module_name* is installed matching the optional version + or None otherwise. + """ + if not is_installed(module_name, version): + return None + return __import__(module_name, + fromlist=[module_name.rpartition('.')[-1]]) diff --git a/skimage/exposure/_adapthist.py b/skimage/exposure/_adapthist.py index 5da50029..9cfc716e 100644 --- a/skimage/exposure/_adapthist.py +++ b/skimage/exposure/_adapthist.py @@ -17,7 +17,7 @@ import numpy as np import skimage from skimage.color.adapt_rgb import adapt_rgb, hsv_value from skimage.exposure import rescale_intensity -from skimage.util import view_as_blocks +from skimage.util import view_as_blocks, pad MAX_REG_X = 16 # max. # contextual regions in x-direction */ @@ -125,7 +125,7 @@ def _clahe(image, ntiles_x, ntiles_y, clip_limit, nbins=128): if h_inner != image.shape[1] or w_inner != image.shape[0]: h_pad = height * ntiles_y - image.shape[0] w_pad = width * ntiles_x - image.shape[1] - image = np.pad(image, ((0, h_pad), (0, w_pad)), 'reflect') + image = pad(image, ((0, h_pad), (0, w_pad)), mode='reflect') h_inner, w_inner = image.shape bin_size = 1 + NR_OF_GREY / nbins diff --git a/skimage/feature/tests/test_util.py b/skimage/feature/tests/test_util.py index 2b601b94..f15bcb2a 100644 --- a/skimage/feature/tests/test_util.py +++ b/skimage/feature/tests/test_util.py @@ -1,5 +1,8 @@ import numpy as np -import matplotlib.pyplot as plt +try: + import matplotlib.pyplot as plt +except ImportError: + plt = None from numpy.testing import assert_equal, assert_raises from skimage.feature.util import (FeatureDetector, DescriptorExtractor, @@ -39,6 +42,7 @@ def test_mask_border_keypoints(): [0, 0, 0, 0, 1]) +@np.testing.decorators.skipif(plt is None) def test_plot_matches(): fig, ax = plt.subplots(nrows=1, ncols=1) diff --git a/skimage/graph/graph_cut.py b/skimage/graph/graph_cut.py index 91d64aec..d9fcfdef 100644 --- a/skimage/graph/graph_cut.py +++ b/skimage/graph/graph_cut.py @@ -1,4 +1,8 @@ -import networkx as nx +try: + import networkx as nx +except ImportError: + import warnings + warnings.warn('"cut_threshold" requires networkx') import numpy as np diff --git a/skimage/graph/rag.py b/skimage/graph/rag.py index 84721736..7c1b7f07 100644 --- a/skimage/graph/rag.py +++ b/skimage/graph/rag.py @@ -1,4 +1,13 @@ -import networkx as nx +try: + import networkx as nx +except ImportError: + msg = "Graph functions require networkx, which is not installed" + class nx: + class Graph: + def __init__(self, *args, **kwargs): + raise ImportError(msg) + import warnings + warnings.warn(msg) import numpy as np from scipy.ndimage import filters from scipy import ndimage as nd diff --git a/skimage/graph/tests/test_rag.py b/skimage/graph/tests/test_rag.py index af7481eb..753bcb74 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -1,5 +1,7 @@ import numpy as np from skimage import graph +from skimage._shared.version_requirements import is_installed +from numpy.testing.decorators import skipif def max_edge(g, src, dst, n): @@ -9,6 +11,7 @@ def max_edge(g, src, dst, n): return max(w1, w2) +@skipif(not is_installed('networkx')) def test_rag_merge(): g = graph.rag.RAG() @@ -43,6 +46,7 @@ def test_rag_merge(): assert g.edges() == [] +@skipif(not is_installed('networkx')) def test_threshold_cut(): img = np.zeros((100, 100, 3), dtype='uint8') diff --git a/skimage/io/_plugins/freeimage_plugin.py b/skimage/io/_plugins/freeimage_plugin.py index 07e8d313..5ebe8214 100644 --- a/skimage/io/_plugins/freeimage_plugin.py +++ b/skimage/io/_plugins/freeimage_plugin.py @@ -85,7 +85,7 @@ def load_freeimage(): if errors: # No freeimage library loaded, and load-errors reported for some # candidate libs - err_txt = ['%s:\n%s' % (l, str(e.message)) for l, e in errors] + err_txt = ['%s:\n%s' % (l, str(e)) for l, e in errors] raise RuntimeError('One or more FreeImage libraries were found, but ' 'could not be loaded due to the following errors:\n' '\n\n'.join(err_txt)) diff --git a/skimage/io/_plugins/imread_plugin.py b/skimage/io/_plugins/imread_plugin.py index 46382cdf..cb715e57 100644 --- a/skimage/io/_plugins/imread_plugin.py +++ b/skimage/io/_plugins/imread_plugin.py @@ -1,6 +1,6 @@ __all__ = ['imread', 'imsave'] -from skimage.utils.dtype import convert +from skimage.util.dtype import convert try: import imread as _imread diff --git a/skimage/io/_plugins/pil_plugin.py b/skimage/io/_plugins/pil_plugin.py index 88e7f04b..e6f9c491 100644 --- a/skimage/io/_plugins/pil_plugin.py +++ b/skimage/io/_plugins/pil_plugin.py @@ -21,6 +21,7 @@ def imread(fname, dtype=None): """ im = Image.open(fname) + fp = im.fp if im.mode == 'P': if _palette_is_grayscale(im): im = im.convert('L') @@ -35,8 +36,9 @@ def imread(fname, dtype=None): im.shape = shape[::-1] elif 'A' in im.mode: im = im.convert('RGBA') - - return np.array(im, dtype=dtype) + im = np.array(im, dtype=dtype) + fp.close() + return im def _palette_is_grayscale(pil_image): diff --git a/skimage/io/_plugins/qt_plugin.py b/skimage/io/_plugins/qt_plugin.py index db4cb4cc..f3a3b8e0 100644 --- a/skimage/io/_plugins/qt_plugin.py +++ b/skimage/io/_plugins/qt_plugin.py @@ -158,7 +158,7 @@ def imsave(filename, img, format_str=None): qbuffer.open(QtCore.QIODevice.ReadWrite) saved = qimg.save(qbuffer, format_str.upper()) qbuffer.seek(0) - filename.write(qbuffer.readAll()) + filename.write(qbuffer.readAll().data()) qbuffer.close() else: saved = qimg.save(filename) diff --git a/skimage/io/tests/test_imread.py b/skimage/io/tests/test_imread.py index 25ad870e..a2c32b79 100644 --- a/skimage/io/tests/test_imread.py +++ b/skimage/io/tests/test_imread.py @@ -7,6 +7,7 @@ from tempfile import NamedTemporaryFile from skimage import data_dir from skimage.io import imread, imsave, use_plugin, reset_plugins +import skimage.io as sio try: import imread as _imread @@ -39,11 +40,13 @@ def test_imread_palette(): img = imread(os.path.join(data_dir, 'palette_color.png')) assert img.ndim == 3 + @skipif(not imread_available) def test_imread_truncated_jpg(): assert_raises((RuntimeError, ValueError), sio.imread, - os.path.join(si.data_dir, 'truncated.jpg')) + os.path.join(data_dir, 'truncated.jpg')) + @skipif(not imread_available) def test_bilevel(): diff --git a/skimage/io/tests/test_simpleitk.py b/skimage/io/tests/test_simpleitk.py index ab8cd592..e7fb1e83 100644 --- a/skimage/io/tests/test_simpleitk.py +++ b/skimage/io/tests/test_simpleitk.py @@ -1,6 +1,7 @@ import os.path import numpy as np from numpy.testing.decorators import skipif +from numpy.testing import assert_raises from tempfile import NamedTemporaryFile @@ -52,12 +53,15 @@ def test_bilevel(): img = imread(os.path.join(data_dir, 'checker_bilevel.png')) np.testing.assert_array_equal(img, expected) - +""" +#TODO: This test causes a Segmentation fault @skipif(not sitk_available) def test_imread_truncated_jpg(): assert_raises((RuntimeError, ValueError), - sio.imread, - os.path.join(si.data_dir, 'truncated.jpg')) + imread, + os.path.join(data_dir, 'truncated.jpg')) +""" + @skipif(not sitk_available) def test_imread_uint16(): diff --git a/skimage/morphology/greyreconstruct.py b/skimage/morphology/greyreconstruct.py index cea200a4..bce26d6b 100644 --- a/skimage/morphology/greyreconstruct.py +++ b/skimage/morphology/greyreconstruct.py @@ -131,7 +131,7 @@ def reconstruction(seed, mask, method='dilation', selem=None, offset=None): if selem is None: selem = np.ones([3] * seed.ndim, dtype=bool) else: - selem = selem.astype(bool, copy=True) + selem = selem.astype(bool) if offset is None: if not all([d % 2 == 1 for d in selem.shape]): diff --git a/skimage/morphology/misc.py b/skimage/morphology/misc.py index 94aadf7a..edf2892b 100644 --- a/skimage/morphology/misc.py +++ b/skimage/morphology/misc.py @@ -5,12 +5,13 @@ from .selem import _default_selem # Our function names don't exactly correspond to ndimages. # This dictionary translates from our names to scipy's. funcs = ('erosion', 'dilation', 'opening', 'closing') -skimage2ndimage = {x: 'grey_' + x for x in funcs} +skimage2ndimage = dict((x, 'grey_' + x) for x in funcs) # These function names are the same in ndimage. funcs = ('binary_erosion', 'binary_dilation', 'binary_opening', 'binary_closing', 'black_tophat', 'white_tophat') -skimage2ndimage.update({x: x for x in funcs}) +skimage2ndimage.update(dict((x, x) for x in funcs)) + def default_fallback(func): """Decorator to fall back on ndimage for images with more than 2 dimensions diff --git a/skimage/restoration/unwrap.py b/skimage/restoration/unwrap.py index 085bef0b..f0025775 100644 --- a/skimage/restoration/unwrap.py +++ b/skimage/restoration/unwrap.py @@ -84,6 +84,7 @@ def unwrap_phase(image, wrap_around=False): if np.ma.isMaskedArray(image): mask = np.require(image.mask, np.uint8, ['C']) + image = image.data else: mask = np.zeros_like(image, dtype=np.uint8, order='C') image_not_masked = np.asarray(image, dtype=np.float64, order='C') diff --git a/skimage/viewer/__init__.py b/skimage/viewer/__init__.py index 5eed9589..4bf70a93 100644 --- a/skimage/viewer/__init__.py +++ b/skimage/viewer/__init__.py @@ -1 +1,11 @@ +from warnings import warn +from skimage._shared.version_requirements import is_installed + from .viewers import ImageViewer, CollectionViewer +from .qt import qt_api + +viewer_available = not qt_api is None and is_installed('matplotlib') +if not viewer_available: + warn('Viewer requires matplotlib and Qt') + +del qt_api, is_installed, warn diff --git a/skimage/viewer/canvastools/base.py b/skimage/viewer/canvastools/base.py index 7f61ce2c..29e6099d 100644 --- a/skimage/viewer/canvastools/base.py +++ b/skimage/viewer/canvastools/base.py @@ -1,10 +1,8 @@ import numpy as np - try: from matplotlib import lines except ImportError: - print("Could not import matplotlib -- skimage.viewer not available.") - + pass __all__ = ['CanvasToolBase', 'ToolHandles'] diff --git a/skimage/viewer/canvastools/linetool.py b/skimage/viewer/canvastools/linetool.py index fed18383..61d94825 100644 --- a/skimage/viewer/canvastools/linetool.py +++ b/skimage/viewer/canvastools/linetool.py @@ -3,8 +3,7 @@ import numpy as np try: from matplotlib import lines except ImportError: - print("Could not import matplotlib -- skimage.viewer not available.") - + pass from skimage.viewer.canvastools.base import CanvasToolBase, ToolHandles diff --git a/skimage/viewer/canvastools/painttool.py b/skimage/viewer/canvastools/painttool.py index 6af8b5d3..fd994a10 100644 --- a/skimage/viewer/canvastools/painttool.py +++ b/skimage/viewer/canvastools/painttool.py @@ -1,9 +1,11 @@ import numpy as np -import matplotlib.pyplot as plt -import matplotlib.colors as mcolors -LABELS_CMAP = mcolors.ListedColormap(['white', 'red', 'dodgerblue', 'gold', - 'greenyellow', 'blueviolet']) - +try: + import matplotlib.pyplot as plt + import matplotlib.colors as mcolors + LABELS_CMAP = mcolors.ListedColormap(['white', 'red', 'dodgerblue', 'gold', + 'greenyellow', 'blueviolet']) +except ImportError: + pass from skimage.viewer.canvastools.base import CanvasToolBase diff --git a/skimage/viewer/canvastools/recttool.py b/skimage/viewer/canvastools/recttool.py index f055e439..929fe939 100644 --- a/skimage/viewer/canvastools/recttool.py +++ b/skimage/viewer/canvastools/recttool.py @@ -2,7 +2,6 @@ try: from matplotlib.widgets import RectangleSelector except ImportError: RectangleSelector = object - print("Could not import matplotlib -- skimage.viewer not available.") from skimage.viewer.canvastools.base import CanvasToolBase from skimage.viewer.canvastools.base import ToolHandles diff --git a/skimage/viewer/plugins/color_histogram.py b/skimage/viewer/plugins/color_histogram.py index dd54a93d..4826fc44 100644 --- a/skimage/viewer/plugins/color_histogram.py +++ b/skimage/viewer/plugins/color_histogram.py @@ -1,6 +1,8 @@ import numpy as np -import matplotlib.pyplot as plt - +try: + import matplotlib.pyplot as plt +except ImportError: + pass from skimage import color from skimage import exposure from .plotplugin import PlotPlugin diff --git a/skimage/viewer/plugins/overlayplugin.py b/skimage/viewer/plugins/overlayplugin.py index 373d6bf4..7f016328 100644 --- a/skimage/viewer/plugins/overlayplugin.py +++ b/skimage/viewer/plugins/overlayplugin.py @@ -5,17 +5,12 @@ from .base import Plugin from ..utils import ClearColormap, update_axes_image import six +from skimage._shared.version_requirements import is_installed __all__ = ['OverlayPlugin'] -def recent_mpl_version(): - import matplotlib - version = matplotlib.__version__.split('.') - return int(version[0]) == 1 and int(version[1]) >= 2 - - class OverlayPlugin(Plugin): """Plugin for ImageViewer that displays an overlay on top of main image. @@ -38,14 +33,14 @@ class OverlayPlugin(Plugin): 'cyan': (0, 1, 1)} def __init__(self, **kwargs): - if not recent_mpl_version(): + if not is_installed('matplotlib', '>=1.2'): msg = "Matplotlib >= 1.2 required for OverlayPlugin." warn(RuntimeWarning(msg)) super(OverlayPlugin, self).__init__(**kwargs) self._overlay_plot = None self._overlay = None self.cmap = None - self.color_names = list(self.colors.keys()) + self.color_names = sorted(list(self.colors.keys())) def attach(self, image_viewer): super(OverlayPlugin, self).attach(image_viewer) diff --git a/skimage/viewer/tests/test_plugins.py b/skimage/viewer/tests/test_plugins.py index 7ef62730..497708a4 100644 --- a/skimage/viewer/tests/test_plugins.py +++ b/skimage/viewer/tests/test_plugins.py @@ -4,20 +4,14 @@ import skimage import skimage.data as data from skimage.filter.rank import median from skimage.morphology import disk +from skimage.viewer import ImageViewer, viewer_available from numpy.testing import assert_equal, assert_allclose, assert_almost_equal from numpy.testing.decorators import skipif - -try: - from skimage.viewer.qt import qt_api - from skimage.viewer import ImageViewer - from skimage.viewer.plugins import ( - LineProfile, Measure, CannyPlugin, LabelPainter, Crop, ColorHistogram, - PlotPlugin) - from skimage.viewer.plugins.base import Plugin - from skimage.viewer.widgets import Slider - viewer_available = not qt_api is None -except ImportError: - viewer_available = False +from skimage.viewer.plugins import ( + LineProfile, Measure, CannyPlugin, LabelPainter, Crop, ColorHistogram, + PlotPlugin) +from skimage.viewer.plugins.base import Plugin +from skimage.viewer.widgets import Slider def setup_line_profile(image, limits='image'): diff --git a/skimage/viewer/tests/test_tools.py b/skimage/viewer/tests/test_tools.py index 9f02e762..6663c465 100644 --- a/skimage/viewer/tests/test_tools.py +++ b/skimage/viewer/tests/test_tools.py @@ -1,19 +1,15 @@ from collections import namedtuple import numpy as np -from skimage import data from numpy.testing import assert_equal from numpy.testing.decorators import skipif - -try: - from skimage.viewer.qt import qt_api - from skimage.viewer import ImageViewer - from skimage.viewer.canvastools import ( - LineTool, ThickLineTool, RectangleTool, PaintTool) - from skimage.viewer.canvastools.base import CanvasToolBase - viewer_available = not qt_api is None -except ImportError: - viewer_available = False +from skimage import data +from skimage.viewer import ImageViewer, viewer_available +from skimage.viewer.canvastools import ( + LineTool, ThickLineTool, RectangleTool, PaintTool) +from skimage.viewer.canvastools.base import CanvasToolBase +from numpy.testing import assert_equal +from numpy.testing.decorators import skipif def get_end_points(image): diff --git a/skimage/viewer/tests/test_utils.py b/skimage/viewer/tests/test_utils.py index 30d74a51..3f9ec396 100644 --- a/skimage/viewer/tests/test_utils.py +++ b/skimage/viewer/tests/test_utils.py @@ -1,15 +1,14 @@ # -*- coding: utf-8 -*- +from skimage.viewer import viewer_available +from skimage.viewer.qt import QtCore, QtGui +from skimage.viewer import utils +from skimage.viewer.utils import dialogs from numpy.testing.decorators import skipif - -try: - from skimage.viewer.qt import qt_api, QtCore, QtGui - from skimage.viewer import utils - from skimage.viewer.utils import dialogs -except ImportError: - qt_api = None +from skimage.viewer import utils +from skimage.viewer.utils import dialogs -@skipif(qt_api is None) +@skipif(not viewer_available) def test_event_loop(): utils.init_qtapp() timer = QtCore.QTimer() @@ -17,7 +16,7 @@ def test_event_loop(): utils.start_qtapp() -@skipif(qt_api is None) +@skipif(not viewer_available) def test_format_filename(): fname = dialogs._format_filename(('apple', 2)) assert fname == 'apple' @@ -25,7 +24,7 @@ def test_format_filename(): assert fname is None -@skipif(qt_api is None) +@skipif(not viewer_available) def test_open_file_dialog(): utils.init_qtapp() timer = QtCore.QTimer() @@ -34,7 +33,7 @@ def test_open_file_dialog(): assert filename is None -@skipif(qt_api is None) +@skipif(not viewer_available) def test_save_file_dialog(): utils.init_qtapp() timer = QtCore.QTimer() diff --git a/skimage/viewer/tests/test_viewer.py b/skimage/viewer/tests/test_viewer.py index cd6c7acb..1b93ff52 100644 --- a/skimage/viewer/tests/test_viewer.py +++ b/skimage/viewer/tests/test_viewer.py @@ -1,18 +1,13 @@ from skimage import data +from skimage.viewer.qt import QtGui, QtCore +from skimage.viewer import ImageViewer, CollectionViewer, viewer_available from skimage.transform import pyramid_gaussian +from skimage.viewer.plugins import OverlayPlugin from skimage.filter import sobel from numpy.testing import assert_equal from numpy.testing.decorators import skipif - -try: - from skimage.viewer.qt import qt_api, QtGui, QtCore - from skimage.viewer.plugins import OverlayPlugin - from skimage.viewer.plugins.overlayplugin import recent_mpl_version - from skimage.viewer import ImageViewer, CollectionViewer - viewer_available = not qt_api is None -except ImportError: - viewer_available = False +from skimage._shared.version_requirements import is_installed @skipif(not viewer_available) @@ -58,7 +53,8 @@ def test_collection_viewer(): view._format_coord(10, 10) -@skipif(not viewer_available or not recent_mpl_version()) +@skipif(not viewer_available) +@skipif(not is_installed('matplotlib', '>=1.2')) def test_viewer_with_overlay(): img = data.coins() ov = OverlayPlugin(image_filter=sobel) @@ -68,7 +64,7 @@ def test_viewer_with_overlay(): import tempfile _, filename = tempfile.mkstemp(suffix='.png') - ov.color = 2 + ov.color = 3 assert_equal(ov.color, 'yellow') viewer.save_to_file(filename) ov.display_filtered_image(img) @@ -79,5 +75,3 @@ def test_viewer_with_overlay(): assert_equal(ov.overlay, img) assert_equal(ov.filtered_image, img) - - diff --git a/skimage/viewer/tests/test_widgets.py b/skimage/viewer/tests/test_widgets.py index aeb4ede2..ef3cb83c 100644 --- a/skimage/viewer/tests/test_widgets.py +++ b/skimage/viewer/tests/test_widgets.py @@ -1,19 +1,14 @@ import os from skimage import data, img_as_float, io +from skimage.viewer import ImageViewer, viewer_available +from skimage.viewer.widgets import ( + Slider, OKCancelButtons, SaveButtons, ComboBox, Text) +from skimage.viewer.plugins.base import Plugin +from skimage.viewer.qt import QtGui, QtCore from numpy.testing import assert_almost_equal, assert_equal from numpy.testing.decorators import skipif -try: - from skimage.viewer.qt import qt_api, QtGui, QtCore - from skimage.viewer import ImageViewer - from skimage.viewer.widgets import ( - Slider, OKCancelButtons, SaveButtons, ComboBox, Text) - from skimage.viewer.plugins.base import Plugin - viewer_available = not qt_api is None -except ImportError: - viewer_available = False - def get_image_viewer(): image = data.coins() diff --git a/skimage/viewer/utils/core.py b/skimage/viewer/utils/core.py index 95176fcd..19a5c73d 100644 --- a/skimage/viewer/utils/core.py +++ b/skimage/viewer/utils/core.py @@ -9,18 +9,15 @@ try: from matplotlib.figure import Figure from matplotlib import _pylab_helpers from matplotlib.colors import LinearSegmentedColormap - if qt_api is None: - raise ImportError - else: + if not qt_api is None: from matplotlib.backends.backend_qt4 import FigureManagerQT from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg - if 'agg' not in mpl.get_backend().lower(): - print("Recommended matplotlib backend is `Agg` for full " - "skimage.viewer functionality.") + if 'agg' not in mpl.get_backend().lower(): + print("Recommended matplotlib backend is `Agg` for full " + "skimage.viewer functionality.") except ImportError: FigureCanvasQTAgg = object # hack to prevent nosetest and autodoc errors LinearSegmentedColormap = object - print("Could not import matplotlib -- skimage.viewer not available.") from ..qt import QtGui diff --git a/skimage/viewer/viewers/core.py b/skimage/viewer/viewers/core.py index 476017ef..52ac073a 100644 --- a/skimage/viewer/viewers/core.py +++ b/skimage/viewer/viewers/core.py @@ -123,7 +123,6 @@ class ImageViewer(QtGui.QMainWindow): self.fig, self.ax = utils.figimage(image) self.canvas = self.fig.canvas self.canvas.setParent(self) - self.ax.autoscale(enable=False) self._image_plot = self.ax.images[0] diff --git a/tools/build_versions.py b/tools/build_versions.py index 2c0e32ba..5e33dbfa 100755 --- a/tools/build_versions.py +++ b/tools/build_versions.py @@ -4,9 +4,12 @@ from __future__ import print_function import numpy as np import scipy as sp -import matplotlib as mpl +from PIL import Image import six -for m in (np, sp, mpl, six): - print(m.__name__.rjust(10), ' ', m.__version__) - +for m in (np, sp, Image, six): + if not m is None: + if m is Image: + print('PIL'.rjust(10), ' ', Image.VERSION) + else: + print(m.__name__.rjust(10), ' ', m.__version__)