diff --git a/.travis.yml b/.travis.yml index 1f292dfa..1ef5b85d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,28 +3,17 @@ # After changing this file, check it on: # http://lint.travis-ci.org/ - language: python python: - - 2.6 + - 3.2 -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 +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 +21,92 @@ 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 - + - 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; + 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 + 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 + - if [[ $ENV != python=2.6* ]]; then + for f in doc/examples/*.py; do python "$f"; if [ $? -ne 0 ]; then exit 1; fi done + fi + - 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 + # doctests fail on python 2.6 for restoration/unwrap + - if [[ $ENV == python=3.3* ]]; then + nosetests --exe -v --with-doctest --with-cov --cover-package skimage; + elif [[ $ENV == python=2.6* ]]; then + nosetests --exe -v 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/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..443b1561 100644 --- a/skimage/graph/graph_cut.py +++ b/skimage/graph/graph_cut.py @@ -1,4 +1,9 @@ -import networkx as nx +try: + import networkx as nx +except ImportError: + import warnings + warnings.warn('"cut_threshold" requires networkx') + nx = None import numpy as np diff --git a/skimage/graph/rag.py b/skimage/graph/rag.py index 84721736..fc4a1aaa 100644 --- a/skimage/graph/rag.py +++ b/skimage/graph/rag.py @@ -1,4 +1,11 @@ -import networkx as nx +try: + import networkx as nx +except ImportError: + class nx: + class Graph: + pass + import warnings + warnings.warn('Region Adjacency Graph (RAG) features require networkx') 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..b6f95cb2 100644 --- a/skimage/graph/tests/test_rag.py +++ b/skimage/graph/tests/test_rag.py @@ -1,5 +1,6 @@ import numpy as np from skimage import graph +from skimage.util.version_requirements import require def max_edge(g, src, dst, n): @@ -9,6 +10,7 @@ def max_edge(g, src, dst, n): return max(w1, w2) +@require('networkx') def test_rag_merge(): g = graph.rag.RAG() @@ -43,6 +45,7 @@ def test_rag_merge(): assert g.edges() == [] +@require('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/tests/test_unwrap.py b/skimage/restoration/tests/test_unwrap.py index fca63724..2c2381e3 100644 --- a/skimage/restoration/tests/test_unwrap.py +++ b/skimage/restoration/tests/test_unwrap.py @@ -7,6 +7,7 @@ from numpy.testing import (run_module_suite, assert_array_almost_equal, import warnings from skimage.restoration import unwrap_phase +from skimage.util.version_requirements import require def assert_phase_almost_equal(a, b, *args, **kwargs): @@ -41,6 +42,7 @@ def check_unwrap(image, mask=None): assert_phase_almost_equal(image_unwrapped, image) +@require('python', '>=2.7') def test_unwrap_1d(): image = np.linspace(0, 10 * np.pi, 100) check_unwrap(image) @@ -50,6 +52,7 @@ def test_unwrap_1d(): assert_raises(ValueError, unwrap_phase, image, True) +@require('python', '>=2.7') def test_unwrap_2d(): x, y = np.ogrid[:8, :16] image = 2 * np.pi * (x * 0.2 + y * 0.1) @@ -59,6 +62,7 @@ def test_unwrap_2d(): yield check_unwrap, image, mask +@require('python', '>=2.7') def test_unwrap_3d(): x, y, z = np.ogrid[:8, :12, :16] image = 2 * np.pi * (x * 0.2 + y * 0.1 + z * 0.05) @@ -104,12 +108,14 @@ def check_wrap_around(ndim, axis): image_unwrap_wrap_around[index_last]) +@require('python', '>=2.7') def test_wrap_around(): for ndim in (2, 3): for axis in range(ndim): yield check_wrap_around, ndim, axis +@require('python', '>=2.7') def test_mask(): length = 100 ramps = [np.linspace(0, 4 * np.pi, length), @@ -137,6 +143,7 @@ def test_mask(): assert_array_almost_equal(image_unwrapped_3d[:, :, -1], image[i, -1]) +@require('python', '>=2.7') def test_invalid_input(): assert_raises(ValueError, unwrap_phase, np.zeros([])) assert_raises(ValueError, unwrap_phase, np.zeros((1, 1, 1, 1))) diff --git a/skimage/restoration/unwrap.py b/skimage/restoration/unwrap.py index 085bef0b..14da2677 100644 --- a/skimage/restoration/unwrap.py +++ b/skimage/restoration/unwrap.py @@ -1,12 +1,14 @@ import numpy as np import warnings from six import string_types +from skimage.util.version_requirements import require from ._unwrap_1d import unwrap_1d from ._unwrap_2d import unwrap_2d from ._unwrap_3d import unwrap_3d +@require('python', '>=2.7') def unwrap_phase(image, wrap_around=False): '''From ``image``, wrapped to lie in the interval [-pi, pi), recover the original, unwrapped image. diff --git a/skimage/util/tests/test_version_requirements.py b/skimage/util/tests/test_version_requirements.py new file mode 100644 index 00000000..765baaa8 --- /dev/null +++ b/skimage/util/tests/test_version_requirements.py @@ -0,0 +1,46 @@ +"""Tests for the version requirment functions. + +""" +import numpy as np +from numpy.testing import assert_raises, assert_equal +import nose +from skimage.util import version_requirements as vr + + +def test_get_module_version(): + assert vr.get_module_version('numpy') + assert vr.get_module_version('scipy') + assert_raises(ImportError, lambda: vr.get_module_version('fakenumpy')) + + +def test_is_installed(): + assert vr.is_installed('python', '>=2.6') + assert not vr.is_installed('numpy', '<1.0') + + +def test_require(): + + @vr.require('python', '>2.6') + @vr.require('numpy', '>1.5') + def foo(): + return 1 + + assert_equal(foo(), 1) + + @vr.require('scipy', '<0.1') + def bar(): + return 0 + + assert_raises(ImportError, lambda: bar()) + + @vr.require('numpy', '<1.0') + def test_this(): + assert False + + assert_raises(nose.SkipTest, lambda: test_this()()) + + +def test_get_module(): + assert_equal(vr.get_module('numpy'), np) + assert_equal(vr.get_module('nose'), nose) + diff --git a/skimage/util/version_requirements.py b/skimage/util/version_requirements.py new file mode 100644 index 00000000..2858aa5d --- /dev/null +++ b/skimage/util/version_requirements.py @@ -0,0 +1,125 @@ +from distutils.version import LooseVersion +import functools +import re +import sys +import types +from numpy.testing.decorators import skipif + + +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 + + Note + ---- + Original Copyright (C) 2009-2011 Pierre Raybaut + Licensed under the terms of the MIT License. + + Returns + ------- + True if *name* is installed matching the optional version. + """ + 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 + """ + 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 + '"') + if (isinstance(obj, types.FunctionType) and + obj.__name__.startswith('test_')): + if not is_installed(name, version): + func_wrapped = lambda: skipif(True)(obj) + else: + func_wrapped = obj + return func_wrapped + return decorator + + +def get_module(module_name, version=None): + if not is_installed(module_name, version): + return None + return __import__(module_name, + fromlist=[module_name.rpartition('.')[-1]]) diff --git a/skimage/viewer/__init__.py b/skimage/viewer/__init__.py index 5eed9589..a146f348 100644 --- a/skimage/viewer/__init__.py +++ b/skimage/viewer/__init__.py @@ -1 +1,11 @@ +from warnings import warn +from skimage.util.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..1850992d 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.util.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..4708db6e 100644 --- a/skimage/viewer/tests/test_plugins.py +++ b/skimage/viewer/tests/test_plugins.py @@ -4,21 +4,15 @@ 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 +from skimage.util.version_requirements import is_installed def setup_line_profile(image, limits='image'): viewer = ImageViewer(skimage.img_as_float(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..85a0d76f 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.util.version_requirements import require @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) +@require('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__)