Merge tag 'v0.11.3' into releases

Bugfix release (addresses pip package dependency issues)

* tag 'v0.11.3': (42 commits)
  Set version to 0.11.3
  Do not read in the whole array
  Fix handling of non-gif and tiff files
  Only index tif and gif files in collection
  Another random warning guard
  Fix setup to allow installing from PyPI ≈ Fix setup to allow installing from PyPi Another attempt at fixing the setuptools problem
  Fix for broken setuptools
  close conda install code string
  Fix freeimage collection test
  Do not attempt to use _frame_index for exotic load_patterns
  More specific test in freeimage
  Always use _find_images()
  Remove debug error raise
  Add a note to the docstring
  Fix handling of ImageCollection indexing and add test
  TST: Barn-door testing of SaveButtons clicks
  add some more magic
  install sphinx before running tests in travis
  install_requires from requirements.txt
  Update See Also to References
  ...
This commit is contained in:
Yaroslav Halchenko
2015-04-02 20:29:50 -04:00
36 changed files with 358 additions and 220 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ python:
- 3.4
before_install:
- sudo apt-get update
- sudo apt-get update; true
- source tools/travis_before_install.sh
- which python; python --version
+27 -26
View File
@@ -19,8 +19,8 @@ Here's the long and short of it:
* Now, you have remote repositories named:
- ``upstream``, which refers to the ``scikit-image`` repository
- ``origin``, which refers to your personal fork
- ``upstream``, which refers to the ``scikit-image`` repository
- ``origin``, which refers to your personal fork
2. Develop your contribution:
@@ -56,32 +56,32 @@ For a more detailed discussion, read these :doc:`detailed documents
4. Review process:
* Reviewers (the other developers and interested community members) will
write inline and/or general comments on your Pull Request (PR) to help
you improve its implementation, documentation and style. Every single
developer working on the project has their code reviewed, and we've come
to see it as friendly conversation from which we all learn and the
overall code quality benefits. Therefore, please don't let the review
discourage you from contributing: its only aim is to improve the quality
of project, not to criticize (we are, after all, very grateful for the
time you're donating!).
* Reviewers (the other developers and interested community members) will
write inline and/or general comments on your Pull Request (PR) to help
you improve its implementation, documentation and style. Every single
developer working on the project has their code reviewed, and we've come
to see it as friendly conversation from which we all learn and the
overall code quality benefits. Therefore, please don't let the review
discourage you from contributing: its only aim is to improve the quality
of project, not to criticize (we are, after all, very grateful for the
time you're donating!).
* To update your pull request, make your changes on your local repository
and commit. As soon as those changes are pushed up (to the same branch as
before) the pull request will update automatically.
* To update your pull request, make your changes on your local repository
and commit. As soon as those changes are pushed up (to the same branch as
before) the pull request will update automatically.
* `Travis-CI <http://travis-ci.org/>`__, a continuous integration service,
is triggered after each Pull Request update to build the code, run unit
tests, measure code coverage and check coding style (PEP8) of your
branch. The Travis tests must pass before your PR can be merged. If
Travis fails, you can find out why by clicking on the "failed" icon (red
cross) and inspecting the build and test log.
* `Travis-CI <http://travis-ci.org/>`__, a continuous integration service,
is triggered after each Pull Request update to build the code, run unit
tests, measure code coverage and check coding style (PEP8) of your
branch. The Travis tests must pass before your PR can be merged. If
Travis fails, you can find out why by clicking on the "failed" icon (red
cross) and inspecting the build and test log.
5. Document changes
Before merging your commits, you must add a description of your changes
to the release notes of the upcoming version in
``doc/release/release_dev.txt``.
Before merging your commits, you must add a description of your changes
to the release notes of the upcoming version in
``doc/release/release_dev.txt``.
.. note::
@@ -191,10 +191,11 @@ Travis-CI checks all unittests in the project to prevent breakage.
Before sending a pull request, you may want to check that Travis-CI
successfully passes all tests. To do so,
* Go to `Travis-CI <http://travis-ci.org/>`__ and follow the Sign In link at the top
* Go to `Travis-CI <http://travis-ci.org/>`__ and follow the Sign In link at
the top
* Go to your `profile page <https://travis-ci.org/profile>`__ and switch
on your scikit-image fork
* Go to your `profile page <https://travis-ci.org/profile>`__ and switch on
your scikit-image fork
It corresponds to steps one and two in
`Travis-CI documentation <http://about.travis-ci.org/docs/user/getting-started/>`__
+2 -2
View File
@@ -45,11 +45,11 @@ functionality is only available with the following installed:
* `Astropy <http://www.astropy.org>`__ provides FITS io capability.
* `SimpleITK <http://www.simpleitk.org/>`
* `SimpleITK <http://www.simpleitk.org/>`__
Optional io plugin providing a wide variety of `formats <http://www.itk.org/Wiki/ITK_File_Formats>`__.
including specialized formats using in medical imaging.
* `imread <http://pythonhosted.org/imread/>`
* `imread <http://pythonhosted.org/imread/>`__
Optional io plugin providing most standard `formats <http://pythonhosted.org//imread/formats.html>`__.
+1 -1
View File
@@ -11,7 +11,7 @@
- **Debian/Ubuntu:** ``sudo apt-get install python-skimage``
- **OSX:** ``pip install scikit-image``
- **Anaconda:** ``conda install scikit-image
- **Anaconda:** ``conda install scikit-image``
- **Windows:** Download [Windows binaries](http://www.lfd.uci.edu/~gohlke/pythonlibs/#scikits.image)
Also see
+5 -2
View File
@@ -15,7 +15,7 @@ How to make a new release of ``skimage``
3. Copy ``doc/release/release_template.txt`` to
``doc/release/release_dev.txt`` for the next release.
- Update the version number in ``setup.py`` and ``bento.info`` and commit
- Update the version number in ``skimage/__init__.py`` and ``bento.info`` and commit
- Update the docs:
@@ -31,7 +31,10 @@ How to make a new release of ``skimage``
- Add the version number as a tag in git::
git tag v0.X.0
git tag -s v0.X.0
(If you do not have a gpg key, use -m instead; it is important for
Debian packaging that the tags are annotated)
- Push the new meta-data to github::
+10 -9
View File
@@ -13,17 +13,18 @@ environment:
PYTHON_VERSION: "2.7"
PYTHON_ARCH: "32"
- PYTHON: "C:\\Python27_64"
PYTHON_VERSION: "2.7"
PYTHON_ARCH: "64"
# disable other builds until they can be run in parallel
#- PYTHON: "C:\\Python27_64"
# PYTHON_VERSION: "2.7"
# PYTHON_ARCH: "64"
- PYTHON: "C:\\Python34_32"
PYTHON_VERSION: "3.4.2"
PYTHON_ARCH: "32"
#- PYTHON: "C:\\Python34_32"
# PYTHON_VERSION: "3.4.2"
# PYTHON_ARCH: "32"
- PYTHON: "C:\\Python24_64"
PYTHON_VERSION: "3.4.2"
PYTHON_ARCH: "64"
#- PYTHON: "C:\\Python24_64"
# PYTHON_VERSION: "3.4.2"
# PYTHON_ARCH: "64"
install:
# Install Python (from the official .msi of http://python.org) and pip when
+1 -1
View File
@@ -1,5 +1,5 @@
Name: scikit-image
Version: 0.11.0
Version: 0.11.3
Summary: Image processing routines for SciPy
Url: http://scikit-image.org
DownloadUrl: http://github.com/scikit-image/scikit-image
+20 -2
View File
@@ -2,13 +2,31 @@
To build docs, run `make` in this directory. `make help` lists all targets.
## Requirements ##
Sphinx is needed to build doc. Install with `pip install sphinx`.
Sphinx and Latex is needed to build doc.
**Spinx:**
```sh
pip install sphinx
```
**Latex Ubuntu:**
```sh
sudo apt-get install -qq texlive texlive-latex-extra dvipng
```
**Latex Mac:**
Install the full [MacTex](http://www.tug.org/mactex/) installation or install the smaller [BasicTex](http://www.tug.org/mactex/morepackages.html) and add *ucs* and *dvipng* packages:
```sh
sudo tlmgr install ucs dvipng
```
## Fixing Warnings ##
- "citation not found: R###"
$ cd doc/build; grep -rin R### .
There is probably an underscore after a reference
There is probably an underscore after a reference
in the first line of a docstring (e.g. [1]_)
- "Duplicate citation R###, other instance in...""
+9 -3
View File
@@ -186,6 +186,11 @@ def setup(app):
def generate_example_galleries(app):
cfg = app.builder.config
if isinstance(cfg.source_suffix, list):
cfg.source_suffix_str = cfg.source_suffix[0]
else:
cfg.source_suffix_str = cfg.source_suffix
doc_src = Path(os.path.abspath(app.builder.srcdir)) # path/to/doc/source
if isinstance(cfg.plot2rst_paths, tuple):
@@ -205,7 +210,7 @@ def generate_examples_and_gallery(example_dir, rst_dir, cfg):
rst_dir.makedirs()
# we create an index.rst with all examples
gallery_index = open(rst_dir.pjoin('index'+cfg.source_suffix), 'w')
gallery_index = open(rst_dir.pjoin('index'+cfg.source_suffix_str), 'w')
# Here we don't use an os.walk, but we recurse only twice: flat is
# better than nested.
@@ -235,7 +240,7 @@ def write_gallery(gallery_index, src_dir, rst_dir, cfg, depth=0):
cfg : config object
Sphinx config object created by Sphinx.
"""
index_name = cfg.plot2rst_index_name + cfg.source_suffix
index_name = cfg.plot2rst_index_name + cfg.source_suffix_str
gallery_template = src_dir.pjoin(index_name)
if not os.path.exists(gallery_template):
print(src_dir)
@@ -328,7 +333,8 @@ def write_example(src_name, src_dir, rst_dir, cfg):
image_path = image_dir.pjoin(base_image_name + '_{0}.png')
basename, py_ext = os.path.splitext(src_name)
rst_path = rst_dir.pjoin(basename + cfg.source_suffix)
rst_path = rst_dir.pjoin(basename + cfg.source_suffix_str)
notebook_path = notebook_dir.pjoin(basename + '.ipynb')
if _plots_are_current(src_path, image_path) and rst_path.exists and \
+2 -2
View File
@@ -68,10 +68,10 @@ copyright = '2013, the scikit-image team'
#
# The short X.Y version.
setup_lines = open('../../setup.py').readlines()
setup_lines = open('../../skimage/__init__.py').readlines()
version = 'vUndefined'
for l in setup_lines:
if l.startswith('VERSION'):
if l.startswith('__version__'):
version = l.split("'")[1]
break
+3 -3
View File
@@ -37,10 +37,10 @@ if __name__ == '__main__':
installed_version = V(module.__version__)
setup_lines = open('../setup.py').readlines()
source_lines = open('../skimage/__init__.py').readlines()
version = 'vUndefined'
for l in setup_lines:
if l.startswith('VERSION'):
for l in source_lines:
if l.startswith('__version__'):
source_version = V(l.split("'")[1])
break
Executable → Regular
+33 -82
View File
@@ -17,28 +17,40 @@ MAINTAINER_EMAIL = 'stefan@sun.ac.za'
URL = 'http://scikit-image.org'
LICENSE = 'Modified BSD'
DOWNLOAD_URL = 'http://github.com/scikit-image/scikit-image'
VERSION = '0.11.0'
PYTHON_VERSION = (2, 6)
import os
import sys
import setuptools
from distutils.command.build_py import build_py
from distutils.version import LooseVersion
# These are manually checked.
# These packages are sometimes installed outside of the setuptools scope
DEPENDENCIES = {}
with open('requirements.txt', 'rb') as fid:
data = fid.read().decode('utf-8', 'replace')
for line in data.splitlines():
pkg, _, version_info = line.replace('==', '>=').partition('>=')
# Only require Cython if we have a developer checkout
if pkg.lower() == 'cython' and not VERSION.endswith('dev'):
continue
DEPENDENCIES[str(pkg).lower()] = str(version_info)
with open('skimage/__init__.py') as fid:
for line in fid:
if line.startswith('__version__'):
VERSION = line.strip().split()[-1][1:-1]
break
with open('requirements.txt') as fid:
INSTALL_REQUIRES = [l.strip() for l in fid.readlines() if l]
# development versions do not have the cythonized files
if VERSION.endswith('dev'):
SETUP_REQUIRES = [r for r in INSTALL_REQUIRES if r.startswith('cython')]
else:
INSTALL_REQUIRES = [r for r in INSTALL_REQUIRES
if not r.startswith('cython')]
SETUP_REQUIRES = []
# list requirements for PyPI
REQUIRES = [r.replace('>=', ' (>= ') + ')'
for r in INSTALL_REQUIRES + SETUP_REQUIRES]
REQUIRES = [r.replace('==', ' (== ') for r in REQUIRES]
# do not attempt to install numpy and scipy until they have eggs available
INSTALL_REQUIRES = [r for r in INSTALL_REQUIRES
if not r.startswith(('scipy', 'numpy'))]
def configuration(parent_package='', top_path=None):
@@ -59,72 +71,11 @@ def configuration(parent_package='', top_path=None):
return config
def write_version_py(filename='skimage/version.py'):
template = """# THIS FILE IS GENERATED FROM THE SKIMAGE SETUP.PY
version='%s'
"""
try:
vfile = open(os.path.join(os.path.dirname(__file__),
filename), 'w')
vfile.write(template % VERSION)
except IOError:
raise IOError("Could not open/write to skimage/version.py - did you "
"install using sudo in the past? If so, run\n"
"sudo chown -R your_username ./*\n"
"from package root to fix permissions, and try again.")
finally:
vfile.close()
def get_package_version(package):
for version_attr in ('__version__', 'VERSION', 'version'):
version_info = getattr(package, version_attr, None)
if version_info and str(version_attr) == version_attr:
return str(version_info)
def check_requirements():
if sys.version_info < PYTHON_VERSION:
raise SystemExit('You need Python version %d.%d or later.' \
% PYTHON_VERSION)
for (package_name, min_version) in DEPENDENCIES.items():
if package_name == 'cython':
package_name = 'Cython'
dep_error = ''
if package_name.lower() == 'pillow':
package_name = 'PIL.Image'
min_version = '1.1.7'
try:
package = __import__(package_name,
fromlist=[package_name.rpartition('.')[0]])
except ImportError:
dep_error = ('You need `%s` version %s or later.'
% (package_name, min_version))
else:
if package_name == 'PIL':
package_version = package.PILLOW_VERSION
else:
package_version = get_package_version(package)
if LooseVersion(min_version) > LooseVersion(package_version):
dep_error = ('You need `%s` version %s or later,'
'found version %s.'
% (package_name, min_version,
package_version))
if dep_error:
raise ImportError(dep_error)
if __name__ == "__main__":
check_requirements()
write_version_py()
# purposely fail loudly if numpy or scipy are not available
from numpy.distutils.core import setup
import scipy
setup(
name=DISTNAME,
description=DESCRIPTION,
@@ -153,9 +104,9 @@ if __name__ == "__main__":
],
configuration=configuration,
install_requires=[
"six>=%s" % DEPENDENCIES['six']
],
setup_requires=SETUP_REQUIRES,
install_requires=INSTALL_REQUIRES,
requires=REQUIRES,
packages=setuptools.find_packages(exclude=['doc']),
include_package_data=True,
zip_safe=False, # the package can run out of an .egg file
+1 -7
View File
@@ -64,13 +64,7 @@ import warnings as _warnings
pkg_dir = _osp.abspath(_osp.dirname(__file__))
data_dir = _osp.join(pkg_dir, 'data')
try:
from .version import version as __version__
except ImportError:
__version__ = "unbuilt-dev"
else:
del version
__version__ = '0.11.3'
try:
_imp.find_module('nose')
+3 -3
View File
@@ -161,7 +161,7 @@ def mono_check(plugin, fmt='png'):
else:
testing.assert_allclose(r3, img_as_uint(img))
with expected_warnings(['precision loss']):
with expected_warnings(['precision loss|unclosed file']):
img4 = img_as_int(img)
if fmt.lower() in (('tif', 'tiff')):
img4 -= 100
@@ -182,7 +182,7 @@ def setup_test():
"""Default package level setup routine for skimage tests.
Import packages known to raise errors, and then
force warnings to raise errors.
force warnings to raise errors.
Set a random seed
"""
warnings.simplefilter('default')
@@ -190,7 +190,7 @@ def setup_test():
from scipy.io import loadmat
from skimage import viewer, filter
np.random.seed(0)
warnings.simplefilter('error')
warnings.simplefilter('error')
def teardown_test():
+4 -2
View File
@@ -1,6 +1,7 @@
from .exposure import histogram, equalize_hist, \
rescale_intensity, cumulative_distribution, \
adjust_gamma, adjust_sigmoid, adjust_log
adjust_gamma, adjust_sigmoid, adjust_log, \
is_low_contrast
from ._adapthist import equalize_adapthist
@@ -12,4 +13,5 @@ __all__ = ['histogram',
'cumulative_distribution',
'adjust_gamma',
'adjust_sigmoid',
'adjust_log']
'adjust_log',
'is_low_contrast']
+53 -1
View File
@@ -1,7 +1,8 @@
from __future__ import division
import warnings
import numpy as np
from .. import img_as_float
from ..color import rgb2gray
from ..util.dtype import dtype_range, dtype_limits
@@ -463,3 +464,54 @@ def adjust_sigmoid(image, cutoff=0.5, gain=10, inv=False):
out = (1 / (1 + np.exp(gain * (cutoff - image / scale)))) * scale
return dtype(out)
def is_low_contrast(image, fraction_threshold=0.05, lower_percentile=1,
upper_percentile=99, method='linear'):
"""Detemine if an image is low contrast.
Parameters
----------
image : array-like
The image under test.
fraction_threshold : float, optional
The low contrast fraction threshold. An image is considered low-
contrast when its range of brightness spans less than this
fraction of its data type's full range. [1]_
lower_bound : float, optional
Disregard values below this percentile when computing image contrast.
upper_bound : float, optional
Disregard values above this percentile when computing image contrast.
method : str, optional
The contrast determination method. Right now the only available
option is "linear".
Returns
-------
out : bool
True when the image is determined to be low contrast.
References
----------
.. [1] http://scikit-image.org/docs/dev/user_guide/data_types.html
Examples
--------
>>> image = np.linspace(0, 0.04, 100)
>>> is_low_contrast(image)
True
>>> image[-1] = 1
>>> is_low_contrast(image)
True
>>> is_low_contrast(image, upper_percentile=100)
False
"""
image = np.asanyarray(image)
if image.ndim == 3 and image.shape[2] in [3, 4]:
image = rgb2gray(image)
dlimits = dtype_limits(image)
limits = np.percentile(image, [lower_percentile, upper_percentile])
ratio = (limits[1] - limits[0]) / (dlimits[1] - dlimits[0])
return ratio < fraction_threshold
+16
View File
@@ -472,6 +472,22 @@ def test_negative():
assert_raises(ValueError, exposure.adjust_gamma, image)
def test_is_low_contrast():
image = np.linspace(0, 0.04, 100)
assert exposure.is_low_contrast(image)
image[-1] = 1
assert exposure.is_low_contrast(image)
assert not exposure.is_low_contrast(image, upper_percentile=100)
image = (image * 255).astype(np.uint8)
assert exposure.is_low_contrast(image)
assert not exposure.is_low_contrast(image, upper_percentile=100)
image = (image.astype(np.uint16)) * 2**8
assert exposure.is_low_contrast(image)
assert not exposure.is_low_contrast(image, upper_percentile=100)
if __name__ == '__main__':
from numpy import testing
testing.run_module_suite()
+5
View File
@@ -1,4 +1,5 @@
from io import BytesIO
import warnings
import numpy as np
import six
@@ -6,6 +7,8 @@ import six
from ..io.manage_plugins import call_plugin
from ..color import rgb2grey
from .util import file_or_url_context
from ..exposure import is_low_contrast
from .._shared._warnings import all_warnings
__all__ = ['Image', 'imread', 'imread_collection', 'imsave', 'imshow', 'show']
@@ -152,6 +155,8 @@ def imsave(fname, arr, plugin=None, **plugin_args):
Passed to the given plugin.
"""
if is_low_contrast(arr):
warnings.warn('%s is a low contrast image' % fname)
return call_plugin('imsave', fname, arr, plugin=plugin, **plugin_args)
+1 -1
View File
@@ -1,4 +1,4 @@
from util import prepare_for_display, window_manager, GuiLockError
from .util import prepare_for_display, window_manager, GuiLockError
try:
# we try to aquire the gui lock first
+6 -4
View File
@@ -2,7 +2,9 @@ from collections import namedtuple
import numpy as np
import warnings
import matplotlib.pyplot as plt
from skimage.util import dtype as dtypes
from ...util import dtype as dtypes
from ...exposure import is_low_contrast
from ..._shared._warnings import all_warnings
_default_colormap = 'gray'
@@ -49,7 +51,7 @@ def _get_image_properties(image):
out_of_range_float = (np.issubdtype(image.dtype, np.float) and
(immin < lo or immax > hi))
low_dynamic_range = (immin != immax and
(float(immax - immin) / (hi - lo)) < (1. / 255))
is_low_contrast(image))
unsupported_dtype = image.dtype not in dtypes._supported_types
return ImageProperties(signed, out_of_range_float,
@@ -72,8 +74,8 @@ def _raise_warnings(image_properties):
warnings.warn("Low image dynamic range; displaying image with "
"stretched contrast.")
if ip.out_of_range_float:
warnings.warn("Float image out of standard range; displaying image "
"with stretched contrast.")
warnings.warn("Float image out of standard range; displaying "
"image with stretched contrast.")
def _get_display_range(image):
+1 -1
View File
@@ -2,7 +2,7 @@
from PyQt4 import QtGui, QtCore
from PyQt4.QtGui import (QWidget, QStackedWidget, QSlider, QGridLayout, QLabel)
from util import ColorMixer
from .util import ColorMixer
class IntelligentSlider(QWidget):
+1 -1
View File
@@ -2,7 +2,7 @@ import numpy as np
from PyQt4.QtGui import QWidget, QPainter, QGridLayout, QColor, QFrame
from util import histograms
from .util import histograms
class ColorHistogram(QWidget):
+23 -23
View File
@@ -135,7 +135,8 @@ class ImageCollection(object):
ic = ImageCollection('/tmp/*.png', load_func=imread_convert)
For files with multiple images, the images will be flattened into a list
and added to the list of available images.
and added to the list of available images. In this case, ``load_func``
should accept the keyword argument ``img_num``.
Examples
--------
@@ -165,7 +166,7 @@ class ImageCollection(object):
self._numframes = self._find_images()
else:
self._files = load_pattern
self._numframes = len(load_pattern)
self._numframes = len(self._files)
self._frame_index = None
if conserve_memory:
@@ -201,26 +202,22 @@ class ImageCollection(object):
img = TiffFile(fname)
index += [(fname, i) for i in range(len(img.pages))]
else:
im = Image.open(fname)
try:
# this will raise an IOError if the file is not readable
im.getdata()[0]
except IOError:
site = "http://pillow.readthedocs.org/en/latest/installation.html#external-libraries"
raise ValueError(
'Could not load "%s"\nPlease see documentation at: %s' % (fname, site))
else:
i = 0
while True:
try:
im.seek(i)
except EOFError:
break
index.append((fname, i))
i += 1
im = Image.open(fname)
im.seek(0)
except (IOError, OSError):
index.append([fname, i])
continue
i = 0
while True:
try:
im.seek(i)
except EOFError:
break
index.append((fname, i))
i += 1
if hasattr(im, 'fp') and im.fp:
im.fp.close()
self._frame_index = index
return len(index)
@@ -254,13 +251,16 @@ class ImageCollection(object):
if ((self.conserve_memory and n != self._cached) or
(self.data[idx] is None)):
kwargs = self.load_func_kwargs
if self._frame_index:
fname, img_num = self._frame_index[n]
self.data[idx] = self.load_func(fname, img_num=img_num,
**self.load_func_kwargs)
if img_num > 0:
self.data[idx] = self.load_func(fname, img_num=img_num,
**kwargs)
else:
self.data[idx] = self.load_func(fname, **kwargs)
else:
self.data[idx] = self.load_func(self.files[n],
**self.load_func_kwargs)
self.data[idx] = self.load_func(self.files[n], **kwargs)
self._cached = n
return self.data[idx]
+13
View File
@@ -1,6 +1,7 @@
import os
import skimage as si
import skimage.io as sio
from skimage import data_dir
import numpy as np
from numpy.testing import *
@@ -112,5 +113,17 @@ def test_metadata():
assert meta[1][('EXIF_MAIN', 'Software')].startswith('I')
@skipif(not FI_available)
def test_collection():
pattern = [os.path.join(data_dir, pic)
for pic in ['camera.png', 'color.png', 'multipage.tif']]
images = sio.ImageCollection(pattern[:-1])
assert len(images) == 2
assert len(images[:]) == 2
images = sio.ImageCollection(pattern)
assert len(images) == 3
assert len(images[:]) == 3
if __name__ == "__main__":
run_module_suite()
+2 -1
View File
@@ -87,7 +87,8 @@ def test_outside_standard_range():
def test_nonstandard_type():
plt.figure()
with expected_warnings(["Non-standard image type"]):
with expected_warnings(["Non-standard image type",
"Low image dynamic range"]):
ax_im = io.imshow(im64)
assert ax_im.get_clim() == (im64.min(), im64.max())
assert n_subplots(ax_im) == 2
+2 -1
View File
@@ -147,7 +147,8 @@ def test_imsave_filelike():
s = BytesIO()
# save to file-like object
with expected_warnings(['precision loss|unclosed file']):
with expected_warnings(['precision loss|unclosed file',
'is a low contrast image']):
imsave(s, image)
# read from file-like object
+1 -1
View File
@@ -9,7 +9,7 @@ from ._moments import moments, moments_central, moments_normalized, moments_hu
from .profile import profile_line
from .fit import LineModel, CircleModel, EllipseModel, ransac
from .block import block_reduce
from ._ccomp import label
from ._label import label
__all__ = ['find_contours',
+49 -14
View File
@@ -4,9 +4,6 @@ import numpy as np
from scipy import optimize
_EPSILON = np.spacing(1)
def _check_data_dim(data, dim):
if data.ndim != 2 or data.shape[1] != dim:
raise ValueError('Input data must have shape (N, %d).' % dim)
@@ -57,6 +54,11 @@ class LineModel(BaseModel):
data : (N, 2) array
N points with ``(x, y)`` coordinates, respectively.
Returns
-------
success : bool
True, if model estimation succeeds.
"""
_check_data_dim(data, dim=2)
@@ -81,6 +83,8 @@ class LineModel(BaseModel):
self.params = (dist, theta)
return True
def residuals(self, data):
"""Determine residuals of data to model.
@@ -182,6 +186,11 @@ class CircleModel(BaseModel):
data : (N, 2) array
N points with ``(x, y)`` coordinates, respectively.
Returns
-------
success : bool
True, if model estimation succeeds.
"""
_check_data_dim(data, dim=2)
@@ -217,6 +226,8 @@ class CircleModel(BaseModel):
self.params = params
return True
def residuals(self, data):
"""Determine residuals of data to model.
@@ -313,6 +324,11 @@ class EllipseModel(BaseModel):
data : (N, 2) array
N points with ``(x, y)`` coordinates, respectively.
Returns
-------
success : bool
True, if model estimation succeeds.
"""
_check_data_dim(data, dim=2)
@@ -373,6 +389,8 @@ class EllipseModel(BaseModel):
self.params = params[:5]
return True
def residuals(self, data):
"""Determine residuals of data to model.
@@ -471,7 +489,6 @@ class EllipseModel(BaseModel):
def _dynamic_max_trials(n_inliers, n_samples, min_samples, probability):
"""Determine number trials such that at least one outlier-free subset is
sampled for the given inlier/outlier ratio.
Parameters
----------
n_inliers : int
@@ -482,21 +499,31 @@ def _dynamic_max_trials(n_inliers, n_samples, min_samples, probability):
Minimum number of samples chosen randomly from original data.
probability : float
Probability (confidence) that one outlier-free sample is generated.
Returns
-------
trials : int
Number of trials.
"""
if n_inliers == 0:
return np.inf
nom = 1 - probability
if nom == 0:
return np.inf
inlier_ratio = n_inliers / float(n_samples)
nom = max(_EPSILON, 1 - probability)
denom = max(_EPSILON, 1 - inlier_ratio ** min_samples)
if nom == 1:
denom = 1 - inlier_ratio ** min_samples
if denom == 0:
return 1
elif denom == 1:
return np.inf
nom = np.log(nom)
denom = np.log(denom)
if denom == 0:
return 0
if denom == 1:
return float('inf')
return abs(float(np.ceil(np.log(nom) / np.log(denom))))
return int(np.ceil(nom / denom))
def ransac(data, model_class, min_samples, residual_threshold,
@@ -542,9 +569,11 @@ def ransac(data, model_class, min_samples, residual_threshold,
model_class : object
Object with the following object methods:
* ``estimate(*data)``
* ``success = estimate(*data)``
* ``residuals(*data)``
where `success` indicates whether the model estimation succeeded
(`True` or `None` for success, `False` for failure).
min_samples : int
The minimum number of data points to fit a model to.
residual_threshold : float
@@ -612,6 +641,7 @@ def ransac(data, model_class, min_samples, residual_threshold,
>>> model = EllipseModel()
>>> model.estimate(data)
True
>>> model.params # doctest: +SKIP
array([ -3.30354146e+03, -2.87791160e+03, 5.59062118e+03,
7.84365066e+00, 7.19203152e-01])
@@ -688,7 +718,12 @@ def ransac(data, model_class, min_samples, residual_threshold,
# estimate model for current random sample set
sample_model = model_class()
sample_model.estimate(*samples)
success = sample_model.estimate(*samples)
if success is not None: # backwards compatibility
if not success:
continue
# check if estimated model is valid
if is_model_valid is not None and not is_model_valid(sample_model,
+3 -3
View File
@@ -233,9 +233,9 @@ def test_ransac_dynamic_max_trials():
# e = 50%, min_samples = 8
assert_equal(_dynamic_max_trials(50, 100, 8, 0.99), 1177)
# e = 0%, min_samples = 10
assert_equal(_dynamic_max_trials(1, 100, 10, 0), 0)
assert_equal(_dynamic_max_trials(1, 100, 10, 1), float('inf'))
# e = 0%, min_samples = 5
assert_equal(_dynamic_max_trials(1, 100, 5, 0), 0)
assert_equal(_dynamic_max_trials(1, 100, 5, 1), np.inf)
def test_ransac_invalid_input():
+1 -1
View File
@@ -26,7 +26,7 @@ INTENSITY_SAMPLE[1, 9:11] = 2
def test_all_props():
region = regionprops(SAMPLE, INTENSITY_SAMPLE)[0]
for prop in PROPS:
assert_equal(region[prop], getattr(region, PROPS[prop]))
assert_almost_equal(region[prop], getattr(region, PROPS[prop]))
def test_dtype():
+29 -2
View File
@@ -263,6 +263,11 @@ class ProjectiveTransform(GeometricTransform):
dst : (N, 2) array
Destination coordinates.
Returns
-------
success : bool
True, if model estimation succeeds.
"""
try:
@@ -270,7 +275,7 @@ class ProjectiveTransform(GeometricTransform):
dst_matrix, dst = _center_and_normalize_points(dst)
except ZeroDivisionError:
self.params = np.nan * np.empty((3, 3))
return
return False
xs = src[:, 0]
ys = src[:, 1]
@@ -309,6 +314,8 @@ class ProjectiveTransform(GeometricTransform):
self.params = H
return True
def __add__(self, other):
"""Combine this transformation with another.
@@ -459,6 +466,11 @@ class PiecewiseAffineTransform(GeometricTransform):
dst : (N, 2) array
Destination coordinates.
Returns
-------
success : bool
True, if model estimation succeeds.
"""
# forward piecewise affine
@@ -481,6 +493,8 @@ class PiecewiseAffineTransform(GeometricTransform):
affine.estimate(dst[tri, :], src[tri, :])
self.inverse_affines.append(affine)
return True
def __call__(self, coords):
"""Apply forward transformation.
@@ -658,6 +672,11 @@ class SimilarityTransform(ProjectiveTransform):
dst : (N, 2) array
Destination coordinates.
Returns
-------
success : bool
True, if model estimation succeeds.
"""
try:
@@ -665,7 +684,7 @@ class SimilarityTransform(ProjectiveTransform):
dst_matrix, dst = _center_and_normalize_points(dst)
except ZeroDivisionError:
self.params = np.nan * np.empty((3, 3))
return
return False
xs = src[:, 0]
ys = src[:, 1]
@@ -699,6 +718,7 @@ class SimilarityTransform(ProjectiveTransform):
self.params = S
return True
@property
def scale(self):
@@ -798,6 +818,11 @@ class PolynomialTransform(GeometricTransform):
order : int, optional
Polynomial order (number of coefficients is order + 1).
Returns
-------
success : bool
True, if model estimation succeeds.
"""
xs = src[:, 0]
ys = src[:, 1]
@@ -828,6 +853,8 @@ class PolynomialTransform(GeometricTransform):
self.params = params.reshape((2, u // 2))
return True
def __call__(self, coords):
"""Apply forward transformation.
+4
View File
@@ -11,6 +11,10 @@ dtype_range = {np.bool_: (False, True),
np.uint16: (0, 65535),
np.int8: (-128, 127),
np.int16: (-32768, 32767),
np.int64: (-2**63, 2**63 - 1),
np.uint64: (0, 2**64 - 1),
np.int32: (-2**31, 2**31 - 1),
np.uint32: (0, 2**32 - 1),
np.float32: (-1, 1),
np.float64: (-1, 1)}
+5
View File
@@ -101,6 +101,11 @@ def test_save_buttons():
timer = QtCore.QTimer()
timer.singleShot(100, QtGui.QApplication.quit)
# exercise the button clicks
sv.save_stack.click()
sv.save_file.click()
# call the save functions directly
sv.save_to_stack()
with expected_warnings(['precision loss']):
sv.save_to_file(filename)
+18
View File
@@ -1,2 +1,20 @@
"""
Widgets for interacting with ImageViewer.
These widgets should be added to a Plugin subclass using its `add_widget`
method or calling::
plugin += Widget(...)
on a Plugin instance. The Plugin will delegate action based on the widget's
parameter type specified by its `ptype` attribute, which can be::
'arg' : positional argument passed to Plugin's `filter_image` method.
'kwarg' : keyword argument passed to Plugin's `filter_image` method.
'plugin' : attribute of Plugin. You'll probably need to add a class
property of the same name that updates the display.
"""
from .core import *
from .history import *
+1 -18
View File
@@ -1,20 +1,3 @@
"""
Widgets for interacting with ImageViewer.
These widgets should be added to a Plugin subclass using its `add_widget`
method or calling::
plugin += Widget(...)
on a Plugin instance. The Plugin will delegate action based on the widget's
parameter type specified by its `ptype` attribute, which can be:
'arg' : positional argument passed to Plugin's `filter_image` method.
'kwarg' : keyword argument passed to Plugin's `filter_image` method.
'plugin' : attribute of Plugin. You'll probably need to add a class
property of the same name that updates the display.
"""
from ..qt import QtWidgets, QtCore, Qt
from ..utils import RequiredAttr
@@ -275,7 +258,7 @@ class CheckBox(BaseWidget):
added to a plugin.
"""
def __init__(self, name, value=False, alignment='center', ptype='kwarg',
def __init__(self, name, value=False, alignment='center', ptype='kwarg',
callback=None):
super(CheckBox, self).__init__(name, ptype, callback)
+2 -2
View File
@@ -80,9 +80,9 @@ class SaveButtons(BaseWidget):
notify(msg)
def save_to_file(self, filename=None):
if filename is None:
if not filename:
filename = dialogs.save_file_dialog()
if filename is None:
if not filename:
return
image = self.plugin.filtered_image
if image.dtype == np.bool: